MqttClient¶
MqttClient — MQTT-клиент устройства. Оборачивает PubSubClient, управляет подключением и маршрутизацией входящих сообщений. Все топики формируются из серийного номера устройства автоматически.
Инициализация¶
Вызывается CloudStateMachine после успешного provisioning. Не подключается сразу — устанавливает параметры и настраивает TLS.
Параметры:
serialNumber— серийный номер устройства. Используется как MQTT client ID и username.token— токен устройства. Используется как MQTT password.
При сборке с флагом MQTT_USE_TLS=1 клиент настраивает WiFiClientSecure с корневым CA Let's Encrypt (встроен в root_ca.h).
mqttClient_.setServer(MQTT_BROKER, MQTT_PORT);
mqttClient_.setBufferSize(MQTT_BUFFER_SIZE); // см. ниже «Размер буфера»
mqttClient_.setKeepAlive(60);
Размер буфера¶
PubSubClient по умолчанию использует буфер 256 байт — этого хватает только на короткие сообщения. Для устройств iDryer этого мало: основной «тяжёлый» payload — это конфигурация устройства (меню), которая публикуется в топик idryer/{serial}/config целиком.
MqttClient устанавливает буфер в MQTT_BUFFER_SIZE и ограничивает размер чанка большого конфига MQTT_CONFIG_CHUNK_SIZE. Обе константы определены в lib/idryer-core/src/mqtt/mqtt_client.h:
#define MQTT_BUFFER_SIZE 16384 // буфер PubSubClient
#define MQTT_CONFIG_CHUNK_SIZE 16000 // максимум данных в одном чанке config
Соотношение между ними:
MQTT_BUFFER_SIZE(16384 байт) — верхняя граница одного MQTT-сообщения. Любойpublish*()с payload больше этого размера будет отброшенPubSubClientбез отправки.MQTT_CONFIG_CHUNK_SIZE(16000 байт) — максимальный объём"d"(полезной части) внутри одного чанкаpublishConfigRaw. Запас в 384 байта оставлен на envelope чанка:{"tid":..,"idx":..,"total":..,"last":..,"d":"..."}плюс автоматическое полеtimestamp.
Откуда взяли 16384¶
Число подобрано не «по красоте», а от максимального ожидаемого payload устройства, который и есть передача settings/меню:
- Конфиг (меню) Storage Link и Link/iHeater сериализуется как JSON с экранированием. Полный snapshot текущего меню укладывается в ~10–14 КБ.
- Запас до 16384 покрывает рост числа пунктов меню без переезда на разбиение по чанкам.
- Значение кратно 4 КБ — удобно для аллокации на ESP32.
Если ваш продукт имеет более крупный конфиг (например, расширенное меню с большим числом пунктов или бинарными значениями), есть два пути:
-
Поднять
Учтите расход RAM:MQTT_BUFFER_SIZE— переопределить черезbuild_flagsвplatformio.ini:PubSubClientдержит этот буфер постоянно. На ESP32-C3 (~400 КБ свободного heap) 32 КБ — приемлемо, но дальше начинаются риски. -
Использовать
publishConfigRaw(json, length)— сам разобьёт payload на чанки поMQTT_CONFIG_CHUNK_SIZE, бэкенд соберёт по полямtid/idx/total/last. Этот путь предпочтителен для конфигов из RP2040, которые приходят по UART кусками и могут быть произвольной длины.
Применимо к продуктовым публикациям¶
Те же 16384 байт — потолок для publishTelemetry, publishStatus, publishEvent. На практике телеметрия и события сильно меньше (сотни байт), и в этот лимит упираются только публикации конфига. Если в проекте есть периодическая публикация большого payload (например, дамп массива измерений) — оцените его размер заранее или разбивайте сами.
Подключение¶
Выполняет:
- Подключение к брокеру с persistent session (
clean_session = false). Persistent session обязательна — без неё команды, пришедшие пока устройство не в сети, теряются. - Устанавливает LWT-сообщение на топик
idryer/{serial}/offline(QoS 1, не retained). - Подписывается на
idryer/{serial}/commands/#(QoS 1). Делает до 3 попыток, при неудаче отключается.
Возвращает true если подключение и подписка успешны.
Цикл¶
Вызывается каждую итерацию. Переподключается при обрыве, затем вызывает PubSubClient::loop() для получения входящих сообщений.
Публикация¶
Все publish-методы добавляют поле timestamp (ISO 8601 UTC) если оно ещё не присутствует в документе.
| Метод | Топик | Retained |
|---|---|---|
publishInfoJson(const char* json) |
idryer/{serial}/info |
да |
publishTelemetry(JsonDocument&) |
idryer/{serial}/telemetry |
нет |
publishStatus(JsonDocument&) |
idryer/{serial}/status |
да |
publishConfig(JsonDocument&) |
idryer/{serial}/config |
нет |
publishEvent(JsonDocument&) |
idryer/{serial}/events |
нет |
publishIntegrationsStatus(JsonDocument&) |
idryer/{serial}/integrations/status |
да |
publishConfigRaw(const char* json, size_t len) |
idryer/{serial}/config |
нет |
publishConfigDelta(const char* json, size_t len) |
idryer/{serial}/config/delta |
нет |
publishConfigRaw автоматически разбивает payload на чанки если размер превышает MQTT_CONFIG_CHUNK_SIZE (16000 байт). Каждый чанк содержит поля tid, idx, total, last, d.
Note
PubSubClient публикует всегда на QoS 0, независимо от настроек топика. Это ограничение библиотеки.
Приём команд¶
Входящие сообщения в топике idryer/{serial}/commands/{cmd} парсятся как JSON и передаются в зарегистрированный CommandCallback:
void setCommandCallback(CommandCallback callback);
// CommandCallback = std::function<void(const char* command, JsonObjectConst data)>
Часть {cmd} извлекается из топика и передаётся как первый аргумент. IdryerRuntime регистрирует этот колбэк в begin().
Вспомогательные методы¶
static char* getIsoTimestamp(char* buffer); // буфер >= 32 байт
static char* generateUuid(char* buffer); // буфер >= 37 байт
generateUuid генерирует UUID v4 на основе esp_random().
Ограничения¶
- Один экземпляр
MqttClientна устройство (singleton черезinstance_). - Максимальный размер одного JSON-сообщения —
MQTT_BUFFER_SIZE(по умолчанию 16384 байт). Подбирается по размеру самого тяжёлого payload устройства — обычно это сериализованный конфиг (меню). Для бо́льших конфигов поднимите константу черезbuild_flagsили используйтеpublishConfigRawс автоматическим разбиением на чанки. См. раздел «Размер буфера». - TLS включается флагом сборки
MQTT_USE_TLS.