Skip to content

Commit c2e04f8

Browse files
committed
feat(zigbee): Add power source and battery level
1 parent d47771f commit c2e04f8

File tree

6 files changed

+273
-6
lines changed

6 files changed

+273
-6
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Arduino-ESP32 Zigbee Temperature Sensor Example
2+
3+
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) temperature sensor.
4+
5+
# Supported Targets
6+
7+
Currently, this example supports the following targets.
8+
9+
| Supported Targets | ESP32-C6 | ESP32-H2 |
10+
| ----------------- | -------- | -------- |
11+
12+
## Temperature Sensor Functions
13+
14+
Note:
15+
* This board means the board (e.g. ESP32-H2 / C6) loaded with `Zigbee_Temperature_Sensor` example.
16+
* The remote board means the board (e.g. ESP32-H2 / C6) loaded with `Zigbee_Thermostat` example.
17+
18+
Functions:
19+
* 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.
20+
* By clicking the button (BOOT) on this board, this board will immediately send a report of the current measured temperature to the remote board.
21+
22+
## Hardware Required
23+
24+
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee coordinator (loaded with `Zigbee_Thermostat` example)
25+
* A USB cable for power supply and programming
26+
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the `Zigbee_Temperature_Sensor` example
27+
28+
### Configure the Project
29+
30+
In this example, the internal temperature sensor task is reading the chip temperature.
31+
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).
32+
33+
#### Using Arduino IDE
34+
35+
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
36+
37+
* Before Compile/Verify, select the correct board: `Tools -> Board`.
38+
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
39+
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
40+
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
41+
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
42+
43+
## Troubleshooting
44+
45+
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.
46+
You can do the following:
47+
48+
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
49+
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
50+
51+
By default, the coordinator network is closed after rebooting or flashing new firmware.
52+
To open the network you have 2 options:
53+
54+
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
55+
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
56+
57+
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
58+
59+
* **LED not blinking:** Check the wiring connection and the IO selection.
60+
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
61+
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
62+
63+
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
64+
65+
## Contribute
66+
67+
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
68+
69+
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!
70+
71+
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
72+
73+
## Resources
74+
75+
* Official ESP32 Forum: [Link](https://esp32.com)
76+
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
77+
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
78+
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
79+
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright 2024 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 Zigbee temperature sensor Sleepy device.
17+
*
18+
* The example demonstrates how to use Zigbee library to create a end device temperature sensor.
19+
* The temperature sensor 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 coordinator mode is not selected in Tools->Zigbee mode"
31+
#endif
32+
33+
#include "ZigbeeCore.h"
34+
#include "ep/ZigbeeTempSensor.h"
35+
#include <ctime>
36+
37+
#define BUTTON_PIN 9 //Boot button for C6/H2
38+
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
39+
40+
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
41+
#define TIME_TO_SLEEP 30 /* Time ESP32 will go to sleep (in seconds) */
42+
43+
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
44+
45+
/************************ Temp sensor *****************************/
46+
void meausureAndSleep(){
47+
// Measure temperature sensor value
48+
float tsens_value = temperatureRead();
49+
50+
// Initialize random seed
51+
std::srand(static_cast<unsigned int>(std::time(0)));
52+
53+
// Calculate the 1% fluctuation range
54+
float fluctuation = tsens_value * 0.01f;
55+
56+
// Generate a random number between -1% and +1%
57+
float randomFactor = (static_cast<float>(std::rand()) / RAND_MAX) * 2.0f - 1.0f;
58+
59+
// Apply the fluctuation to the original temperature
60+
tsens_value = tsens_value + randomFactor * fluctuation;
61+
62+
// Update temperature value in Temperature sensor EP
63+
zbTempSensor.setTemperature(tsens_value);
64+
65+
// Report temperature value
66+
zbTempSensor.reportTemperature();
67+
68+
// Set battery percentage to 80% and report it
69+
zbTempSensor.setBatteryPercentage(80);
70+
zbTempSensor.reportBatteryPercentage();
71+
72+
// Put device to deep sleep
73+
esp_deep_sleep_start();
74+
}
75+
76+
/********************* Arduino functions **************************/
77+
void setup() {
78+
// Init button switch
79+
pinMode(BUTTON_PIN, INPUT_PULLUP);
80+
81+
// Optional: set Zigbee device name and model
82+
zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensor");
83+
84+
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
85+
zbTempSensor.setMinMaxValue(10, 50);
86+
87+
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
88+
zbTempSensor.setTolerance(1);
89+
90+
// Set power source to battery and battery percentage to 100%
91+
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100);
92+
93+
// Add endpoint to Zigbee Core
94+
Zigbee.addEndpoint(&zbTempSensor);
95+
96+
// Configure the wake up source and set to wake up every 5 seconds
97+
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
98+
99+
// Create a custom Zigbee configuration for End Device
100+
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
101+
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
102+
103+
// When all EPs are registered, start Zigbee in End Device mode
104+
Zigbee.begin(&zigbeeConfig, false);
105+
106+
// Wait for Zigbee to start
107+
while(!Zigbee.isStarted()){
108+
delay(100);
109+
}
110+
111+
// Delay 5s to allow establishing connection with coordinator, needed for sleepy devices
112+
delay(5000);
113+
}
114+
115+
void loop() {
116+
// Checking button for factory reset
117+
if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed
118+
// Key debounce handling
119+
delay(100);
120+
int startTime = millis();
121+
while (digitalRead(BUTTON_PIN) == LOW) {
122+
delay(50);
123+
if ((millis() - startTime) > 3000) {
124+
// If key pressed for more than 3secs, factory reset Zigbee and reboot
125+
Zigbee.factoryReset();
126+
}
127+
}
128+
}
129+
130+
// Call the function to measure temperature and put the device to sleep
131+
meausureAndSleep();
132+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
3+
"requires": [
4+
"CONFIG_SOC_IEEE802154_SUPPORTED=y"
5+
]
6+
}

