Skip to content

Commit 14b99db

Browse files
committed
Dev update: Factory reset, names, multiple EPs
Implemented Factory reset of Zigbee device, in order to connect to new network without reflashing/erasing flash Implemented optional setting for Manufacturer and Model names Added option to allow endpoint to have multiple endpoint connected -> switch - 2 lights (tested) Minor sketches update
1 parent 194693a commit 14b99db

File tree

11 files changed

+396
-118
lines changed

11 files changed

+396
-118
lines changed

libraries/Zigbee/examples/Zigbee_Light_Bulb/Zigbee_Light_Bulb.ino

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "ha/esp_zigbee_ha_standard.h"
4040

4141
#define LED_PIN RGB_BUILTIN
42+
#define BUTTON_PIN 9 // C6/H2 Boot button
4243
#define ZIGBEE_LIGHT_ENDPOINT 10 /* esp light bulb device endpoint, used to process light controlling commands */
4344

4445
class MyZigbeeLight : public ZigbeeLight {
@@ -48,7 +49,6 @@ public:
4849

4950
// Override the set_on_off function
5051
void setOnOff(bool value) override {
51-
log_v("Overwritten method, set on/off: %d", value);
5252
neopixelWrite(LED_PIN, 255 * value, 255 * value, 255 * value); // Toggle light
5353
}
5454
};
@@ -60,6 +60,12 @@ void setup() {
6060
// Init RMT and leave light OFF
6161
neopixelWrite(LED_PIN, 0, 0, 0);
6262

63+
// Init button for factory reset
64+
pinMode(BUTTON_PIN, INPUT);
65+
66+
//Optional: set Zigbee device name and model
67+
zbLight.setManufacturerAndModel("Espressif", "ZBLightBulb");
68+
6369
//Add endpoint to Zigbee Core
6470
log_d("Adding ZigbeeLight endpoint to Zigbee Core");
6571
Zigbee.addEndpoint(&zbLight);
@@ -70,5 +76,19 @@ void setup() {
7076
}
7177

7278
void loop() {
73-
//empty, zigbee running in task
79+
// Cheking button for factory reset
80+
if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed
81+
// Key debounce handling
82+
delay(100);
83+
int startTime = millis();
84+
while (digitalRead(BUTTON_PIN) == LOW) {
85+
delay(50);
86+
if((millis() - startTime) > 3000) {
87+
// If key pressed for more than 3secs, factory reset Zigbee and reboot
88+
Serial.printf("Reseting Zigbee to factory settings, reboot.\n");
89+
Zigbee.factoryReset();
90+
}
91+
}
92+
}
93+
delay(100);
7494
}

