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

Zigbee ota updates #192

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
4 changes: 4 additions & 0 deletions code/nrf-connect/samples/zigbee/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ config PRST_ZB_BUILD_DATE
string "Zigbee basic cluster build date attribute. Max 16 bytes."
default ""

config PRST_ZB_SOFTWARE_VERSION
string "Zigbee basic cluster software version attribute. Max 16 bytes."
default ""

config PRST_ZB_MODEL_ID
string "Zigbee basic cluster model id attribute. Max 32 bytes."
default "b-parasite"
Expand Down
7 changes: 7 additions & 0 deletions code/nrf-connect/samples/zigbee/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ 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
* `CONFIG_PRST_ZB_PARENT_POLL_INTERVAL_SEC`: amount of time (in seconds) the device waits between polling its parent for data

### Over the air (OTA) updates
The firmware is prepared for accepting over the air updates. You need to set the toggle `CONFIG_ZIGBEE_OTA` in the `prj.conf` to `y` to compile OTA functionality in the firmware.

Once the toggle is set, during compilation there will be a *.zigbee file generated as well, which is the firmware update file. This needs to be provided to your zigbee coordinator, so that it can be discovered by your deployed and paired parasites. The version `CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION` needs to be increased to allow the device to recognize the firmware file as an actual update.

