Skip to content

feat(zigbee): Add endpoint identification in read handlers + command structures fix #11425

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 45 additions & 11 deletions libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
#include "Zigbee.h"

/* Zigbee thermostat configuration */
#define THERMOSTAT_ENDPOINT_NUMBER 5
#define THERMOSTAT_ENDPOINT_NUMBER 1
#define USE_RECEIVE_TEMP_WITH_SOURCE 1
uint8_t button = BOOT_PIN;

ZigbeeThermostat zbThermostat = ZigbeeThermostat(THERMOSTAT_ENDPOINT_NUMBER);
Expand All @@ -48,13 +49,28 @@ float sensor_tolerance;
struct tm timeinfo = {}; // Time structure for Time cluster

/****************** Temperature sensor handling *******************/
void recieveSensorTemp(float temperature) {
#if USE_RECEIVE_TEMP_WITH_SOURCE == 0
void receiveSensorTemp(float temperature) {
Serial.printf("Temperature sensor value: %.2f°C\n", temperature);
sensor_temp = temperature;
}
#else
void receiveSensorTempWithSource(float temperature, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {
if (src_address.addr_type == ESP_ZB_ZCL_ADDR_TYPE_SHORT) {
Serial.printf("Temperature sensor value: %.2f°C from endpoint %d, address 0x%04x\n", temperature, src_endpoint, src_address.u.short_addr);
} else {
Serial.printf(
"Temperature sensor value: %.2f°C from endpoint %d, address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", temperature, src_endpoint,
src_address.u.ieee_addr[7], src_address.u.ieee_addr[6], src_address.u.ieee_addr[5], src_address.u.ieee_addr[4], src_address.u.ieee_addr[3],
src_address.u.ieee_addr[2], src_address.u.ieee_addr[1], src_address.u.ieee_addr[0]
);
}
sensor_temp = temperature;
}
#endif

void recieveSensorConfig(float min_temp, float max_temp, float tolerance) {
Serial.printf("Temperature sensor settings: min %.2f°C, max %.2f°C, tolerance %.2f°C\n", min_temp, max_temp, tolerance);
void receiveSensorConfig(float min_temp, float max_temp, float tolerance) {
Serial.printf("Temperature sensor config: min %.2f°C, max %.2f°C, tolerance %.2f°C\n", min_temp, max_temp, tolerance);
sensor_min_temp = min_temp;
sensor_max_temp = max_temp;
sensor_tolerance = tolerance;
Expand All @@ -66,9 +82,15 @@ void setup() {
// Init button switch
pinMode(button, INPUT_PULLUP);

// Set callback functions for temperature and configuration receive
zbThermostat.onTempRecieve(recieveSensorTemp);
zbThermostat.onConfigRecieve(recieveSensorConfig);
// Set callback function for receiving temperature from sensor - Use only one option
#if USE_RECEIVE_TEMP_WITH_SOURCE == 0
zbThermostat.onTempReceive(receiveSensorTemp); // If you bound only one sensor or you don't need to know the source of the temperature
#else
zbThermostat.onTempReceiveWithSource(receiveSensorTempWithSource);
#endif

// Set callback function for receiving sensor configuration
zbThermostat.onConfigReceive(receiveSensorConfig);

//Optional: set Zigbee device name and model
zbThermostat.setManufacturerAndModel("Espressif", "ZigbeeThermostat");
Expand Down Expand Up @@ -107,19 +129,30 @@ void setup() {

Serial.println();

// Get temperature sensor configuration
zbThermostat.getSensorSettings();
// Get temperature sensor configuration for all bound sensors by endpoint number and address
std::list<zb_device_params_t *> boundSensors = zbThermostat.getBoundDevices();
for (const auto &device : boundSensors) {
Serial.println("--------------------------------");
if (device->short_addr == 0x0000 || device->short_addr == 0xFFFF) { //End devices never have 0x0000 short address or 0xFFFF group address
Serial.printf(
"Device on endpoint %d, IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", device->endpoint, 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]
);
zbThermostat.getSensorSettings(device->endpoint, device->ieee_addr);
} else {
Serial.printf("Device on endpoint %d, short address: 0x%x\r\n", device->endpoint, device->short_addr);
zbThermostat.getSensorSettings(device->endpoint, device->short_addr);
}
}
}

void loop() {
// Handle button switch in loop()
if (digitalRead(button) == LOW) { // Push button pressed

// Key debounce handling
while (digitalRead(button) == LOW) {
delay(50);
}

// Set reporting interval for temperature sensor
zbThermostat.setTemperatureReporting(0, 10, 2);
}
Expand All @@ -130,5 +163,6 @@ void loop() {
last_print = millis();
int temp_percent = (int)((sensor_temp - sensor_min_temp) / (sensor_max_temp - sensor_min_temp) * 100);
Serial.printf("Loop temperature info: %.2f°C (%d %%)\n", sensor_temp, temp_percent);
zbThermostat.printBoundDevices(Serial);
}
}
3 changes: 2 additions & 1 deletion libraries/Zigbee/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ setLightColor KEYWORD2
# ZigbeeThermostat
onTempRecieve KEYWORD2
onConfigRecieve KEYWORD2
onTempReceiveWithSource KEYWORD2
getTemperature KEYWORD2
getSensorSettings KEYWORD2
setTemperatureReporting KEYWORD2
Expand Down Expand Up @@ -191,4 +192,4 @@ ZIGBEE_DEFAULT_COORDINATOR_CONFIG LITERAL1
ZIGBEE_DEFAULT_RADIO_CONFIG LITERAL1
ZIGBEE_DEFAULT_UART_RCP_RADIO_CONFIG LITERAL1
ZIGBEE_DEFAULT_HOST_CONFIG LITERAL1
ZB_ARRAY_LENTH LITERAL1
ZB_ARRAY_LENGHT LITERAL1
20 changes: 10 additions & 10 deletions libraries/Zigbee/src/ZigbeeEP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ bool ZigbeeEP::setBatteryVoltage(uint8_t voltage) {

bool ZigbeeEP::reportBatteryPercentage() {
/* Send report attributes command */
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
esp_zb_zcl_report_attr_cmd_t report_attr_cmd = {0};
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID;
report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
Expand All @@ -166,7 +166,7 @@ bool ZigbeeEP::reportBatteryPercentage() {

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;
esp_zb_zcl_read_attr_cmd_t read_req = {0};

if (short_addr != 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
Expand All @@ -183,7 +183,7 @@ char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_i
uint16_t attributes[] = {
ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID,
};
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
read_req.attr_number = ZB_ARRAY_LENGHT(attributes);
read_req.attr_field = attributes;

if (_read_manufacturer != NULL) {
Expand All @@ -204,7 +204,7 @@ char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_i

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;
esp_zb_zcl_read_attr_cmd_t read_req = {0};

if (short_addr != 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
Expand All @@ -221,7 +221,7 @@ char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_add
uint16_t attributes[] = {
ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID,
};
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
read_req.attr_number = ZB_ARRAY_LENGHT(attributes);
read_req.attr_field = attributes;

if (_read_model != NULL) {
Expand Down Expand Up @@ -375,7 +375,7 @@ bool ZigbeeEP::setTimezone(int32_t gmt_offset) {

tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
/* Read peer time */
esp_zb_zcl_read_attr_cmd_t read_req;
esp_zb_zcl_read_attr_cmd_t read_req = {0};

if (short_addr >= 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
Expand All @@ -386,7 +386,7 @@ tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ie
}

uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ID};
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
read_req.attr_number = ZB_ARRAY_LENGHT(attributes);
read_req.attr_field = attributes;

read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME;
Expand Down Expand Up @@ -427,7 +427,7 @@ tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ie

int32_t ZigbeeEP::getTimezone(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) {
/* Read peer timezone */
esp_zb_zcl_read_attr_cmd_t read_req;
esp_zb_zcl_read_attr_cmd_t read_req = {0};

if (short_addr >= 0) {
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
Expand All @@ -438,7 +438,7 @@ int32_t ZigbeeEP::getTimezone(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_
}

uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ZONE_ID};
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
read_req.attr_number = ZB_ARRAY_LENGHT(attributes);
read_req.attr_field = attributes;

read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME;
Expand Down Expand Up @@ -543,7 +543,7 @@ static void findOTAServer(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t
}

void ZigbeeEP::requestOTAUpdate() {
esp_zb_zdo_match_desc_req_param_t req;
esp_zb_zdo_match_desc_req_param_t req = {0};
uint16_t cluster_list[] = {ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE};

/* Match the OTA server of coordinator */
Expand Down
4 changes: 2 additions & 2 deletions libraries/Zigbee/src/ZigbeeEP.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#define ZB_CMD_TIMEOUT 10000 // 10 seconds
#define OTA_UPGRADE_QUERY_INTERVAL (1 * 60) // 1 hour = 60 minutes

#define ZB_ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0]))
#define ZB_ARRAY_LENGHT(arr) (sizeof(arr) / sizeof(arr[0]))

#define RGB_TO_XYZ(r, g, b, X, Y, Z) \
{ \
Expand Down Expand Up @@ -131,7 +131,7 @@ class ZigbeeEP {

// list of all handlers function calls, to be override by EPs implementation
virtual void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {};
virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {};
virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {};
virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented
virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message);
virtual void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) {};
Expand Down
8 changes: 6 additions & 2 deletions libraries/Zigbee/src/ZigbeeHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_mes
// List through all Zigbee EPs and call the callback function, with the message
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
if (message->dst_endpoint == (*it)->getEndpoint()) {
(*it)->zbAttributeRead(message->cluster, &message->attribute); //method zbAttributeRead must be implemented in specific EP class
(*it)->zbAttributeRead(
message->cluster, &message->attribute, message->src_endpoint, message->src_address
); //method zbAttributeRead must be implemented in specific EP class
}
}
return ESP_OK;
Expand Down Expand Up @@ -142,7 +144,9 @@ static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_re
} else if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_TIME) {
(*it)->zbReadTimeCluster(&variable->attribute); //method zbReadTimeCluster implemented in the common EP class
} else {
(*it)->zbAttributeRead(message->info.cluster, &variable->attribute); //method zbAttributeRead must be implemented in specific EP class
(*it)->zbAttributeRead(
message->info.cluster, &variable->attribute, message->info.src_endpoint, message->info.src_address
); //method zbAttributeRead must be implemented in specific EP class
}
}
variable = variable->next;
Expand Down
Loading
Loading