libraries/Zigbee/examples/Zigbee_Light_Switch/Zigbee_Light_Switch.ino

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,23 @@ typedef enum {
7171
static switch_func_pair_t button_func_pair[] = {{GPIO_INPUT_IO_TOGGLE_SWITCH, SWITCH_ONOFF_TOGGLE_CONTROL}};
7272

7373
/* Zigbee switch */
74-
ZigbeeSwitch zbSwitch = ZigbeeSwitch(SWITCH_ENDPOINT_NUMBER);
74+
class MyZigbeeSwitch : public ZigbeeSwitch {
75+
public:
76+
// Constructor that passes parameters to the base class constructor
77+
MyZigbeeSwitch(uint8_t endpoint) : ZigbeeSwitch(endpoint) {}
78+
79+
// Override the set_on_off function
80+
void readManufacturer(char* manufacturer) override {
81+
//Do what you want with the manufacturer string
82+
Serial.printf("Manufacturer: %s\n", manufacturer);
83+
}
84+
void readModel(char* model) override {
85+
//Do what you want with the model string
86+
Serial.printf("Model: %s\n", model);
87+
}
88+
};
89+
90+
MyZigbeeSwitch zbSwitch = MyZigbeeSwitch(SWITCH_ENDPOINT_NUMBER);
7591

7692
/********************* Zigbee functions **************************/
7793
static void esp_zb_buttons_handler(switch_func_pair_t *button_func_pair) {
@@ -103,13 +119,20 @@ void setup() {
103119

104120
Serial.begin(115200);
105121

122+
//Optional: set Zigbee device name and model
123+
zbSwitch.setManufacturerAndModel("Espressif", "ZigbeeSwitch");
124+
125+
//Optional to allow multiple light to bind to the switch
126+
zbSwitch.allowMultipleBinding(true);
127+
106128
//Add endpoint to Zigbee Core
107129
log_d("Adding ZigbeeSwitch endpoint to Zigbee Core");
108130
Zigbee.addEndpoint(&zbSwitch);
109131

110132
//Open network for 180 seconds after boot
111133
Zigbee.setRebootOpenNetwork(180);
112134

135+
113136
// Init button switch
114137
for (int i = 0; i < PAIR_SIZE(button_func_pair); i++) {
115138
pinMode(button_func_pair[i].pin, INPUT_PULLUP);
@@ -169,4 +192,11 @@ void loop() {
169192
}
170193
vTaskDelay(10 / portTICK_PERIOD_MS);
171194
}
195+
196+
// print the bound lights every 10 seconds
197+
static uint32_t last_print = 0;
198+
if (millis() - last_print > 10000) {
199+
last_print = millis();
200+
zbSwitch.printBoundLights();
201+
}
172202
}

libraries/Zigbee/src/Zigbee_core.cpp

Lines changed: 51 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "Zigbee_handlers.cpp"
44
#include "Arduino.h"
55

6+
67
Zigbee_Core::Zigbee_Core() {
78
_radio_config = ZIGBEE_DEFAULT_RADIO_CONFIG();
89
_host_config = ZIGBEE_DEFAULT_HOST_CONFIG();
@@ -254,9 +255,9 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
254255

255256
//TODO: Save the device short address and endpoint to the list ????????
256257

257-
// for each endpoint in the list call the find_endpoint function if not bounded
258+
// for each endpoint in the list call the find_endpoint function if not bounded or allowed to bind multiple devices
258259
for (std::list<Zigbee_EP*>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
259-
if (!(*it)->_is_bound) {
260+
if (!(*it)->_is_bound || (*it)->_allow_multiple_binding) {
260261
(*it)->find_endpoint(&cmd_req);
261262
}
262263
}
@@ -277,87 +278,10 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
277278
}
278279
}
279280

280-
// // Zigbee action handlers
281-
// static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message) {
282-
// esp_err_t ret = ESP_OK;
283-
// /* TODO:
284-
// Implement handlers for different Zigbee actions (callback_id's)
285-
// */
286-
// // NOTE: Implement all Zigbee actions that can be handled by the Zigbee_Core class, or just call user defined callback function and let the user handle the action and read the message properly
287-
// // NOTE: This may me harder for users, to know what callback_id's are available and what message type is received
288-
// switch (callback_id) {
289-
// case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID: ret = zb_attribute_set_handler((esp_zb_zcl_set_attr_value_message_t *)message); break;
290-
// case ESP_ZB_CORE_REPORT_ATTR_CB_ID: ret = zb_attribute_reporting_handler((esp_zb_zcl_report_attr_message_t *)message); break;
291-
// case ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID: ret = zb_read_attr_resp_handler((esp_zb_zcl_cmd_read_attr_resp_message_t *)message); break;
292-
// case ESP_ZB_CORE_CMD_REPORT_CONFIG_RESP_CB_ID: ret = zb_configure_report_resp_handler((esp_zb_zcl_cmd_config_report_resp_message_t *)message); break;
293-
// case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: log_i("Received default response"); break;
294-
// default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break;
295-
// }
296-
297-
// //TODO: get destination endpoint from the message:
298-
// uint8_t dst_endpoint = ((esp_zb_zcl_set_attr_value_message_t *)message)->info.dst_endpoint;
299-
300-
301-
302-
// return ret;
303-
// }
304-
305-
// static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_message_t *message) {
306-
// if (!message) {
307-
// log_e("Empty message");
308-
// }
309-
// if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
310-
// log_e("Received message: error status(%d)", message->info.status);
311-
// }
312-
313-
// log_i(
314-
// "Received message: endpoint(%d), cluster(0x%x), attribute(0x%x), data size(%d)", message->info.dst_endpoint, message->info.cluster, message->attribute.id,
315-
// message->attribute.data.size
316-
// );
317-
318-
// // List through all Zigbee EPs and call the callback function, with the message
319-
// for (std::list<Zigbee_EP*>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
320-
// if (message->info.dst_endpoint == (*it)->_endpoint) {
321-
// //TODO: implement argument passing to the callback function
322-
// //if Zigbee_EP argument is set, pass it to the callback function
323-
// // if ((*it)->_arg) {
324-
// // (*it)->_cb(message, (*it)->_arg);
325-
// // }
326-
// // else {
327-
// (*it)->_cb(message); //method zb_attribute_set_handler in the LIGHT EP
328-
// // }
329-
// }
330-
// }
331-
// return ESP_OK;
332-
// }
333-
334-
// static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_message_t *message) {
335-
// if (!message) {
336-
// log_e("Empty message");
337-
// }
338-
// if (message->status != ESP_ZB_ZCL_STATUS_SUCCESS) {
339-
// log_e("Received message: error status(%d)", message->status);
340-
// }
341-
// log_i(
342-
// "Received report from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->src_address.u.short_addr, message->src_endpoint,
343-
// message->dst_endpoint, message->cluster
344-
// );
345-
// // List through all Zigbee EPs and call the callback function, with the message
346-
// for (std::list<Zigbee_EP*>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
347-
// if (message->info.dst_endpoint == (*it)->_endpoint) {
348-
// //TODO: implement argument passing to the callback function
349-
// //if Zigbee_EP argument is set, pass it to the callback function
350-
// // if ((*it)->_arg) {
351-
// // (*it)->_cb(message, (*it)->_arg);
352-
// // }
353-
// // else {
354-
// (*it)->_cb(message);
355-
// // }
356-
// }
357-
// }
358-
// return ESP_OK;
359-
// }
360-
281+
void Zigbee_Core::factoryReset() {
282+
log_v("Factory reseting Zigbee stack, device will reboot");
283+
esp_zb_factory_reset();
284+
}
361285

362286

363287
// TODO: Implement scanning network
@@ -394,4 +318,48 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
394318
// esp_host_zb_output(ESP_ZNSP_ZDO_BIND_SET, &zdo_data, sizeof(esp_zb_zdo_bind_req_t), &output, &outlen);
395319
// }
396320

321+
// Function to convert enum value to string
322+
const char* Zigbee_Core::getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId) {
323+
switch (deviceId) {
324+
case ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID: return "General On/Off switch";
325+
case ESP_ZB_HA_LEVEL_CONTROL_SWITCH_DEVICE_ID: return "Level Control Switch";
326+
case ESP_ZB_HA_ON_OFF_OUTPUT_DEVICE_ID: return "General On/Off output";
327+
case ESP_ZB_HA_LEVEL_CONTROLLABLE_OUTPUT_DEVICE_ID: return "Level Controllable Output";
328+
case ESP_ZB_HA_SCENE_SELECTOR_DEVICE_ID: return "Scene Selector";
329+
case ESP_ZB_HA_CONFIGURATION_TOOL_DEVICE_ID: return "Configuration Tool";
330+
case ESP_ZB_HA_REMOTE_CONTROL_DEVICE_ID: return "Remote Control";
331+
case ESP_ZB_HA_COMBINED_INTERFACE_DEVICE_ID: return "Combined Interface";
332+
case ESP_ZB_HA_RANGE_EXTENDER_DEVICE_ID: return "Range Extender";
333+
case ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID: return "Mains Power Outlet";
334+
case ESP_ZB_HA_DOOR_LOCK_DEVICE_ID: return "Door lock client";
335+
case ESP_ZB_HA_DOOR_LOCK_CONTROLLER_DEVICE_ID: return "Door lock controller";
336+
case ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID: return "Simple Sensor device";
337+
case ESP_ZB_HA_CONSUMPTION_AWARENESS_DEVICE_ID: return "Consumption Awareness Device";
338+
case ESP_ZB_HA_HOME_GATEWAY_DEVICE_ID: return "Home Gateway";
339+
case ESP_ZB_HA_SMART_PLUG_DEVICE_ID: return "Smart plug";
340+
case ESP_ZB_HA_WHITE_GOODS_DEVICE_ID: return "White Goods";
341+
case ESP_ZB_HA_METER_INTERFACE_DEVICE_ID: return "Meter Interface";
342+
case ESP_ZB_HA_ON_OFF_LIGHT_DEVICE_ID: return "On/Off Light Device";
343+
case ESP_ZB_HA_DIMMABLE_LIGHT_DEVICE_ID: return "Dimmable Light Device";
344+
case ESP_ZB_HA_COLOR_DIMMABLE_LIGHT_DEVICE_ID: return "Color Dimmable Light Device";
345+
case ESP_ZB_HA_DIMMER_SWITCH_DEVICE_ID: return "Dimmer Switch Device";
346+
case ESP_ZB_HA_COLOR_DIMMER_SWITCH_DEVICE_ID: return "Color Dimmer Switch Device";
347+
case ESP_ZB_HA_SHADE_DEVICE_ID: return "Shade";
348+
case ESP_ZB_HA_SHADE_CONTROLLER_DEVICE_ID: return "Shade controller";
349+
case ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID: return "Window Covering client";
350+
case ESP_ZB_HA_WINDOW_COVERING_CONTROLLER_DEVICE_ID: return "Window Covering controller";
351+
case ESP_ZB_HA_HEATING_COOLING_UNIT_DEVICE_ID: return "Heating/Cooling Unit device";
352+
case ESP_ZB_HA_THERMOSTAT_DEVICE_ID: return "Thermostat Device";
353+
case ESP_ZB_HA_TEMPERATURE_SENSOR_DEVICE_ID: return "Temperature Sensor";
354+
case ESP_ZB_HA_IAS_CONTROL_INDICATING_EQUIPMENT_ID: return "IAS Control and Indicating Equipment";
355+
case ESP_ZB_HA_IAS_ANCILLARY_CONTROL_EQUIPMENT_ID: return "IAS Ancillary Control Equipment";
356+
case ESP_ZB_HA_IAS_ZONE_ID: return "IAS Zone";
357+
case ESP_ZB_HA_IAS_WARNING_DEVICE_ID: return "IAS Warning Device";
358+
case ESP_ZB_HA_TEST_DEVICE_ID: return "Custom HA device for test";
359+
case ESP_ZB_HA_CUSTOM_TUNNEL_DEVICE_ID: return "Custom Tunnel device";
360+
case ESP_ZB_HA_CUSTOM_ATTR_DEVICE_ID: return "Custom Attributes Device";
361+
default: return "Unknown device type";
362+
}
363+
}
364+
397365
Zigbee_Core Zigbee = Zigbee_Core();

libraries/Zigbee/src/Zigbee_core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class Zigbee_Core {
108108

109109
void setPrimaryChannelMask(uint32_t mask);
110110
void setRebootOpenNetwork(uint8_t time);
111+
112+
void factoryReset();
111113
};
112114

113115
extern Zigbee_Core Zigbee;

libraries/Zigbee/src/Zigbee_ep.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,99 @@
11
/* Common Class for Zigbee End Point */
22

33
#include "Zigbee_ep.h"
4+
#include "esp_zigbee_cluster.h"
45

56
uint8_t Zigbee_EP::_endpoint = 0;
67
bool Zigbee_EP::_is_bound = false;
8+
bool Zigbee_EP::_allow_multiple_binding = false;
79

810
/* Zigbee End Device Class */
911
Zigbee_EP::Zigbee_EP(uint8_t endpoint) {
1012
_endpoint = endpoint;
1113
_ep_config.endpoint = 0;
1214
_cluster_list = nullptr;
15+
_attribute_cluster = nullptr;
1316
}
1417

1518
Zigbee_EP::~Zigbee_EP() {
1619

1720
}
21+
22+
void Zigbee_EP::setManufacturerAndModel(const char *name, const char *model) {
23+
// Convert manufacturer to ZCL string
24+
size_t length = strlen(name);
25+
if (length > 32) {
26+
log_e("Manufacturer name is too long");
27+
return;
28+
}
29+
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
30+
char* zb_name = new char[length + 2];
31+
// Store the length as the first element
32+
zb_name[0] = static_cast<char>(length); // Cast size_t to char
33+
// Use memcpy to copy the characters to the result array
34+
memcpy(zb_name + 1, name, length);
35+
// Null-terminate the array
36+
zb_name[length + 1] = '\0';
37+
38+
// Convert model to ZCL string
39+
length = strlen(model);
40+
if (length > 32) {
41+
log_e("Model name is too long");
42+
delete[] zb_name;
43+
return;
44+
}
45+
char* zb_model = new char[length + 2];
46+
zb_model[0] = static_cast<char>(length);
47+
memcpy(zb_model + 1, model, length);
48+
zb_model[length + 1] = '\0';
49+
50+
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);
51+
esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, (void *)zb_name);
52+
esp_zb_basic_cluster_add_attr(basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, (void *)zb_model);
53+
54+
}
55+
56+
void Zigbee_EP::readManufacturerAndModel(uint8_t endpoint, uint16_t short_addr) {
57+
/* Read peer Manufacture Name & Model Identifier */
58+
esp_zb_zcl_read_attr_cmd_t read_req;
59+
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
60+
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
61+
read_req.zcl_basic_cmd.dst_endpoint = endpoint;
62+
read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr;
63+
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BASIC;
64+
65+
uint16_t attributes[] = {
66+
ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID,
67+
ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID,
68+
};
69+
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
70+
read_req.attr_field = attributes;
71+
72+
esp_zb_zcl_read_attr_cmd_req(&read_req);
73+
}
74+
75+
void Zigbee_EP::attribute_read_cmd(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {
76+
/* Basic cluster attributes */
77+
if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_BASIC) {
78+
if (attribute->id == ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING && attribute->data.value) {
79+
zbstring_t *zbstr = (zbstring_t *)attribute->data.value;
80+
char *string = (char *)malloc(zbstr->len + 1);
81+
memcpy(string, zbstr->data, zbstr->len);
82+
string[zbstr->len] = '\0';
83+
log_i("Peer Manufacturer is \"%s\"", string);
84+
readManufacturer(string);
85+
free(string);
86+
}
87+
if (attribute->id == ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING && attribute->data.value) {
88+
zbstring_t *zbstr = (zbstring_t *)attribute->data.value;
89+
char *string = (char *)malloc(zbstr->len + 1);
90+
memcpy(string, zbstr->data, zbstr->len);
91+
string[zbstr->len] = '\0';
92+
log_i("Peer Model is \"%s\"", string);
93+
readModel(string);
94+
free(string);
95+
}
96+
}
97+
}
98+
//NOTE:
99+
// esp_zb_cluster_add_attr function

0 commit comments

Comments
 (0)