Добавить в избранное

ESP32 и MQTT: умный дом с брокером, топики и PubSubClient

Чтение ≈ 4 мин

MQTT на ESP32: протокол для умного дома, брокер Mosquitto, публикация показаний датчиков и подписка на команды, топики и QoS, Home Assistant интеграция и PubSubClient в примерах.

Что такое 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.
Поделиться
Понравился материал?

Комментарии

Добавить комментарий