Что такое MQTT
MQTT — лёгкий протокол «издатель-подписчик» для IoT: устройства шлют сообщения в топики на брокере (центральном сервере), а подписчики их читают. Нет прямых связей устройство-устройство — все говорят через брокера. Идеально для умного дома: датчик публикует температуру в home/bedroom/temp, а Home Assistant, телефон и другие устройства подписаны и получают обновления.
Архитектура:
ESP32(датчик) ──publish──> Брокер (Mosquitto) <──subscribe── Home Assistant
ESP32(лампа) <──subscribe── Брокер <──publish── Приложение «включи свет»
Брокер: где он живёт
- Свой: Mosquitto на Raspberry Pi, NAS или VPS — полный контроль.
- Публичные тестовые:
broker.emqx.io,test.mosquitto.org— только для экспериментов, без приватности. - Home Assistant: Mosquitto идёт как аддон — стандартный путь для HA.
PubSubClient на ESP32
Ставим библиотеку PubSubClient (Nick O'Leary):
#include <WiFi.h>
#include <PubSubClient.h>
WiFiClient espClient;
PubSubClient mqtt(espClient);
const char* BROKER = "192.168.1.10"; // ваш Mosquitto
void reconnect() {
while (!mqtt.connected()) {
if (mqtt.connect("esp32-room1", "user", "pass")) {
mqtt.subscribe("home/room1/light/set"); // подписка на команды
} else {
delay(5000);
}
}
}
void setup() {
WiFi.begin("SSID", "pass");
while (WiFi.status() != WL_CONNECTED) delay(500);
mqtt.setServer(BROKER, 1883);
mqtt.setCallback(onMqtt);
}
void loop() {
if (!mqtt.connected()) reconnect();
mqtt.loop(); // обязательно — обрабатывает входящие
}
Публикация показаний
float t = dht.readTemperature();
mqtt.publish("home/room1/temp", String(t).c_str());
mqtt.publish("home/room1/status", "online");
Все подписанные на home/room1/temp мгновенно получают значение. Структура топиков — ваша: принято место/устройство/измерение — home/kitchen/temp, home/garage/door.
Приём команд: колбэк
void onMqtt(char* topic, byte* payload, unsigned int len) {
String msg;
for (unsigned int i = 0; i < len; ++i) msg += (char)payload[i];
if (String(topic) == "home/room1/light/set") {
digitalWrite(RELAY, msg == "ON" ? HIGH : LOW);
mqtt.publish("home/room1/light/state", msg.c_str()); // подтверждение
}
}
Паттерн /set для команд и /state для состояния — конвенция Home Assistant: публикуете в light/set, лампа отвечает в light/state — интерфейс HA синхронизируется.
QoS и retain: гарантии доставки
- QoS 0 — «выстрелил и забыл»: может потеряться, самый быстрый. Для телеметрии.
- QoS 1 — доставлено хотя бы раз, возможны дубли. Для команд.
- QoS 2 — ровно один раз, медленно. Для критичных операций.
- Retain — брокер хранит последнее сообщение в топике; новый подписчик сразу видит состояние:
mqtt.publish("home/room1/light/state", "ON", true); // retained
Подписавшийся позже узнает текущее состояние лампы без опроса — именно retain делает MQTT удобным для статусов.
Last Will: «я умер»
Устройство при подключении регистрирует LWT — сообщение, которое брокер опубликует, если связь оборвётся:
mqtt.connect("esp32-room1", "user", "pass",
"home/room1/status", // LWT-топик
0, // QoS
true, // retain
"offline"); // сообщение при отключении
mqtt.publish("home/room1/status", "online", true);
При обрыве Wi-Fi или выключении питания брокер сам выставит offline — мониторинг видимости устройств бесплатно.
Интеграция с Home Assistant
HA имеет MQTT Discovery: публикуете конфиг-топик, и устройство появляется в интерфейсе само:
mqtt.publish("homeassistant/switch/esp32_light/config",
"{\"name\":\"Свет\",\"cmd_t\":\"home/room1/light/set\","
"\"stat_t\":\"home/room1/light/state\",\"uniq_id\":\"esp32_light\"}",
true);
После этого в Home Assistant появляется выключатель «Свет» — без YAML-настройки.
Типичные проблемы
connectвозвращает -2/5: брокер недоступен, неправильные логин/пароль, порт 1883 закрыт фаерволом.- Сообщения не доходят:
mqtt.loop()не вызывается в loop — входящие не обрабатываются. - Отваливается каждые N секунд: keepalive истекает —
mqtt.loop()должен крутиться, избегайтеdelay> keepalive. - Пакеты больше 256 байт: лимит PubSubClient по умолчанию —
setBufferSize(1024)для конфигов HA. - Хаотичные топики: стандартизуйте
место/устройство/метрикас самого начала.
Итог
MQTT — язык умного дома: публикация телеметрии, подписка на команды, retain для статусов, LWT для «жив/мёртв», discovery для Home Assistant. На ESP32 это PubSubClient + три строки на сообщение. С этого строят датчики, реле, сигнализации и интеграцию всей домашней автоматики.
Полный пример: датчик + команда
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
WiFiClient esp;
PubSubClient mqtt(esp);
DHT dht(4, DHT22);
const int RELAY = 5;
unsigned long lastPub = 0;
void onMsg(char* topic, byte* p, unsigned int len) {
String msg((char*)p, len);
if (String(topic) == "home/room1/relay/set") {
digitalWrite(RELAY, msg == "ON");
mqtt.publish("home/room1/relay/state", msg.c_str(), true);
}
}
void setup() {
pinMode(RELAY, OUTPUT);
dht.begin();
WiFi.begin("SSID", "pass");
while (WiFi.status() != WL_CONNECTED) delay(500);
mqtt.setServer("192.168.1.10", 1883);
mqtt.setCallback(onMsg);
}
void loop() {
if (!mqtt.connected()) {
if (mqtt.connect("esp32-room1", "user", "pass",
"home/room1/status", 0, true, "offline")) {
mqtt.subscribe("home/room1/relay/set");
mqtt.publish("home/room1/status", "online", true);
}
}
mqtt.loop();
if (millis() - lastPub > 10000) {
lastPub = millis();
float t = dht.readTemperature();
if (!isnan(t)) mqtt.publish("home/room1/temp", String(t,1).c_str(), true);
}
}
MQTT-топики: конвенция
home/<комната>/<устройство>/<метрика>
home/bedroom/temp — показания
home/bedroom/relay/set — команда
home/bedroom/relay/state — текущее состояние
home/bedroom/status — online/offline (LWT)
QoS и retain на практике
- Датчики: QoS 0 — потеря одного пакета не страшна.
- Команды: QoS 1 — гарантированная доставка.
- Состояние: retain — новый клиент сразу видит актуальное.
- LWT: retain + "offline" — устройство "умирает" автоматом при обрыве.
Discovery для Home Assistant
String cfg = R"({
"name": "Комната",
"stat_t": "home/room1/temp",
"unit_of_meas": "°C",
"dev_cla": "temperature",
"uniq_id": "esp32_room1_temp"
})";
mqtt.publish("homeassistant/sensor/esp32_room1_temp/config", cfg.c_str(), true);
Сенсор появляется в HA без ручной настройки.
Частые проблемы
- connect возвращает -2: брокер не отвечает — проверьте IP/порт/firewall.
- Постоянные переподключения: keepalive истекает —
mqtt.loop()реже. - Хаотичные топики: стандартизируйте
место/устройство/метрикас самого начала. - Пакеты режутся:
setBufferSize(1024)для конфигов HA. - Wi-Fi обрыв рвёт MQTT: переподключайте оба — Wi-Fi потом MQTT.