Руководства · 15 мин чтения

ESP32 и MQTT: руководство по Mosquitto, Home Assistant и ESPHome

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

Связка ESP32 и MQTT — самый распространённый способ научить датчики и реле в доме общаться друг с другом и с системой умного дома вроде Home Assistant. MQTT — лёгкий протокол обмена сообщениями, созданный для небольших устройств: ESP32 кладёт измеренную температуру в топик, например «ev/salon/sicaklik», и каждая программа, которая слушает этот топик, мгновенно получает значение. Точно так же, когда ты нажимаешь кнопку в приложении Home Assistant на телефоне, команда «ON» доходит до ESP32 за доли секунды и переключает реле. В отличие от HTTP, здесь не открывается новое соединение на каждый запрос: всё идёт через одно открытое соединение с заголовками всего в два байта, поэтому это быстро и бережёт и Wi-Fi, и процессор.

В этом руководстве ты с нуля соберёшь рабочий узел умного дома. Сначала разберёшься с понятиями MQTT: брокер, топик, публикация и подписка, затем установишь брокер Mosquitto в Home Assistant или на Raspberry Pi. Дальше с библиотекой PubSubClient будешь публиковать температуру и влажность с DHT22 на ESP32, слушать команды для реле и добавишь MQTT Discovery, чтобы устройство само появилось в Home Assistant. Мы также разберём QoS и retain, сообщение «последней воли» (LWT), которое помечает устройство как «недоступное» при обрыве связи, альтернативу ESPHome, которая делает то же самое без единой строки на C++, отличия на ESP8266 и Raspberry Pi Pico W и самые частые ошибки по порядку.

Отладочная плата ESP32, подключённая к Home Assistant по MQTT
Благодаря встроенному Wi-Fi ESP32 подключается прямо к MQTT-брокеру в домашней сети; данные датчиков и команды для реле идут по одному и тому же соединению.

Что такое MQTT и как он работает?

MQTT (Message Queuing Telemetry Transport) работает по модели публикация/подписка. В центре стоит сервер, который называется брокер; ESP32, Home Assistant, твой телефон или программа на компьютере подключаются к нему как клиенты. Клиенты не знают друг о друге и не знают IP-адресов друг друга: один публикует сообщение в топик, а брокер пересылает его всем, кто подписан на этот топик. Представь себе почту: ESP32 бросает письмо в ящик с адресом «ev/salon/sicaklik», а Home Assistant, который следит за этим ящиком, сразу его получает. Соединение всегда открывает клиент, поэтому ESP32 не нужен ни статический IP, ни открытый порт на роутере; достаточно знать адрес брокера.

ПонятиеЧто делаетПример
Брокер (broker)Сервер, который принимает сообщения и раздаёт их подписчикамMosquitto, брокер Mosquitto в Home Assistant
Клиент (client)Любое устройство или программа, подключённые к брокеру; у каждого свой уникальный client IDESP32, Home Assistant, MQTT Explorer
Топик (topic)Адрес сообщения из уровней, разделённых косой чертойev/salon/sicaklik
Публикация (publish)Отправка сообщения в топикОтправка значения «23.4» в ev/salon/sicaklik
Подписка (subscribe)Регистрация на получение сообщений из топикаESP32 слушает ev/salon/role/komut
Полезная нагрузка (payload)Содержимое сообщения: текст, число или JSONON, 23.4, {"sicaklik":23.4}
RetainБрокер хранит последнее сообщение топика и сразу отдаёт его новым подписчикамСостояние реле: ON
LWT («последняя воля»)Сообщение, которое брокер публикует от имени клиента, если тот неожиданно отключилсяev/salon/baglanti: offline (не в сети)
QoSУровень гарантии доставки сообщения (0, 1 или 2)QoS 1 для команд реле

Почему MQTT? Сравнение с веб-сервером, Blynk и Telegram

СпособПлюсыМинусыЛучше всего для
MQTT + Home AssistantЛокально и быстро, в обе стороны; сотни устройств на одной панели, автоматизацииНужен работающий брокерУправления всем домом из одного места
Веб-сервер на ESP32Не нужен дополнительный сервер, открывается в браузереУ каждого устройства своя страница, нет автоматизаций между устройствамиПанели управления одним устройством
Blynk IoTГотовая панель на телефоне, доступ через интернетЗависит от облака, ограничения на бесплатном тарифеБыстрых мобильных прототипов
Telegram-ботБесплатные мгновенные уведомления, без проброса портовНет графиков и панелиТревог и простых команд
ESP-NOWПлаты ESP общаются без роутера и брокераТолько между платами ESP, напрямую до Home Assistant не достаётПередачи данных с датчика на батарейке на шлюз

Коротко: MQTT раскрывается, когда устройств становится больше. Добавляя новый датчик, ты не трогаешь существующий код, а просто начинаешь публиковать в новый топик. Если нужно управлять одним устройством из браузера, быстрее дойти до цели поможет руководство асинхронный веб-сервер и WebSocket на ESP32; для уведомлений на телефон смотри Telegram-бот на ESP32, а для готовой мобильной панели — Blynk и ESP32. Для датчика в саду, куда не добивает роутер, хорошее решение — передавать данные через ESP-NOW на ESP32-шлюз, подключённый к MQTT.

Структура топиков MQTT и подстановочные символы

Топики похожи на пути к папкам: у «ev/salon/sicaklik» (по-турецки «дом/гостиная/температура») три уровня, и регистр в них важен, поэтому «Ev/Salon» и «ev/salon» — два разных топика. Не начинай с косой черты («/ev/salon» создаёт пустой первый уровень) и избегай пробелов, кириллицы и букв с диакритикой; хватит латинских букв, цифр, дефисов и подчёркиваний. При подписке можно использовать два подстановочных символа: + заменяет ровно один уровень («ev/+/sicaklik» вернёт температуру всех комнат), а # охватывает всё, что после него, и может стоять только в конце («ev/#» вернёт все сообщения дома). Подстановочные символы работают только при подписке; публикуется сообщение всегда в один конкретный топик. Топики, начинающиеся с $ (например, $SYS), зарезервированы для собственной статистики брокера и не попадают под подписку на #. Для проекта из этого руководства мы возьмём такую схему:

ТопикКто публикуетКто слушаетRetain
ev/salon/sicaklikESP32 (каждые 30 секунд)Home AssistantДа
ev/salon/nemESP32 (каждые 30 секунд)Home AssistantДа
ev/salon/role/komut (команда реле)Home Assistant, телефон, автоматизацииESP32Нет
ev/salon/role/durum (состояние реле)ESP32 (при каждом изменении)Home AssistantДа
ev/salon/baglantiESP32 (online), брокер (offline)Home AssistantДа
homeassistant/statusHome Assistant (online при запуске)ESP32Нет

Установка MQTT-брокера: Mosquitto

