
arduino-esp32 Zigbee 模拟量输入输出实战用 ZigbeeAnalog 类在 ESP32-C6/H2 上构建 HA 模拟量设备【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32本文以仓库中的 Zigbee_Analog_Input_Output 示例 为主体完整讲解如何在 arduino-esp32 核心上配置一台 Zigbee 从设备End Device使其以 Home AutomationHA模拟量输入/输出设备身份入网上报 ADC 读数、接收网络侧的模拟量输出指令。读完本篇你可以掌握 Arduino IDE 下 Zigbee 编译模式与分区表的选择、ZigbeeAnalog 端点类的簇配置与报告机制以及组网失败、网络关闭等典型问题的排查手段。一、示例概述与适用目标该示例演示如何配置一台 Zigbee 从设备作为 HA 模拟量输入/输出设备工作板子上电后示例会在本地配置为“模拟量值变化时或每 30 秒”上报一次模拟量输入按下板上的 BOOT 按键设备会立即把当前测量值上报到网络。根据 README当前示例支持的目标芯片为支持目标说明ESP32-C6支持 2.4 GHz Zigbee Radio可在boards.txt中找到ZigbeeMode.ed/ZigbeeMode.zczr编译选项ESP32-H2支持 2.4 GHz Zigbee Radio802.15.4同样提供 ED / ZCZR 两种编译模式示例目录结构如下Zigbee_Analog_Input_Output.ino示例主体代码ci.ymlCI 编译配置声明了fqbn_append: PartitionSchemezigbee_zczr,ZigbeeModezczr并要求CONFIG_ZB_ENABLEDy即 CI 使用 ZCZR 模式 Zigbee ZCZR 分区表编译这为本文第四节的编译配置提供了直接的仓库证据。二、硬件准备与引脚配置硬件需求很简单一根 USB 线同时用于供电和烧录。示例中涉及的两个引脚在 Zigbee_Analog_Input_Output.ino 中定义可按需修改uint8_t analogPin A0; // ADC 输入引脚默认 A0 uint8_t button BOOT_PIN; // 按键引脚默认 BOOT_PINESP32-C6/H2 板上的 BOOT 按键analogPin连接模拟传感器如分压后的电位器的 ADC 引脚示例中将其解析度设置为 10 位analogReadResolution(10)button使用内部上拉INPUT_PULLUP按下为低电平用于触发即时上报和长按出厂复位。官方提醒务必使用质量可靠的 USB 线和稳定的电源不稳定的供电是 Zigbee 入网失败最常见的原因之一。三、Arduino IDE 编译与烧录配置按 README 的说明编译前需要在 IDE 中完成以下选择选择正确的开发板Tools - Board选择 Zigbee 模式Tools - Zigbee mode: ...选择 Zigbee 分区表Tools - Partition Scheme: Zigbee 4MB with spiffs选择串口号Tools - Port: xxx可选将Tools - Core Debug Level设为Verbose可以看到 Zigbee 协议栈的全部日志。关于 Zigbee mode 的一个重要修正以当前仓库源码为准README 文字提到选择 “Zigbee ED (end device)”但当前示例代码开头有一个编译期硬检查#ifndef ZIGBEE_MODE_ZCZR #error Zigbee coordinator/router device mode is not selected in Tools-Zigbee mode #endif从 boards.txt 可见ZigbeeMode.zczr选项才会向构建传入-DZIGBEE_MODE_ZCZR而ZigbeeMode.ed传入的是-DZIGBEE_MODE_ED会触发上面的#errorci.yml 也明确使用ZigbeeModezczr。因此按当前代码实际编译时应选择Zigbee ZCZR (coordinator/router)模式——该模式同时支持协调器/路由器角色。运行时角色示例在setup()中调用Zigbee.begin(ZIGBEE_ROUTER)即以路由器角色入网。角色枚举定义在 ZigbeeCore.hZIGBEE_COORDINATOR 0、ZIGBEE_ROUTER 1、ZIGBEE_END_DEVICE 2。分区表README 要求选择Zigbee 4MB with spiffsboards.txt中 C6/H2 还提供了Zigbee 2MB with spiffs、Zigbee 8MB with spiffs以及面向协调器/路由器的Zigbee ZCZR 4MB with spiffsPartitionScheme.zigbee_zczr等变体。由于本示例以 ZCZR 模式编译建议与 CI 保持一致选择Zigbee ZCZR 4MB with spiffs若你的板子是 2MB/8MB Flash再按容量切换。四、示例源码逐段精讲4.1 端点定义一个“模拟量输入输出”端点与三个纯输入端点示例注册了 4 个ZigbeeAnalog端点端点号从 1 开始连续分配源码#define ANALOG_DEVICE_ENDPOINT_NUMBER 1 ZigbeeAnalog zbAnalogDevice ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER); // EP1: 输入输出 ZigbeeAnalog zbAnalogTemp ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER 1); // EP2: 温度 ZigbeeAnalog zbAnalogFan ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER 2); // EP3: 转速 ZigbeeAnalog zbAnalogPercent ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER 3); // EP4: 百分比这种“同一 ADC 采样值拆分成多个不同应用类型的端点”的写法是为了让 HA 控制器能分别识别为不同的物理量。setup()中的完整配置如下void setup() { Serial.begin(115200); pinMode(button, INPUT_PULLUP); analogReadResolution(10); // ADC 解析度设为 10 位 // EP1模拟输入 功耗瓦特模拟输出 风扇转速RPM zbAnalogDevice.setManufacturerAndModel(Espressif, ZigbeeAnalogDevice); zbAnalogDevice.addAnalogInput(); zbAnalogDevice.setAnalogInputApplication(ESP_ZB_ZCL_AI_POWER_IN_WATTS_CONSUMPTION); zbAnalogDevice.setAnalogInputDescription(Power Consumption (Watts)); zbAnalogDevice.setAnalogInputResolution(0.01); zbAnalogDevice.addAnalogOutput(); zbAnalogDevice.setAnalogOutputApplication(ESP_ZB_ZCL_AI_RPM_OTHER); zbAnalogDevice.setAnalogOutputDescription(Fan Speed (RPM)); zbAnalogDevice.setAnalogOutputResolution(1); // HA 会用 Min/Max PresentValue 属性限制模拟输出的取值范围 zbAnalogDevice.setAnalogOutputMinMax(-10000, 10000); // -10000 ~ 10000 RPM // 网络侧修改模拟输出值时的本地回调 zbAnalogDevice.onAnalogOutputChange(onAnalogOutputChange); // EP2温度 zbAnalogTemp.addAnalogInput(); zbAnalogTemp.setAnalogInputApplication(ESP_ZB_ZCL_AI_TEMPERATURE_OTHER); zbAnalogTemp.setAnalogInputDescription(Temperature); zbAnalogTemp.setAnalogInputResolution(0.1); // EP3转速 zbAnalogFan.addAnalogInput(); zbAnalogFan.setAnalogInputApplication(ESP_ZB_ZCL_AI_RPM_OTHER); zbAnalogFan.setAnalogInputDescription(RPM); zbAnalogFan.setAnalogInputResolution(1); // EP4百分比 zbAnalogPercent.addAnalogInput(); zbAnalogPercent.setAnalogInputApplication(ESP_ZB_ZCL_AI_PERCENTAGE_OTHER); zbAnalogPercent.setAnalogInputDescription(Percentage); zbAnalogPercent.setAnalogInputResolution(0.01); // 把 4 个端点注册进 Zigbee 核心 Zigbee.addEndpoint(zbAnalogDevice); Zigbee.addEndpoint(zbAnalogTemp); Zigbee.addEndpoint(zbAnalogFan); Zigbee.addEndpoint(zbAnalogPercent); // 所有端点注册完毕后启动协议栈路由器角色 if (!Zigbee.begin(ZIGBEE_ROUTER)) { Serial.println(Zigbee failed to start!); ESP.restart(); } // 阻塞等待入网 while (!Zigbee.connected()) { Serial.print(.); delay(100); } // 配置自动报告最小间隔 0s、最大间隔 30s、变化量阈值 10 zbAnalogDevice.setAnalogInputReporting(0, 30, 10); }几点说明setAnalogInputApplication/setAnalogOutputApplication传入的是 ZCL 规范中的“应用类型”Application Type常量例如ESP_ZB_ZCL_AI_POWER_IN_WATTS_CONSUMPTION、ESP_ZB_ZCL_AI_TEMPERATURE_OTHER、ESP_ZB_ZCL_AI_RPM_OTHER、ESP_ZB_ZCL_AI_PERCENTAGE_OTHER完整取值可查阅 ESP-IDF 的esp_zigbee_zcl_analog_input.h/esp_zigbee_zcl_analog_output.hsetAnalogInputResolution告诉协调器/控制器“本设备的量化步长”示例中功耗端点精度 0.01 W、温度端点 0.1、转速端点 1setAnalogOutputMinMax(-10000, 10000)用于向 HA 声明输出量的合法范围控制器据此做限幅setAnalogInputReporting(min_interval, max_interval, delta)配置 ZCL Report Attributes 机制值变化超过delta10即可在min_interval到max_interval0~30 秒窗口内自动上报这正是 README 中“值变化时或每 30 秒上报一次”的实现来源。4.2 主循环周期采样上报与按键交互void onAnalogOutputChange(float analog_output) { Serial.printf(Received analog output change: %.1f\r\n, analog_output); } void loop() { static uint32_t timeCounter 0; // 每 2s100ms x 20读一次 ADC更新 4 个端点的模拟量并主动上报 if (!(timeCounter % 20)) { float analog (float)analogRead(analogPin); Serial.printf(Updating analog input to %.1f\r\n, analog); zbAnalogDevice.setAnalogInput(analog); zbAnalogTemp.setAnalogInput(analog / 100); zbAnalogFan.setAnalogInput(analog); zbAnalogPercent.setAnalogInput(analog / 10); // 主动触发 Report Attributes zbAnalogDevice.reportAnalogInput(); zbAnalogTemp.reportAnalogInput(); zbAnalogFan.reportAnalogInput(); zbAnalogPercent.reportAnalogInput(); } // 按键处理 if (digitalRead(button) LOW) { delay(100); // 消抖 int startTime millis(); while (digitalRead(button) LOW) { delay(50); if ((millis() - startTime) 3000) { // 长按超过 3 秒Zigbee 出厂复位并重启 Serial.println(Resetting Zigbee to factory and rebooting in 1s.); delay(1000); Zigbee.factoryReset(); } } // 短按演示模拟输出值 100 并立即上报 zbAnalogDevice.setAnalogOutput(zbAnalogDevice.getAnalogOutput() 100); zbAnalogDevice.reportAnalogOutput(); } delay(100); }行为归纳每 2 秒读一次 ADC同时更新 4 个端点的 PresentValue其中 EP2/EP4 做了/100、/10的缩放并对每个端点调用reportAnalogInput()主动发送一次报告短按 BOOT模拟输出当前值 100 并reportAnalogOutput()演示“设备侧主动改变模拟输出并上报”长按 BOOT 超过 3 秒调用Zigbee.factoryReset()擦除 Zigbee 网络凭证并重启便于设备重新入网。五、ZigbeeAnalog 类源码深度解析头文件声明的公开接口与 实现 是理解示例行为的钥匙。5.1 构造函数HA Simple Sensor 设备身份ZigbeeAnalog::ZigbeeAnalog(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID; _on_analog_output_change nullptr; _cluster_list esp_zb_zcl_cluster_list_create(); esp_zb_cluster_list_add_basic_cluster(_cluster_list, esp_zb_basic_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_identify_cluster(_cluster_list, esp_zb_identify_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); _ep_config {.endpoint _endpoint, .app_profile_id ESP_ZB_AF_HA_PROFILE_ID, .app_device_id ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID, .app_device_version 0}; }ZigbeeAnalog.cpp#L19-L29可以看到每个ZigbeeAnalog端点默认携带Basic与Identify两个服务器簇设备类型固定为 HA 设备库中的ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_IDSimple Sensor Device应用轮廓为ESP_ZB_AF_HA_PROFILE_IDHA Profile。这就是为什么 HA 网关会把它识别为“模拟量传感器类”设备的原因。5.2 模拟输入/输出簇的默认属性addAnalogInput()L31-L80创建 Analog Input 簇时写入一组默认属性随后示例中再逐项覆盖属性默认值簇创建时示例中覆盖为DescriptionAnalog InputZCL 字符串首字节为长度 0x0CPower Consumption (Watts)等Application TypeGroup 位为ESP_ZB_ZCL_AI_GROUP_ID类型 0ESP_ZB_ZCL_AI_POWER_IN_WATTS_CONSUMPTION等Resolution0.10.01 / 0.1 / 1Min/Max Present Value-FLT_MAX/FLT_MAX近似全浮点范围可通过setAnalogInputMinMax覆盖addAnalogOutput()L102-L151逻辑对称默认 Resolution 为 1。setAnalogInputApplication/setAnalogOutputApplication会把应用类型的高字节Group 位自动补上(ESP_ZB_ZCL_AI_GROUP_ID 24) | application_type因此调用方只需传入具体类型常量。描述字符串有长度限制setAnalogInputDescription/setAnalogOutputDescription内部按 ZCL 字符串格式编码首字节存长度超过ZB_MAX_NAME_LENGTH会直接返回false并打Description is too long日志L282-L320。5.3 值更新与报告setAnalogInput / reportAnalogInputsetAnalogInput(float)把参数写入 Analog Input 簇的PresentValue属性L195-L208而reportAnalogInput()L225-L242构造一条 ZCLReport Attributes命令report_attr_cmd.address_mode ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; // 不发往指定目标走绑定表 report_attr_cmd.attributeID ESP_ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID; report_attr_cmd.direction ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; // 服务器 - 客户端 report_attr_cmd.clusterID ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT; report_attr_cmd.zcl_basic_cmd.src_endpoint _endpoint;两个细节值得注意address_mode为DST_ADDR_ENDP_NOT_PRESENT说明报告不指定单播目标而是依靠 APS 绑定表把报告投给绑定的协调器/控制器端点若从未配置过setAnalogInputReporting控制器侧就没有对应的 Reporting Configuration主动报告与自动报告都会缺少接收方这正是示例在入网成功后才调用setAnalogInputReporting(0, 30, 10)的原因。setAnalogInputReportingL263-L280把min_interval、max_interval、delta填入esp_zb_zcl_reporting_info_t后调用setClusterReportingdelta以单精度浮点delta.s32传递与setAnalogInputResolution声明的量化精度相配合。5.4 模拟输出回调zbAttributeSet 的覆盖机制当网络侧如 HA 面板向 Analog Output 簇写入新的PresentValue时ZCL 消息分发到端点的zbAttributeSet虚函数。ZigbeeAnalog重写了它L174-L193void ZigbeeAnalog::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { if (message-info.cluster ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT) { if (message-attribute.id ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID message-attribute.data.type ESP_ZB_ZCL_ATTR_TYPE_SINGLE) { _output_state *(float *)message-attribute.data.value; analogOutputChanged(); // 触发用户回调 } } }analogOutputChanged()调用onAnalogOutputChange注册的用户回调示例中打印到串口未注册时仅打印警告。注意setAnalogOutput本地改值时也会同步_output_state并触发同一回调因此回调中不要做阻塞操作。getAnalogOutput()返回缓存的_output_state示例短按按键时就用它做 100 的增量修改。以上类均通过 Zigbee.h 统一导出#include ep/ZigbeeAnalog.h该头文件同时汇总了库提供的全部端点类开关、灯、传感器、网关、中继器等ZigbeeAnalog位于“Sensors”分组。六、组网管理入网等待、开放网络与出厂复位示例使用Zigbee.connected()阻塞等待入网完成。协调器/网关侧的配套操作在 README 与 ZigbeeCore 中都有对应 API默认情况下协调器网络在重启或刷写新固件后是关闭的从设备无法加入。README 给出两种打开方式重启后自动开放在协调器setup()中、Zigbee.begin()之前调用Zigbee.setRebootOpenNetwork(time);time为开放时长运行时随时开放Zigbee.openNetwork(time);需要时也可用Zigbee.closeNetwork()关闭。从设备连不上协调器刷写示例前先擦除从设备 Flash尤其是重新刷写过协调器之后。做法二选一Arduino IDE 的Tools菜单中把Erase All Flash Before Sketch Upload设为Enabled或在代码里调用Zigbee.factoryReset();头文件签名factoryReset(bool restart true)默认复位后自动重启示例正是用它实现 BOOT 长按出厂复位。此外若组网困难可检查协调器侧的扫描参数ZigbeeCore提供了setPrimaryChannelMask默认扫描全部 11~26 信道掩码0x07FFF800与setScanDuration1 最快 ~ 4 最慢等接口ZigbeeCore.h#L169-L174。七、故障排查README Troubleshooting 全解现象排查手段从设备无法连接协调器先擦除从设备 Flash 再刷写IDE 开启Erase All Flash Before Sketch Upload或代码中Zigbee.factoryReset();重新刷写过协调器时尤应如此协调器重启后从设备无法再入网网络默认关闭用Zigbee.setRebootOpenNetwork(time);begin()之前或运行时Zigbee.openNetwork(time);开放网络LED 不闪检查接线与 IO 选择是否正确烧录失败Programming Fail降低串口波特率重试COM 口识别不到检查 USB 线与 USB 转串口驱动安装供电不稳换用优质 USB 线与稳定电源官方特别强调若设置Tools - Core Debug Level: Verbose后仍无法定位问题可参考仓库的 CONTRIBUTING.md 参与讨论或提交 Issue。八、小结与延伸阅读本示例展示了 arduino-esp32 Zigbee 库的标准开发闭环选对编译模式与分区表 → 用端点类声明簇与属性 → 入网后配置报告 → 主循环中读传感器/响应按键并主动报告 → 用出厂复位与开放网络接口运维组网。核心文件索引示例说明与排障libraries/Zigbee/examples/Zigbee_Analog_Input_Output/README.md示例代码libraries/Zigbee/examples/Zigbee_Analog_Input_Output/Zigbee_Analog_Input_Output.ino端点类接口libraries/Zigbee/src/ep/ZigbeeAnalog.h、libraries/Zigbee/src/ep/ZigbeeAnalog.cpp核心库与网络接口libraries/Zigbee/src/ZigbeeCore.h、libraries/Zigbee/src/Zigbee.h编译选项Zigbee mode / 分区表boards.txt需要提醒的适用前提本文所有配置以当前仓库代码为准——示例要求 ZCZR 编译模式 ZCZR 分区表、运行时以ZIGBEE_ROUTER角色入网README 中“Zigbee ED (end device)”的表述与代码编译守卫不一致请以代码与ci.yml的ZigbeeModezczr配置为准。若后续需要真正的电池供电 End Device 角色可参考ZigbeeCore::begin(zigbee_role_t role, bool erase_nvs)的ZIGBEE_END_DEVICE选项并结合ZigbeeCore.h中的 ED 电池模式相关配置项做调整。【免费下载链接】arduino-esp32Arduino core for the ESP32 family of SoCs项目地址: https://gitcode.com/GitHub_Trending/ar/arduino-esp32创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考