This commit is contained in:
2026-05-22 21:52:50 +03:00
commit be7c60e4dd
1854 changed files with 583428 additions and 0 deletions
@@ -0,0 +1,72 @@
# Arduino-ESP32 Zigbee Analog Input Output Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) analog input/output device.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Analog Sensor Functions
* After this board first starts up, it would be configured locally to report an analog input on change or every 30 seconds.
* By clicking the button (BOOT) on this board, this board will immediately send a report of the current measured value to the network.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the ADC GPIO by changing the `analogPin` variable. By default, it's the pin `A0`.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,163 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee analog input / output device.
*
* The example demonstrates how to use Zigbee library to create a end device analog device.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
* Modified by Pat Clay
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator/router device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee analog device configuration */
#define ANALOG_DEVICE_ENDPOINT_NUMBER 1
uint8_t analogPin = A0;
uint8_t button = BOOT_PIN;
ZigbeeAnalog zbAnalogDevice = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
ZigbeeAnalog zbAnalogTemp = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 1);
ZigbeeAnalog zbAnalogFan = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 2);
ZigbeeAnalog zbAnalogPercent = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 3);
void onAnalogOutputChange(float analog_output) {
Serial.printf("Received analog output change: %.1f\r\n", analog_output);
}
void setup() {
Serial.begin(115200);
Serial.println("Starting...");
// Init button switch
pinMode(button, INPUT_PULLUP);
// Set analog resolution to 10 bits
analogReadResolution(10);
// Optional: set Zigbee device name and model
zbAnalogDevice.setManufacturerAndModel("Espressif", "ZigbeeAnalogDevice");
// Set up analog input
zbAnalogDevice.addAnalogInput();
zbAnalogDevice.setAnalogInputApplication(ESP_ZB_ZCL_AI_POWER_IN_WATTS_CONSUMPTION);
zbAnalogDevice.setAnalogInputDescription("Power Consumption (Watts)");
zbAnalogDevice.setAnalogInputResolution(0.01);
// Set up analog output
zbAnalogDevice.addAnalogOutput();
zbAnalogDevice.setAnalogOutputApplication(ESP_ZB_ZCL_AI_RPM_OTHER);
zbAnalogDevice.setAnalogOutputDescription("Fan Speed (RPM)");
zbAnalogDevice.setAnalogOutputResolution(1);
// Set the min and max values for the analog output which is used by HA to limit the range of the analog output
zbAnalogDevice.setAnalogOutputMinMax(-10000, 10000); //-10000 to 10000 RPM
// If analog output cluster is added, set callback function for analog output change
zbAnalogDevice.onAnalogOutputChange(onAnalogOutputChange);
// Set up analog input
zbAnalogTemp.addAnalogInput();
zbAnalogTemp.setAnalogInputApplication(ESP_ZB_ZCL_AI_TEMPERATURE_OTHER);
zbAnalogTemp.setAnalogInputDescription("Temperature");
zbAnalogTemp.setAnalogInputResolution(0.1);
// Set up analog input
zbAnalogFan.addAnalogInput();
zbAnalogFan.setAnalogInputApplication(ESP_ZB_ZCL_AI_RPM_OTHER);
zbAnalogFan.setAnalogInputDescription("RPM");
zbAnalogFan.setAnalogInputResolution(1);
// Set up analog input
zbAnalogPercent.addAnalogInput();
zbAnalogPercent.setAnalogInputApplication(ESP_ZB_ZCL_AI_PERCENTAGE_OTHER);
zbAnalogPercent.setAnalogInputDescription("Percentage");
zbAnalogPercent.setAnalogInputResolution(0.01);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbAnalogDevice);
Zigbee.addEndpoint(&zbAnalogTemp);
Zigbee.addEndpoint(&zbAnalogFan);
Zigbee.addEndpoint(&zbAnalogPercent);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in Router Device mode
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println("Connected");
// Optional: Add reporting for analog input
zbAnalogDevice.setAnalogInputReporting(0, 30, 10); // report every 30 seconds if value changes by 10
}
void loop() {
static uint32_t timeCounter = 0;
// Read ADC value and update the analog value every 2s
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
float analog = (float)analogRead(analogPin);
Serial.printf("Updating analog input to %.1f\r\n", analog);
zbAnalogDevice.setAnalogInput(analog);
zbAnalogTemp.setAnalogInput(analog / 100);
zbAnalogFan.setAnalogInput(analog);
zbAnalogPercent.setAnalogInput(analog / 10);
// Analog input supports reporting
zbAnalogDevice.reportAnalogInput();
zbAnalogTemp.reportAnalogInput();
zbAnalogFan.reportAnalogInput();
zbAnalogPercent.reportAnalogInput();
}
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// For demonstration purposes, increment the analog output value by 100
zbAnalogDevice.setAnalogOutput(zbAnalogDevice.getAnalogOutput() + 100);
zbAnalogDevice.reportAnalogOutput();
}
delay(100);
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,80 @@
# Arduino-ESP32 Zigbee Binary Input Output Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) binary input/output device with multiple applications: HVAC fan status/control, security zone armed status, and HVAC humidifier control.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Binary Input/Output Functions
* The example implements three binary devices:
- **Binary Fan Device (Endpoint 1)**:
- Binary Input: HVAC Fan Status - Reports the current state of a fan
- Binary Output: HVAC Fan - Controls the fan switch with callback function
- **Binary Zone Device (Endpoint 2)**:
- Binary Input: Security Zone Armed - Reports the armed state of a security zone
- **Binary Humidifier Device (Endpoint 3)**:
- Binary Output: HVAC Humidifier - Controls the humidifier switch with callback function
* By clicking the button (BOOT) on this board, it will toggle all binary inputs/outputs and immediately send a report of their states to the network.
* Holding the button for more than 3 seconds will trigger a factory reset of the Zigbee device.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
The example uses the following default pins:
* Button: `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2)
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,144 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee binary input/output device.
*
* The example demonstrates how to use Zigbee library to create an end device binary sensor/switch device.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee binary sensor device configuration */
#define BINARY_DEVICE_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
ZigbeeBinary zbBinaryFan = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER);
ZigbeeBinary zbBinaryZone = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER + 1);
ZigbeeBinary zbBinaryHumidifier = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER + 2);
bool zoneStatus = false;
void fanSwitch(bool state) {
Serial.println("Fan switch changed to: " + String(state));
// Switch Fan status input signaling the fan status has changed
zbBinaryFan.setBinaryInput(state);
zbBinaryFan.reportBinaryInput();
}
void humidifierSwitch(bool state) {
Serial.println("Humidifier switch changed to: " + String(state));
}
void setup() {
Serial.begin(115200);
Serial.println("Starting...");
// Init button switch
pinMode(button, INPUT_PULLUP);
// Set analog resolution to 10 bits
analogReadResolution(10);
// Optional: set Zigbee device name and model
zbBinaryFan.setManufacturerAndModel("Espressif", "ZigbeeBinarySensor");
// Set up binary fan status input + switch output (HVAC)
zbBinaryFan.addBinaryInput();
zbBinaryFan.setBinaryInputApplication(BINARY_INPUT_APPLICATION_TYPE_HVAC_FAN_STATUS);
zbBinaryFan.setBinaryInputDescription("Fan Status");
zbBinaryFan.addBinaryOutput();
zbBinaryFan.setBinaryOutputApplication(BINARY_OUTPUT_APPLICATION_TYPE_HVAC_FAN);
zbBinaryFan.setBinaryOutputDescription("Fan Switch");
zbBinaryFan.onBinaryOutputChange(fanSwitch);
// Set up binary zone armed input (Security)
zbBinaryZone.addBinaryInput();
zbBinaryZone.setBinaryInputApplication(BINARY_INPUT_APPLICATION_TYPE_SECURITY_ZONE_ARMED);
zbBinaryZone.setBinaryInputDescription("Zone Armed");
// Set up binary humidifier output (HVAC)
zbBinaryHumidifier.addBinaryOutput();
zbBinaryHumidifier.setBinaryOutputApplication(BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HUMIDIFIER);
zbBinaryHumidifier.setBinaryOutputDescription("Humidifier Switch");
zbBinaryHumidifier.onBinaryOutputChange(humidifierSwitch);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbBinaryFan);
Zigbee.addEndpoint(&zbBinaryZone);
Zigbee.addEndpoint(&zbBinaryHumidifier);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println("Connected");
}
void loop() {
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Toggle fan
zbBinaryFan.setBinaryOutput(!zbBinaryFan.getBinaryOutput());
zbBinaryFan.reportBinaryOutput();
// Toggle zone
zoneStatus = !zoneStatus;
zbBinaryZone.setBinaryInput(zoneStatus);
zbBinaryZone.reportBinaryInput();
// Toggle humidifier
zbBinaryHumidifier.setBinaryOutput(!zbBinaryHumidifier.getBinaryOutput());
zbBinaryHumidifier.reportBinaryOutput();
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,72 @@
# Arduino-ESP32 Carbon dioxide (CO2) Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) simple sensor device type with carbon dioxide measuring.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Pressure + Flow Sensor Functions
* After this board first starts up, it would be configured locally to report the carbon dioxide on every 30 seconds.
* By clicking the button (BOOT) on this board, this board will immediately send a report of the current measured carbon dioxide to the network.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
In this example, the internal temperature sensor is used to demonstrate reading of the carbon dioxide sensors.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,106 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee carbon dioxide sensor.
*
* The example demonstrates how to use Zigbee library to create a end device carbon dioxide sensor.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee carbon dioxide sensor configuration */
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 10
uint8_t button = BOOT_PIN;
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
void setup() {
Serial.begin(115200);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Optional: set Zigbee device name and model
zbCarbonDioxideSensor.setManufacturerAndModel("Espressif", "ZigbeeCarbonDioxideSensor");
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor.setMinMaxValue(0, 1500);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Set reporting interval for carbon dioxide measurement to be done every 30 seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (carbon dioxide change in ppm)
// if min = 1 and max = 0, reporting is sent only when carbon dioxide changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or when carbon dioxide changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of delta change
zbCarbonDioxideSensor.setReporting(0, 30, 0);
}
void loop() {
static uint32_t timeCounter = 0;
// Read carbon dioxide sensor every 2s
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
// Read sensor value - here is chip temperature used + 300 as a dummy value for demonstration
uint16_t carbon_dioxide_value = 300 + (uint16_t)temperatureRead();
Serial.printf("Updating carbon dioxide sensor value to %d ppm\r\n", carbon_dioxide_value);
zbCarbonDioxideSensor.setCarbonDioxide(carbon_dioxide_value);
}
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
zbCarbonDioxideSensor.report();
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,68 @@
# Arduino-ESP32 Zigbee Color Dimmable Light Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) color dimmable light.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee coordinator (loaded with Zigbee_Color_Dimmer_Switch example)
* A USB cable for power supply and programming
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the Zigbee_Color_Dimmable_Light example
### Configure the Project
Set the LED GPIO by changing the `LED_PIN` definition. By default, the LED_PIN is `RGB_BUILTIN`.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,153 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee Color Dimmable light bulb with RGB and Temperature support.
*
* The example demonstrates how to use Zigbee library to create an end device with
* color dimmable light end point supporting both RGB (X/Y) and Color Temperature modes.
* The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee color dimmable light configuration */
#define ZIGBEE_RGB_LIGHT_ENDPOINT 10
uint8_t led = RGB_BUILTIN;
uint8_t button = BOOT_PIN;
ZigbeeColorDimmableLight zbColorLight = ZigbeeColorDimmableLight(ZIGBEE_RGB_LIGHT_ENDPOINT);
/********************* Temperature conversion functions **************************/
uint16_t kelvinToMireds(uint16_t kelvin) {
return 1000000 / kelvin;
}
uint16_t miredsToKelvin(uint16_t mireds) {
return 1000000 / mireds;
}
/********************* RGB LED functions **************************/
void setRGBLight(bool state, uint8_t red, uint8_t green, uint8_t blue, uint8_t level) {
if (!state) {
rgbLedWrite(led, 0, 0, 0);
return;
}
float brightness = (float)level / 255;
rgbLedWrite(led, red * brightness, green * brightness, blue * brightness);
}
/********************* Temperature LED functions **************************/
void setTempLight(bool state, uint8_t level, uint16_t mireds) {
if (!state) {
rgbLedWrite(led, 0, 0, 0);
return;
}
float brightness = (float)level / 255;
// Convert mireds to color temperature (K) and map to white/yellow
uint16_t kelvin = miredsToKelvin(mireds);
uint8_t warm = constrain(map(kelvin, 2000, 6500, 255, 0), 0, 255);
uint8_t cold = constrain(map(kelvin, 2000, 6500, 0, 255), 0, 255);
rgbLedWrite(led, warm * brightness, warm * brightness, cold * brightness);
}
// Create a task on identify call to handle the identify function
void identify(uint16_t time) {
static uint8_t blink = 1;
log_d("Identify called for %d seconds", time);
if (time == 0) {
// If identify time is 0, stop blinking and restore light as it was used for identify
zbColorLight.restoreLight();
return;
}
rgbLedWrite(led, 255 * blink, 255 * blink, 255 * blink);
blink = !blink;
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init RMT and leave light OFF
rgbLedWrite(led, 0, 0, 0);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
// Enable both XY (RGB) and Temperature color capabilities
uint16_t capabilities = ZIGBEE_COLOR_CAPABILITY_X_Y | ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP;
zbColorLight.setLightColorCapabilities(capabilities);
// Set callback functions for RGB and Temperature modes
zbColorLight.onLightChangeRgb(setRGBLight);
zbColorLight.onLightChangeTemp(setTempLight);
// Optional: Set callback function for device identify
zbColorLight.onIdentify(identify);
// Optional: Set Zigbee device name and model
zbColorLight.setManufacturerAndModel("Espressif", "ZBColorLightBulb");
// Set min/max temperature range (High Kelvin -> Low Mireds: Min and Max is switched)
zbColorLight.setLightColorTemperatureRange(kelvinToMireds(6500), kelvinToMireds(2000));
// Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeLight endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbColorLight);
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Increase blightness by 50 every time the button is pressed
zbColorLight.setLightLevel(zbColorLight.getLightLevel() + 50);
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,68 @@
# Arduino-ESP32 Zigbee Color Dimmer Switch Example
This example shows how to configure Zigbee Coordinator and use it as a Home Automation (HA) color dimmer light switch.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee end device (loaded with Zigbee_Color_Dimmable_Light example).
* A USB cable for power supply and programming.
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee coordinator and upload the Zigbee_Color_Dimmable_Light example.
### Configure the Project
Set the Button Switch GPIO by changing the `GPIO_SWITCH` definition. By default, it's the pin `9` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the Coordinator Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`.
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`.
* Select the COM port: `Tools -> Port: xxx where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with the example `Zigbee_Color_Dimmable_Light` is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,165 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee color dimmer switch.
*
* The example demonstrates how to use Zigbee library to control a RGB light bulb.
* The RGB light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator (Switch).
* To turn on/off the light, push the button on the switch.
* To change the color, level, or step the level of the light, send serial commands to the switch.
*
* By setting the switch to allow multiple binding, so it can bind to multiple lights.
* Also every 30 seconds, all bound lights are printed to the serial console.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee color dimmer switch configuration */
#define SWITCH_ENDPOINT_NUMBER 5
uint8_t button = BOOT_PIN;
/* Zigbee switch */
ZigbeeColorDimmerSwitch zbSwitch = ZigbeeColorDimmerSwitch(SWITCH_ENDPOINT_NUMBER);
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
//Init button switch
pinMode(button, INPUT_PULLUP);
//Optional: set Zigbee device name and model
zbSwitch.setManufacturerAndModel("Espressif", "ZigbeeSwitch");
//Optional to allow multiple light to bind to the switch
zbSwitch.allowMultipleBinding(true);
//Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbSwitch);
//Open network for 180 seconds after boot
Zigbee.setRebootOpenNetwork(180);
//When all EPs are registered, start Zigbee with ZIGBEE_COORDINATOR mode
if (!Zigbee.begin(ZIGBEE_COORDINATOR)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Waiting for Light to bound to the switch");
//Wait for switch to bound to a light:
while (!zbSwitch.bound()) {
Serial.printf(".");
delay(500);
}
Serial.println();
}
void loop() {
// Handle button switch in loop()
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
while (digitalRead(button) == LOW) {
delay(50);
}
// Toggle light
zbSwitch.lightToggle();
}
// Handle serial input to control color and level of the light
if (Serial.available()) {
String command = Serial.readString();
if (command == "on") {
zbSwitch.lightOn();
} else if (command == "off") {
zbSwitch.lightOff();
} else if (command == "toggle") {
zbSwitch.lightToggle();
} else if (command == "red") {
zbSwitch.setLightColor(255, 0, 0);
} else if (command == "green") {
zbSwitch.setLightColor(0, 255, 0);
} else if (command == "blue") {
zbSwitch.setLightColor(0, 0, 255);
} else if (command == "white") {
zbSwitch.setLightColor(255, 255, 255);
} else if (command == "color") {
//wait for color value
Serial.println("Enter red value (0-255):");
while (!Serial.available()) {
delay(100);
}
int red = Serial.parseInt();
Serial.println("Enter green value (0-255):");
while (!Serial.available()) {
delay(100);
}
int green = Serial.parseInt();
Serial.println("Enter blue value (0-255):");
while (!Serial.available()) {
delay(100);
}
int blue = Serial.parseInt();
zbSwitch.setLightColor(red, green, blue);
} else if (command == "level") {
//wait for level value
Serial.println("Enter level value (0-255):");
while (!Serial.available()) {
delay(100);
}
int level = Serial.parseInt();
zbSwitch.setLightLevel(level);
} else if (command == "stepup") {
// Step level up by 20 units over 1 second
zbSwitch.setLightLevelStep(ZIGBEE_LEVEL_STEP_UP, 20, 10);
Serial.println("Step level up");
} else if (command == "stepdown") {
// Step level down by 20 units over 1 second
zbSwitch.setLightLevelStep(ZIGBEE_LEVEL_STEP_DOWN, 20, 10);
Serial.println("Step level down");
} else if (command == "stepupfast") {
// Step level up by 10 units as fast as possible (transition_time 0xFFFF)
zbSwitch.setLightLevelStep(ZIGBEE_LEVEL_STEP_UP, 10, 0xFFFF);
Serial.println("Step level up (fast)");
} else if (command == "stepdownfast") {
// Step level down by 10 units as fast as possible
zbSwitch.setLightLevelStep(ZIGBEE_LEVEL_STEP_DOWN, 10, 0xFFFF);
Serial.println("Step level down (fast)");
} else if (command == "help") {
Serial.println("Commands: on, off, toggle, red, green, blue, white, color, level, stepup, stepdown, stepupfast, stepdownfast");
} else {
Serial.println("Unknown command (type 'help' for list)");
}
}
// print the bound devices (lights) every 30 seconds
static uint32_t last_print = 0;
if (millis() - last_print > 30000) {
last_print = millis();
zbSwitch.printBoundDevices(Serial);
}
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,58 @@
# Arduino-ESP32 Zigbee Contact Switch Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) contact switch (IAS Zone),
that can be used for example as window/door sensor having 2 states - closed/open.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
Set the Sensor GPIO by changing the `sensor_pin` variable.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,138 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee contact switch (IAS Zone).
*
* The example demonstrates how to use Zigbee library to create a end device contact switch.
* The contact switch is a Zigbee end device, which is reporting data to the Zigbee network.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#include <Preferences.h>
/* Zigbee contact sensor configuration */
#define CONTACT_SWITCH_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 4;
ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER);
/* Preferences for storing ENROLLED flag to persist across reboots */
Preferences preferences;
void setup() {
Serial.begin(115200);
preferences.begin("Zigbee", false); // Save ENROLLED flag in flash so it persists across reboots
bool enrolled = preferences.getBool("ENROLLED"); // Get ENROLLED flag from preferences
preferences.end();
// Init button + switch
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT_PULLUP);
// Optional: set Zigbee device name and model
zbContactSwitch.setManufacturerAndModel("Espressif", "ZigbeeContactSwitch");
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbContactSwitch);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Check if device has been enrolled before restarting - if so, restore IAS Zone enroll, otherwise request new IAS Zone enroll
if (enrolled) {
Serial.println("Device has been enrolled before - restoring IAS Zone enrollment");
zbContactSwitch.restoreIASZoneEnroll();
} else {
Serial.println("Device is factory new - first time joining network - requesting new IAS Zone enrollment");
zbContactSwitch.requestIASZoneEnroll();
}
while (!zbContactSwitch.enrolled()) {
Serial.print(".");
delay(100);
}
Serial.println();
Serial.println("Zigbee enrolled successfully!");
// Store ENROLLED flag only if this was a new enrollment (previous flag was false)
// Skip writing if we just restored enrollment (flag was already true)
if (!enrolled) {
preferences.begin("Zigbee", false);
preferences.putBool("ENROLLED", true); // set ENROLLED flag to true
preferences.end();
Serial.println("ENROLLED flag saved to preferences");
}
}
void loop() {
// Checking pin for contact change
static bool contact = false;
if (digitalRead(sensor_pin) == HIGH && !contact) {
// Update contact sensor value
zbContactSwitch.setOpen();
contact = true;
} else if (digitalRead(sensor_pin) == LOW && contact) {
zbContactSwitch.setClosed();
contact = false;
}
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
// Clear the ENROLLED flag from preferences
preferences.begin("Zigbee", false);
preferences.putBool("ENROLLED", false); // set ENROLLED flag to false
preferences.end();
Serial.println("ENROLLED flag cleared from preferences");
delay(1000);
Zigbee.factoryReset();
}
}
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,68 @@
# Arduino-ESP32 Zigbee Dimmable Light Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) dimmable light.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
* Board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the Zigbee_Dimmable_Light example
* Zigbee network / coordinator (Other board with switch examples or Zigbee2mqtt or ZigbeeHomeAssistant like application)
### Configure the Project
Set the LED GPIO by changing the `LED_PIN` definition. By default, the LED_PIN is `RGB_BUILTIN`.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,121 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee Dimmable light bulb.
*
* The example demonstrates how to use Zigbee library to create an end device with
* dimmable light end point.
* The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by [FaBjE](https://github.com/FaBjE) based on examples by [Jan Procházka](https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee dimmable light configuration */
#define ZIGBEE_LIGHT_ENDPOINT 10
uint8_t led = RGB_BUILTIN;
uint8_t button = BOOT_PIN;
ZigbeeDimmableLight zbDimmableLight = ZigbeeDimmableLight(ZIGBEE_LIGHT_ENDPOINT);
/********************* RGB LED functions **************************/
void setLight(bool state, uint8_t level) {
if (!state) {
rgbLedWrite(led, 0, 0, 0);
return;
}
rgbLedWrite(led, level, level, level);
}
// Create a task on identify call to handle the identify function
void identify(uint16_t time) {
static uint8_t blink = 1;
log_d("Identify called for %d seconds", time);
if (time == 0) {
// If identify time is 0, stop blinking and restore light as it was used for identify
zbDimmableLight.restoreLight();
return;
}
rgbLedWrite(led, 255 * blink, 255 * blink, 255 * blink);
blink = !blink;
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init RMT and leave light OFF
rgbLedWrite(led, 0, 0, 0);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
// Set callback function for light change
zbDimmableLight.onLightChange(setLight);
// Optional: Set callback function for device identify
zbDimmableLight.onIdentify(identify);
// Optional: Set Zigbee device name and model
zbDimmableLight.setManufacturerAndModel("Espressif", "ZBLightBulb");
// Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeLight endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbDimmableLight);
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Increase blightness by 50 every time the button is pressed
zbDimmableLight.setLightLevel(zbDimmableLight.getLightLevel() + 50);
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,87 @@
# Arduino-ESP32 Zigbee AC Electrical Measurement Example
This example shows how to configure the Zigbee router device and use it as a Home Automation (HA) AC electrical measurement device that reports voltage, current, power, and frequency measurements.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## AC Electrical Measurement Functions
* After this board first starts up, it would be configured locally to report AC electrical measurements:
- AC voltage in volts (0-300.00 V)
- AC current in amps (0-10.000 A)
- AC power in watts (0-3200.0 W)
- AC frequency in hertz (0-65.000 Hz)
* Holding the button (BOOT) for more than 3 seconds will trigger a factory reset of the device.
* The device reports measurements every 2 seconds with simulated values.
## Measurement Precision
The example demonstrates how to set up proper measurement precision using multiplier and divisor values:
* Voltage: 1/100 = 0.01 V (1 unit = 10 mV)
* Current: 1/1000 = 0.001 A (1 unit = 1 mA)
* Power: 1/10 = 0.1 W (1 unit = 100 mW)
* Frequency: 1/1000 = 0.001 Hz (1 unit = 1 mHz)
These settings ensure accurate reporting of measurements with proper decimal precision in the Zigbee network.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the ADC GPIO by changing the `analogPin` variable. By default, it's the pin `A0`.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the Router Zigbee mode: `Tools -> Zigbee mode: Zigbee Router`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the Router device flashed with this example is not connecting to the coordinator, erase the flash of the Router device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,162 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee electrical AC measurement device.
*
* The example demonstrates how to use Zigbee library to create a router device that measures
* AC electrical parameters like voltage, current, power, power factor and frequency.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
// Recommended to use Router mode, as this type of device is expected to be mains powered.
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator/router mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee AC measurement device configuration */
#define AC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER 1
uint8_t analogPin = A0;
uint8_t button = BOOT_PIN;
ZigbeeElectricalMeasurement zbElectricalMeasurement = ZigbeeElectricalMeasurement(AC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER);
void onAnalogOutputChange(float analog_output) {
Serial.printf("Received analog output change: %.1f\r\n", analog_output);
}
void setup() {
Serial.begin(115200);
Serial.println("Starting...");
// Init button switch
pinMode(button, INPUT_PULLUP);
// Set analog resolution to 10 bits
analogReadResolution(10);
// Optional: set Zigbee device name and model
zbElectricalMeasurement.setManufacturerAndModel("Espressif", "ZigbeeElectricalMeasurementAC");
// Add analog clusters to Zigbee Analog according your needs
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER_FACTOR, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.addACMeasurement(
ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC
); // frequency is not phase specific (shared)
// Optional: set Multiplier/Divisor for the measurements
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, 1, 100); // 1/100 = 0.01V (1 unit of measurement = 0.01V = 10mV)
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, 1, 1000); // 1/1000 = 0.001A (1 unit of measurement = 0.001A = 1mA)
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, 1, 10); // 1/10 = 0.1W (1 unit of measurement = 0.1W = 100mW)
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, 1, 1000); // 1/1000 = 0.001Hz (1 unit of measurement = 0.001Hz = 1mHz)
// Optional: set Min/max values for the measurements
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A, 0, 30000); // 0-300.00V
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A, 0, 10000); // 0-10.000A
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A, 0, 32000); // 0-3200.0W
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC, 0, 65000); // 0-65.000Hz
// Optional: set power factor for the measurements
zbElectricalMeasurement.setACPowerFactor(ZIGBEE_AC_PHASE_TYPE_A, 98); // 100 = 1.00, 0 = 0.00, -100 = -1.00
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbElectricalMeasurement);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in Router mode
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println("Connected");
// Optional: Add reporting for AC measurements (this is overridden by HomeAssistant ZHA if used as a Zigbee coordinator)
zbElectricalMeasurement.setACReporting(
ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A, 0, 30, 10
); // report every 30 seconds if value changes by 10 (0.1V)
zbElectricalMeasurement.setACReporting(
ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A, 0, 30, 100
); // report every 30 seconds if value changes by 10 (0.1A)
zbElectricalMeasurement.setACReporting(
ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A, 0, 30, 10
); // report every 30 seconds if value changes by 10 (1W)
zbElectricalMeasurement.setACReporting(
ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC, 0, 30, 100
); // report every 30 seconds if value changes by 100 (0.1Hz)
}
void loop() {
static uint32_t timeCounter = 0;
static uint8_t randomizer = 0;
// Read ADC value as current to demonstrate the measurements and update the electrical measurement values every 2s
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
uint16_t voltage = 23000 + randomizer; // 230.00 V
uint16_t current = analogReadMilliVolts(analogPin); // demonstrates 0-3.3A
int16_t power = ((voltage / 100) * (current / 1000) * 10); //calculate power in W
uint16_t frequency = 50135; // 50.000 Hz
Serial.printf("Updating AC voltage to %d (0.01V), current to %d (mA), power to %d (0.1W), frequency to %d (mHz)\r\n", voltage, current, power, frequency);
// Update the measurements
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A, voltage);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A, current);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A, power);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC, frequency);
// Report the measurements
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC);
randomizer += 10;
}
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
delay(100);
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,87 @@
# Arduino-ESP32 Zigbee AC Electrical MultiPhase Measurement Example
This example shows how to configure the Zigbee router device and use it as a Home Automation (HA) AC electrical measurement device that reports voltage, current, power, and frequency measurements across three phases.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## AC Electrical Measurement Functions
* After this board first starts up, it would be configured locally to report AC electrical measurements:
- AC voltage in volts (0-300.00 V) for each phase (A, B, C)
- AC current in amps (0-10.000 A) for each phase (A, B, C)
- AC power in watts (0-3200.0 W) for each phase (A, B, C)
- AC frequency in hertz (0-65.000 Hz) shared across all phases
* Holding the button (BOOT) for more than 3 seconds will trigger a factory reset of the device.
* The device reports measurements every 2 seconds with simulated values.
## Measurement Precision
The example demonstrates how to set up proper measurement precision using multiplier and divisor values:
* Voltage: 1/100 = 0.01 V (1 unit = 10 mV)
* Current: 1/1000 = 0.001 A (1 unit = 1 mA)
* Power: 1/10 = 0.1 W (1 unit = 100 mW)
* Frequency: 1/1000 = 0.001 Hz (1 unit = 1 mHz)
These settings ensure accurate reporting of measurements with proper decimal precision in the Zigbee network.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the ADC GPIO by changing the `analogPin` variable. By default, it's the pin `A0`.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the Router Zigbee mode: `Tools -> Zigbee mode: Zigbee Router`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the Router device flashed with this example is not connecting to the coordinator, erase the flash of the Router device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,192 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee electrical AC measurement device with multi-phase support.
*
* The example demonstrates how to use Zigbee library to create a router device that measures
* AC electrical parameters like voltage, current, power, power factor and frequency across
* three phases (A, B, C). This allows monitoring of three-phase power systems commonly used
* in industrial and commercial applications.
*
* The device measures:
* - Per phase: voltage, current, power, power factor
* - Shared: frequency (common across all phases)
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
// Recommended to use Router mode, as this type of device is expected to be mains powered.
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator/router mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee AC measurement device configuration */
#define AC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER 1
uint8_t analogPin = A0;
uint8_t button = BOOT_PIN;
ZigbeeElectricalMeasurement zbElectricalMeasurement = ZigbeeElectricalMeasurement(AC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER);
void onAnalogOutputChange(float analog_output) {
Serial.printf("Received analog output change: %.1f\r\n", analog_output);
}
void setup() {
Serial.begin(115200);
Serial.println("Starting...");
// Init button switch
pinMode(button, INPUT_PULLUP);
// Set analog resolution to 10 bits
analogReadResolution(10);
// Optional: set Zigbee device name and model
zbElectricalMeasurement.setManufacturerAndModel("Espressif", "ZigbeeElectricalMeasurementAC");
// Add analog clusters to Zigbee Analog according your needs
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_B);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_B);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_B);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_C);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_C);
zbElectricalMeasurement.addACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_C);
zbElectricalMeasurement.addACMeasurement(
ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC
); // frequency is not phase specific (shared)
// Recommended: set Multiplier/Divisor for the measurements (common for all phases)
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, 1, 100); // 1/100 = 0.01V (1 unit of measurement = 0.01V = 10mV)
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, 1, 1000); // 1/1000 = 0.001A (1 unit of measurement = 0.001A = 1mA)
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, 1, 10); // 1/10 = 0.1W (1 unit of measurement = 0.1W = 100mW)
zbElectricalMeasurement.setACMultiplierDivisor(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, 1, 1000); // 1/1000 = 0.001Hz (1 unit of measurement = 0.001Hz = 1mHz)
// Optional: set Min/max values for the measurements
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A, 0, 30000); // 0-300.00V
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A, 0, 10000); // 0-10.000A
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A, 0, 32000); // 0-3200.0W
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_B, 0, 30000); // 0-300.00V
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_B, 0, 10000); // 0-10.000A
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_B, 0, 32000); // 0-3200.0W
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_C, 0, 30000); // 0-300.00V
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_C, 0, 10000); // 0-10.000A
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_C, 0, 32000); // 0-3200.0W
zbElectricalMeasurement.setACMinMaxValue(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC, 0, 65000); // 0-65.000Hz
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbElectricalMeasurement);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in Router mode
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println("Connected");
}
void loop() {
static uint32_t timeCounter = 0;
static uint8_t randomizer = 0;
// Read ADC value and update the analog value every 2s
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
uint16_t voltage = 23000 + randomizer; // 230.00 V
uint16_t current = analogReadMilliVolts(analogPin); // demonstrates approx 0-3.3A
int16_t power = ((voltage / 100) * (current / 1000) * 10); //calculate power in W
uint16_t frequency = 50135; // 50.000 Hz
Serial.printf("Updating AC voltage to %d (0.01V), current to %d (mA), power to %d (0.1W), frequency to %d (mHz)\r\n", voltage, current, power, frequency);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A, voltage);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A, current);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A, power);
// Phase B demonstrates phase shift
current += 500;
voltage += 500;
power = ((voltage / 100) * (current / 1000) * 10); //calculate power in W
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_B, voltage);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_B, current);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_B, power);
// Phase C demonstrates phase shift
current += 500;
voltage += 500;
power = ((voltage / 100) * (current / 1000) * 10); //calculate power in W
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_C, voltage);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_C, current);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_C, power);
zbElectricalMeasurement.setACMeasurement(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC, frequency);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_A);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_B);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_B);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_B);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_VOLTAGE, ZIGBEE_AC_PHASE_TYPE_C);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_CURRENT, ZIGBEE_AC_PHASE_TYPE_C);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_POWER, ZIGBEE_AC_PHASE_TYPE_C);
zbElectricalMeasurement.reportAC(ZIGBEE_AC_MEASUREMENT_TYPE_FREQUENCY, ZIGBEE_AC_PHASE_TYPE_NON_SPECIFIC);
randomizer += 10;
}
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
delay(100);
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,85 @@
# Arduino-ESP32 Zigbee DC Electrical Measurement Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) DC electrical measurement device that reports voltage, current, and power measurements.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## DC Electrical Measurement Functions
* After this board first starts up, it would be configured locally to report DC electrical measurements:
- DC voltage in millivolts (0-5000 mV)
- DC current in milliamps (0-1000 mA)
- DC power in milliwatts (0-5000 mW)
* Holding the button (BOOT) for more than 3 seconds will trigger a factory reset of the device.
* The device reports measurements every 30 seconds if the value changes by more than the configured delta.
## Measurement Precision
The example demonstrates how to set up proper measurement precision using multiplier and divisor values:
* Voltage: 1/1000 = 0.001 V (1 unit = 1 mV)
* Current: 1/1000 = 0.001 A (1 unit = 1 mA)
* Power: 1/1000 = 0.001 W (1 unit = 1 mW)
These settings ensure accurate reporting of measurements with proper decimal precision in the Zigbee network.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the ADC GPIO by changing the `analogPin` variable. By default, it's the pin `A0`.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,137 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates a Zigbee DC electrical measurement sensor.
*
* The example shows how to use the Zigbee library to create an end device that measures
* DC voltage, current and power using the Electrical Measurement cluster.
*
* The device reports:
* - DC voltage in millivolts (0-5000mV)
* - DC current in milliamps (0-1000mA)
* - DC power in milliwatts (0-5000mW)
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee DC measurement device configuration */
#define DC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER 1
uint8_t analogPin = A0;
uint8_t button = BOOT_PIN;
ZigbeeElectricalMeasurement zbElectricalMeasurement = ZigbeeElectricalMeasurement(DC_ELECTRICAL_MEASUREMENT_ENDPOINT_NUMBER);
void setup() {
Serial.begin(115200);
Serial.println("Starting...");
// Init button switch
pinMode(button, INPUT_PULLUP);
// Set analog resolution to 10 bits
analogReadResolution(10);
// Optional: set Zigbee device name and model
zbElectricalMeasurement.setManufacturerAndModel("Espressif", "ZigbeeElectricalMeasurementDC");
// Add analog clusters to Zigbee Analog according your needs
zbElectricalMeasurement.addDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE);
zbElectricalMeasurement.addDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT);
zbElectricalMeasurement.addDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_POWER);
// // Optional: set Min/max values for the measurements
zbElectricalMeasurement.setDCMinMaxValue(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE, 0, 5000); // 0-5.000V
zbElectricalMeasurement.setDCMinMaxValue(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT, 0, 1000); // 0-1.000A
zbElectricalMeasurement.setDCMinMaxValue(ZIGBEE_DC_MEASUREMENT_TYPE_POWER, 0, 5000); // 0-5.000W
// // Optional: set Multiplier/Divisor for the measurements
zbElectricalMeasurement.setDCMultiplierDivisor(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE, 1, 1000); // 1/1000 = 0.001V (1 unit of measurement = 0.001V = 1mV)
zbElectricalMeasurement.setDCMultiplierDivisor(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT, 1, 1000); // 1/1000 = 0.001A (1 unit of measurement = 0.001A = 1mA)
zbElectricalMeasurement.setDCMultiplierDivisor(ZIGBEE_DC_MEASUREMENT_TYPE_POWER, 1, 1000); // 1/1000 = 0.001W (1 unit of measurement = 0.001W = 1mW)
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbElectricalMeasurement);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println("Connected");
// Optional: Add reporting for DC measurements (this is overridden by HomeAssistant ZHA if connected to its network)
zbElectricalMeasurement.setDCReporting(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE, 0, 30, 10); // report every 30 seconds if value changes by 10 (0.1V)
zbElectricalMeasurement.setDCReporting(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT, 0, 30, 10); // report every 30 seconds if value changes by 10 (0.1A)
zbElectricalMeasurement.setDCReporting(ZIGBEE_DC_MEASUREMENT_TYPE_POWER, 0, 30, 10); // report every 30 seconds if value changes by 10 (0.1W)
}
void loop() {
static uint32_t timeCounter = 0;
static uint8_t randomizer = 0;
// Read ADC value and update the analog value every 2s
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
int16_t voltage_mv = (int16_t)(analogReadMilliVolts(analogPin));
int16_t current_ma = randomizer; //0-255mA
int16_t power_mw = voltage_mv * current_ma / 1000; //calculate power in mW
Serial.printf("Updating DC voltage to %d mV, current to %d mA, power to %d mW\r\n", voltage_mv, current_ma, power_mw);
zbElectricalMeasurement.setDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE, voltage_mv);
zbElectricalMeasurement.setDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT, current_ma);
zbElectricalMeasurement.setDCMeasurement(ZIGBEE_DC_MEASUREMENT_TYPE_POWER, power_mw);
// Analog input supports reporting
zbElectricalMeasurement.reportDC(ZIGBEE_DC_MEASUREMENT_TYPE_VOLTAGE);
zbElectricalMeasurement.reportDC(ZIGBEE_DC_MEASUREMENT_TYPE_CURRENT);
zbElectricalMeasurement.reportDC(ZIGBEE_DC_MEASUREMENT_TYPE_POWER);
randomizer += 10;
}
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,83 @@
# Arduino-ESP32 Zigbee Fan Control Example
This example demonstrates how to use the Zigbee library to create a router device fan control and use it as a Home Automation (HA) fan control device.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Fan Control Functions
1. Initialize a Zigbee fan control device.
2. Control fan modes (OFF, LOW, MEDIUM, HIGH, ON).
3. Respond to fan control commands from the Zigbee network.
## Hardware Required
* ESP32-H2 or ESP32-C6 development board
* A USB cable for power supply and programming
* RGB LED for visual feedback (built-in on most development boards)
### Configure the Project
In this example the RGB LED is used to indicate the current fan control mode.
The LED colors represent different fan modes:
- OFF: No light
- LOW: Blue
- MEDIUM: Yellow
- HIGH: Red
- ON: White
Set the button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee ZCZR 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,129 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates simple Zigbee fan control.
*
* The example demonstrates how to use Zigbee library to create a router device fan control.
* The fan control is a Zigbee router device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee light bulb configuration */
#define ZIGBEE_FAN_CONTROL_ENDPOINT 1
#ifdef RGB_BUILTIN
uint8_t led = RGB_BUILTIN; // To demonstrate the current fan control mode
#else
uint8_t led = 2;
#endif
uint8_t button = BOOT_PIN;
ZigbeeFanControl zbFanControl = ZigbeeFanControl(ZIGBEE_FAN_CONTROL_ENDPOINT);
/********************* fan control callback function **************************/
void setFan(ZigbeeFanMode mode) {
switch (mode) {
case FAN_MODE_OFF:
rgbLedWrite(led, 0, 0, 0); // Off
Serial.println("Fan mode: OFF");
break;
case FAN_MODE_LOW:
rgbLedWrite(led, 0, 0, 255); // Blue
Serial.println("Fan mode: LOW");
break;
case FAN_MODE_MEDIUM:
rgbLedWrite(led, 255, 255, 0); // Yellow
Serial.println("Fan mode: MEDIUM");
break;
case FAN_MODE_HIGH:
rgbLedWrite(led, 255, 0, 0); // Red
Serial.println("Fan mode: HIGH");
break;
case FAN_MODE_ON:
rgbLedWrite(led, 255, 255, 255); // White
Serial.println("Fan mode: ON");
break;
default: log_e("Unhandled fan mode: %d", mode); break;
}
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init LED that will be used to indicate the current fan control mode
rgbLedWrite(led, 0, 0, 0);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
//Optional: set Zigbee device name and model
zbFanControl.setManufacturerAndModel("Espressif", "ZBFanControl");
// Set the fan mode sequence to LOW_MED_HIGH
zbFanControl.setFanModeSequence(FAN_MODE_SEQUENCE_LOW_MED_HIGH);
// Set callback function for fan mode change
zbFanControl.onFanModeChange(setFan);
//Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeFanControl endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbFanControl);
// When all EPs are registered, start Zigbee in ROUTER mode
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
delay(100);
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,64 @@
# Arduino-ESP32 Zigbee Gateway Example
This example shows how to configure Zigbee Gateway device, running on SoCs without native IEEE 802.15.4.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 |
| ----------------- | ----- | -------- | -------- | -------- |
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee Radio Co-processor loaded with [ot_rcp example](https://github.com/espressif/esp-idf/tree/master/examples/openthread/ot_rcp).
* A USB cable for power supply and programming.
* Choose another board from supported targets as Zigbee coordinator/router and upload the Zigbee_Gateway example.
### Configure the Project
Set the RCP connection (UART) by changing the `GATEWAY_RCP_UART_PORT`, `GATEWAY_RCP_RX_PIN` and `GATEWAY_RCP_TX_PIN` definition.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the Coordinator Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`.
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`.
* Select the COM port: `Tools -> Port: xxx where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,130 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates simple Zigbee Gateway functionality.
*
* The example demonstrates how to use Zigbee library on ESP32s to create a Zigbee Gateway, updating the time from NTP server.
* The Gateway is able to communicate with Zigbee end devices and send/receive data to/from them.
* The Gateway is also able to communicate with the cloud or other devices over Wi-Fi / BLE.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode->Zigbee ZCZR (coordinator/router)
* and also the correct partition scheme must be selected in Tools->Partition Scheme->Zigbee ZCZR
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#include <WiFi.h>
#include "time.h"
#include "esp_sntp.h"
/* Zigbee gateway configuration */
#define GATEWAY_ENDPOINT_NUMBER 1
#define GATEWAY_RCP_UART_PORT UART_NUM_1 // UART 0 is used for Serial communication
#define GATEWAY_RCP_RX_PIN 4
#define GATEWAY_RCP_TX_PIN 5
ZigbeeGateway zbGateway = ZigbeeGateway(GATEWAY_ENDPOINT_NUMBER);
/* Wi-Fi credentials */
const char *ssid = "your-ssid";
const char *password = "your-password";
/* NTP server configuration */
const char *ntpServer1 = "pool.ntp.org";
const char *ntpServer2 = "time.nist.gov";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 3600;
const char *time_zone = "CET-1CEST,M3.5.0,M10.5.0/3"; // TimeZone rule for Europe/Rome including daylight adjustment rules (optional)
/* Time structure */
struct tm timeinfo;
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Initialize Wi-Fi and connect to AP
WiFi.begin(ssid, password);
esp_sntp_servermode_dhcp(1); // (optional)
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
// Initialize Zigbee and Begin Zigbee stack
// Optional: set Zigbee device name and model
zbGateway.setManufacturerAndModel("Espressif", "ZigbeeGateway");
zbGateway.addTimeCluster(timeinfo, gmtOffset_sec);
// Add endpoint to Zigbee Core
Serial.println("Adding Zigbee Gateway endpoint");
Zigbee.addEndpoint(&zbGateway);
// Optional: Open network for 180 seconds after boot
Zigbee.setRebootOpenNetwork(180);
// Set custom radio configuration for RCP communication
esp_zb_radio_config_t radio_config = ZIGBEE_DEFAULT_UART_RCP_RADIO_CONFIG();
radio_config.radio_uart_config.port = GATEWAY_RCP_UART_PORT;
radio_config.radio_uart_config.rx_pin = (gpio_num_t)GATEWAY_RCP_RX_PIN;
radio_config.radio_uart_config.tx_pin = (gpio_num_t)GATEWAY_RCP_TX_PIN;
Zigbee.setRadioConfig(radio_config);
// When all EPs are registered, start Zigbee with ZIGBEE_COORDINATOR or ZIGBEE_ROUTER mode
if (!Zigbee.begin(ZIGBEE_COORDINATOR)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
// set notification call-back function
sntp_set_time_sync_notification_cb(timeavailable);
sntp_set_sync_interval(30000); // sync every 30 seconds
// config time zone and NTP servers
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);
}
void loop() {
// Nothing to do here in this example
}
void printLocalTime() {
if (!getLocalTime(&timeinfo)) {
Serial.println("No time available (yet)");
return;
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
zbGateway.setTime(timeinfo);
Serial.println("Time updated in Zigbee Gateway");
}
// Callback function (gets called when time adjusts via NTP)
void timeavailable(struct timeval *t) {
Serial.println("Got time adjustment from NTP!");
printLocalTime();
}
@@ -0,0 +1,8 @@
fqbn_append: PartitionScheme=zigbee_zczr_8MB,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
targets:
esp32c6: false
esp32h2: false
@@ -0,0 +1,78 @@
# Arduino-ESP32 Zigbee Illuminance Sensor Example
This example demonstrates how to use the Zigbee library to create an end device illuminance sensor and use it as a Home Automation (HA) extended illuminance sensor.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Illuminance Sensor Functions
1. Initialize a Zigbee illuminance sensor.
2. Measure illuminance value.
3. Report the measured value to the Zigbee network.
## Hardware Required
* ESP32-H2 or ESP32-C6 development board
* A USB cable for power supply and programming
* Some kind of light sensor, such as a photoresistor
### Configure the Project
In this example the raw analog value of a light sensor is used to calculate illuminance.
Alter the calculation according to your use case and calibrate it to receive correct lux values.
Set the illuminance sensor GPIO by changing the `illuminance_sensor_pin` variable to the pin to the pin to which your sensor is connected.
Set the button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Enable USB CDC to be able to use the serial monitor: `Tools -> USB CDC On Boot: Enabled`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure that you are using a good quality USB cable with data lines and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,141 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee illuminance sensor.
*
* The example demonstrates how to use Zigbee library to create a end device illuminance sensor.
* The illuminance sensor is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by MikaFromTheRoof (https://github.com/MikaFromTheRoof)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#define ZIGBEE_ILLUMINANCE_SENSOR_ENDPOINT 9
uint8_t button = BOOT_PIN;
uint8_t illuminance_sensor_pin = 6; // Insert the analog pin to which the sensor (e.g. photoresistor) is connected
ZigbeeIlluminanceSensor zbIlluminanceSensor = ZigbeeIlluminanceSensor(ZIGBEE_ILLUMINANCE_SENSOR_ENDPOINT);
/********************* Illuminance sensor **************************/
static void illuminance_sensor_value_update(void *arg) {
for (;;) {
// read the raw analog value from the sensor
int lsens_analog_raw = analogRead(illuminance_sensor_pin);
Serial.printf("[Illuminance Sensor] raw analog value: %d\r\n", lsens_analog_raw);
// conversion into zigbee raw illuminance value (typically between 0 in darkness and 50000 in direct sunlight)
// depends on the value range of the raw analog sensor values and will need calibration for correct lux values
// for demonstration purpose map the 12-bit ADC value (0-4095) to Zigbee illuminance range (0-50000)
int lsens_illuminance_raw = map(lsens_analog_raw, 0, 4095, 0, 50000);
Serial.printf("[Illuminance Sensor] raw illuminance value: %d\r\n", lsens_illuminance_raw);
// according to zigbee documentation the formular 10^(lsens_illuminance_raw/10000)-1 can be used to calculate lux value from raw illuminance value
// Note: Zigbee2MQTT seems to be using the formular 10^(lsens_illuminance_raw/10000) instead (without -1)
int lsens_illuminance_lux = round(pow(10, (lsens_illuminance_raw / 10000.0)) - 1);
Serial.printf("[Illuminance Sensor] lux value: %d lux\r\n", lsens_illuminance_lux);
// Update illuminance in illuminance sensor EP
zbIlluminanceSensor.setIlluminance(lsens_illuminance_raw); // use raw illuminance here!
delay(1000); // reduce delay (in ms), if you want your device to react more quickly to changes in illuminance
}
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Optional: configure analog input
analogSetAttenuation(ADC_11db); // set analog to digital converter (ADC) attenuation to 11 dB (up to ~3.3V input)
analogReadResolution(12); // set analog read resolution to 12 bits (value range from 0 to 4095), 12 is default
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
// Optional: Set Zigbee device name and model
zbIlluminanceSensor.setManufacturerAndModel("Espressif", "ZigbeeIlluminanceSensor");
// Optional: Set power source (choose between ZB_POWER_SOURCE_MAINS and ZB_POWER_SOURCE_BATTERY), defaults to unknown
zbIlluminanceSensor.setPowerSource(ZB_POWER_SOURCE_MAINS);
// Set minimum and maximum for raw illuminance value (0 min and 50000 max equals to 0 lux - 100,000 lux)
zbIlluminanceSensor.setMinMaxValue(0, 50000);
// Optional: Set tolerance for raw illuminance value
zbIlluminanceSensor.setTolerance(1);
// Add endpoint to Zigbee Core
Serial.println("Adding Zigbee illuminance sensor endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbIlluminanceSensor);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Start illuminance sensor reading task
xTaskCreate(illuminance_sensor_value_update, "illuminance_sensor_update", 2048, NULL, 10, NULL);
// Set reporting schedule for illuminance value measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta
// if min = 1 and max = 0, delta = 1000, reporting is sent when raw illuminance value changes by 1000, but at most once per second
// if min = 0 and max = 10, delta = 1000, reporting is sent every 10 seconds or if raw illuminance value changes by 1000
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of illuminance change
// Note: On pairing with Zigbee Home Automation or Zigbee2MQTT the reporting schedule will most likely be overwritten with their default settings
zbIlluminanceSensor.setReporting(1, 0, 1000);
}
/********************* Main loop **************************/
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3 secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s");
delay(1000);
Zigbee.factoryReset();
}
}
// force report of illuminance when button is pressed
zbIlluminanceSensor.report();
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,90 @@
# Arduino-ESP32 Zigbee Multistate Input Output Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) multistate input/output device.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Multistate Device Functions
This example demonstrates two different multistate devices:
1. **Standard Multistate Device** (`zbMultistateDevice`): Uses predefined application states from the Zigbee specification
- Application Type 0: Fan states (Off, On, Auto)
- Application Type 7: Light states (High, Normal, Low)
2. **Custom Multistate Device** (`zbMultistateDeviceCustom`): Uses user-defined custom states
- Custom fan states: Off, On, UltraSlow, Slow, Fast, SuperFast
* After this board first starts up, it will be configured as two multistate devices with different state configurations.
* By clicking the button (BOOT) on this board, the devices will cycle through their respective states and report the changes to the network.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
The example creates two multistate devices:
- **Endpoint 1**: Standard multistate device using predefined Zigbee application types
- **Endpoint 2**: Custom multistate device using user-defined states
You can modify the state names and configurations by changing the following variables:
- `multistate_custom_state_names[]`: Array of custom state names
- Application type and length macros: `ZB_MULTISTATE_APPLICATION_TYPE_X_STATE_NAMES`,
`ZB_MULTISTATE_APPLICATION_TYPE_X_NUM_STATES`, `ZB_MULTISTATE_APPLICATION_TYPE_X_INDEX`
- Device descriptions and application types in the setup() function
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the Coordinator/Router Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee ZCZR 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,189 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee multistate input / output device.
*
* The example demonstrates how to use Zigbee library to create a router multistate device.
* In the example, we have two multistate devices:
* - zbMultistateDevice: uses defined application states from Zigbee specification
* - zbMultistateDeviceCustom: uses custom application states (user defined)
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* NOTE: HomeAssistant ZHA does not support multistate input and output clusters yet.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator/router device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee multistate device configuration */
#define MULTISTATE_DEVICE_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
// zbMultistateDevice will use defined application states
ZigbeeMultistate zbMultistateDevice = ZigbeeMultistate(MULTISTATE_DEVICE_ENDPOINT_NUMBER);
// zbMultistateDeviceCustom will use custom application states (user defined)
ZigbeeMultistate zbMultistateDeviceCustom = ZigbeeMultistate(MULTISTATE_DEVICE_ENDPOINT_NUMBER + 1);
const char *multistate_custom_state_names[6] = {"Off", "On", "UltraSlow", "Slow", "Fast", "SuperFast"};
void onStateChange(uint16_t state) {
// print the state
Serial.printf("Received state change: %d\r\n", state);
// print the state name using the stored state names
const char *const *state_names = ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES;
if (state_names && state < zbMultistateDevice.getMultistateOutputStateNamesLength()) {
Serial.printf("State name: %s\r\n", state_names[state]);
}
// print state index of possible options
Serial.printf("State index: %d / %d\r\n", state, zbMultistateDevice.getMultistateOutputStateNamesLength() - 1);
}
void onStateChangeCustom(uint16_t state) {
// print the state
Serial.printf("Received state change: %d\r\n", state);
// print the state name using the stored state names
if (state < zbMultistateDeviceCustom.getMultistateOutputStateNamesLength()) {
Serial.printf("State name: %s\r\n", multistate_custom_state_names[state]);
}
// print state index of possible options
Serial.printf("State index: %d / %d\r\n", state, zbMultistateDeviceCustom.getMultistateOutputStateNamesLength() - 1);
Serial.print("Changing to fan mode to: ");
switch (state) {
case 0: Serial.println("Off"); break;
case 1: Serial.println("On"); break;
case 2: Serial.println("UltraSlow"); break;
case 3: Serial.println("Slow"); break;
case 4: Serial.println("Fast"); break;
case 5: Serial.println("SuperFast"); break;
default: Serial.println("Invalid state"); break;
}
}
void setup() {
log_d("Starting serial");
Serial.begin(115200);
// Init button switch
log_d("Init button switch");
pinMode(button, INPUT_PULLUP);
// Optional: set Zigbee device name and model
log_d("Set Zigbee device name and model");
zbMultistateDevice.setManufacturerAndModel("Espressif", "ZigbeeMultistateDevice");
// Set up analog input
log_d("Add Multistate Input");
zbMultistateDevice.addMultistateInput();
log_d("Set Multistate Input Application");
zbMultistateDevice.setMultistateInputApplication(ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX);
log_d("Set Multistate Input Description");
zbMultistateDevice.setMultistateInputDescription("Fan (on/off/auto)");
zbMultistateDevice.setMultistateInputStates(ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES);
// Set up analog output
log_d("Add Multistate Output");
zbMultistateDevice.addMultistateOutput();
log_d("Set Multistate Output Application");
zbMultistateDevice.setMultistateOutputApplication(ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX);
log_d("Set Multistate Output Description");
zbMultistateDevice.setMultistateOutputDescription("Light (high/normal/low)");
zbMultistateDevice.setMultistateOutputStates(ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES);
// Set up custom output
log_d("Add Multistate Output");
zbMultistateDeviceCustom.addMultistateOutput();
log_d("Set Multistate Output Application");
zbMultistateDeviceCustom.setMultistateOutputApplication(ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX);
log_d("Set Multistate Output Description");
zbMultistateDeviceCustom.setMultistateOutputDescription("Fan (on/off/slow/medium/fast)");
zbMultistateDeviceCustom.setMultistateOutputStates(5);
// Set callback function for multistate output change
log_d("Set callback function for multistate output change");
zbMultistateDevice.onMultistateOutputChange(onStateChange);
zbMultistateDeviceCustom.onMultistateOutputChange(onStateChangeCustom);
// Add endpoints to Zigbee Core
log_d("Add endpoints to Zigbee Core");
Zigbee.addEndpoint(&zbMultistateDevice);
Zigbee.addEndpoint(&zbMultistateDeviceCustom);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in Router Device mode
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println("Connected");
}
void loop() {
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// For demonstration purposes, increment the multistate output/input value by 1
if (zbMultistateDevice.getMultistateOutput() < zbMultistateDevice.getMultistateOutputStateNamesLength() - 1) {
zbMultistateDevice.setMultistateOutput(zbMultistateDevice.getMultistateOutput() + 1);
zbMultistateDevice.reportMultistateOutput();
zbMultistateDevice.setMultistateInput(zbMultistateDevice.getMultistateOutput());
zbMultistateDevice.reportMultistateInput();
} else {
zbMultistateDevice.setMultistateOutput(0);
zbMultistateDevice.reportMultistateOutput();
zbMultistateDevice.setMultistateInput(zbMultistateDevice.getMultistateOutput());
zbMultistateDevice.reportMultistateInput();
}
if (zbMultistateDeviceCustom.getMultistateOutput() < zbMultistateDeviceCustom.getMultistateOutputStateNamesLength() - 1) {
zbMultistateDeviceCustom.setMultistateOutput(zbMultistateDeviceCustom.getMultistateOutput() + 1);
zbMultistateDeviceCustom.reportMultistateOutput();
} else {
zbMultistateDeviceCustom.setMultistateOutput(0);
zbMultistateDeviceCustom.reportMultistateOutput();
}
}
delay(100);
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,68 @@
# Arduino-ESP32 Zigbee OTA Client + on/off light Example
This example shows how to configure the Zigbee end device with OTA Client and use it as a Home Automation (HA) on/off light.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the LED GPIO by changing the `LED_PIN` definition. By default, the LED_PIN is `RGB_BUILTIN`.
By default, the `rgbLedWrite` function is used to control the LED. You can change it to digitalWrite to control a simple LED.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,131 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates OTA support on light bulb.
*
* The example demonstrates how to use Zigbee library to create a end device light bulb with OTA support.
* The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee light bulb configuration */
#define ZIGBEE_LIGHT_ENDPOINT 1
uint8_t led = RGB_BUILTIN;
uint8_t button = BOOT_PIN;
/* Zigbee OTA configuration */
#define OTA_UPGRADE_RUNNING_FILE_VERSION 0x01010100 // Increment this value when the running image is updated
#define OTA_UPGRADE_DOWNLOADED_FILE_VERSION 0x01010101 // Increment this value when the downloaded image is updated
#define OTA_UPGRADE_HW_VERSION 0x0101 // The hardware version, this can be used to differentiate between different hardware versions
ZigbeeLight zbLight = ZigbeeLight(ZIGBEE_LIGHT_ENDPOINT);
volatile bool otaRunning = false;
/********************* Callbacks *************************/
void otaActiveCallback(bool otaActive) {
otaRunning = otaActive;
if (otaActive) {
Serial.println("OTA started");
} else {
Serial.println("OTA finished");
}
}
/********************* RGB LED functions **************************/
void setLED(bool value) {
digitalWrite(led, value);
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
// Optional: set Zigbee device name and model
zbLight.setManufacturerAndModel("Espressif", "ZBLightBulb");
// Set callback function for light change
zbLight.onLightChange(setLED);
// Add OTA client to the light bulb
zbLight.addOTAClient(OTA_UPGRADE_RUNNING_FILE_VERSION, OTA_UPGRADE_DOWNLOADED_FILE_VERSION, OTA_UPGRADE_HW_VERSION);
// Optional: Register callback for OTA state change
zbLight.onOTAStateChange(otaActiveCallback);
// Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeLight endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbLight);
// When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Start Zigbee OTA client query, first request is within a minute and the next requests are sent every hour automatically
zbLight.requestOTAUpdate();
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
if (otaRunning) {
Serial.println("OTA in progress, cannot reset now");
break;
}
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Toggle light by pressing the button
zbLight.setLight(!zbLight.getLightState());
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,57 @@
# Arduino-ESP32 Zigbee Occupancy Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) occupancy sensor (PIR).
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
Set the Sensor GPIO by changing the `sensor_pin` variable.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,103 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee occupancy sensor.
*
* The example demonstrates how to use Zigbee library to create a end device occupancy sensor.
* The occupancy sensor is a Zigbee end device, which is reporting data to the Zigbee network.
* Tested with PIR sensor HC-SR501 connected to GPIO4.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee occupancy sensor configuration */
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 10
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 4;
ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
void setup() {
Serial.begin(115200);
// Init button + PIR sensor
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT);
// Optional: set Zigbee device name and model
zbOccupancySensor.setManufacturerAndModel("Espressif", "ZigbeeOccupancyPIRSensor");
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbOccupancySensor);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking PIR sensor for occupancy change
static bool occupancy = false;
if (digitalRead(sensor_pin) == HIGH && !occupancy) {
// Update occupancy sensor value
zbOccupancySensor.setOccupancy(true);
zbOccupancySensor.report();
occupancy = true;
} else if (digitalRead(sensor_pin) == LOW && occupancy) {
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
occupancy = false;
}
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,70 @@
# Arduino-ESP32 Zigbee On/Off Light Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) on/off light.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee coordinator (loaded with Zigbee_On_Off_switch example)
* A USB cable for power supply and programming
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the Zigbee_On_Off_Light example
### Configure the Project
Set the LED GPIO by changing the `LED_PIN` definition. By default, the LED_PIN is `RGB_BUILTIN`.
By default, the `rgbLedWrite` function is used to control the LED. You can change it to digitalWrite to control a simple LED.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,101 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates simple Zigbee light bulb.
*
* The example demonstrates how to use Zigbee library to create a end device light bulb.
* The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee light bulb configuration */
#define ZIGBEE_LIGHT_ENDPOINT 10
uint8_t led = RGB_BUILTIN;
uint8_t button = BOOT_PIN;
ZigbeeLight zbLight = ZigbeeLight(ZIGBEE_LIGHT_ENDPOINT);
/********************* RGB LED functions **************************/
void setLED(bool value) {
digitalWrite(led, value);
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
//Optional: set Zigbee device name and model
zbLight.setManufacturerAndModel("Espressif", "ZBLightBulb");
// Set callback function for light change
zbLight.onLightChange(setLED);
//Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeLight endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbLight);
// When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Toggle light by pressing the button
zbLight.setLight(!zbLight.getLightState());
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,110 @@
# Arduino-ESP32 Zigbee Multi-Switch Example
This example demonstrates how to configure a Zigbee device as a multi-switch controller that can control up to three different Zigbee lights independently. The switch can operate in either coordinator or router mode, making it compatible with Home Assistant integration.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee multi-switch controller
* One or more Zigbee light devices (loaded with Zigbee_On_Off_Light example)
* A USB cable for power supply and programming
### Configure the Project
The example uses the BOOT button (pin 9) on ESP32-C6 and ESP32-H2 as the physical switch input. The switch can be configured to operate in two modes:
1. **Coordinator Mode**: For running your own Zigbee network
2. **Router Mode**: For Home Assistant integration
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`
* Select the Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`
## Features
The multi-switch example provides the following functionality:
1. **Light Configuration**
- Configure up to 3 different lights using their endpoint and IEEE address
- Configuration is stored in NVS (Non-Volatile Storage) and persists after power loss
- Remove configured lights when needed
2. **Control Commands**
- Control all bound lights simultaneously:
- Turn all bound lights ON
- Turn all bound lights OFF
- Toggle all bound lights
- Control individual lights (1-3):
- Turn light ON
- Turn light OFF
- Toggle light
3. **Network Management**
- Factory reset capability
- Open network for device joining
- View bound devices and current light configurations
## Serial Commands
The example accepts the following commands through the serial interface:
* `config` - Configure a new light (requires light number, endpoint, and IEEE address)
* `remove` - Remove a configured light
* `on` - Turn all bound lights ON
* `off` - Turn all bound lights OFF
* `toggle` - Toggle all bound lights
* `1on`, `2on`, `3on` - Turn individual light ON
* `1off`, `2off`, `3off` - Turn individual light OFF
* `1toggle`, `2toggle`, `3toggle` - Toggle individual light
* `freset` - Perform factory reset
* `open_network` - Open network for device joining (only for coordinator role)
## Troubleshooting
If the End device flashed with the example `Zigbee_On_Off_Light` is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`
* In the `Zigbee_On_Off_Light` example sketch call `Zigbee.factoryReset()`
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time)` before calling `Zigbee.begin()`
* In application you can anytime call `Zigbee.openNetwork(time)` to open the network for devices to join
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,274 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates simple Zigbee multi-light switch.
*
* The example demonstrates how to use Zigbee library to control multiple light bulbs.
* The light bulbs are Zigbee devices, which are controlled by a Zigbee coordinator/router (Multi-Switch).
* Settings are stored in NVS to not be lost after power loss.
* Configuring and controlling the lights is done via serial input.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#include <Preferences.h>
#define ZIGBEE_ROLE ZIGBEE_ROUTER // ZIGBEE_ROUTER for HomeAssistant integration, ZIGBEE_COORDINATOR for running own network
/* Zigbee switch configuration */
#define SWITCH_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
ZigbeeSwitch zbSwitch = ZigbeeSwitch(SWITCH_ENDPOINT_NUMBER);
int buttonState;
int lastButtonState = LOW;
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
// To be stored in NVS to not be lost after power loss
Preferences prefs;
zb_device_params_t light_1;
zb_device_params_t light_2;
zb_device_params_t light_3;
void storeLightParams(zb_device_params_t *light, int light_number) {
char key[10];
snprintf(key, sizeof(key), "light_%d", light_number);
prefs.putBytes(key, light, sizeof(zb_device_params_t));
}
void loadLightParams(zb_device_params_t *light, int light_number) {
char key[10];
snprintf(key, sizeof(key), "light_%d", light_number);
prefs.getBytes(key, light, sizeof(zb_device_params_t));
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Initialize Preferences
prefs.begin("lights", false); // false means read/write mode
// Load saved light parameters
loadLightParams(&light_1, 1);
loadLightParams(&light_2, 2);
loadLightParams(&light_3, 3);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Set Zigbee device name and model
zbSwitch.setManufacturerAndModel("Espressif", "ZBMultiSwitch");
// Set binding settings depending on the role
if (ZIGBEE_ROLE == ZIGBEE_COORDINATOR) {
zbSwitch.allowMultipleBinding(true); // To allow binding multiple lights to the switch
} else {
zbSwitch.setManualBinding(true); //Set manual binding to true, so binding is done on Home Assistant side
}
// Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeSwitch endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbSwitch);
// When all EPs are registered, start Zigbee with given role
if (!Zigbee.begin(ZIGBEE_ROLE)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Handle button switch in loop()
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
while (digitalRead(button) == LOW) {
delay(50);
}
// Print bound devices
Serial.println("Bound devices:");
zbSwitch.printBoundDevices(Serial);
Serial.println("Lights configured:");
Serial.printf("Light 1: %d %s\n", light_1.endpoint, Zigbee.formatIEEEAddress(light_1.ieee_addr));
Serial.printf("Light 2: %d %s\n", light_2.endpoint, Zigbee.formatIEEEAddress(light_2.ieee_addr));
Serial.printf("Light 3: %d %s\n", light_3.endpoint, Zigbee.formatIEEEAddress(light_3.ieee_addr));
}
// Handle serial input to configure and control the lights
if (Serial.available()) {
String command = Serial.readString();
Serial.println("Command: " + command);
if (command == "config") {
//wait for light number, endpoint and ieee address
Serial.println("Enter light number (1-3):");
while (!Serial.available()) {
delay(100);
}
int light_number = Serial.parseInt();
Serial.println("Enter endpoint:");
while (!Serial.available()) {
delay(100);
}
int endpoint = Serial.parseInt();
Serial.println("Enter ieee address:");
while (!Serial.available()) {
delay(100);
}
String ieee_address = Serial.readStringUntil('\n');
ieee_address.trim();
//convert ieee address to uint8_t array (format in string is 00:00:00:00:00:00:00:00)
uint8_t ieee_address_array[8];
int index = 0;
bool valid = true;
// Check if the string has the correct format (8 hex pairs with colons)
if (ieee_address.length() != 23) { // 8 pairs * 2 + 7 colons
Serial.println("Invalid IEEE address format. Expected format: 00:00:00:00:00:00:00:00");
valid = false;
} else {
for (int i = 0; i < ieee_address.length() && index < 8 && valid; i += 3) {
// Check for colon at expected positions
if (i > 0 && ieee_address.charAt(i - 1) != ':') {
valid = false;
break;
}
// Convert two hex characters to a byte
char hex[3] = {ieee_address.charAt(i), ieee_address.charAt(i + 1), '\0'};
char *endptr;
long value = strtol(hex, &endptr, 16);
if (*endptr != '\0' || value < 0 || value > 255) {
valid = false;
break;
}
// Store bytes in reverse order to match Zigbee standard
ieee_address_array[7 - index++] = (uint8_t)value;
}
}
if (!valid || index != 8) {
Serial.println("Invalid IEEE address. Please enter a valid address in format: 00:00:00:00:00:00:00:00");
return;
}
//set the light parameters
if (light_number == 1) {
light_1.endpoint = endpoint;
memcpy(light_1.ieee_addr, ieee_address_array, 8);
storeLightParams(&light_1, 1);
} else if (light_number == 2) {
light_2.endpoint = endpoint;
memcpy(light_2.ieee_addr, ieee_address_array, 8);
storeLightParams(&light_2, 2);
} else if (light_number == 3) {
light_3.endpoint = endpoint;
memcpy(light_3.ieee_addr, ieee_address_array, 8);
storeLightParams(&light_3, 3);
}
Serial.printf("Light %d configured\n", light_number);
} else if (command == "remove") {
//wait for light number
Serial.println("Enter light number (1-3):");
while (!Serial.available()) {
delay(100);
}
int light_number = Serial.parseInt();
uint8_t ieee_address_empty[8] = {0, 0, 0, 0, 0, 0, 0, 0};
if (light_number == 1) {
light_1.endpoint = 0;
memcpy(light_1.ieee_addr, ieee_address_empty, 8);
storeLightParams(&light_1, 1);
} else if (light_number == 2) {
light_2.endpoint = 0;
memcpy(light_2.ieee_addr, ieee_address_empty, 8);
storeLightParams(&light_2, 2);
} else if (light_number == 3) {
light_3.endpoint = 0;
memcpy(light_3.ieee_addr, ieee_address_empty, 8);
storeLightParams(&light_3, 3);
}
Serial.printf("Light %d removed\n", light_number);
} else if (command == "on") {
Serial.println(" --> SIG Input : All Lights ON");
zbSwitch.lightOn();
} else if (command == "off") {
Serial.println(" --> SIG Input : All Lights OFF");
zbSwitch.lightOff();
} else if (command == "toggle") {
Serial.println(" --> SIG Input : All Lights Toggle");
zbSwitch.lightToggle();
} else if (command == "1on") {
Serial.println(" --> SIG Input : Light 1 ON");
zbSwitch.lightOn(light_1.endpoint, light_1.ieee_addr);
} else if (command == "1off") {
Serial.println(" --> SIG Input : Light 1 OFF");
zbSwitch.lightOff(light_1.endpoint, light_1.ieee_addr);
} else if (command == "1toggle") {
Serial.println(" --> SIG Input : Light 1 Toggle");
zbSwitch.lightToggle(light_1.endpoint, light_1.ieee_addr);
} else if (command == "2on") {
Serial.println(" --> SIG Input : Light 2 ON");
zbSwitch.lightOn(light_2.endpoint, light_2.ieee_addr);
} else if (command == "2off") {
Serial.println(" --> SIG Input : Light 2 OFF");
zbSwitch.lightOff(light_2.endpoint, light_2.ieee_addr);
} else if (command == "2toggle") {
Serial.println(" --> SIG Input : Light 2 Toggle");
zbSwitch.lightToggle(light_2.endpoint, light_2.ieee_addr);
} else if (command == "3on") {
Serial.println(" --> SIG Input : Light 3 ON");
zbSwitch.lightOn(light_3.endpoint, light_3.ieee_addr);
} else if (command == "3off") {
Serial.println(" --> SIG Input : Light 3 OFF");
zbSwitch.lightOff(light_3.endpoint, light_3.ieee_addr);
} else if (command == "3toggle") {
Serial.println(" --> SIG Input : Light 3 Toggle");
zbSwitch.lightToggle(light_3.endpoint, light_3.ieee_addr);
} else if (command == "freset") {
Serial.println(" --> SIG Input : Factory Reset!");
delay(1500);
Zigbee.factoryReset();
} else if (command == "open_network") {
Serial.println(" --> SIG Input : Open Network");
if (ZIGBEE_ROLE == ZIGBEE_COORDINATOR) {
Zigbee.openNetwork(180);
} else {
Serial.println("Open network is only available for coordinator role");
}
} else {
Serial.println("Unknown command");
}
}
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,68 @@
# Arduino-ESP32 Zigbee On/Off Light Switch Example
This example shows how to configure Zigbee Coordinator and use it as a Home Automation (HA) on/off light switch.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee end device (loaded with Zigbee_On_Off_Light example).
* A USB cable for power supply and programming.
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee coordinator and upload the Zigbee_On_Off_Switch example.
### Configure the Project
Set the Button Switch GPIO by changing the `GPIO_INPUT_IO_TOGGLE_SWITCH` definition. By default, it's the pin `9` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the Coordinator Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`.
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`.
* Select the COM port: `Tools -> Port: xxx where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with the example `Zigbee_On_Off_Light` is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* In the `Zigbee_On_Off_Light` example sketch call `Zigbee.factoryReset();`.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,224 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates simple Zigbee light switch.
*
* The example demonstrates how to use Zigbee library to control a light bulb.
* The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator (Switch).
* Button switch and Zigbee runs in separate tasks.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee switch configuration */
#define SWITCH_ENDPOINT_NUMBER 5
#define GPIO_INPUT_IO_TOGGLE_SWITCH BOOT_PIN
#define PAIR_SIZE(TYPE_STR_PAIR) (sizeof(TYPE_STR_PAIR) / sizeof(TYPE_STR_PAIR[0]))
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,
} SwitchFunction;
typedef struct {
uint8_t pin;
SwitchFunction func;
} SwitchData;
typedef enum {
SWITCH_IDLE,
SWITCH_PRESS_ARMED,
SWITCH_PRESS_DETECTED,
SWITCH_PRESSED,
SWITCH_RELEASE_DETECTED,
} SwitchState;
static SwitchData buttonFunctionPair[] = {{GPIO_INPUT_IO_TOGGLE_SWITCH, SWITCH_ONOFF_TOGGLE_CONTROL}};
ZigbeeSwitch zbSwitch = ZigbeeSwitch(SWITCH_ENDPOINT_NUMBER);
static bool light_state = false;
/********************* Zigbee functions **************************/
static void onZbButton(SwitchData *button_func_pair) {
if (button_func_pair->func == SWITCH_ONOFF_TOGGLE_CONTROL) {
// Send toggle command to the light
Serial.println("Toggling light");
zbSwitch.lightToggle();
}
}
static void onLightStateChange(bool state) {
if (state != light_state) {
light_state = state;
Serial.printf("Light state changed to %d\r\n", state);
}
}
/********************* Periodic task ***************************/
void periodicTask(void *arg) {
while (true) {
// print the bound lights every 10 seconds
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10000) {
lastPrint = millis();
zbSwitch.printBoundDevices(Serial);
}
// Poll light state every second
static uint32_t lastPoll = 0;
if (millis() - lastPoll > 1000) {
lastPoll = millis();
zbSwitch.getLightState();
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
/********************* GPIO functions **************************/
static QueueHandle_t gpio_evt_queue = NULL;
static void IRAM_ATTR onGpioInterrupt(void *arg) {
xQueueSendFromISR(gpio_evt_queue, (SwitchData *)arg, NULL);
}
static void enableGpioInterrupt(bool enabled) {
for (int i = 0; i < PAIR_SIZE(buttonFunctionPair); ++i) {
if (enabled) {
enableInterrupt((buttonFunctionPair[i]).pin);
} else {
disableInterrupt((buttonFunctionPair[i]).pin);
}
}
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
//Optional: set Zigbee device name and model
zbSwitch.setManufacturerAndModel("Espressif", "ZigbeeSwitch");
//Optional to allow multiple light to bind to the switch
zbSwitch.allowMultipleBinding(true);
zbSwitch.onLightStateChange(onLightStateChange);
//Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeSwitch endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbSwitch);
//Open network for 180 seconds after boot
Zigbee.setRebootOpenNetwork(180);
// Init button switch
for (int i = 0; i < PAIR_SIZE(buttonFunctionPair); i++) {
pinMode(buttonFunctionPair[i].pin, INPUT_PULLUP);
/* create a queue to handle gpio event from isr */
gpio_evt_queue = xQueueCreate(10, sizeof(SwitchData));
if (gpio_evt_queue == 0) {
Serial.println("Queue creating failed, rebooting...");
ESP.restart();
}
attachInterruptArg(buttonFunctionPair[i].pin, onGpioInterrupt, (void *)(buttonFunctionPair + i), FALLING);
}
// When all EPs are registered, start Zigbee with ZIGBEE_COORDINATOR mode
if (!Zigbee.begin(ZIGBEE_COORDINATOR)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Waiting for Light to bound to the switch");
//Wait for switch to bound to a light:
while (!zbSwitch.bound()) {
Serial.printf(".");
delay(500);
}
// Optional: List all bound devices and read manufacturer and model name
std::list<zb_device_params_t *> boundLights = zbSwitch.getBoundDevices();
for (const auto &device : boundLights) {
Serial.printf("Device on endpoint %d, short address: 0x%x\r\n", device->endpoint, device->short_addr);
Serial.printf(
"IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4],
device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0]
);
char *manufacturer = zbSwitch.readManufacturer(device->endpoint, device->short_addr, device->ieee_addr);
char *model = zbSwitch.readModel(device->endpoint, device->short_addr, device->ieee_addr);
if (manufacturer != nullptr) {
Serial.printf("Light manufacturer: %s\r\n", manufacturer);
}
if (model != nullptr) {
Serial.printf("Light model: %s\r\n", model);
}
}
Serial.println();
xTaskCreate(periodicTask, "periodicTask", 1024 * 4, NULL, 10, NULL);
}
void loop() {
// Handle button switch in loop()
uint8_t pin = 0;
SwitchData buttonSwitch;
static SwitchState buttonState = SWITCH_IDLE;
bool eventFlag = false;
/* check if there is any queue received, if yes read out the buttonSwitch */
if (xQueueReceive(gpio_evt_queue, &buttonSwitch, portMAX_DELAY)) {
pin = buttonSwitch.pin;
enableGpioInterrupt(false);
eventFlag = true;
}
while (eventFlag) {
bool value = digitalRead(pin);
switch (buttonState) {
case SWITCH_IDLE: buttonState = (value == LOW) ? SWITCH_PRESS_DETECTED : SWITCH_IDLE; break;
case SWITCH_PRESS_DETECTED: buttonState = (value == LOW) ? SWITCH_PRESS_DETECTED : SWITCH_RELEASE_DETECTED; break;
case SWITCH_RELEASE_DETECTED:
buttonState = SWITCH_IDLE;
/* callback to button_handler */
(*onZbButton)(&buttonSwitch);
break;
default: break;
}
if (buttonState == SWITCH_IDLE) {
enableGpioInterrupt(true);
eventFlag = false;
break;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,72 @@
# Arduino-ESP32 PM2.5 Sensor
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) simple sensor device type with particulate matter (PM2.5) measuring
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Pressure + Flow Sensor Functions
* After this board first starts up, it would be configured locally to report the PM2.5 on every 30 seconds.
* By clicking the button (BOOT) on this board, this board will immediately send a report of the current PM2.5 to the network.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
In this example, the internal temperature sensor is used to demonstrate reading of the PM2.5 sensors.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,109 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee PM2.5 sensor.
*
* The example demonstrates how to use Zigbee library to create a end device PM2.5 sensor.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee PM2.5 sensor configuration */
#define PM2_5_SENSOR_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
ZigbeePM25Sensor zbPM25Sensor = ZigbeePM25Sensor(PM2_5_SENSOR_ENDPOINT_NUMBER);
void setup() {
Serial.begin(115200);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Optional: set Zigbee device name and model
zbPM25Sensor.setManufacturerAndModel("Espressif", "ZigbeePM25Sensor");
// Set minimum and maximum PM2.5 measurement value in µg/m³
zbPM25Sensor.setMinMaxValue(0, 350);
// Set tolerance for PM2.5 measurement in µg/m³
zbPM25Sensor.setTolerance(0.1);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbPM25Sensor);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Set reporting interval for PM2.5 measurement to be done every 30 seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (PM2.5 change in µg/m³)
// if min = 1 and max = 0, reporting is sent only when PM2.5 changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or when PM2.5 changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of delta change
zbPM25Sensor.setReporting(0, 30, 0);
}
void loop() {
static uint32_t timeCounter = 0;
// Read PM2.5 sensor every 2s
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
// Read sensor value - here is chip temperature used + 50 as a dummy value for demonstration
float pm25_value = 50.5 + temperatureRead();
Serial.printf("Updating PM2.5 sensor value to %0.1f µg/m³\r\n", pm25_value);
zbPM25Sensor.setPM25(pm25_value);
}
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
zbPM25Sensor.report();
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,61 @@
@ -0,0 +1,68 @@
# Arduino-ESP32 Zigbee On/Off Power Outlet Example
This example shows how to configure Zigbee Router device and use it as a Home Automation (HA) on/off power outlet.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming.
### Configure the Project
Set the Button GPIO by changing the `button` definition. By default, it's the pin `9` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the Coordinator Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`.
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee ZCZR 4MB with spiffs`.
* Select the COM port: `Tools -> Port: xxx where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the device flashed with the example `Zigbee_Power_Outlet` is not connecting to the coordinator, erase the flash of the device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator or do some big changes in the application code.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* In the `Zigbee_Power_Outlet` example sketch call `Zigbee.factoryReset();`.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,107 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates simple Zigbee power outlet.
*
* The example demonstrates how to use Zigbee library to create a end device power outlet.
* The power outlet is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Ludovic Boué (https://github.com/lboue)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee router mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee power outlet configuration */
#define ZIGBEE_OUTLET_ENDPOINT 1
#ifdef LED_BUILTIN // Use built-in LED if defined for the board
uint8_t led = LED_BUILTIN;
#else
uint8_t led = 2; // Use custom LED pin
#endif
uint8_t button = BOOT_PIN;
ZigbeePowerOutlet zbOutlet = ZigbeePowerOutlet(ZIGBEE_OUTLET_ENDPOINT);
/********************* RGB LED functions **************************/
void setLED(bool value) {
digitalWrite(led, value);
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
//Optional: set Zigbee device name and model
zbOutlet.setManufacturerAndModel("Espressif", "ZBPowerOutlet");
// Set callback function for power outlet change
zbOutlet.onPowerOutletChange(setLED);
//Add endpoint to Zigbee Core
Serial.println("Adding ZigbeePowerOutlet endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbOutlet);
// When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Toggle state by pressing the button
zbOutlet.setState(!zbOutlet.getPowerOutletState());
}
delay(100);
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,72 @@
# Arduino-ESP32 Zigbee Pressure + Flow Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) simple sensor device type with pressure and flow measuring.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Pressure + Flow Sensor Functions
* After this board first starts up, it would be configured locally to report the pressure and flow on change or every 30 seconds.
* By clicking the button (BOOT) on this board, this board will immediately send a report of the current measured flow and pressure to the network.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
In this example, the internal temperature sensor is used to demonstrate reading of the flow and pressure sensors.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,128 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee temperature sensor.
*
* The example demonstrates how to use Zigbee library to create a end device temperature sensor.
* The temperature sensor is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee flow + pressure sensor configuration */
#define FLOW_SENSOR_ENDPOINT_NUMBER 10
#define PRESSURE_SENSOR_ENDPOINT_NUMBER 11
uint8_t button = BOOT_PIN;
ZigbeeFlowSensor zbFlowSensor = ZigbeeFlowSensor(FLOW_SENSOR_ENDPOINT_NUMBER);
ZigbeePressureSensor zbPressureSensor = ZigbeePressureSensor(PRESSURE_SENSOR_ENDPOINT_NUMBER);
void setup() {
Serial.begin(115200);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Optional: set Zigbee device name and model
zbFlowSensor.setManufacturerAndModel("Espressif", "ZigbeeFlowSensor");
// Set minimum and maximum flow measurement value in 0,1 m3/h
zbFlowSensor.setMinMaxValue(0.0, 100.0);
// Optional: Set tolerance for flow measurement in 0,1 m3/h
zbFlowSensor.setTolerance(1.0);
// Optional: set Zigbee device name and model
zbPressureSensor.setManufacturerAndModel("Espressif", "ZigbeePressureSensor");
// Set minimum and maximum pressure measurement value in hPa
zbPressureSensor.setMinMaxValue(0, 10000);
// Optional: Set tolerance for pressure measurement in hPa
zbPressureSensor.setTolerance(1);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbFlowSensor);
Zigbee.addEndpoint(&zbPressureSensor);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Set reporting interval for flow and pressure measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (pressure change in hPa, flow change in 0,1 m3/h)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of delta change
zbFlowSensor.setReporting(0, 30, 1.0);
zbPressureSensor.setReporting(0, 30, 1);
}
void loop() {
static uint32_t timeCounter = 0;
// Read flow and pressure sensors every 2s
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
float flow_value = temperatureRead();
uint16_t pressure_value = (uint16_t)temperatureRead() * 100; //*100 for demonstration so the value is in 1000-3000hPa
Serial.printf("Updating flow sensor value to %.2f m3/h\r\n", flow_value);
zbFlowSensor.setFlow(flow_value);
Serial.printf("Updating pressure sensor value to %d hPa\r\n", pressure_value);
zbPressureSensor.setPressure(pressure_value);
}
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
zbFlowSensor.report();
zbPressureSensor.report();
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,68 @@
# Arduino-ESP32 Zigbee Range Extender (Router) Example
This example shows how to configure the Zigbee Router device and use it as a Home Automation (HA) network range extender.
To see if the communication with your Zigbee network works, use the Serial monitor and watch for output there.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
* Board (ESP32-H2 or ESP32-C6) as Zigbee router device and upload the Zigbee_Range_Extender example
* Zigbee network / coordinator (Other board with switch examples or Zigbee2mqtt or ZigbeeHomeAssistant like application)
### Configure the Project
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the Coordinator/Router device Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs` (select correct size)
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the Router device flashed with this example is not connecting to the coordinator, erase the flash of the Router device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,120 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates simple Zigbee Range Extender (router).
*
* The example demonstrates how to use Zigbee library to create a Zigbee network ragbe extender (router).
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator/router mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee light bulb configuration */
#define USE_CUSTOM_ZIGBEE_CONFIG 1
#define ZIGBEE_EXTENDER_ENDPOINT 1
#ifndef LED_BUILTIN
#define LED_BUILTIN 4
#endif
uint8_t led = LED_BUILTIN;
uint8_t button = BOOT_PIN;
ZigbeeRangeExtender zbExtender = ZigbeeRangeExtender(ZIGBEE_EXTENDER_ENDPOINT);
/************************** Identify ******************************/
// Create a task on identify call to handle the identify function
void identify(uint16_t time) {
static uint8_t blink = 1;
log_d("Identify called for %d seconds", time);
if (time == 0) {
digitalWrite(led, LOW);
return;
}
digitalWrite(led, blink);
blink = !blink;
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
// Optional: Set callback function for device identify
zbExtender.onIdentify(identify);
// Optional: Set Zigbee device name and model
zbExtender.setManufacturerAndModel("Espressif", "ZigbeeRangeExtender");
// Add endpoint to Zigbee Core
Serial.println("Adding Zigbee Extender endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbExtender);
#if USE_CUSTOM_ZIGBEE_CONFIG
// Optional: Create a custom Zigbee configuration for Zigbee Extender
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ROUTER_CONFIG();
zigbeeConfig.nwk_cfg.zczr_cfg.max_children = 20; // 10 is default
// When all EPs are registered, start Zigbee with custom config
if (!Zigbee.begin(&zigbeeConfig)) {
#else
// When all EPs are registered, start Zigbee as ROUTER device
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
#endif
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,71 @@
# Arduino-ESP32 Zigbee Networks Scan Example
This example shows how to scan Zigbee Networks.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Example Output
Setup done
Loop running...
Loop running...
Loop running...
Loop running...
Scan done
2 networks found:
Nr | PAN ID | CH | Permit Joining | Router Capacity | End Device Capacity | Extended PAN ID
1 | 0xe6f0 | 14 | Yes | Yes | Yes | f0:f5:bd:ff:fe:02:3f:24
2 | 0xa9bb | 24 | No | Yes | Yes | 60:55:f9:00:00:f7:52:d0
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee coordinator (loaded with `Zigbee_Thermostat` example)
* A USB cable for power supply and programming
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee end device (loaded with `Zigbee_Temperature_Sensor` example)
### Configure the Project
In this example, the internal temperature sensor task is reading the chip temperature.
Set the Button Switch GPIO by changing the `GPIO_INPUT_IO_TOGGLE_SWITCH` definition. By default, it's the `GPIO_NUM_9` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,109 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee Network Scanning.
*
* The example demonstrates how to use ESP Zigbee stack to scan for Zigbee networks.
*
* Any Zigbee mode can be selected in Tools->Zigbee mode
* with proper Zigbee partition scheme in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#if !defined(ZIGBEE_MODE_ED) && !defined(ZIGBEE_MODE_ZCZR)
#error "Zigbee device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#ifdef ZIGBEE_MODE_ZCZR
zigbee_role_t role = ZIGBEE_ROUTER; // or can be ZIGBEE_COORDINATOR, but it won't scan itself
#else
zigbee_role_t role = ZIGBEE_END_DEVICE;
#endif
void printScannedNetworks(uint16_t networksFound) {
if (networksFound == 0) {
Serial.println("No networks found");
} else {
zigbee_scan_result_t *scan_result = Zigbee.getScanResult();
Serial.println("\nScan done");
Serial.print(networksFound);
Serial.println(" networks found:");
Serial.println("Nr | PAN ID | CH | Permit Joining | Router Capacity | End Device Capacity | Extended PAN ID");
for (int i = 0; i < networksFound; ++i) {
// Print all available info for each network found
Serial.printf("%2d", i + 1);
Serial.print(" | ");
Serial.printf("0x%04hx", scan_result[i].short_pan_id);
Serial.print(" | ");
Serial.printf("%2d", scan_result[i].logic_channel);
Serial.print(" | ");
Serial.printf("%-14.14s", scan_result[i].permit_joining ? "Yes" : "No");
Serial.print(" | ");
Serial.printf("%-15.15s", scan_result[i].router_capacity ? "Yes" : "No");
Serial.print(" | ");
Serial.printf("%-19.19s", scan_result[i].end_device_capacity ? "Yes" : "No");
Serial.print(" | ");
Serial.printf(
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", scan_result[i].extended_pan_id[7], scan_result[i].extended_pan_id[6], scan_result[i].extended_pan_id[5],
scan_result[i].extended_pan_id[4], scan_result[i].extended_pan_id[3], scan_result[i].extended_pan_id[2], scan_result[i].extended_pan_id[1],
scan_result[i].extended_pan_id[0]
);
Serial.println();
delay(10);
}
Serial.println("");
// Delete the scan result to free memory for code below.
Zigbee.scanDelete();
}
}
void setup() {
Serial.begin(115200);
// Initialize Zigbee stack without any EPs just for scanning
if (!Zigbee.begin(role)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Setup done, starting Zigbee network scan...");
// Start Zigbee Network Scan with default parameters (all channels, scan time 5)
Zigbee.scanNetworks();
}
void loop() {
// check Zigbee Network Scan process
int16_t ZigbeeScanStatus = Zigbee.scanComplete();
if (ZigbeeScanStatus < 0) { // it is busy scanning or got an error
if (ZigbeeScanStatus == ZB_SCAN_FAILED) {
Serial.println("Zigbee scan has failed. Starting again.");
Zigbee.scanNetworks();
}
// other option is status ZB_SCAN_RUNNING - just wait.
} else { // Found Zero or more Wireless Networks
printScannedNetworks(ZigbeeScanStatus);
Zigbee.scanNetworks(); // start over...
}
// Loop can do something else...
delay(500);
Serial.println("Loop running...");
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,75 @@
# Arduino-ESP32 Zigbee Temperature and Humidity Sensor Sleepy Device Example
This example demonstrates how to use the Zigbee library to create an end device temperature/humidity sensor and use it as a Home Automation (HA) extended temperature sensor.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Temperature Sensor Functions
1. Initialize a Zigbee temperature and humidity sensor.
2. Measure temperature and humidity values.
3. Report the measured values to the Zigbee network.
4. Put the device to sleep to save power.
## Hardware Required
* ESP32-H2 or ESP32-C6 development board
* A USB cable for power supply and programming
### Configure the Project
In this example, to demonstrate the functionality the chip temperature is used and reported as temperature and humidity.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,211 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee temperature and humidity sensor Sleepy device.
*
* The example demonstrates how to use Zigbee library to create an end device temperature and humidity sensor.
* The sensor is a Zigbee end device, which is reporting data to the Zigbee network.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#define USE_GLOBAL_ON_RESPONSE_CALLBACK 1 // Set to 0 to use local callback specified directly for the endpoint.
/* Zigbee temperature + humidity sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 55 /* Sleep for 55s will + 5s delay for establishing connection => data reported every 1 minute */
#define REPORT_TIMEOUT 1000 /* Timeout for response from coordinator in ms */
uint8_t button = BOOT_PIN;
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
uint8_t dataToSend = 2; // Temperature and humidity values are reported in same endpoint, so 2 values are reported
bool resend = false;
/************************ Callbacks *****************************/
#if USE_GLOBAL_ON_RESPONSE_CALLBACK
void onGlobalResponse(zb_cmd_type_t command, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster) {
Serial.printf("Global response command: %d, status: %s, endpoint: %d, cluster: 0x%04x\r\n", command, esp_zb_zcl_status_to_name(status), endpoint, cluster);
if ((command == ZB_CMD_REPORT_ATTRIBUTE) && (endpoint == TEMP_SENSOR_ENDPOINT_NUMBER)) {
switch (status) {
case ESP_ZB_ZCL_STATUS_SUCCESS: dataToSend--; break;
case ESP_ZB_ZCL_STATUS_FAIL: resend = true; break;
default: break; // add more statuses like ESP_ZB_ZCL_STATUS_INVALID_VALUE, ESP_ZB_ZCL_STATUS_TIMEOUT etc.
}
}
}
#else
void onResponse(zb_cmd_type_t command, esp_zb_zcl_status_t status) {
Serial.printf("Response command: %d, status: %s\r\n", command, esp_zb_zcl_status_to_name(status));
if (command == ZB_CMD_REPORT_ATTRIBUTE) {
switch (status) {
case ESP_ZB_ZCL_STATUS_SUCCESS: dataToSend--; break;
case ESP_ZB_ZCL_STATUS_FAIL: resend = true; break;
default: break; // add more statuses like ESP_ZB_ZCL_STATUS_INVALID_VALUE, ESP_ZB_ZCL_STATUS_TIMEOUT etc.
}
}
}
#endif
/************************ Temp sensor *****************************/
static void meausureAndSleep(void *arg) {
// Measure temperature sensor value
float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
float humidity = temperature;
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.report(); // reports temperature and humidity values (if humidity sensor is not added, only temperature is reported)
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
unsigned long startTime = millis();
const unsigned long timeout = REPORT_TIMEOUT;
Serial.printf("Waiting for data report to be confirmed \r\n");
// Wait until data was successfully sent
int tries = 0;
const int maxTries = 3;
while (dataToSend != 0 && tries < maxTries) {
if (resend) {
Serial.println("Resending data on failure!");
resend = false;
dataToSend = 2;
zbTempSensor.report(); // report again
}
if (millis() - startTime >= timeout) {
Serial.println("\nReport timeout! Report Again");
dataToSend = 2;
zbTempSensor.report(); // report again
startTime = millis();
tries++;
}
Serial.printf(".");
delay(50); // 50ms delay to avoid busy-waiting
}
// Put device to deep sleep after data was sent successfully or timeout
Serial.println("Going to sleep now");
esp_deep_sleep_start();
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensor");
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set default (initial) value for the temperature sensor to 10.0°C to match the minimum temperature measurement value (default value is 0.0°C)
zbTempSensor.setDefaultValue(10.0);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max, tolerance and default values
zbTempSensor.addHumiditySensor(0, 100, 1, 0.0);
// Set callback for default response to handle status of reported data, there are 2 options.
#if USE_GLOBAL_ON_RESPONSE_CALLBACK
// Global callback for all endpoints with more params to determine the endpoint and cluster in the callback function.
Zigbee.onGlobalDefaultResponse(onGlobalResponse);
#else
// Callback specified for endpoint
zbTempSensor.onDefaultResponse(onResponse);
#endif
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
// For battery powered devices, it can be better to set timeout for Zigbee Begin to lower value to save battery
// If the timeout has been reached, the network channel mask will be reset and the device will try to connect again after reset (scanning all channels)
Zigbee.setTimeout(10000); // Set timeout for Zigbee Begin to 10s (default is 30s)
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart(); // If Zigbee failed to start, reboot the device and try again
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
Serial.println("Successfully connected to Zigbee network");
// Start Temperature sensor reading task
xTaskCreate(meausureAndSleep, "temp_sensor_update", 2048, NULL, 10, NULL);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 10000) {
// If key pressed for more than 10secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
// Optional set reset in factoryReset to false, to not restart device after erasing nvram, but set it to endless sleep manually instead
Zigbee.factoryReset(false);
Serial.println("Going to endless sleep, press RESET button or power off/on the device to wake up");
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER);
esp_deep_sleep_start();
}
}
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,79 @@
# Arduino-ESP32 Zigbee Temperature Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) temperature sensor.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Temperature Sensor Functions
Note:
* This board means the board (e.g. ESP32-H2 / C6) loaded with `Zigbee_Temperature_Sensor` example.
* The remote board means the board (e.g. ESP32-H2 / C6) loaded with `Zigbee_Thermostat` example.
Functions:
* After this board first starts up, it would be configured locally to report the temperature on 1 degree change and no periodic reporting to the remote board.
* By clicking the button (BOOT) on this board, this board will immediately send a report of the current measured temperature to the remote board.
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee coordinator (loaded with `Zigbee_Thermostat` example)
* A USB cable for power supply and programming
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the `Zigbee_Temperature_Sensor` example
### Configure the Project
In this example, the internal temperature sensor task is reading the chip temperature.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,141 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee temperature sensor.
*
* The example demonstrates how to use Zigbee library to create a end device temperature sensor.
* The temperature sensor is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee temperature sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
uint8_t button = BOOT_PIN;
// Optional Time cluster variables
struct tm timeinfo;
struct tm *localTime;
int32_t timezone;
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
/************************ Temp sensor *****************************/
static void temp_sensor_value_update(void *arg) {
for (;;) {
// Read temperature sensor value
float tsens_value = temperatureRead();
Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// Update temperature value in Temperature sensor EP
zbTempSensor.setTemperature(tsens_value);
delay(1000);
}
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Espressif", "ZigbeeTempSensor");
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Optional: Set default (initial) value for the temperature sensor to 10.0°C to match the minimum temperature measurement value
zbTempSensor.setDefaultValue(10.0);
// Optional: Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Optional: Time cluster configuration (default params, as this device will revieve time from coordinator)
zbTempSensor.addTimeCluster();
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Optional: If time cluster is added, time can be read from the coordinator
timeinfo = zbTempSensor.getTime();
timezone = zbTempSensor.getTimezone();
Serial.println("UTC time:");
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
time_t local = mktime(&timeinfo) + timezone;
localTime = localtime(&local);
Serial.println("Local time with timezone:");
Serial.println(localTime, "%A, %B %d %Y %H:%M:%S");
// Start Temperature sensor reading task
xTaskCreate(temp_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
zbTempSensor.setReporting(1, 0, 1);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
zbTempSensor.reportTemperature();
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,79 @@
# Arduino-ESP32 Zigbee Thermostat Example
This example shows how to configure Zigbee Coordinator and use it as a Home Automation (HA) thermostat.
**This example is based on ESP-Zigbee-SDK example esp_zigbee_HA_sample/HA_thermostat.**
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Thermostat Functions
Note:
* This board means the board (e.g. ESP32-H2) loaded with `Zigbee_Thermostat` example.
* The remote board means the board (e.g. ESP32-H2) loaded with `Zigbee_Temperature_Sensor` example.
Functions:
* By clicking the button (BOOT) on this board, this board will read temperature value, temperature measurement range and temperature tolerance from the remote board. Also, this board will configure the remote board to report the measured temperature value every 10 seconds or every 2 degree changes.
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee end device (loaded with Zigbee_Temperature_Sensor example).
* A USB cable for power supply and programming.
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee coordinator (loaded with Zigbee_Thermostat example).
### Configure the Project
Set the Button GPIO by changing the `BUTTON_PIN` definition. By default, it's the pin `9` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the Coordinator Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`.
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`.
* Select the COM port: `Tools -> Port: xxx where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with the example `Zigbee_Temperature_Sensor` is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* In the `Zigbee_Temperature_Sensor` example sketch call `Zigbee.factoryReset();`.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,168 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates simple Zigbee thermostat.
*
* The example demonstrates how to use Zigbee library to get data from temperature
* sensor end device and act as an thermostat.
* The temperature sensor is a Zigbee end device, which is controlled by a Zigbee coordinator (thermostat).
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee thermostat configuration */
#define THERMOSTAT_ENDPOINT_NUMBER 1
#define USE_RECEIVE_TEMP_WITH_SOURCE 1
uint8_t button = BOOT_PIN;
ZigbeeThermostat zbThermostat = ZigbeeThermostat(THERMOSTAT_ENDPOINT_NUMBER);
// Save temperature sensor data
float sensor_temp;
float sensor_max_temp;
float sensor_min_temp;
float sensor_tolerance;
struct tm timeinfo = {}; // Time structure for Time cluster
/****************** Temperature sensor handling *******************/
#if USE_RECEIVE_TEMP_WITH_SOURCE == 0
void receiveSensorTemp(float temperature) {
Serial.printf("Temperature sensor value: %.2f°C\n", temperature);
sensor_temp = temperature;
}
#else
void receiveSensorTempWithSource(float temperature, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {
if (src_address.addr_type == ESP_ZB_ZCL_ADDR_TYPE_SHORT) {
Serial.printf("Temperature sensor value: %.2f°C from endpoint %d, address 0x%04x\n", temperature, src_endpoint, src_address.u.short_addr);
} else {
Serial.printf(
"Temperature sensor value: %.2f°C from endpoint %d, address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", temperature, src_endpoint,
src_address.u.ieee_addr[7], src_address.u.ieee_addr[6], src_address.u.ieee_addr[5], src_address.u.ieee_addr[4], src_address.u.ieee_addr[3],
src_address.u.ieee_addr[2], src_address.u.ieee_addr[1], src_address.u.ieee_addr[0]
);
}
sensor_temp = temperature;
}
#endif
void receiveSensorConfig(float min_temp, float max_temp, float tolerance) {
Serial.printf("Temperature sensor config: min %.2f°C, max %.2f°C, tolerance %.2f°C\n", min_temp, max_temp, tolerance);
sensor_min_temp = min_temp;
sensor_max_temp = max_temp;
sensor_tolerance = tolerance;
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Set callback function for receiving temperature from sensor - Use only one option
#if USE_RECEIVE_TEMP_WITH_SOURCE == 0
zbThermostat.onTempReceive(receiveSensorTemp); // If you bound only one sensor or you don't need to know the source of the temperature
#else
zbThermostat.onTempReceiveWithSource(receiveSensorTempWithSource);
#endif
// Set callback function for receiving sensor configuration
zbThermostat.onTempConfigReceive(receiveSensorConfig);
//Optional: set Zigbee device name and model
zbThermostat.setManufacturerAndModel("Espressif", "ZigbeeThermostat");
//Optional Time cluster configuration
//example time January 13, 2025 13:30:30 CET
timeinfo.tm_year = 2025 - 1900; // = 2025
timeinfo.tm_mon = 0; // January
timeinfo.tm_mday = 13; // 13th
timeinfo.tm_hour = 12; // 12 hours - 1 hour (CET)
timeinfo.tm_min = 30; // 30 minutes
timeinfo.tm_sec = 30; // 30 seconds
timeinfo.tm_isdst = -1;
// Set time and gmt offset (timezone in seconds -> CET = +3600 seconds)
zbThermostat.addTimeCluster(timeinfo, 3600);
//Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbThermostat);
//Open network for 180 seconds after boot
Zigbee.setRebootOpenNetwork(180);
// When all EPs are registered, start Zigbee with ZIGBEE_COORDINATOR mode
if (!Zigbee.begin(ZIGBEE_COORDINATOR)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Waiting for Temperature sensor to bound to the thermostat");
while (!zbThermostat.bound()) {
Serial.printf(".");
delay(500);
}
Serial.println();
// Get temperature sensor configuration for all bound sensors by endpoint number and address
std::list<zb_device_params_t *> boundSensors = zbThermostat.getBoundDevices();
for (const auto &device : boundSensors) {
Serial.println("--------------------------------");
if (device->short_addr == 0x0000 || device->short_addr == 0xFFFF) { //End devices never have 0x0000 short address or 0xFFFF group address
Serial.printf(
"Device on endpoint %d, IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", device->endpoint, device->ieee_addr[7], device->ieee_addr[6],
device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0]
);
zbThermostat.getTemperatureSettings(device->endpoint, device->ieee_addr);
} else {
Serial.printf("Device on endpoint %d, short address: 0x%x\r\n", device->endpoint, device->short_addr);
zbThermostat.getTemperatureSettings(device->endpoint, device->short_addr);
}
}
}
void loop() {
// Handle button switch in loop()
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
while (digitalRead(button) == LOW) {
delay(50);
}
// Set reporting interval for temperature sensor
zbThermostat.setTemperatureReporting(0, 10, 2);
}
// Print temperature sensor data each 10 seconds
static uint32_t last_print = 0;
if (millis() - last_print > 10000) {
last_print = millis();
int temp_percent = (int)((sensor_temp - sensor_min_temp) / (sensor_max_temp - sensor_min_temp) * 100);
Serial.printf("Loop temperature info: %.2f°C (%d %%)\n", sensor_temp, temp_percent);
zbThermostat.printBoundDevices(Serial);
}
}
@@ -0,0 +1,4 @@
fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr
requires:
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,58 @@
# Arduino-ESP32 Zigbee Vibration Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) vibration sensor (IAS Zone),
that can be used for example as a security device which is sensing a vibrations.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
Set the Sensor GPIO by changing the `sensor_pin` variable.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,142 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee vibration sensor (IAS Zone).
*
* The example demonstrates how to use Zigbee library to create a end device vibration sensor.
* The vibration sensor is a Zigbee end device, which is reporting data to the Zigbee network.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#include <Preferences.h>
/* Zigbee vibration sensor configuration */
#define VIBRATION_SENSOR_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 4;
ZigbeeVibrationSensor zbVibrationSensor = ZigbeeVibrationSensor(VIBRATION_SENSOR_ENDPOINT_NUMBER);
/* Preferences for storing ENROLLED flag to persist across reboots */
Preferences preferences;
void setup() {
Serial.begin(115200);
preferences.begin("Zigbee", false); // Save ENROLLED flag in flash so it persists across reboots
bool enrolled = preferences.getBool("ENROLLED"); // Get ENROLLED flag from preferences
preferences.end();
// Init button + sensor
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT);
// Optional: set Zigbee device name and model
zbVibrationSensor.setManufacturerAndModel("Espressif", "ZigbeeVibrationSensor");
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbVibrationSensor);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Check if device has been enrolled before restarting - if so, restore IAS Zone enroll, otherwise request new IAS Zone enroll
if (enrolled) {
Serial.println("Device has been enrolled before - restoring IAS Zone enrollment");
zbVibrationSensor.restoreIASZoneEnroll();
} else {
Serial.println("Device is factory new - first time joining network - requesting new IAS Zone enrollment");
zbVibrationSensor.requestIASZoneEnroll();
}
while (!zbVibrationSensor.enrolled()) {
Serial.print(".");
delay(100);
}
Serial.println();
Serial.println("Zigbee enrolled successfully!");
// Store ENROLLED flag only if this was a new enrollment (previous flag was false)
// Skip writing if we just restored enrollment (flag was already true)
if (!enrolled) {
preferences.begin("Zigbee", false);
preferences.putBool("ENROLLED", true); // set ENROLLED flag to true
preferences.end();
Serial.println("ENROLLED flag saved to preferences");
}
}
void loop() {
// Checking pin for contact change
static bool sensed = false;
if (digitalRead(sensor_pin) == HIGH && !sensed) {
// Update contact sensor value
zbVibrationSensor.setVibration(true);
sensed = true;
//if sensed, wait 2 seconds before next sensing
delay(2000);
} else if (digitalRead(sensor_pin) == LOW && sensed) {
zbVibrationSensor.setVibration(false);
sensed = false;
//if not sensed, wait 0,5 seconds before next sensing
delay(500);
}
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
// Clear the ENROLLED flag from preferences
preferences.begin("Zigbee", false);
preferences.putBool("ENROLLED", false); // set ENROLLED flag to false
preferences.end();
Serial.println("ENROLLED flag cleared from preferences");
delay(1000);
Zigbee.factoryReset();
}
}
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,60 @@
# Zigbee Wind Speed Sensor Integration with HomeAssistant ZHA
This guide provides a workaround for integrating a Zigbee Wind Speed Sensor with HomeAssistant using the ZHA integration. Since the wind speed cluster is not natively supported, we will use the ZHA Toolkit from HACS to read the wind speed attribute and store it in a helper variable.
## Alternative Option: Creating a Custom Quirk
For advanced users, a more robust solution is to create a custom quirk for your Zigbee Wind Speed Sensor. This approach involves writing a custom device handler that directly supports the wind speed cluster, providing a more seamless integration with HomeAssistant.
Creating a custom quirk can be complex and requires familiarity with Python and the Zigbee protocol. However, it offers greater flexibility and control over your device's behavior.
For more information and guidance on creating custom quirks, visit the [ZHA Device Handlers repository](https://github.com/zigpy/zha-device-handlers/).
## Prerequisites
- HomeAssistant installed and running
- Zigbee Wind Speed Sensor paired with HomeAssistant ZHA
- HACS (Home Assistant Community Store) installed. For more information, visit [HACS](https://hacs.xyz)
## Steps
### 1. Install ZHA Toolkit
1. Open HomeAssistant.
2. Navigate to HACS > Integrations.
3. Search for "ZHA Toolkit - Service for advanced Zigbee Usage" and install it. For more information, visit the [ZHA Toolkit repository](https://github.com/mdeweerd/zha-toolkit).
4. Restart HomeAssistant to apply changes.
### 2. Create a Helper Variable
1. Go to Configuration -> Devices & Services -> Helpers.
2. Click on "Add Helper" and select "Number".
3. Name the helper (e.g., `wind_speed`), set the minimum and maximum values, and save it.
### 3. Create an Automation
1. Go to Configuration > Automations & Scenes.
2. Click on "Add Automation" and choose "Start with an empty automation".
3. Set a name for the automation (e.g., `Read Wind Speed`).
4. Add a trigger:
- Trigger Type: Time Pattern
- Every: 30 seconds
5. Add an action (Then do):
- Action Type: ZHA Toolkit: Read Attribute
- Setup the action:
```yaml
action: zha_toolkit.attr_read
metadata: {}
data:
ieee: f0:f5:bd:ff:fe:0e:61:30 #set device IEEE address
endpoint: 10 #set windspeed device endpoint
cluster: 1035 #use this windspeed cluster
attribute: 0 #read measurement value
state_id: input_number.wind_speed #save to created helper variable
state_value_template: value/100 #use correct value format (convert u16 to float)
```
6. Save the automation.
## Conclusion
By following these steps, you can successfully integrate your Zigbee Wind Speed Sensor with HomeAssistant using the ZHA integration and ZHA Toolkit. The wind speed readings will be updated every 30 seconds and stored in the helper variable for use in your HomeAssistant setup.
The helper variable `wind_speed` is now an entity in HomeAssistant. You can use this entity to display the wind speed on your dashboard or in other automations.
@@ -0,0 +1,119 @@
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee windspeed sensor.
*
* The example demonstrates how to use Zigbee library to create a end device wind speed sensor.
* The wind speed sensor is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#define BUTTON_PIN 9 //Boot button for C6/H2
#define WIND_SPEED_SENSOR_ENDPOINT_NUMBER 10
ZigbeeWindSpeedSensor zbWindSpeedSensor = ZigbeeWindSpeedSensor(WIND_SPEED_SENSOR_ENDPOINT_NUMBER);
/************************ WindSpeed sensor *****************************/
static void windspeed_sensor_value_update(void *arg) {
for (;;) {
// Read wind speed sensor value (simulated now by temperature sensor)
float windspeed = temperatureRead();
log_v("Wind speed sensor value: %.2fm/s", windspeed);
// Update windspeed value in Windspeed sensor EP
zbWindSpeedSensor.setWindSpeed(windspeed);
delay(1000);
}
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(10);
}
// Init button switch
pinMode(BUTTON_PIN, INPUT);
// Optional: set Zigbee device name and model
zbWindSpeedSensor.setManufacturerAndModel("Espressif", "ZigbeeWindSpeedSensor");
// Set minimum and maximum windspeed measurement value in m/s
zbWindSpeedSensor.setMinMaxValue(0, 50);
// Set tolerance for windspeed measurement in m/s (lowest possible value is 0.01 m/s)
zbWindSpeedSensor.setTolerance(1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbWindSpeedSensor);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Start Wind speed sensor reading task
xTaskCreate(windspeed_sensor_value_update, "wind_speed_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for windspeed measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (WindSpeed change in m/s)
// if min = 1 and max = 0, reporting is sent only when windspeed changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or windspeed changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of windspeed change
zbWindSpeedSensor.setReporting(1, 0, 1);
}
void loop() {
// Checking button for factory reset
if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(BUTTON_PIN) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
zbWindSpeedSensor.reportWindSpeed();
}
delay(100);
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
@@ -0,0 +1,69 @@
# Arduino-ESP32 Window Covering Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) window covering device.
To see if the communication with your Zigbee network works, use the Serial monitor and watch for output there.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
* Board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the Zigbee_Window_Covering example
* Zigbee network / coordinator (Other board with switch examples or Zigbee2mqtt or ZigbeeHomeAssistant like application)
### Configure the Project
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Tools / USB CDC On Boot: "Enabled"
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
@@ -0,0 +1,198 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @brief This example demonstrates Zigbee Window Covering.
*
* The example demonstrates how to use Zigbee library to create a end device window covering device.
* The window covering is a Zigbee end device, which is moving the blinds (lift+tilt) and reporting
* its current position to the Zigbee network.
*
* Use setCoveringType() to set the type of covering (blind, shade, etc.).
*
* The example also demonstrates how to use the button to manually control the lift position.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by hennikul and Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "ZigbeeCore.h"
#include "ep/ZigbeeWindowCovering.h"
#define ZIGBEE_COVERING_ENDPOINT 10
#define BUTTON_PIN 9 // ESP32-C6/H2 Boot button
#define MAX_LIFT 200 // centimeters from open position (0-900)
#define MIN_LIFT 0
#define MAX_TILT 40 // centimeters from open position (0-900)
#define MIN_TILT 0
uint16_t currentLift = MAX_LIFT;
uint8_t currentLiftPercentage = 100;
uint16_t currentTilt = MAX_TILT;
uint8_t currentTiltPercentage = 100;
ZigbeeWindowCovering zbCovering = ZigbeeWindowCovering(ZIGBEE_COVERING_ENDPOINT);
void setup() {
Serial.begin(115200);
// Init button for factory reset
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Optional: set Zigbee device name and model
zbCovering.setManufacturerAndModel("Espressif", "WindowBlinds");
// Set proper covering type, it defines which attributes are available
zbCovering.setCoveringType(BLIND_LIFT_AND_TILT);
// Set configuration: operational, online, not commands_reversed, lift / tilt closed_loop, lift / tilt encoder_controlled
zbCovering.setConfigStatus(true, true, false, true, true, true, true);
// Set mode: not motor_reversed, calibration_mode, not maintenance_mode, not leds_on
zbCovering.setMode(false, true, false, false);
// Set limits of motion
zbCovering.setLimits(MIN_LIFT, MAX_LIFT, MIN_TILT, MAX_TILT);
// Set callback function for open, close, filt and tilt change, stop
zbCovering.onOpen(fullOpen);
zbCovering.onClose(fullClose);
zbCovering.onGoToLiftPercentage(goToLiftPercentage);
zbCovering.onGoToTiltPercentage(goToTiltPercentage);
zbCovering.onStop(stopMotor);
// Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeWindowCovering endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbCovering);
// When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE
Serial.println("Calling Zigbee.begin()");
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Set initial position
zbCovering.setLiftPercentage(currentLiftPercentage);
zbCovering.setTiltPercentage(currentTiltPercentage);
}
void loop() {
// Checking button for factory reset
if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(BUTTON_PIN) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.printf("Resetting Zigbee to factory settings, reboot.\n");
Zigbee.factoryReset();
delay(30000);
}
}
// Manual lift control simulation by pressing button
manualControl();
}
delay(500);
}
void fullOpen() {
/* This is where you would trigger your motor to go to full open state, currentLift should
be updated until full open has been reached in order to provide feedback to controller of actual position
The stop can be always called, so the movement can be stopped at any time */
// Our cover updates instantly!
currentLift = MAX_LIFT;
currentLiftPercentage = 100;
Serial.println("Opening cover");
// Update the current position
zbCovering.setLiftPercentage(currentLiftPercentage);
}
void fullClose() {
/* This is where you would trigger your motor to go to full close state, currentLift should
be updated until full close has been reached in order to provide feedback to controller of actual position
The stop can be always called, so the movement can be stopped at any time */
// Our cover updates instantly!
currentLift = MIN_LIFT;
currentLiftPercentage = 0;
Serial.println("Closing cover");
// Update the current position
zbCovering.setLiftPercentage(currentLiftPercentage);
}
void goToLiftPercentage(uint8_t liftPercentage) {
/* This is where you would trigger your motor to go towards liftPercentage, currentLift should
be updated until liftPercentage has been reached in order to provide feedback to controller */
// Our simulated cover updates instantly!
currentLift = (liftPercentage * MAX_LIFT) / 100;
currentLiftPercentage = liftPercentage;
Serial.printf("New requested lift from Zigbee: %d (%d)\n", currentLift, liftPercentage);
// Update the current position
zbCovering.setLiftPercentage(currentLiftPercentage); //or setLiftPosition()
}
void goToTiltPercentage(uint8_t tiltPercentage) {
/* This is where you would trigger your motor to go towards tiltPercentage, currentTilt should
be updated until tiltPercentage has been reached in order to provide feedback to controller */
// Our simulated cover updates instantly!
currentTilt = (tiltPercentage * MAX_TILT) / 100;
currentTiltPercentage = tiltPercentage;
Serial.printf("New requested tilt from Zigbee: %d (%d)\n", currentTilt, tiltPercentage);
// Update the current position
zbCovering.setTiltPercentage(currentTiltPercentage); //or setTiltPosition()
}
void stopMotor() {
// Motor can be stopped while moving cover toward current target, when stopped the actual position should be updated
Serial.println("Stopping motor");
// Update the current position of both lift and tilt
zbCovering.setLiftPercentage(currentLiftPercentage);
zbCovering.setTiltPercentage(currentTiltPercentage);
}
void manualControl() {
// Simulate lift percentage move by increasing it by 20% each time
currentLiftPercentage += 20;
if (currentLiftPercentage > 100) {
currentLiftPercentage = 0;
}
zbCovering.setLiftPercentage(currentLiftPercentage);
// Also setLiftPosition() can be used to set the exact position instead of percentage
}
@@ -0,0 +1,5 @@
fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed
requires:
- CONFIG_SOC_IEEE802154_SUPPORTED=y
- CONFIG_ZB_ENABLED=y
+355
View File
@@ -0,0 +1,355 @@
#######################################
# Syntax Coloring Map For Zigbee
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
# Base Classes
ZigbeeCore KEYWORD1
Zigbee KEYWORD1
ZigbeeEP KEYWORD1
# Endpoint Classes
ZigbeeAnalog KEYWORD1
ZigbeeBinary KEYWORD1
ZigbeeCarbonDioxideSensor KEYWORD1
ZigbeeColorDimmableLight KEYWORD1
ZigbeeColorDimmerSwitch KEYWORD1
ZigbeeContactSwitch KEYWORD1
ZigbeeDimableLight KEYWORD1
ZigbeeDoorWindowHandle KEYWORD1
ZigbeeElectricalMeasurement KEYWORD1
ZigbeeFanControl KEYWORD1
ZigbeeFlowSensor KEYWORD1
ZigbeeGateway KEYWORD1
ZigbeeIlluminanceSensor KEYWORD1
ZigbeeLight KEYWORD1
ZigbeeMultistate KEYWORD1
ZigbeeOccupancySensor KEYWORD1
ZigbeePM25Sensor KEYWORD1
ZigbeePowerOutlet KEYWORD1
ZigbeePressureSensor KEYWORD1
ZigbeeRangeExtender KEYWORD1
ZigbeeSwitch KEYWORD1
ZigbeeTempSensor KEYWORD1
ZigbeeThermostat KEYWORD1
ZigbeeVibrationSensor KEYWORD1
ZigbeeWindowCovering KEYWORD1
ZigbeeWindSpeedSensor KEYWORD1
# Other
zigbee_role_t KEYWORD1
zbstring_t KEYWORD1
zb_device_params_t KEYWORD1
zigbee_scan_result_t KEYWORD1
zb_power_source_t KEYWORD1
ZigbeeWindowCoveringType KEYWORD1
ZigbeeLevelStepDirection KEYWORD1
ZigbeeFanMode KEYWORD1
ZigbeeFanModeSequence KEYWORD1
zb_cmd_type_t KEYWORD1
ZigbeeColorMode KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
# ZigbeeCore
begin KEYWORD2
start KEYWORD2
stop KEYWORD2
started KEYWORD2
connected KEYWORD2
getRole KEYWORD2
addEndpoint KEYWORD2
setRadioConfig KEYWORD2
getRadioConfig KEYWORD2
setHostConfig KEYWORD2
getHostConfig KEYWORD2
setPrimaryChannelMask KEYWORD2
setScanDuration KEYWORD2
getScanDuration KEYWORD2
setRebootOpenNetwork KEYWORD2
openNetwork KEYWORD2
scanNetworks KEYWORD2
scanComplete KEYWORD2
getScanResult KEYWORD2
scanDelete KEYWORD2
factoryReset KEYWORD2
allowMultiEndpointBinding KEYWORD2
# Common ZigbeeEP
setEpConfig KEYWORD2
setVersion KEYWORD2
getEndpoint KEYWORD2
printBoundDevices KEYWORD2
getBoundDevices KEYWORD2
bound KEYWORD2
allowMultipleBinding KEYWORD2
setManualBinding KEYWORD2
setManufacturerAndModel KEYWORD2
setPowerSource KEYWORD2
setBatteryPercentage KEYWORD2
reportBatteryPercentage KEYWORD2
readManufacturer KEYWORD2
readModel KEYWORD2
onIdentify KEYWORD2
addTimeCluster KEYWORD2
setTime KEYWORD2
setTimezone KEYWORD2
getTime KEYWORD2
getTimezone KEYWORD2
addOTAClient KEYWORD2
clearBoundDevices KEYWORD2
onDefaultResponse KEYWORD2
# ZigbeeLight + ZigbeeColorDimmableLight
restoreLight KEYWORD2
setLight KEYWORD2
setLightState KEYWORD2
setLightLevel KEYWORD2
setLightColor KEYWORD2
setLightColorTemperature KEYWORD2
setLightColorCapabilities KEYWORD2
setLightColorTemperatureRange KEYWORD2
getLightState KEYWORD2
getLightLevel KEYWORD2
getLightRed KEYWORD2
getLightGreen KEYWORD2
getLightBlue KEYWORD2
getLightColorTemperature KEYWORD2
getLightColorMode KEYWORD2
getLightColorHue KEYWORD2
getLightColorSaturation KEYWORD2
getLightColorCapabilities KEYWORD2
onLightChange KEYWORD2
onLightChangeRgb KEYWORD2
onLightChangeHsv KEYWORD2
onLightChangeTemp KEYWORD2
onLightColorChangeWithSource KEYWORD2
onLightLevelChange KEYWORD2
onLightLevelChangeWithSource KEYWORD2
onLightStateChange KEYWORD2
onLightStateChangeWithSource KEYWORD2
# ZigbeeSwitch + ZigbeeColorDimmerSwitch
lightToggle KEYWORD2
lightOn KEYWORD2
lightOff KEYWORD2
lightOffWithEffect KEYWORD2
lightOnWithTimedOff KEYWORD2
lightOnWithSceneRecall KEYWORD2
setLightLevel KEYWORD2
setLightLevelStep KEYWORD2
setLightColor KEYWORD2
getLightState KEYWORD2
getLightLevel KEYWORD2
getLightColor KEYWORD2
onLightStateChange KEYWORD2
onLightStateChangeWithSource KEYWORD2
onLightLevelChange KEYWORD2
onLightLevelChangeWithSource KEYWORD2
onLightColorChange KEYWORD2
onLightColorChangeWithSource KEYWORD2
# ZigbeeThermostat
onTempRecieve KEYWORD2
onTempReceiveWithSource KEYWORD2
onTempConfigReceive KEYWORD2
getTemperature KEYWORD2
getTemperatureSettings KEYWORD2
setTemperatureReporting KEYWORD2
onHumidityReceive KEYWORD2
onHumidityReceiveWithSource KEYWORD2
onHumidityConfigReceive KEYWORD2
getHumidity KEYWORD2
getHumiditySettings KEYWORD2
setHumidityReporting KEYWORD2
# Common Zigbee Sensor
setMinMaxValue KEYWORD2
setTolerance KEYWORD2
setReporting KEYWORD2
report KEYWORD2
# ZigbeeTempSensor + humidity
setTemperature KEYWORD2
reportTemperature KEYWORD2
addHumiditySensor KEYWORD2
setHumidity KEYWORD2
setHumidityReporting KEYWORD2
reportHumidity KEYWORD2
# ZigbeeIlluminanceSensor
setIlluminance KEYWORD2
# ZigbeeFlowSensor
setFlow KEYWORD2
# ZigbeePressureSensor
setPressure KEYWORD2
# ZigbeeOccupancySensor
setOccupancy KEYWORD2
setSensorType KEYWORD2
# ZigbeeCarbonDioxideSensor
setCarbonDioxide KEYWORD2
# ZigbeeAnalog
addAnalogInput KEYWORD2
addAnalogOutput KEYWORD2
onAnalogOutputChange KEYWORD2
setAnalogInput KEYWORD2
setAnalogOutput KEYWORD2
getAnalogOutput KEYWORD2
reportAnalogInput KEYWORD2
reportAnalogOutput KEYWORD2
setAnalogInputReporting KEYWORD2
setAnalogInputApplication KEYWORD2
setAnalogInputDescription KEYWORD2
setAnalogInputResolution KEYWORD2
setAnalogOutputApplication KEYWORD2
setAnalogOutputDescription KEYWORD2
setAnalogOutputResolution KEYWORD2
# ZigbeeCarbonDioxideSensor
setCarbonDioxide KEYWORD2
# ZigbeeContactSwitch + ZigbeeDoorWindowHandle
setIASClientEndpoint KEYWORD2
setClosed KEYWORD2
setOpen KEYWORD2
setTilted KEYWORD2
requestIASZoneEnroll KEYWORD2
restoreIASZoneEnroll KEYWORD2
enrolled KEYWORD2
# ZigbeeVibrationSensor
setVibration KEYWORD2
# ZigbeeWindowCovering
onOpen KEYWORD2
onClose KEYWORD2
onGoToLiftPercentage KEYWORD2
onGoToTiltPercentage KEYWORD2
onStop KEYWORD2
setLiftPosition KEYWORD2
setLiftPercentage KEYWORD2
setTiltPosition KEYWORD2
setTiltPercentage KEYWORD2
setCoveringType KEYWORD2
setConfigStatus KEYWORD2
setMode KEYWORD2
setLimits KEYWORD2
# ZigbeeBinary
addBinaryInput KEYWORD2
addBinaryOutput KEYWORD2
onBinaryOutputChange KEYWORD2
setBinaryInput KEYWORD2
setBinaryOutput KEYWORD2
getBinaryOutput KEYWORD2
reportBinaryInput KEYWORD2
reportBinaryOutput KEYWORD2
setBinaryInputApplication KEYWORD2
setBinaryInputDescription KEYWORD2
setBinaryOutputApplication KEYWORD2
setBinaryOutputDescription KEYWORD2
# ZigbeeFanControl
setFanModeSequence KEYWORD2
getFanMode KEYWORD2
getFanModeSequence KEYWORD2
onFanModeChange KEYWORD2
# ZigbeeMultistate
addMultistateInput KEYWORD2
addMultistateOutput KEYWORD2
onMultistateOutputChange KEYWORD2
setMultistateInput KEYWORD2
getMultistateInput KEYWORD2
setMultistateOutput KEYWORD2
getMultistateOutput KEYWORD2
reportMultistateInput KEYWORD2
reportMultistateOutput KEYWORD2
setMultistateInputApplication KEYWORD2
setMultistateInputDescription KEYWORD2
setMultistateInputStates KEYWORD2
setMultistateOutputApplication KEYWORD2
setMultistateOutputDescription KEYWORD2
setMultistateOutputStates KEYWORD2
#getMultistateInputStateNames KEYWORD2
getMultistateInputStateNamesLength KEYWORD2
#getMultistateOutputStateNames KEYWORD2
getMultistateOutputStateNamesLength KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
ZIGBEE_COORDINATOR LITERAL1
ZIGBEE_ROUTER LITERAL1
ZIGBEE_END_DEVICE LITERAL1
ZIGBEE_DEFAULT_ED_CONFIG LITERAL1
ZIGBEE_DEFAULT_ROUTER_CONFIG LITERAL1
ZIGBEE_DEFAULT_COORDINATOR_CONFIG LITERAL1
ZIGBEE_DEFAULT_RADIO_CONFIG LITERAL1
ZIGBEE_DEFAULT_UART_RCP_RADIO_CONFIG LITERAL1
ZIGBEE_DEFAULT_HOST_CONFIG LITERAL1
ZB_ARRAY_LENGHT LITERAL1
# ZigbeeMultistate
ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_0_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_1_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_1_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_1_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_2_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_2_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_2_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_3_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_3_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_3_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_4_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_4_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_4_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_5_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_5_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_5_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_6_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_6_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_6_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_8_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_8_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_8_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_9_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_9_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_9_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_10_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_10_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_10_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_11_INDEX LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_11_NUM_STATES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_11_STATE_NAMES LITERAL1
ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX LITERAL1
# ZigbeeColorDimmerSwitch level step
ZIGBEE_LEVEL_STEP_UP LITERAL1
ZIGBEE_LEVEL_STEP_DOWN LITERAL1
#ZigbeeColorDimmableLight
ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION LITERAL1
ZIGBEE_COLOR_CAPABILITY_ENHANCED_HUE LITERAL1
ZIGBEE_COLOR_CAPABILITY_COLOR_LOOP LITERAL1
ZIGBEE_COLOR_CAPABILITY_X_Y LITERAL1
ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP LITERAL1
ZIGBEE_COLOR_MODE_HUE_SATURATION LITERAL1
ZIGBEE_COLOR_MODE_CURRENT_X_Y LITERAL1
ZIGBEE_COLOR_MODE_TEMPERATURE LITERAL1
+9
View File
@@ -0,0 +1,9 @@
name=Zigbee
version=3.3.7
author=P-R-O-C-H-Y
maintainer=Jan Procházka <jan.prochazka@espressif.com>
sentence=Enables zigbee connection with the ESP32
paragraph=With this library you can create zigbee end devices, routers, coordinators and connect them to the zigbee network.
category=Communication
url=
architectures=esp32
+58
View File
@@ -0,0 +1,58 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Zigbee library header file for includes of all Zigbee library headers.
#pragma once
// Common types and functions
#include "ZigbeeTypes.h"
// Core
#include "ZigbeeCore.h"
#include "ZigbeeEP.h"
// Endpoints
//// Switches
#include "ep/ZigbeeColorDimmerSwitch.h"
#include "ep/ZigbeeSwitch.h"
//// Lights
#include "ep/ZigbeeColorDimmableLight.h"
#include "ep/ZigbeeDimmableLight.h"
#include "ep/ZigbeeLight.h"
//// Controllers
#include "ep/ZigbeeThermostat.h"
#include "ep/ZigbeeFanControl.h"
////Outlets
#include "ep/ZigbeePowerOutlet.h"
//// Sensors
#include "ep/ZigbeeAnalog.h"
#include "ep/ZigbeeBinary.h"
#include "ep/ZigbeeCarbonDioxideSensor.h"
#include "ep/ZigbeeContactSwitch.h"
#include "ep/ZigbeeDoorWindowHandle.h"
#include "ep/ZigbeeElectricalMeasurement.h"
#include "ep/ZigbeeFlowSensor.h"
#include "ep/ZigbeeIlluminanceSensor.h"
#include "ep/ZigbeeMultistate.h"
#include "ep/ZigbeeOccupancySensor.h"
#include "ep/ZigbeePM25Sensor.h"
#include "ep/ZigbeePressureSensor.h"
#include "ep/ZigbeeTempSensor.h"
#include "ep/ZigbeeVibrationSensor.h"
#include "ep/ZigbeeWindSpeedSensor.h"
#include "ep/ZigbeeWindowCovering.h"
//// Other
#include "ep/ZigbeeGateway.h"
#include "ep/ZigbeeRangeExtender.h"
+849
View File
@@ -0,0 +1,849 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* Zigbee Core Functions */
#include "ZigbeeCore.h"
#if CONFIG_ZB_ENABLED
#include "ZigbeeHandlers.cpp"
#include "Arduino.h"
#include <set>
#ifdef __cplusplus
extern "C" {
#endif
#include "zboss_api.h"
extern zb_ret_t zb_nvram_write_dataset(zb_nvram_dataset_types_t t); // rejoin scanning workaround
extern void zb_set_ed_node_descriptor(bool power_src, bool rx_on_when_idle, bool alloc_addr); // sleepy device power mode workaround
#ifdef __cplusplus
}
#endif
static bool edBatteryPowered = false;
ZigbeeCore::ZigbeeCore() {
_radio_config.radio_mode = ZB_RADIO_MODE_NATIVE; // Use the native 15.4 radio
_host_config.host_connection_mode = ZB_HOST_CONNECTION_MODE_NONE; // Disable host connection
_zb_ep_list = esp_zb_ep_list_create();
_primary_channel_mask = ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK;
_open_network = 0;
_scan_status = ZB_SCAN_FAILED;
_begin_timeout = ZB_BEGIN_TIMEOUT_DEFAULT;
_started = false;
_connected = false;
_scan_duration = 3; // default scan duration
_rx_on_when_idle = true;
_debug = false;
_allow_multi_endpoint_binding = false;
_global_default_response_cb = nullptr; // Initialize global callback to nullptr
if (!lock) {
lock = xSemaphoreCreateBinary();
if (lock == NULL) {
log_e("Semaphore creation failed");
}
}
}
//forward declaration
static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message);
bool zb_apsde_data_indication_handler(esp_zb_apsde_data_ind_t ind);
bool ZigbeeCore::begin(esp_zb_cfg_t *role_cfg, bool erase_nvs) {
if (!zigbeeInit(role_cfg, erase_nvs)) {
log_e("ZigbeeCore begin failed");
return false;
}
_role = (zigbee_role_t)role_cfg->esp_zb_role;
if (xSemaphoreTake(lock, _begin_timeout) != pdTRUE) {
log_e("ZigbeeCore begin failed or timeout");
if (_role != ZIGBEE_COORDINATOR) { // Only End Device and Router can rejoin
resetNVRAMChannelMask();
}
}
return started();
}
bool ZigbeeCore::begin(zigbee_role_t role, bool erase_nvs) {
bool status = true;
switch (role) {
case ZIGBEE_COORDINATOR:
{
_role = ZIGBEE_COORDINATOR;
esp_zb_cfg_t zb_nwk_cfg = ZIGBEE_DEFAULT_COORDINATOR_CONFIG();
status = zigbeeInit(&zb_nwk_cfg, erase_nvs);
break;
}
case ZIGBEE_ROUTER:
{
_role = ZIGBEE_ROUTER;
esp_zb_cfg_t zb_nwk_cfg = ZIGBEE_DEFAULT_ROUTER_CONFIG();
status = zigbeeInit(&zb_nwk_cfg, erase_nvs);
break;
}
case ZIGBEE_END_DEVICE:
{
_role = ZIGBEE_END_DEVICE;
esp_zb_cfg_t zb_nwk_cfg = ZIGBEE_DEFAULT_ED_CONFIG();
status = zigbeeInit(&zb_nwk_cfg, erase_nvs);
break;
}
default: log_e("Invalid Zigbee Role"); return false;
}
if (!status || xSemaphoreTake(lock, _begin_timeout) != pdTRUE) {
log_e("ZigbeeCore begin failed or timeout");
if (_role != ZIGBEE_COORDINATOR) { // Only End Device and Router can rejoin
resetNVRAMChannelMask();
}
}
return started();
}
bool ZigbeeCore::addEndpoint(ZigbeeEP *ep) {
ep_objects.push_back(ep);
log_d("Endpoint: %d, Device ID: 0x%04x", ep->_endpoint, ep->_device_id);
//Register clusters and ep_list to the ZigbeeCore class's ep_list
if (ep->_ep_config.endpoint == 0 || ep->_cluster_list == nullptr) {
log_e("Endpoint config or Cluster list is not initialized, EP not added to ZigbeeCore's EP list");
return false;
}
esp_err_t ret = ESP_OK;
if (ep->_device_id == ESP_ZB_HA_HOME_GATEWAY_DEVICE_ID) {
ret = esp_zb_ep_list_add_gateway_ep(_zb_ep_list, ep->_cluster_list, ep->_ep_config);
} else {
ret = esp_zb_ep_list_add_ep(_zb_ep_list, ep->_cluster_list, ep->_ep_config);
}
if (ret != ESP_OK) {
log_e("Failed to add endpoint: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
static void esp_zb_task(void *pvParameters) {
esp_zb_bdb_set_scan_duration(Zigbee.getScanDuration());
/* initialize Zigbee stack */
ESP_ERROR_CHECK(esp_zb_start(false));
//NOTE: This is a workaround to make battery powered devices to be discovered as battery powered
if (((zigbee_role_t)Zigbee.getRole() == ZIGBEE_END_DEVICE) && edBatteryPowered) {
zb_set_ed_node_descriptor(0, Zigbee.getRxOnWhenIdle(), 1);
}
esp_zb_stack_main_loop();
}
// Zigbee core init function
bool ZigbeeCore::zigbeeInit(esp_zb_cfg_t *zb_cfg, bool erase_nvs) {
// Zigbee platform configuration
esp_zb_platform_config_t platform_config = {
.radio_config = _radio_config,
.host_config = _host_config,
};
esp_err_t err = esp_zb_platform_config(&platform_config);
if (err != ESP_OK) {
log_e("Failed to configure Zigbee platform");
return false;
}
// Initialize Zigbee stack
log_d("Initialize Zigbee stack");
esp_zb_init(zb_cfg);
// Register all Zigbee EPs in list
if (ep_objects.empty()) {
log_w("No Zigbee EPs to register");
} else {
log_d("Register all Zigbee EPs in list");
err = esp_zb_device_register(_zb_ep_list);
if (err != ESP_OK) {
log_e("Failed to register Zigbee EPs");
return false;
}
//print the list of Zigbee EPs from ep_objects
log_i("List of registered Zigbee EPs:");
for (std::list<ZigbeeEP *>::iterator it = ep_objects.begin(); it != ep_objects.end(); ++it) {
log_i("Device type: %s, Endpoint: %d, Device ID: 0x%04x", getDeviceTypeString((*it)->_device_id), (*it)->_endpoint, (*it)->_device_id);
if ((*it)->_power_source == ZB_POWER_SOURCE_BATTERY) {
edBatteryPowered = true;
}
}
}
// Register Zigbee action handler
esp_zb_core_action_handler_register(zb_action_handler);
err = esp_zb_set_primary_network_channel_set(_primary_channel_mask);
if (err != ESP_OK) {
log_e("Failed to set primary network channel mask");
return false;
}
// Register APSDATA INDICATION handler to catch bind/unbind requests
esp_zb_aps_data_indication_handler_register(zb_apsde_data_indication_handler);
//Erase NVRAM before creating connection to new Coordinator
if (erase_nvs) {
esp_zb_nvram_erase_at_start(true);
}
// Create Zigbee task and start Zigbee stack
xTaskCreate(esp_zb_task, "Zigbee_main", 8192, NULL, 5, NULL);
return true;
}
void ZigbeeCore::setRadioConfig(esp_zb_radio_config_t config) {
_radio_config = config;
}
esp_zb_radio_config_t ZigbeeCore::getRadioConfig() {
return _radio_config;
}
void ZigbeeCore::setHostConfig(esp_zb_host_config_t config) {
_host_config = config;
}
esp_zb_host_config_t ZigbeeCore::getHostConfig() {
return _host_config;
}
void ZigbeeCore::setPrimaryChannelMask(uint32_t mask) {
_primary_channel_mask = mask;
}
void ZigbeeCore::setScanDuration(uint8_t duration) {
if (duration < 1 || duration > 4) {
log_e("Invalid scan duration, must be between 1 and 4");
return;
}
_scan_duration = duration;
}
void ZigbeeCore::setRebootOpenNetwork(uint8_t time) {
_open_network = time;
}
void ZigbeeCore::openNetwork(uint8_t time) {
if (started()) {
log_v("Opening network for joining for %d seconds", time);
esp_zb_bdb_open_network(time);
}
}
void ZigbeeCore::closeNetwork() {
if (started()) {
log_v("Closing network");
esp_zb_bdb_close_network();
}
}
static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) {
if (esp_zb_bdb_start_top_level_commissioning(mode_mask) != ESP_OK) {
log_e("Failed to start Zigbee commissioning");
}
}
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
//common variables
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 = (esp_zb_app_signal_type_t)*p_sg_p;
//coordinator variables
esp_zb_zdo_signal_device_annce_params_t *dev_annce_params = NULL;
esp_zb_zdo_signal_leave_params_t *leave_params = NULL;
//router variables
esp_zb_zdo_signal_device_update_params_t *dev_update_params = NULL;
//main switch
switch (sig_type) {
case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP: // Common
log_i("Zigbee stack initialized");
log_d("Zigbee channel mask: 0x%08x", esp_zb_get_channel_mask());
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
break;
case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START: // Common
case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT: // Common
if (err_status == ESP_OK) {
log_i("Device started up in %s factory-reset mode", esp_zb_bdb_is_factory_new() ? "" : "non");
if (esp_zb_bdb_is_factory_new()) {
// Role specific code
if ((zigbee_role_t)Zigbee.getRole() == ZIGBEE_COORDINATOR) {
log_i("Start network formation");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_FORMATION);
} else {
log_i("Start network steering");
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
Zigbee._started = true;
xSemaphoreGive(Zigbee.lock);
}
} else {
log_i("Device rebooted");
Zigbee._started = true;
xSemaphoreGive(Zigbee.lock);
if ((zigbee_role_t)Zigbee.getRole() == ZIGBEE_COORDINATOR && Zigbee._open_network > 0) {
log_i("Opening network for joining for %d seconds", Zigbee._open_network);
esp_zb_bdb_open_network(Zigbee._open_network);
} else {
// Save the channel mask to NVRAM in case of reboot which may be on a different channel after a change in the network
Zigbee.setNVRAMChannelMask(1 << esp_zb_get_current_channel());
Zigbee._connected = true; // Coordinator is always connected
}
Zigbee.searchBindings();
}
} else {
/* commissioning failed */
log_w("Commissioning failed, trying again...", esp_err_to_name(err_status));
esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_INITIALIZATION, 500);
}
break;
case ESP_ZB_BDB_SIGNAL_FORMATION: // Coordinator
if ((zigbee_role_t)Zigbee.getRole() == ZIGBEE_COORDINATOR) {
if (err_status == ESP_OK) {
esp_zb_ieee_addr_t extended_pan_id;
esp_zb_get_extended_pan_id(extended_pan_id);
log_i(
"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()
);
Zigbee._connected = true;
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
} else {
log_i("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: // Router and End Device
if ((zigbee_role_t)Zigbee.getRole() == ZIGBEE_COORDINATOR) {
if (err_status == ESP_OK) {
log_i("Network steering started");
}
Zigbee._started = true;
xSemaphoreGive(Zigbee.lock);
} else {
if (err_status == ESP_OK) {
esp_zb_ieee_addr_t extended_pan_id;
esp_zb_get_extended_pan_id(extended_pan_id);
log_i(
"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()
);
Zigbee._connected = true;
// Set channel mask and write to NVRAM, so that the device will re-join the network faster after reboot (scan only on the current channel)
Zigbee.setNVRAMChannelMask(1 << esp_zb_get_current_channel());
} else {
log_i("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);
}
}
break;
case ESP_ZB_ZDO_SIGNAL_DEVICE_ANNCE: // Coordinator
if ((zigbee_role_t)Zigbee.getRole() == ZIGBEE_COORDINATOR) {
dev_annce_params = (esp_zb_zdo_signal_device_annce_params_t *)esp_zb_app_signal_get_params(p_sg_p);
log_i("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;
log_v("Device capabilities: 0x%02x", dev_annce_params->capability);
/*
capability:
Bit 0 Alternate PAN Coordinator
Bit 1 Device type: 1- ZigBee Router; 0 End Device
Bit 2 Power Source: 1 Main powered
Bit 3 Receiver on when Idle
Bit 4 Reserved
Bit 5 Reserved
Bit 6 Security capability
Bit 7 Reserved
*/
// for each endpoint in the list call the findEndpoint function if not bounded or allowed to bind multiple devices
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
log_d("Checking endpoint %d", (*it)->getEndpoint());
if (!(*it)->epUseManualBinding()) {
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
// Check if the device is already bound
bool found = false;
// Get the list of devices bound to the EP
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
for (std::list<zb_device_params_t *>::iterator device = bound_devices.begin(); device != bound_devices.end(); ++device) {
if (((*device)->short_addr == dev_annce_params->device_short_addr) || (memcmp((*device)->ieee_addr, dev_annce_params->ieee_addr, 8) == 0)) {
found = true;
log_d("Device already bound to endpoint %d", (*it)->getEndpoint());
break;
}
}
if (!found) {
log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint());
(*it)->findEndpoint(&cmd_req);
log_d("Endpoint %d is searching for device", (*it)->getEndpoint());
if (!Zigbee.allowMultiEndpointBinding()) { // If multi endpoint binding is not allowed, break the loop to keep backwards compatibility
break;
}
}
}
}
}
}
break;
case ESP_ZB_ZDO_SIGNAL_DEVICE_UPDATE: // Router
if ((zigbee_role_t)Zigbee.getRole() == ZIGBEE_ROUTER) {
dev_update_params = (esp_zb_zdo_signal_device_update_params_t *)esp_zb_app_signal_get_params(p_sg_p);
log_i("New device commissioned or rejoined (short: 0x%04hx)", dev_update_params->short_addr);
esp_zb_zdo_match_desc_req_param_t cmd_req;
cmd_req.dst_nwk_addr = dev_update_params->short_addr;
cmd_req.addr_of_interest = dev_update_params->short_addr;
// for each endpoint in the list call the findEndpoint function if not bounded or allowed to bind multiple devices
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
log_d("Checking endpoint %d", (*it)->getEndpoint());
if (!(*it)->epUseManualBinding()) {
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
// Check if the device is already bound
bool found = false;
// Get the list of devices bound to the EP
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
for (std::list<zb_device_params_t *>::iterator device = bound_devices.begin(); device != bound_devices.end(); ++device) {
if (((*device)->short_addr == dev_update_params->short_addr) || (memcmp((*device)->ieee_addr, dev_update_params->long_addr, 8) == 0)) {
found = true;
log_d("Device already bound to endpoint %d", (*it)->getEndpoint());
break;
}
}
if (!found) {
log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint());
(*it)->findEndpoint(&cmd_req);
log_d("Endpoint %d is searching for device", (*it)->getEndpoint());
if (!Zigbee.allowMultiEndpointBinding()) { // If multi endpoint binding is not allowed, break the loop to keep backwards compatibility
break;
}
}
}
}
}
}
break;
case ESP_ZB_NWK_SIGNAL_PERMIT_JOIN_STATUS: // Coordinator
if ((zigbee_role_t)Zigbee.getRole() == ZIGBEE_COORDINATOR) {
if (err_status == ESP_OK) {
if (*(uint8_t *)esp_zb_app_signal_get_params(p_sg_p)) {
log_i("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 {
log_i("Network(0x%04hx) closed, devices joining not allowed.", esp_zb_get_pan_id());
}
}
}
break;
case ESP_ZB_ZDO_SIGNAL_LEAVE: // End Device + Router
// Received signal to leave the network
if ((zigbee_role_t)Zigbee.getRole() != ZIGBEE_COORDINATOR) {
leave_params = (esp_zb_zdo_signal_leave_params_t *)esp_zb_app_signal_get_params(p_sg_p);
log_v("Signal to leave the network, leave type: %d", leave_params->leave_type);
if (leave_params->leave_type == ESP_ZB_NWK_LEAVE_TYPE_RESET) { // Leave without rejoin -> Factory reset
log_i("Leave without rejoin, factory reset the device");
Zigbee.factoryReset(true);
} else { // Leave with rejoin -> Rejoin the network, only reboot the device
log_i("Leave with rejoin, only reboot the device");
ESP.restart();
}
}
break;
default: log_v("ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type, esp_err_to_name(err_status)); break;
}
}
// APS DATA INDICATION HANDLER to catch bind/unbind requests
bool zb_apsde_data_indication_handler(esp_zb_apsde_data_ind_t ind) {
if (Zigbee.getDebugMode()) {
log_d("APSDE INDICATION - Received APSDE-DATA indication, status: %d", ind.status);
log_d(
"APSDE INDICATION - dst_endpoint: %d, src_endpoint: %d, dst_addr_mode: %d, src_addr_mode: %d, cluster_id: 0x%04x, asdu_length: %d", ind.dst_endpoint,
ind.src_endpoint, ind.dst_addr_mode, ind.src_addr_mode, ind.cluster_id, ind.asdu_length
);
log_d(
"APSDE INDICATION - dst_short_addr: 0x%04x, src_short_addr: 0x%04x, profile_id: 0x%04x, security_status: %d, lqi: %d, rx_time: %d", ind.dst_short_addr,
ind.src_short_addr, ind.profile_id, ind.security_status, ind.lqi, ind.rx_time
);
}
if (ind.status == 0x00) {
// Catch bind/unbind requests to update the bound devices list
if (ind.cluster_id == 0x21 || ind.cluster_id == 0x22) {
Zigbee.searchBindings();
}
} else {
log_e("APSDE INDICATION - Invalid status of APSDE-DATA indication, error code: %d", ind.status);
}
return false; //False to let the stack process the message as usual
}
void ZigbeeCore::factoryReset(bool restart) {
if (restart) {
log_v("Factory resetting Zigbee stack, device will reboot");
esp_zb_factory_reset();
} else {
log_v("Factory resetting Zigbee NVRAM to factory default");
log_w("The device will not reboot, to take effect please reboot the device manually");
esp_zb_zcl_reset_nvram_to_factory_default();
}
}
void ZigbeeCore::scanCompleteCallback(esp_zb_zdp_status_t zdo_status, uint8_t count, esp_zb_network_descriptor_t *nwk_descriptor) {
log_v("Zigbee network scan complete");
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
log_v("Found %d networks", count);
//print Zigbee networks
for (int i = 0; i < count; i++) {
log_v(
"Network %d: PAN ID: 0x%04hx, Permit Joining: %s, Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, Channel: %d, Router Capacity: %s, End "
"Device Capacity: %s",
i, nwk_descriptor[i].short_pan_id, nwk_descriptor[i].permit_joining ? "Yes" : "No", nwk_descriptor[i].extended_pan_id[7],
nwk_descriptor[i].extended_pan_id[6], nwk_descriptor[i].extended_pan_id[5], nwk_descriptor[i].extended_pan_id[4], nwk_descriptor[i].extended_pan_id[3],
nwk_descriptor[i].extended_pan_id[2], nwk_descriptor[i].extended_pan_id[1], nwk_descriptor[i].extended_pan_id[0], nwk_descriptor[i].logic_channel,
nwk_descriptor[i].router_capacity ? "Yes" : "No", nwk_descriptor[i].end_device_capacity ? "Yes" : "No"
);
}
//save scan result and update scan status
//copy network descriptor to _scan_result to keep the data after the callback
Zigbee._scan_result = (esp_zb_network_descriptor_t *)malloc(count * sizeof(esp_zb_network_descriptor_t));
memcpy(Zigbee._scan_result, nwk_descriptor, count * sizeof(esp_zb_network_descriptor_t));
Zigbee._scan_status = count;
} else {
log_e("Failed to scan Zigbee network (status: 0x%x)", zdo_status);
Zigbee._scan_status = ZB_SCAN_FAILED;
Zigbee._scan_result = nullptr;
}
}
void ZigbeeCore::scanNetworks(u_int32_t channel_mask, u_int8_t scan_duration) {
if (!started()) {
log_e("Zigbee stack is not started, cannot scan networks");
return;
}
if (_scan_status == ZB_SCAN_RUNNING) {
log_w("Scan already in progress, ignoring new scan request");
return;
}
log_v("Scanning Zigbee networks");
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zdo_active_scan_request(channel_mask, scan_duration, scanCompleteCallback);
esp_zb_lock_release();
_scan_status = ZB_SCAN_RUNNING;
}
int16_t ZigbeeCore::scanComplete() {
return _scan_status;
}
zigbee_scan_result_t *ZigbeeCore::getScanResult() {
return _scan_result;
}
void ZigbeeCore::scanDelete() {
if (_scan_result != nullptr) {
free(_scan_result);
_scan_result = nullptr;
}
_scan_status = ZB_SCAN_FAILED;
}
// Recall bounded devices from the binding table after reboot or when requested
void ZigbeeCore::bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx) {
esp_zb_zdo_mgmt_bind_param_t *req = (esp_zb_zdo_mgmt_bind_param_t *)user_ctx;
esp_zb_zdp_status_t zdo_status = (esp_zb_zdp_status_t)table_info->status;
log_d("Binding table callback for address 0x%04x with status %d", req->dst_addr, zdo_status);
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
// Print binding table log simple
log_d("Binding table info: total %d, index %d, count %d", table_info->total, table_info->index, table_info->count);
if (table_info->total == 0) {
log_d("No binding table entries found");
// Clear all bound devices since there are no entries
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
log_d("Clearing bound devices for EP %d", (*it)->getEndpoint());
(*it)->clearBoundDevices();
}
free(req);
return;
}
// Create a set to track found devices using both short and IEEE addresses
struct DeviceIdentifier {
uint8_t endpoint;
uint16_t short_addr;
esp_zb_ieee_addr_t ieee_addr;
bool is_ieee;
bool operator<(const DeviceIdentifier &other) const {
if (endpoint != other.endpoint) {
return endpoint < other.endpoint;
}
if (is_ieee != other.is_ieee) {
return is_ieee < other.is_ieee;
}
if (is_ieee) {
return memcmp(ieee_addr, other.ieee_addr, sizeof(esp_zb_ieee_addr_t)) < 0;
}
return short_addr < other.short_addr;
}
};
static std::set<DeviceIdentifier> found_devices;
static std::vector<esp_zb_zdo_binding_table_record_t> all_records;
// If this is the first chunk (index 0), clear the previous data
if (table_info->index == 0) {
found_devices.clear();
all_records.clear();
}
// Add current records to our collection
esp_zb_zdo_binding_table_record_t *record = table_info->record;
for (int i = 0; i < table_info->count; i++) {
log_d(
"Processing record %d: src_endp %d, dst_endp %d, cluster_id 0x%04x, dst_addr_mode %d", i, record->src_endp, record->dst_endp, record->cluster_id,
record->dst_addr_mode
);
all_records.push_back(*record);
record = record->next;
}
// If this is not the last chunk, request the next one
if (table_info->index + table_info->count < table_info->total) {
log_d("Requesting next chunk of binding table (current index: %d, count: %d, total: %d)", table_info->index, table_info->count, table_info->total);
req->start_index = table_info->index + table_info->count;
esp_zb_zdo_binding_table_req(req, bindingTableCb, req);
} else {
// This is the last chunk, process all records
log_d("Processing final chunk of binding table, total records: %d", all_records.size());
for (const auto &record : all_records) {
DeviceIdentifier dev_id;
dev_id.endpoint = record.src_endp;
dev_id.is_ieee = (record.dst_addr_mode == ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT);
if (dev_id.is_ieee) {
memcpy(dev_id.ieee_addr, record.dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
dev_id.short_addr = 0xFFFF; // Invalid short address
} else {
dev_id.short_addr = record.dst_address.addr_short;
memset(dev_id.ieee_addr, 0, sizeof(esp_zb_ieee_addr_t));
}
// Track this device as found
found_devices.insert(dev_id);
}
// Now process each endpoint and update its bound devices
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
log_d("Processing endpoint %d", (*it)->getEndpoint());
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
std::list<zb_device_params_t *> devices_to_remove;
// First, identify devices that need to be removed
for (std::list<zb_device_params_t *>::iterator dev_it = bound_devices.begin(); dev_it != bound_devices.end(); ++dev_it) {
DeviceIdentifier dev_id;
dev_id.endpoint = (*it)->getEndpoint();
// Create both short and IEEE address identifiers for the device
bool found = false;
// Check if device exists with short address
if ((*dev_it)->short_addr != 0xFFFF) {
dev_id.is_ieee = false;
dev_id.short_addr = (*dev_it)->short_addr;
memset(dev_id.ieee_addr, 0, sizeof(esp_zb_ieee_addr_t));
if (found_devices.find(dev_id) != found_devices.end()) {
found = true;
}
}
// Check if device exists with IEEE address
if (!found) {
dev_id.is_ieee = true;
memcpy(dev_id.ieee_addr, (*dev_it)->ieee_addr, sizeof(esp_zb_ieee_addr_t));
dev_id.short_addr = 0xFFFF;
if (found_devices.find(dev_id) != found_devices.end()) {
found = true;
}
}
if (!found) {
devices_to_remove.push_back(*dev_it);
}
}
// Remove devices that are no longer in the binding table
for (std::list<zb_device_params_t *>::iterator dev_it = devices_to_remove.begin(); dev_it != devices_to_remove.end(); ++dev_it) {
(*it)->removeBoundDevice(*dev_it);
free(*dev_it);
}
// Now add new devices from the binding table
for (const auto &record : all_records) {
if (record.src_endp == (*it)->getEndpoint()) {
log_d("Processing binding record for EP %d", record.src_endp);
zb_device_params_t *device = (zb_device_params_t *)calloc(1, sizeof(zb_device_params_t));
if (!device) {
log_e("Failed to allocate memory for device params");
continue;
}
device->endpoint = record.dst_endp;
bool is_ieee = (record.dst_addr_mode == ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT);
if (is_ieee) {
memcpy(device->ieee_addr, record.dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
device->short_addr = 0xFFFF;
} else {
device->short_addr = record.dst_address.addr_short;
memset(device->ieee_addr, 0, sizeof(esp_zb_ieee_addr_t));
}
// Check if device already exists
bool device_exists = false;
for (std::list<zb_device_params_t *>::iterator dev_it = bound_devices.begin(); dev_it != bound_devices.end(); ++dev_it) {
if (is_ieee) {
if (memcmp((*dev_it)->ieee_addr, device->ieee_addr, sizeof(esp_zb_ieee_addr_t)) == 0) {
device_exists = true;
break;
}
} else {
if ((*dev_it)->short_addr == device->short_addr) {
device_exists = true;
break;
}
}
}
if (!device_exists) {
(*it)->addBoundDevice(device);
log_d(
"Device bound to EP %d -> device endpoint: %d, %s: %s", record.src_endp, device->endpoint, is_ieee ? "ieee addr" : "short addr",
is_ieee ? formatIEEEAddress(device->ieee_addr) : formatShortAddress(device->short_addr)
);
} else {
log_d("Device already exists, freeing allocated memory");
free(device); // Free the device if it already exists
}
}
}
}
// Print bound devices
log_d("Filling bounded devices finished");
free(req);
}
} else {
log_e("Binding table request failed with status: %d", zdo_status);
free(req);
}
}
void ZigbeeCore::searchBindings() {
esp_zb_zdo_mgmt_bind_param_t *mb_req = (esp_zb_zdo_mgmt_bind_param_t *)malloc(sizeof(esp_zb_zdo_mgmt_bind_param_t));
mb_req->dst_addr = esp_zb_get_short_address();
mb_req->start_index = 0;
log_d("Requesting binding table for address 0x%04x", mb_req->dst_addr);
esp_zb_zdo_binding_table_req(mb_req, bindingTableCb, (void *)mb_req);
}
void ZigbeeCore::resetNVRAMChannelMask() {
_primary_channel_mask = ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK;
esp_zb_set_channel_mask(_primary_channel_mask);
zb_nvram_write_dataset(ZB_NVRAM_COMMON_DATA);
log_v("Channel mask reset to all channels");
}
void ZigbeeCore::setNVRAMChannelMask(uint32_t mask) {
_primary_channel_mask = mask;
esp_zb_set_channel_mask(_primary_channel_mask);
zb_nvram_write_dataset(ZB_NVRAM_COMMON_DATA);
log_v("Channel mask set to 0x%08x", mask);
}
void ZigbeeCore::stop() {
if (started()) {
vTaskSuspend(xTaskGetHandle("Zigbee_main"));
log_v("Zigbee stack stopped");
_started = false;
}
return;
}
void ZigbeeCore::start() {
if (!started()) {
vTaskResume(xTaskGetHandle("Zigbee_main"));
log_v("Zigbee stack started");
_started = true;
}
return;
}
// Function to convert enum value to string
const char *ZigbeeCore::getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId) {
switch (deviceId) {
case ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID: return "General On/Off switch";
case ESP_ZB_HA_LEVEL_CONTROL_SWITCH_DEVICE_ID: return "Level Control Switch";
case ESP_ZB_HA_ON_OFF_OUTPUT_DEVICE_ID: return "General On/Off output";
case ESP_ZB_HA_LEVEL_CONTROLLABLE_OUTPUT_DEVICE_ID: return "Level Controllable Output";
case ESP_ZB_HA_SCENE_SELECTOR_DEVICE_ID: return "Scene Selector";
case ESP_ZB_HA_CONFIGURATION_TOOL_DEVICE_ID: return "Configuration Tool";
case ESP_ZB_HA_REMOTE_CONTROL_DEVICE_ID: return "Remote Control";
case ESP_ZB_HA_COMBINED_INTERFACE_DEVICE_ID: return "Combined Interface";
case ESP_ZB_HA_RANGE_EXTENDER_DEVICE_ID: return "Range Extender";
case ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID: return "Mains Power Outlet";
case ESP_ZB_HA_DOOR_LOCK_DEVICE_ID: return "Door lock client";
case ESP_ZB_HA_DOOR_LOCK_CONTROLLER_DEVICE_ID: return "Door lock controller";
case ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID: return "Simple Sensor device";
case ESP_ZB_HA_CONSUMPTION_AWARENESS_DEVICE_ID: return "Consumption Awareness Device";
case ESP_ZB_HA_HOME_GATEWAY_DEVICE_ID: return "Home Gateway";
case ESP_ZB_HA_SMART_PLUG_DEVICE_ID: return "Smart plug";
case ESP_ZB_HA_WHITE_GOODS_DEVICE_ID: return "White Goods";
case ESP_ZB_HA_METER_INTERFACE_DEVICE_ID: return "Meter Interface";
case ESP_ZB_HA_ON_OFF_LIGHT_DEVICE_ID: return "On/Off Light Device";
case ESP_ZB_HA_DIMMABLE_LIGHT_DEVICE_ID: return "Dimmable Light Device";
case ESP_ZB_HA_COLOR_DIMMABLE_LIGHT_DEVICE_ID: return "Color Dimmable Light Device";
case ESP_ZB_HA_DIMMER_SWITCH_DEVICE_ID: return "Dimmer Switch Device";
case ESP_ZB_HA_COLOR_DIMMER_SWITCH_DEVICE_ID: return "Color Dimmer Switch Device";
case ESP_ZB_HA_LIGHT_SENSOR_DEVICE_ID: return "Light Sensor";
case ESP_ZB_HA_SHADE_DEVICE_ID: return "Shade";
case ESP_ZB_HA_SHADE_CONTROLLER_DEVICE_ID: return "Shade controller";
case ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID: return "Window Covering client";
case ESP_ZB_HA_WINDOW_COVERING_CONTROLLER_DEVICE_ID: return "Window Covering controller";
case ESP_ZB_HA_HEATING_COOLING_UNIT_DEVICE_ID: return "Heating/Cooling Unit device";
case ESP_ZB_HA_THERMOSTAT_DEVICE_ID: return "Thermostat Device";
case ESP_ZB_HA_TEMPERATURE_SENSOR_DEVICE_ID: return "Temperature Sensor";
case ESP_ZB_HA_IAS_CONTROL_INDICATING_EQUIPMENT_ID: return "IAS Control and Indicating Equipment";
case ESP_ZB_HA_IAS_ANCILLARY_CONTROL_EQUIPMENT_ID: return "IAS Ancillary Control Equipment";
case ESP_ZB_HA_IAS_ZONE_ID: return "IAS Zone";
case ESP_ZB_HA_IAS_WARNING_DEVICE_ID: return "IAS Warning Device";
case ESP_ZB_HA_TEST_DEVICE_ID: return "Custom HA device for test";
case ESP_ZB_HA_CUSTOM_TUNNEL_DEVICE_ID: return "Custom Tunnel device";
case ESP_ZB_HA_CUSTOM_ATTR_DEVICE_ID: return "Custom Attributes Device";
default: return "Unknown device type";
}
}
void ZigbeeCore::callDefaultResponseCallback(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster) {
if (_global_default_response_cb) {
_global_default_response_cb(resp_to_cmd, status, endpoint, cluster);
}
}
ZigbeeCore Zigbee = ZigbeeCore();
#endif // CONFIG_ZB_ENABLED
+237
View File
@@ -0,0 +1,237 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* Zigbee core class */
#pragma once
#include "soc/soc_caps.h"
#include "sdkconfig.h"
#if CONFIG_ZB_ENABLED
#include "esp_zigbee_core.h"
#include "zdo/esp_zigbee_zdo_common.h"
#include "aps/esp_zigbee_aps.h"
#include <esp32-hal-log.h>
#include <list>
#include "ZigbeeTypes.h"
#include "ZigbeeEP.h"
class ZigbeeEP;
typedef void (*voidFuncPtr)(void);
typedef void (*voidFuncPtrArg)(void *);
typedef esp_zb_network_descriptor_t zigbee_scan_result_t;
// enum of Zigbee Roles
typedef enum {
ZIGBEE_COORDINATOR = 0,
ZIGBEE_ROUTER = 1,
ZIGBEE_END_DEVICE = 2
} zigbee_role_t;
#define ZB_SCAN_RUNNING (-1)
#define ZB_SCAN_FAILED (-2)
#define ZB_BEGIN_TIMEOUT_DEFAULT 30000 // 30 seconds
#define ZIGBEE_DEFAULT_ED_CONFIG() \
{ \
.esp_zb_role = ESP_ZB_DEVICE_TYPE_ED, .install_code_policy = false, \
.nwk_cfg = { \
.zed_cfg = \
{ \
.ed_timeout = ESP_ZB_ED_AGING_TIMEOUT_64MIN, \
.keep_alive = 3000, \
}, \
}, \
}
#define ZIGBEE_DEFAULT_ROUTER_CONFIG() \
{ \
.esp_zb_role = ESP_ZB_DEVICE_TYPE_ROUTER, .install_code_policy = false, .nwk_cfg = { \
.zczr_cfg = \
{ \
.max_children = 10, \
}, \
} \
}
#define ZIGBEE_DEFAULT_COORDINATOR_CONFIG() \
{ \
.esp_zb_role = ESP_ZB_DEVICE_TYPE_COORDINATOR, .install_code_policy = false, .nwk_cfg = { \
.zczr_cfg = \
{ \
.max_children = 10, \
}, \
} \
}
#define ZIGBEE_DEFAULT_UART_RCP_RADIO_CONFIG() \
{ \
.radio_mode = ZB_RADIO_MODE_UART_RCP, \
.radio_uart_config = { \
.port = UART_NUM_1, \
.rx_pin = GPIO_NUM_NC, \
.tx_pin = GPIO_NUM_NC, \
.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, \
}, \
}, \
}
class ZigbeeCore {
private:
esp_zb_radio_config_t _radio_config;
esp_zb_host_config_t _host_config;
uint32_t _primary_channel_mask;
uint32_t _begin_timeout;
int16_t _scan_status;
uint8_t _scan_duration;
bool _rx_on_when_idle;
esp_zb_ep_list_t *_zb_ep_list;
zigbee_role_t _role;
bool _started;
bool _connected;
uint8_t _open_network;
zigbee_scan_result_t *_scan_result;
SemaphoreHandle_t lock;
bool _debug;
bool _allow_multi_endpoint_binding;
// Global default response callback
void (*_global_default_response_cb)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster);
bool zigbeeInit(esp_zb_cfg_t *zb_cfg, bool erase_nvs);
static void scanCompleteCallback(esp_zb_zdp_status_t zdo_status, uint8_t count, esp_zb_network_descriptor_t *nwk_descriptor);
const char *getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId);
void searchBindings();
static void bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx);
void resetNVRAMChannelMask(); // Reset to default mask also in NVRAM
void setNVRAMChannelMask(uint32_t mask); // Set channel mask in NVRAM
public:
ZigbeeCore();
~ZigbeeCore() {}
std::list<ZigbeeEP *> ep_objects;
bool begin(zigbee_role_t role = ZIGBEE_END_DEVICE, bool erase_nvs = false);
bool begin(esp_zb_cfg_t *role_cfg, bool erase_nvs = false);
// bool end();
void stop();
void start();
bool started() {
return _started;
}
bool connected() {
return _connected;
}
zigbee_role_t getRole() {
return _role;
}
bool addEndpoint(ZigbeeEP *ep);
//void removeEndpoint(ZigbeeEP *ep);
void setRadioConfig(esp_zb_radio_config_t config);
esp_zb_radio_config_t getRadioConfig();
void setHostConfig(esp_zb_host_config_t config);
esp_zb_host_config_t getHostConfig();
void setPrimaryChannelMask(uint32_t mask); // By default all channels are scanned (11-26) -> mask 0x07FFF800
void setScanDuration(uint8_t duration); // Can be set from 1 - 4. 1 is fastest, 4 is slowest
uint8_t getScanDuration() {
return _scan_duration;
}
void setRxOnWhenIdle(bool rx_on_when_idle) {
_rx_on_when_idle = rx_on_when_idle;
}
bool getRxOnWhenIdle() {
return _rx_on_when_idle;
}
void setTimeout(uint32_t timeout) {
_begin_timeout = timeout;
}
void setRebootOpenNetwork(uint8_t time);
void openNetwork(uint8_t time);
void closeNetwork();
//scan_duration Time spent scanning each channel, in units of ((1 << scan_duration) + 1) * a beacon time. (15.36 microseconds)
void scanNetworks(uint32_t channel_mask = ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK, uint8_t scan_duration = 5);
// Zigbee scan complete status check, -2: failed or not started, -1: running, 0: no networks found, >0: number of networks found
int16_t scanComplete();
zigbee_scan_result_t *getScanResult();
void scanDelete();
void factoryReset(bool restart = true);
void setDebugMode(bool debug) {
_debug = debug;
}
bool getDebugMode() {
return _debug;
}
void allowMultiEndpointBinding(bool allow) {
_allow_multi_endpoint_binding = allow;
}
bool allowMultiEndpointBinding() {
return _allow_multi_endpoint_binding;
}
// Set global default response callback
void onGlobalDefaultResponse(void (*callback)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster)) {
_global_default_response_cb = callback;
}
// Call global default response callback (for internal use)
void callDefaultResponseCallback(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster);
// Friend function declaration to allow access to private members
friend void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct);
friend bool zb_apsde_data_indication_handler(esp_zb_apsde_data_ind_t ind);
// Helper functions for formatting addresses
static inline const char *formatIEEEAddress(const esp_zb_ieee_addr_t addr) {
static char buf[24];
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", addr[7], addr[6], addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
return buf;
}
static inline const char *formatShortAddress(uint16_t addr) {
static char buf[7];
snprintf(buf, sizeof(buf), "0x%04X", addr);
return buf;
}
};
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ZIGBEE)
extern ZigbeeCore Zigbee;
#endif
#endif // CONFIG_ZB_ENABLED
+709
View File
@@ -0,0 +1,709 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* Common Class for Zigbee End Point */
#include "ZigbeeEP.h"
#if CONFIG_ZB_ENABLED
#include "esp_zigbee_cluster.h"
#include "zcl/esp_zigbee_zcl_power_config.h"
/* Zigbee End Device Class */
ZigbeeEP::ZigbeeEP(uint8_t endpoint) {
_endpoint = endpoint;
log_v("Endpoint: %d", _endpoint);
_ep_config.endpoint = 0;
_cluster_list = nullptr;
_on_identify = nullptr;
_on_ota_state_change = nullptr;
_on_default_response = nullptr;
_read_model = NULL;
_read_manufacturer = NULL;
_time_status = 0;
_is_bound = false;
_use_manual_binding = false;
_allow_multiple_binding = false;
if (!lock) {
lock = xSemaphoreCreateBinary();
if (lock == NULL) {
log_e("Semaphore creation failed");
}
}
}
void ZigbeeEP::setVersion(uint8_t version) {
_ep_config.app_device_version = version;
esp_zb_attribute_list_t *basic_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BASIC, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (basic_cluster == nullptr) {
log_e("Failed to get basic cluster for application version");
return;
}
esp_err_t ret = esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_APPLICATION_VERSION_ID, (void *)&version);
if (ret != ESP_OK) {
log_e("Failed to add application version to basic cluster: 0x%x: %s", ret, esp_err_to_name(ret));
}
}
void ZigbeeEP::setHardwareVersion(uint8_t version) {
esp_zb_attribute_list_t *basic_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BASIC, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (basic_cluster == nullptr) {
log_e("Failed to get basic cluster for hardware version");
return;
}
esp_err_t ret = esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_HW_VERSION_ID, (void *)&version);
if (ret != ESP_OK) {
log_e("Failed to add hardware version to basic cluster: 0x%x: %s", ret, esp_err_to_name(ret));
}
}
bool ZigbeeEP::setManufacturerAndModel(const char *name, const char *model) {
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
char zb_name[ZB_MAX_NAME_LENGTH + 2];
char zb_model[ZB_MAX_NAME_LENGTH + 2];
// Convert manufacturer to ZCL string
size_t name_length = strlen(name);
size_t model_length = strlen(model);
if (name_length > ZB_MAX_NAME_LENGTH || model_length > ZB_MAX_NAME_LENGTH) {
log_e("Manufacturer or model name is too long");
return false;
}
// Get and check the basic cluster
esp_zb_attribute_list_t *basic_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BASIC, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (basic_cluster == nullptr) {
log_e("Failed to get basic cluster");
return false;
}
// Store the length as the first element
zb_name[0] = static_cast<char>(name_length); // Cast size_t to char
zb_model[0] = static_cast<char>(model_length);
// Use memcpy to copy the characters to the result array
memcpy(zb_name + 1, name, name_length);
memcpy(zb_model + 1, model, model_length);
// Null-terminate the array
zb_name[name_length + 1] = '\0';
zb_model[model_length + 1] = '\0';
// Update the manufacturer and model attributes
esp_err_t ret_name = esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, (void *)zb_name);
if (ret_name != ESP_OK) {
log_e("Failed to set manufacturer: 0x%x: %s", ret_name, esp_err_to_name(ret_name));
}
esp_err_t ret_model = esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, (void *)zb_model);
if (ret_model != ESP_OK) {
log_e("Failed to set model: 0x%x: %s", ret_model, esp_err_to_name(ret_model));
}
return ret_name == ESP_OK && ret_model == ESP_OK;
}
bool ZigbeeEP::setPowerSource(zb_power_source_t power_source, uint8_t battery_percentage, uint8_t battery_voltage) {
esp_zb_attribute_list_t *basic_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BASIC, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_err_t ret = esp_zb_cluster_update_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_POWER_SOURCE_ID, (void *)&power_source);
if (ret != ESP_OK) {
log_e("Failed to set power source: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
if (power_source == ZB_POWER_SOURCE_BATTERY) {
// Add power config cluster and battery percentage attribute
if (battery_percentage > 100) {
battery_percentage = 100;
}
battery_percentage = battery_percentage * 2;
esp_zb_attribute_list_t *power_config_cluster = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG);
ret = esp_zb_power_config_cluster_add_attr(power_config_cluster, ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID, (void *)&battery_percentage);
if (ret != ESP_OK) {
log_e("Failed to add battery percentage attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_power_config_cluster_add_attr(power_config_cluster, ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_ID, (void *)&battery_voltage);
if (ret != ESP_OK) {
log_e("Failed to add battery voltage attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_cluster_list_add_power_config_cluster(_cluster_list, power_config_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (ret != ESP_OK) {
log_e("Failed to add power config cluster: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
}
_power_source = power_source;
return true;
}
bool ZigbeeEP::setBatteryPercentage(uint8_t percentage) {
// 100% = 200 in decimal, 0% = 0
// Convert percentage to 0-200 range
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
if (percentage > 100) {
percentage = 100;
}
percentage = percentage * 2;
esp_zb_lock_acquire(portMAX_DELAY);
ret = esp_zb_zcl_set_attribute_val(
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID, &percentage,
false
);
esp_zb_lock_release();
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Failed to set battery percentage: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
return false;
}
log_v("Battery percentage updated");
return true;
}
bool ZigbeeEP::setBatteryVoltage(uint8_t voltage) {
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
esp_zb_lock_acquire(portMAX_DELAY);
ret = esp_zb_zcl_set_attribute_val(
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_VOLTAGE_ID, &voltage, false
);
esp_zb_lock_release();
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Failed to set battery voltage: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
return false;
}
log_v("Battery voltage updated");
return true;
}
bool ZigbeeEP::reportBatteryPercentage() {
/* Send report attributes command */
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
memset(&report_attr_cmd, 0, sizeof(report_attr_cmd));
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID;
report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG;
report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
esp_zb_lock_acquire(portMAX_DELAY);
esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
esp_zb_lock_release();
if (ret != ESP_OK) {
log_e("Failed to report battery percentage: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
log_v("Battery percentage reported");
return true;
}
char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
/* Read peer Manufacture Name & Model Identifier */
esp_zb_zcl_read_attr_cmd_t read_req;
memset(&read_req, 0, sizeof(read_req));
if (short_addr != 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
} else {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
}
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BASIC;
uint16_t attributes[] = {
ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID,
};
read_req.attr_number = ZB_ARRAY_LENGHT(attributes);
read_req.attr_field = attributes;
if (_read_manufacturer != NULL) {
free(_read_manufacturer);
}
_read_manufacturer = NULL;
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_read_attr_cmd_req(&read_req);
esp_zb_lock_release();
//Wait for response or timeout
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
log_e("Error while reading manufacturer");
}
return _read_manufacturer;
}
char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
/* Read peer Manufacture Name & Model Identifier */
esp_zb_zcl_read_attr_cmd_t read_req;
memset(&read_req, 0, sizeof(read_req));
if (short_addr != 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
} else {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
}
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BASIC;
uint16_t attributes[] = {
ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID,
};
read_req.attr_number = ZB_ARRAY_LENGHT(attributes);
read_req.attr_field = attributes;
if (_read_model != NULL) {
free(_read_model);
}
_read_model = NULL;
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_read_attr_cmd_req(&read_req);
esp_zb_lock_release();
//Wait for response or timeout
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
log_e("Error while reading model");
}
return _read_model;
}
void ZigbeeEP::printBoundDevices() {
log_i("Bound devices:");
for ([[maybe_unused]]
const auto &device : _bound_devices) {
log_i(
"Device on endpoint %d, short address: 0x%x, ieee address: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", device->endpoint, device->short_addr,
device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1],
device->ieee_addr[0]
);
}
}
void ZigbeeEP::printBoundDevices(Print &print) {
print.println("Bound devices:");
for ([[maybe_unused]]
const auto &device : _bound_devices) {
print.printf(
"Device on endpoint %d, short address: 0x%x, ieee address: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\r\n", device->endpoint, device->short_addr,
device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1],
device->ieee_addr[0]
);
}
}
void ZigbeeEP::zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute) {
/* Basic cluster attributes */
if (attribute->id == ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING && attribute->data.value) {
zbstring_t *zbstr = (zbstring_t *)attribute->data.value;
_read_manufacturer = (char *)malloc(zbstr->len + 1);
if (_read_manufacturer == NULL) {
log_e("Failed to allocate memory for manufacturer data");
xSemaphoreGive(lock);
return;
}
memcpy(_read_manufacturer, zbstr->data, zbstr->len);
_read_manufacturer[zbstr->len] = '\0';
log_i("Peer Manufacturer is \"%s\"", _read_manufacturer);
xSemaphoreGive(lock);
}
if (attribute->id == ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING && attribute->data.value) {
zbstring_t *zbstr = (zbstring_t *)attribute->data.value;
_read_model = (char *)malloc(zbstr->len + 1);
if (_read_model == NULL) {
log_e("Failed to allocate memory for model data");
xSemaphoreGive(lock);
return;
}
memcpy(_read_model, zbstr->data, zbstr->len);
_read_model[zbstr->len] = '\0';
log_i("Peer Model is \"%s\"", _read_model);
xSemaphoreGive(lock);
}
}
void ZigbeeEP::zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message) {
if (message->attribute.id == ESP_ZB_ZCL_CMD_IDENTIFY_IDENTIFY_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) {
if (_on_identify != NULL) {
_on_identify(*(uint16_t *)message->attribute.data.value);
}
} else {
log_w("Other identify commands are not implemented yet.");
}
}
void ZigbeeEP::zbOTAState(bool otaActive) {
if (_on_ota_state_change != NULL) {
_on_ota_state_change(otaActive);
}
}
bool ZigbeeEP::addTimeCluster(tm time, int32_t gmt_offset) {
time_t utc_time = 0;
// Check if time is set
if (time.tm_year > 0) {
// Convert time to UTC
utc_time = mktime(&time);
}
// Create time cluster server attributes
esp_zb_attribute_list_t *time_cluster_server = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_TIME);
esp_err_t ret = esp_zb_time_cluster_add_attr(time_cluster_server, ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID, (void *)&gmt_offset);
if (ret != ESP_OK) {
log_e("Failed to add time zone attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_time_cluster_add_attr(time_cluster_server, ESP_ZB_ZCL_ATTR_TIME_TIME_ID, (void *)&utc_time);
if (ret != ESP_OK) {
log_e("Failed to add time attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_time_cluster_add_attr(time_cluster_server, ESP_ZB_ZCL_ATTR_TIME_TIME_STATUS_ID, (void *)&_time_status);
if (ret != ESP_OK) {
log_e("Failed to add time status attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
// Create time cluster client attributes
esp_zb_attribute_list_t *time_cluster_client = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_TIME);
// Add time clusters to cluster list
ret = esp_zb_cluster_list_add_time_cluster(_cluster_list, time_cluster_server, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (ret != ESP_OK) {
log_e("Failed to add time cluster (server role): 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_cluster_list_add_time_cluster(_cluster_list, time_cluster_client, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
if (ret != ESP_OK) {
log_e("Failed to add time cluster (client role): 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
bool ZigbeeEP::setTime(tm time) {
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
time_t utc_time = mktime(&time);
log_d("Setting time to %lld", utc_time);
esp_zb_lock_acquire(portMAX_DELAY);
ret = esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_ID, &utc_time, false);
esp_zb_lock_release();
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Failed to set time: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
return false;
}
return true;
}
bool ZigbeeEP::setTimezone(int32_t gmt_offset) {
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
log_d("Setting timezone to %d", gmt_offset);
esp_zb_lock_acquire(portMAX_DELAY);
ret =
esp_zb_zcl_set_attribute_val(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID, &gmt_offset, false);
esp_zb_lock_release();
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Failed to set timezone: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
return false;
}
return true;
}
tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
/* Read peer time */
esp_zb_zcl_read_attr_cmd_t read_req;
memset(&read_req, 0, sizeof(read_req));
if (short_addr >= 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
read_req.zcl_basic_cmd.dst_addr_u.addr_short = (uint16_t)short_addr;
} else {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
}
uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ID};
read_req.attr_number = ZB_ARRAY_LENGHT(attributes);
read_req.attr_field = attributes;
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME;
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
// clear read time
_read_time = 0;
log_v("Reading time from endpoint %d", endpoint);
esp_zb_zcl_read_attr_cmd_req(&read_req);
//Wait for response or timeout
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
log_e("Error while reading time");
return tm();
}
struct tm *timeinfo = localtime(&_read_time);
if (timeinfo) {
// Update time
setTime(*timeinfo);
// Update time status to synced
_time_status |= 0x02;
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_set_attribute_val(
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_TIME, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_TIME_TIME_STATUS_ID, &_time_status, false
);
esp_zb_lock_release();
return *timeinfo;
} else {
log_e("Error while converting time");
return tm();
}
}
int32_t ZigbeeEP::getTimezone(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
/* Read peer timezone */
esp_zb_zcl_read_attr_cmd_t read_req;
memset(&read_req, 0, sizeof(read_req));
if (short_addr >= 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
read_req.zcl_basic_cmd.dst_addr_u.addr_short = (uint16_t)short_addr;
} else {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
}
uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID};
read_req.attr_number = ZB_ARRAY_LENGHT(attributes);
read_req.attr_field = attributes;
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME;
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
// clear read timezone
_read_timezone = 0;
log_v("Reading timezone from endpoint %d", endpoint);
esp_zb_zcl_read_attr_cmd_req(&read_req);
//Wait for response or timeout
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
log_e("Error while reading timezone");
return 0;
}
setTimezone(_read_timezone);
return _read_timezone;
}
void ZigbeeEP::zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute) {
/* Time cluster attributes */
if (attribute->id == ESP_ZB_ZCL_ATTR_TIME_TIME_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_UTC_TIME) {
log_v("Time attribute received");
log_v("Time: %lld", *(uint32_t *)attribute->data.value);
_read_time = *(uint32_t *)attribute->data.value;
xSemaphoreGive(lock);
} else if (attribute->id == ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_S32) {
log_v("Timezone attribute received");
log_v("Timezone: %d", *(int32_t *)attribute->data.value);
_read_timezone = *(int32_t *)attribute->data.value;
xSemaphoreGive(lock);
}
}
// typedef struct esp_zb_ota_cluster_cfg_s {
// uint32_t ota_upgrade_file_version; /*!< The attribute indicates the file version of the running firmware image on the device */
// uint16_t ota_upgrade_manufacturer; /*!< The attribute indicates the value for the manufacturer of the device */
// uint16_t ota_upgrade_image_type; /*!< The attribute indicates the the image type of the file that the client is currently downloading */
// uint32_t ota_upgrade_downloaded_file_ver; /*!< The attribute indicates the file version of the downloaded image on the device*/
// esp_zb_ota_cluster_cfg_t;
// typedef struct esp_zb_zcl_ota_upgrade_client_variable_s {
// uint16_t timer_query; /*!< The field indicates the time of querying OTA image for OTA upgrade client */
// uint16_t hw_version; /*!< The hardware version */
// uint8_t max_data_size; /*!< The maximum size of OTA data */
// } esp_zb_zcl_ota_upgrade_client_variable_t;
bool ZigbeeEP::addOTAClient(
uint32_t file_version, uint32_t downloaded_file_ver, uint16_t hw_version, uint16_t manufacturer, uint16_t image_type, uint8_t max_data_size
) {
esp_zb_ota_cluster_cfg_t ota_cluster_cfg = {};
ota_cluster_cfg.ota_upgrade_file_version = file_version; //OTA_UPGRADE_RUNNING_FILE_VERSION;
ota_cluster_cfg.ota_upgrade_downloaded_file_ver = downloaded_file_ver; //OTA_UPGRADE_DOWNLOADED_FILE_VERSION;
ota_cluster_cfg.ota_upgrade_manufacturer = manufacturer; //OTA_UPGRADE_MANUFACTURER;
ota_cluster_cfg.ota_upgrade_image_type = image_type; //OTA_UPGRADE_IMAGE_TYPE;
esp_zb_attribute_list_t *ota_cluster = esp_zb_ota_cluster_create(&ota_cluster_cfg);
esp_zb_zcl_ota_upgrade_client_variable_t variable_config = {};
variable_config.timer_query = ESP_ZB_ZCL_OTA_UPGRADE_QUERY_TIMER_COUNT_DEF;
variable_config.hw_version = hw_version; //OTA_UPGRADE_HW_VERSION;
variable_config.max_data_size = max_data_size; //OTA_UPGRADE_MAX_DATA_SIZE;
uint16_t ota_upgrade_server_addr = 0xffff;
uint8_t ota_upgrade_server_ep = 0xff;
esp_err_t ret = esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_CLIENT_DATA_ID, (void *)&variable_config);
if (ret != ESP_OK) {
log_e("Failed to add OTA client data: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_SERVER_ADDR_ID, (void *)&ota_upgrade_server_addr);
if (ret != ESP_OK) {
log_e("Failed to add OTA server address: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_SERVER_ENDPOINT_ID, (void *)&ota_upgrade_server_ep);
if (ret != ESP_OK) {
log_e("Failed to add OTA server endpoint: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_cluster_list_add_ota_cluster(_cluster_list, ota_cluster, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
if (ret != ESP_OK) {
log_e("Failed to add OTA cluster: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
static void findOTAServer(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_zb_ota_upgrade_client_query_interval_set(*((uint8_t *)user_ctx), OTA_UPGRADE_QUERY_INTERVAL);
esp_zb_ota_upgrade_client_query_image_req(addr, endpoint);
log_i("Query OTA upgrade from server endpoint: %d after %d seconds", endpoint, OTA_UPGRADE_QUERY_INTERVAL);
} else {
log_w("No OTA Server found");
}
}
void ZigbeeEP::requestOTAUpdate() {
esp_zb_zdo_match_desc_req_param_t req;
memset(&req, 0, sizeof(req));
uint16_t cluster_list[] = {ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE};
/* Match the OTA server of coordinator */
req.addr_of_interest = 0x0000;
req.dst_nwk_addr = 0x0000;
req.num_in_clusters = 1;
req.num_out_clusters = 0;
req.profile_id = ESP_ZB_AF_HA_PROFILE_ID;
req.cluster_list = cluster_list;
esp_zb_lock_acquire(portMAX_DELAY);
if (esp_zb_bdb_dev_joined()) {
esp_zb_zdo_match_cluster(&req, findOTAServer, &_endpoint);
}
esp_zb_lock_release();
}
void ZigbeeEP::removeBoundDevice(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) {
log_d(
"Attempting to remove device with endpoint %d and IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], ieee_addr[5],
ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0]
);
for (std::list<zb_device_params_t *>::iterator it = _bound_devices.begin(); it != _bound_devices.end(); ++it) {
if ((*it)->endpoint == endpoint && memcmp((*it)->ieee_addr, ieee_addr, sizeof(esp_zb_ieee_addr_t)) == 0) {
log_d("Found matching device, removing it");
_bound_devices.erase(it);
if (_bound_devices.empty()) {
_is_bound = false;
}
return;
}
}
log_w("No matching device found for removal");
}
void ZigbeeEP::removeBoundDevice(zb_device_params_t *device) {
if (!device) {
log_e("Invalid device parameters provided");
return;
}
log_d(
"Attempting to remove device with endpoint %d, short address 0x%04x, IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", device->endpoint,
device->short_addr, device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2],
device->ieee_addr[1], device->ieee_addr[0]
);
for (std::list<zb_device_params_t *>::iterator it = _bound_devices.begin(); it != _bound_devices.end(); ++it) {
bool endpoint_matches = ((*it)->endpoint == device->endpoint);
bool short_addr_matches = (device->short_addr != 0xFFFF && (*it)->short_addr == device->short_addr);
bool ieee_addr_matches = (memcmp((*it)->ieee_addr, device->ieee_addr, sizeof(esp_zb_ieee_addr_t)) == 0);
if (endpoint_matches && (short_addr_matches || ieee_addr_matches)) {
log_d("Found matching device by %s, removing it", short_addr_matches ? "short address" : "IEEE address");
_bound_devices.erase(it);
if (_bound_devices.empty()) {
_is_bound = false;
}
return;
}
}
log_w("No matching device found for removal");
}
void ZigbeeEP::zbDefaultResponse(const esp_zb_zcl_cmd_default_resp_message_t *message) {
log_v("Default response received for endpoint %d", _endpoint);
log_v("Status code: %s", esp_zb_zcl_status_to_name(message->status_code));
log_v("Response to command: %d", message->resp_to_cmd);
if (_on_default_response) {
_on_default_response((zb_cmd_type_t)message->resp_to_cmd, message->status_code);
}
}
// Global function implementation
const char *esp_zb_zcl_status_to_name(esp_zb_zcl_status_t status) {
switch (status) {
case ESP_ZB_ZCL_STATUS_SUCCESS: return "Success";
case ESP_ZB_ZCL_STATUS_FAIL: return "Fail";
case ESP_ZB_ZCL_STATUS_NOT_AUTHORIZED: return "Not authorized";
case ESP_ZB_ZCL_STATUS_MALFORMED_CMD: return "Malformed command";
case ESP_ZB_ZCL_STATUS_UNSUP_CLUST_CMD: return "Unsupported cluster command";
case ESP_ZB_ZCL_STATUS_UNSUP_GEN_CMD: return "Unsupported general command";
case ESP_ZB_ZCL_STATUS_UNSUP_MANUF_CLUST_CMD: return "Unsupported manufacturer cluster command";
case ESP_ZB_ZCL_STATUS_UNSUP_MANUF_GEN_CMD: return "Unsupported manufacturer general command";
case ESP_ZB_ZCL_STATUS_INVALID_FIELD: return "Invalid field";
case ESP_ZB_ZCL_STATUS_UNSUP_ATTRIB: return "Unsupported attribute";
case ESP_ZB_ZCL_STATUS_INVALID_VALUE: return "Invalid value";
case ESP_ZB_ZCL_STATUS_READ_ONLY: return "Read only";
case ESP_ZB_ZCL_STATUS_INSUFF_SPACE: return "Insufficient space";
case ESP_ZB_ZCL_STATUS_DUPE_EXISTS: return "Duplicate exists";
case ESP_ZB_ZCL_STATUS_NOT_FOUND: return "Not found";
case ESP_ZB_ZCL_STATUS_UNREPORTABLE_ATTRIB: return "Unreportable attribute";
case ESP_ZB_ZCL_STATUS_INVALID_TYPE: return "Invalid type";
case ESP_ZB_ZCL_STATUS_WRITE_ONLY: return "Write only";
case ESP_ZB_ZCL_STATUS_INCONSISTENT: return "Inconsistent";
case ESP_ZB_ZCL_STATUS_ACTION_DENIED: return "Action denied";
case ESP_ZB_ZCL_STATUS_TIMEOUT: return "Timeout";
case ESP_ZB_ZCL_STATUS_ABORT: return "Abort";
case ESP_ZB_ZCL_STATUS_INVALID_IMAGE: return "Invalid OTA upgrade image";
case ESP_ZB_ZCL_STATUS_WAIT_FOR_DATA: return "Server does not have data block available yet";
case ESP_ZB_ZCL_STATUS_NO_IMAGE_AVAILABLE: return "No image available";
case ESP_ZB_ZCL_STATUS_REQUIRE_MORE_IMAGE: return "Require more image";
case ESP_ZB_ZCL_STATUS_NOTIFICATION_PENDING: return "Notification pending";
case ESP_ZB_ZCL_STATUS_HW_FAIL: return "Hardware failure";
case ESP_ZB_ZCL_STATUS_SW_FAIL: return "Software failure";
case ESP_ZB_ZCL_STATUS_CALIB_ERR: return "Calibration error";
case ESP_ZB_ZCL_STATUS_UNSUP_CLUST: return "Cluster is not found on the target endpoint";
case ESP_ZB_ZCL_STATUS_LIMIT_REACHED: return "Limit reached";
default: return "Unknown status";
}
}
#endif // CONFIG_ZB_ENABLED
+219
View File
@@ -0,0 +1,219 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* Common Class for Zigbee End point */
#pragma once
#include "ZigbeeCore.h"
#if CONFIG_ZB_ENABLED
#include <Arduino.h>
#include <ColorFormat.h>
/* Useful defines */
#define ZB_CMD_TIMEOUT 10000 // 10 seconds
#define OTA_UPGRADE_QUERY_INTERVAL (1 * 60) // 1 hour = 60 minutes
#define ZB_ARRAY_LENGHT(arr) (sizeof(arr) / sizeof(arr[0]))
#define RGB_TO_XYZ(r, g, b, X, Y, Z) \
{ \
X = (float)(0.412453 * (r) + 0.357580 * (g) + 0.180423 * (b)); \
Y = (float)(0.212671 * (r) + 0.715160 * (g) + 0.072169 * (b)); \
Z = (float)(0.019334 * (r) + 0.119193 * (g) + 0.950227 * (b)); \
}
typedef struct zbstring_s {
uint8_t len;
char data[];
} ESP_ZB_PACKED_STRUCT zbstring_t;
typedef struct zb_device_params_s {
esp_zb_ieee_addr_t ieee_addr;
uint8_t endpoint;
uint16_t short_addr;
} zb_device_params_t;
typedef enum {
ZB_POWER_SOURCE_UNKNOWN = 0x00,
ZB_POWER_SOURCE_MAINS = 0x01,
ZB_POWER_SOURCE_BATTERY = 0x03,
} zb_power_source_t;
// Global function for converting ZCL status to name
const char *esp_zb_zcl_status_to_name(esp_zb_zcl_status_t status);
/* Zigbee End Device Class */
class ZigbeeEP {
public:
// constants and limits
static constexpr size_t ZB_MAX_NAME_LENGTH = 32;
// constructors and destructor
ZigbeeEP(uint8_t endpoint = 10);
~ZigbeeEP() {}
// Set ep config and cluster list
void setEpConfig(esp_zb_endpoint_config_t ep_config, esp_zb_cluster_list_t *cluster_list) {
_ep_config = ep_config;
_cluster_list = cluster_list;
}
// Set application version and hardware version
void setVersion(uint8_t version);
void setHardwareVersion(uint8_t version);
uint8_t getEndpoint() {
return _endpoint;
}
void printBoundDevices();
void printBoundDevices(Print &print);
std::list<zb_device_params_t *> getBoundDevices() const {
return _bound_devices;
}
bool bound() {
return _is_bound;
}
void allowMultipleBinding(bool bind) {
_allow_multiple_binding = bind;
}
void setManualBinding(bool bind) {
_use_manual_binding = bind;
}
// Set Manufacturer name and model
bool setManufacturerAndModel(const char *name, const char *model);
// Methods to read manufacturer and model name from selected endpoint and short address
char *readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr);
char *readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr);
// Set Power source and battery percentage for battery powered devices
bool setPowerSource(zb_power_source_t power_source, uint8_t percentage = 0xff, uint8_t voltage = 0xff); // voltage in 100mV
bool setBatteryPercentage(uint8_t percentage); // 0-100 %
bool setBatteryVoltage(uint8_t voltage); // voltage in 100mV (example value 35 for 3.5V)
bool reportBatteryPercentage(); // battery voltage is not reportable attribute
// Set time
bool addTimeCluster(tm time = {}, int32_t gmt_offset = 0); // gmt offset in seconds
bool setTime(tm time);
bool setTimezone(int32_t gmt_offset);
// Get time from Coordinator or specific endpoint (blocking until response)
struct tm getTime(uint8_t endpoint = 1, int32_t short_addr = 0x0000, esp_zb_ieee_addr_t ieee_addr = {0});
int32_t getTimezone(uint8_t endpoint = 1, int32_t short_addr = 0x0000, esp_zb_ieee_addr_t ieee_addr = {0}); // gmt offset in seconds
bool epAllowMultipleBinding() {
return _allow_multiple_binding;
}
bool epUseManualBinding() {
return _use_manual_binding;
}
// OTA methods
/**
* @brief Add OTA client to the Zigbee endpoint.
*
* @param file_version The current file version of the OTA client.
* @param downloaded_file_ver The version of the downloaded file.
* @param hw_version The hardware version of the device.
* @param manufacturer The manufacturer code (default: 0x1001).
* @param image_type The image type code (default: 0x1011).
* @param max_data_size The maximum data size for OTA transfer (default and recommended: 223).
* @return true if the OTA client was added successfully, false otherwise.
*/
bool addOTAClient(
uint32_t file_version, uint32_t downloaded_file_ver, uint16_t hw_version, uint16_t manufacturer = 0x1001, uint16_t image_type = 0x1011,
uint8_t max_data_size = 223
);
/**
* @brief Request OTA update from the server, first request is within a minute and the next requests are sent every hour automatically.
*/
void requestOTAUpdate();
// findEndpoint may be implemented by EPs to find and bind devices
virtual void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req) {};
// list of all handlers function calls, to be override by EPs implementation
virtual void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {};
virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {};
virtual void
zbWriteAttributeResponse(uint16_t cluster_id, uint16_t attribute_id, esp_zb_zcl_status_t status, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {};
virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented
virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message);
virtual void zbOTAState(bool otaActive);
virtual void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) {};
virtual void zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented
virtual void zbIASZoneStatusChangeNotification(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message) {};
virtual void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) {};
virtual void zbDefaultResponse(const esp_zb_zcl_cmd_default_resp_message_t *message); //already implemented
virtual void addBoundDevice(zb_device_params_t *device) {
_bound_devices.push_back(device);
_is_bound = true;
}
virtual void removeBoundDevice(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr);
virtual void removeBoundDevice(zb_device_params_t *device);
virtual void clearBoundDevices() {
_bound_devices.clear();
_is_bound = false;
}
void onIdentify(void (*callback)(uint16_t)) {
_on_identify = callback;
}
void onOTAStateChange(void (*callback)(bool state)) {
_on_ota_state_change = callback;
}
void onDefaultResponse(void (*callback)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status)) {
_on_default_response = callback;
}
// Convert ZCL status to name
private:
char *_read_manufacturer;
char *_read_model;
void (*_on_identify)(uint16_t time);
void (*_on_ota_state_change)(bool state);
void (*_on_default_response)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status);
time_t _read_time;
int32_t _read_timezone;
protected:
uint8_t _endpoint;
esp_zb_ha_standard_devices_t _device_id;
esp_zb_endpoint_config_t _ep_config;
esp_zb_cluster_list_t *_cluster_list;
bool _is_bound;
bool _allow_multiple_binding;
bool _use_manual_binding;
std::list<zb_device_params_t *> _bound_devices;
SemaphoreHandle_t lock;
zb_power_source_t _power_source;
uint8_t _time_status;
// Friend class declaration to allow access to protected members
friend class ZigbeeCore;
};
#endif // CONFIG_ZB_ENABLED
+475
View File
@@ -0,0 +1,475 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* Zigbee Common Functions */
#include "ZigbeeCore.h"
#include "Arduino.h"
#if CONFIG_ZB_ENABLED
#include "esp_ota_ops.h"
#if CONFIG_ZB_DELTA_OTA // Delta OTA, code is prepared for this feature but not enabled by default
#include "esp_delta_ota_ops.h"
#endif
//OTA Upgrade defines and variables
#define OTA_ELEMENT_HEADER_LEN 6 /* OTA element format header size include tag identifier and length field */
/**
* @name Enumeration for the tag identifier denotes the type and format of the data within the element
* @anchor esp_ota_element_tag_id_t
*/
typedef enum esp_ota_element_tag_id_e {
UPGRADE_IMAGE = 0x0000, /*!< Upgrade image */
} esp_ota_element_tag_id_t;
static const esp_partition_t *s_ota_partition = NULL;
static esp_ota_handle_t s_ota_handle = 0;
static bool s_tagid_received = false;
// forward declaration of all implemented handlers
static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_message_t *message);
static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_message_t *message);
static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message);
static esp_err_t zb_cmd_write_attr_resp_handler(const esp_zb_zcl_cmd_write_attr_resp_message_t *message);
static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message);
static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message);
static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zone_enroll_response_message_t *message);
static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message);
static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_window_covering_movement_message_t *message);
static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_value_message_t *message);
static esp_err_t zb_ota_upgrade_query_image_resp_handler(const esp_zb_zcl_ota_upgrade_query_image_resp_message_t *message);
// Zigbee action handlers
[[maybe_unused]]
static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message) {
esp_err_t ret = ESP_OK;
switch (callback_id) {
case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID: ret = zb_attribute_set_handler((esp_zb_zcl_set_attr_value_message_t *)message); break;
case ESP_ZB_CORE_REPORT_ATTR_CB_ID: ret = zb_attribute_reporting_handler((esp_zb_zcl_report_attr_message_t *)message); break;
case ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID: ret = zb_cmd_read_attr_resp_handler((esp_zb_zcl_cmd_read_attr_resp_message_t *)message); break;
case ESP_ZB_CORE_CMD_REPORT_CONFIG_RESP_CB_ID: ret = zb_configure_report_resp_handler((esp_zb_zcl_cmd_config_report_resp_message_t *)message); break;
case ESP_ZB_CORE_CMD_IAS_ZONE_ZONE_STATUS_CHANGE_NOT_ID:
ret = zb_cmd_ias_zone_status_change_handler((esp_zb_zcl_ias_zone_status_change_notification_message_t *)message);
break;
case ESP_ZB_CORE_IAS_ZONE_ENROLL_RESPONSE_VALUE_CB_ID:
ret = zb_cmd_ias_zone_enroll_response_handler((esp_zb_zcl_ias_zone_enroll_response_message_t *)message);
break;
case ESP_ZB_CORE_WINDOW_COVERING_MOVEMENT_CB_ID:
ret = zb_window_covering_movement_resp_handler((esp_zb_zcl_window_covering_movement_message_t *)message);
break;
case ESP_ZB_CORE_OTA_UPGRADE_VALUE_CB_ID: ret = zb_ota_upgrade_status_handler((esp_zb_zcl_ota_upgrade_value_message_t *)message); break;
case ESP_ZB_CORE_OTA_UPGRADE_QUERY_IMAGE_RESP_CB_ID:
ret = zb_ota_upgrade_query_image_resp_handler((esp_zb_zcl_ota_upgrade_query_image_resp_message_t *)message);
break;
case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break;
case ESP_ZB_CORE_CMD_WRITE_ATTR_RESP_CB_ID: ret = zb_cmd_write_attr_resp_handler((esp_zb_zcl_cmd_write_attr_resp_message_t *)message); break;
default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break;
}
return ret;
}
static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_message_t *message) {
if (!message) {
log_e("Empty message");
return ESP_FAIL;
}
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Received message: error status(%d)", message->info.status);
return ESP_ERR_INVALID_ARG;
}
log_v(
"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
);
// List through all Zigbee EPs and call the callback function, with the message
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if (message->info.dst_endpoint == (*it)->getEndpoint()) {
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY) {
(*it)->zbIdentify(message); //method zbIdentify implemented in the common EP class
} else {
(*it)->zbAttributeSet(message); //method zbAttributeSet must be implemented in specific EP class
}
}
}
return ESP_OK;
}
static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_message_t *message) {
if (!message) {
log_e("Empty message");
return ESP_FAIL;
}
if (message->status != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Received message: error status(%d)", message->status);
return ESP_ERR_INVALID_ARG;
}
log_v(
"Received report from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->src_address.u.short_addr, message->src_endpoint,
message->dst_endpoint, message->cluster
);
// List through all Zigbee EPs and call the callback function, with the message
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if (message->dst_endpoint == (*it)->getEndpoint()) {
(*it)->zbAttributeRead(
message->cluster, &message->attribute, message->src_endpoint, message->src_address
); //method zbAttributeRead must be implemented in specific EP class
}
}
return ESP_OK;
}
static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message) {
if (!message) {
log_e("Empty message");
return ESP_FAIL;
}
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Received message: error status(%d)", message->info.status);
return ESP_ERR_INVALID_ARG;
}
log_v(
"Read attribute response: from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->info.src_address.u.short_addr,
message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster
);
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if (message->info.dst_endpoint == (*it)->getEndpoint()) {
esp_zb_zcl_read_attr_resp_variable_t *variable = message->variables;
while (variable) {
log_v(
"Read attribute response: status(%d), cluster(0x%x), attribute(0x%x), type(0x%x), value(%d)", variable->status, message->info.cluster,
variable->attribute.id, variable->attribute.data.type, variable->attribute.data.value ? *(uint8_t *)variable->attribute.data.value : 0
);
if (variable->status == ESP_ZB_ZCL_STATUS_SUCCESS) {
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_BASIC) {
(*it)->zbReadBasicCluster(&variable->attribute); //method zbReadBasicCluster implemented in the common EP class
} else if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_TIME) {
(*it)->zbReadTimeCluster(&variable->attribute); //method zbReadTimeCluster implemented in the common EP class
} else {
(*it)->zbAttributeRead(
message->info.cluster, &variable->attribute, message->info.src_endpoint, message->info.src_address
); //method zbAttributeRead must be implemented in specific EP class
}
}
variable = variable->next;
}
}
}
return ESP_OK;
}
static esp_err_t zb_cmd_write_attr_resp_handler(const esp_zb_zcl_cmd_write_attr_resp_message_t *message) {
if (!message) {
log_e("Empty message");
return ESP_FAIL;
}
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Received message: error status(%d)", message->info.status);
return ESP_ERR_INVALID_ARG;
}
log_v(
"Write attribute response: from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->info.src_address.u.short_addr,
message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster
);
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if (message->info.dst_endpoint == (*it)->getEndpoint()) {
esp_zb_zcl_write_attr_resp_variable_t *variable = message->variables;
while (variable) {
log_v("Write attribute response: status(%d), cluster(0x%x), attribute(0x%x)", variable->status, message->info.cluster, variable->attribute_id);
if (variable->status == ESP_ZB_ZCL_STATUS_SUCCESS) {
(*it)->zbWriteAttributeResponse(
message->info.cluster, variable->attribute_id, variable->status, message->info.src_endpoint, message->info.src_address
);
}
variable = variable->next;
}
}
}
return ESP_OK;
}
static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message) {
if (!message) {
log_e("Empty message");
return ESP_FAIL;
}
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Received message: error status(%d)", message->info.status);
return ESP_ERR_INVALID_ARG;
}
esp_zb_zcl_config_report_resp_variable_t *variable = message->variables;
while (variable) {
log_v(
"Configure report response: status(%d), cluster(0x%x), direction(0x%x), attribute(0x%x)", variable->status, message->info.cluster, variable->direction,
variable->attribute_id
);
variable = variable->next;
}
return ESP_OK;
}
static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message) {
if (!message) {
log_e("Empty message");
return ESP_FAIL;
}
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Received message: error status(%d)", message->info.status);
return ESP_ERR_INVALID_ARG;
}
log_v(
"IAS Zone Status Notification: from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->info.src_address.u.short_addr,
message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster
);
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if (message->info.dst_endpoint == (*it)->getEndpoint()) {
(*it)->zbIASZoneStatusChangeNotification(message);
}
}
return ESP_OK;
}
static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) {
if (!message) {
log_e("Empty message");
return ESP_FAIL;
}
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Received message: error status(%d)", message->info.status);
return ESP_ERR_INVALID_ARG;
}
log_v("IAS Zone Enroll Response received");
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if (message->info.dst_endpoint == (*it)->getEndpoint()) {
(*it)->zbIASZoneEnrollResponse(message);
}
}
return ESP_OK;
}
static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_window_covering_movement_message_t *message) {
if (!message) {
log_e("Empty message");
}
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Received message: error status(%d)", message->info.status);
}
log_v(
"Received message: endpoint(%d), cluster(0x%x), command(0x%x), payload(%d)", message->info.dst_endpoint, message->info.cluster, message->command,
message->payload
);
// List through all Zigbee EPs and call the callback function, with the message
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if (message->info.dst_endpoint == (*it)->getEndpoint()) {
(*it)->zbWindowCoveringMovementCmd(message); //method zbWindowCoveringMovementCmd must be implemented in specific EP class
}
}
return ESP_OK;
}
static esp_err_t esp_element_ota_data(uint32_t total_size, const void *payload, uint16_t payload_size, void **outbuf, uint16_t *outlen) {
static uint16_t tagid = 0;
void *data_buf = NULL;
uint16_t data_len;
if (!s_tagid_received) {
uint32_t length = 0;
if (!payload || payload_size <= OTA_ELEMENT_HEADER_LEN) {
log_e("Invalid element format");
return ESP_ERR_INVALID_ARG;
}
const uint8_t *payload_ptr = (const uint8_t *)payload;
tagid = *(const uint16_t *)payload_ptr;
length = *(const uint32_t *)(payload_ptr + sizeof(tagid));
if ((length + OTA_ELEMENT_HEADER_LEN) != total_size) {
log_e("Invalid element length [%ld/%ld]", length, total_size);
return ESP_ERR_INVALID_ARG;
}
s_tagid_received = true;
data_buf = (void *)(payload_ptr + OTA_ELEMENT_HEADER_LEN);
data_len = payload_size - OTA_ELEMENT_HEADER_LEN;
} else {
data_buf = (void *)payload;
data_len = payload_size;
}
switch (tagid) {
case UPGRADE_IMAGE:
*outbuf = data_buf;
*outlen = data_len;
break;
default:
log_e("Unsupported element tag identifier %d", tagid);
return ESP_ERR_INVALID_ARG;
break;
}
return ESP_OK;
}
static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_value_message_t *message) {
static uint32_t total_size = 0;
static uint32_t offset = 0;
[[maybe_unused]]
static int64_t start_time = 0;
esp_err_t ret = ESP_OK;
if (message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS) {
switch (message->upgrade_status) {
case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_START:
log_i("Zigbee - OTA upgrade start");
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
(*it)->zbOTAState(true); // Notify that OTA is active
}
start_time = esp_timer_get_time();
s_ota_partition = esp_ota_get_next_update_partition(NULL);
assert(s_ota_partition);
#if CONFIG_ZB_DELTA_OTA
ret = esp_delta_ota_begin(s_ota_partition, 0, &s_ota_handle);
#else
ret = esp_ota_begin(s_ota_partition, 0, &s_ota_handle);
#endif
if (ret != ESP_OK) {
log_e("Zigbee - Failed to begin OTA partition, status: %s", esp_err_to_name(ret));
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
(*it)->zbOTAState(false); // Notify that OTA is no longer active
}
return ret;
}
break;
case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_RECEIVE:
total_size = message->ota_header.image_size;
offset += message->payload_size;
log_i("Zigbee - OTA Client receives data: progress [%ld/%ld]", offset, total_size);
if (message->payload_size && message->payload) {
uint16_t payload_size = 0;
void *payload = NULL;
ret = esp_element_ota_data(total_size, message->payload, message->payload_size, &payload, &payload_size);
if (ret != ESP_OK) {
log_e("Zigbee - Failed to element OTA data, status: %s", esp_err_to_name(ret));
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
(*it)->zbOTAState(false); // Notify that OTA is no longer active
}
return ret;
}
#if CONFIG_ZB_DELTA_OTA
ret = esp_delta_ota_write(s_ota_handle, payload, payload_size);
#else
ret = esp_ota_write(s_ota_handle, (const void *)payload, payload_size);
#endif
if (ret != ESP_OK) {
log_e("Zigbee - Failed to write OTA data to partition, status: %s", esp_err_to_name(ret));
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
(*it)->zbOTAState(false); // Notify that OTA is no longer active
}
return ret;
}
}
break;
case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_APPLY: log_i("Zigbee - OTA upgrade apply"); break;
case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_CHECK:
ret = offset == total_size ? ESP_OK : ESP_FAIL;
offset = 0;
total_size = 0;
s_tagid_received = false;
log_i("Zigbee - OTA upgrade check status: %s", esp_err_to_name(ret));
break;
case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_FINISH:
log_i("Zigbee - OTA Finish");
log_i(
"Zigbee - OTA Information: version: 0x%lx, manufacturer code: 0x%x, image type: 0x%x, total size: %ld bytes, cost time: %lld ms,",
message->ota_header.file_version, message->ota_header.manufacturer_code, message->ota_header.image_type, message->ota_header.image_size,
(esp_timer_get_time() - start_time) / 1000
);
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
(*it)->zbOTAState(false); // Notify that OTA is no longer active
}
#if CONFIG_ZB_DELTA_OTA
ret = esp_delta_ota_end(s_ota_handle);
#else
ret = esp_ota_end(s_ota_handle);
#endif
if (ret != ESP_OK) {
log_e("Zigbee - Failed to end OTA partition, status: %s", esp_err_to_name(ret));
return ret;
}
ret = esp_ota_set_boot_partition(s_ota_partition);
if (ret != ESP_OK) {
log_e("Zigbee - Failed to set OTA boot partition, status: %s", esp_err_to_name(ret));
return ret;
}
log_w("Zigbee - Prepare to restart system");
esp_restart();
break;
default: log_i("Zigbee - OTA status: %d", message->upgrade_status); break;
}
}
return ret;
}
static esp_err_t zb_ota_upgrade_query_image_resp_handler(const esp_zb_zcl_ota_upgrade_query_image_resp_message_t *message) {
if (message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS) {
log_i("Zigbee - Queried OTA image from address: 0x%04hx, endpoint: %d", message->server_addr.u.short_addr, message->server_endpoint);
log_i("Zigbee - Image version: 0x%lx, manufacturer code: 0x%x, image size: %ld", message->file_version, message->manufacturer_code, message->image_size);
if (message->image_size == 0) {
log_i("Zigbee - Rejecting OTA image upgrade, image size is 0");
return ESP_FAIL;
}
if (message->file_version == 0) {
log_i("Zigbee - Rejecting OTA image upgrade, file version is 0");
return ESP_FAIL;
}
log_i("Zigbee - Approving OTA image upgrade");
} else {
log_i("Zigbee - OTA image upgrade response status: 0x%x", message->info.status);
}
return ESP_OK;
}
static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message) {
if (!message) {
log_e("Empty message");
return ESP_FAIL;
}
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Received message: error status(%d)", message->info.status);
return ESP_ERR_INVALID_ARG;
}
log_v(
"Received default response: from address(0x%x), src_endpoint(%d) to dst_endpoint(%d), cluster(0x%x) with status 0x%x",
message->info.src_address.u.short_addr, message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster, message->status_code
);
// Call global callback if set
Zigbee.callDefaultResponseCallback((zb_cmd_type_t)message->resp_to_cmd, message->status_code, message->info.dst_endpoint, message->info.cluster);
// List through all Zigbee EPs and call the callback function, with the message
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if (message->info.dst_endpoint == (*it)->getEndpoint()) {
(*it)->zbDefaultResponse(message); //method zbDefaultResponse is implemented in the common EP class
}
}
return ESP_OK;
}
#endif // CONFIG_ZB_ENABLED
+44
View File
@@ -0,0 +1,44 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "esp_zigbee_core.h"
// Foundation Command Types
typedef enum {
ZB_CMD_READ_ATTRIBUTE = 0x00U, /*!< Read attributes command */
ZB_CMD_READ_ATTRIBUTE_RESPONSE = 0x01U, /*!< Read attributes response command */
ZB_CMD_WRITE_ATTRIBUTE = 0x02U, /*!< Write attributes foundation command */
ZB_CMD_WRITE_ATTRIBUTE_UNDIVIDED = 0x03U, /*!< Write attributes undivided command */
ZB_CMD_WRITE_ATTRIBUTE_RESPONSE = 0x04U, /*!< Write attributes response command */
ZB_CMD_WRITE_ATTRIBUTE_NO_RESPONSE = 0x05U, /*!< Write attributes no response command */
ZB_CMD_CONFIGURE_REPORTING = 0x06U, /*!< Configure reporting command */
ZB_CMD_CONFIGURE_REPORTING_RESPONSE = 0x07U, /*!< Configure reporting response command */
ZB_CMD_READ_REPORTING_CONFIG = 0x08U, /*!< Read reporting config command */
ZB_CMD_READ_REPORTING_CONFIG_RESPONSE = 0x09U, /*!< Read reporting config response command */
ZB_CMD_REPORT_ATTRIBUTE = 0x0aU, /*!< Report attribute command */
ZB_CMD_DEFAULT_RESPONSE = 0x0bU, /*!< Default response command */
ZB_CMD_DISCOVER_ATTRIBUTES = 0x0cU, /*!< Discover attributes command */
ZB_CMD_DISCOVER_ATTRIBUTES_RESPONSE = 0x0dU, /*!< Discover attributes response command */
ZB_CMD_READ_ATTRIBUTE_STRUCTURED = 0x0eU, /*!< Read attributes structured */
ZB_CMD_WRITE_ATTRIBUTE_STRUCTURED = 0x0fU, /*!< Write attributes structured */
ZB_CMD_WRITE_ATTRIBUTE_STRUCTURED_RESPONSE = 0x10U, /*!< Write attributes structured response */
ZB_CMD_DISCOVER_COMMANDS_RECEIVED = 0x11U, /*!< Discover Commands Received command */
ZB_CMD_DISCOVER_COMMANDS_RECEIVED_RESPONSE = 0x12U, /*!< Discover Commands Received response command */
ZB_CMD_DISCOVER_COMMANDS_GENERATED = 0x13U, /*!< Discover Commands Generated command */
ZB_CMD_DISCOVER_COMMANDS_GENERATED_RESPONSE = 0x14U, /*!< Discover Commands Generated response command */
ZB_CMD_DISCOVER_ATTRIBUTES_EXTENDED = 0x15U, /*!< Discover attributes extended command */
ZB_CMD_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE = 0x16U, /*!< Discover attributes extended response command */
} zb_cmd_type_t;
+480
View File
@@ -0,0 +1,480 @@
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ZigbeeAnalog.h"
#if CONFIG_ZB_ENABLED
#include <cfloat>
ZigbeeAnalog::ZigbeeAnalog(uint8_t endpoint) : ZigbeeEP(endpoint) {
_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID;
_on_analog_output_change = nullptr;
//Create basic analog sensor clusters without configuration
_cluster_list = esp_zb_zcl_cluster_list_create();
esp_zb_cluster_list_add_basic_cluster(_cluster_list, esp_zb_basic_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(_cluster_list, esp_zb_identify_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
_ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID, .app_device_version = 0};
}
bool ZigbeeAnalog::addAnalogInput() {
esp_zb_attribute_list_t *esp_zb_analog_input_cluster = esp_zb_analog_input_cluster_create(NULL);
// Create default description for Analog Input
char default_description[] = "\x0C"
"Analog Input";
uint32_t application_type = 0x00000000 | (ESP_ZB_ZCL_AI_GROUP_ID << 24);
float resolution = 0.1; // Default resolution of 0.1
float min = -FLT_MAX; // Default min value for float
float max = FLT_MAX; // Default max value for float
esp_err_t ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_DESCRIPTION_ID, (void *)default_description);
if (ret != ESP_OK) {
log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_APPLICATION_TYPE_ID, (void *)&application_type);
if (ret != ESP_OK) {
log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_RESOLUTION_ID, (void *)&resolution);
if (ret != ESP_OK) {
log_e("Failed to add resolution attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_MIN_PRESENT_VALUE_ID, (void *)&min);
if (ret != ESP_OK) {
log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_MAX_PRESENT_VALUE_ID, (void *)&max);
if (ret != ESP_OK) {
log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_cluster_list_add_analog_input_cluster(_cluster_list, esp_zb_analog_input_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (ret != ESP_OK) {
log_e("Failed to add Analog Input cluster: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
_analog_clusters |= ANALOG_INPUT;
return true;
}
// Check esp_zigbee_zcl_analog_input.h for application type values
bool ZigbeeAnalog::setAnalogInputApplication(uint32_t application_type) {
if (!(_analog_clusters & ANALOG_INPUT)) {
log_e("Analog Input cluster not added");
return false;
}
// Add the Analog Input group ID (0x00) to the application type
uint32_t application_type_value = (ESP_ZB_ZCL_AI_GROUP_ID << 24) | application_type;
esp_zb_attribute_list_t *analog_input_cluster =
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_APPLICATION_TYPE_ID, (void *)&application_type_value);
if (ret != ESP_OK) {
log_e("Failed to set AI application type: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
bool ZigbeeAnalog::addAnalogOutput() {
esp_zb_attribute_list_t *esp_zb_analog_output_cluster = esp_zb_analog_output_cluster_create(NULL);
// Create default description for Analog Output
char default_description[] = "\x0D"
"Analog Output";
uint32_t application_type = 0x00000000 | (ESP_ZB_ZCL_AO_GROUP_ID << 24);
float resolution = 1; // Default resolution of 1
float min = -FLT_MAX; // Default min value for float
float max = FLT_MAX; // Default max value for float
esp_err_t ret =
esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID, (void *)default_description);
if (ret != ESP_OK) {
log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type);
if (ret != ESP_OK) {
log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_RESOLUTION_ID, (void *)&resolution);
if (ret != ESP_OK) {
log_e("Failed to add resolution attribute: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_MIN_PRESENT_VALUE_ID, (void *)&min);
if (ret != ESP_OK) {
log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_MAX_PRESENT_VALUE_ID, (void *)&max);
if (ret != ESP_OK) {
log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_cluster_list_add_analog_output_cluster(_cluster_list, esp_zb_analog_output_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (ret != ESP_OK) {
log_e("Failed to add Analog Output cluster: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
_analog_clusters |= ANALOG_OUTPUT;
return true;
}
// Check esp_zigbee_zcl_analog_output.h for application type values
bool ZigbeeAnalog::setAnalogOutputApplication(uint32_t application_type) {
if (!(_analog_clusters & ANALOG_OUTPUT)) {
log_e("Analog Output cluster not added");
return false;
}
// Add the Analog Output group ID (0x00) to the application type
uint32_t application_type_value = (ESP_ZB_ZCL_AO_GROUP_ID << 24) | application_type;
esp_zb_attribute_list_t *analog_output_cluster =
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type_value);
if (ret != ESP_OK) {
log_e("Failed to set AO application type: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
//set attribute method -> method overridden in child class
void ZigbeeAnalog::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT) {
if (message->attribute.id == ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_SINGLE) {
_output_state = *(float *)message->attribute.data.value;
analogOutputChanged();
} else {
log_w("Received message ignored. Attribute ID: %d not supported for Analog Output", message->attribute.id);
}
} else {
log_w("Received message ignored. Cluster ID: %d not supported for Analog endpoint", message->info.cluster);
}
}
void ZigbeeAnalog::analogOutputChanged() {
if (_on_analog_output_change) {
_on_analog_output_change(_output_state);
} else {
log_w("No callback function set for analog output change");
}
}
bool ZigbeeAnalog::setAnalogInput(float analog) {
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
if (!(_analog_clusters & ANALOG_INPUT)) {
log_e("Analog Input cluster not added");
return false;
}
log_d("Setting analog input to %.1f", analog);
esp_zb_lock_acquire(portMAX_DELAY);
ret = esp_zb_zcl_set_attribute_val(
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID, &analog, false
);
esp_zb_lock_release();
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Failed to set analog input: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
return false;
}
return true;
}
bool ZigbeeAnalog::setAnalogOutput(float analog) {
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
_output_state = analog;
analogOutputChanged();
log_v("Updating analog output to %.2f", analog);
/* Update analog output */
esp_zb_lock_acquire(portMAX_DELAY);
ret = esp_zb_zcl_set_attribute_val(
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID, &_output_state, false
);
esp_zb_lock_release();
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
log_e("Failed to set analog output: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
return false;
}
return true;
}
bool ZigbeeAnalog::reportAnalogInput() {
/* Send report attributes command */
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID;
report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT;
report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
report_attr_cmd.manuf_specific = 0x00U; // Standard profile command. Manufacturer code field shall not be included into ZCL frame header.
report_attr_cmd.dis_default_resp = 0x00U; // Default response is enabled.
esp_zb_lock_acquire(portMAX_DELAY);
esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
esp_zb_lock_release();
if (ret != ESP_OK) {
log_e("Failed to send Analog Input report: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
log_v("Analog Input report sent");
return true;
}
bool ZigbeeAnalog::reportAnalogOutput() {
/* Send report attributes command */
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID;
report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT;
report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
report_attr_cmd.manuf_specific = 0x00U; // Standard profile command. Manufacturer code field shall not be included into ZCL frame header.
report_attr_cmd.dis_default_resp = 0x00U; // Default response is enabled.
esp_zb_lock_acquire(portMAX_DELAY);
esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
esp_zb_lock_release();
if (ret != ESP_OK) {
log_e("Failed to send Analog Output report: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
log_v("Analog Output report sent");
return true;
}
bool ZigbeeAnalog::setAnalogInputReporting(uint16_t min_interval, uint16_t max_interval, float delta) {
esp_zb_zcl_reporting_info_t reporting_info;
memset(&reporting_info, 0, sizeof(esp_zb_zcl_reporting_info_t));
reporting_info.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV;
reporting_info.ep = _endpoint;
reporting_info.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT;
reporting_info.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE;
reporting_info.attr_id = ESP_ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID;
reporting_info.u.send_info.min_interval = min_interval;
reporting_info.u.send_info.max_interval = max_interval;
reporting_info.u.send_info.def_min_interval = min_interval;
reporting_info.u.send_info.def_max_interval = max_interval;
reporting_info.u.send_info.delta.s32 = delta;
reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID;
reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
esp_zb_lock_acquire(portMAX_DELAY);
esp_err_t ret = esp_zb_zcl_update_reporting_info(&reporting_info);
esp_zb_lock_release();
if (ret != ESP_OK) {
log_e("Failed to set Analog Input reporting: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
bool ZigbeeAnalog::setAnalogInputDescription(const char *description) {
if (!(_analog_clusters & ANALOG_INPUT)) {
log_e("Analog Input cluster not added");
return false;
}
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
char zb_description[ZB_MAX_NAME_LENGTH + 2];
// Convert description to ZCL string
size_t description_length = strlen(description);
if (description_length > ZB_MAX_NAME_LENGTH) {
log_e("Description is too long");
return false;
}
// Get and check the analog input cluster
esp_zb_attribute_list_t *analog_input_cluster =
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (analog_input_cluster == nullptr) {
log_e("Failed to get analog input cluster");
return false;
}
// Store the length as the first element
zb_description[0] = static_cast<char>(description_length); // Cast size_t to char
// Use memcpy to copy the characters to the result array
memcpy(zb_description + 1, description, description_length);
// Null-terminate the array
zb_description[description_length + 1] = '\0';
// Update the description attribute
esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_DESCRIPTION_ID, (void *)zb_description);
if (ret != ESP_OK) {
log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
bool ZigbeeAnalog::setAnalogOutputDescription(const char *description) {
if (!(_analog_clusters & ANALOG_OUTPUT)) {
log_e("Analog Output cluster not added");
return false;
}
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
char zb_description[ZB_MAX_NAME_LENGTH + 2];
// Convert description to ZCL string
size_t description_length = strlen(description);
if (description_length > ZB_MAX_NAME_LENGTH) {
log_e("Description is too long");
return false;
}
// Get and check the analog output cluster
esp_zb_attribute_list_t *analog_output_cluster =
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (analog_output_cluster == nullptr) {
log_e("Failed to get analog output cluster");
return false;
}
// Store the length as the first element
zb_description[0] = static_cast<char>(description_length); // Cast size_t to char
// Use memcpy to copy the characters to the result array
memcpy(zb_description + 1, description, description_length);
// Null-terminate the array
zb_description[description_length + 1] = '\0';
// Update the description attribute
esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID, (void *)zb_description);
if (ret != ESP_OK) {
log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
bool ZigbeeAnalog::setAnalogInputResolution(float resolution) {
if (!(_analog_clusters & ANALOG_INPUT)) {
log_e("Analog Input cluster not added");
return false;
}
esp_zb_attribute_list_t *analog_input_cluster =
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (analog_input_cluster == nullptr) {
log_e("Failed to get analog input cluster");
return false;
}
esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_RESOLUTION_ID, (void *)&resolution);
if (ret != ESP_OK) {
log_e("Failed to set resolution: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
bool ZigbeeAnalog::setAnalogOutputResolution(float resolution) {
if (!(_analog_clusters & ANALOG_OUTPUT)) {
log_e("Analog Output cluster not added");
return false;
}
esp_zb_attribute_list_t *analog_output_cluster =
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (analog_output_cluster == nullptr) {
log_e("Failed to get analog output cluster");
return false;
}
esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_RESOLUTION_ID, (void *)&resolution);
if (ret != ESP_OK) {
log_e("Failed to set resolution: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
bool ZigbeeAnalog::setAnalogOutputMinMax(float min, float max) {
if (!(_analog_clusters & ANALOG_OUTPUT)) {
log_e("Analog Output cluster not added");
return false;
}
esp_zb_attribute_list_t *analog_output_cluster =
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (analog_output_cluster == nullptr) {
log_e("Failed to get analog output cluster");
return false;
}
esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_MIN_PRESENT_VALUE_ID, (void *)&min);
if (ret != ESP_OK) {
log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_MAX_PRESENT_VALUE_ID, (void *)&max);
if (ret != ESP_OK) {
log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
bool ZigbeeAnalog::setAnalogInputMinMax(float min, float max) {
if (!(_analog_clusters & ANALOG_INPUT)) {
log_e("Analog Input cluster not added");
return false;
}
esp_zb_attribute_list_t *analog_input_cluster =
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
if (analog_input_cluster == nullptr) {
log_e("Failed to get analog input cluster");
return false;
}
esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_MIN_PRESENT_VALUE_ID, (void *)&min);
if (ret != ESP_OK) {
log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_MAX_PRESENT_VALUE_ID, (void *)&max);
if (ret != ESP_OK) {
log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret));
return false;
}
return true;
}
#endif // CONFIG_ZB_ENABLED

Some files were not shown because too many files have changed in this diff Show More