Merge pull request #91 from rbaron/zigbee-double-reset
[zb] Implement a double-reset factory resetting method
This commit is contained in:
commit
1fbd971c4f
10 changed files with 181 additions and 28 deletions
|
|
@ -5,13 +5,13 @@
|
|||
#define PRST_TO_STRING(x) PRST_STRINGIFY(x)
|
||||
#define PRST_LOCATION __FILE__ ":" PRST_TO_STRING(__LINE__)
|
||||
|
||||
#define RET_IF_ERR_MSG(expr, msg) \
|
||||
{ \
|
||||
int err = (expr); \
|
||||
if (err) { \
|
||||
LOG_ERR("Error: " msg " in " PRST_LOCATION); \
|
||||
return err; \
|
||||
} \
|
||||
#define RET_IF_ERR_MSG(expr, msg) \
|
||||
{ \
|
||||
int err = (expr); \
|
||||
if (err) { \
|
||||
LOG_ERR("Error %d: " msg " in " PRST_LOCATION, err); \
|
||||
return err; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define RET_IF_ERR(expr) RET_IF_ERR_MSG(expr, "")
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ include_directories(src)
|
|||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
src/double_reset_detector.c
|
||||
src/factory_reset.c
|
||||
src/prst_zb_attrs.c
|
||||
src/prst_zb_soil_moisture_defs.c
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,3 +20,15 @@ config PRST_ZB_MODEL_ID
|
|||
config PRST_ZB_HARDWARE_VERSION
|
||||
int "Zigbee basic cluster hardware version attribute. 1 byte."
|
||||
default 2
|
||||
|
||||
choice PRST_ZB_FACTORY_RESET_METHOD
|
||||
bool "Factory reset method"
|
||||
default PRST_ZB_FACTORY_RESET_VIA_DOUBLE_RESET
|
||||
|
||||
config PRST_ZB_FACTORY_RESET_VIA_DOUBLE_RESET
|
||||
bool "Double resetting factory resets the device."
|
||||
|
||||
config PRST_ZB_FACTORY_RESET_VIA_RESET_PIN
|
||||
bool "Resetting via the reset pin will factory reset the device. Power cycling through battery replacement will not."
|
||||
|
||||
endchoice # PRST_ZB_FACTORY_RESET_METHOD
|
||||
|
|
|
|||
|
|
@ -16,15 +16,24 @@ These [clusters](https://en.wikipedia.org/wiki/Zigbee#Cluster_library) are defin
|
|||
The sample will first boot and start looking for a Zigbee coordinator - in pairing mode. The onboard LED will be flashing once a second while in this mode. Once a suitable network is found, the LED will briefly flash 3 times and remain off.
|
||||
|
||||
### Factory Reset
|
||||
Most Zigbee devices provide a physical button to "factory reset" it - causing it to forget its joined network and look for a new one.
|
||||
A factory reset will make b-parasite forget its network pairing information and switch to pairing mode. There are two (mutually exclusive) methods to perform a factory reset, controlled by the `CONFIG_PRST_ZB_FACTORY_RESET_METHOD` config flag.
|
||||
|
||||
#### Factory Reset Method 1 (default) - Double reset
|
||||
Resetting b-parasite twice in the timestamp of 5 seconds will perform a factory reset. With this method, both shorting the `RST` pin to ground and removing-inserting the battery counts as a reset.
|
||||
|
||||
For better results, wait > 1 and < 5 seconds second between the resets. The LED will flash a total of 8 times to indicate it worked.
|
||||
|
||||
#### Factory Reset Method 2 - Reset Pin
|
||||
In this method, there's a distinction between two reset modes.
|
||||
|
||||
b-parasite has no physical buttons, and the implemented work around is to distinguish between two *reset modes*:
|
||||
#### Power up mode
|
||||
The device enters this mode when it is powered. For example, swapping an old battery or connecting to eternal power. This is the "usual" reset mode, and joined networks will be remembered.
|
||||
|
||||
#### Reset pin mode
|
||||
If the device's RESET pin is briefly grounded, the device will effectively be **factory reset**. The device will leave its previous network and start looking for a new one.
|
||||
|
||||
While it works, this method can be finicky - an accidental pin reset will perform an unwanted factory reset.
|
||||
|
||||
## Configs
|
||||
Available options in `Kconfig`. Notable options:
|
||||
* `CONFIG_PRST_ZB_SLEEP_DURATION_SEC`: amount of time (in seconds) the device sleeps between reading all sensors and updating its clusters
|
||||
|
|
|
|||
|
|
@ -41,3 +41,11 @@ CONFIG_ZIGBEE_CHANNEL_SELECTION_MODE_MULTI=y
|
|||
# Enable API for powering down unused RAM parts.
|
||||
# https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.7.1/nrf/ug_zigbee_configuring.html#power-saving-during-sleep
|
||||
CONFIG_RAM_POWER_DOWN_LIBRARY=y
|
||||
|
||||
# LittleFS.
|
||||
CONFIG_MAIN_STACK_SIZE=2048
|
||||
CONFIG_FLASH=y
|
||||
CONFIG_FLASH_MAP=y
|
||||
CONFIG_FLASH_PAGE_LAYOUT=y
|
||||
CONFIG_FILE_SYSTEM=y
|
||||
CONFIG_FILE_SYSTEM_LITTLEFS=y
|
||||
78
code/nrf-connect/samples/zigbee/src/double_reset_detector.c
Normal file
78
code/nrf-connect/samples/zigbee/src/double_reset_detector.c
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#include "double_reset_detector.h"
|
||||
|
||||
#include <prstlib/macros.h>
|
||||
#include <string.h>
|
||||
#include <zephyr/fs/fs.h>
|
||||
#include <zephyr/fs/littlefs.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(double_reset_detector, CONFIG_LOG_DEFAULT_LEVEL);
|
||||
|
||||
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
|
||||
|
||||
static struct fs_mount_t lfs_storage_mnt = {
|
||||
.type = FS_LITTLEFS,
|
||||
.fs_data = &storage,
|
||||
.storage_dev = (void *)FLASH_AREA_ID(storage),
|
||||
.mnt_point = "/lfs",
|
||||
};
|
||||
|
||||
static const char *flag_filename = "/lfs/reset_flag";
|
||||
|
||||
static const char flag_prefix[] = "prst-rst-count";
|
||||
|
||||
static struct fs_file_t flag_file;
|
||||
|
||||
static int erase_flag() {
|
||||
return fs_unlink(flag_filename);
|
||||
}
|
||||
|
||||
void erase_flag_callback(struct k_work *work) {
|
||||
LOG_INF("Erasing double reset flag.");
|
||||
if (erase_flag() != 0) {
|
||||
LOG_ERR("Error deleting flag");
|
||||
}
|
||||
}
|
||||
|
||||
K_WORK_DELAYABLE_DEFINE(erase_flag_work, erase_flag_callback);
|
||||
|
||||
int prst_detect_double_reset(prst_double_reset_callback_t on_double_reset) {
|
||||
// TODO: if booting for the first time after a full flash erase, fs_mount will
|
||||
// complain (via a LOG_ERR) and then automatically format the flash. It all works,
|
||||
// but avoiding a scary red message would be ideal. Maybe somehow check if it's
|
||||
// formatted before mounting?
|
||||
RET_IF_ERR(fs_mount(&lfs_storage_mnt));
|
||||
|
||||
fs_file_t_init(&flag_file);
|
||||
|
||||
RET_IF_ERR(fs_open(&flag_file, flag_filename, FS_O_CREATE | FS_O_RDWR));
|
||||
|
||||
char buff[sizeof(flag_prefix)];
|
||||
RET_CHECK(fs_read(&flag_file, buff, sizeof(buff)) >= 0, "Unable to read file");
|
||||
|
||||
// Consider it a double reset if the flag is present in the FS.
|
||||
if (strcmp(buff, flag_prefix) == 0) {
|
||||
RET_IF_ERR(fs_close(&flag_file));
|
||||
RET_IF_ERR(erase_flag());
|
||||
return on_double_reset();
|
||||
}
|
||||
|
||||
// Rewind file.
|
||||
RET_IF_ERR(fs_seek(&flag_file, 0, SEEK_SET));
|
||||
|
||||
// Write the flag and erase it after some time.
|
||||
ssize_t written = fs_write(&flag_file, flag_prefix, sizeof(flag_prefix));
|
||||
if (written != sizeof(flag_prefix)) {
|
||||
LOG_ERR("s_write returned %d, expected %d", written, sizeof(flag_prefix));
|
||||
return -1;
|
||||
}
|
||||
|
||||
RET_IF_ERR(fs_close(&flag_file));
|
||||
|
||||
// Schedule the erasure of the flag after some time.
|
||||
RET_CHECK(k_work_schedule(&erase_flag_work, K_SECONDS(5)) == 1,
|
||||
"Work not scheduled");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef _PRST_ZB_DOUBLE_RESET_DETECTOR_H_
|
||||
#define _PRST_ZB_DOUBLE_RESET_DETECTOR_H_
|
||||
|
||||
typedef int (*prst_double_reset_callback_t)();
|
||||
|
||||
int prst_detect_double_reset(prst_double_reset_callback_t on_double_reset);
|
||||
|
||||
#endif // _PRST_ZB_DOUBLE_RESET_DETECTOR_H_
|
||||
46
code/nrf-connect/samples/zigbee/src/factory_reset.c
Normal file
46
code/nrf-connect/samples/zigbee/src/factory_reset.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "factory_reset.h"
|
||||
|
||||
#include <hal/nrf_power.h>
|
||||
#include <prstlib/led.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zigbee/zigbee_app_utils.h>
|
||||
|
||||
#include "double_reset_detector.h"
|
||||
|
||||
LOG_MODULE_REGISTER(factory_reset, CONFIG_LOG_DEFAULT_LEVEL);
|
||||
|
||||
static int factory_reset() {
|
||||
// TODO: consider zb_bdb_reset_via_local_action(/*param=*/0);
|
||||
zigbee_erase_persistent_storage(/*erase=*/true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_PRST_ZB_FACTORY_RESET_VIA_DOUBLE_RESET
|
||||
int double_reset_handler() {
|
||||
LOG_WRN("Called double reset handler");
|
||||
prst_led_flash(5);
|
||||
return factory_reset();
|
||||
}
|
||||
#endif // CONFIG_PRST_ZB_FACTORY_RESET_VIA_DOUBLE_RESET
|
||||
|
||||
#if CONFIG_PRST_ZB_FACTORY_RESET_VIA_RESET_PIN
|
||||
static int factory_reset_if_reset_via_reset_pin() {
|
||||
uint32_t reset_reason = nrf_power_resetreas_get(NRF_POWER);
|
||||
// If we're resetting via the RESET pin (e.g.: reset pin shorting, firmware flashing).
|
||||
if (reset_reason & 0x1) {
|
||||
LOG_WRN("Manual reset / re-flashing detected - erasing pairing info");
|
||||
return factory_reset();
|
||||
} else { // It's a power-on cycle (e.g.: swapping battery, first boot).
|
||||
LOG_INF("Power-on cycle - keeping pairing info");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif // CONFIG_PRST_ZB_FACTORY_RESET_VIA_RESET_PIN
|
||||
|
||||
int prst_zb_factory_reset_check() {
|
||||
#if CONFIG_PRST_ZB_FACTORY_RESET_VIA_DOUBLE_RESET
|
||||
return prst_detect_double_reset(double_reset_handler);
|
||||
#elif CONFIG_PRST_ZB_FACTORY_RESET_VIA_RESET_PIN
|
||||
return factory_reset_if_reset_via_reset_pin();
|
||||
#endif // CONFIG_PRST_ZB_FACTORY_RESET_VIA_RESET_PIN
|
||||
}
|
||||
6
code/nrf-connect/samples/zigbee/src/factory_reset.h
Normal file
6
code/nrf-connect/samples/zigbee/src/factory_reset.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef _PRST_ZB_FACTORY_RESET_H_
|
||||
#define _PRST_ZB_FACTORY_RESET_H_
|
||||
|
||||
int prst_zb_factory_reset_check();
|
||||
|
||||
#endif // _PRST_ZB_FACTORY_RESET_H_
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
#include <dk_buttons_and_leds.h>
|
||||
#include <hal/nrf_power.h>
|
||||
#include <math.h>
|
||||
#include <prstlib/adc.h>
|
||||
#include <prstlib/button.h>
|
||||
|
|
@ -17,30 +15,17 @@
|
|||
#include <zigbee/zigbee_app_utils.h>
|
||||
#include <zigbee/zigbee_error_handler.h>
|
||||
|
||||
#include "factory_reset.h"
|
||||
#include "prst_zb_attrs.h"
|
||||
#include "prst_zb_endpoint_defs.h"
|
||||
#include "prst_zb_soil_moisture_defs.h"
|
||||
|
||||
#define FACTORY_RESET_BUTTON DK_BTN4_MSK
|
||||
|
||||
LOG_MODULE_REGISTER(app, CONFIG_LOG_DEFAULT_LEVEL);
|
||||
|
||||
static struct zb_device_ctx dev_ctx;
|
||||
|
||||
static prst_sensors_t sensors;
|
||||
|
||||
static void maybe_erase_pairing_info(struct k_timer *timer) {
|
||||
uint32_t reset_reason = nrf_power_resetreas_get(NRF_POWER);
|
||||
// If we're resetting via the RESET pin (e.g.: reset pin shorting, firmware flashing).
|
||||
if (reset_reason & 0x1) {
|
||||
LOG_WRN("Manual reset / re-flashing detected - erasing pairing info");
|
||||
// TODO: consider zb_bdb_reset_via_local_action(/*param=*/0);
|
||||
zigbee_erase_persistent_storage(/*erase=*/true);
|
||||
} else { // It's a power-on cycle (e.g.: swapping battery, first boot).
|
||||
LOG_INF("Power-on cycle - keeping pairing info");
|
||||
}
|
||||
}
|
||||
|
||||
static void led_flashing_cb(struct k_timer *timer) {
|
||||
prst_led_toggle();
|
||||
}
|
||||
|
|
@ -209,9 +194,7 @@ int main(void) {
|
|||
RET_IF_ERR(prst_led_init());
|
||||
RET_IF_ERR(prst_button_init());
|
||||
|
||||
maybe_erase_pairing_info(NULL);
|
||||
|
||||
register_factory_reset_button(FACTORY_RESET_BUTTON);
|
||||
RET_IF_ERR(prst_zb_factory_reset_check());
|
||||
|
||||
prst_zb_attrs_init(&dev_ctx);
|
||||
|
||||
|
|
@ -224,6 +207,7 @@ int main(void) {
|
|||
power_down_unused_ram();
|
||||
|
||||
RET_IF_ERR(prst_led_flash(2));
|
||||
k_msleep(100);
|
||||
|
||||
zigbee_enable();
|
||||
zigbee_configure_sleepy_behavior(/*enable=*/true);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue