MQTT симулятор ESP32: PubSubClient и брокер в браузере без платы
MQTT на ESP32 — протокол умного дома: PubSubClient подключается к брокеру (Mosquitto, HiveMQ, CloudMQTT), подписывается на топики, публикует телеметрию. На живой плате три вещи ломаются по очереди: WiFi не подключается (пароль, роутер), брокер недоступен (брандмауэр, порт 1883), сертификат TLS на 8883 не тот. В браузере можно прогнать логику connect-publish-subscribe без реальной сети: WiFi фейковый, брокер фейковый, а скетч работает по тем же вызовам.
PubSubClient в движке симулятора поддержан: connect, publish, subscribe, loop, disconnect — те же методы, что в библиотеке. Ниже — проба publish, callback на подписку, граница «браузер против платы» и грабли TLS, ClientID и LWT.
Что у MQTT в браузере
В движке PubSubClient — объект, который имитирует брокер: connect(host, clientId) печатает «MQTT: подключено к broker (клиент «id»)», publish(topic, payload) пишет «MQTT ↑ topic = payload» в монитор, subscribe(topic) — «MQTT: подписка на topic», connected() возвращает 1 после connect и 0 после disconnect. WiFi.begin(ssid, pass) тоже фейковый: «WiFi: подключение к «MyWiFi»...», через секунду «WiFi: подключено! IP: 192.168.1.100». Callback на подписку в браузере не вызывается без живого сообщения — но сам подписочный код компилируется.
- connect(host, id) — фейковый «брокер», всегда успех; печать в монитор как на плате.
- publish(topic, payload) — пишет «MQTT ↑ topic = payload»; payload — строка или число.
- subscribe(topic) — фейковая подписка, печать «подписка на topic»; callback в браузере не вызывается.
- loop() — заглушка, как на плате; без вызова callback на плате не срабатывает.
- disconnect() — закрывает соединение, connected() возвращает 0; переподключение через reconnect() в коде.
Скетч-проба: publish телеметрии
#include <WiFi.h>#include <PubSubClient.h> const char* SSID = "MyWiFi";const char* PASS = "12345678";const char* BROKER = "broker.local"; WiFiClient net;PubSubClient client(net);unsigned long last = 0; void setup() { Serial.begin(115200); WiFi.begin(SSID, PASS); client.setServer(BROKER, 1883);} void loop() { if (!client.connected()) { if (client.connect("esp32-1")) { Serial.println("MQTT podkljuchen"); } else { delay(2000); return; } } client.loop(); if (millis() - last >= 5000) { last += 5000; float t = 22.5; char buf[10]; dtostrf(t, 4, 1, buf); client.publish("home/temp", buf); }}Скетч вставьте в основной симулятор: WiFi.begin подключится к фейковому MyWiFi, client.connect выведет «MQTT: подключено к broker.local (клиент «esp32-1»)», и каждые 5 секунд пойдёт «MQTT ↑ home/temp = 22.5». Логика publish работает без брокера — это и есть тест. Полный разбор PubSubClient — в уроке про PubSubClient первый скетч.
Callback на подписку
#include <WiFi.h>#include <PubSubClient.h> WiFiClient net;PubSubClient client(net); void onMessage(char* topic, byte* payload, unsigned int len) { String cmd; for (unsigned int i = 0; i < len; i++) cmd += (char)payload[i]; Serial.print("prishlo: "); Serial.println(cmd); if (cmd == "ON") digitalWrite(2, HIGH); if (cmd == "OFF") digitalWrite(2, LOW);} void setup() { pinMode(2, OUTPUT); Serial.begin(115200); WiFi.begin("MyWiFi", "12345678"); client.setServer("broker.local", 1883); client.setCallback(onMessage);} void loop() { if (client.connected()) { client.loop(); } else { if (client.connect("esp32-1")) client.subscribe("home/light/set"); }}Callback onMessage вызывается в client.loop(), когда брокер прислал сообщение в подписанный топик. В браузере без реального брокера callback не вызывается — но код компилируется, и логика «принял команду — переключил пин» проверяется через Serial. На живой плате Mosquitto-cli шлёт `mosquitto_pub -t home/light/set -m ON` — и светодиод зажигается. Полный разбор callback — в уроке про subscribe и callback.
Честная граница: браузер против живого брокера
| Проверка | Симулятор | Только железо |
|---|---|---|
| WiFi.begin и подключение | да: фейковое за 1 с | то же, плюс реальные сети |
| client.connect(host, id) | да: фейковый «брокер» | то же, плюс реальный Mosquitto/HiveMQ |
| publish(topic, payload) | да: печать в монитор | то же, плюс реальный брокер |
| subscribe и callback onMessage | компилируется, callback не вызывается | да — на реальном сообщении |
| TLS/SSL на порту 8883 | нет: фейковый стек без TLS | да — WiFiClientSecure + сертификат |
| Last Will и Retained | частично: API есть, физики нет | да — LWT уходит при разрыве |
Браузер исполняет логику publish и подключение, а реальная доставка сообщения через callback требует живого брокера. После того как скетч «публикует раз в 5 секунд» в основном симуляторе, переходите на плату: поднимите локальный Mosquitto, или используйте публичный HiveMQ broker.hivemq.com:1883 для теста. Полный разбор LWT и retain — в уроке про Last Will и уроке про retained-сообщения.
Грабли MQTT на ESP32
- Забытый client.loop(): без него callback не вызывается, и подписки «не работают». В loop — первой строкой, после проверки connected.
- ClientID уникален: два устройства с одинаковым id «вытесняют» друг друга — брокер держит одно подключение на id. Генерируйте из MAC-адреса.
- WiFi падает и MQTT зависает: клиент PubSubClient не переподключается автоматически. Машина состояний в loop — в уроке про reconnect.
- TLS на 8883: PubSubClient + WiFiClientSecure требуют сертификат корневого CA — без него подключение падает по таймауту. Используйте setCACert() с сертификатом брокера.
- Размер payload: PubSubClient по умолчанию держит 256 байт; для больших JSON-сообщений поднимите setBufferSize(1024) в setup.