feat(zigbee): upgrade zigbee example to v2.0

This commit is contained in:
xieqinan@espressif.com
2026-09-09 11:34:28 +08:00
parent a8a5106421
commit 256403fe73
53 changed files with 1880 additions and 1251 deletions

View File

@@ -6,23 +6,22 @@
- openthread
- protocol_examples_common
depends_filepatterns:
- examples/zigbee/esp_zigbee_gateway/*
- examples/zigbee/light_sample/**/*
examples/zigbee/esp_zigbee_gateway:
enable:
- if: SOC_WIFI_SUPPORTED == 1 and IDF_TARGET not in ["esp32c2", "esp32c61"]
reason: not supported esp32c2 and esp32c61
disable:
- if: IDF_TARGET == "esp32s31"
temporary: true
reason: Zigbee gateway example does not support esp32s31 yet
- if: (SOC_WIFI_SUPPORTED == 1 and IDF_TARGET not in ["esp32c61", "esp32c2"]) or IDF_TARGET in ["esp32p4"]
disable_test:
- if: IDF_TARGET not in ["esp32s3"]
reason: only test on esp32s3
<<: *zigbee_dependencies
examples/zigbee/light_sample:
enable:
- if: SOC_IEEE802154_SUPPORTED == 1 and IDF_TARGET not in ["esp32h4", "esp32s31", "esp32h21"]
- if: SOC_IEEE802154_SUPPORTED == 1
disable_test:
- if: IDF_TARGET != "esp32h2"
- if: IDF_TARGET not in ["esp32h2"]
temporary: true
reason: only test on esp32h2
<<: *zigbee_dependencies

11
examples/zigbee/README.md Normal file
View File

@@ -0,0 +1,11 @@
# Zigbee Examples
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.
## Overview
In this folder, it contains following Zigbee examples:
* [esp_zigbee_gateway](esp_zigbee_gateway) is a Zigbee gateway example. It runs on a Wi-Fi SoC such as ESP32, ESP32-C3 and ESP32-S3. It needs an 802.15.4 SoC like ESP32-H2 running [ot_rcp](../openthread/ot_rcp) example to provide 802.15.4 radio. For more details see [README](esp_zigbee_gateway/README.md).
* [light_sample](light_sample) It is a pair of typical Zigbee Light On/Off switch and bulb examples. They run on an 802.15.4 SoC like ESP32-H2. For more details see [README](light_sample/README.md).

View File

@@ -1,46 +1,68 @@
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | --------- |
# Gateway Example
This example demonstrates how to build a Zigbee Gateway device.
This example demonstrates how to build a Zigbee Gateway device. It runs on a Wi-Fi SoC such as ESP32, ESP32-C3 and ESP32-S3, with an 802.15.4 SoC like ESP32-H2
running [ot_rcp](https://github.com/espressif/esp-idf/tree/master/examples/openthread/ot_rcp) to provide 802.15.4 radio.
The ESP Zigbee SDK provides more examples and tools for productization:
* [ESP Zigbee SDK Docs](https://docs.espressif.com/projects/esp-zigbee-sdk)
* [ESP Zigbee SDK Repo](https://github.com/espressif/esp-zigbee-sdk)
## Hardware Platforms
## Hardware Required
### Wi-Fi based ESP Zigbee Gateway
By default, two SoCs are required to run this example:
* An ESP32 series Wi-Fi SoC (ESP32, ESP32-C, ESP32-S, etc) loaded with this esp_zigbee_gateway example.
* An ESP32-H2 802.15.4 SoC loaded with [ot_rcp](../../openthread/ot_rcp/) example
The Wi-Fi based ESP Zigbee Gateway consists of two SoCs:
Connect the two SoCs via UART, below is an example setup with ESP32-DevKitC and ESP32-H2-DevKitC:
![Zigbee_gateway](../../openthread/ot_br/image/thread-border-router-esp32-esp32h2.jpg)
* An ESP32 series Wi-Fi SoC (ESP32, ESP32-C, ESP32-S, etc) loaded with this example.
* An 802.15.4 enabled SoC (e.g., ESP32-H2 or ESP32-C6) loaded with [OpenThread RCP](https://github.com/espressif/esp-idf/tree/master/examples/openthread/ot_rcp) example.
#### ESP Zigbee Gateway Board
The ESP Zigbee gateway board provides an integrated module of an ESP32-S3 SoC and an ESP32-H2 RCP.
![br_dev_kit](images/esp-zigbee-gateway-board.webp)
The two SoCs are connected with following interfaces:
* UART for serial communication
* RESET and BOOT pins for RCP Update
* 3-Wires PTA for RF coexistence
No jumper wires needed. No `idf.py menuconfig` under menu "OpenThread RCP Update" configure to change.
#### Standalone Modules
The SDK also supports manually connecting an ESP32-H2 RCP to an ESP32 series Wi-Fi SoC. As one way of connection is following:
ESP32 pin | ESP32-H2 pin
------------- |-------------
GND | G
GPIO4 (RX) | TX
GPIO5 (TX) | RX
GPI017 | RST
GPIO18 | GPIO9(BOOT)
* TX, RX pin can be also configured by user in esp_zigbee_gateway.h
TX, RX, RST and BOOT pin from ESP32 side can be configured by user in `idf.py menuconfig` under menu "ESP Zigbee gateway rcp update".
The example could also run on a single SoC which supports both Wi-Fi and Zigbee (e.g., ESP32-C6), but since there is only one RF path in ESP32-C6, which means Wi-Fi and Zigbee can't receive simultaneously, it has a significant impact on performance. Hence the two SoCs solution is recommended.
TX, RX pins from ESP32-H2 should be configured in [ot_rcp example](https://github.com/espressif/esp-idf/tree/master/examples/openthread/ot_rcp) via "OpenThread RCP Example > Configure RCP UART pin manually" options.
## Configure the RCP
Other pin number is also available for user to configure if needed.
The `OPENTHREAD_NCP_VENDOR_HOOK` of `ot_rcp` should be selected via menuconfig when the [ot_rcp](../../openthread/ot_rcp/) example is built. Then use `idf.py -p PORT erase-flash` to flash the RCP firmware to ESP32-H2-DevKitC.
The following image shows an example connection between ESP32 DevKitC and ESP32-H2 DevKitC:
## Configure the project
![Zigbee_gateway](images/esp-zigbee-standalone-modules.webp)
Before project configuration and build, make sure to set the correct chip target using `idf.py set-target <chip_name>`.
## Erase the NVRAM
In order to run the example on single SoC which supports both Wi-Fi and Thread, the option `CONFIG_ESP_COEX_SW_COEXIST_ENABLE` and option `CONFIG_ZB_RADIO_NATIVE` should be enabled. The two options are enabled by default for ESP32-C6 target.
Before flash it to the board, it is recommended to erase NVRAM if user doesn't want to keep the previous examples or other projects stored info
using `idf.py -p PORT erase-flash`
## Erase the NVRAM
## Create the RCP firmware image
Before flash it to the board, it is recommended to erase NVRAM if user doesn't want to keep the previous examples or other projects stored info using `idf.py -p PORT erase-flash`
The esp_zigbee_gateway supports updating the RCP image. Generate rcp image before building the esp_zigbee_gateway example.
Build the [ot_rcp](https://github.com/espressif/esp-idf/tree/master/examples/openthread/ot_rcp) example. Later in the esp_zigbee_gateway building process, the built RCP image will be automatically packed into the esp_zigbee_gateway firmware. See detail in [CMakeLists.txt](main/CMakeLists.txt).
Notes:
- `OPENTHREAD_NCP_VENDOR_HOOK` of `ot_rcp` should be selected via menuconfig.
## Build and Flash
@@ -48,86 +70,94 @@ Build the project, flash it to the board, and start the monitor tool to view the
(To exit the serial monitor, type ``Ctrl-]``.)
## Example Output
## Application Functions
As you run the example, you will see the following log:
esp_zigbee_gateway:
- When the program starts, the board will attempt to read the RCP version from the remote radio via UART.
```
I (499) main_task: Calling app_main()
I (519) ESP_RADIO_SPINEL: spinel UART interface initialization completed
I (519) ESP_RADIO_SPINEL: Spinel UART interface has been successfully enabled
I (519) ZB_ESP_SPINEL: Spinel UART interface enable successfully
I (529) main_task: Returned from app_main()
I(529) OPENTHREAD:[I] P-RadioSpinel-: RCP reset: RESET_POWER_ON
I(539) OPENTHREAD:[I] P-RadioSpinel-: Software reset RCP successfully
I (569) ZB_ESP_SPINEL: Radio spinel workflow register successfully
I (769) ESP_ZB_GATEWAY: Production configuration is ready
W (769) ESP_ZB_GATEWAY: Production configuration is not present
I (769) example_connect: Start example_connect.
I (779) pp: pp rom version: e7ae62f
I (779) net80211: net80211 rom version: e7ae62f
I (799) wifi:wifi driver task: 3fca80d8, prio:23, stack:6656, core=0
I (799) wifi:wifi firmware version: 3ce09e5
I (799) wifi:wifi certification version: v7.0
I (799) wifi:config NVS flash: enabled
I (799) wifi:config nano formatting: disabled
I (809) wifi:Init data frame dynamic rx buffer num: 32
I (809) wifi:Init static rx mgmt buffer num: 5
I (819) wifi:Init management short buffer num: 32
I (819) wifi:Init dynamic tx buffer num: 32
I (819) wifi:Init static tx FG buffer num: 2
I (829) wifi:Init static rx buffer size: 1600
I (829) wifi:Init static rx buffer num: 10
I (839) wifi:Init dynamic rx buffer num: 32
I (839) wifi_init: rx ba win: 6
I (839) wifi_init: tcpip mbox: 32
I (849) wifi_init: udp mbox: 6
I (849) wifi_init: tcp mbox: 6
I (849) wifi_init: tcp tx win: 5760
I (859) wifi_init: tcp rx win: 5760
I (859) wifi_init: tcp mss: 1440
I (869) wifi_init: WiFi IRAM OP enabled
I (869) wifi_init: WiFi RX IRAM OP enabled
I (879) phy_init: phy_version 670,b7bc9b9,Apr 30 2024,10:54:13
W (879) phy_init: failed to load RF calibration data (0x1102), falling back to full calibration
I (989) wifi:mode : sta (f4:12:fa:41:a7:f4)
I (989) wifi:enable tsf
I (999) example_connect: Connecting to esp-office-2.4G...
I (999) example_connect: Waiting for IP(s)
I (3409) wifi:new:<13,0>, old:<1,0>, ap:<255,255>, sta:<13,0>, prof:1
I (3649) wifi:state: init -> auth (b0)
I (3719) wifi:state: auth -> assoc (0)
I (3759) wifi:state: assoc -> run (10)
I (3769) wifi:connected with esp-office-2.4G, aid = 1, channel 13, BW20, bssid = 9c:3a:9a:04:18:92
I (3769) wifi:security: WPA2-PSK, phy: bgn, rssi: -42
I (3769) wifi:pm start, type: 1
I (3779) wifi:dp: 1, bi: 102400, li: 3, scale listen interval from 307200 us to 307200 us
I (3789) wifi:set rx beacon pti, rx_bcn_pti: 0, bcn_timeout: 25000, mt_pti: 0, mt_time: 10000
I (3819) wifi:AP's beacon interval = 102400 us, DTIM period = 1
I (3849) wifi:<ba-add>idx:0 (ifx:0, 9c:3a:9a:04:18:92), tid:0, ssn:0, winSize:64
I (4799) esp_netif_handlers: example_netif_sta ip: 192.168.200.133, mask: 255.255.252.0, gw: 192.168.200.1
I (4799) example_connect: Got IPv4 event: Interface "example_netif_sta" address: 192.168.200.133
I (5509) example_connect: Got IPv6 event: Interface "example_netif_sta" address: fe80:0000:0000:0000:f612:faff:fe41:a7f4, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (5509) example_common: Connected to example_netif_sta
I (5519) example_common: - IPv4 address: 192.168.200.133,
I (5519) example_common: - IPv6 address: fe80:0000:0000:0000:f612:faff:fe41:a7f4, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (5529) wifi:Set ps type: 0, coexist: 0
I (5539) ESP_ZB_GATEWAY: Initialize Zigbee stack
I (5549) ESP_ZB_GATEWAY: Device started up in factory-reset mode
I (5549) ESP_ZB_GATEWAY: Start network formation
W (5729) ESP_ZB_GATEWAY: Network(0xb8e9) closed, devices joining not allowed.
I (5729) ESP_ZB_GATEWAY: Formed network successfully (Extended PAN ID: 60:55:f9:ff:fe:f7:73:e8, PAN ID: 0xb8e9, Channel:13, Short Address: 0x0000)
I (6339) ESP_ZB_GATEWAY: Network(0xb8e9) is open for 180 seconds
I (6339) ESP_ZB_GATEWAY: Network steering started
I (433) main_task: Started on CPU0
I (443) main_task: Calling app_main()
I (453) ZIGBEE_GATEWAY: Start ESP Zigbee Stack
I (493) main_task: Returned from app_main()
I (503) RCP_UPDATE: RCP: using update sequence 0
I (503) ESP_RADIO_SPINEL: spinel UART interface initialization completed
I (503) ESP_RADIO_SPINEL: Spinel UART interface has been successfully enabled
I (503) ESP_ZIGBEE_RADIO_SPINEL_UART: Spinel UART interface enable successfully
I(513) OPENTHREAD:[I] P-SpinelDrive-: co-processor reset: RESET_POWER_ON
E(523) OPENTHREAD:[C] P-SpinelDrive-: Software reset co-processor successfully
I (573) ESP_ZIGBEE_RCP: Running RCP version: openthread-esp32/fcae32885b029-b945928d7; esp32h2; 2026-03-11 02:30:22 UTC
```
## Gateway Functions
- If `CONFIG_AUTO_UPDATE_RCP` is enabled and the version does not match the stored one, the board will automatically update the RCP using the stored version.
* When the device starts up, it will attempt to connect to a Wi-Fi network and then interface with the OT-RCP via UART to form a Zigbee network.
* For more Gateway functionalities, please refer to [matter zigbee bridge](https://github.com/espressif/esp-matter/tree/main/examples/bridge_apps/zigbee_bridge/) and [Rainmaker Zigbee Gateway](https://github.com/espressif/esp-rainmaker/tree/master/examples/zigbee_gateway) examples.
- After confirming that the remote radio is functioning properly, the board will connect to Wi-Fi as configured in menuconfig.
```
I (573) example_connect: Start example_connect.
I (583) pp: pp rom version: e7ae62f
I (583) net80211: net80211 rom version: e7ae62f
I (593) wifi:wifi driver task: 3fcb86d0, prio:23, stack:6656, core=0
I (593) wifi:wifi firmware version: 14da9b7
I (593) wifi:wifi certification version: v7.0
I (603) wifi:config NVS flash: enabled
I (603) wifi:config nano formatting: disabled
I (603) wifi:Init data frame dynamic rx buffer num: 32
I (613) wifi:Init static rx mgmt buffer num: 5
I (613) wifi:Init management short buffer num: 32
I (623) wifi:Init dynamic tx buffer num: 32
I (623) wifi:Init static tx FG buffer num: 2
I (633) wifi:Init static rx buffer size: 1600
I (633) wifi:Init static rx buffer num: 10
I (633) wifi:Init dynamic rx buffer num: 32
I (643) wifi_init: rx ba win: 6
I (643) wifi_init: accept mbox: 6
I (643) wifi_init: tcpip mbox: 32
I (653) wifi_init: udp mbox: 6
I (653) wifi_init: tcp mbox: 6
I (653) wifi_init: tcp tx win: 5760
I (663) wifi_init: tcp rx win: 5760
I (663) wifi_init: tcp mss: 1440
I (663) wifi_init: WiFi IRAM OP enabled
I (673) wifi_init: WiFi RX IRAM OP enabled
I (673) phy_init: phy_version 701,f4f1da3a,Mar 3 2025,15:50:10
W (673) phy_init: failed to load RF calibration data (0x1102), falling back to full calibration
I (783) phy_init: Saving new calibration data due to checksum failure or outdated calibration data, mode(2)
I (793) wifi:mode : sta (48:27:e2:14:43:38)
I (793) wifi:enable tsf
I (793) example_connect: Connecting to esp-guest...
W (793) wifi:Password length matches WPA2 standards, authmode threshold changes from OPEN to WPA2
I (803) example_connect: Waiting for IP(s)
I (3293) wifi:new:<6,0>, old:<1,0>, ap:<255,255>, sta:<6,0>, prof:1, snd_ch_cfg:0x0
I (3303) wifi:state: init -> auth (0xb0)
I (3303) wifi:state: auth -> assoc (0x0)
I (3323) wifi:state: assoc -> run (0x10)
I (3413) wifi:connected with esp-guest, aid = 1, channel 6, BW20, bssid = 84:78:48:86:87:31
I (3423) wifi:security: WPA2-PSK, phy: bgn, rssi: -30
I (3423) wifi:pm start, type: 1
I (3423) wifi:dp: 1, bi: 102400, li: 3, scale listen interval from 307200 us to 307200 us
I (3433) wifi:set rx beacon pti, rx_bcn_pti: 0, bcn_timeout: 25000, mt_pti: 0, mt_time: 10000
I (3453) wifi:AP's beacon interval = 102400 us, DTIM period = 1
I (3503) wifi:<ba-add>idx:0 (ifx:0, 84:78:48:86:87:31), tid:0, ssn:0, winSize:64
I (4573) example_connect: Got IPv6 event: Interface "example_netif_sta" address: fe80:0000:0000:0000:4a27:e2ff:fe14:4338, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (4843) esp_netif_handlers: example_netif_sta ip: 10.18.40.174, mask: 255.255.255.0, gw: 10.18.40.1
I (4843) example_connect: Got IPv4 event: Interface "example_netif_sta" address: 10.18.40.174
I (4843) example_common: Connected to example_netif_sta
I (4853) example_common: - IPv4 address: 10.18.40.174,
I (4853) example_common: - IPv6 address: fe80:0000:0000:0000:4a27:e2ff:fe14:4338, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (4863) wifi:Set ps type: 0, coexist: 0
```
- Finally, the board will act as the `Zigbee Gateway`, forming a network and handling commands from the network.
```
I (4883) ZIGBEE_GATEWAY: Initialize Zigbee stack
I (4883) ZIGBEE_GATEWAY: Deferred driver initialization successful
I (4883) ZIGBEE_GATEWAY: Device started up in factory-reset mode
I (5303) ZIGBEE_GATEWAY: Formed network successfully: PAN ID(0x4494, EXT: 0x6055f9fffef7a2ad), Channel(13), Short Address(0x0000)
I (5573) ZIGBEE_GATEWAY: Network(0x4494) is open for 180 seconds
I (5573) ZIGBEE_GATEWAY: Network steering completed
```
- Gateway functionalities supporting Wi-Fi interaction will be added in future updates.
## Troubleshooting

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "esp_zigbee_gateway.c"
PRIV_REQUIRES ieee802154 esp_driver_usb_serial_jtag esp_coex esp_netif
vfs esp_wifi nvs_flash esp_driver_uart
INCLUDE_DIRS ".")
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES ieee802154 esp_driver_usb_serial_jtag esp_coex esp_netif
vfs esp_wifi spiffs nvs_flash esp_driver_uart)

View File

@@ -0,0 +1,17 @@
choice ZB_GW_RCP_CHIP
depends on AUTO_UPDATE_RCP
prompt "Zigbee gateway RCP chip for auto update"
default ZB_GW_RCP_CHIP_ESP32H2
help
Select the RCP chip connected to the Zigbee gateway.
config ZB_GW_RCP_CHIP_ESP32H2
bool "ESP32-H2"
help
ESP32-H2 is the RCP chip connected to the Zigbee gateway.
config ZB_GW_RCP_CHIP_ESP32C6
bool "ESP32-C6"
help
ESP32-C6 is the RCP chip connected to the Zigbee gateway.
endchoice

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
@@ -11,172 +11,255 @@
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include <fcntl.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/usb_serial_jtag.h"
#include "esp_coexist.h"
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_vfs_dev.h"
#include "esp_vfs_eventfd.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#if CONFIG_EXAMPLE_CONNECT_WIFI || CONFIG_EXAMPLE_CONNECT_ETHERNET
#include "esp_event.h"
#include "protocol_examples_common.h"
#include "esp_zigbee_gateway.h"
#include "zb_config_platform.h"
static const char *TAG = "ESP_ZB_GATEWAY";
/* Note: Please select the correct console output port based on the development board in menuconfig */
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
esp_err_t esp_zb_gateway_console_init(void)
{
esp_err_t ret = ESP_OK;
/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
usb_serial_jtag_vfs_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
usb_serial_jtag_vfs_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
/* Enable non-blocking mode on stdin and stdout */
fcntl(fileno(stdout), F_SETFL, O_NONBLOCK);
fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
usb_serial_jtag_driver_config_t usb_serial_jtag_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
ret = usb_serial_jtag_driver_install(&usb_serial_jtag_config);
usb_serial_jtag_vfs_use_driver();
uart_vfs_dev_register();
return ret;
}
#if CONFIG_EXAMPLE_CONNECT_WIFI
#include "esp_wifi.h"
#endif
#endif
static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask)
#if CONFIG_AUTO_UPDATE_RCP
#include "zigbee_rcp.h"
#endif
#if CONFIG_ESP_COEX_SW_COEXIST_ENABLE
#include "esp_coexist.h"
#endif
#include "light_driver.h"
#include "alarm_timer.h"
#include "esp_zigbee.h"
#include "ezbee/zha.h"
#include "esp_zigbee_gateway.h"
static const char *TAG = "ZIGBEE_GATEWAY";
#if (CONFIG_EXAMPLE_CONNECT_WIFI || CONFIG_EXAMPLE_CONNECT_ETHERNET)
static esp_err_t esp_zigbee_connect_netif(void)
{
ESP_RETURN_ON_FALSE(esp_zb_bdb_start_top_level_commissioning(mode_mask) == ESP_OK, , TAG, "Failed to start Zigbee bdb commissioning");
esp_err_t ret = ESP_OK;
#if CONFIG_EXAMPLE_CONNECT_WIFI
wifi_ps_type_t ps_type = WIFI_PS_NONE;
#if CONFIG_ESP_COEX_SW_COEXIST_ENABLE
ps_type = WIFI_PS_MIN_MODEM;
#endif /* CONFIG_ESP_COEX_SW_COEXIST_ENABLE */
#endif /* CONFIG_EXAMPLE_CONNECT_WIFI */
ESP_RETURN_ON_ERROR(esp_netif_init(), TAG, "Failed to initialize network interface");
ESP_RETURN_ON_ERROR(esp_event_loop_create_default(), TAG, "Failed to create event loop");
#if CONFIG_ESP_COEX_SW_COEXIST_ENABLE
esp_coex_wifi_i154_enable();
#endif /* CONFIG_ESP_COEX_SW_COEXIST_ENABLE*/
ESP_RETURN_ON_ERROR(example_connect(), TAG, "Failed to connect to Network Interface");
#if CONFIG_EXAMPLE_CONNECT_WIFI
ESP_RETURN_ON_ERROR(esp_wifi_set_ps(ps_type), TAG, "Failed to set power save type for Wi-Fi");
#endif /* CONFIG_EXAMPLE_CONNECT_WIFI */
return ret;
}
#endif /* (CONFIG_EXAMPLE_CONNECT_WIFI || CONFIG_EXAMPLE_CONNECT_ETHERNET) */
static esp_err_t deferred_driver_init(void)
{
static bool is_inited = false;
ESP_RETURN_ON_FALSE(!is_inited, ESP_OK, TAG, "Deferred driver already initialized");
light_driver_init(false);
is_inited = true;
return ESP_OK;
}
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
static void esp_zigbee_alarm_bdb_commissioning(alarm_timer_arg_t arg)
{
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;
esp_zigbee_lock_acquire(portMAX_DELAY);
(void)ezb_bdb_start_top_level_commissioning(arg);
esp_zigbee_lock_release();
}
switch (sig_type) {
case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
#if CONFIG_EXAMPLE_CONNECT_WIFI
#if CONFIG_ESP_COEX_SW_COEXIST_ENABLE
esp_coex_wifi_i154_enable();
#endif /* CONFIG_ESP_COEX_SW_COEXIST_ENABLE */
ESP_RETURN_ON_FALSE(example_connect() == ESP_OK, , TAG, "Failed to connect to Wi-Fi");
ESP_RETURN_ON_FALSE(esp_wifi_set_ps(WIFI_PS_MIN_MODEM) == ESP_OK, , TAG, "Failed to set Wi-Fi minimum modem power save type");
#endif /* CONFIG_EXAMPLE_CONNECT_WIFI */
static bool esp_zigbee_app_signal_handler(const ezb_app_signal_t *app_signal)
{
ezb_app_signal_type_t signal_type = ezb_app_signal_get_type(app_signal);
switch (signal_type) {
case EZB_ZDO_SIGNAL_SKIP_STARTUP:
ESP_LOGI(TAG, "Initialize Zigbee stack");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
ezb_bdb_start_top_level_commissioning(EZB_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, "Device started up in %s factory-reset mode", esp_zb_bdb_is_factory_new() ? "" : "non");
if (esp_zb_bdb_is_factory_new()) {
ESP_LOGI(TAG, "Start network formation");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_FORMATION);
case EZB_BDB_SIGNAL_DEVICE_FIRST_START:
case EZB_BDB_SIGNAL_DEVICE_REBOOT: {
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *)ezb_app_signal_get_params(app_signal));
if (status == EZB_BDB_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Deferred driver initialization %s", deferred_driver_init() ? "failed" : "successful");
ESP_LOGI(TAG, "Device started up in%s factory-reset mode", ezb_bdb_is_factory_new() ? "" : " non");
if (ezb_bdb_is_factory_new()) {
ESP_ERROR_CHECK(ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_NETWORK_FORMATION));
} else {
esp_zb_bdb_open_network(180);
ESP_LOGI(TAG, "Device rebooted");
ezb_bdb_open_network(180);
ESP_LOGI(TAG, "Device reboot");
}
} else {
ESP_LOGE(TAG, "Failed to initialize Zigbee stack (status: %s)", esp_err_to_name(err_status));
ESP_LOGW(TAG, "The %s failed with status(0x%02x), please retry", ezb_app_signal_to_string(signal_type), status);
alarm_timer_schedule(esp_zigbee_alarm_bdb_commissioning, EZB_BDB_MODE_INITIALIZATION, 1000);
}
break;
case ESP_ZB_BDB_SIGNAL_FORMATION:
if (err_status == ESP_OK) {
esp_zb_ieee_addr_t ieee_address;
esp_zb_get_long_address(ieee_address);
ESP_LOGI(TAG, "Formed network successfully (Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, Channel:%d, Short Address: 0x%04hx)",
ieee_address[7], ieee_address[6], ieee_address[5], ieee_address[4],
ieee_address[3], ieee_address[2], ieee_address[1], ieee_address[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);
} break;
case EZB_BDB_SIGNAL_FORMATION: {
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *)ezb_app_signal_get_params(app_signal));
if (status == EZB_BDB_STATUS_SUCCESS) {
ezb_extpanid_t extended_pan_id;
ezb_nwk_get_extended_panid(&extended_pan_id);
ESP_LOGI(TAG, "Formed network successfully: PAN ID(0x%04hx, EXT: 0x%llx), Channel(%d), Short Address(0x%04hx)",
ezb_nwk_get_panid(), extended_pan_id.u64, ezb_nwk_get_current_channel(), ezb_nwk_get_short_address());
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_NETWORK_STEERING);
} else {
ESP_LOGI(TAG, "Restart network formation (status: %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);
ESP_LOGW(TAG, "Failed to form network with status(0x%02x)", status);
alarm_timer_schedule(esp_zigbee_alarm_bdb_commissioning, EZB_BDB_MODE_NETWORK_FORMATION, 1000);
}
break;
case ESP_ZB_BDB_SIGNAL_STEERING:
if (err_status == ESP_OK) {
ESP_LOGI(TAG, "Network steering started");
} break;
case EZB_BDB_SIGNAL_STEERING: {
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *)ezb_app_signal_get_params(app_signal));
if (status == EZB_BDB_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Network steering completed");
} else {
ESP_LOGW(TAG, "Failed to steering network with status(0x%02x)", status);
alarm_timer_schedule(esp_zigbee_alarm_bdb_commissioning, EZB_BDB_MODE_NETWORK_FORMATION, 1000);
}
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, "New device commissioned or rejoined (short: 0x%04hx)", dev_annce_params->device_short_addr);
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, "Network(0x%04hx) is open for %d seconds", esp_zb_get_pan_id(), *(uint8_t *)esp_zb_app_signal_get_params(p_sg_p));
} else {
ESP_LOGW(TAG, "Network(0x%04hx) closed, devices joining not allowed.", esp_zb_get_pan_id());
}
} break;
case EZB_ZDO_SIGNAL_DEVICE_ANNCE: {
const ezb_zdo_signal_device_annce_params_t *dev_annce_params = ezb_app_signal_get_params(app_signal);
ESP_LOGI(TAG, "New device commissioned or rejoined (short: 0x%04hx)", dev_annce_params->short_addr);
} break;
case EZB_ZDO_SIGNAL_LEAVE_INDICATION: {
const ezb_zdo_signal_leave_indication_params_t *leave_ind_params = ezb_app_signal_get_params(app_signal);
ESP_LOGI(TAG, "Zigbee Node(0x%04hx) is leaving network", leave_ind_params->short_addr);
} break;
case EZB_NWK_SIGNAL_PERMIT_JOIN_STATUS: {
uint8_t duration = *(uint8_t *)ezb_app_signal_get_params(app_signal);
if (duration) {
ESP_LOGI(TAG, "Network(0x%04hx) is open for %d seconds", ezb_nwk_get_panid(), duration);
} else {
ESP_LOGW(TAG, "Network(0x%04hx) closed, devices joining not allowed.", ezb_nwk_get_panid());
}
break;
case ESP_ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY:
ESP_LOGI(TAG, "Production configuration is %s", err_status == ESP_OK ? "ready" : "not present");
esp_zb_set_node_descriptor_manufacturer_code(ESP_MANUFACTURER_CODE);
break;
} break;
default:
ESP_LOGI(TAG, "ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type,
esp_err_to_name(err_status));
ESP_LOGI(TAG, "Zigbee APP Signal: %s(type: 0x%02x)", ezb_app_signal_to_string(signal_type), signal_type);
break;
}
return true;
}
static void zcl_core_set_attr_value_handler(ezb_zcl_set_attr_value_message_t *message)
{
ESP_RETURN_ON_FALSE(message, , TAG, "message is empty");
ESP_LOGI(TAG, "ZCL SetAttributeValue message for endpoint(%d) cluster(0x%04x) %s with status(0x%02x)", message->info.dst_ep,
message->info.cluster_id, message->info.cluster_role == EZB_ZCL_CLUSTER_SERVER ? "server" : "client",
message->info.status);
if (message->info.cluster_id == EZB_ZCL_CLUSTER_ID_ON_OFF) {
light_driver_set_power(*(uint8_t *)message->in.attribute.data.value);
ESP_LOGI(TAG, "Set On/Off: %d", *(uint8_t *)message->in.attribute.data.value);
} else {
ESP_LOGW(TAG, "Unsupported cluster ID(0x%04x)", message->info.cluster_id);
}
}
static void esp_zigbee_zcl_core_action_handler(ezb_zcl_core_action_callback_id_t callback_id, void *message)
{
switch (callback_id) {
case EZB_ZCL_CORE_SET_ATTR_VALUE_CB_ID:
zcl_core_set_attr_value_handler(message);
break;
case EZB_ZCL_CORE_DEFAULT_RSP_CB_ID: {
ezb_zcl_cmd_default_rsp_message_t *default_rsp = (ezb_zcl_cmd_default_rsp_message_t *)message;
ESP_LOGI(TAG, "Received ZCL Default Response with status(0x%02x)", default_rsp->in.status_code);
} break;
default:
ESP_LOGW(TAG, "ZCL Core Action: ID(0x%04lx)", callback_id);
break;
}
}
static void esp_zb_task(void *pvParameters)
esp_err_t esp_zigbee_create_zha_gateway_device(void)
{
/* initialize Zigbee stack */
esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZC_CONFIG();
esp_zb_init(&zb_nwk_cfg);
esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
esp_zb_ep_list_t *ep_list = esp_zb_ep_list_create();
esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create();
esp_zb_endpoint_config_t endpoint_config = {
.endpoint = ESP_ZB_GATEWAY_ENDPOINT,
.app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
.app_device_id = ESP_ZB_HA_REMOTE_CONTROL_DEVICE_ID,
.app_device_version = 0,
};
ezb_af_device_desc_t dev_desc = ezb_af_create_device_desc();
ezb_zha_custom_gateway_config_t gateway_cfg = EZB_ZHA_CUSTOM_GATEWAY_CONFIG();
ezb_af_ep_desc_t ep_desc = ezb_zha_create_custom_gateway(ESP_ZIGBEE_CUSTOM_GATEWAY_EP_ID, &gateway_cfg);
ezb_zcl_cluster_desc_t basic_desc = {0};
basic_desc = ezb_af_endpoint_get_cluster_desc(ep_desc, EZB_ZCL_CLUSTER_ID_BASIC, EZB_ZCL_CLUSTER_SERVER);
ezb_zcl_basic_cluster_desc_add_attr(basic_desc, EZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, (void *)ESP_MANUFACTURER_NAME);
ezb_zcl_basic_cluster_desc_add_attr(basic_desc, EZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, (void *)ESP_MODEL_IDENTIFIER);
ezb_af_endpoint_add_cluster_desc(ep_desc, ezb_zcl_on_off_create_cluster_desc(NULL, EZB_ZCL_CLUSTER_SERVER));
ESP_ERROR_CHECK(ezb_af_device_add_endpoint_desc(dev_desc, ep_desc));
ESP_ERROR_CHECK(ezb_af_device_desc_register(dev_desc));
ezb_zcl_core_action_handler_register(esp_zigbee_zcl_core_action_handler);
return ESP_OK;
}
esp_err_t esp_zigbee_setup_commissioning(void)
{
ezb_aps_secur_enable_distributed_security(false);
ESP_ERROR_CHECK(ezb_bdb_set_primary_channel_set(ESP_ZIGBEE_PRIMARY_CHANNEL_MASK));
ESP_ERROR_CHECK(ezb_bdb_set_secondary_channel_set(ESP_ZIGBEE_SECONDARY_CHANNEL_MASK));
ESP_ERROR_CHECK(ezb_app_signal_add_handler(esp_zigbee_app_signal_handler));
return ESP_OK;
}
static void esp_zigbee_stack_main_task(void *pvParameters)
{
esp_zigbee_config_t zigbee_config = ESP_ZIGBEE_DEFAULT_CONFIG();
#if CONFIG_AUTO_UPDATE_RCP
esp_rcp_update_config_t rcp_config = ESP_ZIGBEE_RCP_CONFIG();
ESP_ERROR_CHECK(esp_zigbee_rcp_init(&rcp_config));
#endif
ESP_ERROR_CHECK(esp_zigbee_init(&zigbee_config));
#if CONFIG_AUTO_UPDATE_RCP
ESP_ERROR_CHECK(esp_zigbee_rcp_update());
#endif
#if (CONFIG_EXAMPLE_CONNECT_WIFI || CONFIG_EXAMPLE_CONNECT_ETHERNET)
ESP_ERROR_CHECK(esp_zigbee_connect_netif());
#endif
ESP_ERROR_CHECK(esp_zigbee_setup_commissioning());
ESP_ERROR_CHECK(esp_zigbee_create_zha_gateway_device());
ESP_ERROR_CHECK(esp_zigbee_start(false));
esp_zigbee_launch_mainloop();
esp_zigbee_deinit();
#if CONFIG_AUTO_UPDATE_RCP
esp_zigbee_rcp_deinit();
#endif
esp_zb_attribute_list_t *basic_cluser = esp_zb_basic_cluster_create(NULL);
esp_zb_basic_cluster_add_attr(basic_cluser, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, ESP_MANUFACTURER_NAME);
esp_zb_basic_cluster_add_attr(basic_cluser, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, ESP_MODEL_IDENTIFIER);
esp_zb_cluster_list_add_basic_cluster(cluster_list, basic_cluser, 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);
esp_zb_ep_list_add_gateway_ep(ep_list, cluster_list, endpoint_config);
esp_zb_device_register(ep_list);
ESP_ERROR_CHECK(esp_zb_start(false));
esp_zb_stack_main_loop();
vTaskDelete(NULL);
}
void app_main(void)
{
esp_zb_platform_config_t config = {
.radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(),
.host_config = ESP_ZB_DEFAULT_HOST_CONFIG(),
};
ESP_ERROR_CHECK(esp_zb_platform_config(&config));
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
ESP_ERROR_CHECK(esp_zb_gateway_console_init());
#endif
xTaskCreate(esp_zb_task, "Zigbee_main", 8192, NULL, 5, NULL);
ESP_LOGI(TAG, "Start ESP Zigbee Stack");
xTaskCreate(esp_zigbee_stack_main_task, "Zigbee_main", 4096 * 2, NULL, 5, NULL);
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
@@ -12,62 +12,87 @@
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include "esp_err.h"
#include "esp_zigbee_core.h"
#pragma once
/* Zigbee Configuration */
#define MAX_CHILDREN 10 /* the max amount of connected devices */
#define INSTALLCODE_POLICY_ENABLE false /* enable the install code policy for security */
#define ESP_ZB_PRIMARY_CHANNEL_MASK (1l << 13) /* Zigbee primary channel mask use in the example */
#define ESP_ZB_GATEWAY_ENDPOINT 1 /* Gateway endpoint identifier */
#define APP_PROD_CFG_CURRENT_VERSION 0x0001 /* Production configuration version */
#define ESP_ZIGBEE_PRIMARY_CHANNEL_MASK (1U << CONFIG_ZB_EXAMPLE_PRIMARY_CHANNEL)
#define ESP_ZIGBEE_SECONDARY_CHANNEL_MASK CONFIG_ZB_EXAMPLE_SECONDARY_CHANNEL_MASK
/* Basic manufacturer information */
#define ESP_MANUFACTURER_CODE 0x131B /* Customized manufacturer code */
#define ESP_MANUFACTURER_NAME "\x09""ESPRESSIF" /* Customized manufacturer name */
#define ESP_MODEL_IDENTIFIER "\x07"CONFIG_IDF_TARGET /* Customized model identifier */
#define ESP_ZIGBEE_CUSTOM_GATEWAY_EP_ID (10)
/* RCP connection pins */
#define HOST_RX_PIN_TO_RCP_TX 4
#define HOST_TX_PIN_TO_RCP_RX 5
#define ESP_ZIGBEE_STORAGE_PARTITION_NAME "nvs"
#define ESP_ZB_ZC_CONFIG() \
{ \
.esp_zb_role = ESP_ZB_DEVICE_TYPE_COORDINATOR, \
.install_code_policy = INSTALLCODE_POLICY_ENABLE, \
.nwk_cfg.zczr_cfg = { \
.max_children = MAX_CHILDREN, \
}, \
#define ESP_MANUFACTURER_NAME "\x09""ESPRESSIF"
#define ESP_MODEL_IDENTIFIER "\x07"CONFIG_IDF_TARGET
#if defined(CONFIG_ZB_GW_RCP_CHIP_ESP32C6)
#define ESP_ZIGBEE_RCP_TARGET_CHIP ESP32C6_CHIP
#elif defined(CONFIG_ZB_GW_RCP_CHIP_ESP32H2)
#define ESP_ZIGBEE_RCP_TARGET_CHIP ESP32H2_CHIP
#else
#define ESP_ZIGBEE_RCP_TARGET_CHIP ESP_UNKNOWN_CHIP
#endif
#define ESP_ZIGBEE_ZC_CONFIG() \
{ \
.device_type = EZB_NWK_DEVICE_TYPE_COORDINATOR, \
.install_code_policy = false, \
.zczr_config = { \
.max_children = 10, \
}, \
}
#if CONFIG_ZB_RADIO_NATIVE
#define ESP_ZB_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = ZB_RADIO_MODE_NATIVE, \
#if CONFIG_SOC_IEEE802154_SUPPORTED
#define ESP_ZIGBEE_PLATFORM_CONFIG() \
{ \
.storage_partition_name = ESP_ZIGBEE_STORAGE_PARTITION_NAME, \
.radio_config = { \
.radio_mode = ESP_ZIGBEE_RADIO_MODE_NATIVE, \
}, \
}
#else
#define ESP_ZB_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = ZB_RADIO_MODE_UART_RCP, \
.radio_uart_config = { \
.port = 1, \
.uart_config = \
{ \
.baud_rate = 460800, \
.data_bits = UART_DATA_8_BITS, \
.parity = UART_PARITY_DISABLE, \
.stop_bits = UART_STOP_BITS_1, \
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \
.rx_flow_ctrl_thresh = 0, \
.source_clk = UART_SCLK_DEFAULT, \
}, \
.rx_pin = HOST_RX_PIN_TO_RCP_TX, \
.tx_pin = HOST_TX_PIN_TO_RCP_RX, \
}, \
#define ESP_ZIGBEE_UART_CONFIG() \
{ \
.port = 1, \
.uart_config = \
{ \
.baud_rate = 460800, \
.data_bits = UART_DATA_8_BITS, \
.parity = UART_PARITY_DISABLE, \
.stop_bits = UART_STOP_BITS_1, \
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE, \
.rx_flow_ctrl_thresh = 0, \
.source_clk = UART_SCLK_DEFAULT, \
}, \
.rx_pin = CONFIG_DEFAULT_PIN_TO_RCP_TX, \
.tx_pin = CONFIG_DEFAULT_PIN_TO_RCP_RX, \
}
#define ESP_ZIGBEE_PLATFORM_CONFIG() \
{ \
.storage_partition_name = ESP_ZIGBEE_STORAGE_PARTITION_NAME, \
.radio_config = { \
.radio_mode = ESP_ZIGBEE_RADIO_MODE_UART_RCP, \
.radio_uart_config = ESP_ZIGBEE_UART_CONFIG(), \
}, \
}
#endif
#define ESP_ZB_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = ZB_HOST_CONNECTION_MODE_NONE, \
}
#define ESP_ZIGBEE_DEFAULT_CONFIG() \
{ \
.device_config = ESP_ZIGBEE_ZC_CONFIG(), \
.platform_config = ESP_ZIGBEE_PLATFORM_CONFIG(), \
};
#define ESP_ZIGBEE_RCP_CONFIG() \
{ \
.rcp_type = RCP_TYPE_UART, \
.uart_rx_pin = CONFIG_DEFAULT_PIN_TO_RCP_TX, \
.uart_tx_pin = CONFIG_DEFAULT_PIN_TO_RCP_RX, \
.uart_port = 1, \
.uart_baudrate = 115200, \
.reset_pin = CONFIG_DEFAULT_PIN_TO_RCP_RESET, \
.boot_pin = CONFIG_DEFAULT_PIN_TO_RCP_BOOT, \
.update_baudrate = 460800, \
.firmware_dir = "/" CONFIG_RCP_PARTITION_NAME "/ot_rcp", \
.target_chip = ESP_ZIGBEE_RCP_TARGET_CHIP, \
}

View File

@@ -1,9 +1,13 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp-zboss-lib: "~1.6.0"
espressif/esp-zigbee-lib: "~1.6.0"
## Required IDF version
espressif/esp_rcp_update: "~1.6.0"
espressif/esp-zigbee-lib:
version: ">=2.0.0"
idf:
version: ">=5.0.0"
version: ">=5.2"
example_common:
path: ${IDF_PATH}/examples/zigbee/zb_common_components/example_common
light_driver:
path: ${IDF_PATH}/examples/zigbee/zb_common_components/light_driver
protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View File

@@ -0,0 +1,124 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* This example code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "esp_log.h"
#include "esp_check.h"
#include "sdkconfig.h"
#if CONFIG_AUTO_UPDATE_RCP
#include "esp_rcp_update.h"
#include "esp_spiffs.h"
#endif
#if CONFIG_OPENTHREAD_SPINEL_ONLY
#include "esp_radio_spinel.h"
#define ESP_ZIGBEE_RCP_VERSION_MAX_SIZE 80
#endif
#include "esp_zigbee.h"
#include "zigbee_rcp.h"
#define TAG "ZIGBEE_RCP"
#if CONFIG_AUTO_UPDATE_RCP
static void rcp_update(void)
{
ESP_LOGI(TAG, "Update RCP");
/* Stop Zigbee stack during RCP update */
esp_zigbee_deinit();
if (esp_rcp_update() != ESP_OK) {
esp_rcp_mark_image_verified(false);
}
esp_restart();
}
static void rcp_fatal_failure_handler(void)
{
#if CONFIG_AUTO_UPDATE_RCP
ESP_LOGW(TAG, "RCP failure, re-flashing RCP");
rcp_update();
#endif
esp_restart();
}
static void rcp_failure_handler(void)
{
ESP_LOGW(TAG, "RCP failure, reset RCP");
esp_rcp_reset();
}
esp_err_t esp_zigbee_rcp_update(void)
{
char rcp_version[ESP_ZIGBEE_RCP_VERSION_MAX_SIZE] = {0};
ESP_RETURN_ON_ERROR(esp_radio_spinel_rcp_version_get(rcp_version, ESP_RADIO_SPINEL_ZIGBEE), TAG,
"Failed to get rcp version from radio spinel");
#if (CONFIG_AUTO_UPDATE_RCP)
char storage_rcp_version[ESP_ZIGBEE_RCP_VERSION_MAX_SIZE] = {0};
if (esp_rcp_load_version_in_storage(storage_rcp_version, sizeof(storage_rcp_version)) == ESP_OK) {
if (strcmp(storage_rcp_version, rcp_version)) {
ESP_LOGI(TAG, "Update the RCP version from %s to %s", storage_rcp_version, rcp_version);
rcp_update();
} else {
ESP_LOGI(TAG, "Running RCP version: %s", rcp_version);
esp_rcp_mark_image_verified(true);
}
} else {
ESP_LOGI(TAG, "No RCP firmware found in storage, reboot to try next image");
esp_rcp_mark_image_verified(false);
esp_restart();
}
#endif
return ESP_OK;
}
static esp_err_t spiffs_init(void)
{
esp_vfs_spiffs_conf_t rcp_fw_conf = {
.base_path = "/" CONFIG_RCP_PARTITION_NAME,
.partition_label = CONFIG_RCP_PARTITION_NAME,
.max_files = 10,
.format_if_mount_failed = false,
};
return esp_vfs_spiffs_register(&rcp_fw_conf);
}
static void spiffs_deinit(void)
{
esp_vfs_spiffs_unregister(CONFIG_RCP_PARTITION_NAME);
}
esp_err_t esp_zigbee_rcp_init(esp_rcp_update_config_t *config)
{
ESP_RETURN_ON_FALSE(config != NULL, ESP_ERR_INVALID_ARG, TAG, "config is NULL");
ESP_ERROR_CHECK(spiffs_init());
ESP_ERROR_CHECK(esp_rcp_update_init(config));
#if CONFIG_OPENTHREAD_SPINEL_ONLY
esp_radio_spinel_register_rcp_failure_handler(rcp_failure_handler, ESP_RADIO_SPINEL_ZIGBEE);
esp_radio_spinel_set_compatibility_error_callback(rcp_fatal_failure_handler);
esp_radio_spinel_set_coprocessor_reset_failure_callback(rcp_fatal_failure_handler);
#endif
return ESP_OK;
}
void esp_zigbee_rcp_deinit(void)
{
#if CONFIG_OPENTHREAD_SPINEL_ONLY
esp_radio_spinel_set_coprocessor_reset_failure_callback(NULL);
esp_radio_spinel_set_compatibility_error_callback(NULL);
esp_radio_spinel_register_rcp_failure_handler(NULL, ESP_RADIO_SPINEL_ZIGBEE);
#endif
esp_rcp_update_deinit();
spiffs_deinit();
}
#endif

View File

@@ -0,0 +1,22 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* This example code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include "esp_err.h"
#include "esp_rcp_update.h"
esp_err_t esp_zigbee_rcp_init(esp_rcp_update_config_t *config);
void esp_zigbee_rcp_deinit(void);
esp_err_t esp_zigbee_rcp_update(void);

View File

@@ -1,8 +1,8 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1400K,
zb_storage, data, fat, 0x16e000,16K,
zb_fct, data, fat, 0x172000,1K,
rcp_fw, data, spiffs, 0x173000,500k,
nvs, data, nvs, , 0x6000,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 1708K,
zb_fct, data, fat, , 1K,
rcp_fw, data, spiffs, , 640k,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, otadata, data, ota, , 0x2000,
5 factory, app, factory, 0x10000, 1400K, phy_init, data, phy, , 0x1000,
6 zb_storage, data, fat, 0x16e000,16K, factory, app, factory, , 1708K,
7 zb_fct, data, fat, 0x172000,1K, zb_fct, data, fat, , 1K,
8 rcp_fw, data, spiffs, 0x173000,500k, rcp_fw, data, spiffs, , 640k,

View File

@@ -8,12 +8,28 @@ CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_MD5=y
# end of Partition Table
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
#
# Serial flasher config
#
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
# end of Serial flasher config
#
# Zboss
# Event
#
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
#end of Event
#
# Zigbee
#
CONFIG_ZB_ENABLED=y
CONFIG_ZB_ZCZR=y
# end of Zboss
# end of Component config
# end of Zigbee
#
# ESP Zigbee gateway RCP update
#
CONFIG_AUTO_UPDATE_RCP=n
# end of ESP Zigbee gateway RCP update

View File

@@ -3,6 +3,9 @@
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
list(PREPEND SDKCONFIG_DEFAULTS
"${IDF_PATH}/examples/zigbee/zb_common_components/example_common/sdkconfig.mbedtls_minimal"
"sdkconfig.defaults")
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(light_bulb)

View File

@@ -1,9 +1,9 @@
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- | -------- |
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-S31 |
| ----------------- | -------- | -------- | -------- | --------- | -------- | --------- |
# Light Bulb Example
This test code shows how to configure Zigbee end device and use it as HA on/off light bulb.
This example demonstrates how to configure a Home Automation on/off light on a Zigbee Coordinator using ESP Zigbee SDK v2.
The ESP Zigbee SDK provides more examples and tools for productization:
* [ESP Zigbee SDK Docs](https://docs.espressif.com/projects/esp-zigbee-sdk)
@@ -11,13 +11,13 @@ The ESP Zigbee SDK provides more examples and tools for productization:
## Hardware Required
* One development board with ESP32-H2 SoC acting as Zigbee end-device (loaded with HA_on_off_light example)
* One 802.15.4-enabled development board running this example as a Zigbee Coordinator
* A USB cable for power supply and programming
* Choose another ESP32-H2 as Zigbee coordinator (see [HA_on_off_switch example](../HA_on_off_switch))
* A second board running as a Zigbee Router/End Device (see [HA_on_off_switch](../HA_on_off_switch))
## Configure the project
Before project configuration and build, make sure to set the correct chip target using `idf.py --preview set-target TARGET` command.
Before project configuration and build, make sure to set the correct chip target using `idf.py set-target TARGET`.
## Erase the NVRAM
@@ -31,36 +31,28 @@ Build the project, flash it to the board, and start the monitor tool to view the
## Example Output
As you run the example, you will see the following log:
When the program starts, the board forms a Zigbee network and opens it for joining. As you run the example, you will see output similar to:
```
I (403) app_start: Starting scheduler on CPU0
I (408) main_task: Started on CPU0
I (408) main_task: Calling app_main()
I (428) phy: phy_version: 230,2, 9aae6ea, Jan 15 2024, 11:17:12
I (428) phy: libbtbb version: 944f18e, Jan 15 2024, 11:17:25
I (438) main_task: Returned from app_main()
I (548) ESP_ZB_ON_OFF_LIGHT: ZDO signal: ZDO Config Ready (0x17), status: ESP_FAIL
I (548) ESP_ZB_ON_OFF_LIGHT: Initialize Zigbee stack
W (548) rmt: channel resolution loss, real=10666666
I (558) gpio: GPIO[8]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
I (548) ESP_ZB_ON_OFF_LIGHT: Deferred driver initialization successful
I (568) ESP_ZB_ON_OFF_LIGHT: Device started up in factory-reset mode
I (578) ESP_ZB_ON_OFF_LIGHT: Start network steering
I (3558) ESP_ZB_ON_OFF_LIGHT: Joined network successfully (Extended PAN ID: 74:4d:bd:ff:fe:63:f7:30, PAN ID: 0x13af, Channel:13, Short Address: 0x7c16)
I (10238) ESP_ZB_ON_OFF_LIGHT: Received message: endpoint(10), cluster(0x6), attribute(0x0), data size(1)
I (10238) ESP_ZB_ON_OFF_LIGHT: Light sets to On
I (10798) ESP_ZB_ON_OFF_LIGHT: Received message: endpoint(10), cluster(0x6), attribute(0x0), data size(1)
I (10798) ESP_ZB_ON_OFF_LIGHT: Light sets to Off
I (11228) ESP_ZB_ON_OFF_LIGHT: Received message: endpoint(10), cluster(0x6), attribute(0x0), data size(1)
I (11228) ESP_ZB_ON_OFF_LIGHT: Light sets to On
I (11618) ESP_ZB_ON_OFF_LIGHT: Received message: endpoint(10), cluster(0x6), attribute(0x0), data size(1)
I (11618) ESP_ZB_ON_OFF_LIGHT: Light sets to Off
I (435) ON_OFF_LIGHT: Start ESP Zigbee Stack
I (475) ON_OFF_LIGHT: Initialize Zigbee stack
I (475) ON_OFF_LIGHT: Deferred driver initialization successful
I (485) ON_OFF_LIGHT: Device started up in factory-reset mode
I (795) ON_OFF_LIGHT: Formed network successfully: PAN ID(0x3051, EXT: 0x4831b7fffec183f0), Channel(13), Short Address(0x0000)
I (1175) ON_OFF_LIGHT: Network steering completed
I (3705) ON_OFF_LIGHT: New device commissioned or rejoined(short: 0xa515)
```
## Light Control Functions
* By toggling the switch button (BOOT) on the ESP32-H2 board loaded with the `HA_on_off_switch` example, the LED on this board loaded with `HA_on_off_light` example will be on and off.
* By toggling the switch button (BOOT) on the board loaded with the `HA_on_off_switch` example, the LED on this board will turn on and off.
```
I (26905) ON_OFF_LIGHT: ZCL SetAttributeValue message for endpoint(10) cluster(0x0006) server with status(0x00)
I (26905) ON_OFF_LIGHT: Set On/Off: 1
I (27535) ON_OFF_LIGHT: ZCL SetAttributeValue message for endpoint(10) cluster(0x0006) server with status(0x00)
I (27535) ON_OFF_LIGHT: Set On/Off: 0
```
## Troubleshooting

View File

@@ -1,5 +1,3 @@
idf_component_register(
SRC_DIRS "."
INCLUDE_DIRS "."
PRIV_REQUIRES nvs_flash esp_driver_uart ieee802154
)
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES nvs_flash esp_driver_uart ieee802154)

View File

@@ -1,7 +1,7 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
* SPDX-License-Identifier: LicenseRef-Included
*
* Zigbee HA_on_off_light Example
*
@@ -11,141 +11,188 @@
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "ha/esp_zigbee_ha_standard.h"
#include "zcl_utility.h"
#include "light_driver.h"
#include "alarm_timer.h"
#include "esp_zigbee.h"
#include "ezbee/zha.h"
#include "esp_zb_light.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 = "ON_OFF_LIGHT";
static const char *TAG = "ESP_ZB_ON_OFF_LIGHT";
/********************* Define functions **************************/
static esp_err_t deferred_driver_init(void)
esp_err_t deferred_driver_init(void)
{
light_driver_init(LIGHT_DEFAULT_OFF);
static bool is_inited = false;
ESP_RETURN_ON_FALSE(!is_inited, ESP_OK, TAG, "Deferred driver already initialized");
light_driver_init(false);
is_inited = true;
return ESP_OK;
}
static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask)
static void esp_zigbee_alarm_bdb_commissioning(alarm_timer_arg_t arg)
{
ESP_RETURN_ON_FALSE(esp_zb_bdb_start_top_level_commissioning(mode_mask) == ESP_OK, , TAG, "Failed to start Zigbee commissioning");
esp_zigbee_lock_acquire(portMAX_DELAY);
(void)ezb_bdb_start_top_level_commissioning(arg);
esp_zigbee_lock_release();
}
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
static bool esp_zigbee_app_signal_handler(const ezb_app_signal_t *app_signal)
{
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:
ezb_app_signal_type_t signal_type = ezb_app_signal_get_type(app_signal);
switch (signal_type) {
case EZB_ZDO_SIGNAL_SKIP_STARTUP:
ESP_LOGI(TAG, "Initialize Zigbee stack");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_INITIALIZATION);
break;
case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:
if (err_status == ESP_OK) {
case EZB_BDB_SIGNAL_DEVICE_FIRST_START:
case EZB_BDB_SIGNAL_DEVICE_REBOOT: {
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *)ezb_app_signal_get_params(app_signal));
if (status == EZB_BDB_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Deferred driver initialization %s", deferred_driver_init() ? "failed" : "successful");
ESP_LOGI(TAG, "Device started up in %s factory-reset mode", esp_zb_bdb_is_factory_new() ? "" : "non");
if (esp_zb_bdb_is_factory_new()) {
ESP_LOGI(TAG, "Start network steering");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
ESP_LOGI(TAG, "Device started up in%s factory-reset mode", ezb_bdb_is_factory_new() ? "" : " non");
if (ezb_bdb_is_factory_new()) {
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_NETWORK_FORMATION);
} else {
ESP_LOGI(TAG, "Device rebooted");
ezb_bdb_open_network(180);
ESP_LOGI(TAG, "Device reboot");
}
} else {
/* commissioning failed */
ESP_LOGW(TAG, "Failed to initialize Zigbee stack (status: %s)", esp_err_to_name(err_status));
ESP_LOGW(TAG, "%s failed with status(0x%02x), please retry", ezb_app_signal_to_string(signal_type), status);
alarm_timer_schedule(esp_zigbee_alarm_bdb_commissioning, EZB_BDB_MODE_INITIALIZATION, 1000);
}
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, "Joined network successfully (Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, Channel:%d, Short Address: 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());
} break;
case EZB_BDB_SIGNAL_FORMATION: {
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *)ezb_app_signal_get_params(app_signal));
if (status == EZB_BDB_STATUS_SUCCESS) {
ezb_extpanid_t extended_pan_id;
ezb_nwk_get_extended_panid(&extended_pan_id);
ESP_LOGI(TAG, "Formed network successfully: PAN ID(0x%04hx, EXT: 0x%llx), Channel(%d), Short Address(0x%04hx)",
ezb_nwk_get_panid(), extended_pan_id.u64, ezb_nwk_get_current_channel(), ezb_nwk_get_short_address());
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_NETWORK_STEERING);
} else {
ESP_LOGI(TAG, "Network steering was not successful (status: %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);
ESP_LOGW(TAG, "Failed to form network with status(0x%02x)", status);
alarm_timer_schedule(esp_zigbee_alarm_bdb_commissioning, EZB_BDB_MODE_NETWORK_FORMATION, 1000);
}
break;
} break;
case EZB_BDB_SIGNAL_STEERING: {
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *)ezb_app_signal_get_params(app_signal));
if (status == EZB_BDB_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Network steering completed");
} else {
ESP_LOGW(TAG, "Failed to steering network with status(0x%02x)", status);
alarm_timer_schedule(esp_zigbee_alarm_bdb_commissioning, EZB_BDB_MODE_NETWORK_STEERING, 1000);
}
} break;
case EZB_ZDO_SIGNAL_DEVICE_ANNCE: {
const ezb_zdo_signal_device_annce_params_t *dev_annce_params = ezb_app_signal_get_params(app_signal);
ESP_LOGI(TAG, "New device commissioned or rejoined(short: 0x%04hx)", dev_annce_params->short_addr);
} break;
case EZB_NWK_SIGNAL_PERMIT_JOIN_STATUS: {
uint8_t duration = *(uint8_t *)ezb_app_signal_get_params(app_signal);
if (duration) {
ESP_LOGI(TAG, "Network(0x%04hx) is open for %d seconds", ezb_nwk_get_panid(), duration);
} else {
ESP_LOGW(TAG, "Network(0x%04hx) closed, devices joining not allowed.", ezb_nwk_get_panid());
}
} break;
default:
ESP_LOGI(TAG, "ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type,
esp_err_to_name(err_status));
ESP_LOGI(TAG, "Zigbee APP Signal: %s(type: 0x%02x)", ezb_app_signal_to_string(signal_type), signal_type);
break;
}
return true;
}
static esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message)
static void zcl_core_set_attr_value_handler(ezb_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, "Empty message");
ESP_RETURN_ON_FALSE(message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS, ESP_ERR_INVALID_ARG, TAG, "Received message: error status(%d)",
message->info.status);
ESP_LOGI(TAG, "Received message: endpoint(%d), cluster(0x%x), attribute(0x%x), data size(%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, "Light sets to %s", light_state ? "On" : "Off");
light_driver_set_power(light_state);
}
}
ESP_RETURN_ON_FALSE(message, , TAG, "message is empty");
ESP_LOGI(TAG, "ZCL SetAttributeValue message for endpoint(%d) cluster(0x%04x) %s with status(0x%02x)", message->info.dst_ep,
message->info.cluster_id, message->info.cluster_role == EZB_ZCL_CLUSTER_SERVER ? "server" : "client",
message->info.status);
if (message->info.cluster_id == EZB_ZCL_CLUSTER_ID_ON_OFF) {
light_driver_set_power(*(uint8_t *)message->in.attribute.data.value);
ESP_LOGI(TAG, "Set On/Off: %d", *(uint8_t *)message->in.attribute.data.value);
} else {
ESP_LOGW(TAG, "Unsupported cluster ID(0x%04x)", message->info.cluster_id);
}
return ret;
}
static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message)
static void esp_zigbee_zcl_core_action_handler(ezb_zcl_core_action_callback_id_t callback_id, 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);
case EZB_ZCL_CORE_SET_ATTR_VALUE_CB_ID:
zcl_core_set_attr_value_handler(message);
break;
case EZB_ZCL_CORE_DEFAULT_RSP_CB_ID: {
ezb_zcl_cmd_default_rsp_message_t *default_rsp = (ezb_zcl_cmd_default_rsp_message_t *)message;
ESP_LOGI(TAG, "Received ZCL Default Response with status(0x%02x)", default_rsp->in.status_code);
} break;
default:
ESP_LOGW(TAG, "Receive Zigbee action(0x%x) callback", callback_id);
ESP_LOGW(TAG, "ZCL Core Action: ID(0x%04lx)", callback_id);
break;
}
return ret;
}
static void esp_zb_task(void *pvParameters)
esp_err_t esp_zigbee_create_zha_on_off_light_device(void)
{
/* 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);
zcl_basic_manufacturer_info_t info = {
.manufacturer_name = ESP_MANUFACTURER_NAME,
.model_identifier = ESP_MODEL_IDENTIFIER,
};
ezb_af_device_desc_t dev_desc = ezb_af_create_device_desc();
ezb_zha_on_off_light_config_t light_cfg = EZB_ZHA_ON_OFF_LIGHT_CONFIG();
ezb_af_ep_desc_t ep_desc = ezb_zha_create_on_off_light(ESP_ZIGBEE_HA_ON_OFF_LIGHT_EP_ID, &light_cfg);
ezb_zcl_cluster_desc_t basic_desc = {0};
esp_zcl_utility_add_ep_basic_manufacturer_info(esp_zb_on_off_light_ep, HA_ESP_LIGHT_ENDPOINT, &info);
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_stack_main_loop();
basic_desc = ezb_af_endpoint_get_cluster_desc(ep_desc, EZB_ZCL_CLUSTER_ID_BASIC, EZB_ZCL_CLUSTER_SERVER);
ezb_zcl_basic_cluster_desc_add_attr(basic_desc, EZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, (void *)ESP_MANUFACTURER_NAME);
ezb_zcl_basic_cluster_desc_add_attr(basic_desc, EZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, (void *)ESP_MODEL_IDENTIFIER);
ESP_ERROR_CHECK(ezb_af_device_add_endpoint_desc(dev_desc, ep_desc));
ESP_ERROR_CHECK(ezb_af_device_desc_register(dev_desc));
ezb_zcl_core_action_handler_register(esp_zigbee_zcl_core_action_handler);
return ESP_OK;
}
esp_err_t esp_zigbee_setup_commissioning(void)
{
ezb_aps_secur_enable_distributed_security(false);
ESP_ERROR_CHECK(ezb_bdb_set_primary_channel_set(ESP_ZIGBEE_PRIMARY_CHANNEL_MASK));
ESP_ERROR_CHECK(ezb_bdb_set_secondary_channel_set(ESP_ZIGBEE_SECONDARY_CHANNEL_MASK));
ESP_ERROR_CHECK(ezb_app_signal_add_handler(esp_zigbee_app_signal_handler));
return ESP_OK;
}
static void esp_zigbee_stack_main_task(void *pvParameters)
{
esp_zigbee_config_t config = ESP_ZIGBEE_DEFAULT_CONFIG();
ESP_ERROR_CHECK(esp_zigbee_init(&config));
ESP_ERROR_CHECK(esp_zigbee_setup_commissioning());
ESP_ERROR_CHECK(esp_zigbee_create_zha_on_off_light_device());
ESP_ERROR_CHECK(esp_zigbee_start(false));
esp_zigbee_launch_mainloop();
esp_zigbee_deinit();
vTaskDelete(NULL);
}
void app_main(void)
{
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);
ESP_LOGI(TAG, "Start ESP Zigbee Stack");
xTaskCreate(esp_zigbee_stack_main_task, "Zigbee_main", 4096, NULL, 5, NULL);
}

View File

@@ -1,7 +1,7 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
* SPDX-License-Identifier: LicenseRef-Included
*
* Zigbee HA_on_off_light Example
*
@@ -12,36 +12,41 @@
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include "esp_zigbee_core.h"
#include "light_driver.h"
#pragma once
/* Zigbee configuration */
#define INSTALLCODE_POLICY_ENABLE false /* enable the install code policy for security */
#define ED_AGING_TIMEOUT ESP_ZB_ED_AGING_TIMEOUT_64MIN /* aging timeout of device */
#define ED_KEEP_ALIVE 3000 /* 3000 millisecond */
#define HA_ESP_LIGHT_ENDPOINT 10 /* esp light bulb device endpoint, used to process light controlling commands */
#define ESP_ZB_PRIMARY_CHANNEL_MASK ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK /* Zigbee primary channel mask use in the example */
#define ESP_ZIGBEE_PRIMARY_CHANNEL_MASK (1U << CONFIG_ZB_EXAMPLE_PRIMARY_CHANNEL)
#define ESP_ZIGBEE_SECONDARY_CHANNEL_MASK CONFIG_ZB_EXAMPLE_SECONDARY_CHANNEL_MASK
/* Basic manufacturer information */
#define ESP_MANUFACTURER_NAME "\x09""ESPRESSIF" /* Customized manufacturer name */
#define ESP_MODEL_IDENTIFIER "\x07"CONFIG_IDF_TARGET /* Customized model identifier */
#define ESP_ZIGBEE_HA_ON_OFF_LIGHT_EP_ID (10)
#define ESP_ZB_ZED_CONFIG() \
{ \
.esp_zb_role = ESP_ZB_DEVICE_TYPE_ED, \
.install_code_policy = INSTALLCODE_POLICY_ENABLE, \
.nwk_cfg.zed_cfg = { \
.ed_timeout = ED_AGING_TIMEOUT, \
.keep_alive = ED_KEEP_ALIVE, \
}, \
#define ESP_ZIGBEE_STORAGE_PARTITION_NAME "nvs"
#define ESP_MANUFACTURER_NAME "\x09""ESPRESSIF"
#define ESP_MODEL_IDENTIFIER "\x07"CONFIG_IDF_TARGET
#define ESP_ZIGBEE_ZC_CONFIG() \
{ \
.device_type = EZB_NWK_DEVICE_TYPE_COORDINATOR, \
.install_code_policy = false, \
.zczr_config = { \
.max_children = 10, \
}, \
}
#define ESP_ZB_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = ZB_RADIO_MODE_NATIVE, \
#if CONFIG_SOC_IEEE802154_SUPPORTED
#define ESP_ZIGBEE_PLATFORM_CONFIG() \
{ \
.storage_partition_name = ESP_ZIGBEE_STORAGE_PARTITION_NAME, \
.radio_config = { \
.radio_mode = ESP_ZIGBEE_RADIO_MODE_NATIVE, \
}, \
}
#else
#warning "The example is not for IEEE 802.15.4-disabled SoC usage, please refer to esp_zigbee_gateway for RCP configuration"
#endif
#define ESP_ZB_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = ZB_HOST_CONNECTION_MODE_NONE, \
}
#define ESP_ZIGBEE_DEFAULT_CONFIG() \
{ \
.device_config = ESP_ZIGBEE_ZC_CONFIG(), \
.platform_config = ESP_ZIGBEE_PLATFORM_CONFIG(), \
};

View File

@@ -1,10 +1,11 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp-zboss-lib: "~1.6.0"
espressif/esp-zigbee-lib: "~1.6.0"
espressif/led_strip: "~3.0.0"
## Required IDF version
idf:
version: ">=5.0.0"
examples_utils:
path: ${IDF_PATH}/examples/zigbee/zb_common_components/examples_utils
version: ">=5.2"
espressif/esp-zigbee-lib:
version: ">=2.0.0"
example_common:
path: ${IDF_PATH}/examples/zigbee/zb_common_components/example_common
light_driver:
path: ${IDF_PATH}/examples/zigbee/zb_common_components/light_driver

View File

@@ -1,62 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Espressif Systems
* integrated circuit in a product or a software update for such product,
* must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* 4. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "esp_log.h"
#include "led_strip.h"
#include "light_driver.h"
static led_strip_handle_t s_led_strip;
static uint8_t s_red = 255, s_green = 255, s_blue = 255;
void light_driver_set_power(bool power)
{
ESP_ERROR_CHECK(led_strip_set_pixel(s_led_strip, 0, s_red * power, s_green * power, s_blue * power));
ESP_ERROR_CHECK(led_strip_refresh(s_led_strip));
}
void light_driver_init(bool power)
{
led_strip_config_t led_strip_conf = {
.max_leds = CONFIG_EXAMPLE_STRIP_LED_NUMBER,
.strip_gpio_num = CONFIG_EXAMPLE_STRIP_LED_GPIO,
};
led_strip_rmt_config_t rmt_conf = {
.resolution_hz = 10 * 1000 * 1000, // 10MHz
};
ESP_ERROR_CHECK(led_strip_new_rmt_device(&led_strip_conf, &rmt_conf, &s_led_strip));
light_driver_set_power(power);
}

View File

@@ -1,70 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Espressif Systems
* integrated circuit in a product or a software update for such product,
* must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* 4. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* light intensity level */
#define LIGHT_DEFAULT_ON 1
#define LIGHT_DEFAULT_OFF 0
/* LED strip configuration */
#define CONFIG_EXAMPLE_STRIP_LED_GPIO 8
#define CONFIG_EXAMPLE_STRIP_LED_NUMBER 1
/**
* @brief Set light power (on/off).
*
* @param power The light power to be set
*/
void light_driver_set_power(bool power);
/**
* @brief color light driver init, be invoked where you want to use color light
*
* @param power power on/off
*/
void light_driver_init(bool power);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -1,7 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 900K,
zb_storage, data, fat, 0xf1000, 16K,
zb_fct, data, fat, 0xf5000, 1K,
nvs, data, nvs, , 0x6000,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 940K,
zb_fct, data, fat, , 1K,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, otadata, data, ota, , 0x2000,
5 factory, app, factory, 0x10000, 900K, phy_init, data, phy, , 0x1000,
6 zb_storage, data, fat, 0xf1000, 16K, factory, app, factory, , 940K,
7 zb_fct, data, fat, 0xf5000, 1K, zb_fct, data, fat, , 1K,

View File

@@ -3,15 +3,10 @@
#
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_MD5=y
# end of Partition Table
#
# Zboss
# Zigbee
#
CONFIG_ZB_ENABLED=y
CONFIG_ZB_ZED=y
# end of Zboss
# end of Component config
# end of Zigbee

View File

@@ -2,6 +2,9 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
list(PREPEND SDKCONFIG_DEFAULTS
"${IDF_PATH}/examples/zigbee/zb_common_components/example_common/sdkconfig.mbedtls_minimal"
"sdkconfig.defaults")
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(light_switch)

View File

@@ -1,9 +1,9 @@
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- | -------- |
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-S31 |
| ----------------- | -------- | -------- | -------- | --------- | -------- | --------- |
# Light Switch Example
This test code shows how to configure Zigbee Coordinator and use it as an HA on/off_switch.
This example demonstrates how to configure a Home Automation On/Off switch on a Zigbee End Device.
The ESP Zigbee SDK provides more examples and tools for productization:
* [ESP Zigbee SDK Docs](https://docs.espressif.com/projects/esp-zigbee-sdk)
@@ -11,64 +11,53 @@ The ESP Zigbee SDK provides more examples and tools for productization:
## Hardware Required
* One development board with ESP32-H2 SoC acting as Zigbee Coordinator (loaded with HA_on_off_switch)
* A USB cable for power supply and programming
* Choose another ESP32-H2 as Zigbee end-device (see [HA_on_off_light](../HA_on_off_light/))
* One 802.15.4-enabled development board running this example.
* A second board running as a Zigbee Coordinator (see the [HA_on_off_light](../HA_on_off_light/) example).
* A USB cable for power supply and programming.
## Configure the project
Before project configuration and build, make sure to set the correct chip target using `idf.py --preview set-target TARGET` command.
Before project configuration and build, set the correct chip target using `idf.py set-target TARGET`.
## Erase the NVRAM
Before flash it to the board, it is recommended to erase NVRAM if user doesn't want to keep the previous examples or other projects stored info using `idf.py -p PORT erase-flash`
Before flashing the board, erase the NVRAM with `idf.py -p PORT erase-flash` if you do not want to retain
information stored by previous examples or projects.
## Build and Flash
Build the project, flash it to the board, and start the monitor tool to view the serial output by running `idf.py -p PORT flash monitor`.
Build the project, flash it to the board, and start the monitor tool to view the serial output by running
`idf.py -p PORT flash monitor`.
(To exit the serial monitor, type ``Ctrl-]``.)
## Example Output
## Application Functions
As you run the example, you will see the following log:
When the program starts, the board attempts to join an available Zigbee network:
```
I (441) main_task: Started on CPU0
I (441) main_task: Calling app_main()
I (461) phy: phy_version: 230,2, 9aae6ea, Jan 15 2024, 11:17:12
I (461) phy: libbtbb version: 944f18e, Jan 15 2024, 11:17:25
I (471) main_task: Returned from app_main()
I (601) ESP_ZB_ON_OFF_SWITCH: ZDO signal: ZDO Config Ready (0x17), status: ESP_FAIL
I (601) ESP_ZB_ON_OFF_SWITCH: Initialize Zigbee stack
I (611) gpio: GPIO[9]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2
I (611) ESP_ZB_ON_OFF_SWITCH: Deferred driver initialization successful
I (621) ESP_ZB_ON_OFF_SWITCH: Device started up in factory-reset mode
I (621) ESP_ZB_ON_OFF_SWITCH: Start network formation
W (781) ESP_ZB_ON_OFF_SWITCH: Network(0x13af) closed, devices joining not allowed.
I (781) ESP_ZB_ON_OFF_SWITCH: Formed network successfully (Extended PAN ID: 74:4d:bd:ff:fe:63:f7:30, PAN ID: 0x13af, Channel:13, Short Address: 0x0000)
I (1391) ESP_ZB_ON_OFF_SWITCH: Network(0x13af) is open for 180 seconds
I (1391) ESP_ZB_ON_OFF_SWITCH: Network steering started
I (9561) ESP_ZB_ON_OFF_SWITCH: ZDO signal: NWK Device Associated (0x12), status: ESP_OK
I (9561) ESP_ZB_ON_OFF_SWITCH: ZDO signal: ZDO Device Update (0x30), status: ESP_OK
I (9601) ESP_ZB_ON_OFF_SWITCH: New device commissioned or rejoined (short: 0x7c16)
I (9671) ESP_ZB_ON_OFF_SWITCH: Found light
I (9671) ESP_ZB_ON_OFF_SWITCH: Try to bind On/Off
I (9681) ESP_ZB_ON_OFF_SWITCH: Bound successfully!
I (9681) ESP_ZB_ON_OFF_SWITCH: The light originating from address(0x7c16) on endpoint(10)
I (9751) ESP_ZB_ON_OFF_SWITCH: ZDO signal: ZDO Device Authorized (0x2f), status: ESP_OK
I (9781) ESP_ZB_ON_OFF_SWITCH: Network(0x13af) is open for 180 seconds
I (16451) ESP_ZB_ON_OFF_SWITCH: Send 'on_off toggle' command
I (17011) ESP_ZB_ON_OFF_SWITCH: Send 'on_off toggle' command
I (17441) ESP_ZB_ON_OFF_SWITCH: Send 'on_off toggle' command
I (17831) ESP_ZB_ON_OFF_SWITCH: Send 'on_off toggle' command
I (427) ON_OFF_SWITCH: Start ESP Zigbee Stack
I (467) ON_OFF_SWITCH: Initialize Zigbee stack
I (467) ON_OFF_SWITCH: Deferred driver initialization successful
I (467) ON_OFF_SWITCH: Device started up in factory-reset mode
I (1727) ON_OFF_SWITCH: Joined network successfully: PAN ID(0x3051, EXT: 0x4831b7fffec183f0), Channel(13), Short Address(0xe5e0)
```
## Light Control Functions
After joining, the switch finds and binds a Home Automation light:
* By toggling the switch button (BOOT) on this board, the LED on the board loaded with the `HA_on_off_light` example will turn on and off.
```
I (1727) ON_OFF_SWITCH: Attempt to find HA light device
I (1827) ON_OFF_SWITCH: Attempt to bind HA light device (short address: 0x0000)
I (1847) ON_OFF_SWITCH: Bound HA light device successfully
```
Pressing the `BOOT` button sends an On/Off Toggle command to the bound device:
```
I (24447) ON_OFF_SWITCH: Sent ZCL On/Off Toggle request
I (24517) ON_OFF_SWITCH: Received ZCL Default Response with status(0x00)
```
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub.

View File

@@ -1,5 +1,3 @@
idf_component_register(
SRC_DIRS "."
INCLUDE_DIRS "."
PRIV_REQUIRES nvs_flash esp_driver_gpio esp_driver_uart ieee802154
)
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES nvs_flash esp_driver_gpio esp_driver_uart ieee802154)

View File

@@ -1,7 +1,7 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
* SPDX-License-Identifier: LicenseRef-Included
*
* Zigbee HA_on_off_switch Example
*
@@ -12,189 +12,272 @@
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include "string.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "ha/esp_zigbee_ha_standard.h"
#include "zcl_utility.h"
#include "switch_driver.h"
#include "alarm_timer.h"
#include "esp_zigbee.h"
#include "ezbee/zha.h"
#include "esp_zb_switch.h"
#if defined ZB_ED_ROLE
#error Define ZB_COORDINATOR_ROLE in idf.py menuconfig to compile light switch source code.
#endif
typedef struct light_bulb_device_params_s {
esp_zb_ieee_addr_t ieee_addr;
uint8_t endpoint;
uint16_t short_addr;
} light_bulb_device_params_t;
static const char *TAG = "ON_OFF_SWITCH";
static switch_func_pair_t button_func_pair[] = {
{GPIO_INPUT_IO_TOGGLE_SWITCH, SWITCH_ONOFF_TOGGLE_CONTROL}
};
static const char *TAG = "ESP_ZB_ON_OFF_SWITCH";
static void zb_buttons_handler(switch_func_pair_t *button_func_pair)
static void button_event_handler(switch_driver_handle_t handle)
{
if (button_func_pair->func == SWITCH_ONOFF_TOGGLE_CONTROL) {
/* implemented light switch toggle functionality */
esp_zb_zcl_on_off_cmd_t cmd_req;
cmd_req.zcl_basic_cmd.src_endpoint = HA_ONOFF_SWITCH_ENDPOINT;
cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_TOGGLE_ID;
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_on_off_cmd_req(&cmd_req);
esp_zb_lock_release();
ESP_EARLY_LOGI(TAG, "Send 'on_off toggle' command");
}
ESP_RETURN_ON_FALSE(handle != SWITCH_INV_HANDLE, , TAG, "Invalid switch handle");
ezb_zcl_on_off_cmd_t cmd_req = {
.cmd_ctrl =
{
.dst_addr.addr_mode = EZB_ADDR_MODE_NONE,
.src_ep = ESP_ZIGBEE_HA_ON_OFF_SWITCH_EP_ID,
},
};
esp_zigbee_lock_acquire(portMAX_DELAY);
ezb_zcl_on_off_toggle_cmd_req(&cmd_req);
esp_zigbee_lock_release();
ESP_EARLY_LOGI(TAG, "Sent ZCL On/Off Toggle request");
}
static esp_err_t deferred_driver_init(void)
{
ESP_RETURN_ON_FALSE(switch_driver_init(button_func_pair, PAIR_SIZE(button_func_pair), zb_buttons_handler), ESP_FAIL, TAG,
"Failed to initialize switch driver");
static bool is_inited = false;
ESP_RETURN_ON_FALSE(!is_inited, ESP_OK, TAG, "Deferred driver already initialized");
switch_driver_config_t config = {
.gpio_num = CONFIG_GPIO_BOOT_ON_DEVKIT,
.event_cb = button_event_handler,
};
switch_driver_handle_t handle = switch_driver_init(&config);
is_inited = handle == SWITCH_INV_HANDLE ? false : true;
return is_inited ? ESP_OK : ESP_FAIL;
}
static void zdo_bind_ha_light_device_result(const ezb_zdp_bind_req_result_t *result, void *user_ctx)
{
assert(result);
if (result->error == EZB_ERR_NONE) {
if (result->rsp && result->rsp->status == EZB_ZDP_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Bound HA light device successfully");
} else {
ESP_LOGE(TAG, "Failed to bind HA light device with status (0x%02x)", result->rsp->status);
}
} else {
ESP_LOGE(TAG, "Failed to bind HA light device with error (0x%04x)", result->error);
}
}
static ezb_err_t zdo_bind_ha_light_device(uint16_t dst_short_addr, uint8_t dst_ep)
{
ezb_err_t ret = EZB_ERR_FAIL;
ezb_zdo_bind_req_t bind_req = {
.dst_nwk_addr = ezb_nwk_get_short_address(),
.field =
{
.src_ep = ESP_ZIGBEE_HA_ON_OFF_SWITCH_EP_ID,
.cluster_id = EZB_ZCL_CLUSTER_ID_ON_OFF,
.dst_addr_mode = EZB_ADDR_MODE_EXT,
.dst_ep = dst_ep,
},
.cb = zdo_bind_ha_light_device_result,
.user_ctx = NULL,
};
ezb_nwk_get_extended_address(&bind_req.field.src_addr);
ESP_RETURN_ON_ERROR(ezb_address_extended_by_short(dst_short_addr, &bind_req.field.dst_addr.extended_addr), TAG,
"Failed to get extended address for destination device(0x%04hx)", dst_short_addr);
ret = ezb_zdo_bind_req(&bind_req);
if (ret == EZB_ERR_NONE) {
ESP_LOGI(TAG, "Attempt to bind HA light device (short address: 0x%04hx)", dst_short_addr);
} else {
ESP_LOGE(TAG, "Failed to bind HA light device (short address: 0x%04hx) with error(0x%04x)", dst_short_addr, ret);
}
return ret;
}
static void zdo_find_ha_light_device_result(const ezb_zdo_match_desc_req_result_t *result, void *user_ctx)
{
assert(result);
if (result->error == EZB_ERR_NONE) {
if (result->rsp && result->rsp->status == EZB_ZDP_STATUS_SUCCESS && result->rsp->match_length > 0 &&
result->rsp->match_list) {
for (size_t i = 0; i < result->rsp->match_length; i++) {
zdo_bind_ha_light_device(result->rsp->nwk_addr_of_interest, result->rsp->match_list[i]);
}
}
} else {
ESP_LOGE(TAG, "Failed to find HA light device in the network with error(0x%04x)", result->error);
}
}
static ezb_err_t zdo_find_ha_light_device(void)
{
ezb_err_t ret = EZB_ERR_FAIL;
uint16_t cluster_list[1] = {EZB_ZCL_CLUSTER_ID_ON_OFF};
ezb_zdo_match_desc_req_t req = {
.dst_nwk_addr = 0xFFFD,
.field =
{
.nwk_addr_of_interest = 0xFFFD,
.profile_id = EZB_AF_HA_PROFILE_ID,
.num_in_clusters = 1,
.num_out_clusters = 0,
.cluster_list = cluster_list,
},
.cb = zdo_find_ha_light_device_result,
.user_ctx = NULL,
};
ret = ezb_zdo_match_desc_req(&req);
if (ret == EZB_ERR_NONE) {
ESP_LOGI(TAG, "Attempt to find HA light device");
} else {
ESP_LOGE(TAG, "Failed to find HA light device with error(0x%04x)", ret);
}
return ret;
}
static void esp_zigbee_alarm_bdb_commissioning(alarm_timer_arg_t arg)
{
esp_zigbee_lock_acquire(portMAX_DELAY);
(void)ezb_bdb_start_top_level_commissioning(arg);
esp_zigbee_lock_release();
}
static bool esp_zigbee_app_signal_handler(const ezb_app_signal_t *app_signal)
{
ezb_app_signal_type_t signal_type = ezb_app_signal_get_type(app_signal);
switch (signal_type) {
case EZB_ZDO_SIGNAL_SKIP_STARTUP:
ESP_LOGI(TAG, "Initialize Zigbee stack");
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_INITIALIZATION);
break;
case EZB_BDB_SIGNAL_DEVICE_FIRST_START:
case EZB_BDB_SIGNAL_DEVICE_REBOOT: {
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *)ezb_app_signal_get_params(app_signal));
if (status == EZB_BDB_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Deferred driver initialization %s", deferred_driver_init() ? "failed" : "successful");
ESP_LOGI(TAG, "Device started up in%s factory-reset mode", ezb_bdb_is_factory_new() ? "" : " non");
if (ezb_bdb_is_factory_new()) {
ezb_bdb_start_top_level_commissioning(EZB_BDB_MODE_NETWORK_STEERING);
} else {
ESP_LOGI(TAG, "Device reboot");
}
} else {
ESP_LOGW(TAG, "The %s failed with status(0x%02x), please retry", ezb_app_signal_to_string(signal_type), status);
alarm_timer_schedule(esp_zigbee_alarm_bdb_commissioning, EZB_BDB_MODE_INITIALIZATION, 1000);
}
} break;
case EZB_BDB_SIGNAL_STEERING: {
ezb_bdb_comm_status_t status = *((ezb_bdb_comm_status_t *)ezb_app_signal_get_params(app_signal));
if (status == EZB_BDB_STATUS_SUCCESS) {
ezb_extpanid_t extended_pan_id;
ezb_nwk_get_extended_panid(&extended_pan_id);
ESP_LOGI(TAG, "Joined network successfully: PAN ID(0x%04hx, EXT: 0x%llx), Channel(%d), Short Address(0x%04hx)",
ezb_nwk_get_panid(), extended_pan_id.u64, ezb_nwk_get_current_channel(), ezb_nwk_get_short_address());
zdo_find_ha_light_device();
} else {
ESP_LOGW(TAG, "Failed to join network with status(0x%02x)", status);
alarm_timer_schedule(esp_zigbee_alarm_bdb_commissioning, EZB_BDB_MODE_NETWORK_STEERING, 1000);
}
} break;
case EZB_ZDO_SIGNAL_LEAVE: {
const ezb_zdo_signal_leave_params_t *leave_params = ezb_app_signal_get_params(app_signal);
ESP_LOGI(TAG, "Left network successfully with type(0x%02x)", leave_params->leave_type);
} break;
case EZB_NWK_SIGNAL_PERMIT_JOIN_STATUS: {
uint8_t duration = *(uint8_t *)ezb_app_signal_get_params(app_signal);
if (duration) {
ESP_LOGI(TAG, "Network(0x%04hx) is open for %d seconds", ezb_nwk_get_panid(), duration);
} else {
ESP_LOGW(TAG, "Network(0x%04hx) closed, devices joining not allowed.", ezb_nwk_get_panid());
}
} break;
case EZB_ZDO_SIGNAL_LEAVE_INDICATION: {
const ezb_zdo_signal_leave_indication_params_t *leave_ind_params = ezb_app_signal_get_params(app_signal);
ESP_LOGI(TAG, "Zigbee Node(0x%04hx) is leaving network", leave_ind_params->short_addr);
} break;
default:
ESP_LOGI(TAG, "Zigbee APP Signal: %s(type: 0x%02x)", ezb_app_signal_to_string(signal_type), signal_type);
break;
}
return true;
}
static void esp_zigbee_zcl_core_action_handler(ezb_zcl_core_action_callback_id_t callback_id, void *message)
{
switch (callback_id) {
case EZB_ZCL_CORE_DEFAULT_RSP_CB_ID: {
ezb_zcl_cmd_default_rsp_message_t *default_rsp = (ezb_zcl_cmd_default_rsp_message_t *)message;
ESP_LOGI(TAG, "Received ZCL Default Response with status(0x%02x)", default_rsp->in.status_code);
} break;
default:
ESP_LOGW(TAG, "ZCL Core Action: ID(0x%04lx)", callback_id);
break;
}
}
esp_err_t esp_zigbee_create_zha_on_off_switch_device(void)
{
ezb_af_device_desc_t dev_desc = ezb_af_create_device_desc();
ezb_zha_on_off_switch_config_t switch_cfg = EZB_ZHA_ON_OFF_SWITCH_CONFIG();
ezb_af_ep_desc_t ep_desc = ezb_zha_create_on_off_switch(ESP_ZIGBEE_HA_ON_OFF_SWITCH_EP_ID, &switch_cfg);
ezb_zcl_cluster_desc_t basic_desc = {0};
basic_desc = ezb_af_endpoint_get_cluster_desc(ep_desc, EZB_ZCL_CLUSTER_ID_BASIC, EZB_ZCL_CLUSTER_SERVER);
ezb_zcl_basic_cluster_desc_add_attr(basic_desc, EZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, (void *)ESP_MANUFACTURER_NAME);
ezb_zcl_basic_cluster_desc_add_attr(basic_desc, EZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, (void *)ESP_MODEL_IDENTIFIER);
ESP_ERROR_CHECK(ezb_af_device_add_endpoint_desc(dev_desc, ep_desc));
ESP_ERROR_CHECK(ezb_af_device_desc_register(dev_desc));
ezb_zcl_core_action_handler_register(esp_zigbee_zcl_core_action_handler);
return ESP_OK;
}
static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask)
esp_err_t esp_zigbee_setup_commissioning(void)
{
ESP_RETURN_ON_FALSE(esp_zb_bdb_start_top_level_commissioning(mode_mask) == ESP_OK, , TAG, "Failed to start Zigbee bdb commissioning");
ezb_aps_secur_enable_distributed_security(false);
ESP_ERROR_CHECK(ezb_bdb_set_primary_channel_set(ESP_ZIGBEE_PRIMARY_CHANNEL_MASK));
ESP_ERROR_CHECK(ezb_bdb_set_secondary_channel_set(ESP_ZIGBEE_SECONDARY_CHANNEL_MASK));
ESP_ERROR_CHECK(ezb_app_signal_add_handler(esp_zigbee_app_signal_handler));
return ESP_OK;
}
static void bind_cb(esp_zb_zdp_status_t zdo_status, void *user_ctx)
static void esp_zigbee_stack_main_task(void *pvParameters)
{
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Bound successfully!");
if (user_ctx) {
light_bulb_device_params_t *light = (light_bulb_device_params_t *)user_ctx;
ESP_LOGI(TAG, "The light originating from address(0x%x) on endpoint(%d)", light->short_addr, light->endpoint);
free(light);
}
}
}
esp_zigbee_config_t config = ESP_ZIGBEE_DEFAULT_CONFIG();
static void user_find_cb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx)
{
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
ESP_LOGI(TAG, "Found light");
esp_zb_zdo_bind_req_param_t bind_req;
light_bulb_device_params_t *light = (light_bulb_device_params_t *)malloc(sizeof(light_bulb_device_params_t));
light->endpoint = endpoint;
light->short_addr = addr;
esp_zb_ieee_address_by_short(light->short_addr, light->ieee_addr);
esp_zb_get_long_address(bind_req.src_address);
bind_req.src_endp = HA_ONOFF_SWITCH_ENDPOINT;
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF;
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
memcpy(bind_req.dst_address_u.addr_long, light->ieee_addr, sizeof(esp_zb_ieee_addr_t));
bind_req.dst_endp = endpoint;
bind_req.req_dst_addr = esp_zb_get_short_address();
ESP_LOGI(TAG, "Try to bind On/Off");
esp_zb_zdo_device_bind_req(&bind_req, bind_cb, (void *)light);
}
}
ESP_ERROR_CHECK(esp_zigbee_init(&config));
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, "Initialize Zigbee stack");
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, "Deferred driver initialization %s", deferred_driver_init() ? "failed" : "successful");
ESP_LOGI(TAG, "Device started up in %s factory-reset mode", esp_zb_bdb_is_factory_new() ? "" : "non");
if (esp_zb_bdb_is_factory_new()) {
ESP_LOGI(TAG, "Start network formation");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_FORMATION);
} else {
esp_zb_bdb_open_network(180);
ESP_LOGI(TAG, "Device rebooted");
}
} else {
ESP_LOGE(TAG, "Failed to initialize Zigbee stack (status: %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, "Formed network successfully (Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, Channel:%d, Short Address: 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, "Restart network formation (status: %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, "Network steering started");
}
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, "New device commissioned or rejoined (short: 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_on_off_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, "Network(0x%04hx) is open for %d seconds", esp_zb_get_pan_id(), *(uint8_t *)esp_zb_app_signal_get_params(p_sg_p));
} else {
ESP_LOGW(TAG, "Network(0x%04hx) closed, devices joining not allowed.", esp_zb_get_pan_id());
}
}
break;
default:
ESP_LOGI(TAG, "ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type,
esp_err_to_name(err_status));
break;
}
}
ESP_ERROR_CHECK(esp_zigbee_setup_commissioning());
static void esp_zb_task(void *pvParameters)
{
/* initialize Zigbee stack */
esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZC_CONFIG();
esp_zb_init(&zb_nwk_cfg);
esp_zb_on_off_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG();
esp_zb_ep_list_t *esp_zb_on_off_switch_ep = esp_zb_on_off_switch_ep_create(HA_ONOFF_SWITCH_ENDPOINT, &switch_cfg);
zcl_basic_manufacturer_info_t info = {
.manufacturer_name = ESP_MANUFACTURER_NAME,
.model_identifier = ESP_MODEL_IDENTIFIER,
};
ESP_ERROR_CHECK(esp_zigbee_create_zha_on_off_switch_device());
esp_zcl_utility_add_ep_basic_manufacturer_info(esp_zb_on_off_switch_ep, HA_ONOFF_SWITCH_ENDPOINT, &info);
esp_zb_device_register(esp_zb_on_off_switch_ep);
esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
ESP_ERROR_CHECK(esp_zb_start(false));
esp_zb_stack_main_loop();
ESP_ERROR_CHECK(esp_zigbee_start(false));
esp_zigbee_launch_mainloop();
esp_zigbee_deinit();
vTaskDelete(NULL);
}
void app_main(void)
{
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);
ESP_LOGI(TAG, "Start ESP Zigbee Stack");
xTaskCreate(esp_zigbee_stack_main_task, "Zigbee_main", 4096, NULL, 5, NULL);
}

View File

@@ -1,7 +1,7 @@
/*
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
* SPDX-License-Identifier: LicenseRef-Included
*
* Zigbee HA_on_off_switch Example
*
@@ -11,34 +11,43 @@
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include "esp_zigbee_core.h"
#include "switch_driver.h"
/* Zigbee configuration */
#define MAX_CHILDREN 10 /* the max amount of connected devices */
#define INSTALLCODE_POLICY_ENABLE false /* enable the install code policy for security */
#define HA_ONOFF_SWITCH_ENDPOINT 1 /* esp light switch device endpoint */
#define ESP_ZB_PRIMARY_CHANNEL_MASK (1l << 13) /* Zigbee primary channel mask use in the example */
#pragma once
/* Basic manufacturer information */
#define ESP_MANUFACTURER_NAME "\x09""ESPRESSIF" /* Customized manufacturer name */
#define ESP_MODEL_IDENTIFIER "\x07"CONFIG_IDF_TARGET /* Customized model identifier */
#define ESP_ZIGBEE_PRIMARY_CHANNEL_MASK (1U << CONFIG_ZB_EXAMPLE_PRIMARY_CHANNEL)
#define ESP_ZIGBEE_SECONDARY_CHANNEL_MASK CONFIG_ZB_EXAMPLE_SECONDARY_CHANNEL_MASK
#define ESP_ZB_ZC_CONFIG() \
{ \
.esp_zb_role = ESP_ZB_DEVICE_TYPE_COORDINATOR, \
.install_code_policy = INSTALLCODE_POLICY_ENABLE, \
.nwk_cfg.zczr_cfg = { \
.max_children = MAX_CHILDREN, \
}, \
#define ESP_ZIGBEE_HA_ON_OFF_SWITCH_EP_ID (1)
#define ESP_ZIGBEE_STORAGE_PARTITION_NAME "nvs"
#define ESP_MANUFACTURER_NAME "\x09""ESPRESSIF"
#define ESP_MODEL_IDENTIFIER "\x07" CONFIG_IDF_TARGET
#define ESP_ZIGBEE_ZED_CONFIG() \
{ \
.device_type = EZB_NWK_DEVICE_TYPE_END_DEVICE, \
.install_code_policy = false, \
.zed_config = { \
.ed_timeout = EZB_NWK_ED_TIMEOUT_64MIN, \
.keep_alive = 4000, \
}, \
}
#define ESP_ZB_DEFAULT_RADIO_CONFIG() \
{ \
.radio_mode = ZB_RADIO_MODE_NATIVE, \
#if CONFIG_SOC_IEEE802154_SUPPORTED
#define ESP_ZIGBEE_PLATFORM_CONFIG() \
{ \
.storage_partition_name = ESP_ZIGBEE_STORAGE_PARTITION_NAME, \
.radio_config = { \
.radio_mode = ESP_ZIGBEE_RADIO_MODE_NATIVE, \
}, \
}
#else
#warning "The example is not for IEEE 802.15.4-disabled SoC usage, please refer to esp_zigbee_gateway for RCP configuration"
#endif
#define ESP_ZB_DEFAULT_HOST_CONFIG() \
{ \
.host_connection_mode = ZB_HOST_CONNECTION_MODE_NONE, \
}
#define ESP_ZIGBEE_DEFAULT_CONFIG() \
{ \
.device_config = ESP_ZIGBEE_ZED_CONFIG(), \
.platform_config = ESP_ZIGBEE_PLATFORM_CONFIG(), \
};

View File

@@ -1,9 +1,11 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp-zboss-lib: "~1.6.0"
espressif/esp-zigbee-lib: "~1.6.0"
## Required IDF version
idf:
version: ">=5.0.0"
examples_utils:
path: ${IDF_PATH}/examples/zigbee/zb_common_components/examples_utils
version: ">=5.2"
espressif/esp-zigbee-lib:
version: ">=2.0.0"
example_common:
path: ${IDF_PATH}/examples/zigbee/zb_common_components/example_common
switch_driver:
path: ${IDF_PATH}/examples/zigbee/zb_common_components/switch_driver

View File

@@ -1,178 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Espressif Systems
* integrated circuit in a product or a software update for such product,
* must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* 4. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "switch_driver.h"
/**
* @brief:
* This example code shows how to configure light switch with attribute as well as button switch handler.
*
* @note:
Currently only support toggle switch functionality available
*
* @note:
* For other possible switch functions (on/off,level up/down,step up/down). User need to implement and create them by themselves
*/
static QueueHandle_t gpio_evt_queue = NULL;
/* button function pair, should be defined in switch example source file */
static switch_func_pair_t *switch_func_pair;
/* call back function pointer */
static esp_switch_callback_t func_ptr;
/* which button is pressed */
static uint8_t switch_num;
static const char *TAG = "ESP_ZB_SWITCH";
static void IRAM_ATTR gpio_isr_handler(void *arg)
{
xQueueSendFromISR(gpio_evt_queue, (switch_func_pair_t *)arg, NULL);
}
/**
* @brief Enable GPIO (switches refer to) isr
*
* @param enabled enable isr if true.
*/
static void switch_driver_gpios_intr_enabled(bool enabled)
{
for (int i = 0; i < switch_num; ++i) {
if (enabled) {
gpio_intr_enable((switch_func_pair + i)->pin);
} else {
gpio_intr_disable((switch_func_pair + i)->pin);
}
}
}
/**
* @brief Tasks for checking the button event and debounce the switch state
*
* @param arg Unused value.
*/
static void switch_driver_button_detected(void *arg)
{
gpio_num_t io_num = GPIO_NUM_NC;
switch_func_pair_t button_func_pair;
static switch_state_t switch_state = SWITCH_IDLE;
bool evt_flag = false;
for (;;) {
/* check if there is any queue received, if yes read out the button_func_pair */
if (xQueueReceive(gpio_evt_queue, &button_func_pair, portMAX_DELAY)) {
io_num = button_func_pair.pin;
switch_driver_gpios_intr_enabled(false);
evt_flag = true;
}
while (evt_flag) {
bool value = gpio_get_level(io_num);
switch (switch_state) {
case SWITCH_IDLE:
switch_state = (value == GPIO_INPUT_LEVEL_ON) ? SWITCH_PRESS_DETECTED : SWITCH_IDLE;
break;
case SWITCH_PRESS_DETECTED:
switch_state = (value == GPIO_INPUT_LEVEL_ON) ? SWITCH_PRESS_DETECTED : SWITCH_RELEASE_DETECTED;
break;
case SWITCH_RELEASE_DETECTED:
switch_state = SWITCH_IDLE;
/* callback to button_handler */
(*func_ptr)(&button_func_pair);
break;
default:
break;
}
if (switch_state == SWITCH_IDLE) {
switch_driver_gpios_intr_enabled(true);
evt_flag = false;
break;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
}
/**
* @brief init GPIO configuration as well as isr
*
* @param button_func_pair pointer of the button pair.
* @param button_num number of button pair.
*/
static bool switch_driver_gpio_init(switch_func_pair_t *button_func_pair, uint8_t button_num)
{
gpio_config_t io_conf = {};
switch_func_pair = button_func_pair;
switch_num = button_num;
uint64_t pin_bit_mask = 0;
/* set up button func pair pin mask */
for (int i = 0; i < button_num; ++i) {
pin_bit_mask |= (1ULL << (button_func_pair + i)->pin);
}
/* interrupt of falling edge */
io_conf.intr_type = GPIO_INTR_NEGEDGE;
io_conf.pin_bit_mask = pin_bit_mask;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
/* configure GPIO with the given settings */
gpio_config(&io_conf);
/* create a queue to handle gpio event from isr */
gpio_evt_queue = xQueueCreate(10, sizeof(switch_func_pair_t));
if ( gpio_evt_queue == 0) {
ESP_LOGE(TAG, "Queue was not created and must not be used");
return false;
}
/* start gpio task */
xTaskCreate(switch_driver_button_detected, "button_detected", 4096, NULL, 10, NULL);
/* install gpio isr service */
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
for (int i = 0; i < button_num; ++i) {
gpio_isr_handler_add((button_func_pair + i)->pin, gpio_isr_handler, (void *) (button_func_pair + i));
}
return true;
}
bool switch_driver_init(switch_func_pair_t *button_func_pair, uint8_t button_num, esp_switch_callback_t cb)
{
if (!switch_driver_gpio_init(button_func_pair, button_num)) {
return false;
}
func_ptr = cb;
return true;
}

View File

@@ -1,94 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Espressif Systems
* integrated circuit in a product or a software update for such product,
* must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* 4. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "driver/gpio.h"
#ifdef __cplusplus
extern "C" {
#endif
/* user should configure which I/O port as toggle switch input, default is GPIO9 */
#define GPIO_INPUT_IO_TOGGLE_SWITCH GPIO_NUM_9
/* config button level depends on the pull up/down setting
push button level is on level = 1 when pull-down enable
push button level is on level = 0 when pull-up enable
*/
#define GPIO_INPUT_LEVEL_ON 0
#define ESP_INTR_FLAG_DEFAULT 0
#define PAIR_SIZE(TYPE_STR_PAIR) (sizeof(TYPE_STR_PAIR) / sizeof(TYPE_STR_PAIR[0]))
typedef enum {
SWITCH_IDLE,
SWITCH_PRESS_ARMED,
SWITCH_PRESS_DETECTED,
SWITCH_PRESSED,
SWITCH_RELEASE_DETECTED,
} switch_state_t;
typedef enum {
SWITCH_ON_CONTROL,
SWITCH_OFF_CONTROL,
SWITCH_ONOFF_TOGGLE_CONTROL,
SWITCH_LEVEL_UP_CONTROL,
SWITCH_LEVEL_DOWN_CONTROL,
SWITCH_LEVEL_CYCLE_CONTROL,
SWITCH_COLOR_CONTROL,
} switch_func_t;
typedef struct {
uint32_t pin;
switch_func_t func;
} switch_func_pair_t;
typedef void (*esp_switch_callback_t)(switch_func_pair_t *param);
/**
* @brief init function for switch and callback setup
*
* @param button_func_pair pointer of the button pair.
* @param button_num number of button pair.
* @param cb callback pointer.
*/
bool switch_driver_init(switch_func_pair_t *button_func_pair, uint8_t button_num, esp_switch_callback_t cb);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -1,7 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 900K,
zb_storage, data, fat, 0xf1000, 16K,
zb_fct, data, fat, 0xf5000, 1K,
nvs, data, nvs, , 0x6000,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 940K,
zb_fct, data, fat, , 1K,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000, nvs, data, nvs, , 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000, otadata, data, ota, , 0x2000,
5 factory, app, factory, 0x10000, 900K, phy_init, data, phy, , 0x1000,
6 zb_storage, data, fat, 0xf1000, 16K, factory, app, factory, , 940K,
7 zb_fct, data, fat, 0xf5000, 1K, zb_fct, data, fat, , 1K,

View File

@@ -3,15 +3,11 @@
#
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_MD5=y
# end of Partition Table
#
# Zboss
# Zigbee
#
CONFIG_ZB_ENABLED=y
CONFIG_ZB_ZCZR=y
# end of Zboss
# end of Component config
CONFIG_ZB_ZED=y
# end of Zigbee

View File

@@ -1,12 +1,11 @@
# Light Switch and Bulb Control Example
(See the `README.md` file in the upper level 'examples' directory for more information about examples.)
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.
## Overview
The folder contains examples demonstrating Zigbee Coordinator and End-Device roles
The folder contains two Zigbee examples demonstrating Coordinator and End Device roles.
* [HA_on_off_light](HA_on_off_light) is a standard HA on-off light bulb example demonstrating Zigbee End-device. It provides a simple on/off condition for a Zigbee light. It runs on an 802.15.4 SoC like ESP32-H2. For more details see the example readme file.
* [HA_on_off_switch](HA_on_off_switch) is a standard HA on-off switch example demonstrating Zigbee Coordinator role. It provides an on/off toggle to control a Zigbee HA on off light. It runs on an 802.15.4 SoC like ESP32-H2. For more details to see the example readme file.
* [HA_on_off_light](HA_on_off_light) is a standard Zigbee coordinator acting as Home Automation On/Off light bulb. It runs on an 802.15.4 SoC like ESP32-H2. For more details see [README](HA_on_off_light/README.md).
* [HA_on_off_switch](HA_on_off_switch) is a standard Zigbee End-device acting as Home Automation On/Off switch. It runs on an 802.15.4 SoC like ESP32-H2. For more details to see [README](HA_on_off_switch/README.md).

View File

@@ -1,9 +1,8 @@
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
# !/usr/bin/env python3
import pathlib
import time
from typing import Tuple
import pytest
from pytest_embedded import Dut
@@ -24,24 +23,25 @@ pytest_build_dir = CURRENT_DIR_LIGHT + '|' + CURRENT_DIR_SWITCH
)
# config Zigbee network
@idf_parametrize('target', ['esp32h2'], indirect=['target'])
def test_config_zigbee_network(dut: Tuple[Dut, Dut]) -> None:
def test_config_zigbee_network(dut: tuple[Dut, Dut]) -> None:
light = dut[0]
switch = dut[1]
time.sleep(3)
switch.expect('ESP_ZB_ON_OFF_SWITCH: Formed network successfully', timeout=30)
# get the switch extpanid
switch_node_expanid = switch.expect(r'Extended PAN ID: (([a-z0-9]{2}:?){8})', timeout=3)[1].decode()
switch_node_expanid = switch_node_expanid.replace(':', '')
# get the switch panid
switch_node_panid = switch.expect(r'PAN ID: 0x([a-z0-9]+:?)', timeout=2)[1].decode()
# new device commissioned successfully
switch.expect(r'New device commissioned or rejoined \(short: 0x([a-z0-9]+)[^a-z0-9]', timeout=30)[1].decode()
# get the light node extpanid
light.expect('ESP_ZB_ON_OFF_LIGHT: Joined network successfully', timeout=20)
light_node_expanid = light.expect(r'Extended PAN ID: (([a-z0-9]{2}:?){8})', timeout=3)[1].decode()
light_node_expanid = light_node_expanid.replace(':', '')
# get the light panid
light_node_panid = light.expect(r'PAN ID: 0x([a-z0-9]+:?)', timeout=2)[1].decode()
# make sure the light node join the network that switch node formed (same expanid)
if (light_node_expanid != switch_node_expanid) or (light_node_panid != switch_node_panid):
assert False
light_network = light.expect(
r'ON_OFF_LIGHT: Formed network successfully: PAN ID\(0x([a-fA-F0-9]+), EXT: 0x([a-fA-F0-9]+)\)',
timeout=30,
)
light_panid = light_network[1].decode()
light_extpanid = light_network[2].decode()
switch_network = switch.expect(
r'ON_OFF_SWITCH: Joined network successfully: PAN ID\(0x([a-fA-F0-9]+), EXT: 0x([a-fA-F0-9]+)\)',
timeout=30,
)
switch_panid = switch_network[1].decode()
switch_extpanid = switch_network[2].decode()
light.expect(r'New device commissioned or rejoined ?\(short: 0x([a-fA-F0-9]+)\)', timeout=30)
assert switch_panid == light_panid
assert switch_extpanid == light_extpanid

View File

@@ -0,0 +1,4 @@
idf_component_register(SRC_DIRS "src"
INCLUDE_DIRS "include"
REQUIRES esp_timer
)

View File

@@ -0,0 +1,12 @@
menu "Zigbee Example Configuration"
config ZB_EXAMPLE_PRIMARY_CHANNEL
int "Zigbee primary channel"
range 11 26
default 13
config ZB_EXAMPLE_SECONDARY_CHANNEL_MASK
hex "Zigbee secondary channel mask"
default 0x07FFF800
endmenu

View File

@@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* Alarm driver
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief The argument type for the alarm callback.
*
*/
typedef uintptr_t alarm_timer_arg_t;
/**
* @brief The callback for the timer.
*
*/
typedef void (*alarm_timer_callback_t)(alarm_timer_arg_t arg);
/**
* @brief Schedule an alarm to fire at a given time.
*
* @param cb The callback to execute when the alarm fires.
* @param arg The argument to pass to the callback.
* @param time_ms The time to fire the timer in milliseconds.
*
* @return ESP_OK on success, error code otherwise.
*/
esp_err_t alarm_timer_schedule(alarm_timer_callback_t cb, alarm_timer_arg_t arg, uint32_t time_ms);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,51 @@
# This config file optimizes the mbedTLS code size by disabling
# unused features by ESP-Zigbee-SDK.
#
# mbedTLS
#
# Enable required
CONFIG_MBEDTLS_AES_C=y
CONFIG_MBEDTLS_CCM_C=y
# Disable unused
CONFIG_MBEDTLS_GCM_C=n
CONFIG_MBEDTLS_CMAC_C=n
CONFIG_MBEDTLS_SHA1_C=n
CONFIG_MBEDTLS_SHA3_C=n
CONFIG_MBEDTLS_SHA512_C=n
CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=n
CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=n
CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=n
CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=n
CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=n
CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=n
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=n
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_PSK=n
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=n
## v5.0
CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=n
CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=n
CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=n
CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=n
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=n
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=n
## v6.0
CONFIG_MBEDTLS_AES_ONLY_128_BIT_KEY_LENGTH=y
CONFIG_MBEDTLS_CTR_DRBG_C=y
CONFIG_MBEDTLS_HMAC_DRBG_C=n
CONFIG_MBEDTLS_X509_RSASSA_PSS_SUPPORT=n
CONFIG_MBEDTLS_CIPHER_MODE_CBC=n
CONFIG_MBEDTLS_CIPHER_MODE_CFB=n
CONFIG_MBEDTLS_CIPHER_MODE_CTR=n
CONFIG_MBEDTLS_CIPHER_MODE_OFB=n
CONFIG_MBEDTLS_CIPHER_MODE_XTS=n
# PSA_WANT_ALG_RSA_PSS needs CONFIG_MBEDTLS_RSA_C=y on ESP-IDF master
CONFIG_MBEDTLS_RSA_C=y
CONFIG_MBEDTLS_MD5_C=n
CONFIG_MBEDTLS_SHA384_C=n
# end of mbedTLS

View File

@@ -0,0 +1,69 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* Alarm Timer
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdlib.h>
#include "esp_timer.h"
#include "alarm_timer.h"
typedef struct alarm_timer_s {
esp_timer_handle_t timer;
alarm_timer_callback_t cb;
alarm_timer_arg_t arg;
} alarm_timer_t;
static void alarm_timer_cb(void *arg)
{
alarm_timer_t *alarm = (alarm_timer_t *)arg;
esp_timer_delete(alarm->timer);
alarm->cb(alarm->arg);
free(alarm);
}
esp_err_t alarm_timer_schedule(alarm_timer_callback_t cb, alarm_timer_arg_t arg, uint32_t time_ms)
{
if (cb == NULL) {
return ESP_ERR_INVALID_ARG;
}
alarm_timer_t *alarm = malloc(sizeof(alarm_timer_t));
if (alarm == NULL) {
return ESP_ERR_NO_MEM;
}
alarm->cb = cb;
alarm->arg = arg;
alarm->timer = NULL;
esp_timer_create_args_t timer_args = {
.callback = alarm_timer_cb,
.arg = alarm,
};
esp_err_t err = esp_timer_create(&timer_args, &alarm->timer);
if (err != ESP_OK) {
free(alarm);
return err;
}
uint64_t time_us = (uint64_t)time_ms * 1000;
err = esp_timer_start_once(alarm->timer, time_us);
if (err != ESP_OK) {
esp_timer_delete(alarm->timer);
free(alarm);
return err;
}
return ESP_OK;
}

View File

@@ -1,3 +0,0 @@
idf_component_register(SRCS "zcl_utility.c"
INCLUDE_DIRS "include"
PRIV_REQUIRES espressif__esp-zigbee-lib)

View File

@@ -1,50 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* ZCL utility functions for Zigbee examples
*
* This example code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include "esp_err.h"
#include "esp_zigbee_core.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! Maximum length of ManufacturerName string field */
#define ESP_ZB_ZCL_CLUSTER_ID_BASIC_MANUFACTURER_NAME_MAX_LEN 32
/*! Maximum length of ModelIdentifier string field */
#define ESP_ZB_ZCL_CLUSTER_ID_BASIC_MODEL_IDENTIFIER_MAX_LEN 32
/** optional basic manufacturer information */
typedef struct zcl_basic_manufacturer_info_s {
char *manufacturer_name;
char *model_identifier;
} zcl_basic_manufacturer_info_t;
/**
* @brief Adds manufacturer information to the ZCL basic cluster of endpoint
*
* @param ep_list The pointer to the endpoint list with @p endpoint_id
* @param endpoint_id The endpoint identifier indicating where the ZCL basic cluster resides
* @param info The pointer to the basic manufacturer information, @see zcl_basic_manufacturer_info_t
* @return
* - ESP_OK: On success
* - ESP_ERR_INVALID_ARG: Invalid argument
*/
esp_err_t esp_zcl_utility_add_ep_basic_manufacturer_info(esp_zb_ep_list_t *ep_list, uint8_t endpoint_id, zcl_basic_manufacturer_info_t *info);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -1,40 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* ZCL utility functions for Zigbee examples
*
* This example code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdint.h>
#include <string.h>
#include "esp_check.h"
#include "esp_zigbee_core.h"
#include "zcl_utility.h"
static const char *TAG = "ZCL_UTILITY";
esp_err_t esp_zcl_utility_add_ep_basic_manufacturer_info(esp_zb_ep_list_t *ep_list, uint8_t endpoint_id, zcl_basic_manufacturer_info_t *info)
{
esp_err_t ret = ESP_OK;
esp_zb_cluster_list_t *cluster_list = NULL;
esp_zb_attribute_list_t *basic_cluster = NULL;
cluster_list = esp_zb_ep_list_get_ep(ep_list, endpoint_id);
ESP_RETURN_ON_FALSE(cluster_list, ESP_ERR_INVALID_ARG, TAG, "Failed to find endpoint id: %d in list: %p", endpoint_id, ep_list);
basic_cluster = esp_zb_cluster_list_get_cluster(cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BASIC, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
ESP_RETURN_ON_FALSE(basic_cluster, ESP_ERR_INVALID_ARG, TAG, "Failed to find basic cluster in endpoint: %d", endpoint_id);
ESP_RETURN_ON_FALSE((info && info->manufacturer_name), ESP_ERR_INVALID_ARG, TAG, "Invalid manufacturer name");
ESP_ERROR_CHECK(esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, info->manufacturer_name));
ESP_RETURN_ON_FALSE((info && info->model_identifier), ESP_ERR_INVALID_ARG, TAG, "Invalid model identifier");
ESP_ERROR_CHECK(esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, info->model_identifier));
return ret;
}

View File

@@ -0,0 +1,3 @@
idf_component_register(SRC_DIRS "src"
INCLUDE_DIRS "include"
REQUIRES led_strip)

View File

@@ -0,0 +1,13 @@
menu "ESP Zigbee DevKit LED Configuration"
config GPIO_LED_ON_DEVKIT
int "LED GPIO on devkit"
default 8 if IDF_TARGET_ESP32H2
default 8 if IDF_TARGET_ESP32C6
default 27 if IDF_TARGET_ESP32C5
default 8
help
The GPIO connected to the LED on the devkits
endmenu

View File

@@ -0,0 +1,3 @@
## IDF Component Manager Manifest File
dependencies:
espressif/led_strip: "~3.0.0"

View File

@@ -0,0 +1,151 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* Zigbee light driver example
*
* This example code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include <stdbool.h>
#include <math.h>
#ifdef __cplusplus
extern "C" {
#endif
/* light intensity level */
#define LIGHT_DEFAULT_ON 1
#define LIGHT_DEFAULT_OFF 0
/* LED strip configuration */
#define CONFIG_EXAMPLE_STRIP_LED_GPIO CONFIG_GPIO_LED_ON_DEVKIT
#define CONFIG_EXAMPLE_STRIP_LED_NUMBER 1
/** Convert Hue,Saturation,V to RGB
* RGB - [0..0xffff]
* hue - [0..0xff]
* Sat - [0..0xff]
* V always = (ZB_UINT16_MAX-1)
*/
#define HSV_to_RGB(h, s, v, r, g, b) \
{ \
uint8_t i; \
uint8_t sector = UINT8_MAX / 6; \
float f, p, q, t; \
if (s == 0) { /* achromatic (grey)*/ \
r = g = b = (v); \
} else { \
i = h / sector; /* sector 0 to 5 */ \
f = h % sector; /* factorial part of h*/ \
p = (float)(v * (1.0 - (float)s / UINT8_MAX)); \
q = (float)(v * (1.0 - (float)s / UINT8_MAX * f / (float)sector)); \
t = (float)(v * (1.0 - (float)s / UINT8_MAX * (1 - f / (float)sector))); \
switch (i) { \
case 0: \
r = (v); \
g = t; \
b = p; \
break; \
case 1: \
r = q; \
g = (v); \
b = p; \
break; \
case 2: \
r = p; \
g = (v); \
b = t; \
break; \
case 3: \
r = p; \
g = q; \
b = (v); \
break; \
case 4: \
r = t; \
g = p; \
b = (v); \
break; \
case 5: \
default: \
r = (v); \
g = p; \
b = q; \
break; \
} \
} \
}
#define XYZ_to_RGB(X, Y, Z, r, g, b) \
{ \
r = (float)(3.240479 * (X)-1.537150 * (Y)-0.498535 * (Z)); \
g = (float)(-0.969256 * (X) + 1.875992 * (Y) + 0.041556 * (Z)); \
b = (float)(0.055648 * (X)-0.204043 * (Y) + 1.057311 * (Z)); \
if (r > 1) { \
r = 1; \
} \
if (g > 1) { \
g = 1; \
} \
if (b > 1) { \
b = 1; \
} \
}
/**
* @brief Set light power (on/off).
*
* @param power The light power value to be set
*/
void light_driver_set_power(bool power);
/**
* @brief color light driver init, be invoked where you want to use color light
*
* @param power power on/off
*/
void light_driver_init(bool power);
/**
* @brief Set light level
*
* @param level The light level value to be set
*/
void light_driver_set_level(uint8_t level);
/**
* @brief Set light color from RGB
*
* @param red The red color value to be set
* @param green The green color value to be set
* @param blue The blue color value to be set
*/
void light_driver_set_color_RGB(uint8_t red, uint8_t green, uint8_t blue);
/**
* @brief Set light color from color xy
*
* @param color_current_x The color x value to be set
* @param color_current_y The color y value to be set
*/
void light_driver_set_color_xy(uint16_t color_current_x, uint16_t color_current_y);
/**
* @brief Set light color from hue saturation
*
* @param hue The hue value to be set
* @param sat The saturation value to be set
*/
void light_driver_set_color_hue_sat(uint8_t hue, uint8_t sat);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* Zigbee light driver example
*
* This example code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include "led_strip.h"
#include "light_driver.h"
static led_strip_handle_t s_led_strip;
static uint8_t s_red = 255, s_green = 255, s_blue = 255, s_level = 255;
void light_driver_set_color_xy(uint16_t color_current_x, uint16_t color_current_y)
{
float red_f = 0, green_f = 0, blue_f = 0, color_x, color_y;
color_x = (float)color_current_x / 65535;
color_y = (float)color_current_y / 65535;
/* assume color_Y is full light level value 1 (0-1.0) */
float color_X = color_x / color_y;
float color_Z = (1 - color_x - color_y) / color_y;
/* change from xy to linear RGB NOT sRGB */
XYZ_to_RGB(color_X, 1, color_Z, red_f, green_f, blue_f);
float ratio = (float)s_level / 255;
s_red = (uint8_t)(red_f * (float)255);
s_green = (uint8_t)(green_f * (float)255);
s_blue = (uint8_t)(blue_f * (float)255);
ESP_ERROR_CHECK(led_strip_set_pixel(s_led_strip, 0, s_red * ratio, s_green * ratio, s_blue * ratio));
ESP_ERROR_CHECK(led_strip_refresh(s_led_strip));
}
void light_driver_set_color_hue_sat(uint8_t hue, uint8_t sat)
{
float red_f, green_f, blue_f;
HSV_to_RGB(hue, sat, UINT8_MAX, red_f, green_f, blue_f);
float ratio = (float)s_level / 255;
s_red = (uint8_t)red_f;
s_green = (uint8_t)green_f;
s_blue = (uint8_t)blue_f;
ESP_ERROR_CHECK(led_strip_set_pixel(s_led_strip, 0, s_red * ratio, s_green * ratio, s_blue * ratio));
ESP_ERROR_CHECK(led_strip_refresh(s_led_strip));
}
void light_driver_set_color_RGB(uint8_t red, uint8_t green, uint8_t blue)
{
float ratio = (float)s_level / 255;
s_red = red;
s_green = green;
s_blue = blue;
ESP_ERROR_CHECK(led_strip_set_pixel(s_led_strip, 0, red * ratio, green * ratio, blue * ratio));
ESP_ERROR_CHECK(led_strip_refresh(s_led_strip));
}
void light_driver_set_power(bool power)
{
ESP_ERROR_CHECK(led_strip_set_pixel(s_led_strip, 0, s_red * power, s_green * power, s_blue * power));
ESP_ERROR_CHECK(led_strip_refresh(s_led_strip));
}
void light_driver_set_level(uint8_t level)
{
s_level = level;
float ratio = (float)s_level / 255;
ESP_ERROR_CHECK(led_strip_set_pixel(s_led_strip, 0, s_red * ratio, s_green * ratio, s_blue * ratio));
ESP_ERROR_CHECK(led_strip_refresh(s_led_strip));
}
void light_driver_init(bool power)
{
led_strip_config_t led_strip_conf = {
.max_leds = CONFIG_EXAMPLE_STRIP_LED_NUMBER,
.strip_gpio_num = CONFIG_EXAMPLE_STRIP_LED_GPIO,
};
led_strip_rmt_config_t rmt_conf = {
.resolution_hz = 10 * 1000 * 1000, // 10MHz
};
ESP_ERROR_CHECK(led_strip_new_rmt_device(&led_strip_conf, &rmt_conf, &s_led_strip));
light_driver_set_power(power);
}

View File

@@ -0,0 +1,13 @@
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
set(public_requires "freertos")
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
list(APPEND public_requires "esp_driver_gpio")
else()
list(APPEND public_requires "driver")
endif()
idf_component_register(SRC_DIRS "src"
INCLUDE_DIRS "include"
REQUIRES ${public_requires})

View File

@@ -0,0 +1,27 @@
menu "ESP Zigbee DevKit GPIO Configuration"
config GPIO_BOOT_ON_DEVKIT
int "BOOT GPIO on devkit"
default 9 if IDF_TARGET_ESP32H2
default 9 if IDF_TARGET_ESP32C6
default 28 if IDF_TARGET_ESP32C5
default 9
help
The GPIO connected to the BOOT button on the devkits
config GPIO_EXT1_WAKEUP_SOURCE
int "External wakeup GPIO"
default 9 if IDF_TARGET_ESP32H2
default 7 if IDF_TARGET_ESP32C6
default 7 if IDF_TARGET_ESP32S31
default 6 if IDF_TARGET_ESP32C5
range 7 14 if IDF_TARGET_ESP32H2
range 0 7 if IDF_TARGET_ESP32C6
range 0 7 if IDF_TARGET_ESP32S31
range 0 6 if IDF_TARGET_ESP32C5
default 9
help
The GPIO can be used as external wake up source for sleep device
endmenu

View File

@@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* Zigbee switch driver
*
* This driver reads a physical GPIO switch/button (e.g. BOOT button on devkit),
* uses GPIO interrupt and a queue to report press/release events to the application
* via a callback. Typical use: Zigbee HA On/Off Switch sending commands on button press.
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include <stdbool.h>
#include "driver/gpio.h"
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Opaque handle for the switch driver instance.
*/
typedef void* switch_driver_handle_t;
/**
* @brief Invalid handle value.
*/
#define SWITCH_INV_HANDLE NULL
/**
* @brief Callback invoked from GPIO ISR (low-level) when the switch level changes.
*
* Runs in interrupt context: keep it minimal (e.g. set a flag or post to a queue).
* Do not block or call blocking / non-ISR-safe APIs.
*
* @param user_ctx User context passed at init
*/
typedef void (*switch_event_cb_t)(switch_driver_handle_t handle);
/**
* @brief Switch driver configuration.
*/
typedef struct {
gpio_num_t gpio_num; /*!< GPIO number used as switch input (e.g. BOOT button) */
switch_event_cb_t event_cb; /*!< Callback on level change (invoked from ISR); may be NULL if only polling */
} switch_driver_config_t;
/**
* @brief Initialize the switch driver.
*
* Configures the GPIO in switch_driver_init() as input with pull-up enabled,
* installs the GPIO ISR, and hooks the callback. The callback is triggered in low-level
* (GPIO ISR) context on level change.
*
* @param config Driver configuration (gpio_num required; event_cb optional)
* @return Handle
*/
switch_driver_handle_t switch_driver_init(const switch_driver_config_t *config);
/**
* @brief Deinitialize the switch driver.
*
* Removes ISR handler and resets GPIO.
*
* @param handle Handle from switch_driver_init; may be NULL
*/
void switch_driver_deinit(switch_driver_handle_t handle);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,116 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: LicenseRef-Included
*
* Zigbee switch driver
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdlib.h>
#include <string.h>
#include "switch_driver.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "esp_attr.h"
#include "esp_log.h"
static QueueHandle_t gpio_event_queue = NULL;
static void switch_driver_gpio_intr_enabled(gpio_num_t gpio_num, bool enabled)
{
if (enabled) {
gpio_intr_enable(gpio_num);
} else {
gpio_intr_disable(gpio_num);
}
}
static void IRAM_ATTR gpio_isr_handler(void *arg)
{
switch_driver_config_t *config = (switch_driver_config_t *)arg;
switch_driver_gpio_intr_enabled(config->gpio_num, false);
xQueueSendFromISR(gpio_event_queue, &config, NULL);
}
static void switch_driver_button_detected(void *arg)
{
(void)arg;
switch_driver_config_t *config = NULL;
while (1) {
if (xQueueReceive(gpio_event_queue, &config, portMAX_DELAY) == pdTRUE) {
if (config) {
if (config->event_cb) {
config->event_cb(config);
}
}
}
vTaskDelay(pdMS_TO_TICKS(200));
switch_driver_gpio_intr_enabled(config->gpio_num, true);
}
}
switch_driver_handle_t switch_driver_init(const switch_driver_config_t *config)
{
if (config == NULL || config->gpio_num < 0) {
return NULL;
}
switch_driver_config_t *handle = (switch_driver_config_t *)malloc(sizeof(switch_driver_config_t));
if (handle == NULL) {
return NULL;
}
memcpy(handle, (void *)config, sizeof(switch_driver_config_t));
/* Configure GPIO in init: input, pull-up, and negative-edge interrupt */
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << config->gpio_num),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_LOW_LEVEL,
};
esp_err_t ret = gpio_config(&io_conf);
if (ret != ESP_OK) {
free(handle);
return NULL;
}
gpio_event_queue = xQueueCreate(10, sizeof(switch_driver_config_t *));
if (gpio_event_queue == 0) {
gpio_reset_pin(config->gpio_num);
free(handle);
return NULL;
}
xTaskCreate(switch_driver_button_detected, "button_detected", 4096, NULL, 10, NULL);
ret = gpio_install_isr_service(0);
if (ret != ESP_OK) {
gpio_reset_pin(config->gpio_num);
free(handle);
return NULL;
}
ret = gpio_isr_handler_add(config->gpio_num, gpio_isr_handler, (void *)handle);
if (ret != ESP_OK) {
gpio_reset_pin(config->gpio_num);
free(handle);
return NULL;
}
return handle;
}
void switch_driver_deinit(switch_driver_handle_t handle)
{
if (handle == NULL) {
return;
}
switch_driver_config_t *config = (switch_driver_config_t *)handle;
gpio_isr_handler_remove(config->gpio_num);
gpio_reset_pin(config->gpio_num);
free(config);
}