ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

arduino-esp32 OpenThread:ThreadScan_Callback 流式回调式 Thread 网络发现实战解析

arduino-esp32 OpenThread:ThreadScan_Callback 流式回调式 Thread 网络发现实战解析 arduino-esp32 OpenThreadThreadScan_Callback 流式回调式 Thread 网络发现实战解析【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32本篇围绕 arduino-esp32 仓库中libraries/OpenThread库的ThreadScan_Callback示例展开讲解如何使用 Native APIOThreadScanOThreadNetworkInfo注册onResult()/onComplete()流式回调以非阻塞方式发现周边 Thread 网络。读完后你将掌握OpenThread 启动与接口上电的最小前置条件、回调式发现的标准调用流程、结果去重与存储机制的底层实现以及各异常场景的排查方法——这正是 Matter 设备在配网前枚举 Thread 网络所用的同一底层原语。示例定位三种发现模式中的流式回调ThreadScan_Callback属于 Thread 网络发现示例组。该组示例统一调用OThreadScan.discoverNetworks()底层封装 OpenThread 的otThreadDiscover()对应 CLI 的discover命令只是消费结果的姿势不同示例模式ThreadScan_Discover阻塞式discoverNetworks()一次性拿到结果数组ThreadScan_Async非阻塞启动 scanComplete()轮询ThreadScan_Callback非阻塞启动 onResult()/onComplete()流式回调ThreadScan_Callback的关键特征是发现响应逐条流式送达而不是扫描结束后一次性给一个批次——这与 OpenThread/Matter 的交付模型一致MLE Discovery Response 一到就触发onResult()扫描结束时再触发onComplete()。每个结果包含两部分字段Thread 身份网络名、Extended PAN ID、joinable 标志与 IEEE 802.15.4 链路信息扩展地址、PAN ID、信道、RSSI、LQI这正是 Matter 在配网阶段列出 Thread 网络所使用的基本能力。支持平台与 sdkconfig 前置条件支持目标SoC 需具备 802.15.4 无线电SoCThread状态ESP32-H2yesSupportedESP32-C6yesSupportedESP32-C5yesSupported必需 IDF 特性sdkconfig特性作用CONFIG_OPENTHREAD_ENABLEDy编译 OpenThread 协议栈CONFIG_SOC_IEEE802154_SUPPORTEDy确保 SoC 具备 802.15.4 射频示例目录下的 ci.yml 以requires声明了同样的两项配置这两项也正是 OThreadScan.h 中编译开关所检查的条件——两者任一不满足时整个OThreadScan接口都不会被编译进工程。前置条件先有一张运行中的 Thread 网发现本身不需要本机入网不需要启动 Thread只需OThread.networkInterfaceUp()但要扫到东西RF 范围内必须有一个活跃的网络在第二块 ESP32-H2 / C6 / C5 开发板上烧录 LeaderNode网络形成方示例串口监视器选115200等待串口打印出Role: Leader再烧录本ThreadScan_Callback草图到另一块开发板。LeaderNode 会新建一个数据集网络名ESP_OpenThread、信道 15、PAN ID0x1234、Extended PAN IDdead00beef00cafe成为新分区的 Leader——这也是本文预期输出中这些参数的来源。草图逐行解析完整的回调式发现流程完整草图见 ThreadScan_Callback.ino。核心流程如下1setup()启动协议栈并注册流式回调OThread.begin(false); // 不加载 NVS 数据集仅初始化 OpenThread OThread.networkInterfaceUp(); // 仅上电 IPv6 接口无需 start() 入网 // 1) 注册流式回调 OThreadScan.onResult(onDiscoverResult); // 每收到一个 Discovery Response 触发一次 OThreadScan.onComplete(onDiscoverComplete); // 扫描结束时触发一次 OThreadScan.setScanTimeout(30000); // 总超时 30 s库默认值同为 30000 ms Serial.println(Setup done — callback discovery);2loop()非阻塞启动10 秒一轮周期发现void loop() { Serial.println(Thread discovery start (callbacks)); networksSeen 0; int16_t rc OThreadScan.discoverNetworks(true); // 非阻塞启动 if (rc OT_DISCOVER_RUNNING) { while (OThreadScan.scanComplete() OT_DISCOVER_RUNNING) { delay(50); } OThreadScan.scanDelete(); // 从 loop() 释放结果切勿在回调里调用 } else if (rc 0) { Serial.printf(discovery finished immediately with %d network(s)\r\n, rc); OThreadScan.scanDelete(); } else { Serial.println(discovery failed to start); } delay(10000); // 每 10 s 重新发现一轮 }3回调函数逐条打印 汇总static volatile int networksSeen 0; static void onDiscoverResult(const OThreadNetworkInfo info, void *) { networksSeen; Serial.printf( found: %s | extPan%s | pan%04x | ch%u | %d dBm | joinable%s\r\n, info.networkName, info.extendedPanIdStr().c_str(), info.panId, info.channel, info.rssi, info.joinable ? yes : no); } static void onDiscoverComplete(int16_t resultCount, otError error, void *) { if (error ! OT_ERROR_NONE) { Serial.printf(discovery complete: error %d\r\n, error); } else if (resultCount OT_DISCOVER_FAILED) { Serial.println(discovery complete: failed); } else { Serial.printf(discovery complete: %d network(s) (%d reported live)\r\n, resultCount, networksSeen); } networksSeen 0; }onDiscoverResult每收到一个OThreadNetworkInfo就打印一次onDiscoverComplete只在扫描结束时打印汇总。onComplete()的两个参数resultCount收集到的网络数失败时为OT_DISCOVER_FAILED与error启动请求的 OpenThread 错误码成功时OT_ERROR_NONE分别对应汇总输出里的最终计数与错误分支。一个容易踩的时序点scanDelete()必须从loop()调用且必须在scanComplete()表明扫描结束之后。不要在onResult()/onComplete()回调内部调用scanDelete()或其他OThreadScan方法——回调运行在 OpenThread 任务上且持有 API 锁此时释放内部存储会与完成路径竞争。源码级原理回调如何被流式触发结合 OThreadScan.cpp 可以看到整个交付模型启动即返回。discoverNetworks(true)在 OThreadScan.cpp#L146-L211 中获取 OpenThread API 锁、调用otThreadDiscover()并注册静态处理器handleDiscoverResultasync 模式下立即返回OT_DISCOVER_RUNNING-1。注意向量预分配prepareResultStorage()刻意放在锁外避免持锁堆分配。每条响应都过一遍回调。OThreadScan.cpp#L377-L420 的onDiscoverResult()对每个非空otActiveScanResult都会调用用户注册的_resultCb——包括重复响应同一 Extended PAN ID 来自不同路由器/信标。存储层按 Extended PAN ID 去重合并仅保留 RSSI 最强的一份findResultByExtendedPanId() RSSI 比较所以onResult()的live 计数可能大于最终resultCount——这正是排障表中live 计数与最终计数不一致现象的来源。完成由最终回调定义。OpenThread 在发现结束时投递一个aResult nullptr的最终回调库据此置位_done、释放完成信号量并触发onComplete()。scanComplete() 特意不依赖otThreadIsDiscoverInProgress()判断结束——OpenThread 可能已在空闲状态但最终回调尚未送达提前认定完成会让onComplete()报出 0 计数。同理scanDelete() 在最终回调到达前是 no-op延迟释放防止清零正在上报的结果。超时与失败约定。OT_DISCOVER_RUNNING -1、OT_DISCOVER_FAILED -2与 Wi-Fi 扫描的常量约定一致定义于 OThreadScan.h#L42-L51默认总超时OT_DISCOVER_DEFAULT_TIMEOUT_MS为 30000 ms可用setScanTimeout()覆盖。存储上限。每轮扫描最多存储OT_DISCOVER_MAX_RESULTS默认16个唯一网络超限的响应仍会通过onResult()送达但不会进入getResult()索引存储。如需更大容量可在#include OThreadScan.h之前#define OT_DISCOVER_MAX_RESULTS 32ThreadScan_Discover 演示了该做法。结果字段与 joinable 的判定细节草图打印的每个字段都来自 OThreadNetworkInfo 结构字段说明networkNameNull 结尾的 Thread 网络名OT_NETWORK_NAME_MAX_SIZE内extendedPanId/extendedPanIdStr()8 字节 Extended PAN ID 及其 16 位小写十六进制串panIdIEEE 802.15.4 PAN IDextAddress/extAddressStr()响应方扩展地址16 位十六进制串channel802.15.4 信道11..26rssi接收信号强度dBmlqi链路质量指示threadVersion4 位 MLE Thread 版本值joinable是否允许加入nativeCommissioner原生 Commissioner 标志值得注意的一个实现细节MLE Discovery 的可加入并不读mIsJoinable标志那是 802.15.4 Beacon 主动扫描的字段而是检查响应中的Steering Data 布隆过滤器——OThreadScan.cpp#L55-L65 中discoverResultIsJoinable()只要 Steering Data 有任何非零字节即判定joinable trueMeshCoP Leader 关闭加入时会将该过滤器清空。因此输出里的joinableyes表示 Leader 当前允许新节点加入。预期串口输出扫到 Leader 网络时115200 波特率Setup done — callback discovery Thread discovery start (callbacks) found: ESP_OpenThread | extPandead00beef00cafe | pan1234 | ch15 | -45 dBm | joinableyes discovery complete: 1 network(s) (1 reported live)未发现网络时Thread discovery start (callbacks) discovery complete: 0 network(s) (0 reported live)启动失败时Thread discovery start (callbacks) discovery failed to start扫描完成但内部出错/超时时onComplete()会打印discovery complete: error n启动错误或discovery complete: failedresultCount OT_DISCOVER_FAILED。自定义上下文指针、过滤器与单信道回调注册支持第二个void *context参数见 OThreadScan.h#L187-L193可在多实例/多对象场景传递状态OThreadScan.onResult(myResultHandler, myContext); OThreadScan.onComplete(myCompleteHandler, myContext);在调用discoverNetworks()之前还可配置发现过滤器OThreadDiscoverFilters默认值与 ESP-IDF CLIdiscover命令一致即不做任何过滤OThreadDiscoverFilters filters; filters.joinerOnly true; // 仅接受加入者模式发现 // filters.panIdFilter 0x1234; // 指定 PAN ID保持默认 OT_PANID_BROADCAST(0xffff) 则接受任意网络 // filters.eui64Filter false; // 按 EUI-64 过滤响应 OThreadScan.setDiscoverFilters(filters);另外两个常用旋钮setScanTimeout(ms)调整阻塞/异步完成的总超时默认 30000 mssetChannel(channel)将扫描限制在单个 802.15.4 信道11..26传0表示扫描全部支持信道默认。排障启动顺序先在另一块开发板上启动 LeaderNode 并等待Role: Leader再烧录本草图。症状可能原因没有任何found:行Leader 未运行或超出 RF 范围——在另一块板上先启动 Leaderdiscovery failed to start接口未上电漏调OThread.networkInterfaceUp()或扫描已在进行中discovery complete: failed超时或内部错误——调大setScanTimeout()live 计数与最终计数不一致onResult()对每个 Discovery Response 都会触发含重复响应方存储层按 Extended PAN ID 合并仅保留最强 RSSI若串口完全无输出检查监视器波特率是否为115200、USB 口是否正确来自示例组排障说明。延伸阅读Thread Network Discovery 示例组总览三种发现模式对照与运行步骤ThreadScan_Discover阻塞式discoverNetworks()与OT_DISCOVER_MAX_RESULTS覆盖示例ThreadScan_AsyncscanComplete()轮询、无逐条回调SimpleThreadNetwork / LeaderNode多板测试的组网配套示例OThreadScan.h 与 OThreadScan.cppOThreadScan全部接口、状态机与结果存储实现libraries/OpenThread/README.md库级 API 总览与结果向量/锁的说明本文基于仓库内 ThreadScan_Callback/README.md、ThreadScan_Callback.ino 及libraries/OpenThread库源码整理示例许可为 Apache License 2.0。【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表