Arduino-ESP32 官方 Zigbee OTA Client 示例实战:构建支持无线固件升级的 On/Off 智能灯
2026/9/14 5:50:57 网站建设 项目流程

Arduino-ESP32 官方 Zigbee OTA Client 示例实战:构建支持无线固件升级的 On/Off 智能灯

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

导读

本篇文章以 Arduino-ESP32 官方示例 Zigbee_OTA_Client 为核心,完整讲解如何在 ESP32-C6 / ESP32-H2 上构建一个具备 OTA(Over-The-Air)在线升级能力的 Zigbee 终端设备(End Device)智能灯。文章将带你走通从 Arduino IDE 配置、分区方案选择、源码逐行解析,到设备入网与无线升级的完整链路,并深入ZigbeeEP库源码剖析 OTA Client 的底层实现(版本协商、查询定时器、协调器匹配流程),让你既能在板子上跑通示例,也能理解其背后的 Zigbee OTA 协议机制。


一、示例概览:一个会“自升级”的 Zigbee 智能灯

Zigbee 是智能家居领域最主流的低功耗无线协议之一,其设备分为协调器(Coordinator)、路由器(Router)和终端设备(End Device,ED)三种角色。在传统 Zigbee 开发中,终端设备一旦烧录完成,固件升级往往需要重新接线、重新烧录,这在已安装到位的智能家居场景中非常痛苦。

Zigbee_OTA_Client示例正是为了解决这一问题而生:它将 ESP32 配置为一个Zigbee ED 终端设备,挂载OTA Upgrade Client 集群,既能作为Home Automation(HA)On/Off Light(开/关灯)被协调器控制,又能主动向协调器上的 OTA Server 查询并下载新固件,实现无线升级。

示例的工程结构如下:

文件作用
Zigbee_OTA_Client.ino主程序:创建 On/Off 灯端点、注册 OTA Client、处理灯控与按键逻辑
README.md官方使用说明:支持目标、Arduino IDE 配置步骤、故障排查
ci.ymlCI 编译验证配置,可用于确认示例的编译要求

支持的目标芯片

当前示例仅支持具备IEEE 802.15.4 射频(即 Zigbee 射频)的芯片。README 明确列出:

支持目标ESP32-C6ESP32-H2
支持情况

这一限制在 ci.yml 中得到印证——CI 要求CONFIG_SOC_IEEE802154_SUPPORTED=y(芯片带 802.15.4 射频)以及CONFIG_ZB_ENABLED=y(使能 Zigbee 协议栈)。

硬件要求

  • 一块 ESP32-C6 或 ESP32-H2 开发板;
  • 一条质量良好的 USB 数据线,用于供电和程序烧录。

提示:开发板上通常自带 RGB LED 和 BOOT 按键,本示例默认直接使用它们(RGB_BUILTINBOOT_PIN),因此无需额外接线即可跑通。


二、Arduino IDE 配置:四个关键选项一个都不能少

在编译烧录前,必须在 Arduino IDE 中完成以下配置(这也是示例源码开头#ifndef ZIGBEE_MODE_ED编译期检查所强制要求的):

  1. 选择开发板Tools -> Board,选择对应的 ESP32-C6 / ESP32-H2 开发板型号;
  2. 选择 Zigbee 模式为终端设备Tools -> Zigbee mode: Zigbee ED (end device)
  3. 选择 Zigbee 专用分区方案Tools -> Partition Scheme: Zigbee 4MB with spiffs
  4. 选择串口Tools -> Port: xxx,其中xxx为实际检测到的 COM 口;
  5. (可选)开启调试日志Tools -> Core Debug Level: Verbose,可查看 Zigbee 协议栈全量日志,便于排查入网、OTA 过程中的问题。

这些菜单选项在仓库中的真实含义

在 boards.txt 中可以看到上述菜单的真实定义。以 ESP32-H2 为例(ESP32-C6 定义完全相同):

esp32h2.menu.ZigbeeMode.default=Disabled esp32h2.menu.ZigbeeMode.default.build.zigbee_mode= esp32h2.menu.ZigbeeMode.ed=Zigbee ED (end device) esp32h2.menu.ZigbeeMode.ed.build.zigbee_mode=-DZIGBEE_MODE_ED esp32h2.menu.ZigbeeMode.ed.build.zigbee_libs=-lesp_zb_api.ed -lzboss_stack.ed -lzboss_port.native esp32h2.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) esp32h2.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR esp32h2.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.native
  • 选择Zigbee ED (end device)时,编译系统会自动定义ZIGBEE_MODE_ED-DZIGBEE_MODE_ED)并链接 ED 版本的 Zigbee 协议栈库(-lesp_zb_api.ed -lzboss_stack.ed);
  • 若未选择该模式,Zigbee_OTA_Client.ino 中的#error "Zigbee end device mode is not selected in Tools->Zigbee mode"会直接导致编译失败,从编译期杜绝误用。