Самый популярный бесплатный MQTT-брокер — Eclipse Mosquitto; даже на Raspberry Pi он легко справляется с сотнями сообщений в секунду. Установить его можно тремя простыми способами: официальное приложение Mosquitto broker, если у тебя Home Assistant OS, пакет apt на отдельном Raspberry Pi или Linux-компьютере либо установщик Mosquitto для Windows. Какой бы вариант ни был выбран, брокер использует порт 1883 для незашифрованного MQTT и 8883 для MQTT с шифрованием TLS, и ему достаточно быть в той же домашней сети, что и ESP32. В Home Assistant настройка выглядит так:

  1. В Home Assistant открой Настройки › Приложения (Settings › Apps; в старых версиях раздел называется «Дополнения», Add-ons), нажми Установить приложение (Install app) и выбери в списке Mosquitto broker.
  2. Нажми Установить (Install); когда установка закончится, нажми Запустить (Start) и оставь включённым запуск при загрузке.
  3. Создай для ESP32 отдельного пользователя: в разделе Настройки › Люди › Пользователи (Settings › People › Users) добавь пользователя «esp32» с надёжным паролем. Если этой вкладки не видно, включи в профиле расширенный режим (Advanced mode). Имена «homeassistant» и «addons» зарезервированы для брокера, их не используй.
  4. На странице Настройки › Устройства и службы (Settings › Devices & services) интеграция MQTT обычно появляется как «Обнаружено» (Discovered); нажми Настроить (Configure) и подтверди. Если её нет, нажми Добавить интеграцию (Add integration), найди MQTT и выбери вариант, который настраивает Mosquitto автоматически.
  5. В коде для ESP32 в качестве адреса брокера нужно будет указать IP-адрес Home Assistant (например, 192.168.1.10), порт 1883 и только что созданного пользователя.

На Linux-машине вроде Raspberry Pi или Ubuntu установка занимает несколько команд. Установи брокер и инструменты для проверки командой sudo apt install mosquitto mosquitto-clients. Создай пользователя «esp32» командой sudo mosquitto_passwd -c /etc/mosquitto/passwd esp32 и дважды введи пароль; опция -c создаёт файл заново, поэтому при добавлении второго пользователя её не указывай, иначе существующие пользователи сотрутся. Затем создай файл local.conf в папке /etc/mosquitto/conf.d/, впиши туда три строки из таблицы ниже и перезапусти службу командой sudo systemctl restart mosquitto. В Windows добавь те же строки в файл mosquitto.conf в папке установки Mosquitto и перезапусти службу Mosquitto Broker.

СтрокаЗначение
listener 1883Слушает порт 1883 на всех сетевых интерфейсах; без этой строки Mosquitto 2.x принимает подключения только с того же компьютера
allow_anonymous falseОтклоняет подключения без имени пользователя и пароля; в 2.x это и так поведение по умолчанию, но прописывать его явно — хорошая привычка
password_file /etc/mosquitto/passwd (файл паролей)Использует файл паролей, созданный через mosquitto_passwd; пароли хранятся в нём в виде хешей
Если в конфигурации не задан ни один listener, Mosquitto 2.0 и новее слушает только 127.0.0.1 и пишет в журнал предупреждение «Starting in local only mode. Connections will only be possible from clients running on this machine» (запуск в локальном режиме: подключаться смогут только клиенты на этом компьютере). В таком случае ESP32 не может подключиться и получает ошибку rc=-2. С другой стороны, никогда не открывай порт 1883 в интернет на роутере (никакого проброса портов): незашифрованный трафик MQTT можно прочитать по пути, и реле в твоём доме могут оказаться в чужих руках. Если нужен доступ извне, используй VPN или варианты удалённого доступа Home Assistant.

Прежде чем переходить к ESP32, проверь брокер с компьютера: если сразу отделить проблемы брокера от проблем кода, можно сэкономить уйму времени. В одном терминале начни слушать все топики дома командой mosquitto_sub -h 192.168.1.10 -u esp32 -P yourpassword -t "ev/#" -v. Во втором терминале введи mosquitto_pub -h 192.168.1.10 -u esp32 -P yourpassword -t ev/salon/test -m hello (вместо yourpassword — твой пароль). Если в первом терминале появилась строка «ev/salon/test hello», брокер работает и пароль верный. Опция -v выводит перед каждым сообщением его топик, а опция -r отправляет сообщение как сохраняемое (retain).

Если командная строка — не твоё, бесплатная программа MQTT Explorer показывает все топики брокера деревом, с последними значениями и флагами retain. В самом Home Assistant тоже можно открыть Настройки › Устройства и службы › MQTT › Настроить (Settings › Devices & services › MQTT › Configure), ввести ev/# в поле «Прослушать топик» (Listen to a topic) и начать прослушивание, а в разделе «Опубликовать пакет» (Publish a packet) вручную отправлять команды на ESP32.

Список деталей и схема подключения

Для этого проекта понадобятся отладочная плата ESP32, датчик температуры и влажности DHT22, модуль реле, который срабатывает от 3,3 V, макетная плата и перемычки. DHT22 точнее DHT11: он измеряет температуру с точностью ±0,5 °C, влажность примерно ±2–5% и работает от -40 до 80 °C; семейство этих датчиков мы подробно разбираем в статье датчик температуры и влажности DHT11. Если у тебя DHT11, просто замени в коде DHT22 на DHT11. Для первой проверки подключения хватит ESP32 и USB-кабеля; датчик и реле можно добавить позже.

ESP32
ESP32
DHT22
DHT22
Модуль реле
Модуль реле
ПодключениеESP32ESP8266 (NodeMCU)Raspberry Pi Pico W
DHT22 VCC3V33V33V3(OUT)
DHT22 DATAGPIO 4D2 (GPIO 4)GP15
DHT22 GNDGNDGNDGND
Реле VCCVIN (5V)VIN (5V)VBUS (5V)
Реле INGPIO 26D1 (GPIO 5)GP14
Реле GNDGNDGNDGND
Пины ESP32, ESP8266 и Pico работают на 3,3 V и не терпят 5 V, поэтому DHT22 мы питаем от пина 3V3. На трёхпиновых модулях DHT22 подтягивающий резистор обычно уже стоит на плате; если используется голый четырёхпиновый датчик, добавь резистор 4,7–10 kΩ между DATA и VCC. Если реле будет коммутировать нагрузку от сети 230 V, делай всю проводку при вынутой из розетки вилке и попроси помощи у взрослых; подробности — в статье что такое модуль реле. Все пины своей платы можно посмотреть в утилите распиновки.

Установка PubSubClient в Arduino IDE

  1. Если пакет плат ESP32 в Arduino IDE ещё не установлен, сначала установи его; шаги описаны в статье что такое ESP32.
  2. Открой «Инструменты › Управлять библиотеками» (Tools › Manage Libraries), введи в поиске PubSubClient и установи библиотеку от Nick O'Leary. Код в этом руководстве компилировался с версией 2.8.
  3. В том же окне найди DHT sensor library и установи библиотеку Adafruit; когда появится вопрос о зависимостях, выбери Установить все (Install all), чтобы заодно установилась Adafruit Unified Sensor.
  4. Если с установкой библиотек возникли трудности, смотри руководство как добавить библиотеку в Arduino.

Первый тест: подключаем ESP32 к MQTT-брокеру

