news 2026/9/14 10:41:48

ESP32 OpenThread CoAP Greenhouse 实战:同一 Mesh 网络上 Plain CoAP 遥测与 CoAPS 安全控制的双传输设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ESP32 OpenThread CoAP Greenhouse 实战:同一 Mesh 网络上 Plain CoAP 遥测与 CoAPS 安全控制的双传输设计

ESP32 OpenThread CoAP Greenhouse 实战:同一 Mesh 网络上 Plain CoAP 遥测与 CoAPS 安全控制的双传输设计

【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32

本文基于 arduino-esp32 仓库中的 OpenThread 原生 API 示例CoAP_Greenhouse(示例总览),讲解一个典型的 IoT 双传输架构:开放遥测走明文 CoAP(5683,NON 可靠级),经认证的执行器命令走 CoAPS/DTLS(5684,CON 可靠级),两套协议运行在同一对 Thread Mesh 节点上。读完后你将掌握:Thread Leader/Commissioner 与 Joiner 的双板组网流程、OThreadCoAPServerOThreadCoAPSecureServer双服务器共存方式、CoAPS 的 sdkconfig/menuconfig 开启方法,以及 PSK 密钥在客户端与服务端之间的匹配细节。

双传输架构:为什么遥测和执行器要分开

该示例模拟一个温室场景:只读环境数据(温度、光照)对网络内任意节点开放,而可写控制命令(灌溉阀门、通风风扇)必须通过 DTLS 认证通道下发。这种"开放遥测 + 认证执行器"的拆分是物联网网关的常见安全模型。仓库用一对 sketch 完整演示了多个资源、CON 与 NON 的可靠性差异,以及同一对 Mesh 节点上的双传输共存:

Sketch角色
greenhouse_server(Leader + 网关)Leader + Commissioner + 明文 CoAP 服务器 + CoAPS 服务器
greenhouse_client(Joiner + 控制器)Joiner + 明文 NON 轮询遥测 + CoAPS CON 控制命令

原始文档中的架构示意图如下(ASCII 图):

┌─────────────────────────────────────┐ greenhouse_client │ PlainClient (5683) │ │ NON GET greenhouse/temp │ │ NON GET greenhouse/light │ │ SecureClient (5684) │ │ CON PUT valve/water │ │ CON PUT fan/speed │ └─────────────────┬───────────────────┘ │ Thread mesh ┌──────────────────────▼──────────────────────────┐ greenhouse_server │ OThreadCoAPServer — telemetry (read) │ │ OThreadCoAPSecureServer — actuators (write) │ └─────────────────────────────────────────────────┘

从源码结构看,客户端通过OThread.getLeaderRloc()直接取 Leader 的 RLOC16 作为服务器地址(greenhouse_client.ino 中serverIp = OThread.getLeaderRloc();),无需额外的 mDNS 解析,因为服务器就是组网时先烧录的那块板子。

资源清单:端口、方法与可靠性

示例共注册 4 个 URI 资源,明文与加密传输各有分工:

路径传输方法客户端可靠性用途
greenhouse/tempPlain 5683GETNON温度(°C)
greenhouse/lightPlain 5683GETNON光照强度(lux)
valve/waterCoAPS 5684PUTCON灌溉 0–100 %
fan/speedCoAPS 5684PUTCON通风 0–100 %

端口约定与 OThreadCoAP.h 中的宏定义一致:OT_COAP_DEFAULT_PORT 5683(明文)、OT_COAP_SECURE_DEFAULT_PORT 5684(CoAPS)。响应码使用头文件中的OT_COAP_RESP_*常量(如 205/204/405),PUT 成功时服务端回204 Changed,方法不匹配回405 Method Not Allowed(见 greenhouse_server.ino 中resp.setCode(OT_COAP_RESP_METHOD_NA)的处理)。

服务端:组网、Commissioner 与双服务器启动

服务端 sketch 位于 greenhouse_server.ino,文件头部声明了全部可调参数:

常量含义
PSKDCommissioner 接受的 Joiner 密钥,值J01NME
JOINER_WINDOW_SECaddJoiner()窗口有效期,默认 600 秒
CHANNEL802.15.4 信道,默认 15
PAN_ID16 位 PAN ID,默认0xBEE5
NETKEY128 位网络密钥(硬编码的演示值)
COAP_PSK16 字节 CoAPS 预共享密钥,必须与客户端一致
COAP_PSK_IDPSK 身份字符串,默认esp-coap-demo