分区方案同理,esp32c6.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs(见 boards.txt)提供了同时容纳 Zigbee 协议栈 NVS 数据与 OTA 双分区(运行镜像 + 下载镜像)的 4MB 布局。


三、硬件引脚配置:让 LED 适配你的开发板

示例默认使用开发板自带 RGB LED(RGB_BUILTIN)和 BOOT 按键(BOOT_PIN):

uint8_t led = RGB_BUILTIN; uint8_t button = BOOT_PIN;

如果你使用的是普通单色 LED,需要两处调整:

  1. led改为实际连接的 GPIO,例如uint8_t led = 2;
  2. setLED()中的控制方式由rgbLedWrite()改为digitalWrite()

值得注意的是:示例源码中的setLED()直接使用了digitalWrite(),而注释明确指出——“如果LED_PIN == RGB_BUILTINrgbLedWrite()会在底层被自动调用”。这意味着:

  • 使用板载 RGB LED 时,digitalWrite会经由 Arduino 核心的 RGB 支持被路由到正确的 RGB 通道;
  • 使用外接普通 LED 时,digitalWrite就是普通的 GPIO 高低电平输出。

初始化部分(见 Zigbee_OTA_Client.ino)将 LED 引脚设为输出并初始化为关闭(LOW),将按键设为INPUT_PULLUP上拉输入,等待按键触发。


四、源码逐段解析:从建端点、注册 OTA Client 到自动查询升级

4.1 定义 OTA 版本号:固件版本协商的关键

#define OTA_UPGRADE_RUNNING_FILE_VERSION 0x01010100 // 当前运行镜像的版本号 #define OTA_UPGRADE_DOWNLOADED_FILE_VERSION 0x01010101 // 已下载镜像的版本号 #define OTA_UPGRADE_HW_VERSION 0x0101 // 硬件版本号

这三个宏直接对应 Zigbee OTA Upgrade 集群中用于版本协商的关键属性:OTA Server 会比较客户端上报的FileVersion与服务器上的镜像版本,只有服务器版本更新时才会发起升级。因此:

  • 每次发布新固件时,必须递增OTA_UPGRADE_RUNNING_FILE_VERSION(源码注释明确要求 "Increment this value when the running image is updated"),否则 OTA Server 会认为设备已是最新版本而不下发新镜像;
  • OTA_UPGRADE_HW_VERSION用于区分不同硬件版本,避免给硬件不匹配的设备推送镜像。

4.2 创建 On/Off 灯端点并注册

#define ZIGBEE_LIGHT_ENDPOINT 1 ZigbeeLight zbLight = ZigbeeLight(ZIGBEE_LIGHT_ENDPOINT);

ZigbeeLight是 Arduino-ESP32 Zigbee 库提供的 Home AutomationOn/Off Light端点类,其实现位于 ZigbeeLight.cpp:构造函数内部将_device_id设为ESP_ZB_HA_ON_OFF_LIGHT_DEVICE_ID,并调用esp_zb_on_off_light_clusters_create()创建 On/Off 集群。当协调器下发开/关命令时,库会自动调用zbAttributeSet()解析ESP_ZB_ZCL_CLUSTER_ID_ON_OFF集群的ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID属性,最终触发onLightChange()注册的用户回调(见 ZigbeeLight.h)。

4.3 挂载 OTA Client 并注册状态回调

zbLight.addOTAClient(OTA_UPGRADE_RUNNING_FILE_VERSION, OTA_UPGRADE_DOWNLOADED_FILE_VERSION, OTA_UPGRADE_HW_VERSION); zbLight.onOTAStateChange(otaActiveCallback);

addOTAClient()的完整签名定义在 ZigbeeEP.h:

bool addOTAClient( uint32_t file_version, uint32_t downloaded_file_ver, uint16_t hw_version, uint16_t manufacturer = 0x1001, uint16_t image_type = 0x1011, uint8_t max_data_size = 223 );
参数默认值说明
file_version必填当前运行固件镜像的版本号
downloaded_file_ver必填已下载镜像的版本号
hw_version必填硬件版本号
manufacturer0x1001厂商代码
image_type0x1011镜像类型代码
max_data_size223OTA 单次传输最大数据块大小(默认且推荐值)

onOTAStateChange()注册的回调在 OTA 启动/结束时被触发,示例中用它打印 "OTA started" / "OTA finished" 日志,并将状态存入otaRunning标志——该标志同时被按键长按逻辑用于防止在 OTA 进行中误触发工厂复位(见源码 Zigbee_OTA_Client.ino)。

4.4 底层实现:addOTAClient 如何构建 OTA 集群

从源码看,addOTAClient()(ZigbeeEP.cpp)主要完成四件事:

  1. 用传入的file_versiondownloaded_file_vermanufacturerimage_type填充esp_zb_ota_cluster_cfg_t并创建 OTA 集群(esp_zb_ota_cluster_create);
  2. 配置 OTA Client 运行变量esp_zb_zcl_ota_upgrade_client_variable_t:查询定时器使用ESP_ZB_ZCL_OTA_UPGRADE_QUERY_TIMER_COUNT_DEF默认值,hw_versionmax_data_size取自入参;
  3. 添加客户端数据、OTA Server 地址(初始0xffff)与 Server 端点(初始0xff)三个属性;
  4. 通过esp_zb_cluster_list_add_ota_cluster()Client 角色挂载到端点集群列表。

任一步骤失败都会返回false并打印错误日志,帮助定位配置问题。

4.5 启动网络并等待入网

if (!Zigbee.begin()) { ... ESP.restart(); } while (!Zigbee.connected()) { Serial.print("."); delay(100); }

Zigbee.begin()启动 Zigbee 协议栈,之后程序阻塞等待设备成功加入协调器网络(Zigbee.connected()返回 true)。入网成功后,示例才调用requestOTAUpdate()开始 OTA 查询——因为 OTA Server 匹配需要已建立网络连接。

4.6 requestOTAUpdate:如何找到协调器上的 OTA Server

zbLight.requestOTAUpdate();

源码注释指出其行为:首次 OTA 查询在入网后 1 分钟内发起,之后每小时自动重复一次。从 ZigbeeEP.cpp 的实现看,该函数的核心机制是:

  1. 构造ZDO Match Descriptor 请求,目标地址为0x0000(协调器短地址),匹配集群为ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE(OTA Upgrade 集群),Profile 为ESP_ZB_AF_HA_PROFILE_ID
  2. 通过esp_zb_bdb_dev_joined()确认设备已入网后,调用esp_zb_zdo_match_cluster()广播查询;
  3. 匹配成功后回调findOTAServer()(ZigbeeEP.cpp):先通过esp_zb_ota_upgrade_client_query_interval_set()设置查询间隔,再调用esp_zb_ota_upgrade_client_query_image_req(addr, endpoint)向 OTA Server 发起镜像查询;
  4. 若未找到 OTA Server,则打印 "No OTA Server found" 日志。

注意:示例的requestOTAUpdate()面向**协调器(0x0000)**上的 OTA Server 设计。如果你的 OTA Server 运行在路由器或其他节点上,需要自行调整req.addr_of_interestreq.dst_nwk_addr

4.7 loop():按键控制与工厂复位

主循环实现了两个功能(Zigbee_OTA_Client.ino):

  • 短按:切换灯的开关状态(zbLight.setLight(!zbLight.getLightState()));
  • 长按 ≥3 秒:先做防抖延时,若 OTA 正在进行则拒绝复位;否则打印日志、延时 1 秒后调用Zigbee.factoryReset()将设备与 Zigbee 协议栈恢复出厂设置并重启。

五、Coordinator 侧与 OTA Server 的准备

