Не блокировать loop() бесконечными попытками подключения к MQTT

mqttClient.connect() блокирует на время TCP-таймаута, если брокер
недоступен. Без паузы между попытками это происходило на каждой итерации
loop() — веб-интерфейс и опрос датчиков/реле (не зависят от MQTT и должны
работать даже без платформы) подвисали. Теперь переподключение пробуется не
чаще раза в 5 секунд.

В README добавлен раздел "Автономная работа без платформы" — что именно
работает локально без docker compose, а что требует брокера.
This commit is contained in:
2026-08-18 13:09:15 +05:00
parent 138caef3ee
commit 988c574183
2 changed files with 33 additions and 1 deletions
+17
View File
@@ -137,6 +137,23 @@ DHT22 (готовый модуль, 3 провода):
авторизации — только для локальной сети, как и mosquitto платформы; для авторизации — только для локальной сети, как и mosquitto платформы; для
выхода за пределы LAN сначала нужно добавить хотя бы Basic Auth. выхода за пределы LAN сначала нужно добавить хотя бы Basic Auth.
## Автономная работа без платформы
Плате не нужен запущенный `docker compose` (`home_automatization`), чтобы
показывать датчики и включать/выключать свет/насос/вентилятор — для этого
достаточно Wi-Fi и веб-интерфейса выше (`http://<ip-платы>/`). MQTT-брокер
нужен только для синхронизации с платформой (телеметрия в ClickHouse,
команды из Laravel/rule-engine, device shadow в Redis) — если он недоступен,
эта синхронизация просто не происходит, но локальное управление продолжает
работать.
Важная деталь реализации: `mqttEnsureConnected()` пробует переподключиться к
брокеру не чаще раза в 5 секунд (`RECONNECT_INTERVAL_MS` в `mqtt_link.cpp`)
— без этой паузы блокирующий `mqttClient.connect()` при недоступном брокере
съедал бы почти весь `loop()` на каждой итерации, и веб-интерфейс с опросом
датчиков подвисали бы. Serial-лог в этом случае пишет `failed, rc=...` раз в
5 секунд — это ожидаемо, если платформа сейчас не поднята, а не поломка.
## Настройка и прошивка через Arduino IDE ## Настройка и прошивка через Arduino IDE
1. **Поддержка ESP32.** Arduino IDE → Settings → «Additional boards manager 1. **Поддержка ESP32.** Arduino IDE → Settings → «Additional boards manager
+16 -1
View File
@@ -63,11 +63,25 @@ void mqttBegin() {
mqttClient.setCallback(onMessage); mqttClient.setCallback(onMessage);
} }
// mqttClient.connect() блокирует на время TCP-таймаута, если брокер
// недоступен. Без паузы между попытками это съедало бы почти весь loop() —
// а значит и веб-интерфейс, и опрос датчиков/реле, которые от MQTT не
// зависят и обязаны работать даже без связи с платформой (см. README,
// "Автономная работа без платформы").
static unsigned long lastConnectAttempt = 0;
static const unsigned long RECONNECT_INTERVAL_MS = 5000;
void mqttEnsureConnected() { void mqttEnsureConnected() {
if (mqttClient.connected()) { if (mqttClient.connected()) {
return; return;
} }
unsigned long now = millis();
if (now - lastConnectAttempt < RECONNECT_INTERVAL_MS && lastConnectAttempt != 0) {
return;
}
lastConnectAttempt = now;
Serial.print("[mqtt] connecting to "); Serial.print("[mqtt] connecting to ");
Serial.print(MQTT_BROKER_HOST); Serial.print(MQTT_BROKER_HOST);
if (mqttClient.connect(MQTT_CLIENT_ID)) { if (mqttClient.connect(MQTT_CLIENT_ID)) {
@@ -75,7 +89,8 @@ void mqttEnsureConnected() {
mqttClient.subscribe("devices/+/commands", 1); mqttClient.subscribe("devices/+/commands", 1);
} else { } else {
Serial.print(" failed, rc="); Serial.print(" failed, rc=");
Serial.println(mqttClient.state()); Serial.print(mqttClient.state());
Serial.println(" (это ок, если платформа сейчас не поднята — веб-интерфейс и реле работают локально)");
} }
} }