libraries/Zigbee/src/ZigbeeCore.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ void ZigbeeCore::setRebootOpenNetwork(uint8_t time) {
155155
}
156156

157157
void ZigbeeCore::openNetwork(uint8_t time) {
158-
if (_started) {
158+
if (isStarted()) {
159159
log_v("Opening network for joining for %d seconds", time);
160160
esp_zb_bdb_open_network(time);
161161
}
@@ -201,10 +201,15 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
201201
log_i("Opening network for joining for %d seconds", Zigbee._open_network);
202202
esp_zb_bdb_open_network(Zigbee._open_network);
203203
}
204+
if(Zigbee._sleepy_device) {
205+
log_i("Device is a sleepy end device");
206+
Zigbee._can_sleep = true;
207+
}
204208
}
205209
} else {
206210
/* commissioning failed */
207211
log_e("Failed to initialize Zigbee stack (status: %s)", esp_err_to_name(err_status));
212+
esp_restart();
208213
}
209214
break;
210215
case ESP_ZB_BDB_SIGNAL_FORMATION: // Coordinator
@@ -225,11 +230,11 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
225230
}
226231
break;
227232
case ESP_ZB_BDB_SIGNAL_STEERING: // Router and End Device
228-
Zigbee._started = true;
229233
if ((zigbee_role_t)Zigbee.getRole() == ZIGBEE_COORDINATOR) {
230234
if (err_status == ESP_OK) {
231235
log_i("Network steering started");
232236
}
237+
Zigbee._started = true;
233238
} else {
234239
if (err_status == ESP_OK) {
235240
esp_zb_ieee_addr_t extended_pan_id;
@@ -239,6 +244,7 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
239244
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],
240245
extended_pan_id[0], esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address()
241246
);
247+
Zigbee._started = true;
242248
} else {
243249
log_i("Network steering was not successful (status: %s)", esp_err_to_name(err_status));
244250
esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_STEERING, 1000);
@@ -321,7 +327,7 @@ void ZigbeeCore::scanCompleteCallback(esp_zb_zdp_status_t zdo_status, uint8_t co
321327
}
322328

