XIAO ESP32C6 Zigbee 快速入门指南 (ESP-IDF)
Zigbee 是一种广泛采用的无线通信协议,在家庭自动化、智能能源管理和物联网 (IoT) 应用中得到广泛使用。Zigbee 以其低功耗、可靠的数据传输和网状网络功能而闻名,是构建可扩展且高效的无线网络的绝佳选择。
在本教程中,我们将踏上使用 XIAO ESP32C6 开发板探索 Zigbee 应用开发的旅程。XIAO ESP32C6 是一款紧凑而强大的开发板,搭载 ESP32-C6 芯片,提供集成的 Wi-Fi 和蓝牙低功耗 (BLE) 连接功能。通过利用 ESP Zigbee SDK,我们可以充分发挥 XIAO ESP32C6 的潜力,并扩展其功能以包含 Zigbee 功能。

为了深入了解 Zigbee 开发,我们将重点关注 ESP Zigbee SDK 提供的两个示例程序:HA_on_off_light 和 HA_on_off_switch。这些示例分别展示了 Zigbee 灯设备和 Zigbee 开关设备的实现。通过深入研究这些示例背后的代码结构、数据模型和工作原理,我们将全面了解 Zigbee 设备开发。
在本教程中,我们将涵盖以下关键方面:
- 为 XIAO ESP32C6 和 ESP Zigbee SDK 设置开发环境。
- 分析 HA_on_off_light 和 HA_on_off_switch 示例的代码结构和组织。
- 了解 Zigbee 设备数据模型以及它们在代码中的定义方式。
- 探索 Zigbee 设备中的初始化过程和事件处理机制。
- 研究 Zigbee 设备之间的通信模式和消息交换。
在本教程结束时,您将在使用 XIAO ESP32C6 和 ESP Zigbee SDK 进行 Zigbee 开发方面打下坚实的基础。凭借这些知识和实用技能,您将能够创建自己的基于 Zigbee 的项目,并为不断增长的 Zigbee 设备生态系统做出贡献。
那么,让我们踏上使用 XIAO ESP32C6 进行 Zigbee 开发的激动人心的旅程,释放这一强大无线通信协议的全部潜力!
硬件准备
在本教程中,我们将使用两个 XIAO ESP32C6 作为示例来解释 Zigbee。您可以通过下面的链接跳转购买。一个作为 Zigbee 终端设备,一个作为 Zigbee 协调器。
Seeed Studio XIAO ESP32C6 |
---|
![]() |
环境准备和演示
在本节中,我们将指导您完成开发环境的配置并上传示例中的两个程序。
步骤 1. 准备 ESP-IDF 环境
要使用 Zigbee SDK,建议使用 Espressif 的 ESP-IDF 开发框架。ESP-IDF 的安装和环境配置在 Espressif 官网提供了针对不同系统的详细安装过程,您可以通过下面的按钮跳转阅读。
如果您恰好使用的是 Ubuntu 系统,在终端中大致需要执行的命令如下:
git clone --recursive https://github.com/espressif/esp-idf.git
cd esp-idf
git checkout v5.1.3
git submodule update --init --recursive
./install.sh
source ./export.sh
cd ..
Espressif 推荐使用 ESP-IDF v5.1.3 进行 Zigbee 开发,这是本教程验证过的版本。
步骤 2. 下载 Zigbee SDK
克隆 esp-zigbee-sdk:
git clone https://github.com/espressif/esp-zigbee-sdk.git
cd esp-zigbee-sdk/examples/esp_zigbee_HA_sample
步骤 3. 编写 HA_on_off_light 程序
让我们准备第一个 XIAO ESP32C6。我们为它编写并烧录终端设备程序。
cd HA_on_off_light/main
由于提供的示例程序使用 GPIO8 作为 LED,但 XIAO 上的 LED 是 GPIO15,我们需要对示例程序进行一些简单的修改以显示效果。
需要修改的程序位于主文件中的 esp_zb_light.c
。修改后的完整代码如下。
#include "esp_zb_light.h"
#include "esp_check.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ha/esp_zigbee_ha_standard.h"
#include "driver/gpio.h"
#if !defined ZB_ED_ROLE
#error Define ZB_ED_ROLE in idf.py menuconfig to compile light (End Device) source code.
#endif
static const char *TAG = "ESP_ZB_ON_OFF_LIGHT";
#define BLINK_GPIO 15
/********************* 定义函数 **************************/
static esp_err_t deferred_driver_init(void)
{
light_driver_init(LIGHT_DEFAULT_OFF);
return ESP_OK;
}
static void configure_led(void)
{
ESP_LOGI(TAG, "示例配置为闪烁 GPIO LED!");
gpio_reset_pin(BLINK_GPIO);
/* 将 GPIO 设置为推挽输出 */
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
}
static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask)
{
ESP_RETURN_ON_FALSE(esp_zb_bdb_start_top_level_commissioning(mode_mask) == ESP_OK, , TAG, "启动 Zigbee 调试失败");
}
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
{
uint32_t *p_sg_p = signal_struct->p_app_signal;
esp_err_t err_status = signal_struct->esp_err_status;
esp_zb_app_signal_type_t sig_type = *p_sg_p;
switch (sig_type) {
case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
ESP_LOGI(TAG, "初始化 Zigbee 协议栈");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
break;
case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:
if (err_status == ESP_OK) {
ESP_LOGI(TAG, "延迟驱动程序初始化 %s", deferred_driver_init() ? "失败" : "成功");
ESP_LOGI(TAG, "设备在 %s 出厂重置模式下启动", esp_zb_bdb_is_factory_new() ? "" : "非");
if (esp_zb_bdb_is_factory_new()) {
ESP_LOGI(TAG, "开始网络引导");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
} else {
ESP_LOGI(TAG, "设备重启");
}
} else {
/* 调试失败 */
ESP_LOGW(TAG, "初始化 Zigbee 协议栈失败 (状态: %s)", esp_err_to_name(err_status));
}
break;
case ESP_ZB_BDB_SIGNAL_STEERING:
if (err_status == ESP_OK) {
esp_zb_ieee_addr_t extended_pan_id;
esp_zb_get_extended_pan_id(extended_pan_id);
ESP_LOGI(TAG, "成功加入网络 (扩展 PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, 信道:%d, 短地址: 0x%04hx)",
extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4],
extended_pan_id[3], extended_pan_id[2], extended_pan_id[1], extended_pan_id[0],
esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address());
} else {
ESP_LOGI(TAG, "网络引导不成功 (状态: %s)", esp_err_to_name(err_status));
esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_STEERING, 1000);
}
break;
default:
ESP_LOGI(TAG, "ZDO 信号: %s (0x%x), 状态: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type,
esp_err_to_name(err_status));
break;
}
}
static esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message)
{
esp_err_t ret = ESP_OK;
bool light_state = 0;
ESP_RETURN_ON_FALSE(message, ESP_FAIL, TAG, "空消息");
ESP_RETURN_ON_FALSE(message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS, ESP_ERR_INVALID_ARG, TAG, "接收到消息: 错误状态(%d)",
message->info.status);
ESP_LOGI(TAG, "接收到消息: 端点(%d), 簇(0x%x), 属性(0x%x), 数据大小(%d)", message->info.dst_endpoint, message->info.cluster,
message->attribute.id, message->attribute.data.size);
if (message->info.dst_endpoint == HA_ESP_LIGHT_ENDPOINT) {
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) {
if (message->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) {
light_state = message->attribute.data.value ? *(bool *)message->attribute.data.value : light_state;
ESP_LOGI(TAG, "灯设置为 %s", light_state ? "开" : "关");
gpio_set_level(BLINK_GPIO, light_state);
// light_driver_set_power(light_state);
}
}
}
return ret;
}
static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message)
{
esp_err_t ret = ESP_OK;
switch (callback_id) {
case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID:
ret = zb_attribute_handler((esp_zb_zcl_set_attr_value_message_t *)message);
break;
default:
ESP_LOGW(TAG, "接收到 Zigbee 动作(0x%x) 回调", callback_id);
break;
}
return ret;
}
static void esp_zb_task(void *pvParameters)
{
/* 初始化 Zigbee 协议栈 */
esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG();
esp_zb_init(&zb_nwk_cfg);
esp_zb_on_off_light_cfg_t light_cfg = ESP_ZB_DEFAULT_ON_OFF_LIGHT_CONFIG();
esp_zb_ep_list_t *esp_zb_on_off_light_ep = esp_zb_on_off_light_ep_create(HA_ESP_LIGHT_ENDPOINT, &light_cfg);
esp_zb_device_register(esp_zb_on_off_light_ep);
esp_zb_core_action_handler_register(zb_action_handler);
esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
ESP_ERROR_CHECK(esp_zb_start(false));
esp_zb_main_loop_iteration();
}
void app_main(void)
{
configure_led();
esp_zb_platform_config_t config = {
.radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_ZB_DEFAULT_HOST_CONFIG(),
};
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_zb_platform_config(&config));
xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL);
}
请保存它。
步骤 4. 烧录 HA_on_off_light 程序
现在将您的 XIAO ESP32C6 开发板连接到计算机,并检查开发板在哪个串口下可见。
串口具有以下命名模式:/dev/tty
。通常,您的计算机可能有许多以 tty
开头的端口。

确定端口也很容易,您可以使用查询命令查看在未连接 XIAO 时默认存在哪些端口。
ls /dev/tty*
然后,将 XIAO 连接到计算机并再次查询,额外的串口名称就是 XIAO 的端口号。
设置目标设备。
idf.py set-target esp32c6
通过运行以下命令构建项目:
idf.py build
要烧录您在上一步中为 ESP32 构建的二进制文件,您需要运行以下命令:
idf.py -p PORT flash
将 PORT
替换为您的 XIAO ESP32C6 USB 端口名称。如果未定义 PORT,idf.py
将尝试使用可用的 USB 端口自动连接。根据我们在第一步中查询到的设备端口号,对我来说,我应该输入以下命令来烧录程序。
idf.py -p /dev/ttyACM0 flash
如果刷写过程结束时没有问题,XIAO ESP32C6 将重启并启动 Zigbee 灯应用程序。
步骤 5. 刷写 HA_on_off_switch 程序
同样,我们取出另一个 XIAO ESP32C6 并为其上传开关程序。步骤类似。
cd ../HA_on_off_switch
idf.py set-target esp32c6
idf.py build
idf.py -p PORT flash
如果一切顺利,接下来您可以使用 SWITCH 程序的 XIAO 上的 BOOT 按钮来控制 LIGHT 程序的 XIAO 的 LED 开启或关闭。
HA_on_off_light 和 HA_on_off_switch 的程序结构
该文件夹包含演示 Zigbee HA 标准设备的示例
-
HA_on_off_light
是一个标准的 HA 开关灯泡示例,演示 Zigbee 终端设备。 -
HA_on_off_switch
是一个标准的 HA 开关示例,演示 Zigbee 协调器角色。它提供一个开/关切换功能来控制 Zigbee HA 开关灯。
在本教程中,我们将深入研究 ESP Zigbee SDK 提供的两个示例程序:HA_on_off_light
和 HA_on_off_switch
。通过分析这些示例的代码结构和组织,我们将全面了解如何开发 Zigbee 设备应用程序。
- esp_zigbee_HA_sample/
- HA_on_off_light/
- main/
- CMakeLists.txt
- esp_zb_light.c
- esp_zb_light.h
- idf_component.yml
- CMakeLists.txt
- partitions.csv
- sdkconfig.defaults
...
- HA_on_off_switch/
- main/
- CMakeLists.txt
- esp_zb_switch.c
- esp_zb_switch.h
- idf_component.yml
- CMakeLists.txt
- partitions.csv
- sdkconfig.defaults
...
-
esp_zigbee_HA_sample/: 此目录包含ESP Zigbee SDK提供的家庭自动化(HA)示例项目。
-
HA_on_off_light/: 此子目录代表"开关灯"示例项目。
- main/: 此目录包含"开关灯"示例的主要源文件。
- CMakeLists.txt: 此文件由CMake构建系统使用,用于指定"开关灯"示例的源文件和依赖项。
- esp_zb_light.c: 此文件包含Zigbee灯设备的主要实现代码,包括初始化、事件处理和与其他Zigbee设备的通信。
- esp_zb_light.h: 此头文件包含Zigbee灯设备所需的函数声明、常量和数据结构。
- idf_component.yml: 此文件是ESP-IDF组件配置文件,指定了"开关灯"示例的组件依赖项和构建设置。
- CMakeLists.txt: 此文件是"开关灯"示例项目的顶级CMakeLists文件,包含必要的配置和构建目标。
- partitions.csv: 此文件定义了"开关灯"示例的分区表,指定了各种分区(如引导加载程序、应用程序和存储)的内存布局和大小。
- sdkconfig.defaults: 此文件包含"开关灯"示例项目的默认配置设置,用户可以覆盖这些设置。
- main/: 此目录包含"开关灯"示例的主要源文件。
-
HA_on_off_switch/: 此子目录代表"开关按钮"示例项目。
- main/: 此目录包含"开关按钮"示例的主要源文件。
- CMakeLists.txt: 与"开关灯"示例类似,此文件由CMake构建系统使用,用于指定"开关按钮"示例的源文件和依赖项。
- esp_zb_switch.c: 此文件包含Zigbee开关设备的主要实现代码,包括初始化、事件处理和与其他Zigbee设备的通信。
- esp_zb_switch.h: 此头文件包含Zigbee开关设备所需的函数声明、常量和数据结构。
- idf_component.yml: 此文件是"开关按钮"示例的ESP-IDF组件配置文件。
- CMakeLists.txt: 这是"开关按钮"示例项目的顶级CMakeLists文件。
- partitions.csv: 此文件定义了"开关按钮"示例的分区表。
- sdkconfig.defaults: 此文件包含"开关按钮"示例项目的默认配置设置。
- main/: 此目录包含"开关按钮"示例的主要源文件。
这些文件协同工作,提供了使用ESP Zigbee SDK的Zigbee设备的完整示例实现。.c和.h文件包含实际的代码实现,而CMakeLists.txt、partitions.csv和sdkconfig.defaults文件用于构建配置和内存分区。
Zigbee终端设备和Zigbee数据模型
在本教程中,我们将探索Zigbee HA开关灯示例代码如何基于Zigbee数据模型进行结构化。通过理解代码与数据模型之间的关系,您将深入了解如何根据特定需求解释和修改代码。
在深入代码之前,掌握Zigbee数据模型的关键概念至关重要:
-
节点: 节点代表单个基于ESP32-H2的产品和Zigbee网络中的网络节点。一个节点可以有多个端点。
-
端点: 端点由1到240之间的数字标识,定义了在Zigbee节点上运行的应用程序。一个节点可以有多个端点,每个端点服务于不同的目的或代表单独的设备。
-
簇: 簇由16位数字标识,是定义与端点相关的功能和数据的应用程序对象。簇包含属性和命令。
-
属性: 属性由16位数字标识,表示簇内的当前状态或物理量。

现在,让我们检查HA开关灯示例代码,看看它如何映射到Zigbee数据模型。
- 创建端点
在示例代码中,esp_zb_on_off_light_ep_create()
函数用于创建HA开关灯端点。此函数定义端点ID、设备ID和相关的簇。
static void esp_zb_task(void *pvParameters)
{
/* initialize Zigbee stack */
esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG();
esp_zb_init(&zb_nwk_cfg);
esp_zb_on_off_light_cfg_t light_cfg = ESP_ZB_DEFAULT_ON_OFF_LIGHT_CONFIG();
esp_zb_ep_list_t *esp_zb_on_off_light_ep = esp_zb_on_off_light_ep_create(HA_ESP_LIGHT_ENDPOINT, &light_cfg);
esp_zb_device_register(esp_zb_on_off_light_ep);
esp_zb_core_action_handler_register(zb_action_handler);
esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
ESP_ERROR_CHECK(esp_zb_start(false));
esp_zb_main_loop_iteration();
}
- 注册设备
创建端点后,调用 esp_zb_device_register()
函数将 Zigbee 设备注册到创建的端点。
esp_zb_device_register(esp_zb_on_off_light_ep);
- 属性回调
示例代码使用 esp_zb_core_action_handler_register()
注册属性变化回调。当某些属性被修改时,会调用此回调,允许您根据应用程序逻辑处理属性变化。
esp_zb_core_action_handler_register(zb_action_handler);
在 zb_action_handler
函数中,您可以实现当开/关属性发生变化时所需的行为,例如控制LED灯。
- Zigbee 协议栈配置和启动
示例代码使用 ESP_ZB_ZED_CONFIG()
配置 Zigbee 终端设备,并使用 esp_zb_init()
初始化 Zigbee 协议栈。然后使用 esp_zb_start()
启动协议栈,主循环由 esp_zb_main_loop_iteration()
处理。
esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG();
esp_zb_init(&zb_nwk_cfg);
...
ESP_ERROR_CHECK(esp_zb_start(false));
esp_zb_main_loop_iteration();
esp_zb_app_signal_handler
函数负责处理来自 Zigbee 应用层的各种信号。
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
{
uint32_t *p_sg_p = signal_struct->p_app_signal;
esp_err_t err_status = signal_struct->esp_err_status;
esp_zb_app_signal_type_t sig_type = *p_sg_p;
switch (sig_type) {
case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
ESP_LOGI(TAG, "初始化 Zigbee 协议栈");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
break;
case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:
if (err_status == ESP_OK) {
ESP_LOGI(TAG, "延迟驱动初始化 %s", deferred_driver_init() ? "失败" : "成功");
ESP_LOGI(TAG, "设备在 %s 出厂重置模式下启动", esp_zb_bdb_is_factory_new() ? "" : "非");
if (esp_zb_bdb_is_factory_new()) {
ESP_LOGI(TAG, "开始网络引导");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
} else {
ESP_LOGI(TAG, "设备重启");
}
} else {
/* commissioning failed */
ESP_LOGW(TAG, "初始化 Zigbee 协议栈失败 (状态: %s)", esp_err_to_name(err_status));
}
break;
case ESP_ZB_BDB_SIGNAL_STEERING:
if (err_status == ESP_OK) {
esp_zb_ieee_addr_t extended_pan_id;
esp_zb_get_extended_pan_id(extended_pan_id);
ESP_LOGI(TAG, "成功加入网络 (扩展 PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, 信道:%d, 短地址: 0x%04hx)",
extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4],
extended_pan_id[3], extended_pan_id[2], extended_pan_id[1], extended_pan_id[0],
esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address());
} else {
ESP_LOGI(TAG, "网络引导不成功 (状态: %s)", esp_err_to_name(err_status));
esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_STEERING, 1000);
}
break;
default:
ESP_LOGI(TAG, "ZDO 信号: %s (0x%x), 状态: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type,
esp_err_to_name(err_status));
break;
}
}
-
首先,函数从传递的
esp_zb_app_signal_t
结构体中获取信号类型sig_type
和错误状态err_status
。 -
然后,它使用 switch 语句根据信号类型执行不同的操作:
-
ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP
:此信号表示跳过 Zigbee 协议栈的启动。在这种情况下,我们初始化 Zigbee 协议栈,然后调用esp_zb_bdb_start_top_level_commissioning
函数以模式设置为ESP_ZB_BDB_MODE_INITIALIZATION
来启动顶级调试过程。 -
ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START
和ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT
:这些信号表示设备的首次启动或重启。如果错误状态为ESP_OK
,我们执行一些初始化任务,例如延迟驱动程序初始化。然后,我们检查设备是否处于出厂新状态。如果是,我们启动网络引导过程;否则,我们输出一条消息表示设备已重启。如果错误状态不是ESP_OK
,我们输出一条消息表示 Zigbee 协议栈初始化失败。 -
ESP_ZB_BDB_SIGNAL_STEERING
:此信号表示网络引导过程的结果。如果错误状态为ESP_OK
,表示设备成功加入网络。在这种情况下,我们输出一些网络信息,例如 PAN ID、信道号和短地址。如果错误状态不是ESP_OK
,表示网络引导失败,我们输出错误消息。然后,我们使用esp_zb_scheduler_alarm
函数设置定时器,在 1 秒延迟后重新启动网络引导过程。 -
其他信号:我们简单地输出信号名称、类型和错误状态。
-
此函数的目的是根据不同的 Zigbee 应用层信号执行适当的操作。它是 Zigbee 应用程序的核心部分之一。它处理设备启动、初始化和网络加入等关键过程。
Zigbee 协调器
对于 Zigbee 协调器设备,其初始化和 RTOS 任务与终端设备类似,除了在 RTOS 任务中,少了注册回调函数的步骤。
因此对于 Zigbee 协调器,最关键的部分是搜索和匹配相应的设备,并向设备发出控制命令。
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
{
uint32_t *p_sg_p = signal_struct->p_app_signal;
esp_err_t err_status = signal_struct->esp_err_status;
esp_zb_app_signal_type_t sig_type = *p_sg_p;
esp_zb_zdo_signal_device_annce_params_t *dev_annce_params = NULL;
switch (sig_type) {
case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
ESP_LOGI(TAG, "初始化 Zigbee 协议栈");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
break;
case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:
if (err_status == ESP_OK) {
ESP_LOGI(TAG, "延迟驱动初始化 %s", deferred_driver_init() ? "失败" : "成功");
ESP_LOGI(TAG, "设备在 %s 出厂重置模式下启动", esp_zb_bdb_is_factory_new() ? "" : "非");
if (esp_zb_bdb_is_factory_new()) {
ESP_LOGI(TAG, "开始网络组建");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_FORMATION);
} else {
ESP_LOGI(TAG, "设备重启");
}
} else {
ESP_LOGE(TAG, "初始化 Zigbee 协议栈失败 (状态: %s)", esp_err_to_name(err_status));
}
break;
case ESP_ZB_BDB_SIGNAL_FORMATION:
if (err_status == ESP_OK) {
esp_zb_ieee_addr_t extended_pan_id;
esp_zb_get_extended_pan_id(extended_pan_id);
ESP_LOGI(TAG, "网络组建成功 (扩展 PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, 信道:%d, 短地址: 0x%04hx)",
extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4],
extended_pan_id[3], extended_pan_id[2], extended_pan_id[1], extended_pan_id[0],
esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address());
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
} else {
ESP_LOGI(TAG, "重启网络组建 (状态: %s)", esp_err_to_name(err_status));
esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_FORMATION, 1000);
}
break;
case ESP_ZB_BDB_SIGNAL_STEERING:
if (err_status == ESP_OK) {
ESP_LOGI(TAG, "网络引导已启动");
}
break;
case ESP_ZB_ZDO_SIGNAL_DEVICE_ANNCE:
dev_annce_params = (esp_zb_zdo_signal_device_annce_params_t *)esp_zb_app_signal_get_params(p_sg_p);
ESP_LOGI(TAG, "新设备已入网或重新加入 (短地址: 0x%04hx)", dev_annce_params->device_short_addr);
esp_zb_zdo_match_desc_req_param_t cmd_req;
cmd_req.dst_nwk_addr = dev_annce_params->device_short_addr;
cmd_req.addr_of_interest = dev_annce_params->device_short_addr;
/* 设备加入网络后查找彩色调光灯 */
esp_zb_zdo_find_color_dimmable_light(&cmd_req, user_find_cb, NULL);
break;
case ESP_ZB_NWK_SIGNAL_PERMIT_JOIN_STATUS:
if (err_status == ESP_OK) {
if (*(uint8_t *)esp_zb_app_signal_get_params(p_sg_p)) {
ESP_LOGI(TAG, "网络(0x%04hx) 开放 %d 秒", esp_zb_get_pan_id(), *(uint8_t *)esp_zb_app_signal_get_params(p_sg_p));
} else {
ESP_LOGW(TAG, "网络(0x%04hx) 已关闭,不允许设备加入。", esp_zb_get_pan_id());
}
}
break;
default:
ESP_LOGI(TAG, "ZDO 信号: %s (0x%x), 状态: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type,
esp_err_to_name(err_status));
break;
}
}
让我们来看看不同情况及其功能:
-
ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP
:- 此信号表示应跳过Zigbee协议栈初始化。
- 它记录一条表示Zigbee协议栈初始化的消息。
- 它启动顶级调试过程,模式设置为
ESP_ZB_BDB_MODE_INITIALIZATION
。
-
ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START
和ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT
:- 这些信号表示设备首次启动或已重启。
- 如果错误状态为
ESP_OK
,它会记录关于延迟驱动程序初始化状态的消息,以及设备是否在出厂重置模式下启动。 - 如果设备处于出厂新模式,它通过调用
esp_zb_bdb_start_top_level_commissioning
启动网络形成过程,模式设置为ESP_ZB_BDB_MODE_NETWORK_FORMATION
。 - 如果设备不处于出厂新模式,它记录一条表示设备已重启的消息。
- 如果错误状态不是
ESP_OK
,它记录一条错误消息。
-
ESP_ZB_BDB_SIGNAL_FORMATION
:- 此信号表示网络形成过程的状态。
- 如果错误状态为
ESP_OK
,它检索扩展PAN ID,记录关于已形成网络的信息(PAN ID、信道、短地址),并通过调用esp_zb_bdb_start_top_level_commissioning
启动网络引导过程,模式设置为ESP_ZB_BDB_MODE_NETWORK_STEERING
。 - 如果错误状态不是
ESP_OK
,它记录一条重启网络形成的消息,并安排一个警报在1000毫秒延迟后调用bdb_start_top_level_commissioning_cb
。
-
ESP_ZB_BDB_SIGNAL_STEERING
:- 此信号表示网络引导过程的状态。
- 如果错误状态为
ESP_OK
,它记录一条表示网络引导已启动的消息。
-
ESP_ZB_ZDO_SIGNAL_DEVICE_ANNCE
:- 当新设备被调试或重新加入网络时触发此信号。
- 它检索设备公告参数并记录一条包含新设备短地址的消息。
- 它准备一个匹配描述符请求(
esp_zb_zdo_match_desc_req_param_t
),将目标和感兴趣的地址设置为新设备的短地址。 - 它调用
esp_zb_zdo_find_color_dimmable_light
来查找彩色可调光设备,并指定user_find_cb
作为回调函数。
-
ESP_ZB_NWK_SIGNAL_PERMIT_JOIN_STATUS
:- 此信号表示网络允许加入状态的状态。
- 如果错误状态为
ESP_OK
,它记录一条消息,表示网络是否开放加入以及开放的持续时间。如果网络关闭,它记录一条警告消息。
-
默认情况:
- 对于任何其他信号类型,它记录一条包含信号类型和错误状态的通用消息。
总的来说,此代码处理各种Zigbee相关事件并执行诸如初始化Zigbee协议栈、形成网络、引导网络、处理设备公告和查找彩色可调光设备等操作。
示例的其余部分涉及按键稳定化和按键中断的逻辑。如果您感兴趣,可以自己阅读和理解。
故障排除
Q1: ESP_ZB_ON_OFF_LIGHT 持续出现问题:网络引导无法成功匹配另一个 XIAO。
首先,请排查您使用的ESP-IDF版本,确保您使用ESP-IDF v5.1.3来编译Zigbee示例应用程序,如果不是,请更改IDF版本。
接下来,尝试重新插拔设备。您可以尝试先给上传了HA_on_off_switch程序的设备上电,然后给上传了HA_on_off_light程序的设备上电。
如果仍然不起作用,请擦除所有闪存并重新上传程序。
idf.py erase_flash flash monitor
如果以上方法都不起作用,请向 Espressif 提交问题。
Q2: 成功匹配后,如果我想匹配新设备应该怎么做?
直接使用 flash 命令上传器不会擦除 flash 保存的历史配对记录。请使用以下命令重新上传程序以匹配新设备。
idf.py erase_flash flash monitor
资源
- [Espressif 官方文档] 使用 ESP Zigbee SDK 进行开发
- [GITHUB] Zigbee SDK 仓库
技术支持与产品讨论
感谢您选择我们的产品!我们在这里为您提供不同的支持,以确保您对我们产品的体验尽可能顺畅。我们提供多种沟通渠道,以满足不同的偏好和需求。