Прежде чем добавлять датчики, проверим самым простым кодом, что данные Wi-Fi, адрес брокера и пароль верны. Код ниже подключается к брокеру, публикует «merhaba» (по-турецки «привет») в топик «test/esp32/durum» и выводит в монитор порта каждое сообщение, пришедшее в «test/esp32/komut». Замени имя сети Wi-Fi, пароль, IP брокера и пользователя MQTT на свои, загрузи код и открой монитор порта на скорости 115200 бод:

C++ (Arduino IDE)
1#include <WiFi.h>
2#include <PubSubClient.h>
3
4const char* WIFI_SSID = "AgAdi";              // Wi-Fi adın (yalnız 2,4 GHz) / your Wi-Fi name (2.4 GHz only)
5const char* WIFI_PASS = "Sifre123";           // Wi-Fi şifren / your Wi-Fi password
6const char* MQTT_SUNUCU = "192.168.1.10";     // broker'ın IP adresi / broker IP address
7const int   MQTT_PORT = 1883;                 // şifresiz MQTT portu / unencrypted MQTT port
8const char* MQTT_KULLANICI = "esp32";         // broker kullanıcı adı / broker username
9const char* MQTT_SIFRE = "mqttSifre";         // broker şifresi / broker password
10
11WiFiClient wifiIstemci;                       // TCP bağlantısı / TCP connection
12PubSubClient mqtt(wifiIstemci);               // MQTT istemcisi / MQTT client
13
14// Abone olunan konuya mesaj gelince çalışır / runs when a subscribed topic receives a message
15void mesajGeldi(char* topic, byte* payload, unsigned int length) {
16  Serial.print("Mesaj [");
17  Serial.print(topic);
18  Serial.print("] ");
19  for (unsigned int i = 0; i < length; i++) {
20    Serial.print((char)payload[i]);           // payload sonunda \0 yoktur / payload is not null-terminated
21  }
22  Serial.println();
23}
24
25void setup() {
26  Serial.begin(115200);
27  WiFi.mode(WIFI_STA);
28  WiFi.begin(WIFI_SSID, WIFI_PASS);
29  while (WiFi.status() != WL_CONNECTED) {
30    delay(500);
31    Serial.print(".");
32  }
33  Serial.print("\nIP: ");
34  Serial.println(WiFi.localIP());
35
36  mqtt.setServer(MQTT_SUNUCU, MQTT_PORT);     // broker adresi / broker address
37  mqtt.setCallback(mesajGeldi);               // gelen mesaj fonksiyonu / incoming message handler
38}
39
40void loop() {
41  if (!mqtt.connected()) {
42    Serial.print("MQTT baglaniyor... ");
43    if (mqtt.connect("esp32-test", MQTT_KULLANICI, MQTT_SIFRE)) {   // istemci kimliği benzersiz olmalı / client ID must be unique
44      Serial.println("baglandi");
45      mqtt.subscribe("test/esp32/komut");                  // bu konuyu dinle / listen to this topic
46      mqtt.publish("test/esp32/durum", "merhaba");         // ilk mesaj / first message
47    } else {
48      Serial.print("hata, rc=");
49      Serial.println(mqtt.state());                        // hata kodu / error code
50      delay(5000);
51      return;
52    }
53  }
54  mqtt.loop();                                             // mesajları işle, bağlantıyı canlı tut / process messages, keep the connection alive
55}

Когда в мониторе порта появится «baglandi» (подключено), отправь с компьютера из терминала mosquitto_pub -h 192.168.1.10 -u esp32 -P yourpassword -t test/esp32/komut -m hi; через несколько миллисекунд в мониторе порта появится строка «Mesaj [test/esp32/komut] hi». Если подключиться не удалось, код выводит код ошибки через mqtt.state() и через 5 секунд пробует снова. Эти коды точно показывают, где проблема:

rcИмя в PubSubClientЗначение и решение
-4MQTT_CONNECTION_TIMEOUTБрокер не ответил вовремя; слабая сеть или перегруженный брокер
-3MQTT_CONNECTION_LOSTУстановленное соединение оборвалось; обычно потому, что loop() долго не вызывался или пропал Wi-Fi
-2MQTT_CONNECT_FAILEDTCP-соединение так и не открылось: неверный IP или порт, брокер не запущен, Mosquitto в локальном режиме или брандмауэр блокирует 1883
-1MQTT_DISCONNECTEDСоединение закрыто штатно (вызван disconnect) или его вообще не было
2MQTT_CONNECT_BAD_CLIENT_IDClient ID отклонён; использован пустой или недопустимый client ID
4MQTT_CONNECT_BAD_CREDENTIALSНеверное имя пользователя или пароль (некоторые брокеры возвращают этот код)
5MQTT_CONNECT_UNAUTHORIZEDНет авторизации; Mosquitto возвращает этот код и при неверном пароле, и при попытке подключиться без учётных данных

Публикуем данные датчика и слушаем команды для реле на ESP32

Теперь соберём настоящий проект. Код ниже каждые 30 секунд читает DHT22 и публикует температуру и влажность с флагом retain, управляет реле командами ON, OFF и TOGGLE, приходящими в «ev/salon/role/komut», и сообщает новое состояние реле в «ev/salon/role/durum». При подключении он оставляет брокеру «последнюю волю», представляется Home Assistant через MQTT Discovery, а если связь обрывается, повторяет попытку каждые 5 секунд, не блокируя loop(). Благодаря блоку #if defined(ESP8266) в начале тот же код без изменений компилируется и на NodeMCU.

C++ (Arduino IDE)
1#if defined(ESP8266)
2#include <ESP8266WiFi.h>
3#else
4#include <WiFi.h>
5#endif
6#include <PubSubClient.h>
7#include <DHT.h>
8
9// ---- Ayarlar / Settings ----
10const char* WIFI_SSID = "AgAdi";
11const char* WIFI_PASS = "Sifre123";
12const char* MQTT_SUNUCU = "192.168.1.10";        // broker IP (Home Assistant ya da Raspberry Pi) / broker IP (Home Assistant or Raspberry Pi)
13const int   MQTT_PORT = 1883;
14const char* MQTT_KULLANICI = "esp32";
15const char* MQTT_SIFRE = "mqttSifre";
16const char* ISTEMCI_ID = "esp32-salon";          // her cihazda farklı olmalı / must be different on every device
17
18#if defined(ESP8266)
19const int DHT_PIN = 4;                           // NodeMCU D2
20const int ROLE_PIN = 5;                          // NodeMCU D1
21#else
22const int DHT_PIN = 4;                           // ESP32 GPIO 4
23const int ROLE_PIN = 26;                         // ESP32 GPIO 26
24#endif
25const bool ROLE_AKTIF_LOW = true;                // çoğu röle modülü LOW ile çeker / most relay modules switch on LOW
26const unsigned long OLCUM_ARALIGI = 30000;       // 30 saniyede bir ölçüm / one reading every 30 s
27
28// ---- Konular (topic) / Topics ----
29const char* T_SICAKLIK = "ev/salon/sicaklik";
30const char* T_NEM      = "ev/salon/nem";
31const char* T_KOMUT    = "ev/salon/role/komut";  // ON, OFF ya da TOGGLE / ON, OFF or TOGGLE
32const char* T_DURUM    = "ev/salon/role/durum";
33const char* T_BAGLANTI = "ev/salon/baglanti";    // online / offline (son vasiyet / last will)
34const char* T_HA_DURUM = "homeassistant/status"; // HA açılınca "online" yayınlar / HA publishes "online" when it starts
35
36WiFiClient wifiIstemci;
37PubSubClient mqtt(wifiIstemci);
38DHT dht(DHT_PIN, DHT22);
39
40bool roleAcik = false;
41unsigned long sonOlcum = 0;
42unsigned long sonDeneme = 0;
43
44// Röleyi sür ve yeni durumu kalıcı (retain) yayınla / drive the relay and publish the new state as retained
45void roleAyarla(bool acik) {
46  roleAcik = acik;
47  bool seviye = ROLE_AKTIF_LOW ? !acik : acik;
48  digitalWrite(ROLE_PIN, seviye ? HIGH : LOW);
49  mqtt.publish(T_DURUM, acik ? "ON" : "OFF", true);
50  Serial.println(acik ? "Role: ON" : "Role: OFF");
51}
52
53// Tek bir Home Assistant keşif mesajı gönder / send one Home Assistant discovery message
54void kesifYayinla(const char* tur, const char* nesne, const char* ad, const char* ekAlanlar) {
55  char konu[96];
56  snprintf(konu, sizeof(konu), "homeassistant/%s/%s/%s/config", tur, ISTEMCI_ID, nesne);
57  char json[600];
58  snprintf(json, sizeof(json),
59           "{\"name\":\"%s\",\"unique_id\":\"%s_%s\",%s,\"availability_topic\":\"%s\","
60           "\"device\":{\"identifiers\":[\"%s\"],\"name\":\"Salon ESP32\",\"model\":\"ESP32 + DHT22\"}}",
61           ad, ISTEMCI_ID, nesne, ekAlanlar, T_BAGLANTI, ISTEMCI_ID);
62  if (!mqtt.publish(konu, json, true)) {         // false: tampon küçük ya da bağlantı yok / false: buffer too small or not connected
63    Serial.println("Kesif mesaji gonderilemedi");
64  }
65}
66
67// Sıcaklık, nem ve röleyi Home Assistant'a tanıt / announce temperature, humidity and relay to Home Assistant
68void haKesifGonder() {
69  char ek[200];
70  snprintf(ek, sizeof(ek), "\"state_topic\":\"%s\",\"device_class\":\"temperature\",\"unit_of_measurement\":\"°C\",\"state_class\":\"measurement\"", T_SICAKLIK);
71  kesifYayinla("sensor", "sicaklik", "Sıcaklık", ek);
72  snprintf(ek, sizeof(ek), "\"state_topic\":\"%s\",\"device_class\":\"humidity\",\"unit_of_measurement\":\"%%\",\"state_class\":\"measurement\"", T_NEM);
73  kesifYayinla("sensor", "nem", "Nem", ek);
74  snprintf(ek, sizeof(ek), "\"state_topic\":\"%s\",\"command_topic\":\"%s\"", T_DURUM, T_KOMUT);
75  kesifYayinla("switch", "role", "Röle", ek);
76}
77
78// DHT22'yi oku ve değerleri yayınla / read the DHT22 and publish the values
79void olcumGonder() {
80  float t = dht.readTemperature();
81  float h = dht.readHumidity();
82  if (isnan(t) || isnan(h)) {
83    Serial.println("DHT22 okunamadi");
84    return;
85  }
86  char deger[12];
87  snprintf(deger, sizeof(deger), "%.1f", t);     // 23.4 gibi / like 23.4
88  mqtt.publish(T_SICAKLIK, deger, true);
89  snprintf(deger, sizeof(deger), "%.0f", h);     // 45 gibi / like 45
90  mqtt.publish(T_NEM, deger, true);
91  Serial.printf("Sicaklik %.1f C, nem %.0f %%\n", t, h);
92}
93
94// Abone olunan konulara mesaj gelince çalışır / runs when a subscribed topic receives a message
95void mesajGeldi(char* topic, byte* payload, unsigned int length) {
96  // Önce kopyala: yayın yapınca kütüphane aynı tamponu kullanır / copy first: publishing reuses the same buffer
97  String konu = topic;
98  String mesaj;
99  for (unsigned int i = 0; i < length; i++) mesaj += (char)payload[i];
100  mesaj.trim();
101
102  if (konu == T_KOMUT) {
103    if (mesaj == "ON") roleAyarla(true);
104    else if (mesaj == "OFF") roleAyarla(false);
105    else if (mesaj == "TOGGLE") roleAyarla(!roleAcik);
106  } else if (konu == T_HA_DURUM && mesaj == "online") {
107    haKesifGonder();                             // HA yeniden başladı, tanıtımı yenile / HA restarted, announce again
108    mqtt.publish(T_DURUM, roleAcik ? "ON" : "OFF", true);
109  }
110}
111
112bool mqttBaglan() {
113  Serial.print("MQTT baglaniyor... ");
114  // Bağlantı koparsa broker T_BAGLANTI'ya "offline" yazar / if we drop, the broker publishes "offline" to T_BAGLANTI
115  if (!mqtt.connect(ISTEMCI_ID, MQTT_KULLANICI, MQTT_SIFRE, T_BAGLANTI, 1, true, "offline")) {
116    Serial.print("hata, rc=");
117    Serial.println(mqtt.state());
118    return false;
119  }
120  Serial.println("baglandi");
121  mqtt.publish(T_BAGLANTI, "online", true);
122  mqtt.subscribe(T_KOMUT, 1);                    // komutları QoS 1 ile dinle / listen to commands with QoS 1
123  mqtt.subscribe(T_HA_DURUM);
124  haKesifGonder();
125  mqtt.publish(T_DURUM, roleAcik ? "ON" : "OFF", true);
126  return true;
127}
128
129void setup() {
130  Serial.begin(115200);
131  pinMode(ROLE_PIN, OUTPUT);
132  roleAyarla(false);                             // güvenli başlangıç: röle kapalı / safe start: relay off
133  dht.begin();
134
135  WiFi.mode(WIFI_STA);
136  WiFi.begin(WIFI_SSID, WIFI_PASS);
137  Serial.print("Wi-Fi");
138  while (WiFi.status() != WL_CONNECTED) {
139    delay(500);
140    Serial.print(".");
141  }
142  Serial.print(" IP: ");
143  Serial.println(WiFi.localIP());
144
145  mqtt.setServer(MQTT_SUNUCU, MQTT_PORT);
146  mqtt.setCallback(mesajGeldi);
147  mqtt.setBufferSize(1024);                      // keşif JSON'u varsayılan 256 bayta sığmaz / discovery JSON doesn't fit the default 256 bytes
148}
149
150void loop() {
151  if (!mqtt.connected()) {
152    unsigned long simdi = millis();
153    if (sonDeneme == 0 || simdi - sonDeneme >= 5000) {   // 5 sn'de bir dene, loop'u kilitleme / retry every 5 s without blocking
154      sonDeneme = simdi;
155      if (mqttBaglan()) sonOlcum = simdi - OLCUM_ARALIGI;  // bağlanınca hemen ölç / measure right after connecting
156    }
157    return;
158  }
159  mqtt.loop();                                   // gelen komutlar ve keep-alive / incoming commands and keep-alive
160
161  if (millis() - sonOlcum >= OLCUM_ARALIGI) {
162    sonOlcum = millis();
163    olcumGonder();
164  }
165}
СтрокаЧто делает
mqtt.connect(ISTEMCI_ID, ..., T_BAGLANTI, 1, true, "offline") (подключение с «последней волей»)Регистрирует «последнюю волю» при подключении: если ESP32 неожиданно отключится, брокер запишет в ev/salon/baglanti сохраняемое «offline»
mqtt.publish(T_BAGLANTI, "online", true) (статус «в сети»)Сразу после подключения ставит статус «online»; Home Assistant снова показывает устройство как доступное
mqtt.subscribe(T_KOMUT, 1)Слушает команды для реле с QoS 1; для подписок PubSubClient поддерживает только QoS 0 и 1
mqtt.setBufferSize(1024)Увеличивает буфер пакетов со стандартных 256 байт; самый большой пакет обнаружения занимает около 355 байт
mqtt.loop()Читает входящие сообщения и отправляет пакеты keep-alive (PINGREQ); его нужно вызывать на каждом проходе loop
ROLE_AKTIF_LOWБольшинство модулей реле включаются сигналом LOW; если твой включается по HIGH, поставь false
OLCUM_ARALIGIКак часто измерять; DHT22 можно читать не чаще чем раз в 2 секунды, а для температуры в комнате идеально 30–60 секунд

