Набор базовых модулей для упрощения и ускорения разработки IoT устройств на основе процессоров ESP82XX
Фреймворк позволяет быстро разрабатывать кастомные прошивки IoT устройств в MQTT инфраструктуре и реализует все необходимые для этого базовые функции:
- Менеджер EEPROM с автоматической отложенной записью изменений
- Поддержание постоянного соединения к WiFi сети и MQTT брокеру с восстановлением при сбоях
- Возможность поиска и перебора MQTT брокеров в локальной сети (при наличии mDNS)
- Интеграция в MQTT инфраструктуру: публикация информации о состоянии и heartbeat устройства, обработка основных команд настройки и управления через MQTT
- Контроль активности сервера Home Assistant и поддержка "управляемого" режима — позволяет изменять логику работы устройства при недоступности HA
- Синхронизация времени через NTP при заданной TIMEZONE (требует доступа в интернет либо локального NTP сервера)
- Поддержка OTA (On The Air update, прошивка устройства по WiFi)
- Модуль обработки кнопок: защита от дребезга, различные режимы работы и типы событий: нажатие/отпускание, короткие/длинные/множественные нажатия кнопок и комбинаций кнопок
- Модуль управления реле: ограничение частоты переключений, разнесение по времени пусковых токов
- Диммер: одноканальный, двухканальный и WC/WW режим работы
- Готовые модули поддержки сенсоров и устройств (DHT, HTU21D, BME68x, SCD4x, BMP280, BH1750 и др.) — требуется установка сторонних библиотек
В репозитории можно найти пример проекта для Arduino IDE (файл AELib.ino), реализующий прошивку типичного смарт-реле. Такое реле, например, можно использовать для управления тремя группами освещения в комнате с помощью двухклавишного выключателя.
В прошивке предполагается что ESP управляет тремя реле, подключенными к выходам GPIO14, 12 и 13 (RELAY1…RELAY3) и имеет 2 нормально разомкнутые кнопки без фиксации, подключенные к GPIO 5 и 4, и служебную кнопку на GPIO 16.
Нажатия кнопок управляют первым и вторым реле, одновременное нажатие двух кнопок — третьим реле.
При наличии активного подключения к Home Assistant события кнопок вместо этого публикуются в MQTT через btnPublishKeypressEvent.
Встроенный светодиод отображает состояние подключения к WiFi/MQTT и нажатия кнопок.
Кнопка на GPIO 16 не поддерживает прерывания — модуль Buttons опрашивает её в цикле.
- MQTT интерфейс электросчётчика Eastron SDM630
- Замена модуля Broadlink в термостатах Beok на MQTT контроллер на базе ESP8266
Файл включается во все модули библиотеки, позволяя напрямую задавать настройки модулей с помощью #define.
При создании нового проекта используйте файл Config.h из репозитория в качестве шаблона.
Необходимо определить как минимум две основные константы: WIFI_SSID и WIFI_Password (либо разместить учётные данные в подключаемом Config.AE.h). Все остальные настройки модулей фреймворка описаны в Config.h и не являются обязательными.
Настройка анонсирования MQTT брокера через mDNS описана здесь.
Помимо функций поддержки модульной архитектуры (до 16 loop-обработчиков) в библиотеке реализовано сохранение зарегистрированных блоков памяти в EEPROM (до 8 блоков). При этом необходимо учитывать, что объём EEPROM для ESP8266 составляет всего 4096 байт и часть памяти уже зарезервирована:
- Модуль Comms, идентификатор блока
**'C'**: настройки WiFi и MQTT (структураCommsConfigв Comms.cpp). - Модуль LightMeter при инициализации с параметром
(true), идентификатор**'L'**: 8 байт, пороговые значения освещённости для восхода и заката. - Модуль Dimmer, идентификатор
**'D'**: параметры диммера (структураDimmerConfigв Dimmer.cpp).
Основные функции:
- aeInit(): инициализация библиотеки. Должна вызываться в функции
setup()одной из первых. - aeRegisterLoop(LOOP loop): регистрация loop-функций модулей. Все loop-функции исполняются при вызове
aeLoop()в порядке регистрации. - aeLoop(): исполнить loop-функции модулей в порядке регистрации. Должен вызываться в основном
loop(). - *storageRegisterBlock(char id, void data, unsigned short size)**: регистрация блока памяти для сохранения в EEPROM.
- storageSave(): сохранение изменившихся блоков памяти в EEPROM немедленно. По умолчанию EEPROM переписывается не чаще одного раза в час.
- storageReset(): очистка EEPROM и перезагрузка контроллера.
Модуль также определяет макросы aePrint / aePrintf / aePrintln через соответствующие вызовы Serial. Если UART интерфейс устройства занят — переопределите эти макросы на пустые в Config.h.
Основной модуль фреймворка. Установление и поддержание соединения WiFi, регистрация на MQTT брокере, heartbeat и взаимодействие с Home Assistant, обновление прошивки по воздуху и т.д. Требует установки библиотеки PubSubClient by Nick O'Leary.
Модуль автоматически публикует следующие топики в поддереве устройства (см. MQTT_Root):
- DeviceInfo: информация об устройстве: MAC и IP адрес, тип контроллера, объём памяти, версия прошивки и т.д. (retained)
- Online:
"1"/"0". Значение"1"перепосылается каждые 10 минут (heartbeat),"0"выставляется MQTT брокером при пропадении устройства из сети (т.н. Last Will). (retained) - Activity:
"1"/"0". Выставляется в"1"при обнаружении активности — нажатие на кнопку либо при вызове triggerActivity(). Сбрасывается автоматически через 10 секунд. (не retained)
Модуль получает и исполняет следующие команды, полученные через MQTT:
- SetName, payload = "hostname": переименование устройства. Исполняется только если не определена константа WIFI_HostName.
- SetRoot, payload = "MQTT root": изменение корневого топика устройства. Исполняется только если не определена константа MQTT_Root.
- EnableOTA: включение режима перепрошивки по воздуху. Пароль OTA берётся из WIFI_Password. Устройство будет перезагружено, если в течение 15 минут не получит запрос на перепрошивку.
- Reset: перезагрузка устройства.
- FactoryReset: сброс значений в EEPROM и перезагрузка.
Если в Config.h определена временная зона (TIMEZONE) — модуль автоматически синхронизирует локальное время c локальным NTP сервером (если он анонсируется в сети через mDNS) либо с time.google.com / time.nist.gov. Удобные функции: commsTimeIsValid(), commsGetTime().
Пример получения локального времени в коде программы:
time_t currentTime = time(nullptr);
if (currentTime > 10000) {
tm* lt = localtime(¤tTime);
aePrintf("%02d:%02d", lt->tm_hour, lt->tm_min);
}Инициализация: commsInit() или commsInit(bool isTimeCritical) — при значении true отключается энергосбережение WiFi для более отзывчивого соединения.
Модуль Buttons поддерживает два режима обработки кнопок.
В режиме по умолчанию события ShortPressed, LongPressed и VeryLongPressed возникают только после отпускания кнопки (длинное нажатие — от 800 мс, очень длинное — от 10 с).
В "простом" режиме событие LongPressed возвращается при нажатии и удержании кнопки, а VeryLongPressed становится недоступно. Для компиляции в "простом" режиме в Config.h необходимо прописать #define BUTTONS_EASY_MODE.
Регистрация и настройка: btnInit(), btnRegister(pin, inverted, pullUp), опционально btnDefaultFunction(pin, bdf) — назначает действие на 10-е быстрое нажатие: BDF_EnableOTA, BDF_SwitchWIFI, BDF_FactoryReset, BDF_Reset.
Функции, возвращающие состояние и события кнопки (принимают GPIO пин; те же функции с двумя пинами — для комбинаций):
- btnState(): текущее состояние кнопки — нажата или отжата
- btnPressed(), btnReleased(): обнаружено нажатие либо отпускание кнопки. Сбрасывается при считывании.
- btnShortPressed(): обнаружено короткое нажатие. Сбрасывается при считывании.
- btnLongPressed(): обнаружено длинное нажатие. Сбрасывается при считывании.
- btnVeryLongPressed(): обнаружено очень длинное (более 10 с) нажатие. Сбрасывается при считывании. Недоступно при BUTTONS_EASY_MODE.
btnPublishKeypressEvent(bool combinations) в основном цикле обрабатывает нажатия кнопок и публикует их в MQTT. При combinations == true обрабатываются также пары одновременно нажатых кнопок. Номер кнопки в топике соответствует порядку регистрации, начиная с 1:
- Btn#Pressed / Btn##Pressed: payload — номер нажатия в серии (1, 2, …)
- Btn#PressedX / Btn##PressedX: одно сообщение на серию, payload — общее число нажатий
- Btn#LongPressed / Btn##LongPressed: длинное нажатие, payload всегда 1
- Btn#VeryLongPressed / Btn##VeryLongPressed: очень длинное нажатие. Доступно только без BUTTONS_EASY_MODE.
Регистрация: relayRegister(pin, inverted) или relayRegister(pin, inverted, initialState) — начальное состояние при старте. Инициализация: relayInit() включает MQTT поддержку, relayInit(false) — только локальное управление.
Модуль публикует состояние реле в поддереве устройства (номер реле — порядок регистрации):
- Relay#/State: "1" или "0", текущее состояние реле (retained)
Топики, обрабатываемые модулем:
- Relay#/SetState, payload "1" или "0": задать текущее состояние реле
- Relay#/Switch: переключить реле в противоположное состояние
Реализация нескольких светодиодных эффектов на встроенном или подключённом GPIO светодиоде. В конфиге можно определить константу LED_Pin (по умолчанию — пин 2) и LED_Inverted для инверсии сигнала.
Поддерживаемые режимы (LedMode):
- On, Off, Standby
- Blink, BlinkFast, BlinkSlow, BlinkTwice, BlinkInverted, BlinkSlowInverted
- Glowing — только при сборке под ESP8266
Пины управления: DIMMER_CH1, DIMMER_CH2 (значение -1 — один канал). Частота ШИМ: DIMMER_PWM_FREQ (по умолчанию 120 Гц). Режим работы фиксируется макросом DIMMER_FIX_MODE (0, 1 или 2); без него режим задаётся через MQTT и сохраняется в EEPROM.
Инициализация: dimmerInit(). Программный API: dimmerSwitch(), dimmerStartGlowing(), dimmerStopGlowing().
Публикуемые и принимаемые MQTT топики:
- Dimmer/Mode, Dimmer/SetMode: режим работы (0 — один канал, 1 — два канала, 2 — WC/WW)
- Dimmer/State, Dimmer/SetState, Dimmer/Switch: включение/выключение первого канала
- Dimmer/State2, Dimmer/SetState2, Dimmer/Switch2: второй канал (режим 1)
- Dimmer/Brightness, Dimmer/SetBrightness: яркость первого канала (0–255)
- Dimmer/Brightness2, Dimmer/SetBrightness2: яркость второго канала
- Dimmer/Temperature, Dimmer/SetTemperature: цветовая температура (0–255, режим WC/WW)
- Dimmer/Mireds, Dimmer/SetMireds, Dimmer/MiredsRange, Dimmer/SetMiredsRange: температура в майредах
- Dimmer/WorkRange, Dimmer/SetWorkRange: рабочий диапазон ШИМ
- Dimmer/Transition, Dimmer/SetTransition: время перехода (мс)
- Dimmer/SaveDefaults: сохранить текущие значения как defaults в EEPROM
Модуль отслеживает цифровые входы с антидребезгом и публикует их состояние в MQTT.
Регистрация: bnsRegister(pin, inverted, pullUp, reportEverySeconds) — при reportEverySeconds > 0 активное состояние периодически дублируется в MQTT. Инициализация: bnsInit().
Публикуемый топик: Sensors/BinarySensor# — payload "1" или "0" (retained), # — порядок регистрации.
Используется библиотека **BMP280_DEV** by MartinL.
В Config.h необходимо определить пины I2C шины (константы I2C_SDA и I2C_SCL).
Публикуемые MQTT топики:
- Sensors/Temperature: температура (retained)
- Sensors/Pressure: давление в мм рт. ст. (retained)
- Sensors/BarometerValid: показания сенсора актуальны (retained)
Используется библиотека **BH1750** by Christopher Laws.
Публикуемые MQTT топики:
- Sensors/LightLevel: текущий уровень освещённости (retained)
- Sensors/LightLevelValid: показания сенсора актуальны (retained)
В случае если функция инициализации модуля вызвана с параметром (true) — модуль отслеживает наступление восхода и заката солнца, аппроксимируя изменение уровня освещённости и фиксируя время перехода пороговых значений. Для нормальной работы трекера необходимо включить синхронизацию времени (#define TIMEZONE TZ_Europe_Moscow).
Дополнительные публикуемые MQTT топики:
- Sensors/LightLevelFiltered: аппроксимация текущего уровня освещённости (retained, lux)
- Sensors/SunriseLevel, Sensors/SunsetLevel: пороговые значения освещённости (retained, lux)
- Sensors/Sunrise, Sensors/Sunset: время последнего восхода и заката (HH:MM, retained)
- Sensors/DayPhase: текущее время суток ("Day", "Night" или "Unknown", retained)
Принимает следующие команды через MQTT:
- Sensors/SetSunriseLevel: задание порогового значения освещённости для восхода (lux)
- Sensors/SetSunsetLevel: задание порогового значения освещённости для заката (lux)
Модуль поддерживает несколько аппаратных вариантов — нужно определить ровно одну константу в Config.h:
TAH_DHT, TAH_HTU21D, TAH_BME68x или TAH_SCD4x.
Публикуемые MQTT топики (общие для всех вариантов):
- Sensors/Temperature: температура (retained)
- Sensors/Humidity: влажность воздуха (retained)
- Sensors/AbsHumidity: абсолютная влажность, г/м³ (retained)
- Sensors/HeatIndex: "ощущаемая температура" (retained)
- Sensors/TAHValid: показания сенсора актуальны (retained)
Дополнительные топики: Sensors/Pressure и Sensors/IAQ для TAH_BME68x; Sensors/CO2 для TAH_SCD4x.
TAH_DHT: пин задаётся константой DHT_Pin, используется библиотека **DHTesp** by beegee-tokyo.
TAH_HTU21D: подключение через I2C, перед инициализацией необходим вызов Wire.begin(). Используется SparkFun HTU21D library. MQTT интерфейс полностью совпадает с TAH_DHT.
Детекторы движения подключаются к цифровым входам ESP. Если при регистрации датчика движения указать непустое имя, то оно будет использоваться в MQTT топиках. Кроме того можно определить "комбинированный" детектор движения через pirSetCompositeName(name), реагирующий на срабатывание любого из зарегистрированных датчиков.
Регистрация: pirRegister(pin, mqttName, inverted, pullUp, timeoutSec) — пустая строка mqttName скрывает датчик из MQTT. Инициализация: pirInit().
Публикуемые MQTT топики (где <Name> — имя, заданное при регистрации):
- Name/Active, "1" или "0": обнаружено движение (retained)
- Name/Enabled, "1" или "0": датчик включён и активен (retained)
- Name/Timeout: интервал в секундах, в течение которого датчик остаётся активен после обнаружения движения (retained)
Топики, обрабатываемые модулем:
- Name/Enable: включить датчик движения
- Name/Disable: выключить датчик движения
- Name/SetTimeout, payload = секунды: установить таймаут