Skip to content

Commit 92dd841

Browse files
feat(Zigbee): Recall bounded devices after reboot + IEEE address option for commands (#10676)
* feat(zigbee): Recall bound devices after reboot * fix(zigbee): Add missing locks + allow printBoundDevices to Serial * fix(Zigbee): Add locks to temp sensor setReporting * fix(Zigbee): remove unnecessary space in formatting * fix(Zigbee): proper parameter in printBoundDevices * feat(Zigbee): factory reset when removed from network * fix(zigbee): Update comment * fix(zigbee): fix serial and add missing factoryReset to example * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent e159bf6 commit 92dd841

File tree

12 files changed

+376
-59
lines changed

12 files changed

+376
-59
lines changed

Diff for: libraries/Zigbee/examples/Zigbee_Color_Dimmer_Switch/Zigbee_Color_Dimmer_Switch.ino

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,6 @@ void loop() {
145145
static uint32_t last_print = 0;
146146
if (millis() - last_print > 30000) {
147147
last_print = millis();
148-
zbSwitch.printBoundDevices();
148+
zbSwitch.printBoundDevices(Serial);
149149
}
150150
}

Diff for: libraries/Zigbee/examples/Zigbee_On_Off_Switch/Zigbee_On_Off_Switch.ino

+7-8
Original file line numberDiff line numberDiff line change
@@ -138,17 +138,16 @@ void setup() {
138138
delay(500);
139139
}
140140

141-
// Optional: read manufacturer and model name from the bound light
141+
// Optional: List all bound devices and read manufacturer and model name
142142
std::list<zb_device_params_t *> boundLights = zbSwitch.getBoundDevices();
143-
//List all bound lights
144143
for (const auto &device : boundLights) {
145-
Serial.printf("Device on endpoint %d, short address: 0x%x\n", device->endpoint, device->short_addr);
144+
Serial.printf("Device on endpoint %d, short address: 0x%x\r\n", device->endpoint, device->short_addr);
146145
Serial.printf(
147-
"IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", device->ieee_addr[0], device->ieee_addr[1], device->ieee_addr[2], device->ieee_addr[3],
148-
device->ieee_addr[4], device->ieee_addr[5], device->ieee_addr[6], device->ieee_addr[7]
146+
"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],
147+
device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0]
149148
);
150-
Serial.printf("Light manufacturer: %s", zbSwitch.readManufacturer(device->endpoint, device->short_addr));
151-
Serial.printf("Light model: %s", zbSwitch.readModel(device->endpoint, device->short_addr));
149+
Serial.printf("Light manufacturer: %s\r\n", zbSwitch.readManufacturer(device->endpoint, device->short_addr, device->ieee_addr));
150+
Serial.printf("Light model: %s\r\n", zbSwitch.readModel(device->endpoint, device->short_addr, device->ieee_addr));
152151
}
153152

154153
Serial.println();
@@ -191,6 +190,6 @@ void loop() {
191190
static uint32_t lastPrint = 0;
192191
if (millis() - lastPrint > 10000) {
193192
lastPrint = millis();
194-
zbSwitch.printBoundDevices();
193+
zbSwitch.printBoundDevices(Serial);
195194
}
196195
}

Diff for: libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/Zigbee_Temp_Hum_Sensor_Sleepy.ino

+1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ void loop() {
128128
// If key pressed for more than 3secs, factory reset Zigbee and reboot
129129
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
130130
delay(1000);
131+
Zigbee.factoryReset();
131132
}
132133
}
133134
}

Diff for: libraries/Zigbee/src/ZigbeeCore.cpp

+76-1
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
243243
} else {
244244
Zigbee._connected = true;
245245
}
246+
Zigbee.searchBindings();
246247
}
247248
} else {
248249
/* commissioning failed */
@@ -309,7 +310,6 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
309310
Bit 6 – Security capability
310311
Bit 7 – Reserved
311312
*/
312-
313313
// for each endpoint in the list call the findEndpoint function if not bounded or allowed to bind multiple devices
314314
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
315315
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
@@ -329,6 +329,12 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
329329
}
330330
}
331331
break;
332+
case ESP_ZB_ZDO_SIGNAL_LEAVE: // End Device + Router
333+
// Device was removed from the network, factory reset the device
334+
if ((zigbee_role_t)Zigbee.getRole() != ZIGBEE_COORDINATOR) {
335+
Zigbee.factoryReset();
336+
}
337+
break;
332338
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;
333339
}
334340
}
@@ -391,6 +397,75 @@ void ZigbeeCore::scanDelete() {
391397
_scan_status = ZB_SCAN_FAILED;
392398
}
393399

