Перейти к содержимому

Термостат на ESP32 с удаленным управлением. Часть 4. MQTT-топики

Добрый день, уважаемый читатель! Продолжаю серию статей про готовое и вполне законченное устройство на ESP32 и Espressif IoT Development Framework. В прошлых статьях серии я рассказывал про:

Если вы не читали их, то рекомендую ознакомиться. В данной статье я приведу подробное (на мой взгляд, конечно) описание генерируемых устройством “стандартных” топиков.

Итак, если вы прошили микроконтроллер предлагаемой прошивкой, то получили на своем MQTT-брокере целый букет топиков, по большей части в JSON-формате. Теперь давайте разберемся, с чем это едят и для чего всё это нужно.

Пример структуры данных, публикуемых прошивкой на MQTT брокере


 


Почему JSON?

Для начала объясню, почему был выбран формат JSON.

Прошивка, в принципе, позволяет публиковать данные с сенсоров “напрямую” PLAIN, без оборачивания в JSON-формат (включить это можно с помощью макроса CONFIG_SENSOR_AS_PLAIN, установив его значение в 1). Но, поскольку прошивка публикует для каждого сенсора сразу целый набор данных (значение, время изменения, экстремумы за разные периоды времени и т.д.), то это зачастую приводит к тому, что MQTT-клиент, да и сам брокер просто “захлебываются”, не справляясь с большим потоком данных. Кроме того, это ведёт к большему расходу кучи, так как приходится генерировать для всего этого отдельные топики. В итоге в своих устройствах я практически полностью отказался от публикации данных в “открытом” виде, исключением являются только топики обмена данными между устройствами.


Локальный и публичный брокеры

Как я уже писал в прошлой статье, прошивка поддерживает два брокера: основной и резервный. При этом каждый из них может быть локальным и публичным (облачным). Локальный брокер расположен “внутри” вашей локальной сети, а публичный – где-то в глобальной сети интернет. Топики для них могут различаться: например для локального брокера нет необходимости указывать локацию, а добавлять её к топику динамически в настройках моста “локальный <-> публичный”. Подробнее об этом можно почитать в самом конце статьи “MQTT брокер на роутере Keenetic“. Составные части топиков для локального брокера помечены как CONFIG_MQTTx_LOC_, для публичных брокеров это будут, соответственно, CONFIG_MQTTx_PUB_Если вы не используете локальный брокер, вы можете не определять макросы CONFIG_MQTTx_LOC_.

Схема генерации топиков

Большинство MQTT топиков программа формирует автоматически, из разных составных частей, определенных в файле конфигурации. Давайте рассмотрим эти части:

PREFIX – общий префикс для топиков, определяется брокером. Большинство брокеров, работающих на mosquitto, не требует никаких префиксов в начале каждого топика. Но некоторые публичные брокеры, например clusterfly.ru или mqtt.by требуют указания имени пользователя в каждом топике, например: “/user_USERNAME/…”. Некоторые MQTT клиенты для Android требуют начинать топики с “/”. Вот для всех этих целей и предназначен префикс. Настраивается он с помощью параметров CONFIG_MQTT1_PUB_PREFIX и CONFIG_MQTT1_LOC_PREFIX (или CONFIG_MQTT2_PUB_PREFIX и CONFIG_MQTT2_LOC_PREFIX для резервного брокера).

LOCATION – расположение устройства, или локация. Если PREFIX не задан, то топики обычно начинаются именно с локации. Для локальных брокеров можно не указывать, но только если у вас одно расположение или если вы настроили автоматическое добавление локации в параметрах моста с локального брокера на публичный. Для указания LOCATION используйте параметры CONFIG_MQTT1_PUB_LOCATION и CONFIG_MQTT1_LOC_LOCATION (или CONFIG_MQTT2_PUB_LOCATION и CONFIG_MQTT2_LOC_LOCATION для резервного брокера).

Применение <LOCATION> дает ещё один не очевидный на первый взгляд плюс – позволяет разделить права на топики между разными пользователями на уровне брокера. Но в этой статье мы не будем пока касаться этой темы

