
arduino-esp32 OpenThread sensor_collector构建 Thread Leader Commissioner 多传感器 UDP 汇聚服务【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32本篇基于 arduino-esp32 仓库中 OpenThread 库的UDP_SensorNetwork示例深入讲解服务端 sketchsensor_collector的完整实现它如何组建或恢复一张 Thread 网络、以 Commissioner 角色为 Joiner 传感器开放入网窗口、在 UDP 5050 端口接收多节点遥测帧并用应用层序列号 权威 ACK 节点表生命周期管理构建一个可对大量传感节点提供服务的高节点 UDP 汇聚服务。读完本文你可以直接复刻该 demo 的部署顺序与调参方法并从源码层面理解 Commissioner 提权、Joiner 窗口、序列号去重与掉线驱逐等关键机制在sensor_collector.ino中的落地方式。sensor_collector 在 UDP Sensor Network 中的定位UDP_SensorNetwork是一个多对一many-to-one遥测示例完全构建在 Arduino OpenThread Native APIOThreadOThreadUDP之上。示例由两个 sketch 组成架构如下sensor_node 1 ----\ sensor_node 2 ------- sensor_collector (Leader Commissioner UDP sink) sensor_node N ----/Sketch角色sensor_collector (Leader UDP sink)服务端ThreadLeader Commissioner并在端口5050上提供高节点 UDP 汇聚。sensor_node (Joiner sensor client)客户端ThreadJoiner可选Sleepy End Device 周期性 UDP 传感器上报。sensor_collector是 sensor_node 的对应端counterpart一台 collector 可以在同一张网络上服务任意多个 sensor 节点。它做五件事组建或恢复名为ESP_OT_SENSOR_NET的 Thread 网络重启后从 NVS 恢复 DataSet申请Commissioner角色为 PSKdJ01NME打开 Joiner 入网窗口在 UDP 端口5050上监听传感器遥测帧跟踪最多MAX_SENSORS个节点 ID把静默节点标记为离线、驱逐过期记录并发送应用层序列号 ACK一旦 collector 自身从网络掉线自动重启 Thread 并重开 UDP 套接字。完整的组网流程、线缆协议wire protocol与运行步骤见 UDP Sensor Network 总览 README。支持的目标平台与 IDF 依赖支持的 SoCSoCThread状态ESP32-H2支持SupportedESP32-C6支持SupportedESP32-C5支持Supported必需的 sdkconfig 特性sensor_collector的 ci.yml 中声明了该示例的编译前提与下表一一对应特性作用CONFIG_OPENTHREAD_ENABLEDy构建 OpenThread 协议栈。CONFIG_SOC_IEEE802154_SUPPORTEDy确认 SoC 具备 802.15.4 射频。CONFIG_OPENTHREAD_COMMISSIONERy启用 Commissioner API 用于 Joiner 准入。其中CONFIG_OPENTHREAD_COMMISSIONER是 collector 独有依赖——在 OThread.cpp 中startCommissioner()、addJoiner()、stopCommissioner()、getCommissionerState()等 Commissioner 相关接口全部被#if CONFIG_OPENTHREAD_COMMISSIONER包裹未开启该开关时这些 API 根本不会被编译出来。而 sensor_node 侧对应的开关是CONFIG_OPENTHREAD_JOINERy两个开关不要混淆。部署前提与启动顺序必须先烧录本 collector再烧录任何 sensor_node 开发板。在烧录或复位传感器之前先等待串口出现Commissioner ACTIVE与Collector listening on UDP port 5050两条日志。首次启动时collector 会 provision 一个全新的 Active Dataset此后每次启动都从 NVS 中恢复已存储的 dataset因此复位 collector 后恢复的是同一张 Thread 网络。已被 commission 过的传感器同样会恢复 NVS 中的 dataset正常掉电复位后不需要重新打开 joiner 窗口。结合 UDP Sensor Network 总览 的 How to Run完整操作是在一块开发板上烧录sensor_collector串口监视器波特率设为115200等待其打印Collector listening on UDP port 5050在其余一块或多块开发板上烧录sensor_node确认每个传感器打印statusACKED、collector 打印到接收到的采样。核心流程源码走读以下以 sensor_collector.ino 为准展开。1) Thread 网络恢复 NVS dataset 或 provision 新网络startThreadNetwork()的核心逻辑sensor_collector.inoOThread.begin(false); if (OThread.hasActiveDataset()) { // 从 NVS 恢复已持久化的 Active Dataset } else { // 首次上电用固定参数构建 DataSet 并提交 DataSet ds; ds.initNew(); ds.setNetworkName(OT_NETWORK_NAME); // ESP_OT_SENSOR_NET ds.setChannel(OT_CHANNEL); // 15 ds.setPanId(OT_PAN_ID); // 0xABCE ds.setExtendedPanId(OT_EXTPANID); ds.setNetworkKey(OT_NETKEY); // 128-bit OThread.commitDataSet(ds); } OThread.networkInterfaceUp(); OThread.start(); // 轮询等待设备角色达到 CHILD 以上最多 24 * 2.5s 60s while (tries-- OThread.otGetDeviceRole() OT_ROLE_CHILD) { delay(2500); }要点网络参数是固定 DataSet——网络名ESP_OT_SENSOR_NET、信道 15、PAN ID0xABCE、扩展 PAN ID 与 128 位网络密钥均写死在文件头部常量定义。collector 与传感器必须使用同一套 dataset 常量PSKD也要一致sensor_node 侧使用同样的J01NME与CHANNEL_HINT 15。如果修改了OT_*常量却不生效是因为持久化在 NVS 中的 Active Dataset 优先级更高——需要擦除 NVS 或执行 OpenThreadfactoryreset后新常量才会生效。等待 attach 的循环以角色判断为终止条件OT_ROLE_CHILD及更高级别Router、Leader都算成功。collector 因为没有父节点最终会以 Leader 身份上线。2) Commissioner 提权与 Joiner 窗口// 2) Petition Commissioner and open the joiner window. OThread.startCommissioner(); OThread.addJoiner(J01NME, JOINER_WINDOW_SEC); // PSKd J01NME默认开 3600 sstartCommissioner()在 setup() 中执行若首次失败loop() 会在设备角色达到 CHILD 后每 5 秒重试一次直到成功。从源码实现看OThread.cpp这一过程比表面上复杂OpenThread::startCommissioner(uint32_t timeoutMs 30000)声明见 OThread.h调用otCommissionerStart()发起petition然后通过一个二进制信号量阻塞等待状态回调直到状态变为OT_COMMISSIONER_STATE_ACTIVEpetition 被 Leader 接受或OT_COMMISSIONER_STATE_DISABLED被拒绝超时默认 30 s若返回OT_ERROR_ALREADY已经是 ACTIVE会直接按成功处理petition 被拒绝则返回OT_ERROR_REJECTED示例中会打印startCommissioner failed: %daddJoiner(pskd, timeoutSec 120, eui64 nullptr)OThread.cpp调用otCommissionerAddJoiner()以 ASCII PSKdJ01NME打开入网窗口JOINER_WINDOW_SEC默认 3600 s本例不传eui64即窗口对任意 EUI-64的 Joiner 开放任何持有正确 PSKd 的 sensor_node 都可以加入。Commissioner 是 Thread 中唯一有权打开 Joiner 窗口的角色因此这个先当 Leader、再当 Commissioner的组合恰好让 collector 成为入网准入的权威节点。同一组合但没有 UDP 流量的精简版本可参考 CommissionerNode 示例。3) UDP 监听与序列号 ACK 协议// 3) Bind UDP on port 5050 and process incoming frames in loop(). OtUdp.begin(COLLECTOR_PORT); // 5050OtUdp是 OThreadUDPclass OThreadUDP : public UDP即绑定在 Thread 网络接口MLDMesh-Local Address 域上的 UDP 套接字而非 WiFi/Ethernet 上的普通 UDP。入站帧为 ASCII 格式wire protocolidnodeId,sequ32,temp_centii32,batt_mvu16示例帧id3CAAB123,seq42,temp_centi2387,batt_mv3810collector 的 ACK 格式OK,nodeId,seqprocessPacket()sensor_collector.ino是整个示例的协议核心它用sscanf解析四个字段后执行一套完整的序列号状态机帧类型判定条件collector 行为首次帧packetCount 0接受读数新帧seq lastSeq接受读数更新lastSeq重复帧seq lastSeq记为(dup)不覆盖读数节点重启seq 1 lastSeq 1视为新流重新开始接受并清零重复计数过期/乱序帧seq lastSeq且不满足重启条件不覆盖最新读数计为 stale关键设计是collector 是序列号的权威方无论收到什么帧ACK 里回的都是 collector自己存储的lastSeq而不是收到的seqsendAck() 与注释。这样 sensor_node 端见 sensor_node.ino 的 parseAckSeq 与 RESYNC 逻辑可以用 collector 的存储值向前滚动RESYNC或保持原序列重试NO_ACK实现应用层重同步——传感器侧持有已被确认的最后序列号s_seq每次发送s_seq 1只有收到 ACK 才前进。端口注意事项应用流量务必使用 UDP 端口5050避开61631Thread TMF CoAP与5683/5684CoAP。若把应用套接字绑定到这些保留端口可能收到二进制的 Thread 管理流量在串口上表现为DROP malformed。4) 节点表离线判定、记录驱逐与统计节点表是纯 RAM 结构static SensorRecord s_records[MAX_SENSORS]每个记录包含used / online / nodeId / lastSeq / lastTempCenti / lastBatteryMv / packetCount / duplicateCount / lastSeenMs / lastIp结构定义。新节点到来时按nodeId线性查找找不到则分配空槽表满时直接丢弃并打印DROP table full (MAX_SENSORS%u)。auditNodes() 在每个报告周期默认 30 s执行两级老化age NODE_OFFLINE_MS默认 95 s约 3 个丢失的 30 s 采样节点online置 false打印node %s - OFFLINE (silent %lus)age NODE_EVICT_MS默认 30 min整个记录被释放槽位归还给新节点。配套的 printCompactReport() 打印角色、已知/在线节点数与累计收包、丢包计数。注意节点表不持久化collector 复位后表会清空这是预期行为——传感器会在 collector 从 NVS 恢复网络后通过重传把表重建起来。5) 角色看门狗掉线自愈loop() 中每 5 秒检查一次设备角色若掉到OT_ROLE_CHILD以下Detached/Disabled触发 restartThreadNetwork()停止 UDP 套接字 - OThread.stop() - OThread.start()复用 NVS 中持久化的 dataset - 最多 20s 轮询等待重新 attach - 重新 begin(5050)即collector 掉线时自动重启 Thread 并重建 UDP socket无需人工干预。对应地sensor_node 侧也有自己的forceReattach()与 3 次连续NO_ACK后强制重挂逻辑sensor_node.ino两端各自自愈。期望的串口输出按 README 描述正常运行时的典型输出为 sensor_collector: Thread Leader UDP sink Attached as Leader. Commissioner ACTIVE. PSKd J01NME open for 3600 s. Collector listening on UDP port 5050 RX [fdde:ad00:beef:0:....]:5050 - id3CAAB123,seq1,temp_centi2387,batt_mv3810 TX ACK - OK,3CAAB123,1 Fleet: known1 online1 total_rx1 dropped0实际 sketch 打印的格式略有不同如RX node... seq... temp...C batt...mV from [...]、[collector] role... nodes... online... packets... dropped...但判断部署成功的标志是稳定的出现Commissioner ACTIVE与Collector listening on UDP port 5050后传感器每 30 s 一帧、每帧都有OK,nodeId,seq应答。可调参数一览所有可调常量集中在 sensor_collector.ino 文件头部常量作用PSKDCommissioner 接受的 Joiner 密钥sensor_node 必须一致J01NME。JOINER_WINDOW_SECaddJoiner()窗口有效期默认 3600 s。OT_CHANNEL802.15.4 信道默认 15。OT_PAN_ID16 位 PAN ID默认0xABCE。OT_EXTPANID扩展 PAN ID 字节序列默认CE 01 00 00 DE AD BE EF。OT_NETKEY128 位网络密钥。OT_NETWORK_NAMEThread 网络名ESP_OT_SENSOR_NET。COLLECTOR_PORTUDP 监听端口默认 5050。MAX_SENSORS内存节点表容量默认 256。NODE_OFFLINE_MS节点被标记离线前的静默时长默认 95 s。NODE_EVICT_MS节点记录被驱逐前的静默时长默认 30 min。REPORT_PERIOD_MS舰队统计打印周期默认 30 s。修改 dataset 相关常量OT_*、PSKD时记住若 NVS 中已有旧 dataset旧数据优先需要擦除 NVS 或factoryreset才能让新常量生效两侧collector 与 node的PSKD、信道等必须同步修改。故障排查启动顺序先烧录本 collector等到Collector listening on UDP port 5050再烧录 sensor_node 开发板对任何在 collector 就绪前启动的传感器执行复位。现象可能原因没有任何传感器能 attach首次 commissionCommissioner join 窗口未开——先等Commissioner ACTIVE再启动传感器。Collector 复位后节点表丢失预期行为——表在 RAM 中collector 从 NVS 重连后传感器会重建表。来自 RLOC 地址的DROP malformed应用绑定到了 Thread 保留端口——用5050不要用 61631/5683/5684。修改OT_*常量不生效Dataset 已持久化在 NVS——先擦 flash 或factoryreset再期望新常量生效。Collector 掉线角色看门狗会自动重启 Thread 并重开 UDP socket——检查射频环境与构建开关CONFIG_OPENTHREAD_COMMISSIONERy等。此外sensor_node 侧 README/总览文档 还补充了几条客户端视角的现象传感器NO_ACKcollector 未绑定、dataset/PSKd 不匹配或端口错误collector 复位后传感器卡住传感器在丢失 ACK 后会强制重挂可等待或复位传感器掉电重启后能工作但首次入网失败预期——已 commission 的传感器走 NVS 恢复joiner 窗口只用于首次 commission 或擦除 NVS 之后。相关资源UDP Sensor Network 总览——线缆协议与多节点架构sensor_nodeJoiner 可选 SED 传感器客户端CommissionerNode——同样的 Leader Commissioner 组合但不带 UDP 流量Native UDP 示例总览——所有 Native UDP demo 与端口约定API 实现OThread.cppCommissioner 部分、OThread.h、OThreadUDP.h。本示例基于 Apache License 2.0 许可。【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考