400+
// Recall bounded devices from the binding table after reboot
401+
void ZigbeeCore::bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx) {
402+
bool done = true;
403+
esp_zb_zdo_mgmt_bind_param_t *req = (esp_zb_zdo_mgmt_bind_param_t *)user_ctx;
404+
esp_zb_zdp_status_t zdo_status = (esp_zb_zdp_status_t)table_info->status;
405+
log_d("Binding table callback for address 0x%04x with status %d", req->dst_addr, zdo_status);
406+
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
407+
// Print binding table log simple
408+
log_d("Binding table info: total %d, index %d, count %d", table_info->total, table_info->index, table_info->count);
409+
410+
if (table_info->total == 0) {
411+
log_d("No binding table entries found");
412+
free(req);
413+
return;
414+
}
415+
416+
esp_zb_zdo_binding_table_record_t *record = table_info->record;
417+
for (int i = 0; i < table_info->count; i++) {
418+
log_d(
419+
"Binding table record: src_endp %d, dst_endp %d, cluster_id 0x%04x, dst_addr_mode %d", record->src_endp, record->dst_endp, record->cluster_id,
420+
record->dst_addr_mode
421+
);
422+
423+
zb_device_params_t *device = (zb_device_params_t *)calloc(1, sizeof(zb_device_params_t));
424+
device->endpoint = record->dst_endp;
425+
if (record->dst_addr_mode == ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT || record->dst_addr_mode == ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT) {
426+
device->short_addr = record->dst_address.addr_short;
427+
} else { //ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT
428+
memcpy(device->ieee_addr, record->dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
429+
}
430+
431+
// Add to list of bound devices of proper endpoint
432+
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
433+
if ((*it)->getEndpoint() == record->src_endp) {
434+
(*it)->addBoundDevice(device);
435+
log_d(
436+
"Device bound to EP %d -> device endpoint: %d, short addr: 0x%04x, ieee addr: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", record->src_endp,
437+
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],
438+
device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0]
439+
);
440+
}
441+
}
442+
record = record->next;
443+
}
444+
445+
// Continue reading the binding table
446+
if (table_info->index + table_info->count < table_info->total) {
447+
/* There are unreported binding table entries, request for them. */
448+
req->start_index = table_info->index + table_info->count;
449+
esp_zb_zdo_binding_table_req(req, bindingTableCb, req);
450+
done = false;
451+
}
452+
}
453+
454+
if (done) {
455+
// Print bound devices
456+
log_d("Filling bounded devices finished");
457+
free(req);
458+
}
459+
}
460+
461+
void ZigbeeCore::searchBindings() {
462+
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));
463+
mb_req->dst_addr = esp_zb_get_short_address();
464+
mb_req->start_index = 0;
465+
log_d("Requesting binding table for address 0x%04x", mb_req->dst_addr);
466+
esp_zb_zdo_binding_table_req(mb_req, bindingTableCb, (void *)mb_req);
467+
}
468+
394469
// Function to convert enum value to string
395470
const char *ZigbeeCore::getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId) {
396471
switch (deviceId) {

Diff for: libraries/Zigbee/src/ZigbeeCore.h

+2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ class ZigbeeCore {
8080
bool zigbeeInit(esp_zb_cfg_t *zb_cfg, bool erase_nvs);
8181
static void scanCompleteCallback(esp_zb_zdp_status_t zdo_status, uint8_t count, esp_zb_network_descriptor_t *nwk_descriptor);
8282
const char *getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId);
83+
void searchBindings();
84+
static void bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx);
8385

8486
public:
8587
ZigbeeCore();

Diff for: libraries/Zigbee/src/ZigbeeEP.cpp

+45-11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include "esp_zigbee_cluster.h"
88
#include "zcl/esp_zigbee_zcl_power_config.h"
99

10+
#define ZB_CMD_TIMEOUT 10000 // 10 seconds
11+
1012
bool ZigbeeEP::_is_bound = false;
1113
bool ZigbeeEP::_allow_multiple_binding = false;
1214

@@ -112,13 +114,20 @@ void ZigbeeEP::reportBatteryPercentage() {
112114
log_v("Battery percentage reported");
113115
}
114116

115-
char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr) {
117+
char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
116118
/* Read peer Manufacture Name & Model Identifier */
117119
esp_zb_zcl_read_attr_cmd_t read_req;
118-
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
120+
121+
if (short_addr != 0) {
122+
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
123+
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
124+
} else {
125+
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
126+
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
127+
}
128+
119129
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
120130
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
121-
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
122131
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BASIC;
123132

124133
uint16_t attributes[] = {
@@ -130,22 +139,31 @@ char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr) {
130139
// clear read manufacturer
131140
_read_manufacturer = nullptr;
132141

142+
esp_zb_lock_acquire(portMAX_DELAY);
133143
esp_zb_zcl_read_attr_cmd_req(&read_req);
144+
esp_zb_lock_release();
134145

135146
//Wait for response or timeout
136-
if (xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE) {
147+
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
137148
log_e("Error while reading manufacturer");
138149
}
139150
return _read_manufacturer;
140151
}
141152

142-
char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr) {
153+
char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
143154
/* Read peer Manufacture Name & Model Identifier */
144155
esp_zb_zcl_read_attr_cmd_t read_req;
145-
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
156+
157+
if (short_addr != 0) {
158+
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
159+
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
160+
} else {
161+
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT;
162+
memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t));
163+
}
164+
146165
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
147166
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
148-
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
149167
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BASIC;
150168

