mirror of
https://github.com/0015/ThatProject.git
synced 2026-01-12 17:27:43 +03:00
163 lines
3.9 KiB
JavaScript
163 lines
3.9 KiB
JavaScript
/////////////////////////////////////////////////////////////////
|
|
/*
|
|
ESP32 | Walkie-Talkie based on Node.js Server for multi-clients (ft. PCM speaker)
|
|
Video Tutorial: https://youtu.be/vq7mPgecGKA
|
|
Created by Eric Nam (ThatProject)
|
|
*/
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
////////////
|
|
// States
|
|
const states = {
|
|
IDLE: "Idle",
|
|
LISTENING: "Listening",
|
|
SPEAKING: "Speaking"
|
|
};
|
|
|
|
////////////
|
|
// Speaker(Optional)
|
|
// MacOS Issue
|
|
// [../deps/mpg123/src/output/coreaudio.c:81] warning: Didn't have any audio data in callback (buffer underflow)
|
|
// https://github.com/audiojs/audio-play/issues/15
|
|
const Speaker = require("speaker");
|
|
const Readable = require("stream").Readable;
|
|
const pcmSpeaker = new Readable();
|
|
pcmSpeaker.bitDepth = 32;
|
|
pcmSpeaker.channels = 1;
|
|
pcmSpeaker.sampleRate = 16000;
|
|
pcmSpeaker._read = bufRead;
|
|
pcmSpeaker.pipe(new Speaker());
|
|
function bufRead(buf) {
|
|
if (buf instanceof Buffer) {
|
|
pcmSpeaker.resume();
|
|
pcmSpeaker.push(buf);
|
|
} else if (buf == null) {
|
|
pcmSpeaker.pause();
|
|
}
|
|
}
|
|
|
|
////////////
|
|
// WebSocket
|
|
const WebSocket = require("ws");
|
|
const WS_PORT = process.env.WS_PORT || 8888;
|
|
const wsServer = new WebSocket.Server(
|
|
{
|
|
port: WS_PORT,
|
|
perMessageDeflate: {
|
|
concurrencyLimit: 10,
|
|
threshold: 1024
|
|
}
|
|
},
|
|
() => console.log(`[Server] WS server is listening at ${WS_PORT}`)
|
|
);
|
|
|
|
wsServer.on("connection", (ws, req) => {
|
|
console.log("[Client] Connected!");
|
|
ws.timestamp = getTimestamp();
|
|
ws.id = wsServer.getUniqueID(ws.timestamp);
|
|
ws.state = states.IDLE;
|
|
|
|
ws.send(ws.timestamp);
|
|
ws.on("message", (event) => {
|
|
messageHandler(ws, event);
|
|
});
|
|
});
|
|
|
|
wsServer.getUniqueID = function (timestamp) {
|
|
function s4() {
|
|
return Math.floor((1 + Math.random()) * 0x10000)
|
|
.toString(16)
|
|
.substring(1);
|
|
}
|
|
return timestamp + "-" + s4();
|
|
};
|
|
|
|
function messageHandler(ws, event) {
|
|
ws.timestamp = getTimestamp();
|
|
if (event instanceof Buffer) {
|
|
if (event.length == 1) {
|
|
console.log("[Client] Ping from: " + ws.id);
|
|
} else if (event.length > 1) {
|
|
sendDataClients(ws, event);
|
|
bufRead(event); // Resume Speaker Out
|
|
}
|
|
} else {
|
|
if (checkAllClientState()) {
|
|
ws.state = states.SPEAKING;
|
|
ws.send(event);
|
|
} else {
|
|
ws.state = states.IDLE;
|
|
bufRead(null); // Pause Speaker Out
|
|
}
|
|
sendDataClients(ws, event);
|
|
}
|
|
}
|
|
|
|
function sendDataClients(d_client, data) {
|
|
var timestamp = getTimestamp();
|
|
wsServer.clients.forEach(function each(client) {
|
|
if (d_client != client) {
|
|
client.timestamp = timestamp;
|
|
console.log("[Client] Client Id: " + client.id + " Client State: " + client.state);
|
|
if (client.state == states.IDLE) {
|
|
// OTHER CLIENTS, IDLE -> LISTENING
|
|
if (data instanceof Buffer) {
|
|
// NO ACTION REQUIRED
|
|
} else {
|
|
client.state = states.LISTENING;
|
|
client.send(timestamp);
|
|
}
|
|
} else {
|
|
// OTHER CLIENTS, LISTENING -> IDLE
|
|
if (data instanceof Buffer) {
|
|
client.send(data);
|
|
} else {
|
|
client.state = states.IDLE;
|
|
client.send(timestamp);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function getTimestamp() {
|
|
var last8digit_Timestamp = Date.now().toString().slice(-8);
|
|
return parseInt(last8digit_Timestamp);
|
|
}
|
|
|
|
function checkAllClientState() {
|
|
var result = false;
|
|
wsServer.clients.forEach(function each(client) {
|
|
if (client.state != states.IDLE) {
|
|
result = false;
|
|
return;
|
|
}
|
|
result = true;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
function checkAlive() {
|
|
console.log("[Server] Total number of connected clients: " + wsServer.clients.size);
|
|
wsServer.clients.forEach(function each(client) {
|
|
if (client.state == states.IDLE) {
|
|
console.log(
|
|
"[Check Clinet Alive] Client Id: " +
|
|
client.id +
|
|
" Timestamp DIFF: " +
|
|
(getTimestamp() - client.timestamp)
|
|
);
|
|
if (getTimestamp() - client.timestamp > 10000) {
|
|
// 10sec
|
|
console.log("[Client TimeOut] Client Id: " + client.id);
|
|
client.close;
|
|
wsServer.clients.delete(client);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
setInterval(function () {
|
|
checkAlive();
|
|
}, 5000);
|