diff --git a/doc/nrf/links.txt b/doc/nrf/links.txt index af2ee76a19b0..d7fcc2400397 100644 --- a/doc/nrf/links.txt +++ b/doc/nrf/links.txt @@ -92,6 +92,7 @@ .. _`Building and installing Android CHIPTool`: https://github.com/nrfconnect/sdk-connectedhomeip/blob/b35e8e/docs/guides/nrfconnect_android_commissioning.md#user-content-building-and-installing-android-chiptool .. _`Configuring PC as Thread Border Router`: https://github.com/nrfconnect/sdk-connectedhomeip/blob/b35e8e/docs/guides/nrfconnect_android_commissioning.md#user-content-configuring-pc-as-thread-border-router .. _`Preparing and commissioning accessory device`: https://github.com/nrfconnect/sdk-connectedhomeip/blob/b35e8e/docs/guides/nrfconnect_android_commissioning.md#user-content-preparing-accessory-device +.. _`Performing Device Firmware Upgrade in Matter device`: https://github.com/nrfconnect/sdk-connectedhomeip/blob/TODO!!!!!!!!!/docs/guides/nrfconnect_examples_software_update.md .. _`Matter Protocol Overview`: https://github.com/nrfconnect/sdk-connectedhomeip/blob/b35e8e/README.md .. _`nRF Connect platform overview`: https://github.com/nrfconnect/sdk-connectedhomeip/blob/b35e8e/docs/guides/nrfconnect_platform_overview.md diff --git a/samples/matter/common/config/overlay-dfu_support.conf b/samples/matter/common/config/overlay-dfu_support.conf new file mode 100644 index 000000000000..15cd51df8e92 --- /dev/null +++ b/samples/matter/common/config/overlay-dfu_support.conf @@ -0,0 +1,31 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + + +CONFIG_BOOTLOADER_MCUBOOT=y + +# QSPI configuration +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=4 + +# External flash memory configuration +CONFIG_PM_EXTERNAL_FLASH=y +CONFIG_PM_EXTERNAL_FLASH_DEV_NAME="MX25R64" +CONFIG_PM_EXTERNAL_FLASH_SIZE=0xf2000 +CONFIG_PM_EXTERNAL_FLASH_BASE=0 + +# MCU Manager and SMP configuration +CONFIG_MCUMGR=y +CONFIG_MCUMGR_CMD_IMG_MGMT=y +CONFIG_MCUMGR_CMD_OS_MGMT=y +CONFIG_MCUMGR_SMP_BT=y +CONFIG_MCUMGR_SMP_BT_AUTHEN=n +CONFIG_MCUMGR_BUF_COUNT=6 + +# Increase BT MTU and RX buffer for big size DFU messages +CONFIG_BT_L2CAP_TX_MTU=260 +CONFIG_BT_BUF_ACL_RX_SIZE=264 diff --git a/samples/matter/light_bulb/README.rst b/samples/matter/light_bulb/README.rst index 520adf483cb2..4f9ed21491e0 100644 --- a/samples/matter/light_bulb/README.rst +++ b/samples/matter/light_bulb/README.rst @@ -50,6 +50,8 @@ It can be tested in the following ways: The remote control testing requires either commissioning by the Matter controller device into a network or using the test mode. Both methods can be enabled after :ref:`building and running the sample `. +The sample can be configured to use the secure bootloader and utilize it for performing over-the-air Device Firmware Upgrade using Bluetooth LE. + .. _matter_light_bulb_network_mode: Remote testing in a network @@ -76,6 +78,13 @@ Configuration |config| +Device Firmware Upgrade support +=============================== + +.. include:: ../lock/README.rst + :start-after: matter_door_lock_sample_build_with_dfu_start + :end-before: matter_door_lock_sample_build_with_dfu_end + User interface ************** @@ -90,8 +99,9 @@ LED 2: * Solid On - The light bulb is on. * Off - The light bulb is off. -Button 1: - Initiates the factory reset of the device. +.. include:: ../lock/README.rst + :start-after: matter_door_lock_sample_button1_start + :end-before: matter_door_lock_sample_button1_end Button 2: Changes the light bulb state to the opposite one. @@ -119,6 +129,8 @@ Building and running .. include:: /includes/build_and_run.txt +See `Configuration`_ for information about building the sample with the DFU support. + Testing ======= @@ -183,6 +195,11 @@ Commissioning the device :start-after: matter_door_lock_sample_commissioning_start :end-before: matter_door_lock_sample_commissioning_end +Upgrading the device firmware +============================= + +To upgrade the device firmware, complete the steps listed for the selected method in the `Performing Device Firmware Upgrade in Matter device`_ tutorial. + Dependencies ************ diff --git a/samples/matter/light_bulb/child_image/mcuboot.conf b/samples/matter/light_bulb/child_image/mcuboot.conf new file mode 100644 index 000000000000..422be5440286 --- /dev/null +++ b/samples/matter/light_bulb/child_image/mcuboot.conf @@ -0,0 +1,19 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# QSPI configuration +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=4 + +CONFIG_MULTITHREADING=y +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# External flash memory configuration +CONFIG_PM_EXTERNAL_FLASH=y +CONFIG_PM_EXTERNAL_FLASH_DEV_NAME="MX25R64" +CONFIG_PM_EXTERNAL_FLASH_SIZE=0xf2000 +CONFIG_PM_EXTERNAL_FLASH_BASE=0 diff --git a/samples/matter/light_bulb/configuration/nrf52840dk_nrf52840/pm_static.yml b/samples/matter/light_bulb/configuration/nrf52840dk_nrf52840/pm_static.yml new file mode 100644 index 000000000000..b194f921be3b --- /dev/null +++ b/samples/matter/light_bulb/configuration/nrf52840dk_nrf52840/pm_static.yml @@ -0,0 +1,38 @@ +mcuboot: + address: 0x0 + size: 0xc000 + region: flash_primary +mcuboot_pad: + address: 0xc000 + size: 0x200 +app: + address: 0xc200 + size: 0xf1e00 +mcuboot_primary: + orig_span: &id001 + - mcuboot_pad + - app + span: *id001 + address: 0xc000 + size: 0xf2000 + region: flash_primary +mcuboot_primary_app: + orig_span: &id002 + - app + span: *id002 + address: 0xc200 + size: 0xf1e00 +settings_storage: + address: 0xfe000 + size: 0x2000 + region: flash_primary +mcuboot_secondary: + address: 0x0 + size: 0xf2000 + device: MX25R64 + region: external_flash +external_flash: + address: 0xf2000 + size: 0x0 + device: MX25R64 + region: external_flash diff --git a/samples/matter/light_bulb/prj.conf b/samples/matter/light_bulb/prj.conf index 04ea5abb9c64..27e6f356b7f6 100644 --- a/samples/matter/light_bulb/prj.conf +++ b/samples/matter/light_bulb/prj.conf @@ -47,6 +47,7 @@ CONFIG_BT_MAX_PAIRED=0 CONFIG_BT_BONDABLE=n CONFIG_BT_TINYCRYPT_ECC=n CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY=y +CONFIG_SDC_MAX_CONN_EVENT_LEN_DEFAULT=3000 # Use NFC to share commissioning information CONFIG_CHIP_NFC_COMMISSIONING=y diff --git a/samples/matter/light_bulb/src/app_event.h b/samples/matter/light_bulb/src/app_event.h index 55a70f510c99..b1cba9a3c81f 100644 --- a/samples/matter/light_bulb/src/app_event.h +++ b/samples/matter/light_bulb/src/app_event.h @@ -11,12 +11,9 @@ struct AppEvent { enum LightEventType : uint8_t { On, Off, Toggle, Level }; - enum OtherEventType : uint8_t { - FactoryReset = Level + 1, - StartThread, - StartBleAdvertising, - PublishLightBulbService - }; + enum FunctionEventType : uint8_t { FunctionPress = Level + 1, FunctionRelease, FunctionTimer }; + + enum OtherEventType : uint8_t { StartThread = FunctionTimer + 1, StartBleAdvertising, PublishLightBulbService }; AppEvent() = default; @@ -25,6 +22,8 @@ struct AppEvent { { } + AppEvent(FunctionEventType type) : Type(type) {} + AppEvent(OtherEventType type) : Type(type) {} uint8_t Type; diff --git a/samples/matter/light_bulb/src/app_task.cpp b/samples/matter/light_bulb/src/app_task.cpp index 4619aa254441..a7e05ff8ba45 100644 --- a/samples/matter/light_bulb/src/app_task.cpp +++ b/samples/matter/light_bulb/src/app_task.cpp @@ -20,6 +20,20 @@ #include "gen/attribute-type.h" #include "gen/cluster-id.h" +/* MCUMgr BT FOTA includes */ +#ifdef CONFIG_MCUMGR_CMD_OS_MGMT +#include "os_mgmt/os_mgmt.h" +#endif +#ifdef CONFIG_MCUMGR_CMD_IMG_MGMT +#include "img_mgmt/img_mgmt.h" +#endif +#ifdef CONFIG_MCUMGR_SMP_BT +#include +#endif +#ifdef CONFIG_BOOTLOADER_MCUBOOT +#include +#endif + #include #include #include @@ -35,8 +49,13 @@ K_MSGQ_DEFINE(sAppEventQueue, sizeof(AppEvent), AppTask::APP_EVENT_QUEUE_SIZE, a namespace { +static constexpr uint32_t kFactoryResetTriggerTimeout = 3000; +static constexpr uint32_t kFactoryResetCancelWindow = 3000; + LEDWidget sStatusLED; LEDWidget sLightLED; +LEDWidget sUnusedLED; +LEDWidget sUnusedLED_1; LightBulbPublishService sLightBulbPublishService; @@ -44,6 +63,8 @@ bool sIsThreadProvisioned; bool sIsThreadEnabled; bool sHaveBLEConnections; bool sHaveServiceConnectivity; + +k_timer sFunctionTimer; } /* namespace */ AppTask AppTask::sAppTask; @@ -56,6 +77,8 @@ int AppTask::Init() sStatusLED.Init(DK_LED1); sLightLED.Init(DK_LED2); sLightLED.Set(false); + sUnusedLED.Init(DK_LED3); + sUnusedLED_1.Init(DK_LED4); /* Initialize buttons */ int ret = dk_buttons_init(ButtonEventHandler); @@ -64,6 +87,22 @@ int AppTask::Init() return ret; } +#ifdef CONFIG_BOOTLOADER_MCUBOOT + /* Check if the image is run in the REVERT mode and eventually + confirm it to prevent reverting on the next boot. */ + if (mcuboot_swap_type() == BOOT_SWAP_TYPE_REVERT) { + if (boot_write_img_confirmed()) { + LOG_ERR("Confirming firmware image failed, it will be reverted on the next boot."); + } else { + LOG_INF("New firmware image confirmed."); + } + } +#endif + + /* Initialize function timer */ + k_timer_init(&sFunctionTimer, &AppTask::TimerEventHandler, nullptr); + k_timer_user_data_set(&sFunctionTimer, this); + /* Initialize light manager */ ret = LightingMgr().Init(DEVICE_DT_GET(DT_PWMS_CTLR(PWM_LED)), DT_PWMS_CHANNEL(PWM_LED)); if (ret) { @@ -141,6 +180,8 @@ int AppTask::StartApp() } sStatusLED.Animate(); + sUnusedLED.Animate(); + sUnusedLED_1.Animate(); } } @@ -173,6 +214,14 @@ void AppTask::UpdateClusterState() } } +int AppTask::SoftwareUpdateConfirmationHandler(uint32_t offset, uint32_t size, void *arg) +{ + /* For now just print update progress and confirm data chunk without any additional checks. */ + LOG_INF("Software update progress %d B / %d B", offset, size); + + return 0; +} + void AppTask::DispatchEvent(const AppEvent &aEvent) { switch (aEvent.Type) { @@ -193,9 +242,14 @@ void AppTask::DispatchEvent(const AppEvent &aEvent) LightingMgr().InitiateAction(LightingManager::Action::Level, aEvent.LightEvent.Value, aEvent.LightEvent.ChipInitiated); break; - case AppEvent::FactoryReset: - LOG_INF("Factory Reset triggered"); - ConfigurationMgr().InitiateFactoryReset(); + case AppEvent::FunctionPress: + FunctionPressHandler(); + break; + case AppEvent::FunctionRelease: + FunctionReleaseHandler(); + break; + case AppEvent::FunctionTimer: + FunctionTimerEventHandler(); break; case AppEvent::StartThread: StartThreadHandler(); @@ -238,6 +292,68 @@ void AppTask::ActionCompleted(LightingManager::Action aAction) } } +void AppTask::FunctionPressHandler() +{ + sAppTask.StartFunctionTimer(kFactoryResetTriggerTimeout); + sAppTask.mFunction = TimerFunction::SoftwareUpdate; +} + +void AppTask::FunctionReleaseHandler() +{ + if (sAppTask.mFunction == TimerFunction::SoftwareUpdate) { + sAppTask.CancelFunctionTimer(); + sAppTask.mFunction = TimerFunction::NoneSelected; + +#if defined(CONFIG_MCUMGR_SMP_BT) && defined(CONFIG_MCUMGR_CMD_IMG_MGMT) && defined(CONFIG_MCUMGR_CMD_OS_MGMT) + if (!sAppTask.mSoftwareUpdateEnabled) { + sAppTask.mSoftwareUpdateEnabled = true; + os_mgmt_register_group(); + img_mgmt_register_group(); + img_mgmt_set_upload_cb(SoftwareUpdateConfirmationHandler, NULL); + smp_bt_register(); + + LOG_INF("Enabled software update"); + } else { + LOG_INF("Software update is already enabled"); + } + +#else + LOG_INF("Software update is disabled"); +#endif + + } else if (sAppTask.mFunction == TimerFunction::FactoryReset) { + sUnusedLED_1.Set(false); + sUnusedLED.Set(false); + + sAppTask.CancelFunctionTimer(); + sAppTask.mFunction = TimerFunction::NoneSelected; + LOG_INF("Factory Reset has been Canceled"); + } +} + +void AppTask::FunctionTimerEventHandler() +{ + if (sAppTask.mFunction == TimerFunction::SoftwareUpdate) { + LOG_INF("Factory Reset Triggered. Release button within %ums to cancel.", kFactoryResetCancelWindow); + sAppTask.StartFunctionTimer(kFactoryResetCancelWindow); + sAppTask.mFunction = TimerFunction::FactoryReset; + + /* Turn off all LEDs before starting blink to make sure blink is co-ordinated. */ + sStatusLED.Set(false); + sUnusedLED_1.Set(false); + sUnusedLED.Set(false); + + sStatusLED.Blink(500); + sUnusedLED.Blink(500); + sUnusedLED_1.Blink(500); + + } else if (sAppTask.mFunction == TimerFunction::FactoryReset) { + sAppTask.mFunction = TimerFunction::NoneSelected; + LOG_INF("Factory Reset triggered"); + ConfigurationMgr().InitiateFactoryReset(); + } +} + void AppTask::StartThreadHandler() { if (AddTestPairing() != CHIP_NO_ERROR) { @@ -256,7 +372,7 @@ void AppTask::StartThreadHandler() void AppTask::StartBLEAdvertisingHandler() { - if (chip::DeviceLayer::ConnectivityMgr().IsThreadProvisioned()) { + if (chip::DeviceLayer::ConnectivityMgr().IsThreadProvisioned() && !sAppTask.mSoftwareUpdateEnabled) { LOG_INF("NFC Tag emulation and BLE advertisement not started - device is commissioned to a Thread network."); return; } @@ -294,7 +410,9 @@ void AppTask::ThreadProvisioningHandler(const ChipDeviceEvent *event, intptr_t) void AppTask::ButtonEventHandler(uint32_t buttonState, uint32_t hasChanged) { if (DK_BTN1_MSK & buttonState & hasChanged) { - GetAppTask().PostEvent(AppEvent{ AppEvent::FactoryReset }); + GetAppTask().PostEvent(AppEvent{ AppEvent::FunctionPress }); + } else if (DK_BTN1_MSK & hasChanged) { + GetAppTask().PostEvent(AppEvent{ AppEvent::FunctionRelease }); } if (DK_BTN2_MSK & buttonState & hasChanged) { @@ -309,3 +427,18 @@ void AppTask::ButtonEventHandler(uint32_t buttonState, uint32_t hasChanged) GetAppTask().PostEvent(AppEvent{ AppEvent::StartBleAdvertising }); } } + +void AppTask::CancelFunctionTimer() +{ + k_timer_stop(&sFunctionTimer); +} + +void AppTask::StartFunctionTimer(uint32_t timeoutInMs) +{ + k_timer_start(&sFunctionTimer, K_MSEC(timeoutInMs), K_NO_WAIT); +} + +void AppTask::TimerEventHandler(k_timer *timer) +{ + GetAppTask().PostEvent(AppEvent{ AppEvent::FunctionTimer }); +} diff --git a/samples/matter/light_bulb/src/app_task.h b/samples/matter/light_bulb/src/app_task.h index f4597b0db531..715ac211c4c7 100644 --- a/samples/matter/light_bulb/src/app_task.h +++ b/samples/matter/light_bulb/src/app_task.h @@ -25,8 +25,13 @@ class AppTask { private: int Init(); + void CancelFunctionTimer(); + void StartFunctionTimer(uint32_t timeoutInMs); + void DispatchEvent(const AppEvent &event); - void CompleteLockActionHandler(); + void FunctionPressHandler(); + void FunctionReleaseHandler(); + void FunctionTimerEventHandler(); void StartThreadHandler(); void StartBLEAdvertisingHandler(); @@ -37,11 +42,18 @@ class AppTask { static void ActionInitiated(LightingManager::Action aAction); static void ActionCompleted(LightingManager::Action aAction); static void ButtonEventHandler(uint32_t buttonState, uint32_t hasChanged); + static void TimerEventHandler(k_timer *timer); static void ThreadProvisioningHandler(const chip::DeviceLayer::ChipDeviceEvent *event, intptr_t arg); + static int SoftwareUpdateConfirmationHandler(uint32_t offset, uint32_t size, void *arg); friend AppTask &GetAppTask(); + enum class TimerFunction { NoneSelected = 0, SoftwareUpdate, FactoryReset }; + + TimerFunction mFunction = TimerFunction::NoneSelected; + static AppTask sAppTask; + bool mSoftwareUpdateEnabled = false; }; inline AppTask &GetAppTask() diff --git a/samples/matter/lock/README.rst b/samples/matter/lock/README.rst index d63ac24f4c3a..112ad9b72b1f 100644 --- a/samples/matter/lock/README.rst +++ b/samples/matter/lock/README.rst @@ -70,6 +70,26 @@ Alternatively to the commissioning procedure, you can use the test mode, which a .. matter_door_lock_sample_test_mode_end +Configuration +************* + +|config| + +Device Firmware Upgrade support +=============================== + +.. matter_door_lock_sample_build_with_dfu_start +You can configure the sample to use the secure bootloader for performing over-the-air Device Firmware Upgrade using Bluetooth LE. + +To build the sample with configuration that enables the DFU, run the following command with *build_target* replaced with the build target name of the hardware platform you are using (see `Requirements`_): + +.. parsed-literal:: + :class: highlight + + west build -b *build_target* -- -DOVERLAY_CONFIG=../common/config/overlay-dfu_support.conf -DPM_STATIC_YML_FILE="configuration/build-target/pm_static.yml" + +.. matter_door_lock_sample_build_with_dfu_end + User interface ************** @@ -94,8 +114,17 @@ LED 2: * Off - The bolt is retracted and the door is unlocked. * Rapid Even Flashing (50 ms on/50 ms off during 2 s) - The simulated bolt is in motion from one position to another. +.. matter_door_lock_sample_button1_start + Button 1: - Initiates the factory reset of the device. + Depending on how long you press it: + + * If pressed for 6 seconds, it initiates the factory reset of the device. + Releasing the button within the 6-second window cancels the factory reset procedure. + * If pressed for less than 3 seconds, it initiates the OTA software update process. + The OTA process is disabled by default, but you can enable it when you build the sample with the DFU support (see `Configuration`_). + +.. matter_door_lock_sample_button1_end Button 2: Changes the lock state to the opposite one. @@ -120,6 +149,8 @@ Building and running .. include:: /includes/build_and_run.txt +See `Configuration`_ for information about building the sample with the DFU support. + Testing ======= @@ -192,10 +223,15 @@ The data payload, which includes the device discriminator and setup PIN code, is .. matter_door_lock_sample_commissioning_end +Upgrading the device firmware +============================= + +To upgrade the device firmware, complete the steps listed for the selected method in the `Performing Device Firmware Upgrade in Matter device`_ tutorial. + Dependencies ************ -This sample uses Connected Home over IP library which includes the |NCS| platform integration layer: +This sample uses the Matter library, which includes the |NCS| platform integration layer: * `Matter`_ diff --git a/samples/matter/lock/child_image/mcuboot.conf b/samples/matter/lock/child_image/mcuboot.conf new file mode 100644 index 000000000000..422be5440286 --- /dev/null +++ b/samples/matter/lock/child_image/mcuboot.conf @@ -0,0 +1,19 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# QSPI configuration +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=4 + +CONFIG_MULTITHREADING=y +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# External flash memory configuration +CONFIG_PM_EXTERNAL_FLASH=y +CONFIG_PM_EXTERNAL_FLASH_DEV_NAME="MX25R64" +CONFIG_PM_EXTERNAL_FLASH_SIZE=0xf2000 +CONFIG_PM_EXTERNAL_FLASH_BASE=0 diff --git a/samples/matter/lock/configuration/nrf52840dk_nrf52840/pm_static.yml b/samples/matter/lock/configuration/nrf52840dk_nrf52840/pm_static.yml new file mode 100644 index 000000000000..b194f921be3b --- /dev/null +++ b/samples/matter/lock/configuration/nrf52840dk_nrf52840/pm_static.yml @@ -0,0 +1,38 @@ +mcuboot: + address: 0x0 + size: 0xc000 + region: flash_primary +mcuboot_pad: + address: 0xc000 + size: 0x200 +app: + address: 0xc200 + size: 0xf1e00 +mcuboot_primary: + orig_span: &id001 + - mcuboot_pad + - app + span: *id001 + address: 0xc000 + size: 0xf2000 + region: flash_primary +mcuboot_primary_app: + orig_span: &id002 + - app + span: *id002 + address: 0xc200 + size: 0xf1e00 +settings_storage: + address: 0xfe000 + size: 0x2000 + region: flash_primary +mcuboot_secondary: + address: 0x0 + size: 0xf2000 + device: MX25R64 + region: external_flash +external_flash: + address: 0xf2000 + size: 0x0 + device: MX25R64 + region: external_flash diff --git a/samples/matter/lock/prj.conf b/samples/matter/lock/prj.conf index 2b49951e59d5..c14dc25e577e 100644 --- a/samples/matter/lock/prj.conf +++ b/samples/matter/lock/prj.conf @@ -47,6 +47,7 @@ CONFIG_BT_MAX_PAIRED=0 CONFIG_BT_BONDABLE=n CONFIG_BT_TINYCRYPT_ECC=n CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY=y +CONFIG_SDC_MAX_CONN_EVENT_LEN_DEFAULT=3000 # Use NFC to share commissioning information CONFIG_CHIP_NFC_COMMISSIONING=y diff --git a/samples/matter/lock/src/app_event.h b/samples/matter/lock/src/app_event.h index fa0e30492589..e8c96de5fe6e 100644 --- a/samples/matter/lock/src/app_event.h +++ b/samples/matter/lock/src/app_event.h @@ -11,10 +11,13 @@ struct AppEvent { enum LockEventType : uint8_t { Lock, Unlock, Toggle, CompleteLockAction }; - enum OtherEventType : uint8_t { FactoryReset = CompleteLockAction + 1, StartThread, StartBleAdvertising }; + enum FunctionEventType : uint8_t { FunctionPress = CompleteLockAction + 1, FunctionRelease, FunctionTimer }; + + enum OtherEventType : uint8_t { StartThread = FunctionTimer + 1, StartBleAdvertising }; AppEvent() = default; AppEvent(LockEventType type, bool chipInitiated) : Type(type), LockEvent{ chipInitiated } {} + explicit AppEvent(FunctionEventType type) : Type(type) {} explicit AppEvent(OtherEventType type) : Type(type) {} uint8_t Type; diff --git a/samples/matter/lock/src/app_task.cpp b/samples/matter/lock/src/app_task.cpp index 7f786eab3e80..3d8ff0fc3673 100644 --- a/samples/matter/lock/src/app_task.cpp +++ b/samples/matter/lock/src/app_task.cpp @@ -19,6 +19,20 @@ #include "gen/attribute-type.h" #include "gen/cluster-id.h" +/* MCUMgr BT FOTA includes */ +#ifdef CONFIG_MCUMGR_CMD_OS_MGMT +#include "os_mgmt/os_mgmt.h" +#endif +#ifdef CONFIG_MCUMGR_CMD_IMG_MGMT +#include "img_mgmt/img_mgmt.h" +#endif +#ifdef CONFIG_MCUMGR_SMP_BT +#include +#endif +#ifdef CONFIG_BOOTLOADER_MCUBOOT +#include +#endif + #include #include #include @@ -32,15 +46,21 @@ LOG_MODULE_DECLARE(app); namespace { static constexpr size_t kAppEventQueueSize = 10; +static constexpr uint32_t kFactoryResetTriggerTimeout = 3000; +static constexpr uint32_t kFactoryResetCancelWindow = 3000; K_MSGQ_DEFINE(sAppEventQueue, sizeof(AppEvent), kAppEventQueueSize, alignof(AppEvent)); LEDWidget sStatusLED; LEDWidget sLockLED; +LEDWidget sUnusedLED; +LEDWidget sUnusedLED_1; bool sIsThreadProvisioned; bool sIsThreadEnabled; bool sHaveBLEConnections; bool sHaveServiceConnectivity; + +k_timer sFunctionTimer; } /* namespace */ AppTask AppTask::sAppTask; @@ -53,6 +73,8 @@ int AppTask::Init() sStatusLED.Init(DK_LED1); sLockLED.Init(DK_LED2); sLockLED.Set(!BoltLockMgr().IsUnlocked()); + sUnusedLED.Init(DK_LED3); + sUnusedLED_1.Init(DK_LED4); /* Initialize buttons */ int ret = dk_buttons_init(ButtonEventHandler); @@ -61,6 +83,22 @@ int AppTask::Init() return ret; } +#ifdef CONFIG_BOOTLOADER_MCUBOOT + /* Check if the image is run in the REVERT mode and eventually + confirm it to prevent reverting on the next boot. */ + if (mcuboot_swap_type() == BOOT_SWAP_TYPE_REVERT) { + if (boot_write_img_confirmed()) { + LOG_ERR("Confirming firmware image failed, it will be reverted on the next boot."); + } else { + LOG_INF("New firmware image confirmed."); + } + } +#endif + + /* Initialize function timer */ + k_timer_init(&sFunctionTimer, &AppTask::TimerEventHandler, nullptr); + k_timer_user_data_set(&sFunctionTimer, this); + /* Initialize lock manager */ BoltLockMgr().Init(); @@ -133,6 +171,8 @@ int AppTask::StartApp() sStatusLED.Animate(); sLockLED.Animate(); + sUnusedLED.Animate(); + sUnusedLED_1.Animate(); } } @@ -156,6 +196,14 @@ void AppTask::UpdateClusterState() } } +int AppTask::SoftwareUpdateConfirmationHandler(uint32_t offset, uint32_t size, void *arg) +{ + /* For now just print update progress and confirm data chunk without any additional checks. */ + LOG_INF("Software update progress %d B / %d B", offset, size); + + return 0; +} + void AppTask::DispatchEvent(const AppEvent &event) { switch (event.Type) { @@ -173,9 +221,14 @@ void AppTask::DispatchEvent(const AppEvent &event) case AppEvent::CompleteLockAction: CompleteLockActionHandler(); break; - case AppEvent::FactoryReset: - LOG_INF("Factory Reset triggered"); - ConfigurationMgr().InitiateFactoryReset(); + case AppEvent::FunctionPress: + FunctionPressHandler(); + break; + case AppEvent::FunctionRelease: + FunctionReleaseHandler(); + break; + case AppEvent::FunctionTimer: + FunctionTimerEventHandler(); break; case AppEvent::StartThread: StartThreadHandler(); @@ -212,6 +265,73 @@ void AppTask::CompleteLockActionHandler() } } +void AppTask::FunctionPressHandler() +{ + sAppTask.StartFunctionTimer(kFactoryResetTriggerTimeout); + sAppTask.mFunction = TimerFunction::SoftwareUpdate; +} + +void AppTask::FunctionReleaseHandler() +{ + if (sAppTask.mFunction == TimerFunction::SoftwareUpdate) { + sAppTask.CancelFunctionTimer(); + sAppTask.mFunction = TimerFunction::NoneSelected; + +#if defined(CONFIG_MCUMGR_SMP_BT) && defined(CONFIG_MCUMGR_CMD_IMG_MGMT) && defined(CONFIG_MCUMGR_CMD_OS_MGMT) + if (!sAppTask.mSoftwareUpdateEnabled) { + sAppTask.mSoftwareUpdateEnabled = true; + os_mgmt_register_group(); + img_mgmt_register_group(); + img_mgmt_set_upload_cb(SoftwareUpdateConfirmationHandler, NULL); + smp_bt_register(); + + LOG_INF("Enabled software update"); + } else { + LOG_INF("Software update is already enabled"); + } + +#else + LOG_INF("Software update is disabled"); +#endif + + } else if (sAppTask.mFunction == TimerFunction::FactoryReset) { + sUnusedLED_1.Set(false); + sUnusedLED.Set(false); + + /* Set lock status LED back to show state of lock. */ + sLockLED.Set(!BoltLockMgr().IsUnlocked()); + + sAppTask.CancelFunctionTimer(); + sAppTask.mFunction = TimerFunction::NoneSelected; + LOG_INF("Factory Reset has been Canceled"); + } +} + +void AppTask::FunctionTimerEventHandler() +{ + if (sAppTask.mFunction == TimerFunction::SoftwareUpdate) { + LOG_INF("Factory Reset Triggered. Release button within %ums to cancel.", kFactoryResetCancelWindow); + sAppTask.StartFunctionTimer(kFactoryResetCancelWindow); + sAppTask.mFunction = TimerFunction::FactoryReset; + + /* Turn off all LEDs before starting blink to make sure blink is co-ordinated. */ + sStatusLED.Set(false); + sLockLED.Set(false); + sUnusedLED_1.Set(false); + sUnusedLED.Set(false); + + sStatusLED.Blink(500); + sLockLED.Blink(500); + sUnusedLED.Blink(500); + sUnusedLED_1.Blink(500); + + } else if (sAppTask.mFunction == TimerFunction::FactoryReset) { + sAppTask.mFunction = TimerFunction::NoneSelected; + LOG_INF("Factory Reset triggered"); + ConfigurationMgr().InitiateFactoryReset(); + } +} + void AppTask::StartThreadHandler() { if (AddTestPairing() != CHIP_NO_ERROR) { @@ -228,7 +348,7 @@ void AppTask::StartThreadHandler() void AppTask::StartBLEAdvertisingHandler() { - if (chip::DeviceLayer::ConnectivityMgr().IsThreadProvisioned()) { + if (chip::DeviceLayer::ConnectivityMgr().IsThreadProvisioned() && !sAppTask.mSoftwareUpdateEnabled) { LOG_INF("NFC Tag emulation and BLE advertisement not started - device is commissioned to a Thread network."); return; } @@ -266,7 +386,9 @@ void AppTask::ThreadProvisioningHandler(const ChipDeviceEvent *event, intptr_t) void AppTask::ButtonEventHandler(uint32_t buttonState, uint32_t hasChanged) { if (DK_BTN1_MSK & buttonState & hasChanged) { - GetAppTask().PostEvent(AppEvent{ AppEvent::FactoryReset }); + GetAppTask().PostEvent(AppEvent{ AppEvent::FunctionPress }); + } else if (DK_BTN1_MSK & hasChanged) { + GetAppTask().PostEvent(AppEvent{ AppEvent::FunctionRelease }); } if (DK_BTN2_MSK & buttonState & hasChanged) { @@ -281,3 +403,18 @@ void AppTask::ButtonEventHandler(uint32_t buttonState, uint32_t hasChanged) GetAppTask().PostEvent(AppEvent{ AppEvent::StartBleAdvertising }); } } + +void AppTask::CancelFunctionTimer() +{ + k_timer_stop(&sFunctionTimer); +} + +void AppTask::StartFunctionTimer(uint32_t timeoutInMs) +{ + k_timer_start(&sFunctionTimer, K_MSEC(timeoutInMs), K_NO_WAIT); +} + +void AppTask::TimerEventHandler(k_timer *timer) +{ + GetAppTask().PostEvent(AppEvent{ AppEvent::FunctionTimer }); +} diff --git a/samples/matter/lock/src/app_task.h b/samples/matter/lock/src/app_task.h index 9c83e687b5d6..667854d25e09 100644 --- a/samples/matter/lock/src/app_task.h +++ b/samples/matter/lock/src/app_task.h @@ -23,9 +23,15 @@ class AppTask { private: int Init(); + void CancelFunctionTimer(); + void StartFunctionTimer(uint32_t timeoutInMs); + void DispatchEvent(const AppEvent &event); void LockActionHandler(BoltLockManager::Action action, bool chipInitiated); void CompleteLockActionHandler(); + void FunctionPressHandler(); + void FunctionReleaseHandler(); + void FunctionTimerEventHandler(); void StartThreadHandler(); void StartBLEAdvertisingHandler(); @@ -34,11 +40,19 @@ class AppTask { #endif static void ButtonEventHandler(uint32_t buttonState, uint32_t hasChanged); + static void TimerEventHandler(k_timer *timer); static void ThreadProvisioningHandler(const chip::DeviceLayer::ChipDeviceEvent *event, intptr_t arg); + static int SoftwareUpdateConfirmationHandler(uint32_t offset, uint32_t size, void *arg); friend AppTask &GetAppTask(); + enum class TimerFunction { NoneSelected = 0, SoftwareUpdate, FactoryReset }; + + TimerFunction mFunction = TimerFunction::NoneSelected; + static AppTask sAppTask; + bool mFunctionTimerActive = false; + bool mSoftwareUpdateEnabled = false; }; inline AppTask &GetAppTask()