Some helpful information on how to provide an firmware update via Home Assistant and ZHA integration can be found in the [Home Assistant forums](https://community.home-assistant.io/t/zha-ota-firmware-update-upgrade/488960).

## Home Assistant Integration
This firmware sample has only been tested with Home Assistant, using one of the following integrations.

Expand Down
112 changes: 112 additions & 0 deletions code/nrf-connect/samples/zigbee/prj_fota.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@

CONFIG_LOG=y
CONFIG_PWM=y
CONFIG_I2C=y
CONFIG_ADC=y
CONFIG_GPIO=y

CONFIG_USE_SEGGER_RTT=y
CONFIG_SEGGER_RTT_BUFFER_SIZE_UP=4096

CONFIG_NEWLIB_LIBC=y
CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y

CONFIG_SERIAL=n

CONFIG_HEAP_MEM_POOL_SIZE=2048
CONFIG_MAIN_THREAD_PRIORITY=7

CONFIG_ZIGBEE=y
CONFIG_ZIGBEE_APP_UTILS=y
CONFIG_ZIGBEE_ROLE_END_DEVICE=y

# This example requires more workqueue stack
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048

# Enable nRF ECB driver
CONFIG_CRYPTO=y
CONFIG_CRYPTO_NRF_ECB=y
CONFIG_CRYPTO_INIT_PRIORITY=80

# Networking
CONFIG_NET_IPV6=n
CONFIG_NET_IPV6_MLD=n
CONFIG_NET_IPV6_NBR_CACHE=n
CONFIG_NET_IPV6_RA_RDNSS=n
CONFIG_NET_IP_ADDR_CHECK=n
CONFIG_NET_UDP=n

##
## ZigBee Channel Selection
##
# Get Zigbee to scan every channel.
CONFIG_ZIGBEE_CHANNEL_SELECTION_MODE_MULTI=y
# By default only scans channel 11 (ZigBee2MQTT default) and 15 (ZHA default).
# Comment to scan all channels - this will make pairing consume more energy.
# CONFIG_ZIGBEE_CHANNEL_MASK=0x8800

# Uncomment to set a specific channel - this will make pairing more energy efficient.
# CONFIG_ZIGBEE_CHANNEL=11

# 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

CONFIG_WATCHDOG=y
CONFIG_HWINFO=y
CONFIG_ASSERT=y

# Uncomment for debug log level.
# CONFIG_LOG_DEFAULT_LEVEL=4

# Factory reset method selection. Only hardware revision 2.0.0+ has button SW1. Earlier
# revisions must select a different method. See Kconfig for options.
# CONFIG_PRST_ZB_FACTORY_RESET_VIA_SW1=y

##
## OTA section.
##

# Software build version in the basic_attrs cluster.
CONFIG_PRST_ZB_SOFTWARE_VERSION="dev"

# Enable Zigbee FOTA library
CONFIG_ZIGBEE_FOTA=y

CONFIG_ZIGBEE_FOTA_ENDPOINT=5
CONFIG_ZIGBEE_FOTA_LOG_LEVEL_DBG=y

# FOTA manufacturer ID. Defaults to 0x127f (Nordic).
# CONFIG_ZIGBEE_FOTA_MANUFACTURER_ID=0x0000

# This must be BOARD_REVISION_CODE.
# TODO: move this to Kconfig.
CONFIG_ZIGBEE_FOTA_HW_VERSION=4

# Set for which hardware versions this FOTA image is valid.
CONFIG_ENABLE_ZIGBEE_FOTA_MIN_HW_VERSION=y
CONFIG_ZIGBEE_FOTA_MIN_HW_VERSION=0x00
CONFIG_ENABLE_ZIGBEE_FOTA_MAX_HW_VERSION=y
CONFIG_ZIGBEE_FOTA_MAX_HW_VERSION=0xff

# 0.0.0 is a safe default for development.
CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="0.0.0"
CONFIG_ZIGBEE_FOTA_COMMENT="b-parasite"

# Ensure an MCUboot-compatible binary is generated.
CONFIG_IMG_MANAGER=y
CONFIG_STREAM_FLASH=y
CONFIG_DFU_TARGET_MCUBOOT=y
CONFIG_IMG_ERASE_PROGRESSIVELY=y
CONFIG_ZIGBEE_FOTA_PROGRESS_EVT=y

# Increase the number of RX buffers
CONFIG_NRF_802154_RX_BUFFERS=32
91 changes: 87 additions & 4 deletions code/nrf-connect/samples/zigbee/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
#include <zigbee/zigbee_app_utils.h>
#include <zigbee/zigbee_error_handler.h>

#if CONFIG_ZIGBEE_FOTA
#include <zephyr/dfu/mcuboot.h>
#include <zephyr/sys/reboot.h>
#include <zigbee/zigbee_fota.h>
#endif

#include "debug_counters.h"
#include "factory_reset.h"
#include "flash_fs.h"
Expand Down Expand Up @@ -115,9 +121,62 @@ PRST_ZB_DECLARE_ENDPOINT(
PRST_ZIGBEE_ENDPOINT,
app_template_clusters);

#ifndef CONFIG_ZIGBEE_FOTA
ZBOSS_DECLARE_DEVICE_CTX_1_EP(
app_template_ctx,
app_template_ep);
#else

extern zb_af_endpoint_desc_t zigbee_fota_client_ep;

ZBOSS_DECLARE_DEVICE_CTX_2_EP(
app_template_ctx,
zigbee_fota_client_ep,
app_template_ep);

static void confirm_image(void) {
if (!boot_is_img_confirmed()) {
int ret = boot_write_img_confirmed();

if (ret) {
LOG_ERR("Couldn't confirm image: %d", ret);
} else {
LOG_INF("Marked image as OK");
}
}
}

static void ota_evt_handler(const struct zigbee_fota_evt *evt) {
switch (evt->id) {
case ZIGBEE_FOTA_EVT_PROGRESS:
LOG_INF("OTA progress: %d%%", evt->dl.progress);
break;
case ZIGBEE_FOTA_EVT_FINISHED:
LOG_INF("Reboot application.");
/* Power on unused sections of RAM to allow MCUboot to use it. */
if (IS_ENABLED(CONFIG_RAM_POWER_DOWN_LIBRARY)) {
power_up_unused_ram();
}
sys_reboot(SYS_REBOOT_COLD);
break;

case ZIGBEE_FOTA_EVT_ERROR:
LOG_ERR("OTA image transfer failed.");
break;
}
}

static void zcl_device_cb(zb_bufid_t bufid) {
zb_zcl_device_callback_param_t *device_cb_param =
ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);

if (device_cb_param->device_cb_id == ZB_ZCL_OTA_UPGRADE_VALUE_CB_ID) {
zigbee_fota_zcl_cb(bufid);
} else {
device_cb_param->status = RET_NOT_IMPLEMENTED;
}
}
#endif /* CONFIG_ZIGBEE_FOTA */