Некоторые детали в коде сделаны намеренно. Функция mesajGeldi() (пришло сообщение) сначала копирует топик и полезную нагрузку в String, потому что PubSubClient использует один буфер и для входящих, и для исходящих сообщений: как только внутри обратного вызова вызывается publish(), данные, на которые указывают указатели топика и нагрузки, перезаписываются. Кроме того, у нагрузки нет завершающего \0, как у строки C, поэтому мы читаем ровно length символов. Для показаний и состояния реле мы используем retain, чтобы после перезапуска Home Assistant сразу видел последнее значение, а не ждал 30 секунд. Время тоже отмеряется через millis(): длинный delay() внутри loop() задержал бы пакеты keep-alive, и брокер разорвал бы соединение. Этот приём подробно разобран в статье millis() и delay() в Arduino.

Никогда не отправляй сообщения в командные топики с флагом retain. Если в «ev/salon/role/komut» останется сохранённое «ON», ESP32 будет получать эту старую команду при каждом переподключении после отключения электричества или обрыва Wi-Fi и сама включать реле посреди ночи. Если сохраняемая команда отправлена по ошибке, очисти её, опубликовав пустое сохраняемое сообщение командой mosquitto_pub -r -n -t ev/salon/role/komut. MQTT-переключатели Home Assistant по умолчанию отправляют команды без retain.

Как это выглядит в Home Assistant: MQTT Discovery

Home Assistant постоянно слушает топики, начинающиеся с префикса homeassistant/. Когда устройство публикует JSON-конфигурацию в топик вида homeassistant/[component]/[node]/[object]/config, Home Assistant автоматически создаёт эту сущность; это и называется MQTT Discovery (автообнаружение). Наш код отправляет три сообщения: два для датчиков (температура и влажность) в homeassistant/sensor/esp32-salon/... и одно для реле в homeassistant/switch/esp32-salon/role/config. Поскольку они сохраняемые, перезапуск Home Assistant им не страшен. Ещё код подписан на homeassistant/status: при запуске Home Assistant пишет туда «online», и ESP32 заново объявляет о себе. Если устройств много, документация Home Assistant рекомендует добавить перед таким повторным объявлением небольшую случайную задержку.

Поле JSONЗначениеНаше значение
nameОтображаемое имя сущности; перед ним добавляется имя устройстваSıcaklık (отображается как «Salon ESP32 Sıcaklık»)
unique_idПостоянный ID сущности; без него имя и зону сущности нельзя изменить в интерфейсеesp32-salon_sicaklik
state_topicТопик, из которого читается значениеev/salon/sicaklik
command_topicТопик, куда отправляются команды переключателяev/salon/role/komut (команда реле)
device_classЗадаёт значок, варианты единиц и тип графикаtemperature, humidity
unit_of_measurementЕдиница измерения°C и %
state_classЗначение measurement включает долгосрочную статистику и графикиmeasurement
availability_topicТопик, из которого читается статус online/offline; полезная нагрузка по умолчанию — online и offlineev/salon/baglanti
deviceОбъединяет сущности в одну карточку устройстваidentifiers, name, model (идентификаторы, имя, модель)

Через несколько секунд после загрузки кода в разделе Настройки › Устройства и службы › MQTT появится устройство «Salon ESP32». В его карточке есть датчики температуры и влажности и переключатель реле; нажми на температуру, чтобы открыть график истории, щёлкни переключателем — и реле щёлкнет в ответ. Эти сущности можно добавить на панель, открыть для голосовых ассистентов или использовать в автоматизациях: например, правило «если в гостиной выше 27 °C, включить реле, к которому подключён вентилятор, а ниже 25 °C — выключить» настраивается в несколько кликов. Если отключить ESP32 от питания, примерно не позже чем через 22 секунды сущности перейдут в состояние «Недоступно» (Unavailable) — значит, «последняя воля» работает. Если обнаружение не нужно, те же сущности можно задать и вручную в configuration.yaml как записи sensor и switch в разделе mqtt:.

Если после смены имени устройства или топиков в Home Assistant остались старые сущности, отправь пустое сохраняемое сообщение в соответствующий топик конфигурации командой mosquitto_pub -r -n -t homeassistant/sensor/esp32-salon/sicaklik/config. Home Assistant удалит сущность, а заодно и устройство, если к нему больше не привязано других сущностей. Если дать каждой ESP32 свой ISTEMCI_ID, значения unique_id тоже автоматически будут разными, так что можно добавить несколько комнат без путаницы.

QoS и retain: что и когда использовать?

QoS (Quality of Service, качество обслуживания) определяет, насколько надёжно сообщение должно дойти до другой стороны. Важная деталь: QoS действует отдельно на двух участках. Между издателем и брокером действует QoS издателя, между брокером и подписчиком — QoS подписки; подписчик получает меньшую из двух гарантий. PubSubClient поддерживает при публикации только QoS 0, а при подписке — QoS 0 и 1. В домашнем Wi-Fi потеря сообщения с QoS 0 случается очень редко, но подписка на команды с QoS 1 — всё равно дешёвая страховка.