本示例是一个Client,它的 OTA 升级流程依赖网络中存在OTA Server(通常在协调器上运行)。要让示例真正完成一次无线升级,你还需要:

  1. 烧录一个支持OTA Server的协调器固件(例如官方其他 Zigbee 示例中的 OTA Server 角色设备),并让 ESP32-C6/H2 终端设备成功入网;
  2. 将编译生成的新版本固件镜像按 Zigbee OTA 镜像格式放置在协调器的 OTA Server 中,且镜像版本必须高于设备当前的OTA_UPGRADE_RUNNING_FILE_VERSION
  3. 确认设备与 OTA Server 的manufacturer/image_type/hw_version匹配(本示例使用默认0x1001/0x1011,硬件版本0x0101)。

六、入网失败排查与网络管理 API

6.1 经典问题:终端设备无法加入协调器网络

README 明确指出:如果终端设备无法连接协调器,请先擦除终端设备 flash 再烧录,尤其在重新烧录过协调器之后必须这样做。两种做法任选其一:

  1. Arduino IDE 图形化操作Tools -> Erase All Flash Before Sketch Upload设为Enabled,再重新上传;
  2. 代码方式:在 sketch 中调用Zigbee.factoryReset();(声明见 ZigbeeCore.h,可带参数bool restart = true控制是否重启),复位设备与 Zigbee 协议栈。

6.2 协调器重启后网络默认关闭

默认情况下,协调器在重启或烧录新固件后会关闭入网窗口。终端设备因此无法加入。打开网络有两种方式:

// 方式一:重启后自动打开网络(必须在 Zigbee.begin() 之前调用) Zigbee.setRebootOpenNetwork(time); // 方式二:运行期间随时打开网络 Zigbee.openNetwork(time);

两个 API 均声明于 ZigbeeCore.h,参数time为网络保持打开的时间。openNetwork()还配对了closeNetwork()用于手动关闭。

6.3 通用排查清单

  • LED 不闪烁:检查 LED 接线与LED_PIN引脚号选择是否正确;
  • 烧录失败:降低串口连接速率后重试;
  • 串口检测不到:检查 USB 数据线(务必使用带数据传输能力的线)及 USB 转串口驱动是否安装。

另外请务必保证使用质量良好的 USB 线稳定可靠的供电——Zigbee 射频对供电波动较敏感,劣质 USB 线是很多偶发问题的根源。


七、CI 配置揭示的编译前提

示例附带的 ci.yml 揭示了自动化验证该示例所需的完整编译条件,这也是本地手动编译时的硬性前提:

fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed requires: - CONFIG_SOC_IEEE802154_SUPPORTED=y - CONFIG_ZB_ENABLED=y
  • PartitionScheme=zigbee对应Zigbee 4MB with spiffs分区方案;
  • ZigbeeMode=ed对应Zigbee ED (end device)终端设备模式;
  • 芯片必须支持 802.15.4(即 ESP32-C6 / ESP32-H2),且必须使能 Zigbee 协议栈。

这与 Arduino IDE 中的手动配置完全一致——终端设备模式 + Zigbee 分区方案是运行本示例不可跳过的组合。


八、总结与下一步

通过本示例,你可以掌握一条完整的Zigbee 终端设备 OTA 升级开发链路

  1. 正确配置Zigbee ED 模式Zigbee 4MB with spiffs 分区
  2. 使用ZigbeeLight快速创建 HA On/Off 灯端点,并通过onLightChange()响应协调器指令;
  3. 通过addOTAClient()一次调用完成 OTA Client 集群挂载,理解file_version/hw_version在版本协商中的作用;
  4. 借助requestOTAUpdate()的"入网 1 分钟内首查、之后每小时轮询"机制实现固件自动升级;
  5. 掌握factoryReset()setRebootOpenNetwork()openNetwork()等网络生命周期管理 API,解决入网与升级中的常见问题。

想要继续深入,建议接着阅读:

  • Zigbee 库核心头文件 Zigbee.h 与端点基类 ZigbeeEP.h,了解ZigbeeLight之外更多 HA 端点类型;
  • OTA 底层实现 ZigbeeEP.cpp 中addOTAClient()requestOTAUpdate()的完整调用链;
  • 官方 ESP-IDF Zigbee OTA 文档,了解 OTA 镜像格式与 Server 端配置细节。

当你将示例中的默认版本号递增、配合协调器侧的 OTA Server 部署后,你的 Zigbee 智能灯就真正具备了"通电即用、远程升级"的生产级能力。

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

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询