如何添加基于 idryer-core 的新产品¶
当你在 idryer-core 上开发新产品(耗材烘干机、加热模块、照明、传感器或其他模块)时,请使用本指南。它说明核心库替你完成了什么,以及产品代码需要补充什么。
一个可以完整编译的示例是 Build-Your-Own-iDryer 文档中的加热储料柜(example/09-cabinet):它覆盖了本页的全部内容。
产品的基础¶
产品通过一个对象与核心库交互——外观类 iDryer::Link(<iDryer.h>)。在 s_link.begin() 和 s_link.loop() 中,核心库会:
- 通过无线方式(ESPTouch)从 iDryer 应用、或通过 USB(Improv)从网页安装程序获取 Wi-Fi 网络,并保持连接;
- 把设备绑定到账户:等待一次性绑定令牌——来自应用(经局域网)或来自串口(
PAIR_TOKEN:<token>)——并在门户上换取永久密钥; - 连接 MQTT,并按
Config中的周期发布telemetry和status; - 在局域网中广播自己(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
; espMqttClient 依赖中的 ESP8266 传输层:无法在 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. 数据:遥测和状态¶
Config中的has*标志决定哪些词汇字段进入遥测、卡片上出现哪些单元格。- 把值写入
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命令时; - 用
menu_apply_by_bind()应用set命令(值、NVS 和缓存一次完成),然后重新发布菜单。
完整代码——Build-Your-Own-iDryer,“来自 YAML 的菜单”一章。
6. 设备卡片¶
卡片显示什么、启动哪些操作,用 s_link.card() 声明——设备卡片:card 清单。
7. 合约¶
当你添加新主题或修改负载时:
- 更新
contracts/mqtt_contract.yaml; - 运行
contracts/regen.sh并提交生成的文件。
双芯片设备¶
对于通过 UART 与独立控制器(例如 RP2040)协作的 ESP32,核心库提供 UART 桥接 idryer_uart.h;可运行的参考实现是 idryer-link 固件。