Как добавить новый продукт на базе idryer-core¶
Эта инструкция нужна, когда вы делаете новый продукт на базе idryer-core: сушилку филамента, нагревательный блок, подсветку, датчик или другой модуль. Она показывает, что ядро делает за вас и что должен добавить код продукта.
Полный собираемый пример — нагреваемый шкаф хранения из документации Build-Your-Own-iDryer (example/09-cabinet): в нём пройдено всё, что есть на этой странице.
На чём строится продукт¶
Продукт общается с ядром через один объект — фасад iDryer::Link (<iDryer.h>). Внутри s_link.begin() и s_link.loop() ядро:
- получает сеть Wi-Fi из приложения iDryer по воздуху (ESPTouch) или из веб-установщика по USB (Improv) и держит подключение;
- привязывает устройство к аккаунту: ждёт одноразовый токен привязки — из приложения по локальной сети или через последовательный порт (
PAIR_TOKEN:<токен>) — и обменивает его на портале на постоянный секрет; - подключается к MQTT и публикует
telemetryиstatusс периодами изConfig; - объявляет себя в локальной сети (mDNS
_idryer._tcp) и принимает команды приложения по WebSocket; - публикует card-манифест карточки устройства.
Классы нижнего уровня (IdryerRuntime, CloudStateMachine, LocalAccess и другие) — внутренности ядра: токен привязки доходит до облачной части только внутри iDryer::Link. Стройте продукт на фасаде.
1. Проект¶
idryer-core кладётся в lib/idryer-core/ (копия или символическая ссылка); библиотеки ядра PlatformIO берёт из его library.json. Минимальный platformio.ini:
[env:my-device]
platform = espressif32
framework = arduino
board = esp32-c3-devkitm-1
; транспорт ESP8266 из зависимостей espMqttClient: на ESP32 не собирается
lib_ignore = ESPAsyncTCP
build_flags =
-DIDRYER_API_BASE='"https://portal.idryer.org/api"'
-DMQTT_BROKER='"mqtt.idryer.org"'
-DMQTT_PORT=8883
-DMQTT_USE_TLS=1
Без lib_ignore = ESPAsyncTCP сборка падает в ESPAsyncTCP.cpp; без MQTT_BROKER и MQTT_PORT ядро не компилируется.
2. Минимальная прошивка¶
#include <iDryer.h>
static const iDryer::Config CFG = {
.deviceType = iDryer::DeviceType::Unknown, // не продукт iDryer: карточка — из манифеста
.unitsCount = 1,
.hasAirTemp = true,
.hasAirHumidity = true,
.hardwareVersion = "1.0",
.firmwareVersion = "0.1.0",
.model = "My Device",
};
static iDryer::Link s_link(CFG);
void setup() {
s_link.begin();
// Устройство отвязали на портале: стереть секрет, ждать новой привязки.
s_link.onCommand("revoke", [](JsonObjectConst) { s_link.handleRevoke(); });
}
void loop() {
s_link.loop();
s_link.telemetry.airTempC[0] = readTemp(); // ваши функции датчиков
s_link.telemetry.airHumidityPct[0] = readHumidity();
}
s_link.begin() запускает Wi-Fi, привязку, MQTT и локальный доступ; s_link.loop() должен крутиться постоянно, без delay(). Команда revoke приходит с портала, когда устройство отвязывают от аккаунта: handleRevoke() стирает секрет, и устройство ждёт нового токена привязки.
3. Wi-Fi и привязка — не в коде¶
В прошивке нет ни пароля от сети, ни данных аккаунта. Пользователь подключает устройство в приложении iDryer: Подключить новое устройство → шаг Wi-Fi (приложение передаёт сеть через ESPTouch) → шаг Привязка (приложение находит устройство через mDNS, получает у портала одноразовый токен и передаёт его устройству; токен на портале устройство активирует само). Пока Wi-Fi не поднялся, последовательный порт молчит: ядро держит его для веб-установщика (Improv).
По шагам и с ожидаемым логом — Build-Your-Own-iDryer, глава «Старт прошивки на ядре».
4. Данные: телеметрия и статус¶
- Флаги
has*вConfigопределяют, какие словарные поля уходят в телеметрию и какие ячейки появятся на карточке. - Значения пишите в
s_link.telemetry(airTempC,airHumidityPct,heaterTempC,heaterPower01,fanOn,servoOpen) иs_link.status(mode,targetTempC,durationS,elapsedS); ядро публикует их с периодами изConfig, аs_link.publishStatusNow()отправляет статус сразу. - Своё поле — через
s_link.onTelemetryPublish(), на карточку — черезs_link.card().sensor(); см. Карточка устройства.
5. Настройки: меню¶
Настройки описываются в src/menu/menu.yaml; генератор menu_gen.py делает из него C++-код, хранение в NVS и JSON меню (Меню как протокол). Код продукта:
- загружает меню до
s_link.begin():menu.initDefaults(),menu.loadFromNVS(),menu_sync_state_to_cache(); - публикует его через
menu_buildFullJson()иs_link.devicePublisher()->publishConfigRaw()— при выходе в онлайн и по командеget_config; - применяет команду
setчерезmenu_apply_by_bind()(значение, NVS и кэш разом) и публикует меню заново.
Полный код — Build-Your-Own-iDryer, глава «Меню из YAML».
6. Карточка устройства¶
Что показывает карточка и какие операции запускает, объявляется через s_link.card() — Карточка устройства: card-манифест.
7. Контракт¶
Когда добавляете новые топики или меняете payload:
- обновите
contracts/mqtt_contract.yaml; - запустите
contracts/regen.shи закоммитьте сгенерированные файлы.
Двухчиповые устройства¶
Для ESP32, который работает с отдельным контроллером (например, RP2040) по UART, в ядре есть UART-мост idryer_uart.h; рабочий образец — прошивка idryer-link.