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 и самые частые ошибки по порядку.

Что такое 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 ID | ESP32, Home Assistant, MQTT Explorer |
| Топик (topic) | Адрес сообщения из уровней, разделённых косой чертой | ev/salon/sicaklik |
| Публикация (publish) | Отправка сообщения в топик | Отправка значения «23.4» в ev/salon/sicaklik |
| Подписка (subscribe) | Регистрация на получение сообщений из топика | ESP32 слушает ev/salon/role/komut |
| Полезная нагрузка (payload) | Содержимое сообщения: текст, число или JSON | ON, 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/sicaklik | ESP32 (каждые 30 секунд) | Home Assistant | Да |
| ev/salon/nem | ESP32 (каждые 30 секунд) | Home Assistant | Да |
| ev/salon/role/komut (команда реле) | Home Assistant, телефон, автоматизации | ESP32 | Нет |
| ev/salon/role/durum (состояние реле) | ESP32 (при каждом изменении) | Home Assistant | Да |
| ev/salon/baglanti | ESP32 (online), брокер (offline) | Home Assistant | Да |
| homeassistant/status | Home 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 настройка выглядит так:
- В Home Assistant открой Настройки › Приложения (Settings › Apps; в старых версиях раздел называется «Дополнения», Add-ons), нажми Установить приложение (Install app) и выбери в списке Mosquitto broker.
- Нажми Установить (Install); когда установка закончится, нажми Запустить (Start) и оставь включённым запуск при загрузке.
- Создай для ESP32 отдельного пользователя: в разделе Настройки › Люди › Пользователи (Settings › People › Users) добавь пользователя «esp32» с надёжным паролем. Если этой вкладки не видно, включи в профиле расширенный режим (Advanced mode). Имена «homeassistant» и «addons» зарезервированы для брокера, их не используй.
- На странице Настройки › Устройства и службы (Settings › Devices & services) интеграция MQTT обычно появляется как «Обнаружено» (Discovered); нажми Настроить (Configure) и подтверди. Если её нет, нажми Добавить интеграцию (Add integration), найди MQTT и выбери вариант, который настраивает Mosquitto автоматически.
- В коде для 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; пароли хранятся в нём в виде хешей |
Прежде чем переходить к 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).
Список деталей и схема подключения
Для этого проекта понадобятся отладочная плата ESP32, датчик температуры и влажности DHT22, модуль реле, который срабатывает от 3,3 V, макетная плата и перемычки. DHT22 точнее DHT11: он измеряет температуру с точностью ±0,5 °C, влажность примерно ±2–5% и работает от -40 до 80 °C; семейство этих датчиков мы подробно разбираем в статье датчик температуры и влажности DHT11. Если у тебя DHT11, просто замени в коде DHT22 на DHT11. Для первой проверки подключения хватит ESP32 и USB-кабеля; датчик и реле можно добавить позже.



| Подключение | ESP32 | ESP8266 (NodeMCU) | Raspberry Pi Pico W |
|---|---|---|---|
| DHT22 VCC | 3V3 | 3V3 | 3V3(OUT) |
| DHT22 DATA | GPIO 4 | D2 (GPIO 4) | GP15 |
| DHT22 GND | GND | GND | GND |
| Реле VCC | VIN (5V) | VIN (5V) | VBUS (5V) |
| Реле IN | GPIO 26 | D1 (GPIO 5) | GP14 |
| Реле GND | GND | GND | GND |
Установка PubSubClient в Arduino IDE
- Если пакет плат ESP32 в Arduino IDE ещё не установлен, сначала установи его; шаги описаны в статье что такое ESP32.
- Открой «Инструменты › Управлять библиотеками» (Tools › Manage Libraries), введи в поиске PubSubClient и установи библиотеку от Nick O'Leary. Код в этом руководстве компилировался с версией 2.8.
- В том же окне найди DHT sensor library и установи библиотеку Adafruit; когда появится вопрос о зависимостях, выбери Установить все (Install all), чтобы заодно установилась Adafruit Unified Sensor.
- Если с установкой библиотек возникли трудности, смотри руководство как добавить библиотеку в Arduino.
Первый тест: подключаем ESP32 к MQTT-брокеру
Прежде чем добавлять датчики, проверим самым простым кодом, что данные Wi-Fi, адрес брокера и пароль верны. Код ниже подключается к брокеру, публикует «merhaba» (по-турецки «привет») в топик «test/esp32/durum» и выводит в монитор порта каждое сообщение, пришедшее в «test/esp32/komut». Замени имя сети Wi-Fi, пароль, IP брокера и пользователя MQTT на свои, загрузи код и открой монитор порта на скорости 115200 бод:
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 | Значение и решение |
|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT | Брокер не ответил вовремя; слабая сеть или перегруженный брокер |
| -3 | MQTT_CONNECTION_LOST | Установленное соединение оборвалось; обычно потому, что loop() долго не вызывался или пропал Wi-Fi |
| -2 | MQTT_CONNECT_FAILED | TCP-соединение так и не открылось: неверный IP или порт, брокер не запущен, Mosquitto в локальном режиме или брандмауэр блокирует 1883 |
| -1 | MQTT_DISCONNECTED | Соединение закрыто штатно (вызван disconnect) или его вообще не было |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID | Client ID отклонён; использован пустой или недопустимый client ID |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS | Неверное имя пользователя или пароль (некоторые брокеры возвращают этот код) |
| 5 | MQTT_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.
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.
Как это выглядит в 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 и offline | ev/salon/baglanti |
| device | Объединяет сущности в одну карточку устройства | identifiers, name, model (идентификаторы, имя, модель) |
Через несколько секунд после загрузки кода в разделе Настройки › Устройства и службы › MQTT появится устройство «Salon ESP32». В его карточке есть датчики температуры и влажности и переключатель реле; нажми на температуру, чтобы открыть график истории, щёлкни переключателем — и реле щёлкнет в ответ. Эти сущности можно добавить на панель, открыть для голосовых ассистентов или использовать в автоматизациях: например, правило «если в гостиной выше 27 °C, включить реле, к которому подключён вентилятор, а ниже 25 °C — выключить» настраивается в несколько кликов. Если отключить ESP32 от питания, примерно не позже чем через 22 секунды сущности перейдут в состояние «Недоступно» (Unavailable) — значит, «последняя воля» работает. Если обнаружение не нужно, те же сущности можно задать и вручную в configuration.yaml как записи sensor и switch в разделе mqtt:.
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 Assistant | JSON обнаружения отправляешь вручную | Обнаруживается автоматически |
| Гибкость | Без ограничений; можно использовать любые библиотеки и логику | Ограничена готовыми компонентами, но поддерживаются сотни датчиков; 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, и он будет запускаться при каждом включении платы:
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 |
С брокером, парой топиков и 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
Учит базовой работе с выходами GPIO на ESP32. Эффект мигания получается включением и выключением светодиода с цифрового пина. Это ESP32-версия классического проекта мигания на Arduino UNO; базовые команды мигания в среде Arduino те же, но расположение пинов ESP32 и уровни GPIO 3,3 V отличаются от Arduino UNO.

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

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

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

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

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

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

