Skip to content
Merged
Prev Previous commit
Next Next commit
feat(zigbee): Add channel mask reset after timeout
  • Loading branch information
P-R-O-C-H-Y committed Mar 18, 2025
commit 1ff918ba0e06a76248145b5d5c2519c028398bf8
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,15 @@ void setup() {
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;

// For battery powered devices, it can be better to set timeout for Zigbee Begin to lower value to save battery
// If the timeout has been reached, the network channel mask will be reset and the device will try to connect again after reset (scanning all channels)
Zigbee.setTimeout(10000); // Set timeout for Zigbee Begin to 10s (default is 30s)

// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
ESP.restart(); // If Zigbee failed to start, reboot the device and try again
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Expand Down
24 changes: 19 additions & 5 deletions libraries/Zigbee/src/ZigbeeCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#include "ZigbeeHandlers.cpp"
#include "Arduino.h"

#define ZB_INIT_TIMEOUT 30000 // 30 seconds

#ifdef __cplusplus
extern "C" {
#endif
Expand All @@ -27,6 +25,7 @@ ZigbeeCore::ZigbeeCore() {
_primary_channel_mask = ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK;
_open_network = 0;
_scan_status = ZB_SCAN_FAILED;
_begin_timeout = ZB_BEGIN_TIMEOUT_DEFAULT;
_started = false;
_connected = false;
_scan_duration = 3; // default scan duration
Expand All @@ -48,7 +47,7 @@ bool ZigbeeCore::begin(esp_zb_cfg_t *role_cfg, bool erase_nvs) {
return false;
}
_role = (zigbee_role_t)role_cfg->esp_zb_role;
if (xSemaphoreTake(lock, ZB_INIT_TIMEOUT) != pdTRUE) {
if (xSemaphoreTake(lock, _begin_timeout) != pdTRUE) {
log_e("ZigbeeCore begin timeout");
}
return started();
Expand Down Expand Up @@ -80,8 +79,11 @@ bool ZigbeeCore::begin(zigbee_role_t role, bool erase_nvs) {
}
default: log_e("Invalid Zigbee Role"); return false;
}
if (!status || xSemaphoreTake(lock, ZB_INIT_TIMEOUT) != pdTRUE) {
if (!status || xSemaphoreTake(lock, _begin_timeout) != pdTRUE) {
log_e("ZigbeeCore begin failed or timeout");
if(role != ZIGBEE_COORDINATOR) { // Only End Device and Router can rejoin
resetChannelMask();
}
}
return started();
}
Expand Down Expand Up @@ -229,6 +231,7 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
switch (sig_type) {
case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP: // Common
log_i("Zigbee stack initialized");
log_d("Zigbee channel mask: 0x%08x", esp_zb_get_channel_mask());
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
break;
case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START: // Common
Expand All @@ -254,6 +257,10 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
log_i("Opening network for joining for %d seconds", Zigbee._open_network);
esp_zb_bdb_open_network(Zigbee._open_network);
} else {
// Save the channel mask to NVRAM in case of reboot which may be on a different channel after a change in the network
Zigbee.setPrimaryChannelMask(1 << esp_zb_get_current_channel());
esp_zb_set_channel_mask(1 << esp_zb_get_current_channel());
zb_nvram_write_dataset(ZB_NVRAM_COMMON_DATA);
Zigbee._connected = true;
}
Zigbee.searchBindings();
Expand Down Expand Up @@ -299,7 +306,8 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
);
Zigbee._connected = true;
// Set channel mask and write to NVRAM, so that the device will re-join the network faster after reboot (scan only on the current channel)
esp_zb_set_channel_mask((1 << esp_zb_get_current_channel()));
Zigbee.setPrimaryChannelMask(1 << esp_zb_get_current_channel());
esp_zb_set_channel_mask(1 << esp_zb_get_current_channel());
zb_nvram_write_dataset(ZB_NVRAM_COMMON_DATA);
} else {
log_i("Network steering was not successful (status: %s)", esp_err_to_name(err_status));
Expand Down Expand Up @@ -495,6 +503,12 @@ void ZigbeeCore::searchBindings() {
esp_zb_zdo_binding_table_req(mb_req, bindingTableCb, (void *)mb_req);
}

void ZigbeeCore::resetChannelMask() {
_primary_channel_mask = ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK;
esp_zb_set_channel_mask(_primary_channel_mask);
zb_nvram_write_dataset(ZB_NVRAM_COMMON_DATA);
log_v("Channel mask reset to all channels");
}
// Function to convert enum value to string
const char *ZigbeeCore::getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId) {
switch (deviceId) {
Expand Down
9 changes: 8 additions & 1 deletion libraries/Zigbee/src/ZigbeeCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ typedef enum {
#define ZB_SCAN_RUNNING (-1)
#define ZB_SCAN_FAILED (-2)

#define ZB_BEGIN_TIMEOUT_DEFAULT 30000 // 30 seconds

#define ZIGBEE_DEFAULT_ED_CONFIG() \
{ \
.esp_zb_role = ESP_ZB_DEVICE_TYPE_ED, .install_code_policy = false, \
Expand Down Expand Up @@ -85,6 +87,7 @@ class ZigbeeCore {
esp_zb_radio_config_t _radio_config;
esp_zb_host_config_t _host_config;
uint32_t _primary_channel_mask;
uint32_t _begin_timeout;
int16_t _scan_status;
uint8_t _scan_duration;
bool _rx_on_when_idle;
Expand Down Expand Up @@ -134,6 +137,8 @@ class ZigbeeCore {
esp_zb_host_config_t getHostConfig();

void setPrimaryChannelMask(uint32_t mask); // By default all channels are scanned (11-26) -> mask 0x07FFF800
void resetChannelMask(); // Reset to default mask also in NVRAM

void setScanDuration(uint8_t duration); // Can be set from 1 - 4. 1 is fastest, 4 is slowest
uint8_t getScanDuration() {
return _scan_duration;
Expand All @@ -145,7 +150,9 @@ class ZigbeeCore {
bool getRxOnWhenIdle() {
return _rx_on_when_idle;
}

void setTimeout(uint32_t timeout) {
_begin_timeout = timeout;
}
void setRebootOpenNetwork(uint8_t time);
void openNetwork(uint8_t time);

Expand Down
1 change: 1 addition & 0 deletions libraries/Zigbee/src/ZigbeeEP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ void ZigbeeEP::reportBatteryPercentage() {
report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG;
report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;

esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
Expand Down
Loading