151169
uint16_t attributes[] = {
@@ -157,11 +175,12 @@ char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr) {
157175
// clear read model
158176
_read_model = nullptr;
159177

178+
esp_zb_lock_acquire(portMAX_DELAY);
160179
esp_zb_zcl_read_attr_cmd_req(&read_req);
180+
esp_zb_lock_release();
161181

162182
//Wait for response or timeout
163-
//Semaphore take
164-
if (xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE) {
183+
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
165184
log_e("Error while reading model");
166185
}
167186
return _read_model;
@@ -171,8 +190,23 @@ void ZigbeeEP::printBoundDevices() {
171190
log_i("Bound devices:");
172191
for ([[maybe_unused]]
173192
const auto &device : _bound_devices) {
174-
log_i("Device on endpoint %d, short address: 0x%x", device->endpoint, device->short_addr);
175-
print_ieee_addr(device->ieee_addr);
193+
log_i(
194+
"Device on endpoint %d, short address: 0x%x, ieee address: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", device->endpoint, device->short_addr,
195+
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],
196+
device->ieee_addr[0]
197+
);
198+
}
199+
}
200+
201+
void ZigbeeEP::printBoundDevices(Print &print) {
202+
print.println("Bound devices:");
203+
for ([[maybe_unused]]
204+
const auto &device : _bound_devices) {
205+
print.printf(
206+
"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,
207+
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],
208+
device->ieee_addr[0]
209+
);
176210
}
177211
}
178212

Diff for: libraries/Zigbee/src/ZigbeeEP.h

+9-5
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99

1010
/* Useful defines */
1111
#define ZB_ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0]))
12-
#define print_ieee_addr(addr) \
13-
log_i("IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7])
1412
#define XYZ_TO_RGB(X, Y, Z, r, g, b) \
1513
{ \
1614
r = (float)(3.240479 * (X) - 1.537150 * (Y) - 0.498535 * (Z)); \
@@ -69,6 +67,8 @@ class ZigbeeEP {
6967
}
7068

7169
void printBoundDevices();
70+
void printBoundDevices(Print &print);
71+
7272
std::list<zb_device_params_t *> getBoundDevices() const {
7373
return _bound_devices;
7474
}
@@ -87,8 +87,8 @@ class ZigbeeEP {
8787
void reportBatteryPercentage();
8888

8989
// Methods to read manufacturer and model name from selected endpoint and short address
90-
char *readManufacturer(uint8_t endpoint, uint16_t short_addr);
91-
char *readModel(uint8_t endpoint, uint16_t short_addr);
90+
char *readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr);
91+
char *readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr);
9292

9393
bool epAllowMultipleBinding() {
9494
return _allow_multiple_binding;
@@ -108,7 +108,6 @@ class ZigbeeEP {
108108
}
109109

110110
private:
111-
static bool _allow_multiple_binding;
112111
char *_read_manufacturer;
113112
char *_read_model;
114113
void (*_on_identify)(uint16_t time);
@@ -119,10 +118,15 @@ class ZigbeeEP {
119118
esp_zb_endpoint_config_t _ep_config;
120119
esp_zb_cluster_list_t *_cluster_list;
121120
static bool _is_bound;
121+
static bool _allow_multiple_binding;
122122
std::list<zb_device_params_t *> _bound_devices;
123123
SemaphoreHandle_t lock;
124124
zb_power_source_t _power_source;
125125

126+
void addBoundDevice(zb_device_params_t *device) {
127+
_bound_devices.push_back(device);
128+
_is_bound = true;
129+
}
126130
friend class ZigbeeCore;
127131
};
128132

0 commit comments

Comments
 (0)