Skip to content
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

feat(zigbee): Save network channel after 1st joining for faster rejoin #11123

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ void setup() {
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensorTest");
zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensor");

// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
Expand All @@ -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
43 changes: 38 additions & 5 deletions libraries/Zigbee/src/ZigbeeCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@
#include "ZigbeeHandlers.cpp"
#include "Arduino.h"

#define ZB_INIT_TIMEOUT 30000 // 30 seconds
#ifdef __cplusplus
extern "C" {
#endif
#include "zboss_api.h"
extern zb_ret_t zb_nvram_write_dataset(zb_nvram_dataset_types_t t); // rejoin scanning workaround
extern void zb_set_ed_node_descriptor(bool power_src, bool rx_on_when_idle, bool alloc_addr); // sleepy device power mode workaround
#ifdef __cplusplus
}
#endif

extern "C" void zb_set_ed_node_descriptor(bool power_src, bool rx_on_when_idle, bool alloc_addr);
static bool edBatteryPowered = false;

ZigbeeCore::ZigbeeCore() {
Expand All @@ -18,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 @@ -39,8 +47,11 @@ 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) {
log_e("ZigbeeCore begin timeout");
if (xSemaphoreTake(lock, _begin_timeout) != pdTRUE) {
log_e("ZigbeeCore begin failed or timeout");
if (_role != ZIGBEE_COORDINATOR) { // Only End Device and Router can rejoin
resetNVRAMChannelMask();
}
}
return started();
}
Expand Down Expand Up @@ -71,8 +82,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
resetNVRAMChannelMask();
}
}
return started();
}
Expand Down Expand Up @@ -220,6 +234,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 @@ -245,6 +260,8 @@ 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.setNVRAMChannelMask(1 << esp_zb_get_current_channel());
Zigbee._connected = true;
}
Zigbee.searchBindings();
Expand Down Expand Up @@ -289,6 +306,8 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
extended_pan_id[0], esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address()
);
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)
Zigbee.setNVRAMChannelMask(1 << esp_zb_get_current_channel());
} else {
log_i("Network steering was not successful (status: %s)", esp_err_to_name(err_status));
esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_STEERING, 1000);
Expand Down Expand Up @@ -483,6 +502,20 @@ void ZigbeeCore::searchBindings() {
esp_zb_zdo_binding_table_req(mb_req, bindingTableCb, (void *)mb_req);
}

void ZigbeeCore::resetNVRAMChannelMask() {
_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");
}

void ZigbeeCore::setNVRAMChannelMask(uint32_t mask) {
_primary_channel_mask = mask;
esp_zb_set_channel_mask(_primary_channel_mask);
zb_nvram_write_dataset(ZB_NVRAM_COMMON_DATA);
log_v("Channel mask set to 0x%08x", mask);
}

// Function to convert enum value to string
const char *ZigbeeCore::getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId) {
switch (deviceId) {
Expand Down
12 changes: 10 additions & 2 deletions 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 All @@ -103,6 +106,8 @@ class ZigbeeCore {
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);
void resetNVRAMChannelMask(); // Reset to default mask also in NVRAM
void setNVRAMChannelMask(uint32_t mask); // Set channel mask in NVRAM

public:
ZigbeeCore();
Expand Down Expand Up @@ -134,7 +139,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 setScanDuration(uint8_t duration); // Can be set from 1 - 4. 1 is fastest, 4 is slowest

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 +151,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