Refact json response and fixed piezo tone

This commit is contained in:
Nomi Nonsense (Nonszy) 2024-03-10 13:56:38 +07:00
parent 76e9ae14e5
commit e064e198a3
6 changed files with 198 additions and 31 deletions

105
README.md
View File

@ -2,6 +2,10 @@
A **SIMPLE** REST API based serial communication. Assisted Firmata protocol and johnny-five API, enabling software communication with the Arduino board using the Server API. that's it. Idk about electronics and networking actually. But that piece of knowledge motivated me to make some IoT stuff. Enjoy (⁠づ⁠ ̄⁠ ⁠³⁠ ̄⁠)⁠づ A **SIMPLE** REST API based serial communication. Assisted Firmata protocol and johnny-five API, enabling software communication with the Arduino board using the Server API. that's it. Idk about electronics and networking actually. But that piece of knowledge motivated me to make some IoT stuff. Enjoy (⁠づ⁠ ̄⁠ ⁠³⁠ ̄⁠)⁠づ
![ENA BBQ](https://media1.tenor.com/m/Nl2_tsGV-qkAAAAC/enadbbq-dreambbq.gif)
<small>*the animation is from ENA BBQ series by JoelG</small>
## Setup ## Setup
Tools that are required Tools that are required
- Arduino Board - Arduino Board
@ -9,7 +13,7 @@ Tools that are required
- [Node.js](https://nodejs.org/en) v16.0.0^ or other javascript runtime - [Node.js](https://nodejs.org/en) v16.0.0^ or other javascript runtime
### Board ### Board
Arduino assembly guide also available in [johnny-five](https://github.com/rwaldron/johnny-five?tab=readme-ov-file#setup-and-assemble-arduino) documentation\ Arduino assembly guide also available in [johnny-five](https://github.com/rwaldron/johnny-five?tab=readme-ov-file#setup-and-assemble-arduino) documentation
1. Plug in your Board via USB 1. Plug in your Board via USB
2. Open Arduino IDE then select your Board 2. Open Arduino IDE then select your Board
3. Go to `File > Examples > Firmata` Select `StandarFirmataPlus` 3. Go to `File > Examples > Firmata` Select `StandarFirmataPlus`
@ -61,8 +65,11 @@ Arduino assembly guide also available in [johnny-five](https://github.com/rwaldr
- [Read pin mode](#read-pin-mode) - [Read pin mode](#read-pin-mode)
- [Set pin mode](#set-pin-mode) - [Set pin mode](#set-pin-mode)
- [LED](#led) - [LED](#led)
- [Read led state](#read-led-state) - [Read LED state](#read-led-state)
- [Set led state](#set-led-state) - [Set LED state](#set-led-state)
- [RGB LED](#rgb-led)
- [Read RGB state](#read-rgb-state)
- [Set RGB state](#set-rgb-state)
## Common HTTP Responses ## Common HTTP Responses
Some HTTP responses are sent with JSON, otherwise an HTML body will be sent which is the default express.js response Some HTTP responses are sent with JSON, otherwise an HTML body will be sent which is the default express.js response
@ -82,7 +89,7 @@ Some HTTP responses are sent with JSON, otherwise an HTML body will be sent whic
"pin_state": { "pin_state": {
"13": "HIGH" "13": "HIGH"
}, },
"message": "Pin 13 Set to HIGH" "message": "Changed pin state 13 to HIGH"
} }
``` ```
| Property | Description | | Property | Description |
@ -98,7 +105,7 @@ This is an example of javascript code sending a request to turn on the LED light
async function turnOnLed() { async function turnOnLed() {
const res = await fetch("http://localhost:3000/led/13/on"); // Set LED to HIGH const res = await fetch("http://localhost:3000/led/13/on"); // Set LED to HIGH
const data = await res.json(); const data = await res.json();
console.log(data); console.log(data); // { status: 200, pin_state: { 13: high }, message: "Pin 13 Set to HIGH" }
} }
``` ```
@ -168,7 +175,7 @@ async function turnOnLed() {
## LED ## LED
### Read led state ### Read LED state
- **URL Endpoint** - **URL Endpoint**
/led/:p/ /led/:p/
@ -195,17 +202,17 @@ async function turnOnLed() {
} }
``` ```
### Set led state ### Set LED state
- **URL Endpoint** - **URL Endpoint**
/pin/:p/:a /led/:p/:a
- **URL Params** - **URL Params**
| Params | Mark | Type | Required | Description | | Params | Mark | Type | Required | Description |
|--------|------|------|----------|-------------| |--------|------|------|----------|-------------|
| p | pin | `string` | true | Seleced pin | | p | pin | `string` | true | Seleced pin |
| a | act | `'on'`, `'off'` | true | Action | | a | act | `'on'`, `'off'`, `'high'`, `'low'` | true | Action |
- **Method(s)** - **Method(s)**
@ -219,7 +226,85 @@ async function turnOnLed() {
"pin_state": { "pin_state": {
"13": "HIGH" "13": "HIGH"
}, },
"message": "Led pin 13 setted HIGH" "message": "Changed pin state 13 to HIGH"
}
```
## RGB LED
### Read RGB state
- **URL Endpoint**
/rgb-led
- **Body**
```javascript
{
r: number,
g: number,
b: number
}
```
- **Method**
`POST`
- **Sample Response**
```json
{
"status": 200,
"pin_state": {
"7": true,
"6": true,
"5": false
}
}
```
### Set RGB state
- **URL Endpoint**
/rgb-led
- **Body**
```javascript
{
r: {
pin: number,
value: boolean
},
g: {
pin: number,
value: boolean
},
b: {
pin: number,
value: boolean
}
}
```
- **Method(s)**
`PATCH`
- **Sample Response**
```json
{
"status": 200,
"pin_state": {
"7": true,
"6": true,
"5": false
},
"message": "Success changed pins 7, 6, 5 to state HIGH, HIGH, LOW"
} }
``` ```

View File

@ -9,13 +9,14 @@ export function readLed (req: Request, res: Response): Response<string | any> {
const { p } = req.params; const { p } = req.params;
const pin: number = Number.parseInt(p); const pin: number = Number.parseInt(p);
const pinState: digitalValue = board.pins[pin].value == 1 ? 'ON' : 'OFF'; const pinState: voltage = board.pins[pin].value == 1 ? 'HIGH' : 'LOW';
return res.status(200).json({ return res.status(200).json({
status: 200, status: 200,
pin_state: { pin_state: {
[pin]: pinState [pin]: pinState
} },
message: `Led pin ${pin} is ${pinState}`
}); });
} }
catch (err) { catch (err) {
@ -32,20 +33,18 @@ export function writeLed (req: Request, res: Response): Response<string | any> {
const act: string = a.toLocaleLowerCase(); const act: string = a.toLocaleLowerCase();
const pin: number = Number.parseInt(p); const pin: number = Number.parseInt(p);
let state: digitalValue; let state: voltage;
let volt: voltage; let volt: voltage;
try { try {
switch (act) { switch (act) {
case 'on': case 'on' || 'high':
state = 'ON'; state = 'HIGH';
volt = 1; volt = 1;
console.log(`${req.hostname} | ${pin} | LED: ${state}`);
break; break;
case 'off': case 'off' || 'low':
state = 'OFF'; state = 'LOW';
volt = 0; volt = 0;
console.log(`${req.hostname} | ${pin} | LED: ${state}`);
break; break;
default: default:
console.log(`${req.hostname} | ${pin} | LED: INVALID ACT`); console.log(`${req.hostname} | ${pin} | LED: INVALID ACT`);
@ -54,12 +53,17 @@ export function writeLed (req: Request, res: Response): Response<string | any> {
message: `Invalid act ${act}` message: `Invalid act ${act}`
}); });
} }
board.digitalWrite(pin, volt); board.digitalWrite(pin, volt);
console.log(`${req.hostname} | ${pin} | LED: ${state}`);
res.status(200).json({ res.status(200).json({
status: 200, status: 200,
message: `Success changed pin ${pin} to state ${state}` pin_state: {
[pin]: state
},
message: `Changed pin state ${pin} to ${state}`
}); });
} }
catch (err) { catch (err) {
@ -147,19 +151,26 @@ export function writeRgbLed (req: Request, res: Response): Response<string | any
isAnode: true isAnode: true
}) })
const isHigh: boolean | string = true || 'HIGH' || 'high';
led.red = new Led(r.pin); led.red = new Led(r.pin);
led.green = new Led(g.pin); led.green = new Led(g.pin);
led.blue = new Led(b.pin); led.blue = new Led(b.pin);
if (r.value == true) led.red.on(); else led.red.off(); if (r.value == isHigh) led.red.on(); else led.red.off();
if (g.value == true) led.green.on(); else led.green.off(); if (g.value == isHigh) led.green.on(); else led.green.off();
if (b.value == true) led.blue.on(); else led.blue.off(); if (b.value == isHigh) led.blue.on(); else led.blue.off();
const pins: string = rgbLeds.map(c => c.pin.toString()).join(", "); const pins: string = rgbLeds.map(c => c.pin.toString()).join(", ");
const values: string = rgbLeds.map(c => `${c.value}`).join(", "); const values: string = rgbLeds.map(c => `${c.value}`).join(", ");
return res.status(200).json({ return res.status(200).json({
status: 200, status: 200,
pin_state: {
[r.pin]: led.red.isOn,
[g.pin]: led.green.isOn,
[b.pin]: led.blue.isOn
},
message: `Success changed pins ${pins} to state ${values}` message: `Success changed pins ${pins} to state ${values}`
}); });
} }

View File

@ -3,7 +3,32 @@ import { Piezo } from "johnny-five";
import { Pitch } from "../melodies"; import { Pitch } from "../melodies";
export function piezoTone (req: Request, res: Response): Response { export function piezoTone (req: Request, res: Response): Response {
const pin: number = Number.parseInt(req.params.p);
const frequency: number = Number.parseInt(req.params.f);
const piezo: Piezo = new Piezo(pin);
piezo.tone(frequency * 7.3, 100);
return res.status(200).json({
status: 200,
pin_tone: {
[pin]: frequency
},
message: `Piezo ${pin} tone ${frequency}`
});
}
export function piezoNote (req: Request, res: Response): Response {
const { pin, note }: { pin: number, note: string } = req.body; const { pin, note }: { pin: number, note: string } = req.body;
const notePitch = Pitch[note.toUpperCase()];
if (notePitch == null) {
return res.status(400).json({
status: 400,
message: `Invalid note ${note}`
});
}
const piezo: Piezo = new Piezo(pin); const piezo: Piezo = new Piezo(pin);
@ -15,7 +40,13 @@ export function piezoTone (req: Request, res: Response): Response {
return res.status(200).json({ return res.status(200).json({
status: 200, status: 200,
message: `Piezo ${pin} tone ${note}` pin_tone: {
[pin]: notePitch
},
pin_note: {
[pin]: note.toUpperCase()
},
message: `Piezo ${pin} tone note ${note}`
}); });
} }
@ -45,12 +76,17 @@ export function piezoPlayNotes (req: Request, res: Response): Response {
}) })
.filter((note) => note[0].trim() != "-"); .filter((note) => note[0].trim() != "-");
const notesS: string = notes.join(", "); const notesS: string = notes.length > 4 ?
notes.slice(0, 4).join(", ")+"..." :
notes.join(", ");
piezo.play({ song, tempo }); piezo.play({ song, tempo });
return res.status(200).json({ return res.status(200).json({
status: 200, status: 200,
pin_notes: {
[pin]: notes
},
message: `Piezo ${pin} play notes ${notesS}` message: `Piezo ${pin} play notes ${notesS}`
}); });
} }

