Skip to content

Commit cb3329b

Browse files
feat(zigbee): Add callback option for default response message (#11613)
* feat(zigbee): Add cb option for default response message * fix(example): Add timeout and fix spelling * feat(zigbee): Add global default response cb option * fix(example): Use task for measure and sleep * fix(zigbee): Remove debug logs * ci(pre-commit): Apply automatic fixes * fix(example): Add retry and fix typo * 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 6015fd7 commit cb3329b

File tree

9 files changed

+158
-14
lines changed

9 files changed

+158
-14
lines changed

libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/Zigbee_Temp_Hum_Sensor_Sleepy.ino

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,49 @@
3232

3333
#include "Zigbee.h"
3434

35+
#define USE_GLOBAL_ON_RESPONSE_CALLBACK 1 // Set to 0 to use local callback specified directly for the endpoint.
36+
3537
/* Zigbee temperature + humidity sensor configuration */
3638
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
3739

3840
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
3941
#define TIME_TO_SLEEP 55 /* Sleep for 55s will + 5s delay for establishing connection => data reported every 1 minute */
42+
#define REPORT_TIMEOUT 1000 /* Timeout for response from coordinator in ms */
4043

4144
uint8_t button = BOOT_PIN;
4245

4346
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
4447

48+
uint8_t dataToSend = 2; // Temperature and humidity values are reported in same endpoint, so 2 values are reported
49+
bool resend = false;
50+
51+
/************************ Callbacks *****************************/
52+
#if USE_GLOBAL_ON_RESPONSE_CALLBACK
53+
void onGlobalResponse(zb_cmd_type_t command, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster) {
54+
Serial.printf("Global response command: %d, status: %s, endpoint: %d, cluster: 0x%04x\r\n", command, esp_zb_zcl_status_to_name(status), endpoint, cluster);
55+
if ((command == ZB_CMD_REPORT_ATTRIBUTE) && (endpoint == TEMP_SENSOR_ENDPOINT_NUMBER)) {
56+
switch (status) {
57+
case ESP_ZB_ZCL_STATUS_SUCCESS: dataToSend--; break;
58+
case ESP_ZB_ZCL_STATUS_FAIL: resend = true; break;
59+
default: break; // add more statuses like ESP_ZB_ZCL_STATUS_INVALID_VALUE, ESP_ZB_ZCL_STATUS_TIMEOUT etc.
60+
}
61+
}
62+
}
63+
#else
64+
void onResponse(zb_cmd_type_t command, esp_zb_zcl_status_t status) {
65+
Serial.printf("Response command: %d, status: %s\r\n", command, esp_zb_zcl_status_to_name(status));
66+
if (command == ZB_CMD_REPORT_ATTRIBUTE) {
67+
switch (status) {
68+
case ESP_ZB_ZCL_STATUS_SUCCESS: dataToSend--; break;
69+
case ESP_ZB_ZCL_STATUS_FAIL: resend = true; break;
70+
default: break; // add more statuses like ESP_ZB_ZCL_STATUS_INVALID_VALUE, ESP_ZB_ZCL_STATUS_TIMEOUT etc.
71+
}
72+
}
73+
}
74+
#endif
75+
4576
/************************ Temp sensor *****************************/
46-
void meausureAndSleep() {
77+
static void meausureAndSleep(void *arg) {
4778
// Measure temperature sensor value
4879
float temperature = temperatureRead();
4980

@@ -55,13 +86,35 @@ void meausureAndSleep() {
5586
zbTempSensor.setHumidity(humidity);
5687

5788
// Report temperature and humidity values
58-
zbTempSensor.report();
89+
zbTempSensor.report(); // reports temperature and humidity values (if humidity sensor is not added, only temperature is reported)
5990
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
6091

61-
// Add small delay to allow the data to be sent before going to sleep
62-
delay(100);
92+
unsigned long startTime = millis();
93+
const unsigned long timeout = REPORT_TIMEOUT;
94+
95+
Serial.printf("Waiting for data report to be confirmed \r\n");
96+
// Wait until data was successfully sent
97+
int tries = 0;
98+
const int maxTries = 3;
99+
while (dataToSend != 0 && tries < maxTries) {
100+
if (resend) {
101+
Serial.println("Resending data on failure!");
102+
resend = false;
103+
dataToSend = 2;
104+
zbTempSensor.report(); // report again
105+
}
106+
if (millis() - startTime >= timeout) {
107+
Serial.println("\nReport timeout! Report Again");
108+
dataToSend = 2;
109+
zbTempSensor.report(); // report again
110+
startTime = millis();
111+
tries++;
112+
}
113+
Serial.printf(".");
114+
delay(50); // 50ms delay to avoid busy-waiting
115+
}
63116

64-
// Put device to deep sleep
117+
// Put device to deep sleep after data was sent successfully or timeout
65118
Serial.println("Going to sleep now");
66119
esp_deep_sleep_start();
67120
}
@@ -92,6 +145,16 @@ void setup() {
92145
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
93146
zbTempSensor.addHumiditySensor(0, 100, 1);
94147

148+
// Set callback for default response to handle status of reported data, there are 2 options.
149+
150+
#if USE_GLOBAL_ON_RESPONSE_CALLBACK
151+
// Global callback for all endpoints with more params to determine the endpoint and cluster in the callback function.
152+
Zigbee.onGlobalDefaultResponse(onGlobalResponse);
153+
#else
154+
// Callback specified for endpoint
155+
zbTempSensor.onDefaultResponse(onResponse);
156+
#endif
157+
95158
// Add endpoint to Zigbee Core
96159
Zigbee.addEndpoint(&zbTempSensor);
97160

@@ -117,8 +180,8 @@ void setup() {
117180
Serial.println();
118181
Serial.println("Successfully connected to Zigbee network");
119182

120-
// Delay approx 1s (may be adjusted) to allow establishing proper connection with coordinator, needed for sleepy devices
121-
delay(1000);
183+
// Start Temperature sensor reading task
184+
xTaskCreate(meausureAndSleep, "temp_sensor_update", 2048, NULL, 10, NULL);
122185
}
123186

124187
void loop() {
@@ -141,7 +204,5 @@ void loop() {
141204
}
142205
}
143206
}
144-
145-
// Call the function to measure temperature and put the device to sleep
146-
meausureAndSleep();
207+
delay(100);
147208
}

libraries/Zigbee/keywords.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ zb_power_source_t KEYWORD1
4747
ZigbeeWindowCoveringType KEYWORD1
4848
ZigbeeFanMode KEYWORD1
4949
ZigbeeFanModeSequence KEYWORD1
50+
zb_cmd_type_t KEYWORD1
5051

5152
#######################################
5253
# Methods and Functions (KEYWORD2)
@@ -96,6 +97,7 @@ getTime KEYWORD2
9697
getTimezone KEYWORD2
9798
addOTAClient KEYWORD2
9899
clearBoundDevices KEYWORD2
100+
onDefaultResponse KEYWORD2
99101

100102
# ZigbeeLight + ZigbeeColorDimmableLight
101103
onLightChange KEYWORD2

libraries/Zigbee/src/Zigbee.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
#pragma once
44

5+
// Common types and functions
6+
#include "ZigbeeTypes.h"
7+
58
// Core
69
#include "ZigbeeCore.h"
710
#include "ZigbeeEP.h"

libraries/Zigbee/src/ZigbeeCore.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ ZigbeeCore::ZigbeeCore() {
3232
_scan_duration = 3; // default scan duration
3333
_rx_on_when_idle = true;
3434
_debug = false;
35+
_global_default_response_cb = nullptr; // Initialize global callback to nullptr
3536
if (!lock) {
3637
lock = xSemaphoreCreateBinary();
3738
if (lock == NULL) {
@@ -792,6 +793,12 @@ const char *ZigbeeCore::getDeviceTypeString(esp_zb_ha_standard_devices_t deviceI
792793
}
793794
}
794795

796+
void ZigbeeCore::callDefaultResponseCallback(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster) {
797+
if (_global_default_response_cb) {
798+
_global_default_response_cb(resp_to_cmd, status, endpoint, cluster);
799+
}
800+
}
801+
795802
ZigbeeCore Zigbee = ZigbeeCore();
796803

797804
#endif // CONFIG_ZB_ENABLED

libraries/Zigbee/src/ZigbeeCore.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "aps/esp_zigbee_aps.h"
1212
#include <esp32-hal-log.h>
1313
#include <list>
14+
#include "ZigbeeTypes.h"
1415
#include "ZigbeeEP.h"
1516
class ZigbeeEP;
1617

@@ -103,6 +104,9 @@ class ZigbeeCore {
103104
SemaphoreHandle_t lock;
104105
bool _debug;
105106

107+
// Global default response callback
108+
void (*_global_default_response_cb)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster);
109+
106110
bool zigbeeInit(esp_zb_cfg_t *zb_cfg, bool erase_nvs);
107111
static void scanCompleteCallback(esp_zb_zdp_status_t zdo_status, uint8_t count, esp_zb_network_descriptor_t *nwk_descriptor);
108112
const char *getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId);
@@ -176,6 +180,14 @@ class ZigbeeCore {
176180
return _debug;
177181
}
178182

183+
// Set global default response callback
184+
void onGlobalDefaultResponse(void (*callback)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster)) {
185+
_global_default_response_cb = callback;
186+
}
187+
188+
// Call global default response callback (for internal use)
189+
void callDefaultResponseCallback(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster);
190+
179191
// Friend function declaration to allow access to private members
180192
friend void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct);
181193
friend bool zb_apsde_data_indication_handler(esp_zb_apsde_data_ind_t ind);

libraries/Zigbee/src/ZigbeeEP.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,17 @@ void ZigbeeEP::removeBoundDevice(zb_device_params_t *device) {
608608
log_w("No matching device found for removal");
609609
}
610610

611-
const char *ZigbeeEP::esp_zb_zcl_status_to_name(esp_zb_zcl_status_t status) {
611+
void ZigbeeEP::zbDefaultResponse(const esp_zb_zcl_cmd_default_resp_message_t *message) {
612+
log_v("Default response received for endpoint %d", _endpoint);
613+
log_v("Status code: %s", esp_zb_zcl_status_to_name(message->status_code));
614+
log_v("Response to command: %d", message->resp_to_cmd);
615+
if (_on_default_response) {
616+
_on_default_response((zb_cmd_type_t)message->resp_to_cmd, message->status_code);
617+
}
618+
}
619+
620+
// Global function implementation
621+
const char *esp_zb_zcl_status_to_name(esp_zb_zcl_status_t status) {
612622
switch (status) {
613623
case ESP_ZB_ZCL_STATUS_SUCCESS: return "Success";
614624
case ESP_ZB_ZCL_STATUS_FAIL: return "Fail";

libraries/Zigbee/src/ZigbeeEP.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ typedef enum {
3838
ZB_POWER_SOURCE_BATTERY = 0x03,
3939
} zb_power_source_t;
4040

41+
// Global function for converting ZCL status to name
42+
const char *esp_zb_zcl_status_to_name(esp_zb_zcl_status_t status);
43+
4144
/* Zigbee End Device Class */
4245
class ZigbeeEP {
4346
public:
@@ -138,6 +141,7 @@ class ZigbeeEP {
138141
virtual void zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented
139142
virtual void zbIASZoneStatusChangeNotification(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message) {};
140143
virtual void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) {};
144+
virtual void zbDefaultResponse(const esp_zb_zcl_cmd_default_resp_message_t *message); //already implemented
141145

142146
virtual void addBoundDevice(zb_device_params_t *device) {
143147
_bound_devices.push_back(device);
@@ -156,17 +160,21 @@ class ZigbeeEP {
156160
_on_identify = callback;
157161
}
158162

163+
void onDefaultResponse(void (*callback)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status)) {
164+
_on_default_response = callback;
165+
}
166+
167+
// Convert ZCL status to name
168+
159169
private:
160170
char *_read_manufacturer;
161171
char *_read_model;
162172
void (*_on_identify)(uint16_t time);
173+
void (*_on_default_response)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status);
163174
time_t _read_time;
164175
int32_t _read_timezone;
165176

166177
protected:
167-
// Convert ZCL status to name
168-
const char *esp_zb_zcl_status_to_name(esp_zb_zcl_status_t status);
169-
170178
uint8_t _endpoint;
171179
esp_zb_ha_standard_devices_t _device_id;
172180
esp_zb_endpoint_config_t _ep_config;
@@ -179,6 +187,7 @@ class ZigbeeEP {
179187
zb_power_source_t _power_source;
180188
uint8_t _time_status;
181189

190+
// Friend class declaration to allow access to protected members
182191
friend class ZigbeeCore;
183192
};
184193

libraries/Zigbee/src/ZigbeeHandlers.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,16 @@ static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_m
398398
"Received default response: from address(0x%x), src_endpoint(%d) to dst_endpoint(%d), cluster(0x%x) with status 0x%x",
399399
message->info.src_address.u.short_addr, message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster, message->status_code
400400
);
401+
402+
// Call global callback if set
403+
Zigbee.callDefaultResponseCallback((zb_cmd_type_t)message->resp_to_cmd, message->status_code, message->info.dst_endpoint, message->info.cluster);
404+
405+
// List through all Zigbee EPs and call the callback function, with the message
406+
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
407+
if (message->info.dst_endpoint == (*it)->getEndpoint()) {
408+
(*it)->zbDefaultResponse(message); //method zbDefaultResponse is implemented in the common EP class
409+
}
410+
}
401411
return ESP_OK;
402412
}
403413

libraries/Zigbee/src/ZigbeeTypes.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#pragma once
2+
3+
#include "esp_zigbee_core.h"
4+
5+
// Foundation Command Types
6+
typedef enum {
7+
ZB_CMD_READ_ATTRIBUTE = 0x00U, /*!< Read attributes command */
8+
ZB_CMD_READ_ATTRIBUTE_RESPONSE = 0x01U, /*!< Read attributes response command */
9+
ZB_CMD_WRITE_ATTRIBUTE = 0x02U, /*!< Write attributes foundation command */
10+
ZB_CMD_WRITE_ATTRIBUTE_UNDIVIDED = 0x03U, /*!< Write attributes undivided command */
11+
ZB_CMD_WRITE_ATTRIBUTE_RESPONSE = 0x04U, /*!< Write attributes response command */
12+
ZB_CMD_WRITE_ATTRIBUTE_NO_RESPONSE = 0x05U, /*!< Write attributes no response command */
13+
ZB_CMD_CONFIGURE_REPORTING = 0x06U, /*!< Configure reporting command */
14+
ZB_CMD_CONFIGURE_REPORTING_RESPONSE = 0x07U, /*!< Configure reporting response command */
15+
ZB_CMD_READ_REPORTING_CONFIG = 0x08U, /*!< Read reporting config command */
16+
ZB_CMD_READ_REPORTING_CONFIG_RESPONSE = 0x09U, /*!< Read reporting config response command */
17+
ZB_CMD_REPORT_ATTRIBUTE = 0x0aU, /*!< Report attribute command */
18+
ZB_CMD_DEFAULT_RESPONSE = 0x0bU, /*!< Default response command */
19+
ZB_CMD_DISCOVER_ATTRIBUTES = 0x0cU, /*!< Discover attributes command */
20+
ZB_CMD_DISCOVER_ATTRIBUTES_RESPONSE = 0x0dU, /*!< Discover attributes response command */
21+
ZB_CMD_READ_ATTRIBUTE_STRUCTURED = 0x0eU, /*!< Read attributes structured */
22+
ZB_CMD_WRITE_ATTRIBUTE_STRUCTURED = 0x0fU, /*!< Write attributes structured */
23+
ZB_CMD_WRITE_ATTRIBUTE_STRUCTURED_RESPONSE = 0x10U, /*!< Write attributes structured response */
24+
ZB_CMD_DISCOVER_COMMANDS_RECEIVED = 0x11U, /*!< Discover Commands Received command */
25+
ZB_CMD_DISCOVER_COMMANDS_RECEIVED_RESPONSE = 0x12U, /*!< Discover Commands Received response command */
26+
ZB_CMD_DISCOVER_COMMANDS_GENERATED = 0x13U, /*!< Discover Commands Generated command */
27+
ZB_CMD_DISCOVER_COMMANDS_GENERATED_RESPONSE = 0x14U, /*!< Discover Commands Generated response command */
28+
ZB_CMD_DISCOVER_ATTRIBUTES_EXTENDED = 0x15U, /*!< Discover attributes extended command */
29+
ZB_CMD_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE = 0x16U, /*!< Discover attributes extended response command */
30+
} zb_cmd_type_t;

0 commit comments

Comments
 (0)