DEVICE – название устройства. Например это может быть конкретное место установки или функциональность устройства. По аналогии название настраивается с помощью макросов CONFIG_MQTT1_PUB_DEVICE и CONFIG_MQTT1_LOC_DEVICE.

Из этих составных частей формируется топики по общему правилу:

<PREFIX><LOCATION>/<DEVICE>/...

Обратите внимание – между <PREFIX> и <LOCATION> нет символа-разделителя /, а между <LOCATION> и <DEVICE> он есть. Поэтому в PREFIX, если он необходим, нужно указать его, а в остальных частях указывать разделитель уже не нужно.

На примере ниже первый уровень иерархии – это локация, второй уровень – устройства. Но иногда нужно добавить дополнительный уровень для дополнительно детализации, в этом случае я просто добавляю его в <DEVICE>, например “home/main“.

Пример структуры топиков без префикса

Пример структуры топиков с префиксом

Думаю, теперь вам понятен принцип генерации топиков, можно переходить к их описанию

 


Системные топики

Устройство генерирует несколько “системных” топиков.

<LOCATION>/<DEVICE>/status: cтатус устройства и одновременно LWT – Last Will and Testament, «последняя воля и завещание». Это один из немногих топиков, публикуемых в открытом виде (без JSON). Здесь может находиться следующая информация:

  • Сразу после подключения к брокеру здесь публикуется значение “online” – это означает, что устройство доступно для управления
  • Если подключение к устройству по какой-либо причине потеряно, брокер сам опубликует сюда значение “offline“. Это и есть LWT payload
  • Когда устройство нормально работает, прошивка периодически (один раз в несколько минут) публикует сюда краткую системную информацию в виде трех строк.
    • Первая строка – время наработки устройства с момента последнего запуска в формате ДНИ : ЧАСЫ : МИНУТЫ.
    • Вторая строка – уровень сигнала WiFi.
    • Третья строка: первая цифра – количество свободной кучи в %; затем идет количество зафиксированных ошибок выделения памяти; и в конце оставшийся объем свободных записей в разделе хранения параметров NVS.

Периодическая публикация системной информации

<LOCATION>/<DEVICE>/time – в этот топик в начале каждой минуты публикуется системное время и дата в различных форматах в виде JSON-пакета. Это позволяет организовать на MQTT-клиенте плитку с датой и временем. С настройками “по умолчанию” данные в топике выглядят так:

{ "time": "10:34", 
  "date": "12.10.22", 
  "weekday": "ср", 
  "timeday": "10:34 ср", 
  "datetime1": "10:34\n12.10.22", 
  "datetime2": "10:34 ср\n12.10.22",
  "year": 2022, 
  "month": 9,
  "day": 12,
  "hour": 10,
  "min": 34,
  "wday": 3,
  "yday": 284,
  "worktime": {"days": 1, "hours": 13, "minutes": 48}
}

<LOCATION>/<DEVICE>/sysinfo – в этот топик периодически публикуется подробная системная информация, предназначенная по большей части для отладки устройства. Можно отключить с помощью макроса CONFIG_MQTT_SYSINFO_ENABLE в файле конфигурации.

Настройки публикации системной информации

Данные в этом топике выглядят примерно так:

<LOCATION>/<DEVICE>/tasklist – в этот топик периодически публикуется список задач с данными стека, также в JSON формате. Это позволяет подобрать оптимальный размер стека для каждой задачи. Можно включить или отключить с помощью макроса CONFIG_MQTT_TASKLIST_ENABLE в файле конфигурации.

 

Есть ещё два топика, которые работают только “на вход”,  то есть через них можно что-то отправить на устройство, но их нельзя увидеть например через MQTT Explorer:

<LOCATION>/<DEVICE>/system/ota – в этот топик вы должны отправить ссылку на прошивку (BIN-файл) для обновления “по воздуху” (ОТА). Подробнее об этом можно почтить тут.

<LOCATION>/<DEVICE>/system/terminal – в этот топик можно отправлять служебные команды для выполнения каких-либо действий, например перезагрузки. Подробнее об этом можно почтить тут.

 


Топики настроек и параметров