View File

@ -18,17 +18,22 @@ const io: Server = new Server(server, {
} }
}); // I have no experience at WebSocket, so.. forgive me :) }); // I have no experience at WebSocket, so.. forgive me :)
// Server configuration
const host: string = 'localhost'; const host: string = 'localhost';
const port: number = 3000; const port: number = 3000;
// Express middleware
app.use(express.json()); app.use(express.json());
app.use(express.static('client')); app.use(express.static('client'));
// Socket.io event handlers
io.on('connection', socketHandler); io.on('connection', socketHandler);
// HTTP Routes
app.use('/', view); app.use('/', view);
app.use('/api-arduino', isBoardConnected, api); app.use('/api-arduino', isBoardConnected, api); // Board API Controllers
// Run server
console.log("\nRunning Server..."); console.log("\nRunning Server...");
server.listen(port, () => { server.listen(port, () => {
console.log(`Server is connected and running in ${host} at port ${port} 🗣️🗣️🗣️`); console.log(`Server is connected and running in ${host} at port ${port} 🗣️🗣️🗣️`);

View File

@ -3,7 +3,7 @@ import { Router } from "express";
import { readLed, readRgbLed, writeLed, writeRgbLed } from "../controller/led"; import { readLed, readRgbLed, writeLed, writeRgbLed } from "../controller/led";
import { readPin, setPin } from "../controller/pin"; import { readPin, setPin } from "../controller/pin";
import { piezoNoTone, piezoPlayNotes, piezoTone } from "../controller/piezo"; import { piezoNoTone, piezoNote, piezoPlayNotes, piezoTone } from "../controller/piezo";
import { rotateServo } from "../controller/servo"; import { rotateServo } from "../controller/servo";
import { readResistor } from "../controller/photoresistor"; import { readResistor } from "../controller/photoresistor";
@ -11,22 +11,28 @@ import { isPinNumeric } from "../middleware/pin";
const router: Router = Router(); const router: Router = Router();
// Client page
router.get('/hello', (req, res: Response): Response<string> => { router.get('/hello', (req, res: Response): Response<string> => {
return res.status(200).send("Hello"); return res.status(200).send("Hello");
}) })
// PinMode
router.get('/pin/:p', readPin); router.get('/pin/:p', readPin);
router.patch('/pin/:p/:m', setPin); router.patch('/pin/:p/:m', setPin);
// LED
router.get('/led/:p', isPinNumeric, readLed); router.get('/led/:p', isPinNumeric, readLed);
router.patch('/led/:p/:a', isPinNumeric, writeLed); router.patch('/led/:p/:a', isPinNumeric, writeLed);
router.get('/rgb-led', readRgbLed); // RGB LED
router.post('/rgb-led', readRgbLed);
router.patch('/rgb-led/', writeRgbLed); router.patch('/rgb-led/', writeRgbLed);
router.patch('/piezo/', piezoTone); // Piezo
router.patch('/piezo/stop/', piezoNoTone); router.patch('/piezo/:p/:f', isPinNumeric, piezoTone);
router.patch('/piezo/note', piezoNote);
router.patch('/piezo/music/', piezoPlayNotes); router.patch('/piezo/music/', piezoPlayNotes);
router.patch('/piezo/stop/', piezoNoTone);
// for real-time communication is deprecated and not recommended // for real-time communication is deprecated and not recommended
// use other protocol like websocket instead // use other protocol like websocket instead

24
test/serial/piezo.ts Normal file
View File

@ -0,0 +1,24 @@
import { Board, Piezo } from "johnny-five";
const board = new Board({
port: '/dev/ttyUSB0',
repl: false
});
board.on('ready', () => {
const piezo = new Piezo(11);
let ins = 0;
board.loop(500, async () => {
piezo.play({
song: "C4",
beats: 1/2,
tempo: 100
})
await new Promise(resolve => setTimeout(resolve, 100));
piezo.note("C4", 100);
await new Promise(resolve => setTimeout(resolve, 100));
piezo.tone(262 * 7.3, 100);
})
})