void identify_cb(zb_bufid_t bufid) {
LOG_DBG("Remote identify command called");
Expand Down Expand Up @@ -185,6 +244,12 @@ void update_sensors_cb(zb_uint8_t arg) {

void zboss_signal_handler(zb_bufid_t bufid) {
// See zigbee_default_signal_handler() for all available signals.

#ifdef CONFIG_ZIGBEE_FOTA
/* Pass signal to the OTA client implementation. */
zigbee_fota_signal_handler(bufid);
#endif /* CONFIG_ZIGBEE_FOTA */

zb_zdo_app_signal_hdr_t *sig_hndler = NULL;
zb_zdo_app_signal_type_t sig = zb_get_app_signal(bufid, /*sg_p=*/&sig_hndler);
zb_ret_t status = ZB_GET_APP_SIGNAL_STATUS(bufid);
Expand Down Expand Up @@ -314,15 +379,33 @@ int main(void) {

prst_zb_attrs_init(&dev_ctx);

ZB_AF_REGISTER_DEVICE_CTX(&app_template_ctx);

prst_led_flash(2);
k_msleep(100);

ZB_AF_SET_IDENTIFY_NOTIFICATION_HANDLER(PRST_ZIGBEE_ENDPOINT, identify_cb);

zigbee_configure_sleepy_behavior(/*enable=*/true);
power_down_unused_ram();

#ifdef CONFIG_ZIGBEE_FOTA
LOG_INF("Firmware version: %s", CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION);

/* Initialize Zigbee FOTA download service. */
zigbee_fota_init(ota_evt_handler);

/* Mark the current firmware as valid. */
confirm_image();

/* Register callback for handling ZCL commands. */
ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
#endif /* CONFIG_ZIGBEE_FOTA */

ZB_AF_REGISTER_DEVICE_CTX(&app_template_ctx);

ZB_AF_SET_IDENTIFY_NOTIFICATION_HANDLER(PRST_ZIGBEE_ENDPOINT, identify_cb);

#ifdef CONFIG_ZIGBEE_FOTA
ZB_AF_SET_IDENTIFY_NOTIFICATION_HANDLER(CONFIG_ZIGBEE_FOTA_ENDPOINT, identify_cb);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, what's the expected behavior here? We have two identify handlers in different endpoints. Where is the FOTA one used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CONFIG_ZIGBEE_FOTA_ENDPOINT comes from the library which is activated by the CONFIG_ZIGBEE_FOTA toggle. Basically an additional zigbee application (?) is registered on the device if OTA functionality is active. This is what I interpreted from the docs and the code.

The lines from above I simply took from the nordic sample. If they are really needed I cannot tell, but I wanted to rule out all possible errors during development by missing something out.

I would suggest, we leave it like it is now, and once we can confirm that OTA is working we could try if it is really needed. 👼

image

#endif

zigbee_enable();

prst_debug_counters_increment("main_finish");
Expand Down
5 changes: 5 additions & 0 deletions code/nrf-connect/samples/zigbee/src/prst_zb_attrs.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ void prst_zb_attrs_init(struct zb_device_ctx *dev_ctx) {
CONFIG_PRST_ZB_BUILD_DATE,
ZB_ZCL_STRING_CONST_SIZE(CONFIG_PRST_ZB_BUILD_DATE));

ZB_ZCL_SET_STRING_VAL(
dev_ctx->basic_attr.sw_ver,
CONFIG_PRST_ZB_SOFTWARE_VERSION,
ZB_ZCL_STRING_CONST_SIZE(CONFIG_PRST_ZB_SOFTWARE_VERSION));

dev_ctx->basic_attr.hw_version = CONFIG_PRST_ZB_HARDWARE_VERSION;

dev_ctx->batt_attrs.quantity = 1;
Expand Down
Loading