Эволюция от идеи до реального кода отражается в следующем:
Сценарий: ESP32-S3 + SIM7670 (A7670), TCP‑шлюз на Node.js и интеграция с WordPress
Исходные данные:
– Устройство: ESP32-S3 с SIM7670 (A7670), связь через LTE/4G.
– Оператор: Билайн (APN: internet.beeline.ru, логин/пароль: beeline).
– На сервере уже есть Node.js проект с HTTP API:
– API listening on port 3005
– API Key: gsm-secret-key-2026
– API URL:
http://localhost:3005
Цель: построить нормальный GSM‑шлюз для холодильников, который:
– держит постоянное TCP‑соединение с устройствами;
– мгновенно передает команды (например, OPEN для замка);
– обменивается телеметрией;
– при этом WordPress работает только с HTTP API шлюза, а не лезет напрямую к железу.
Ниже вся архитектура и протокол в одном месте.
Роли компонентов
Устройство (ESP32-S3 + SIM7670)
– Постоянный TCP‑клиент.
– Подключается к TCP‑серверу, отправляет HELLO с ID и секретом.
– Периодически шлет ping с телеметрией.
– Принимает команды (OPEN и т.п.) и отправляет события (DOOR_OPENED и т.д.).
Node‑шлюз (порт 3005 + отдельный TCP‑порт)
– На Node.js:
– TCP‑сервер для холодильников (отдельный порт, например 4000).
– REST API (уже есть:
http://localhost:3005, с API‑ключом).
– Держит карту подключений fridge_id → socket.
– Принимает телеметрию и события от устройств.
– По HTTP‑запросам от WordPress отправляет команды нужным холодильникам по TCP.
WordPress + плагин (например stolovka-fridges-gateway.php)
– Не общается с устройствами напрямую.
– При нажатии в админке «Открыть холодильник» делает HTTP‑запрос к Node‑шлюзу, например:
POST
http://localhost:3005/open
X-API-Key: gsm-secret-key-2026
fridge_id=123
– Node‑шлюз находит TCP‑сокет для fridge_id=123 и отправляет через него команду OPEN.
Протокол TCP (JSON по строкам)
Все сообщения – одна строка JSON + символ перевода строки \n. Это сильно упрощает парсинг.
2.1. Устройство → Node
Регистрация (HELLO)
Отправляется сразу после установления TCP‑соединения:
Код: Выделить всё
{"type":"hello","id":"fridge-123","secret":"ABCDEF123456","fw":"1.0.0"}
– id – уникальный ID холодильника (из админки).
– secret – секретный ключ (из админки/БД).
– fw – версия прошивки (по желанию).
Пинг с телеметрией
На регулярном интервале (например, каждые N секунд):
Код: Выделить всё
{"type":"ping","id":"fridge-123","t":4.5,"door":0,"comp":1,"mains":1}
Где:
– t – температура (С).
– door – состояние двери (0/1).
– comp – состояние компрессора (0/1).
– mains – наличие 220В (0/1).
События
Например, дверь открылась:
Код: Выделить всё
{"type":"event","id":"fridge-123","event":"DOOR_OPENED"}
Дверь закрылась:
Код: Выделить всё
{"type":"event","id":"fridge-123","event":"DOOR_CLOSED"}
Можно расширять (ALARM, POWER_LOSS и т.д.).
2.2. Node → Устройство
Подтверждение регистрации
После успешного HELLO:
mode – необязательное поле, можно использовать для режимов (idle/active) или вообще не использовать.
Команда открыть замок
Устройство:
– срабатывает openLock();
– ждёт, пока концевик двери покажет открытие;
– отправляет событие DOOR_OPENED.
Позднее можно добавить другие команды:
Код: Выделить всё
{"type":"cmd","cmd":"LOCK"}
{"type":"cmd","cmd":"REBOOT"}
Скетч устройства (основные моменты)
Скетч на ESP32-S3 + SIM7670 (через TinyGSM) делает следующее:
– настраивает APN Билайн;
– поднимает модем и GPRS;
– устанавливает TCP‑соединение с Node‑шлюзом;
– отправляет HELLO сразу после connect;
– периодически шлет ping;
– читает входящие строки JSON и выполняет команды.
Настройки APN под Билайн:
Код: Выделить всё
const char APN[] = "internet.beeline.ru";
const char GPRS_USER[] = "beeline";
const char GPRS_PASS[] = "beeline";
После успешного connectTcp() отправляем HELLO:
Код: Выделить всё
void sendHello() {
String payload = "{";
payload += ""type":"hello",";
payload += ""id":"fridge-123","; // TODO: взять из настроек
payload += ""secret":"ABCDEF123456""; // TODO: взять из настроек
payload += "}\n";
client.print(payload);
}
В loop():
Код: Выделить всё
void loop() {
ensureConnected(); // если TCP отвалился, пробуем пересоздать сеть и коннект
if (client.connected()) {
// Периодический пинг
unsigned long now = millis();
if (now - lastPingMs >= PING_INTERVAL_MS) {
lastPingMs = now;
sendPing(); // JSON: type=ping + телеметрия
}
// Обработка входящих команд
handleIncoming(); // парсит строки JSON, ищет type=cmd, cmd=OPEN и т.п.
}
delay(10);
}
Детали отправки ping и обработки команд можно подгонять под свой формат.
Node‑шлюз: TCP‑сервер + HTTP‑API
Идея: Node‑процесс слушает TCP‑порт для устройств и HTTP‑порт (3005) для WordPress.
4.1. TCP‑сервер (удерживает сокеты устройств)
Псевдокод на Node.js (упрощённый пример):
Код: Выделить всё
const net = require('net');
const socketsById = new Map(); // fridge_id -> socket
const server = net.createServer(socket => {
let buffer = '';
socket.on('data', data => {
buffer += data.toString('utf8');
let index;
while ((index = buffer.indexOf('\n')) >= 0) {
const line = buffer.slice(0, index).trim();
buffer = buffer.slice(index + 1);
if (!line) continue;
try {
const msg = JSON.parse(line);
if (msg.type === 'hello') {
const fridgeId = msg.id;
// TODO: проверить секрет msg.secret по БД или конфигу
socketsById.set(fridgeId, socket);
socket.fridgeId = fridgeId;
socket.write(JSON.stringify({ type: 'hello_ok', mode: 'idle' }) + "\n");
}
if (msg.type === 'ping') {
// обновить телеметрию в БД/кэше
}
if (msg.type === 'event') {
// обработать событие (лог, уведомление, HTTP‑hook в WP и т.д.)
}
} catch (e) {
console.error('Bad JSON from device:', e);
}
}
});
socket.on('close', () => {
if (socket.fridgeId) {
socketsById.delete(socket.fridgeId);
}
});
socket.on('error', err => {
console.error('Socket error:', err);
});
});
server.listen(4000, () => {
console.log('TCP gateway listening on port 4000');
});
4.2. HTTP‑API «открыть холодильник» (для WordPress)
В существующем Express‑API (порт 3005) добавляется endpoint:
Код: Выделить всё
app.post('/open', (req, res) => {
const apiKey = req.header('X-API-Key') || req.query.api_key;
if (apiKey !== 'gsm-secret-key-2026') {
return res.status(403).json({ error: 'forbidden' });
}
const fridgeId = req.body.fridge_id || req.query.fridge_id;
if (!fridgeId) {
return res.status(400).json({ error: 'fridge_id required' });
}
const socket = socketsById.get(fridgeId);
if (!socket) {
return res.status(503).json({ error: 'device_offline' });
}
const cmd = JSON.stringify({ type: 'cmd', cmd: 'OPEN' }) + "\n";
socket.write(cmd);
res.json({ status: 'queued' });
});
Теперь любая система (в том числе WordPress) может сделать:
– POST
http://localhost:3005/open
– Заголовок X-API-Key: gsm-secret-key-2026
– Тело: fridge_id=fridge-123
Node‑шлюз найдёт сокет холодильника и отправит ему команду OPEN по TCP.
Интеграция с WordPress
В плагине stolovka-fridges-gateway.php:
– Кнопка «Открыть холодильник» в админке вызывает REST‑handler плагина.
– Этот handler делает внутренний HTTP‑запрос к Node‑шлюзу.
Примерно так:
Код: Выделить всё
$response = wp_remote_post('http://localhost:3005/open', [
'headers' => [
'X-API-Key' => 'gsm-secret-key-2026',
],
'body' => [
'fridge_id' => $fridge_id, // ID устройства, привязанное к записи/терминалу
],
]);
if (is_wp_error($response)) {
// обработать ошибку
}
Node‑шлюз дальше сам общается с устройством по TCP.
Вывод
– Для мгновенных команд и большого количества устройств (холодильников) постоянное TCP‑соединение + отдельный Node‑шлюз – нормальная и правильная архитектура.
– WordPress в такой схеме работает только с HTTP API Node‑шлюза, не общаясь напрямую с железом.
– Устройство (ESP32-S3 + SIM7670) остаётся максимально простым: TCP‑клиент, который шлет HELLO, pинг и события, и выполняет команды, приходящие от Node‑шлюза.