setup()的启动链条(源码 greenhouse_server.ino#L158-L227):

  1. 组建网络DataSet ds; ds.initNew();后设置网络名ESP_OT_CoAP_Greenhouse、信道、PAN ID、网络密钥,依次OThread.commitDataSet(ds)OThread.networkInterfaceUp()OThread.start(),再轮询OThread.otGetDeviceRole() >= OT_ROLE_CHILD确认附着(30 秒超时,超时则OThread.stop()后 2 秒重试)。
  2. 启动 CommissionerOThread.startCommissioner()成功后调用OThread.addJoiner(PSKD, JOINER_WINDOW_SEC),串口输出Commissioner ready (PSKd "J01NME")。这是客户端能够入网的前提——必须先烧录服务端并等到该提示
  3. 注册明文资源OThreadCoAPServer.on("greenhouse/temp", OT_COAP_METHOD_GET, onGreenhouseTemp, &s_state)等两条,然后OThreadCoAPServer.begin()
  4. 条件注册 CoAPS 资源:只有OThreadCoAP::secureApiEnabled()为 true(即固件编译时开启了OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE)才执行OThreadCoAPSecureServer.setPSK(...)、注册valve/waterfan/speedbegin();否则打印CoAPS is not enabled in this build,仅保留明文遥测。这保证了同一份代码在未开启 CoAPS 的固件上也能降级运行。

资源处理函数遵循 OThreadCoAP.h 中OThreadCoAPHandler回调约定:检查req.method()是否匹配,不匹配则回405;PUT 处理函数用parsePercent()校验 payload 在 0–100 范围内,越界回400 Bad Request(payload 为"0-100"),合法则更新状态并回204 Changed。每个请求还会由logRequestKind()打印[CON][NON]标记与对端 IPv6 地址,便于在串口确认客户端使用的可靠性等级。

loop()中模拟环境漂移:温度每次random(-5,6)/10.0f随机游走并按fanSpeed/200降温(风扇开得越大降温越多),钳制在 18–32 °C;光照在 2000–20000 lux 间随机游走。注意状态是纯内存的(static GreenhouseState s_state),服务端重启后阀门/风扇状态丢失,这也是故障排查表中"服务端重启后需复位客户端重新入网"一行的根源。

客户端:Joiner 入网与 8 秒/24 秒双周期控制

客户端 sketch 位于 greenhouse_client.ino,关键常量与自动化阈值:

常量含义
PSKD必须与服务端 Commissioner 一致的J01NME
CHANNEL_HINT802.15.4 信道提示,默认 15
JOIN_TIMEOUT_MSstartJoiner()内部等待 Commissioner 的最长时间(60000 ms)
TELEMETRY_MS明文 NON 轮询周期(默认 8000 ms)
CONTROL_MSCoAPS 控制循环周期(默认 24000 ms)
TEMP_FAN_ON_C温度超过 26.0 °C 时风扇设为 75%,否则 15%
LIGHT_VALVE_LUX光照低于 8000 lux 时阀门设为 60%,否则 10%
COAP_PSK/COAP_PSK_ID16 字节 PSK 与身份串,必须与服务端完全一致

入网流程joinNetwork()(源码):OThread.setChannel(CHANNEL_HINT)networkInterfaceUp()OThread.startJoiner(PSKD, JOIN_TIMEOUT_MS)(返回非OT_ERROR_NONEJoiner failed: N)→OThread.start()后等待附着。客户端只在setup()里入网,失败则OThread.stop()后 3 秒重试;如果它先于服务端 Commissioner 启动而开机,需要手动复位。

loop()是两个周期叠加:

// 每 8 s:明文 NON 遥测 PlainClient.setConfirmable(false); int code = PlainClient.GET(serverIp, "greenhouse/temp"); // <0 时 OThreadCoAP::errorToString(code) lastTempC = PlainClient.getString().toFloat(); // 每 24 s:自动化 + CoAPS CON PUT(前提:首次遥测成功后 lastTempC > 0) SecureClient.connect(serverIp); // DTLS 握手,失败打印 "DTLS connect failed." SecureClient.setConfirmable(true); sendSecurePut("fan/speed", fanSpeed); // temp > 26 C -> 75% sendSecurePut("valve/water", valveOpen); // light < 8000 -> 60% SecureClient.disconnect();

对应源码中pollTelemetry()(greenhouse_client.ino#L97-L116)与runControlLoop()(greenhouse_client.ino#L130-L151)。几个值得注意的实现细节:

  • 控制前门控loop()if (lastTempC > 0.0f && OThreadCoAP::secureApiEnabled())才执行控制循环,所以"前 24 秒看不到控制输出"是设计行为——它在等待第一次成功的 NON 遥测(lastTempC初始为 0.0f)。
  • DTLS 会话生命周期:每个控制周期connect()→ 两次PUTdisconnect(),即每 24 秒重新握手一次,而不是保持长连接。这与 OThreadCoAPSecureClient 的约束呼应:每设备仅支持一个活动 CoAPS 客户端会话,且同一设备上不能同时运行OThreadCoAPSecureClient::connect()OThreadCoAPSecureServer——本示例因此把 CoAPS 服务器放在服务端板、CoAPS 客户端放在客户端板。
  • 超时配置:明文客户端PlainClient.setTimeout(3000);安全客户端SecureClient.setConnectTimeout(10000)(DTLS 握手)与setTimeout(5000)(请求等待,CoAPS 下仅限制 sketch 等待,线上重传走栈默认值,见头文件对setTimeout的注释 OThreadCoAP.h#L681-L687)。

运行步骤与演示凭据

运行前提(来自总览 README):

  1. 启用 CoAPS 构建选项(见下文"启用 CoAPS"一节;若只跑明文遥测可跳过)。
  2. 先烧录greenhouse_server,等待串口出现Commissioner ready以及明文(5683)/CoAPS(5684)两条监听提示。
  3. 在第二块板烧录 greenhouse_client。
  4. 两块板均用 115200 波特率打开串口监视器:客户端每 8 秒轮询遥测,并在首次遥测成功后每 24 秒发送 CON 控制命令。

两块 sketch 的 CI 配置也印证了各自需要的配置项:greenhouse_server/ci.yml 声明了CONFIG_OPENTHREAD_ENABLED=yCONFIG_SOC_IEEE802154_SUPPORTED=yCONFIG_OPENTHREAD_COMMISSIONER=y

必需的 IDF 特性(sdkconfig)

特性用途
CONFIG_OPENTHREAD_ENABLED=y编入 OpenThread 协议栈
CONFIG_SOC_IEEE802154_SUPPORTED=ySoC 具备 802.15.4 射频
CONFIG_OPENTHREAD_COMMISSIONER=ygreenhouse_server使用
CONFIG_OPENTHREAD_JOINER=ygreenhouse_client使用
OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE=15684 端口 CoAPS 执行器
MBEDTLS_KEY_EXCHANGE_PSK_ENABLEDPSK 密码套件

共享演示凭据

参数
网络名ESP_OT_CoAP_Greenhouse
信道15
PAN ID0xBEE5
Thread Joiner PSKdJ01NME
CoAPS PSK idesp-coap-demo

明文 CoAP 遥测(5683)在不开任何 CoAPS 构建标志的情况下即可工作;执行器 PUT 命令则必须开启。运行期可用OThreadCoAP::secureApiEnabled()判断当前固件是否包含 CoAPS(OThreadCoAP.h#L162-L166 注释明确其返回OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE的编译状态)。

支持的目标

两个 sketch 均声明支持(见 greenhouse_server/README.md):

SoCThread状态
ESP32-H2支持
ESP32-C6支持
ESP32-C5支持

启用 CoAPS:以 ESP-IDF 组件方式构建 Arduino

标准 Arduino IDE 构建的固件可能不含 CoAPS 安全 API。如需 5684 端口上的 CoAPS 执行器,需要把 sketch 当作ESP-IDF 工程(Arduino 作为 component)构建,再在 menuconfig 中开启特性。可参考仓库文档 docs/esp-idf_component.rst(Arduino as an ESP-IDF component)。操作流程:把 sketch 拷入工程main/目录(.ino改名.cpp),执行idf.py set-target <soc>,然后idf.py menuconfig

需要开启的选项:

menuconfig 路径设置
Component config → OpenThreadOpenThread
Component config → OpenThread → Thread Core FeaturesEnable Commissioner(greenhouse_server)
Component config → OpenThread → Thread Core FeaturesEnable Joiner(greenhouse_client)
Component config → mbedTLS → TLS Key Exchange MethodsEnable pre-shared-key ciphersuites
Component config → mbedTLS → TLS Key Exchange MethodsEnable PSK based ciphersuite modes
Component config → OpenThread → Thread Extensioned FeaturesUse a header file defined by customer

关键点:CoAPS API 标志OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE不是menuconfig 里的直接开关。使用自定义 OpenThread 头文件时,在其中定义:

#define OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE 1

把该头文件路径填到Thread Extensioned Features → OpenThread Custom Header Config,保存后执行idf.py build flash monitor

预期串口输出

服务端(CoAPS 可用时):

=== CoAP Greenhouse — server === Forming Thread network... Waiting for attach.. Attached as Leader. Starting Commissioner... Commissioner ready (PSKd "J01NME") Starting CoAP servers... Ready. Plain CoAP on port 5683: GET greenhouse/temp, greenhouse/light CoAPS on port 5684: PUT valve/water, fan/speed (PSK id "esp-coap-demo") Mesh-local: fdde:ad00:beef:0:.... [NON] GET greenhouse/temp from fdde:ad00:beef:0:.... [CoAPS CON] PUT fan/speed from fdde:ad00:beef:0:.... Fan -> 75% Valve -> 60%

客户端:

=== CoAP Greenhouse — client === Joining Thread network (Joiner)... Commissioning with PSKd "J01NME"... Waiting for attach.. Attached as Child. Greenhouse server: fdde:ad00:beef:0:0:ff:fe00:0 Ready. Polling telemetry and running control loop. [plain NON] temp=24.3 C [plain NON] light=11500 lux Control: temp=24.3 fan=15% light=11500 valve=10% [CoAPS CON] PUT fan/speed=15 -> 2.04 Changed [CoAPS CON] PUT valve/water=10 -> 2.04 Changed

若固件未含 CoAPS,两端都会打印CoAPS is not enabled in this build(服务端遥测继续、客户端跳过安全控制);若入网失败,客户端会打印Joiner failed: 7并每 3 秒重试。

故障排查

启动顺序是第一位的排查项:先烧录 greenhouse_server,等Commissioner ready与两条监听提示后再烧录/复位 greenhouse_client;若客户端在 Commissioner 激活前开机,需复位。

症状可能原因
客户端Join failed先烧录 greenhouse_server并等待 Commissioner ready,之后复位客户端
CoAPS is not enabled in this build固件缺少 CoAPS。按上文"启用 CoAPS"以 ESP-IDF 组件方式重新构建;明文 5683 遥测不受影响
明文 GET 正常、CoAPS PUT 失败CoAPS 构建标志缺失或 PSK 不匹配——5683 遥测可以在无安全构建下工作,两者应分开判断
客户端跳过控制输出等待首次成功的 NON 遥测(lastTempC必须非零)
客户端入网后服务端复位复位客户端重新入网;服务端状态存于内存,重启即丢失
服务端CoAPS server start failed构建已启用 CoAPS 但OThreadCoAPSecureServer.begin()失败(PSK、端口或附着问题)
服务端Plain CoAP server start failedOpenThread 未附着
CON PUT ... failed: Timeout服务端 CoAPS 未运行或节点已脱离 Mesh

相关示例

  • CoAP Secure:仅 CoAPS 的最小演示(secure_server / secure_client)。
  • Native CoAP 示例总览:所有原生 CoAP 演示与端口约定,还列出了setPort()useDefaultCoapRetransmit()joinMulticastGroup()等本示例未展示的高级选项。
  • OThreadCoAP.h:OThreadCoAPClient/OThreadCoAPServerClass/OThreadCoAPSecureClient/OThreadCoAPSecureServerClass的完整 API 与约束(单例服务器、单 CoAPS 会话、错误码OT_COAP_ERROR_*等)。

以上示例代码与文档均以 Apache License 2.0 发布。

【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 10:40:49

SMP语言开发环境备份与恢复最佳实践

1. SMP语言备份恢复机制概述在SMP&#xff08;软件制作平台&#xff09;开发环境中&#xff0c;语言基础知识的备份与恢复是保障开发连续性的重要环节。不同于常规文件备份&#xff0c;SMP语言元素的备份需要处理语法结构、编译规则和运行时上下文等特殊数据。我曾参与过多个企…

作者头像 李华
网站建设 2026/9/14 10:37:47

InstaPy 如何手动指定 geckodriver 二进制路径?

InstaPy 如何手动指定 geckodriver 二进制路径&#xff1f; 【免费下载链接】InstaPy &#x1f4f7; Instagram Bot - Tool for automated Instagram interactions 项目地址: https://gitcode.com/GitHub_Trending/in/InstaPy InstaPy 通过 Selenium 驱动 Firefox 完成自…

作者头像 李华
网站建设 2026/9/14 10:37:27

新能源电力系统调度:鲁棒优化与N-1安全校验实践

1. 项目背景与核心挑战在新能源占比持续攀升的现代电力系统中&#xff0c;调度决策面临着前所未有的复杂性。传统调度方法在高比例可再生能源场景下暴露出三个致命缺陷&#xff1a;面对风电、光伏出力的强随机性时要么过于保守导致经济性损失&#xff0c;要么过于激进带来运行风…

作者头像 李华