Для удаленного управления параметрами устройства предусмотрен большой раздел <LOCATION>/<DEVICE>/config. Все параметры здесь публикуются в открытом виде, без JSON.

В прошивке поддерживается изменение параметров с подтверждением получения в “ответном” разделе <LOCATION>/<DEVICE>/confirm (включено по умолчанию). Это решает две задачи: клиент MQTT на смартфоне всегда знает последние актуальные настройки и мы всегда уверены, то изменение какого-либо параметра успешно “принято-понято” нашим устройством.

Как это работает:

В момент запуска и подключения к MQTT устройство публикует все известные ему параметры (которые хранятся в памяти NVS) в разделе <LOCATION>/<DEVICE>/confirm.

Затем, при попытке изменения параметра мы должны отправить новое значение в <LOCATION>/<DEVICE>/config. Получив эти данные, устройство проверяет значение, записывает его в хранилище NVS, и только после этого отправляет его обратно в <LOCATION>/<DEVICE>/confirm. Клиент MQTT на смартфоне должен “откатить” отправленное значение, если не было получено подтверждение в течении некоторого времени.

Параметров много, “на все случаи жизни” даже в прошивке самой простой телеметрии / метеостанции:

Пример структуры топиков настроек

Давайте рассмотрим их поподробнее.

Я буду писать названия топиков “подтверждения” с confirm, но вы должны понимать, то для отправки данных на устройство нужно заменить на config.

<LOCATION>/<DEVICE>/confirm/common/silent_mode – интервал времени суток “тихого режима”, когда отключаются встроенные светодиоды и (опционально) звуки, дабы не мешать сну. Указывать интервалы следует в формате Ч1:М1-Ч2:М2.

Все параметры – интервалы указываются с точностью до минуты. Но при этом интервалы всегда отсчитываются с начала минуты, то есть 0 секунд каждой минуты.

<LOCATION>/<DEVICE>/confirm/notifications – здесь собраны параметры, отвечающие за то, какие уведомления о проблемах вы будете получать от устройства.

Думаю, особые пояснения в данном случае нужны только под одному параметру: delay. Этот параметр отвечает за отложенные уведомления. Если на устройстве произошел какой-то сбой, то уведомление об этом сбое будет отправлено в чат только спустя указанное в delay количество секунд. Если в течение этого времени проблема самоликвидировалась, то и уведомления вы не получите. Это позволяет эффективно фильтровать кратковременные проблемы и отключения от сервисов. Поставьте 0 для немедленных уведомлений (полезно только для отладки).

<LOCATION>/<DEVICE>/confirm/ping – параметры постоянного пинга серверов в интернете для контроля канала связи. Включается с помощью опции в файле конфигурации CONFIG_PINGER_ENABLE. Параметров в этом разделе достаточно много, но изменять их требуется только в особо сложных случаях.

<LOCATION>/<DEVICE>/confirm/sensors/<НАЗВАНИЕ_СЕНСОРА> – параметры сенсоров измерения различных физических величин. Для каждого сенсора доступны следующие параметры:

  • offset – смещение значения. Может использоваться, если вы имеете образцовое средство измерения и хотите скорректировать измеренное до данных образцового
  • filter_mode – режим фильтрации. На текущий момент может принимать три значения: 0 – фильтр отключен, 1 – среднее, 2 – медиана
  • filter_size – размер буфера фильтра для filter_mode = 1 или 2
  • delta_max – максимальное отклонение измеренного значения от предыдущего значения. Используется для сенсоров AHT10, у которых отсутствует CRC при передаче по шине, и невозможно как-то определить достоверность полученных данных. Из-за этого в некоторых случаях полученные данные могут скака аки тыгдымские кони. Вы можете задать максимальный лимит изменения за один период изменений, свыше которого полученное значение будет считаться “плохим” и отбрасываться.

<LOCATION>/<DEVICE>/confirm/temp_control/<НАЗВАНИЕ_СЕНСОРА> – параметры контроля заданных диапазонов температуры. Контролировать можно не только температуры, правильнее было бы range_control, но так уж исторически сложилось…

  • min – минимальное значение допустимого диапазона
  • max – максимальное значение допустимого диапазона
  • hysteresis – гистерезис, то есть разница между значениями перехода границы диапазона “туда” и “обратно”. Должен быть больше 0, чтобы не получать уведомления каждые несколько секунд на границах диапазонов
  • notify – с помощью этого параметра можно включать и отключать уведомления в telegram