QoSГарантияКак работаетКогда использовать
0Не более одного разаСообщение отправляется один раз без подтверждения; если в этот момент связь оборвётся, оно может потерятьсяЧастые показания датчиков
1Хотя бы один разПолучатель подтверждает приём пакетом PUBACK, а если подтверждение не пришло, сообщение отправляется снова; оно может прийти дваждыКоманды реле, тревоги
2Ровно один разЧетырёхэтапное рукопожатие с PUBREC, PUBREL и PUBCOMP; самый медленный уровеньНапример, счётчики, где дубли вредят; PubSubClient не поддерживает

Флаг retain отвечает не за доставку, а за память: брокер хранит только последнее сохраняемое сообщение каждого топика и сразу отправляет его каждому новому подписчику. Это идеально для сообщений о «текущем состоянии» — значений датчиков, состояния реле, статуса в сети, — но опасно для команд и разовых событий. Последняя воля (LWT) дополняет его: если брокер не получает от клиента ни одного пакета в течение 1,5 периода keep-alive, он считает соединение потерянным и публикует сообщение «последней воли». По умолчанию keep-alive в PubSubClient равен 15 секундам, поэтому отключённая ESP32 отображается как «offline» не позже чем примерно через 22 секунды. Это можно изменить строкой вроде mqtt.setKeepAlive(30), но чем больше значение, тем позже будет замечен обрыв.

Альтернатива без кода: ESPHome

ESPHome — бесплатный инструмент, который превращает устройство, описанное в YAML-файле, в готовую к прошивке ESP32 микропрограмму; ни одной строки на C++ писать не нужно. В Home Assistant установи ESPHome Device Builder из раздела Настройки › Приложения (Settings › Apps), открой его интерфейс и создай конфигурацию мастером New device (новое устройство). Первая загрузка делается один раз по USB: Chrome или Edge могут запрограммировать плату напрямую, а если это не сработает, файл, скачанный мастером, можно прошить через страницу ESPHome Web. Все последующие обновления идут по Wi-Fi (OTA). Конфигурация ниже повторяет Arduino-проект выше: каждые 30 секунд читает DHT22 на GPIO 4 и выводит реле с активным LOW на GPIO 26 как переключатель. Данные Wi-Fi и пароли читаются через !secret из отдельного файла secrets.yaml:

Код
1esphome:
2  name: salon-sensor               # küçük harf, rakam ve tire / lowercase, digits and hyphens
3  friendly_name: Salon Sensör
4
5esp32:
6  board: esp32dev
7
8logger:
9
10api:                               # Home Assistant ile doğrudan bağlantı / direct link to Home Assistant
11  encryption:
12    key: !secret api_anahtari
13
14ota:
15  - platform: esphome
16    password: !secret ota_sifresi
17
18wifi:
19  ssid: !secret wifi_ssid
20  password: !secret wifi_sifresi
21  ap:                              # Wi-Fi yoksa kendi ağını açar / opens its own network if Wi-Fi fails
22    ssid: "Salon-Sensor"
23    password: "yedek1234"
24
25captive_portal:
26
27sensor:
28  - platform: dht
29    pin: GPIO4
30    model: DHT22
31    update_interval: 30s
32    temperature:
33      name: "Sıcaklık"
34    humidity:
35      name: "Nem"
36
37switch:
38  - platform: gpio
39    name: "Röle"
40    pin:
41      number: GPIO26
42      inverted: true               # aktif LOW röle / active-LOW relay
43    restore_mode: ALWAYS_OFF       # açılışta kapalı / off at boot
44
45# MQTT ile kullanmak için api: bloğunu sil, bunu aç / to use MQTT, delete api: and uncomment this
46# mqtt:
47#   broker: 192.168.1.10
48#   username: esp32
49#   password: !secret mqtt_sifresi

Есть одно отличие, о котором стоит помнить: по умолчанию ESPHome использует не MQTT, а собственный зашифрованный нативный API. Home Assistant сам находит устройство в сети, и брокер вообще не нужен. MQTT стоит выбирать, только если те же данные должны читать другие системы, например Node-RED, или если Home Assistant не используется. Для этого раскомментируй строки mqtt: в конце файла и удали блок api:; иначе устройство будет само перезагружаться каждый раз, когда к нативному API 15 минут никто не подключался (это настройка reboot_timeout). В режиме MQTT ESPHome сам отправляет сообщения обнаружения для Home Assistant и сообщения online/offline в salon-sensor/status, то есть автоматически делает всё то, что мы вручную делали в коде Arduino.

ВозможностьPubSubClient (код Arduino)ESPHome (YAML)
Написание кодаВсё пишешь на C++Только YAML; прошивка компилируется автоматически
Добавление в Home AssistantJSON обнаружения отправляешь вручнуюОбнаруживается автоматически
ГибкостьБез ограничений; можно использовать любые библиотеки и логикуОграничена готовыми компонентами, но поддерживаются сотни датчиков; C++ можно добавить через лямбды
Обновление по воздухуArduinoOTA в код добавляешь вручнуюВстроено
Нужен брокерДаНет при нативном API
Польза для учёбыУчит протоколу MQTT и программированиюБыстрый результат, но скрывает, что происходит внутри

MQTT на ESP8266 и Raspberry Pi Pico W

Полный код выше без изменений работает на платах ESP8266 NodeMCU и Wemos D1 Mini: если выбрать NodeMCU 1.0 в «Инструменты › Плата» (Tools › Board), блок #if defined(ESP8266) автоматически подключит ESP8266WiFi.h вместо WiFi.h, а заодно выберет пины D2 и D1. На Raspberry Pi Pico W мы используем модуль MicroPython umqtt.simple. В прошивку MicroPython для Pico W он не входит; установи его в Thonny через «Инструменты › Управление пакетами» (Tools › Manage packages), найдя umqtt.simple, или введи в REPL при подключённом Wi-Fi import mip; mip.install("umqtt.simple"). А вот модуль dht, нужный для DHT22, уже есть в прошивке. Код ниже заставляет Pico W на кухне выполнять ту же работу с измерениями и реле. Чтобы код был короче, мы убрали сообщения обнаружения; чтобы увидеть Pico W в Home Assistant, опубликуй тот же JSON обнаружения, что и в коде для ESP32, с топиками ev/mutfak (по-турецки «кухня») или задай сущности вручную в разделе mqtt: файла configuration.yaml. Сохрани файл как main.py, и он будет запускаться при каждом включении платы:

MicroPython
1import network, time, dht
2from machine import Pin
3from umqtt.simple import MQTTClient    # önce mip ile kur / install with mip first
4
5WIFI_SSID = "AgAdi"                    # Wi-Fi adın / your Wi-Fi name
6WIFI_PASS = "Sifre123"                 # Wi-Fi şifren / your Wi-Fi password
7MQTT_SUNUCU = "192.168.1.10"           # broker IP / broker IP
8ISTEMCI_ID = "pico-mutfak"             # benzersiz olmalı / must be unique
9
10T_SICAKLIK = b"ev/mutfak/sicaklik"
11T_NEM = b"ev/mutfak/nem"
12T_KOMUT = b"ev/mutfak/role/komut"
13T_DURUM = b"ev/mutfak/role/durum"
14T_BAGLANTI = b"ev/mutfak/baglanti"
15
16sensor = dht.DHT22(Pin(15))            # DHT22 DATA -> GP15
17role = Pin(14, Pin.OUT, value=1)       # aktif LOW röle kapalı başlar / active-LOW relay starts off
18led = Pin("LED", Pin.OUT)              # Pico W dahili LED / Pico W onboard LED
19
20wlan = network.WLAN(network.STA_IF)
21wlan.active(True)
22wlan.connect(WIFI_SSID, WIFI_PASS)
23while not wlan.isconnected():
24    time.sleep(0.5)
25print("IP:", wlan.ifconfig()[0])
26
27def mesaj_geldi(konu, mesaj):          # ikisi de bytes gelir / both arrive as bytes
28    if konu == T_KOMUT and mesaj in (b"ON", b"OFF"):
29        acik = mesaj == b"ON"
30        role.value(0 if acik else 1)
31        led.value(1 if acik else 0)
32        istemci.publish(T_DURUM, mesaj, retain=True)
33
34istemci = MQTTClient(ISTEMCI_ID, MQTT_SUNUCU, user="esp32", password="mqttSifre", keepalive=60)   # Mosquitto kullanıcısı / Mosquitto user
35istemci.set_callback(mesaj_geldi)
36istemci.set_last_will(T_BAGLANTI, b"offline", retain=True)   # son vasiyet / last will
37
38def baglan():
39    while True:
40        try:
41            if not wlan.isconnected():         # Wi-Fi koptuysa yeniden bağlan / reconnect Wi-Fi if it dropped
42                wlan.connect(WIFI_SSID, WIFI_PASS)
43                time.sleep(5)
44            istemci.connect()
45            istemci.publish(T_BAGLANTI, b"online", retain=True)
46            istemci.publish(T_DURUM, b"OFF" if role.value() else b"ON", retain=True)
47            istemci.subscribe(T_KOMUT)
48            print("MQTT baglandi")
49            return
50        except Exception as e:         # MQTTException(5): yetki yok / not authorized
51            print("MQTT hatasi:", e)
52            time.sleep(5)
53
54baglan()
55son = time.ticks_add(time.ticks_ms(), -30000)   # ilk ölçüm hemen / first reading right away
56while True:
57    try:
58        istemci.check_msg()            # bekleyen komut var mı? / any pending command?
59        if time.ticks_diff(time.ticks_ms(), son) >= 30000:
60            son = time.ticks_ms()
61            try:
62                sensor.measure()
63            except OSError:
64                print("DHT22 okunamadi")
65                istemci.ping()         # veri yoksa da bağlantıyı canlı tut / keep the link alive without data
66            else:
67                istemci.publish(T_SICAKLIK, "{:.1f}".format(sensor.temperature()).encode(), retain=True)
68                istemci.publish(T_NEM, "{:.0f}".format(sensor.humidity()).encode(), retain=True)
69    except OSError:
70        print("Baglanti koptu, yeniden baglaniliyor")
71        time.sleep(5)
72        baglan()
73    time.sleep_ms(100)

Идеи проектов на ESP32 и MQTT

  • Карта температуры по комнатам: Поставь в каждую комнату ESP32 с DHT22 или BME280 и следи за всем домом на одном графике через подписку на ev/+/sicaklik.
  • Умная лампа и розетка: Привяжи реле к автоматизации Home Assistant, которая включает его на закате и выключает в полночь.
  • Датчик двери и окна: С помощью геркона отправляй MQTT-сообщение при открытии двери и получай уведомление на телефон, когда дома никого нет.
  • Свет в коридоре по движению: Пусть PIR-датчик публикует движение, а автоматизация Home Assistant включает свет.
  • Станция полива растений: Публикуй значение датчика влажности почвы и включай насос MQTT-командой, когда почва пересыхает.
  • Учёт энергии: Измеряй ток, который потребляет прибор, датчиком тока ACS712 и строй график его суточного потребления.
  • Датчик на батарейке: Собери узел, который снимает показание, отправляет его по MQTT и снова засыпает; подробности — в статье глубокий сон ESP32.

Частые ошибки ESP32 и MQTT и их решения

Симптом или сообщениеПричинаРешение
Постоянно rc=-2Неверный IP или порт брокера, брокер не запущен или Mosquitto в локальном режимеПроверь IP, добавь в конфигурацию listener 1883, разреши порт 1883 в брандмауэре
rc=5Неверное имя пользователя или пароль либо попытка подключиться без учётных данныхПроверь пароль с компьютера через mosquitto_sub; при необходимости создай пользователя заново
Подключается, через несколько секунд отключается, и так по кругуДва устройства используют один client ID; Mosquitto пишет в журнал «Client esp32-salon already connected, closing old connection» (клиент уже подключён, старое соединение закрывается)Дай каждому устройству уникальный ISTEMCI_ID
Сообщения обнаружения не доходят, publish() возвращает falseБуфер PubSubClient по умолчанию 256 байт, и JSON в него не помещаетсяВызови mqtt.setBufferSize(1024) в setup()
Частые rc=-3Длинный delay() внутри loop() останавливает пакеты keep-alive, или слабый сигнал Wi-FiОтмеряй время через millis() и поставь плату ближе к роутеру
Команды не доходят до ESP32Имя топика отличается регистром или написанием, или subscribe() не повторяется после переподключенияПроверь топик через mosquitto_sub; помести строки subscribe() в функцию подключения
Реле само включается при запускеВ командном топике висит старое сохраняемое сообщениеОчисти его командой mosquitto_pub -r -n -t ev/salon/role/komut
Сущности в Home Assistant показывают «Недоступно»Топик доступности сообщает offline, или online написано по-другомуСледи за ev/salon/baglanti и убедись, что код публикует online после подключения
Мусорные символы вместо значенияПолезная нагрузка не заканчивается на \0, а читалась без учёта lengthКопируй нагрузку в цикле до length
Устройство ESPHome перезагружается каждые 15 минутВ режиме MQTT блок api: всё ещё на месте, но Home Assistant к нему не подключаетсяУдали блок api: или добавь reboot_timeout: 0s
При поиске неисправностей придерживайся порядка: сначала проверь в мониторе порта подключение к Wi-Fi и IP-адрес, потом посмотри, что на самом деле доходит до брокера, командой mosquitto_sub -v -t "#" на компьютере, и только после этого смотри Home Assistant. Если сообщение видно на брокере, но не в Home Assistant, проблема в JSON обнаружения; если оно вообще не доходит до брокера, дело на стороне ESP32. Если всё застряло на этапе Wi-Fi, смотри статью ESP32 не подключается к Wi-Fi, а если плата перезагружается, когда щёлкает реле, — Brownout и Guru Meditation на ESP32.

С брокером, парой топиков и PubSubClient твоя ESP32 превращается в устройство умного дома, которое говорит на одном языке со всем остальным домом. В ту же систему можно добавлять новые датчики, другие комнаты и другие платы, например Pico W, и управлять всем с одной панели Home Assistant. В приложении BlueGrays есть проекты на темы подключения ESP32 к Wi-Fi, измерения температуры и влажности с DHT22, умного освещения с PIR и реле, управления в реальном времени через WebSocket и умного полива растений — пошагово, со схемами и кодом.