323329
void ZigbeeCore::scanNetworks(u_int32_t channel_mask, u_int8_t scan_duration) {
324-
if (!_started) {
330+
if (!isStarted()) {
325331
log_e("Zigbee stack is not started, cannot scan networks");
326332
return;
327333
}

libraries/Zigbee/src/ZigbeeEP.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#if SOC_IEEE802154_SUPPORTED
66

77
#include "esp_zigbee_cluster.h"
8+
#include "zcl/esp_zigbee_zcl_power_config.h"
89

910
uint8_t ZigbeeEP::_endpoint = 0;
1011
bool ZigbeeEP::_is_bound = false;
@@ -65,6 +66,45 @@ void ZigbeeEP::setManufacturerAndModel(const char *name, const char *model) {
6566
esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, (void *)zb_model);
6667
}
6768

69+
void ZigbeeEP::setPowerSource(zb_power_source_t power_source, uint8_t percentage) {
70+
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);
71+
esp_zb_cluster_update_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_POWER_SOURCE_ID, (void *)&power_source);
72+
73+
if(power_source == ZB_POWER_SOURCE_BATTERY){
74+
//TODO:
75+
//UPDATE MAC CAPABILITIES TO SET POWER SOURCE TO BATTERY
76+
//currently its not possible to update mac capabilities
77+
// Add power config cluster and battery percentage attribute
78+
esp_zb_attribute_list_t *power_config_cluster = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG);
79+
esp_zb_power_config_cluster_add_attr(power_config_cluster, ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID, (void *)&percentage);
80+
esp_zb_cluster_list_add_power_config_cluster(_cluster_list, power_config_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
81+
}
82+
}
83+
84+
void ZigbeeEP::setBatteryPercentage(uint8_t percentage) {
85+
esp_zb_lock_acquire(portMAX_DELAY);
86+
esp_zb_zcl_set_attribute_val(
87+
_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
88+
);
89+
esp_zb_lock_release();
90+
log_v("Battery percentage updated");
91+
}
92+
93+
void ZigbeeEP::reportBatteryPercentage() {
94+
/* Send report attributes command */
95+
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
96+
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
97+
report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID;
98+
report_attr_cmd.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE;
99+
report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG;
100+
report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
101+
102+
esp_zb_lock_acquire(portMAX_DELAY);
103+
esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
104+
esp_zb_lock_release();
105+
log_v("Battery percentage reported");
106+
}
107+
68108
char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr) {
69109
/* Read peer Manufacture Name & Model Identifier */
70110
esp_zb_zcl_read_attr_cmd_t read_req;

libraries/Zigbee/src/ZigbeeEP.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ typedef struct zb_device_params_s {
4646
} zb_device_params_t;
4747

4848
typedef enum {
49-
SINGLE_COLOR = 0,
50-
RGB = 1
51-
} zb_identify_led_type_t;
49+
ZB_POWER_SOURCE_UNKNOWN = 0x00,
50+
ZB_POWER_SOURCE_MAINS = 0x01,
51+
ZB_POWER_SOURCE_BATTERY = 0x03,
52+
} zb_power_source_t;
5253

5354
/* Zigbee End Device Class */
5455
class ZigbeeEP {
@@ -81,6 +82,9 @@ class ZigbeeEP {
8182

8283
// Manufacturer name and model implemented
8384
void setManufacturerAndModel(const char *name, const char *model);
85+
void setPowerSource(zb_power_source_t power_source, uint8_t percentage = 255);
86+
void setBatteryPercentage(uint8_t percentage);
87+
void reportBatteryPercentage();
8488

8589
// Methods to read manufacturer and model name from selected endpoint and short address
8690
char *readManufacturer(uint8_t endpoint, uint16_t short_addr);

0 commit comments

Comments
 (0)