Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -137,18 +137,17 @@ void setup() {
Serial.printf(".");
delay(500);
}

// Optional: read manufacturer and model name from the bound light
// Optional: List all bound devices and read manufacturer and model name
std::list<zb_device_params_t *> boundLights = zbSwitch.getBoundDevices();
//List all bound lights
for (const auto &device : boundLights) {
Serial.printf("Device on endpoint %d, short address: 0x%x\n", device->endpoint, device->short_addr);
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\n", device->ieee_addr[0], device->ieee_addr[1], device->ieee_addr[2], device->ieee_addr[3],
device->ieee_addr[4], device->ieee_addr[5], device->ieee_addr[6], device->ieee_addr[7]
"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]
);
Serial.printf("Light manufacturer: %s", zbSwitch.readManufacturer(device->endpoint, device->short_addr));
Serial.printf("Light model: %s", zbSwitch.readModel(device->endpoint, device->short_addr));
Serial.printf("Light manufacturer: %s\r\n", zbSwitch.readManufacturer(device->endpoint, device->short_addr, device->ieee_addr));
Serial.printf("Light model: %s\r\n", zbSwitch.readModel(device->endpoint, device->short_addr, device->ieee_addr));
}

Serial.println();
Expand Down Expand Up @@ -191,6 +190,6 @@ void loop() {
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 10000) {
lastPrint = millis();
zbSwitch.printBoundDevices();
zbSwitch.printBoundDevices(&Serial);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be zbSwitch.printBoundDevices(Serial); with the new API

}
}
}
74 changes: 72 additions & 2 deletions libraries/Zigbee/src/ZigbeeCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
} else {
Zigbee._connected = true;
}
Zigbee.searchBindings();
}
} else {
/* commissioning failed */
Expand Down Expand Up @@ -309,8 +310,7 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
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 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) {
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
(*it)->findEndpoint(&cmd_req);
Expand All @@ -329,6 +329,12 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
}
}
break;
case ESP_ZB_ZDO_SIGNAL_LEAVE: // End Device + Router
// Device was removed from the network, factory reset the device
if ((zigbee_role_t)Zigbee.getRole() != ZIGBEE_COORDINATOR) {
Zigbee.factoryReset();
}
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;
}
}
Expand Down Expand Up @@ -391,6 +397,70 @@ void ZigbeeCore::scanDelete() {
_scan_status = ZB_SCAN_FAILED;
}

// Recall bounded devices from the binding table after reboot
void ZigbeeCore::bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx)
{
bool done = true;
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");
free(req);
return;
}

esp_zb_zdo_binding_table_record_t *record = table_info->record;
for (int i = 0; i < table_info->count; i++) {
log_d("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, record->dst_addr_mode);

zb_device_params_t *device = (zb_device_params_t *)calloc(1, sizeof(zb_device_params_t));
device->endpoint = record->dst_endp;
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) {
device->short_addr = record->dst_address.addr_short;
} else { //ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT
memcpy(device->ieee_addr, record->dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
}

// Add to list of bound devices of proper endpoint
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if ((*it)->getEndpoint() == record->src_endp) {
(*it)->addBoundDevice(device);
log_d("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, 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]);
}
}
record = record->next;
}

// Continue reading the binding table
if (table_info->index + table_info->count < table_info->total) {
/* There are unreported binding table entries, request for them. */
req->start_index = table_info->index + table_info->count;
esp_zb_zdo_binding_table_req(req, bindingTableCb, req);
done = false;
}
}

if (done) {
// Print bound devices
log_d("Filling bounded devices finished");
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);
}

// Function to convert enum value to string
const char *ZigbeeCore::getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId) {
switch (deviceId) {
Expand Down
2 changes: 2 additions & 0 deletions libraries/Zigbee/src/ZigbeeCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ class ZigbeeCore {
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);

public:
ZigbeeCore();
Expand Down
52 changes: 40 additions & 12 deletions libraries/Zigbee/src/ZigbeeEP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "esp_zigbee_cluster.h"
#include "zcl/esp_zigbee_zcl_power_config.h"

#define ZB_CMD_TIMEOUT 10000 // 10 seconds

bool ZigbeeEP::_is_bound = false;
bool ZigbeeEP::_allow_multiple_binding = false;

Expand Down Expand Up @@ -112,13 +114,20 @@ void ZigbeeEP::reportBatteryPercentage() {
log_v("Battery percentage reported");
}

char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr) {
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;
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;

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.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BASIC;

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

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, portMAX_DELAY) != pdTRUE) {
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) {
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;
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;

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.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BASIC;

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

esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_read_attr_cmd_req(&read_req);
esp_zb_lock_release();

//Wait for response or timeout
//Semaphore take
if (xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE) {
if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) {
log_e("Error while reading model");
}
return _read_model;
Expand All @@ -170,9 +189,18 @@ char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr) {
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", device->endpoint, device->short_addr);
print_ieee_addr(device->ieee_addr);
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]);
}
}

Expand Down
15 changes: 10 additions & 5 deletions libraries/Zigbee/src/ZigbeeEP.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

/* Useful defines */
#define ZB_ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0]))
#define print_ieee_addr(addr) \
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])
#define XYZ_TO_RGB(X, Y, Z, r, g, b) \
{ \
r = (float)(3.240479 * (X) - 1.537150 * (Y) - 0.498535 * (Z)); \
Expand Down Expand Up @@ -69,6 +67,8 @@ class ZigbeeEP {
}

void printBoundDevices();
void printBoundDevices(Print &print);

std::list<zb_device_params_t *> getBoundDevices() const {
return _bound_devices;
}
Expand All @@ -87,8 +87,8 @@ class ZigbeeEP {
void reportBatteryPercentage();

// Methods to read manufacturer and model name from selected endpoint and short address
char *readManufacturer(uint8_t endpoint, uint16_t short_addr);
char *readModel(uint8_t endpoint, uint16_t short_addr);
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);

bool epAllowMultipleBinding() {
return _allow_multiple_binding;
Expand All @@ -108,7 +108,7 @@ class ZigbeeEP {
}

private:
static bool _allow_multiple_binding;

char *_read_manufacturer;
char *_read_model;
void (*_on_identify)(uint16_t time);
Expand All @@ -119,10 +119,15 @@ class ZigbeeEP {
esp_zb_endpoint_config_t _ep_config;
esp_zb_cluster_list_t *_cluster_list;
static bool _is_bound;
static bool _allow_multiple_binding;
std::list<zb_device_params_t *> _bound_devices;
SemaphoreHandle_t lock;
zb_power_source_t _power_source;

void addBoundDevice(zb_device_params_t *device) {
_bound_devices.push_back(device);
_is_bound = true;
}
friend class ZigbeeCore;
};

Expand Down
Loading
Loading