Частые вопросы

Для чего используется MQTT?

MQTT — лёгкий протокол обмена сообщениями, в котором устройства отправляют и получают сообщения через брокер по именам топиков. Он расходует мало трафика и энергии, поэтому идеально подходит небольшим устройствам вроде ESP32 для отправки данных датчиков и приёма команд. На MQTT говорят Home Assistant, Node-RED и большинство промышленных IoT-систем.

Какой порт использует MQTT?

Незашифрованный MQTT по умолчанию использует порт 1883, а MQTT с шифрованием TLS — порт 8883. Порт WebSocket для подключений из браузера зависит от брокера; у брокера Mosquitto в Home Assistant это 1884. В домашней сети ESP32 обычно подключается к 1883.

Нужен ли для MQTT интернет?

Нет. Если брокер работает дома на Raspberry Pi или в Home Assistant, трафик между ESP32 и брокером полностью остаётся в локальной сети; датчики и реле продолжают работать, даже если пропадёт интернет. Интернет нужен, только чтобы управлять системой извне или пользоваться облачным брокером.

Почему ESP32 не появляется в Home Assistant?

Чаще всего причина в сообщении обнаружения, которое не помещается в 256-байтовый буфер PubSubClient, в опечатке в топике конфигурации или в ошибке с кавычками в JSON. Послушай homeassistant/# через mosquitto_sub, чтобы проверить, доходит ли сообщение до брокера, и убедись, что в коде есть строка mqtt.setBufferSize(1024). Кроме того, MQTT-интеграция Home Assistant должна быть подключена к тому же брокеру.

Что выбрать: ESPHome или MQTT с Arduino?

Если цель — быстро получить рабочее устройство умного дома, ESPHome требует меньше усилий: пишешь YAML, устройство само появляется в Home Assistant и обновляется по воздуху. Если хочется изучить протокол MQTT, написать свою логику или работать с системами помимо Home Assistant, Arduino с PubSubClient гибче. Оба варианта отлично уживаются в одном доме.

Чем отличаются QoS 0, 1 и 2 в MQTT?

QoS 0 отправляет сообщение один раз, не дожидаясь подтверждения; QoS 1 повторяет отправку, пока не придёт подтверждение, но сообщение может прийти дважды; QoS 2 с помощью четырёхэтапного рукопожатия гарантирует доставку ровно один раз. Для показаний датчиков хватает QoS 0, для команд — QoS 1. PubSubClient умеет публиковать только с QoS 0 и подписываться с QoS 0 или 1.

Все проекты
Мигающий светодиод на ESP32
#103Новичок

Мигающий светодиод на ESP32

Учит базовой работе с выходами GPIO на ESP32. Эффект мигания получается включением и выключением светодиода с цифрового пина. Это ESP32-версия классического проекта мигания на Arduino UNO; базовые команды мигания в среде Arduino те же, но расположение пинов ESP32 и уровни GPIO 3,3 V отличаются от Arduino UNO.

4.7(37)
99
Подключение ESP32 к Wi-Fi в режиме STA (с экраном LCD)
#127Продвинутый

Подключение ESP32 к Wi-Fi в режиме STA (с экраном LCD)

В этом проекте ESP32 надёжно и устойчиво к сбоям подключается к домашней сети Wi-Fi в режиме станции (STA). SSID и пароль хранятся в отдельном файле secrets.h, чтобы не утекли в Git; настроено автоматическое переподключение, а уровень сигнала RSSI и IP-адрес выводятся на I2C LCD-экран. Встроенный светодиод служит индикатором состояния. Это менеджер подключения уровня реальных устройств.

4.4(21)
34
Температура и влажность с DHT22 на ESP32 (с LCD-дисплеем)
#117Средний

Температура и влажность с DHT22 на ESP32 (с LCD-дисплеем)

Этот проект измеряет температуру и влажность воздуха датчиком DHT22 и в реальном времени показывает данные на I2C LCD 16x2. Данные читаются с датчика по однопроводному протоколу через библиотеку Adafruit DHT. Проект служит основой для IoT-метеостанций, умного дома или систем мониторинга среды в теплицах. Связь с DHT22 идёт через пин GPIO 4 у ESP32.

4.8(38)
56
Умное освещение на ESP32 (PIR + LDR + реле + OLED)
#141Продвинутый
Premium

Умное освещение на ESP32 (PIR + LDR + реле + OLED)

Сделай умное освещение для коридора, ванной или кладовки на ESP32! С PIR-датчиком движения, датчиком освещённости LDR и модулем реле собирается классический сценарий «свет включается при движении в темноте»: если вокруг уже светло, движение свет НЕ включает, и днём энергия экономится. Свет горит 30 секунд; если движение продолжается, таймер сбрасывается, и свет горит дольше. OLED SSD1306 крупно показывает состояние (ON / DAYTIME / WAITING) и обратный отсчёт.

4.3(6)
11
Счётчик на WebSocket с ESP32 (2 кнопки + LCD + веб в реальном времени)
#130Продвинутый
Premium

Счётчик на WebSocket с ESP32 (2 кнопки + LCD + веб в реальном времени)

В этом проекте две кнопки, подключённые к ESP32 (GPIO 5 увеличивает, GPIO 4 уменьшает), меняют счётчик. Значение сразу обновляется и на I2C LCD 16x2, и — через поддержку WebSocket в ESPAsyncWebServer — во всех подключённых браузерах без перезагрузки страницы. Вместо опроса по HTTP используется одно постоянное соединение, и ESP32 отправляет новое значение всем через ws.textAll().

4.5(14)
23
mDNS на ESP32: доступ к устройству через bluegrays-esp.local (с экраном LCD)
#131Продвинутый
Premium

mDNS на ESP32: доступ к устройству через bluegrays-esp.local (с экраном LCD)

В этом проекте к ESP32 добавляется служба mDNS (Multicast DNS), и к устройству можно обращаться по понятному имени вроде bluegrays-esp.local вместо IP-адреса. Даже если IP, выданный DHCP, меняется, устройство всегда доступно по одному имени. На устройстве также работает маленький веб-сервер, через который по адресу http://bluegrays-esp.local/ из браузера отправляются команды светодиоду. I2C LCD показывает имя хоста, IP-адрес и состояние светодиода.

4.8(14)
23
Сигнализация движения на ESP32 (PIR + зуммер + светодиод + Telegram)
#145Продвинутый
Premium

Сигнализация движения на ESP32 (PIR + зуммер + светодиод + Telegram)

Умная охранная сигнализация, которая при обнаружении движения PIR-датчиком включает зуммер и светодиод и одновременно сразу отправляет владельцу уведомление в Telegram. В ней есть 30-секундная пауза, ограничивающая частоту повторных тревог; этот механизм не проверяет срабатывания PIR и не устраняет ложные тревоги. Подходит для дома, гаража, склада или мастерской. С ESP32-CAM можно добавить уведомления с фото, а с обычной ESP32 DevKit V1 приходят текстовые.

0.0(0)
9