Skip to content

Commit ae36672

Browse files
committed
feat(esp32): Zigbee power outlet example
Zigbee power outlet example
1 parent 571c2f7 commit ae36672

File tree

8 files changed

+280
-0
lines changed

8 files changed

+280
-0
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS
301301
libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp
302302
libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp
303303
libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp
304+
libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp
304305
)
305306

306307
set(ARDUINO_LIBRARY_BLE_SRCS
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
@ -0,0 +1,68 @@
2+
# Arduino-ESP32 Zigbee On/Off Power Outlet Example
3+
4+
This example shows how to configure Zigbee Coordinator and use it as a Home Automation (HA) on/off power outlet.
5+
6+
# Supported Targets
7+
8+
Currently, this example supports the following targets.
9+
10+
| Supported Targets | ESP32-C6 | ESP32-H2 |
11+
| ----------------- | -------- | -------- |
12+
13+
## Hardware Required
14+
15+
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee end device (loaded with Zigbee_Power_Outlet example).
16+
* A USB cable for power supply and programming.
17+
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee coordinator and upload the Zigbee_On_Off_Switch example.
18+
19+
### Configure the Project
20+
21+
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).
22+
23+
#### Using Arduino IDE
24+
25+
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
26+
27+
* Before Compile/Verify, select the correct board: `Tools -> Board`.
28+
* Select the Coordinator Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`.
29+
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`.
30+
* Select the COM port: `Tools -> Port: xxx where the `xxx` is the detected COM port.
31+
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
32+
33+
## Troubleshooting
34+
35+
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.
36+
You can do the following:
37+
38+
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
39+
* In the `Zigbee_Power_Outlet` example sketch call `Zigbee.factoryReset();`.
40+
41+
By default, the coordinator network is closed after rebooting or flashing new firmware.
42+
To open the network you have 2 options:
43+
44+
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
45+
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
46+
47+
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
48+
49+
* **LED not blinking:** Check the wiring connection and the IO selection.
50+
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
51+
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
52+
53+
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
54+
55+
## Contribute
56+
57+
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
58+
59+
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!
60+
61+
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
62+
63+
## Resources
64+
65+
* Official ESP32 Forum: [Link](https://esp32.com)
66+
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
67+
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
68+
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
69+
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/**
16+
* @brief This example demonstrates simple Zigbee power outlet.
17+
*
18+
* The example demonstrates how to use Zigbee library to create a end device power outlet.
19+
* The power outlet is a Zigbee end device, which is controlled by a Zigbee coordinator.
20+
*
21+
* Proper Zigbee mode must be selected in Tools->Zigbee mode
22+
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
23+
*
24+
* Please check the README.md for instructions and more detailed description.
25+
*
26+
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
27+
*/
28+
29+
#ifndef ZIGBEE_MODE_ED
30+
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
31+
#endif
32+
33+
#include "Zigbee.h"
34+
35+
/* Zigbee power outlet configuration */
36+
#define ZIGBEE_OUTLET_ENDPOINT 10
37+
uint8_t led = RGB_BUILTIN;
38+
uint8_t button = BOOT_PIN;
39+
40+
ZigbeePowerOutlet zbOutlet = ZigbeePowerOutlet(ZIGBEE_OUTLET_ENDPOINT);
41+
42+
43+
44+
/********************* RGB LED functions **************************/
45+
void setLED(bool value) {
46+
digitalWrite(led, value);
47+
}
48+
49+
/********************* Arduino functions **************************/
50+
void setup() {
51+
Serial.begin(115200);
52+
53+
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
54+
pinMode(led, OUTPUT);
55+
digitalWrite(led, LOW);
56+
57+
// Init button for factory reset
58+
pinMode(button, INPUT_PULLUP);
59+
60+
//Optional: set Zigbee device name and model
61+
zbOutlet.setManufacturerAndModel("Espressif", "ZBPowerOutlet");
62+
63+
// Set callback function for power outlet change
64+
zbOutlet.onPowerOutletChange(setLED);
65+
66+
//Add endpoint to Zigbee Core
67+
Serial.println("Adding ZigbeePowerOutlet endpoint to Zigbee Core");
68+
Zigbee.addEndpoint(&zbOutlet);
69+
70+
// When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE
71+
if (!Zigbee.begin()) {
72+
Serial.println("Zigbee failed to start!");
73+
Serial.println("Rebooting...");
74+
ESP.restart();
75+
}
76+
Serial.println("Connecting to network");
77+
while (!Zigbee.connected()) {
78+
Serial.print(".");
79+
delay(100);
80+
}
81+
Serial.println();
82+
}
83+
84+
void loop() {
85+
// Checking button for factory reset
86+
if (digitalRead(button) == LOW) { // Push button pressed
87+
// Key debounce handling
88+
delay(100);
89+
int startTime = millis();
90+
while (digitalRead(button) == LOW) {
91+
delay(50);
92+
if ((millis() - startTime) > 3000) {
93+
// If key pressed for more than 3secs, factory reset Zigbee and reboot
94+
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
95+
delay(1000);
96+
Zigbee.factoryReset();
97+
}
98+
}
99+
// Toggle state by pressing the button
100+
zbOutlet.setState(!zbOutlet.getPowerOutletState());
101+
}
102+
delay(100);
103+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
3+
"requires": [
4+
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
5+
"CONFIG_ZB_ENABLED=y"
6+
]
7+
}

libraries/Zigbee/keywords.txt

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ ZigbeeRangeExtender KEYWORD1
3030
ZigbeeVibrationSensor KEYWORD1
3131
ZigbeeWindowCovering KEYWORD1
3232
ZigbeeIlluminanceSensor KEYWORD1
33+
ZigbeePowerOutlet KEYWORD1
3334

3435
# Other
3536
zigbee_role_t KEYWORD1

libraries/Zigbee/src/Zigbee.h

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include "ep/ZigbeeLight.h"
1717
//// Controllers
1818
#include "ep/ZigbeeThermostat.h"
19+
////Outlets
20+
#include "ep/ZigbeePowerOutlet.h"
1921
//// Sensors
2022
#include "ep/ZigbeeAnalog.h"
2123
#include "ep/ZigbeeCarbonDioxideSensor.h"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#include "ZigbeePowerOutlet.h"
2+
#if CONFIG_ZB_ENABLED
3+
4+
ZigbeePowerOutlet::ZigbeePowerOutlet(uint8_t endpoint) : ZigbeeEP(endpoint) {
5+
_device_id = ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID;
6+
7+
esp_zb_mains_power_outlet_cfg_t outlet_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG();
8+
_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()
9+
_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};
10+
log_v("Outlet endpoint created %d", _endpoint);
11+
}
12+
13+
//set attribute method -> method overridden in child class
14+
void ZigbeePowerOutlet::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {
15+
//check the data and call right method
16+
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) {
17+
if (message->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) {
18+
_current_state = *(bool *)message->attribute.data.value;
19+
stateChanged();
20+
} else {
21+
log_w("Received message ignored. Attribute ID: %d not supported for On/Off Outlet", message->attribute.id);
22+
}
23+
} else {
24+
log_w("Received message ignored. Cluster ID: %d not supported for On/Off Outlet", message->info.cluster);
25+
}
26+
}
27+
28+
void ZigbeePowerOutlet::stateChanged() {
29+
if (_on_state_change) {
30+
_on_state_change(_current_state);
31+
} else {
32+
log_w("No callback function set for outlet change");
33+
}
34+
}
35+
36+
bool ZigbeePowerOutlet::setState(bool state) {
37+
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
38+
_current_state = state;
39+
stateChanged();
40+
41+
log_v("Updating on/off outlet state to %d", state);
42+
/* Update on/off outlet state */
43+
esp_zb_lock_acquire(portMAX_DELAY);
44+
ret = esp_zb_zcl_set_attribute_val(
45+
_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
46+
);
47+
esp_zb_lock_release();
48+
49+
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
50+
log_e("Failed to set outlet state: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
51+
return false;
52+
}
53+
return true;
54+
}
55+
56+
#endif // CONFIG_ZB_ENABLED
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/* Class of Zigbee On/Off Power outlet endpoint inherited from common EP class */
2+
3+
#pragma once
4+
5+
#include "soc/soc_caps.h"
6+
#include "sdkconfig.h"
7+
#if CONFIG_ZB_ENABLED
8+
9+
#include "ZigbeeEP.h"
10+
#include "ha/esp_zigbee_ha_standard.h"
11+
12+
class ZigbeePowerOutlet : public ZigbeeEP {
13+
public:
14+
ZigbeePowerOutlet(uint8_t endpoint);
15+
~ZigbeePowerOutlet() {}
16+
17+
// Use to set a cb function to be called on outlet change
18+
void onPowerOutletChange(void (*callback)(bool)) {
19+
_on_state_change = callback;
20+
}
21+
// Use to restore outlet state
22+
void restoreState() {
23+
stateChanged();
24+
}
25+
// Use to control outlet state
26+
bool setState(bool state);
27+
// Use to get outlet state
28+
bool getPowerOutletState() {
29+
return _current_state;
30+
}
31+
32+
private:
33+
void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override;
34+
//callback function to be called on outlet change
35+
void (*_on_state_change)(bool);
36+
void stateChanged();
37+
38+
bool _current_state;
39+
};
40+
41+
#endif // CONFIG_ZB_ENABLED

0 commit comments

Comments
 (0)