Насос на даче включался так: ноутбук, SSH на Raspberry, командная строка, mosquitto_pub. Каждый раз, когда семья просила полить грядки, интерфейсом умного дома становился я. Веб-панель на самой плате я тоже делал — мини-веб-сервер на ESP32 из соседнего раздела честно работал, — но ей нужен был свой скетч на каждом узле, и знала она ровно те кнопки, которые я в ней нарисовал.
Панель в телефоне оказалась честнее. Телефон — уже готовый MQTT-клиент со своим экраном, батареей и Wi-Fi: приложение MQTT Dash на Android бесплатно, ставится за минуту, панель собирается виджетами без единой строки кода. Вся логика живёт не в приложении, а в брокере — на платах и в топиках, которые мы уже выстроили в разделе. За вечер у меня получился пульт, который понимает все узлы сразу, потому что говорит не с платами, а с топиками. Минус тоже назову: панель — конструктор из готовых блоков, свой алгоритм на экране в ней не напишешь; для этого возвращаются к веб-морде.
Урок не про установку — там две кнопки. Про то, как виджеты ложатся на топики, и про грабли, на которые я наступал: телефон не в той сети, роутер со сменными адресами и панель, которую однажды тянули наружу из дома.
Виджеты: кнопка, метка, график
Кнопка, она же Switch, публикует payload в command-топик — тот самый конвейер из урока про подписку и callback: панель кладёт единицу в dacha/pump/on, callback платы щёлкает реле. На стороне железа менять нечего: если скетч уже принимает команды, панель подключается к нему без правок прошивки. Саму кнопку проверьте тем же mosquitto_sub: откройте command-топик в консоли и нажмите — единица придёт раньше, чем вы дотянете руку до реле. Единственное моё правило — команды исполнительным механизмам живут в отдельных топиках без масок: виджет нажимается легко, и я не хочу, чтобы случайная отладочная подписка dacha/# услышала чью-то единицу.
Метка, она же Text, подписана на state-топик и рисует последнее значение: dacha/greenhouse/temp, dacha/balcony/hum, dacha/balcony/status. Здесь оплата двух прошлых уроков: метка оживает сразу после запуска приложения, только если брокер держит retained-значение. Без него панель после каждого рестарта стоит с пустыми окошками до ближайшей публикации — выглядит как сбой, хотя это просто отсутствие retain у топика. Статус online/offline из урока про индикатор офлайн ставится той же меткой и с retained от will показывает правду даже после перезапуска приложения.
График, он же Chart, строит историю числового топика. Форму графика диктует период публикации: температуру раз в десять-тридцать секунд график любит, раз в триста миллисекунд — превращает в частокол из шума; арифметику периода мы считали в уроке про публикацию показаний. В график ставлю только то, у чего есть ось времени: температуру, влажность, ток. Статусы и команды — не числа, им метки. Тренировочный полигон для графиков — Serial Plotter: та же пара имя-значение, только в мониторе порта.
Привязка у виджетов всегда одна: топик и payload. Имя топика вписываю целиком, по той же схеме дом-комната-имя, что и у платы. Панель не знает про устройства ничего — она знает про топики. Поэтому новый узел появляется на панели не когда вы его добавили в приложение, а когда он начал публиковать знакомые топики. Метку перед подключением платы я проверяю руками: mosquitto_pub с тестовым значением в state-топик — метка в приложении мигает, значит связь и имя топика верные, и дальше виновата только плата, если что-то не так.
| Виджет | Топик | Что делает | Моя грабля |
|---|---|---|---|
| Switch (кнопка) | dacha/pump/on | публикует payload команды | случайная подписка на маску ловила чужие единицы |
| Text (метка) | dacha/greenhouse/temp | показывает последнее значение | пустела после рестарта приложения — лечится retain |
| Chart (график) | dacha/balcony/hum | рисует историю числа | частокол из шума при слишком частой публикации |
| Метка статуса | dacha/balcony/status | online/offline от will | опечатка в топике — и статус чужой платы |
Настройка приложения умещается в один экран: имя панели — подпись для вас, называйте по месту, — адрес брокера в виде 192.168.1.50:1883, логин и пароль, если вы уже закрыли брокер файлом паролей из урока про установку Mosquitto. Адрес — тот же, что в setServer платы: телефон становится равноправным клиентом, брокеру всё равно, кто на том конце — плата с антенной или человек с экраном. Порт, если вы его не переносили, 1883; логин и пароль у телефона могут быть те же, что у платы, — брокер различает клиентов по clientId, а не по учётке.
Адрес вбивайте руками, а не доверяйте автопоиску: в доме с двумя роутерами и старым NAS автоматика угадывает не то. И сразу закрепите адрес окончательно — в настройках роутера есть DHCP-бронь, которая выдаёт Raspberry один и тот же адрес по MAC-адресу. Об этом ниже, в граблях.
Грабли мобильных сетей
Первая грабля — банальная и самая частая: телефон уехал в мобильный интернет. Панель показывает retained-значения двухчасовой давности и молчит; снаружи всё живо — метки заполнены, кнопки нажимаются, только реле не щёлкает. Коварство в том, что приложение не предупреждает об обрыве: недостижимый брокер оно рисует как старое состояние, и это главная ловушка всей затеи. Диагноз простой: LTE не видит ваш 192.168.1.50, потому что адреса из частной сети 192.168.x.x снаружи не существуют. Проверяю так: перезапускаю приложение и смотрю на метку статуса узла — пустая или старая, значит первым делом включаю Wi-Fi, а не перепрошиваю плату.
Вторая грабля — гостевая сеть. Гости привозят свой роутер, телефон к нему цепляется, интернет есть, а брокера нет: изоляция клиентских устройств в гостевых сетях включена почти везде по умолчанию. Распознаётся за две команды: телефон пингует интернет, но не пингует Raspberry — значит, клиенты изолированы. Лечение — панель живёт в домашней сети, гости — в своей. Третья грабля — адрес, который поехал: роутер после прошивки или моргания питания выдал брокеру новый IP, и плата, и панель стучатся в пустоту. DHCP-бронь снимает вопрос навсегда: после неё адрес в приложении и в setServer — константа.
Бонус для стены: списанный смартфон с треснутым стеклом у меня висит в теплице как настенная панель — Wi-Fi, зарядка на 5 В, сон экрана отключён в настройках, автозапуск приложения после включения включён там же. Экран тусклый, зато показания живут своей жизнью без ноутбука, и это точная копия той панели, что в кармане. Метка статуса узла сверху — из прошлых уроков: если она показывает offline, остальное на экране можно не читать.
void callback(char* topic, byte* payload, unsigned int length) { String msg = ""; for (unsigned int i = 0; i < length; i++) { msg += (char)payload[i]; } if (String(topic) == "dacha/pump/on") { if (msg == "1") { digitalWrite(RELE, HIGH); } if (msg == "0") { digitalWrite(RELE, LOW); } }} // в setup(): client.setCallback(callback);// в reconnect(): client.subscribe("dacha/pump/on");// client.publish("dacha/greenhouse/status", "online", true);// state-топики публикуются из millis-цикла, как обычноТелефон в симуляторе не эмулируется — и, честно скажу, не надо: вся польза панели живёт не в приложении, а в потоке сообщений между платой и брокером. Симулятор ниже показывает именно этот поток: потенциометр играет датчик температуры, тумблер — кнопку в приложении, строка BROKER — завещание. Смотрите в монитор как в лупу, подсунутую между платой и панелью.
Соберите панель на своём брокере в том же порядке, в каком идёт урок: метка статуса с retained, метка температуры, график, и только потом кнопки. Метки дают мгновенную обратную связь, что адрес и топики верные; кнопки — это уже команды, с ними аккуратнее. Кнопку добавляйте по одной и проверяйте каждую вживую: реле щёлкнула один раз — топик верный; щёлкнула дважды — панель отправила команду повторно, и для насоса стоит подумать, безвреден ли дубль. Если узел молчит, панель не чинят — идут в консоль брокера и смотрят, публикуется ли топик вообще.
- Покрутите потенциометр и проследите в мониторе за temp=: раз в 4 секунды в state-топик уходит новое значение — ровно это увидят метка и график панели. Поменяйте TEMP_MS на 8000 и сравните шаг.
- Щёлкните тумблером: строка MQTT IN — то, что пришло бы в callback платы от кнопки панели, а реле щёлкает так же, как от команды из урока про callback.
- Оборвите связь кнопкой D2 и нажмите тумблер: команда уходит в никуда, и в мониторе это видно. На живой панели кнопка нажимается так же бесполезно, когда узел offline.
- Составьте чек-лист панели на бумаге: три вида виджетов и топик на каждый, адрес брокера с DHCP-бронью, правило не выпускать 1883 наружу. Сверьте с таблицей урока.
- Телефон — обычный MQTT-клиент: адрес тот же, что в setServer платы; панель знает топики, а не устройства
- Кнопка виджета публикует в command-топик, метка и график читают state-топики; retained делает метки живыми сразу после запуска приложения
- Период публикации диктует форму графика: температура 10–30 секунд, чаще — шум
- Грабли сети: LTE вместо Wi-Fi, гостевая сеть с изоляцией клиентов, съехавший IP брокера — лечится DHCP-бронью
- Панель не выходит из дома: 1883 наружу не пробрасываем, удалённо — только VPN
- 1. Метка в панели после перезапуска приложения стоит пустой. Первая причина?
- 2. С чем связана кнопка в MQTT Dash?
- 3. Телефон в LTE: метки показывают старые значения, реле не реагирует. Почему?
- 4. Роутер перезагрузился и выдал брокеру новый IP. Что убирает проблему навсегда?
- 5. Нужно смотреть теплицу из отпуска. Безопасный путь?
Частые вопросы по теме урока
- Метка в панели после перезапуска приложения стоит пустой. Первая причина?
- Метка оживает сразу, только если брокер держит retained-значение. Без retain панель ждёт ближайшей публикации, а до этого показывает пустоту. Нет retained-значения в state-топике
- С чем связана кнопка в MQTT Dash?
- Виджет знает только топик и payload: при нажатии он публикует их брокеру. До платы сообщение доходит через callback — как любая команда MQTT. С топиком и payload, который публикуется при нажатии
- Телефон в LTE: метки показывают старые значения, реле не реагирует. Почему?
- Адреса из частной сети не существуют снаружи: LTE не видит ваш 192.168.1.50. Метки показывают старые retained-значения, а команды не доходят. Частная сеть 192.168.x.x из мобильного интернета недостижима
- Роутер перезагрузился и выдал брокеру новый IP. Что убирает проблему навсегда?
- DHCP-бронь в настройках роутера выдаёт Raspberry один и тот же адрес по MAC. Адрес в приложении и в setServer становится константой. DHCP-бронь: закрепить адрес за MAC-адресом брокера
- Нужно смотреть теплицу из отпуска. Безопасный путь?
- Проброшенный 1883 — открытая дверь в дом. VPN делает телефон участником домашней сети, и панель работает так, будто вы дома. VPN до домашней сети