Топики сенсоров

Топики сенсоров генерируются по шаблону:

<LOCATION>/<DEVICE>/<НАЗВАНИЕ_СЕНСОРА>

Сюда публикуется JSON пакет данных, содержащий все имеющиеся данные для всех измеряемых величин (например температура, давление и влажность). Выглядит это примерно так:

Данные, полученные с сенсора

Корневой элемент JSON содержит поля:

  • status – для информации о текущем состоянии сенсора
  • display – смешанные многострочные данные с сенсора для отображения на одной плитке MQTT dashboard на смартфонах
  • а также вложенные структуры JSON для каждого из измерительных элементов сенсора, в нашем случае это humidity и temperature.

Каждый элемент, в свою очередь также состоит из нескольких блоков информации:

  • value – текущие (последние измеренные) данные
  • extremums – зафиксированные экстремумы (минимумы и максимумы) за несколько периодов: entirely – всё время работы с момента его первого запуска; weekly – последняя неделя; daily – текущие сутки

Каждый из блоков содержит следующие поля:

  • value – обработанное и отфильтрованное значение с учетом корректировки (смещения)
  • raw – необработанное значение в том виде, а каком оно было получено непосредственно с сенсора
  • time – время измерения
  • tsv – обработанное значение, совмещенное с временем измерения в двух строках (для наглядности отображения)

Структура JSON не строго фиксированная, её можно корректировать в некоторых пределах:

Настройки формирования JSON пакета для сенсоров


 


Топики контроля диапазонов

В программе за контролем значений внутри заданных диапазонов (например температуры в помещении или инкубаторе) “занимается” определенный класс, которому выделен шаблон топиков:

<LOCATION>/<DEVICE>/temp_control/<НАЗВАНИЕ_СЕНСОРА>

Как и в предыдущих случаях, здесь также публикуется JSON-пакет, который содержит следующую информацию:

Контроль диапазонов

  • status – состояние в цифровом виде: ошибка:-2; ниже нормы: -1; норма: 0; выше нормы: 1
  • value – текущее значение
  • last_normal – время, когда был зафиксирован последний переход к нормальному состоянию
  • last_min – время, когда был зафиксирован переход ниже нижней границы
  • last_max– время, когда был зафиксирован переход выше верхней границы

Этот класс может использоваться не только для контроля диапазонов температуры, но и например тока или напряжения.


Топики нагрузки

Если в составе устройства есть какая-либо программно-управляемая нагрузка, то устройство может публиковать состояние этой нагрузки.

<LOCATION>/<DEVICE>/<НАГРУЗКА>

Публикуемая информация имеет следующую структуру:

Информация о включениях нагрузки

  • status – состояние нагрузки в текущий момент времени в цифровом виде: 0 – отключено1 – включено
  • timestamp – время последнего включения и выключения нагрузки
  • durations – длительность работы нагрузки в секундах за последний сеанс, текущий день, предыдущий день, неделю и т.д. Зная мощность нагрузки (если она примерно постоянная – вентилятор, котёл, лампа и т.д.), мы можем легко посчитать потребляемую мощность в кВт.
  • counters – просто количество включений нагрузки за те же периоды. Не спрашивайте зачем – не знаю, путь будет

Разумеется, ваша прошивка может публиковать данные в какие-то другие топикиВы можете проявить фантазию и реализовать ваши идеи. Но базовый набор строительных “кубиков” для сенсоров, диапазонов и нагрузок уже имеется, нужно только их правильно использовать. А как это сделать, я расскажу в следующих статьях.

 


Все статьи цикла “Термостат и ОПС”:

Прошивка K12 для ESP32 и ESP-IDF:

Дополнительные статьи, которые применимы к любым устройствам, запрограммированным с помощью тех же самых библиотек.


Пожалуйста, оцените статью:
[ 5 из 5, всего 2 оценок ]

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *