From ae36672c1832b2328453296afb805b27be29c2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Thu, 24 Apr 2025 21:05:55 +0200 Subject: [PATCH 1/3] feat(esp32): Zigbee power outlet example Zigbee power outlet example --- CMakeLists.txt | 1 + .../examples/Zigbee_Power_Outlet/README.md | 69 ++++++++++++ .../Zigbee_Power_Outlet.ino | 103 ++++++++++++++++++ .../examples/Zigbee_Power_Outlet/ci.json | 7 ++ libraries/Zigbee/keywords.txt | 1 + libraries/Zigbee/src/Zigbee.h | 2 + libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp | 56 ++++++++++ libraries/Zigbee/src/ep/ZigbeePowerOutlet.h | 41 +++++++ 8 files changed, 280 insertions(+) create mode 100644 libraries/Zigbee/examples/Zigbee_Power_Outlet/README.md create mode 100644 libraries/Zigbee/examples/Zigbee_Power_Outlet/Zigbee_Power_Outlet.ino create mode 100644 libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.json create mode 100644 libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp create mode 100644 libraries/Zigbee/src/ep/ZigbeePowerOutlet.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 14fcb19b6da..c8277b32471 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -301,6 +301,7 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp + libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp ) set(ARDUINO_LIBRARY_BLE_SRCS diff --git a/libraries/Zigbee/examples/Zigbee_Power_Outlet/README.md b/libraries/Zigbee/examples/Zigbee_Power_Outlet/README.md new file mode 100644 index 00000000000..ccbdd32f3e7 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Power_Outlet/README.md @@ -0,0 +1,69 @@ +@ -0,0 +1,68 @@ +# Arduino-ESP32 Zigbee On/Off Power Outlet Example + +This example shows how to configure Zigbee Coordinator 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 + +* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee end device (loaded with Zigbee_Power_Outlet 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_Power_Outlet` 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_Power_Outlet` 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) \ No newline at end of file diff --git a/libraries/Zigbee/examples/Zigbee_Power_Outlet/Zigbee_Power_Outlet.ino b/libraries/Zigbee/examples/Zigbee_Power_Outlet/Zigbee_Power_Outlet.ino new file mode 100644 index 00000000000..050a256f595 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Power_Outlet/Zigbee_Power_Outlet.ino @@ -0,0 +1,103 @@ +// 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 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 power outlet configuration */ +#define ZIGBEE_OUTLET_ENDPOINT 10 +uint8_t led = RGB_BUILTIN; +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()) { + 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); +} diff --git a/libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.json b/libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.json new file mode 100644 index 00000000000..ceacc367801 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.json @@ -0,0 +1,7 @@ +{ + "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", + "requires": [ + "CONFIG_SOC_IEEE802154_SUPPORTED=y", + "CONFIG_ZB_ENABLED=y" + ] +} diff --git a/libraries/Zigbee/keywords.txt b/libraries/Zigbee/keywords.txt index 586d2bdc677..4d4cd7d0606 100644 --- a/libraries/Zigbee/keywords.txt +++ b/libraries/Zigbee/keywords.txt @@ -30,6 +30,7 @@ ZigbeeRangeExtender KEYWORD1 ZigbeeVibrationSensor KEYWORD1 ZigbeeWindowCovering KEYWORD1 ZigbeeIlluminanceSensor KEYWORD1 +ZigbeePowerOutlet KEYWORD1 # Other zigbee_role_t KEYWORD1 diff --git a/libraries/Zigbee/src/Zigbee.h b/libraries/Zigbee/src/Zigbee.h index 7f44d7813af..1840a5262bb 100644 --- a/libraries/Zigbee/src/Zigbee.h +++ b/libraries/Zigbee/src/Zigbee.h @@ -16,6 +16,8 @@ #include "ep/ZigbeeLight.h" //// Controllers #include "ep/ZigbeeThermostat.h" +////Outlets +#include "ep/ZigbeePowerOutlet.h" //// Sensors #include "ep/ZigbeeAnalog.h" #include "ep/ZigbeeCarbonDioxideSensor.h" diff --git a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp new file mode 100644 index 00000000000..6c457d31f1b --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp @@ -0,0 +1,56 @@ +#include "ZigbeePowerOutlet.h" +#if CONFIG_ZB_ENABLED + +ZigbeePowerOutlet::ZigbeePowerOutlet(uint8_t endpoint) : ZigbeeEP(endpoint) { + _device_id = ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID; + + esp_zb_mains_power_outlet_cfg_t outlet_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG(); + _cluster_list = esp_zb_mains_power_outlet_clusters_create(&outlet_cfg); // use esp_zb_zcl_cluster_list_create() instead of esp_zb_mains_power_outlet_clusters_create() + _ep_config = {.endpoint = endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID, .app_device_version = 0}; + log_v("Outlet endpoint created %d", _endpoint); +} + +//set attribute method -> method overridden in child class +void ZigbeePowerOutlet::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { + //check the data and call right method + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) { + if (message->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) { + _current_state = *(bool *)message->attribute.data.value; + stateChanged(); + } else { + log_w("Received message ignored. Attribute ID: %d not supported for On/Off Outlet", message->attribute.id); + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for On/Off Outlet", message->info.cluster); + } +} + +void ZigbeePowerOutlet::stateChanged() { + if (_on_state_change) { + _on_state_change(_current_state); + } else { + log_w("No callback function set for outlet change"); + } +} + +bool ZigbeePowerOutlet::setState(bool state) { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + _current_state = state; + stateChanged(); + + log_v("Updating on/off outlet state to %d", state); + /* Update on/off outlet state */ + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_ON_OFF, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID, &_current_state, false + ); + esp_zb_lock_release(); + + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set outlet state: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + return false; + } + return true; +} + +#endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h new file mode 100644 index 00000000000..1ddb19587c7 --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h @@ -0,0 +1,41 @@ +/* Class of Zigbee On/Off Power outlet endpoint inherited from common EP class */ + +#pragma once + +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#if CONFIG_ZB_ENABLED + +#include "ZigbeeEP.h" +#include "ha/esp_zigbee_ha_standard.h" + +class ZigbeePowerOutlet : public ZigbeeEP { +public: + ZigbeePowerOutlet(uint8_t endpoint); + ~ZigbeePowerOutlet() {} + + // Use to set a cb function to be called on outlet change + void onPowerOutletChange(void (*callback)(bool)) { + _on_state_change = callback; + } + // Use to restore outlet state + void restoreState() { + stateChanged(); + } + // Use to control outlet state + bool setState(bool state); + // Use to get outlet state + bool getPowerOutletState() { + return _current_state; + } + +private: + void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; + //callback function to be called on outlet change + void (*_on_state_change)(bool); + void stateChanged(); + + bool _current_state; +}; + +#endif // CONFIG_ZB_ENABLED From b15fb1de6ddd3c718b47f9275016c46ce79d00f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Fri, 25 Apr 2025 08:26:09 +0200 Subject: [PATCH 2/3] feat(esp32): Zigbee power outlet example Change username --- .../Zigbee/examples/Zigbee_Power_Outlet/Zigbee_Power_Outlet.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Zigbee/examples/Zigbee_Power_Outlet/Zigbee_Power_Outlet.ino b/libraries/Zigbee/examples/Zigbee_Power_Outlet/Zigbee_Power_Outlet.ino index 050a256f595..31e9a03591b 100644 --- a/libraries/Zigbee/examples/Zigbee_Power_Outlet/Zigbee_Power_Outlet.ino +++ b/libraries/Zigbee/examples/Zigbee_Power_Outlet/Zigbee_Power_Outlet.ino @@ -23,7 +23,7 @@ * * 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/) + * Created by Ludovic Boué (https://github.com/lboue) */ #ifndef ZIGBEE_MODE_ED From 8f71c44ef48a0fa27ec19702858cac62fd9a4f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20BOU=C3=89?= Date: Fri, 25 Apr 2025 08:27:14 +0200 Subject: [PATCH 3/3] feat(esp32): Zigbee power outlet example Remove old comment --- libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp index 6c457d31f1b..1c4bdd35148 100644 --- a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp @@ -5,7 +5,7 @@ ZigbeePowerOutlet::ZigbeePowerOutlet(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID; esp_zb_mains_power_outlet_cfg_t outlet_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG(); - _cluster_list = esp_zb_mains_power_outlet_clusters_create(&outlet_cfg); // use esp_zb_zcl_cluster_list_create() instead of esp_zb_mains_power_outlet_clusters_create() + _cluster_list = esp_zb_mains_power_outlet_clusters_create(&outlet_cfg); _ep_config = {.endpoint = endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID, .app_device_version = 0}; log_v("Outlet endpoint created %d", _endpoint); }