Задача
Архитектурная подсветка исторических зданий включается из уличного щита — на стене во дворе, иногда на мосту. Проводной сети там нет, Wi-Fi обычно тоже. Заказчику (световая компания, где я работаю техническим директором) нужно было, чтобы каждый щит работал по расписанию восход/закат, включался с телефона и сам сообщал о проблеме — выбитом автомате, мёртвом контакторе, тёмном фасаде — раньше, чем позвонят жители.
Готовые GSM-реле умеют коммутировать, но не умеют остального: нет подтверждения, что свет действительно горит, нет общей картины по парку, нет обновления прошивки в поле. Поэтому я сделал весь стек сам.
Железо: один форм-фактор 87 мм, несколько плат
Все платы шириной 87 мм, чтобы вставать в стандартный DIN-щит. Спроектированы в KiCad, изготовлены в нескольких ревизиях (шелкография текущего ядра — FOURLEDS Online controller v. 2.11.7):
- Core — ESP32-C3-WROOM-02 и LTE-модем SimCom A7672E Cat-1, micro-SIM, антенна SMA, двенадцать преобразователей уровня SN74LVC1T45 для UART модема на 1,8 В, LDO MIC29302, изолированные DC-DC NSD15, чтобы одна плата работала от 12 В и от 48 В, часы DS3231, дальномер VL53L4CX и светодиоды Cloud/4G. 102,8 × 87 мм, два слоя, 75 посадочных мест, 1188 дорожек.
- Coil — семь реле HF32FV-16 на 16 А для контакторов, варисторная защита, DS3231, датчик температуры и влажности HDC1080, изолированный RS-485 на ADM2582E. Узкая плата 32,9 × 86,5 мм.
- Sensor — восемь входов контроля 220 В через оптопары LTV-817S: сервер видит, есть ли напряжение на вводе, после счётчика, на контакторе и на каждой группе освещения.
- DALI — интерфейс DALI с питанием от шины (оптопары EL357N, мост MB6S, изолированный источник URB2403S), пока в работе.
Ранний монолитный All-in-One (ESP32-S3 + SIM800C + измерение энергии BL0937, 229 компонентов) и Simple LED Controller 2025 года показывают, откуда выросла модульная схема.
Прошивка: считать, что сеть упадёт
Прошивка на ESP-IDF 5.4, C++17, сборка PlatformIO. Сотовая связь — PPP поверх CMUX (3GPP 27.010), что оставляет параллельный AT-канал для SMS и опроса уровня сигнала при поднятой сессии данных. Выше — MQTT к брокеру EMQX.
Больше всего полевого времени ушло на лестницу восстановления. Сети операторов за CGNAT молча роняют простаивающие сессии; модем может выглядеть подключённым и быть в «чёрной дыре». Контроллер эскалирует по порядку: переподключение MQTT → повторная активация PDP-контекста → переинициализация DCE/DTE модема → цикл питания через PWRKEY → аппаратный RESET. Каждый шаг измеряется и считается.
Чтобы видеть, что реально происходит в поле, каждая плата ведёт журнал стабильности в памяти RTC_NOINIT — он переживает сбросы — и отправляет его на сервер. Многонедельный разбор этих журналов свёл аварии к двум причинам — «чёрным дырам» CGNAT у оператора и исчерпанию кучи при штормах переподключений — и дал точечные исправления: backoff с учётом шторма, подавление всплесков реконнектов и keepalive по нисходящему каналу.
Между платами внутри одного щита Wi-Fi-сети нет вообще: ядро общается с релейной и сенсорной платами по ESP-NOW с кольцами подтверждений и дедупликации, идемпотентным повтором команд и передачей прошивки зависимым платам (dependency OTA). Релейные платы держат локальное расписание — свет переключается, даже если ядро потеряло сеть.
SMS — транспорт последней надежды: команды и статусы запечатаны AES-GCM, подделанное сообщение фасад не переключит.
Сервер и дашборд
Сервер парка — FastAPI с асинхронным SQLAlchemy и Alembic (58 миграций, 40 тестовых модулей), PostgreSQL 17 с TimescaleDB под телеметрию, EMQX Enterprise как MQTT-брокер. В нём MQTT-консьюмер, сервис команд со сторожевым таймером, планировщик восход/закат (astral), OTA-движок, библиотека артефактов которого публикуется автоматически из сборки PlatformIO, и аналитика питания, реле и соблюдения расписания — сравнение того, что запланировано, с тем, что реально увидела сенсорная плата. Telegram-бот отчитывается и принимает команды.
Дашборд — SvelteKit 2 / Svelte 5: карта парка на MapLibre, страницы устройств (пинг, журнал стабильности, история реле, соблюдение расписания), редактор расписаний, OTA, аудит и служебные виды сырого MQTT/ESP-NOW, на двух языках.
Цифры
Около 109 000 строк в прошивке, сервере и дашборде; девять проектов KiCad с герберами, интерактивными BOM и 3D-выгрузками; примерно 18 объектов в продакшене, включая фасады памятников архитектуры в нескольких городах.
Что сделал бы иначе
Начал бы с журнала стабильности. Я добавил его после первой зимы, и каждое настоящее исправление с тех пор пришло из его данных, а не из стендовых тестов. И поставил бы сенсорную плату в первую же ревизию вместо доверия состоянию реле: знать, что контактор замкнулся, — не то же самое, что знать, что фасад горит.