diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index e80bfe27bba6..3fcd6931120f 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -466,6 +466,7 @@ Cellular samples * The :ref:`nrf_cloud_coap_fota_sample` sample to demonstrate how to use the `nRF Cloud CoAP API`_ for FOTA updates. * The :ref:`nrf_cloud_coap_device_message` sample to demonstrate how to use the `nRF Cloud CoAP API`_ for device messages. * The :ref:`nrf_cloud_mqtt_device_message` sample to demonstrate how to use the `nRF Cloud MQTT API`_ for device messages. + * The :ref:`nrf_cloud_mqtt_fota` sample to demonstrate how to use the `nRF Cloud MQTT API`_ for FOTA updates. * Removed the SLM Shell sample. Use the `Serial Modem Host Shell`_ sample instead. diff --git a/samples/cellular/nrf_cloud_mqtt_fota/CMakeLists.txt b/samples/cellular/nrf_cloud_mqtt_fota/CMakeLists.txt new file mode 100644 index 000000000000..ec54c79431ac --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/CMakeLists.txt @@ -0,0 +1,18 @@ +# +# Copyright (c) 2025 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(nrf_cloud_mqtt_fota) + +# NORDIC SDK APP START +target_sources(app PRIVATE src/main.c) + +if(CONFIG_NRF_CLOUD_FOTA_SMP AND CONFIG_BOARD_NRF9160DK_NRF9160_NS) + target_sources(app PRIVATE src/smp_reset.c) +endif() +# NORDIC SDK APP END diff --git a/samples/cellular/nrf_cloud_mqtt_fota/Kconfig b/samples/cellular/nrf_cloud_mqtt_fota/Kconfig new file mode 100644 index 000000000000..b68d67395e71 --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/Kconfig @@ -0,0 +1,31 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +menu "nRF Cloud MQTT FOTA Sample" + +config CLOUD_CONNECTION_RETRY_TIMEOUT_SECONDS + int "Cloud connection retry timeout (seconds)" + default 30 + help + If connecting to nRF Cloud takes longer than this timeout, it will be + reattempted. + +config CLOUD_READY_TIMEOUT_SECONDS + int "Cloud readiness timeout (seconds)" + default 600 + help + If the connection to nRF Cloud does not become ready within this + timeout, the sample will reset its connection and try again. + +module = NRF_CLOUD_MQTT_FOTA_SAMPLE +module-str = nRF Cloud MQTT FOTA Sample +source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" + +endmenu + +menu "Zephyr Kernel" +source "Kconfig.zephyr" +endmenu diff --git a/samples/cellular/nrf_cloud_mqtt_fota/README.rst b/samples/cellular/nrf_cloud_mqtt_fota/README.rst new file mode 100644 index 000000000000..1616e8069c9c --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/README.rst @@ -0,0 +1,163 @@ +.. _nrf_cloud_mqtt_fota: + +Cellular: nRF Cloud MQTT FOTA +############################# + +.. contents:: + :local: + :depth: 2 + +The nRF Cloud MQTT FOTA sample demonstrates how to use the `nRF Cloud MQTT API`_ to perform :term:`Firmware Over-the-Air (FOTA) ` updates over MQTT on your device. +This covers modem, application, and full modem FOTA updates (FMFU). +Also, with the nRF9160 DK, it supports SMP FOTA updates to the firmware on the nRF52840 SoC present on the DK board (not a separate device). + +When using MQTT, the `FOTA update `_ support is almost entirely implemented by enabling the :kconfig:option:`CONFIG_NRF_CLOUD_FOTA` option, which is implicitly enabled by :kconfig:option:`CONFIG_NRF_CLOUD_MQTT`. + +However, even with the :kconfig:option:`CONFIG_NRF_CLOUD_FOTA` Kconfig option enabled, applications must still reboot themselves manually after FOTA download completion, and must still update their `Device Shadow `_ to reflect FOTA support. + +Requirements +************ + +The sample supports the following development kits: + +.. table-from-sample-yaml:: + +.. include:: /includes/tfm.txt + +The sample requires an `nRF Cloud`_ account. + +Your device must be onboarded to nRF Cloud. +If it is not, follow the instructions in `Device onboarding `_. + +.. note:: + This sample requires modem firmware v1.3.x or later for an nRF9160 SiP and v2.0.0 or later for nRF9161 and nRF9151 SiPs. + +.. include:: /includes/external_flash_nrf91.txt + +.. note:: + Full modem FOTA requires development kit version 0.14.0 or higher if you are using an nRF9160 DK. + +Overview +******** + +You can update your device firmware on the `nRF Cloud`_ portal or directly through the `nRF Cloud MQTT API`_. +See the `nRF Cloud Getting Started FOTA documentation`_ for details. + +.. _nrf_cloud_mqtt_fota_sample_onboarding: + +Setup +===== + +You must onboard your device to nRF Cloud for this sample to function. +You only need to do this once for each device. + +To onboard your device, install `nRF Cloud Utils`_ and follow the instructions in the README. + +Configuration +************* + +|config| + +Configuration options +===================== + +Check and configure the following configuration options for the sample: + +.. options-from-kconfig:: + :show-type: + +.. include:: /libraries/modem/nrf_modem_lib/nrf_modem_lib_trace.rst + :start-after: modem_lib_sending_traces_UART_start + :end-before: modem_lib_sending_traces_UART_end + +Building and running +******************** + +.. |sample path| replace:: :file:`samples/cellular/nrf_cloud_mqtt_fota` + +.. include:: /includes/build_and_run_ns.txt + +The configuration files for this sample are located in the :file:`samples/cellular/nrf_cloud_mqtt_fota` folder. +See :ref:`configure_application` on how to configure the parameters. + +To create a FOTA test version of this sample, change the ``PATCHLEVEL`` in the :file:`VERSION` file. + +To enable full modem FOTA, add the following parameter to your build command: + +``-DEXTRA_CONF_FILE="full_modem_fota.conf"`` + +Also, if you are using an nRF9160 DK, specify your development kit version by appending it to the board target. +For example, if you are using version 0.14.0, use the board target ``nrf9160dk@0.14.0/nrf9160/ns`` in your build command. + +To enable SMP FOTA (nRF9160 DK only), add the following parameters to your build command: + +* ``-DEXTRA_CONF_FILE="smp_fota.conf"`` +* ``-DEXTRA_DTC_OVERLAY_FILE="nrf9160dk_mcumgr_client_uart2.overlay"`` + +Once you have flashed your nRF9160 DK, change the switch **SW10** to the **nRF52** position to be able to flash the nRF52840 firmware on the DK. +The nRF52840 device on your DK must be running firmware compatible with SMP, such as the :ref:`smp_svr` sample. +Otherwise, the MQTT FOTA sample cannot connect to the nRF52840 and will keep trying to connect. +Build the :ref:`smp_svr` sample for the ``nrf9160dk/nrf52840`` board target with the following parameters: + +* ``-DEXTRA_CONF_FILE="overlay-serial.conf"`` +* ``-DEXTRA_DTC_OVERLAY_FILE="nrf9160dk_nrf52840_mcumgr_svr.overlay"`` + +To change :ref:`smp_svr` sample's application version, set the :kconfig:option:`CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION` Kconfig option. + +Testing +======= + +|test_sample| + +#. |connect_kit| +#. |connect_terminal| +#. Reset the development kit. +#. Observe in the terminal window that the application starts. + This is indicated by output similar to the following (there is also a lot of additional information about the LTE connection): + + .. code-block:: console + + *** Booting My Application v1.0.0-f135319f826e *** + *** Using nRF Connect SDK v3.1.99-f135319f826e *** + *** Using Zephyr OS v4.2.99-be1a9fd0eeca *** + [00:00:00.255,889] nrf_cloud_mqtt_fota: nRF Cloud MQTT FOTA Sample, version: 1.0.0 + [00:00:00.255,920] nrf_cloud_mqtt_fota: Reset reason: 0x1 + [00:00:00.828,735] nrf_cloud_credentials: Sec Tag: 16842753; CA: Yes, Client Cert: Yes, Private Key: Yes + [00:00:00.828,765] nrf_cloud_credentials: CA Size: 1824, AWS: Likely, CoAP: Likely + [00:00:00.828,796] nrf_cloud_mqtt_fota: nRF Cloud credentials detected! + [00:00:00.829,589] nrf_cloud_mqtt_fota: Enabling connectivity... + +CGEV: EXCE STATUS 0 + +CEREG: 2,"81A6","03229B10",7 + %MDMEV: PRACH CE-LEVEL 0 + +CSCON: 1 + +CGEV: ME PDN ACT 0 + %MDMEV: SEARCH STATUS 2 + +CEREG: 1,"81A6","03229B10",7,,,"11100000","11100000" + [00:00:02.471,496] nrf_cloud_mqtt_fota: Connected to LTE + +CGEV: IPV6 0 + [00:00:05.473,724] nrf_cloud_info: Device ID: 12345678-1234-5678-9abc-def012345678 + [00:00:05.474,395] nrf_cloud_info: IMEI: 359404230026479 + [00:00:05.474,575] nrf_cloud_info: UUID: 12345678-1234-5678-9abc-def012345678 + [00:00:05.474,822] nrf_cloud_info: Modem FW: mfw_nrf91x1_2.0.3 + [00:00:05.474,853] nrf_cloud_info: Protocol: MQTT + [00:00:05.474,884] nrf_cloud_info: Download protocol: HTTPS + [00:00:05.474,884] nrf_cloud_info: Sec tag: 16842753 + [00:00:05.474,914] nrf_cloud_info: Host name: mqtt.nrfcloud.com + [00:00:05.474,945] nrf_cloud_mqtt_fota: Connecting to nRF Cloud... + [00:00:08.681,701] nrf_cloud_mqtt_fota: Connection to nRF Cloud ready + [00:00:08.681,793] nrf_cloud_info: Team ID: 12345678-1234-5678-9abc-def012345670 + +Dependencies +************ + +This sample uses the following |NCS| libraries: + +* :ref:`lib_nrf_cloud` +* :ref:`lte_lc_readme` +* :ref:`dk_buttons_and_leds_readme` +* :ref:`modem_info_readme` +* :ref:`lib_at_host` + +In addition, it uses the following secure firmware component: + +* :ref:`Trusted Firmware-M ` diff --git a/samples/cellular/nrf_cloud_mqtt_fota/VERSION b/samples/cellular/nrf_cloud_mqtt_fota/VERSION new file mode 100644 index 000000000000..16a13732e3ad --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/VERSION @@ -0,0 +1,5 @@ +VERSION_MAJOR = 1 +VERSION_MINOR = 0 +PATCHLEVEL = 0 +VERSION_TWEAK = 0 +EXTRAVERSION = diff --git a/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9151dk_nrf9151_ns.overlay b/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9151dk_nrf9151_ns.overlay new file mode 100644 index 000000000000..8fb85210ef15 --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9151dk_nrf9151_ns.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + chosen { + nordic,pm-ext-flash = &gd25wb256; + }; +}; + +/* External flash device is disabled by default */ +&gd25wb256 { + status = "okay"; +}; diff --git a/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9160dk_nrf9160_ns_0_14_0.overlay b/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9160dk_nrf9160_ns_0_14_0.overlay new file mode 100644 index 000000000000..22c5c23fe5a3 --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9160dk_nrf9160_ns_0_14_0.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + chosen { + nordic,pm-ext-flash = &mx25r64; + }; +}; + +/* External flash device is disabled by default */ +&mx25r64 { + status = "okay"; +}; diff --git a/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9161dk_nrf9161_ns.overlay b/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9161dk_nrf9161_ns.overlay new file mode 100644 index 000000000000..8fb85210ef15 --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9161dk_nrf9161_ns.overlay @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + chosen { + nordic,pm-ext-flash = &gd25wb256; + }; +}; + +/* External flash device is disabled by default */ +&gd25wb256 { + status = "okay"; +}; diff --git a/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9161dk_nrf9161_ns_0_7_0.overlay b/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9161dk_nrf9161_ns_0_7_0.overlay new file mode 100644 index 000000000000..066247ededed --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/boards/nrf9161dk_nrf9161_ns_0_7_0.overlay @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/delete-node/ &gd25wb256; + +/ { + chosen { + nordic,pm-ext-flash = &gd25lb256; + }; +}; + +&gd25lb256 { + status = "okay"; +}; diff --git a/samples/cellular/nrf_cloud_mqtt_fota/boards/thingy91x_nrf9151_ns.conf b/samples/cellular/nrf_cloud_mqtt_fota/boards/thingy91x_nrf9151_ns.conf new file mode 100644 index 000000000000..39bb084a1493 --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/boards/thingy91x_nrf9151_ns.conf @@ -0,0 +1,14 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Configuration file for Thingy:91 X. +# This file is merged with prj.conf in the application folder, and options +# set here will take precedence if they are present in both files. + +# Enable external flash +CONFIG_SPI_NOR_SFDP_DEVICETREE=y +CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK=y +CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y diff --git a/samples/cellular/nrf_cloud_mqtt_fota/boards/thingy91x_nrf9151_ns.overlay b/samples/cellular/nrf_cloud_mqtt_fota/boards/thingy91x_nrf9151_ns.overlay new file mode 100644 index 000000000000..9a104058b24f --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/boards/thingy91x_nrf9151_ns.overlay @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/ { + aliases { + ext-flash = &flash_ext; + }; +}; diff --git a/samples/cellular/nrf_cloud_mqtt_fota/full_modem_fota.conf b/samples/cellular/nrf_cloud_mqtt_fota/full_modem_fota.conf new file mode 100644 index 000000000000..4678d874b1ee --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/full_modem_fota.conf @@ -0,0 +1,20 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Full Modem FOTA +CONFIG_NRF_CLOUD_FOTA_FULL_MODEM_UPDATE=y +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK=y + +# FMFU requires additional heap space. +# If the heap is too small, a boot loop can occur when the full modem image is installed. +CONFIG_HEAP_MEM_POOL_SIZE=47250 + +CONFIG_IMG_ERASE_PROGRESSIVELY=n +CONFIG_SPI_NOR_SFDP_MINIMAL=n +CONFIG_SPI_NOR_SFDP_DEVICETREE=y diff --git a/samples/cellular/nrf_cloud_mqtt_fota/nrf9160dk_mcumgr_client_uart2.overlay b/samples/cellular/nrf_cloud_mqtt_fota/nrf9160dk_mcumgr_client_uart2.overlay new file mode 100644 index 000000000000..5fd654bff8d3 --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/nrf9160dk_mcumgr_client_uart2.overlay @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/* Generic PIN definition for UART2 or UART1 to nRF52840 */ +&pinctrl { + mcumgr_uart_default: mcumgr_uart_default { + group1 { + psels = , + ; + }; + + group2 { + psels = , + ; + bias-pull-up; + }; + }; + + mcumgr_uart_sleep: mcumgr_uart_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; +}; + +&uart2 { + compatible = "nordic,nrf-uarte"; + current-speed = <1000000>; + status = "okay"; + pinctrl-0 = <&mcumgr_uart_default>; + pinctrl-1 = <&mcumgr_uart_sleep>; + pinctrl-names = "default", "sleep"; + hw-flow-control; +}; + +/ { + chosen { + zephyr,uart-mcumgr = &uart2; + }; +}; diff --git a/samples/cellular/nrf_cloud_mqtt_fota/prj.conf b/samples/cellular/nrf_cloud_mqtt_fota/prj.conf new file mode 100644 index 000000000000..c90430339e2c --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/prj.conf @@ -0,0 +1,108 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# Sample +CONFIG_NRF_CLOUD_MQTT_FOTA_SAMPLE_LOG_LEVEL_INF=y + +# nRF Cloud MQTT +CONFIG_NRF_CLOUD=y +CONFIG_NRF_CLOUD_MQTT=y +CONFIG_NRF_CLOUD_CHECK_CREDENTIALS=y +CONFIG_NRF_CLOUD_CLIENT_ID_SRC_INTERNAL_UUID=y + +# MQTT +CONFIG_MQTT_KEEPALIVE=120 + +# Download Client +CONFIG_DFU_TARGET=y +CONFIG_DOWNLOADER=y +CONFIG_DOWNLOADER_STACK_SIZE=4352 +CONFIG_DOWNLOADER_MAX_HOSTNAME_SIZE=128 + +# MCUBOOT +CONFIG_BOOTLOADER_MCUBOOT=y +CONFIG_IMG_MANAGER=y +CONFIG_MCUBOOT_IMG_MANAGER=y +CONFIG_STREAM_FLASH_ERASE=y +CONFIG_IMG_ERASE_PROGRESSIVELY=y +CONFIG_SECURE_BOOT=y + +# Flash +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y +CONFIG_STREAM_FLASH=y +CONFIG_MPU_ALLOW_FLASH_WRITE=y +CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=n + +# Enable settings storage +CONFIG_SETTINGS=y +CONFIG_SETTINGS_FCB=y +CONFIG_FCB=y + +# LTE link control +CONFIG_LTE_LC_PSM_MODULE=y +CONFIG_LTE_LINK_CONTROL=y + +# LEDS and buttons +CONFIG_LED=y +CONFIG_LED_GPIO=y +CONFIG_DK_LIBRARY=y + +# Networking +CONFIG_NETWORKING=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_OFFLOAD=y + +# Enable LTE Connectivity using Connection Manager +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV6_NBR_CACHE=n +CONFIG_NET_IPV6_MLD=n +CONFIG_NET_CONNECTION_MANAGER=y +CONFIG_NRF_MODEM_LIB=y +CONFIG_NRF_MODEM_LIB_NET_IF=y +CONFIG_NRF_MODEM_LIB_NET_IF_AUTO_DOWN=y +CONFIG_NRF_MODEM_LIB_NET_IF_DOWN_DEFAULT_LTE_DISCONNECT=y +CONFIG_NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE=1024 +CONFIG_NET_MGMT_EVENT_STACK_SIZE=1024 + +# System +CONFIG_HEAP_MEM_POOL_SIZE=8192 +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1024 +CONFIG_ASSERT=y +CONFIG_REBOOT=y +CONFIG_FPU=y +CONFIG_PICOLIBC_IO_FLOAT=y +CONFIG_EVENTS=y +CONFIG_POSIX_API=y + +# Logging +CONFIG_LOG=y +CONFIG_LOG_MODE_DEFERRED=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# AT Host +CONFIG_AT_HOST_LIBRARY=y +CONFIG_AT_MONITOR_HEAP_SIZE=4096 +CONFIG_AT_HOST_STACK_SIZE=2048 + +# Modem info +CONFIG_MODEM_INFO=y +CONFIG_MODEM_INFO_ADD_DEVICE=y +CONFIG_MODEM_INFO_ADD_NETWORK=y +CONFIG_MODEM_INFO_ADD_SIM=y + +# Modem key management +CONFIG_MODEM_KEY_MGMT=y +CONFIG_NRF_CLOUD_SEC_TAG=16842753 + +# On initial connection to the cloud, add info sections to the shadow +CONFIG_NRF_CLOUD_SEND_DEVICE_STATUS=y +CONFIG_NRF_CLOUD_SEND_DEVICE_STATUS_NETWORK=y +CONFIG_NRF_CLOUD_SEND_DEVICE_STATUS_SIM=y +CONFIG_NRF_CLOUD_SEND_SERVICE_INFO_FOTA=y diff --git a/samples/cellular/nrf_cloud_mqtt_fota/rtt.conf b/samples/cellular/nrf_cloud_mqtt_fota/rtt.conf new file mode 100644 index 000000000000..1517c456731f --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/rtt.conf @@ -0,0 +1,9 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_USE_SEGGER_RTT=y +CONFIG_RTT_CONSOLE=y +CONFIG_UART_CONSOLE=n diff --git a/samples/cellular/nrf_cloud_mqtt_fota/sample.yaml b/samples/cellular/nrf_cloud_mqtt_fota/sample.yaml new file mode 100644 index 000000000000..3d0ca1da6b93 --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/sample.yaml @@ -0,0 +1,53 @@ +sample: + name: nRF Cloud MQTT FOTA +tests: + sample.cellular.nrf_cloud_mqtt_fota: + sysbuild: true + build_only: true + integration_platforms: + - nrf9151dk/nrf9151/ns + - nrf9160dk/nrf9160/ns + - nrf9161dk/nrf9161/ns + - thingy91x/nrf9151/ns + platform_allow: + - nrf9151dk/nrf9151/ns + - nrf9160dk/nrf9160/ns + - nrf9161dk/nrf9161/ns + - thingy91x/nrf9151/ns + tags: + - ci_build + - sysbuild + - ci_samples_cellular + sample.cellular.nrf_cloud_mqtt_fota_fmfu: + sysbuild: true + build_only: true + integration_platforms: + - nrf9151dk/nrf9151/ns + - nrf9160dk/nrf9160/ns + - nrf9161dk/nrf9161/ns + - thingy91x/nrf9151/ns + platform_allow: + - nrf9151dk/nrf9151/ns + - nrf9160dk/nrf9160/ns + - nrf9161dk/nrf9161/ns + - thingy91x/nrf9151/ns + tags: + - ci_build + - sysbuild + - ci_samples_cellular + extra_args: + - -DEXTRA_CONF_FILE="full_modem_fota.conf" + sample.cellular.nrf_cloud_mqtt_fota_smp: + sysbuild: true + build_only: true + integration_platforms: + - nrf9160dk/nrf9160/ns + platform_allow: + - nrf9160dk/nrf9160/ns + tags: + - ci_build + - sysbuild + - ci_samples_cellular + extra_args: + - -DEXTRA_CONF_FILE="smp_fota.conf" + - -DEXTRA_DTC_OVERLAY_FILE="nrf9160dk_mcumgr_client_uart2.overlay" diff --git a/samples/cellular/nrf_cloud_mqtt_fota/smp_fota.conf b/samples/cellular/nrf_cloud_mqtt_fota/smp_fota.conf new file mode 100644 index 000000000000..65915aa6815d --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/smp_fota.conf @@ -0,0 +1,15 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# +CONFIG_NRF_CLOUD_FOTA_SMP=y +CONFIG_NRF_MCUMGR_SMP_CLIENT=y +CONFIG_MCUMGR_GRP_IMG=y +CONFIG_MCUMGR_GRP_OS=y +CONFIG_MCUMGR_TRANSPORT_UART=y +CONFIG_UART_MCUMGR_RX_BUF_COUNT=4 +CONFIG_SMP_CMD_RETRY_TIME=1000 + +# Enable MCUboot util library +CONFIG_DFU_TARGET_SMP=y diff --git a/samples/cellular/nrf_cloud_mqtt_fota/src/main.c b/samples/cellular/nrf_cloud_mqtt_fota/src/main.c new file mode 100644 index 000000000000..7b806f0d5b47 --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/src/main.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if CONFIG_NRF_CLOUD_FOTA_SMP +#include "smp_reset.h" +#endif + +LOG_MODULE_REGISTER(nrf_cloud_mqtt_fota, + CONFIG_NRF_CLOUD_MQTT_FOTA_SAMPLE_LOG_LEVEL); + +#define NORMAL_REBOOT_S 10 + +/* LED to indicate LTE connectivity */ +#define LTE_LED_NUM DK_LED1 + +/* Boot message */ +#define SAMPLE_SIGNON_FMT "nRF Cloud MQTT FOTA Sample, version: %s" + +/* Network states */ +#define NETWORK_UP BIT(0) +#define CLOUD_READY BIT(1) +#define CLOUD_CONNECTED BIT(2) +#define CLOUD_DISCONNECTED BIT(3) +#define EVENT_MASK (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED) + +/* Connection event */ +static K_EVENT_DEFINE(connection_events); + +/* Atomic status flag tracking whether an initial association is in progress. */ +static atomic_t initial_association; +static bool device_deleted; +static int reconnect_seconds = CONFIG_CLOUD_CONNECTION_RETRY_TIMEOUT_SECONDS; + +/* nRF Cloud device ID */ +static char device_id[NRF_CLOUD_CLIENT_ID_MAX_LEN + 1]; + +static void disconnect_cloud(void); + +static bool cred_check(struct nrf_cloud_credentials_status *const cs) +{ + int ret = 0; + + ret = nrf_cloud_credentials_check(cs); + if (ret) { + LOG_ERR("nRF Cloud credentials check failed, error: %d", ret); + return false; + } + + /* For MQTT, we need three credentials: + * - a CA for the TLS connection + * - a client certificate for authentication + * - a private key for authentication + */ + if (!cs->ca || !cs->client_cert || !cs->prv_key) { + LOG_WRN("Missing required nRF Cloud credential(s) in sec tag %u:", cs->sec_tag); + } + if (!cs->ca) { + LOG_WRN("\t-CA Cert"); + } + if (!cs->client_cert) { + LOG_WRN("\t-Client Cert"); + } + if (!cs->prv_key) { + LOG_WRN("\t-Private Key"); + } + + return (cs->ca && cs->client_cert && cs->prv_key); +} + +static void await_credentials(void) +{ + struct nrf_cloud_credentials_status cs; + + while (!cred_check(&cs)) { + LOG_INF("Waiting for credentials to be installed..."); + LOG_INF("Press the reset button once the credentials are installed"); + k_sleep(K_FOREVER); + } + + LOG_INF("nRF Cloud credentials detected!"); +} + +static void print_reset_reason(void) +{ + int reset_reason = 0; + + reset_reason = nrfx_reset_reason_get(); + LOG_INF("Reset reason: 0x%x", reset_reason); +} + +static void sample_reboot(void) +{ + LOG_INF("Rebooting..."); + (void)lte_lc_power_off(); + LOG_PANIC(); + k_sleep(K_SECONDS(NORMAL_REBOOT_S)); + sys_reboot(SYS_REBOOT_COLD); +} + +/* Delayable work item for handling cloud readiness timeout. + * The work item is scheduled at a delay any time connection starts and is cancelled when the + * connection to nRF Cloud becomes ready to use (signalled by NRF_CLOUD_EVT_READY). + * + * If the work item executes, that means the nRF Cloud connection did not become ready for use + * within the delay, and thus the connection should be reset (and then try again later). + */ +static void ready_timeout_work_fn(struct k_work *work) +{ + ARG_UNUSED(work); + LOG_INF("nRF Cloud connection did not become ready in time, disconnecting and retrying..."); + disconnect_cloud(); +} + +static K_WORK_DELAYABLE_DEFINE(ready_timeout_work, ready_timeout_work_fn); + +static void clear_readiness_timeout(void) +{ + LOG_DBG("Stopping cloud connection readiness timeout"); + k_work_cancel_delayable(&ready_timeout_work); +} + +/* This function causes the cloud to disconnect, and updates internal state accordingly. + * + * It is also triggered by cloud disconnection, to update internal state. + * + * In this latter case, an unnecessary "Disconnecting from nRF Cloud" and + * "Already disconnected from nRF Cloud" will be printed. + * + * This is done to keep the sample simple, though the log messages may be a bit confusing. + */ +static void disconnect_cloud(void) +{ + /* Clear the readiness timeout in case it was running. */ + clear_readiness_timeout(); + + /* Clear the Ready and Connected events, no longer accurate. */ + k_event_clear(&connection_events, CLOUD_READY | CLOUD_CONNECTED); + LOG_DBG("Cleared CLOUD_READY and CLOUD_CONNECTED"); + + /* Clear the initial association flag, no longer accurate. */ + atomic_set(&initial_association, false); + + /* Disconnect from nRF Cloud -- Blocking call + * Will no-op and return -EACCES if already disconnected. + */ + LOG_INF("Disconnecting from nRF Cloud"); + int err; + + err = nrf_cloud_disconnect(); + + /* nrf_cloud_disconnect returns -EACCES if we are not currently in a connected state. */ + if ((err == -EACCES) || (err == -ENOTCONN)) { + LOG_DBG("Already disconnected from nRF Cloud"); + } else if (err) { + LOG_ERR("Cannot disconnect from nRF Cloud, error: %d. Continuing anyways", err); + } else { + LOG_INF("Successfully disconnected from nRF Cloud"); + } + + /* Fire the disconnected event. */ + k_event_post(&connection_events, CLOUD_DISCONNECTED); +} + +/* Wait for a connection result, and return true if connection was successful within the timeout, + * otherwise return false. + */ +static bool await_connection_result(k_timeout_t timeout) +{ + /* After a connection attempt, either CLOUD_CONNECTED or CLOUD_DISCONNECTED will be + * raised, depending on whether the connection succeeded. + */ + uint32_t events = CLOUD_CONNECTED | CLOUD_DISCONNECTED; + + return (k_event_wait(&connection_events, events, false, timeout) & CLOUD_CONNECTED) != 0; +} + +/* Start the readiness timeout if readiness is not already achieved. */ +static void start_readiness_timeout(void) +{ + /* It doesn't make sense to start the readiness timeout if we're already ready. */ + if (k_event_test(&connection_events, CLOUD_READY)) { + LOG_DBG("Already ready."); + return; + } + + LOG_DBG("Starting cloud connection readiness timeout for %d seconds", + CONFIG_CLOUD_READY_TIMEOUT_SECONDS); + + k_work_reschedule(&ready_timeout_work, K_SECONDS(CONFIG_CLOUD_READY_TIMEOUT_SECONDS)); +} + +/* Attempt to connect to nRF Cloud and update internal state accordingly. + * Blocks until connection attempt either succeeds or fails. + */ +static bool connect_cloud(void) +{ + int err; + + LOG_INF("Connecting to nRF Cloud"); + + /* Clear the disconnected flag, no longer accurate. */ + k_event_clear(&connection_events, CLOUD_DISCONNECTED); + + /* Connect to nRF Cloud -- Non-blocking. State updates are handled in callbacks. */ + err = nrf_cloud_connect(); + + /* If we were already connected, treat as a successful connection, but do nothing. */ + if (err == NRF_CLOUD_CONNECT_RES_ERR_ALREADY_CONNECTED) { + LOG_WRN("Already connected to nRF Cloud"); + return true; + } + + /* If the connection attempt fails immediately, report and exit. */ + if (err != 0) { + LOG_ERR("Could not connect to nRF Cloud, error: %d", err); + return false; + } + + /* Wait for the connection to either complete or fail. */ + if (!await_connection_result(K_FOREVER)) { + LOG_ERR("Could not connect to nRF Cloud"); + return false; + } + + /* If connection succeeded and we aren't already ready, start the readiness timeout. + * (Readiness check is performed by start_readiness_timeout). + */ + start_readiness_timeout(); + return true; +} + +/* Handler for events from nRF Cloud Lib. */ +static void cloud_event_handler(const struct nrf_cloud_evt *nrf_cloud_evt) +{ + int err; + + switch (nrf_cloud_evt->type) { + case NRF_CLOUD_EVT_TRANSPORT_CONNECTED: + LOG_DBG("NRF_CLOUD_EVT_TRANSPORT_CONNECTED"); + break; + case NRF_CLOUD_EVT_TRANSPORT_CONNECTING: + LOG_DBG("NRF_CLOUD_EVT_TRANSPORT_CONNECTING"); + break; + case NRF_CLOUD_EVT_TRANSPORT_CONNECT_ERROR: + LOG_DBG("NRF_CLOUD_EVT_TRANSPORT_CONNECT_ERROR: %d", nrf_cloud_evt->status); + /* Disconnect from cloud immediately rather than wait for retry timeout. */ + err = nrf_cloud_disconnect(); + if ((err == -EACCES) || (err == -ENOTCONN)) { + LOG_DBG("Already disconnected from nRF Cloud"); + } else if (err) { + LOG_ERR("Cannot disconnect from nRF Cloud, error: %d. Continuing anyways", + err); + } else { + LOG_INF("Successfully disconnected from nRF Cloud"); + } + break; + case NRF_CLOUD_EVT_USER_ASSOCIATION_REQUEST: + LOG_DBG("NRF_CLOUD_EVT_USER_ASSOCIATION_REQUEST"); + /* This event indicates that the user must associate the device with their + * nRF Cloud account in the nRF Cloud portal. + * + * The device must then disconnect and reconnect to nRF Cloud after association + * succeeds. + */ + LOG_INF("Please add this device to your nRF Cloud account in the portal."); + /* Store the fact that this is an initial association. + * + * This will cause the next NRF_CLOUD_EVT_USER_ASSOCIATED event to + * disconnect and reconnect the device to nRF Cloud, which is required + * when devices are first associated with an nRF Cloud account. + */ + atomic_set(&initial_association, true); + break; + case NRF_CLOUD_EVT_USER_ASSOCIATED: + LOG_DBG("NRF_CLOUD_EVT_USER_ASSOCIATED"); + /* Indicates successful association with an nRF Cloud account. + * Fired every time the device connects (unless the device is not associated). + * + * If this is an initial association, the device must disconnect and + * and reconnect before using nRF Cloud. + */ + device_deleted = false; + if (atomic_get(&initial_association)) { + /* Disconnect as is required. + * The connection loop will handle reconnection afterwards. + */ + LOG_INF("Device successfully associated with cloud! Reconnecting"); + reconnect_seconds = 1; + disconnect_cloud(); + } + break; + case NRF_CLOUD_EVT_READY: + LOG_DBG("NRF_CLOUD_EVT_READY"); + LOG_INF("Connection to nRF Cloud ready"); + /* Clear the readiness timeout, since we have become ready. */ + clear_readiness_timeout(); + + reconnect_seconds = CONFIG_CLOUD_CONNECTION_RETRY_TIMEOUT_SECONDS; + + k_event_post(&connection_events, CLOUD_READY); + break; + case NRF_CLOUD_EVT_SENSOR_DATA_ACK: + LOG_DBG("NRF_CLOUD_EVT_SENSOR_DATA_ACK"); + break; + case NRF_CLOUD_EVT_TRANSPORT_DISCONNECTED: + LOG_DBG("NRF_CLOUD_EVT_TRANSPORT_DISCONNECTED"); + /* The nRF Cloud library itself has disconnected for some reason. + * Disconnect from cloud immediately rather than wait for retry timeout. + */ + disconnect_cloud(); + + break; + case NRF_CLOUD_EVT_ERROR: + LOG_DBG("NRF_CLOUD_EVT_ERROR: %d", nrf_cloud_evt->status); + break; + case NRF_CLOUD_EVT_RX_DATA_DISCON: + LOG_DBG("NRF_CLOUD_EVT_RX_DATA_DISCON"); + LOG_INF("Device was removed from your account."); + device_deleted = true; + break; + case NRF_CLOUD_EVT_RX_DATA_SHADOW: { + LOG_DBG("NRF_CLOUD_EVT_RX_DATA_SHADOW"); + break; + } + case NRF_CLOUD_EVT_FOTA_START: + LOG_DBG("NRF_CLOUD_EVT_FOTA_START"); + break; + case NRF_CLOUD_EVT_FOTA_DONE: { + enum nrf_cloud_fota_type fota_type = NRF_CLOUD_FOTA_TYPE__INVALID; + + if (nrf_cloud_evt->data.ptr) { + fota_type = *((enum nrf_cloud_fota_type *) nrf_cloud_evt->data.ptr); + } + + LOG_DBG("NRF_CLOUD_EVT_FOTA_DONE, FOTA type: %s", + fota_type == NRF_CLOUD_FOTA_APPLICATION ? "Application" : + fota_type == NRF_CLOUD_FOTA_MODEM_DELTA ? "Modem (delta)" : + fota_type == NRF_CLOUD_FOTA_MODEM_FULL ? "Modem (full)" : + fota_type == NRF_CLOUD_FOTA_BOOTLOADER ? "Bootloader" : + "Invalid"); + + /* Proceed to reboot the device */ + sample_reboot(); + break; + } + case NRF_CLOUD_EVT_FOTA_ERROR: + LOG_DBG("NRF_CLOUD_EVT_FOTA_ERROR"); + break; + default: + LOG_DBG("Unknown event type: %d", nrf_cloud_evt->type); + break; + } +} + +static struct dfu_target_fmfu_fdev *get_full_modem_fota_fdev(void) +{ + if (IS_ENABLED(CONFIG_NRF_CLOUD_FOTA_FULL_MODEM_UPDATE)) { + static struct dfu_target_fmfu_fdev ext_flash_dev = { + .size = 0, + .offset = 0, + /* CONFIG_DFU_TARGET_FULL_MODEM_USE_EXT_PARTITION is enabled, + * so no need to specify the flash device here + */ + .dev = NULL + }; + + return &ext_flash_dev; + } + + return NULL; +} + +/* Callback to track network connectivity */ +static struct net_mgmt_event_callback l4_callback; +static void l4_event_handler(struct net_mgmt_event_callback *cb, uint64_t event, + struct net_if *iface) +{ + if ((event & EVENT_MASK) != event) { + return; + } + + if (event == NET_EVENT_L4_CONNECTED) { + /* Mark network as up. */ + dk_set_led(LTE_LED_NUM, 1); + LOG_INF("Connected to LTE"); + k_event_post(&connection_events, NETWORK_UP); + } + + if (event == NET_EVENT_L4_DISCONNECTED) { + /* Mark network as down. */ + dk_set_led(LTE_LED_NUM, 0); + LOG_INF("Network connectivity lost!"); + } +} + +static int setup(void) +{ + int err = 0; + + print_reset_reason(); + + err = dk_leds_init(); + if (err) { + LOG_ERR("LEDs init failed (err %d)", err); + return err; + } + + /* Set the LEDs off after all modules are ready */ + err = dk_set_leds(0); + if (err) { + LOG_ERR("Failed to set LEDs off"); + return err; + } + + /* Setup handler for Zephyr NET Connection Manager events. */ + net_mgmt_init_event_callback(&l4_callback, l4_event_handler, EVENT_MASK); + net_mgmt_add_event_callback(&l4_callback); + + /* Init modem */ + err = nrf_modem_lib_init(); + if (err) { + LOG_ERR("Failed to initialize modem library: 0x%X", err); + return -EFAULT; + } + + /* Ensure device has credentials installed before proceeding */ + await_credentials(); + + /* Get the device ID */ + err = nrf_cloud_client_id_get(device_id, sizeof(device_id)); + if (err) { + LOG_ERR("Failed to get device ID, error: %d", err); + return err; + } + + /* Enable the connection manager for all interfaces and allow them to connect. */ + conn_mgr_all_if_up(true); + + /* Initiate Connection */ + LOG_INF("Enabling connectivity..."); + conn_mgr_all_if_connect(true); + k_event_wait(&connection_events, NETWORK_UP, false, K_FOREVER); + + /* Initialize nrf_cloud library. */ + struct nrf_cloud_init_param params = { + .event_handler = cloud_event_handler, + .fmfu_dev_inf = get_full_modem_fota_fdev(), +#if CONFIG_NRF_CLOUD_FOTA_SMP + .smp_reset_cb = nrf52840_reset_api, +#endif + .application_version = APP_VERSION_STRING + }; + + err = nrf_cloud_init(¶ms); + if (err) { + LOG_ERR("nRF Cloud library could not be initialized, error: %d", err); + return err; + } + + /* Clear the disconnected flag, no longer accurate. */ + k_event_clear(&connection_events, CLOUD_DISCONNECTED); + + LOG_INF("Connecting to nRF Cloud..."); + err = nrf_cloud_connect(); + /* If we were already connected, treat as a successful connection, but do nothing. */ + if (err == NRF_CLOUD_CONNECT_RES_ERR_ALREADY_CONNECTED) { + LOG_WRN("Already connected to nRF Cloud"); + return 0; + } + + /* If the connection attempt fails immediately, report and exit. */ + if (err != 0) { + LOG_ERR("Could not connect to nRF Cloud, error: %d", err); + return err; + } + + /* Wait for the connection to either complete or fail. */ + if (!await_connection_result(K_FOREVER)) { + LOG_ERR("Could not connect to nRF Cloud"); + return -EIO; + } + + /* If connection succeeded and we aren't already ready, start the readiness timeout. + * (Readiness check is performed by start_readiness_timeout). + */ + start_readiness_timeout(); + + return 0; +} + +int main(void) +{ + int err = 0; + + LOG_INF(SAMPLE_SIGNON_FMT, APP_VERSION_STRING); + + err = setup(); + if (err) { + LOG_ERR("Setup failed, stopping."); + return 0; + } + + while (1) { + LOG_INF("Waiting for network ready..."); + k_event_wait(&connection_events, NETWORK_UP, false, K_FOREVER); + LOG_INF("Network is ready"); + + /* Attempt to connect to nRF Cloud. */ + if (connect_cloud()) { + LOG_DBG("Monitoring nRF Cloud connection"); + + /* and then wait patiently for a connection problem. */ + k_event_wait(&connection_events, CLOUD_DISCONNECTED, false, K_FOREVER); + + LOG_INF("Disconnected from nRF Cloud"); + } + + LOG_INF("Retrying in %d seconds...", reconnect_seconds); + + /* Wait a bit before trying again. */ + k_sleep(K_SECONDS(reconnect_seconds)); + } +} diff --git a/samples/cellular/nrf_cloud_mqtt_fota/src/smp_reset.c b/samples/cellular/nrf_cloud_mqtt_fota/src/smp_reset.c new file mode 100644 index 000000000000..710bacd77f09 --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/src/smp_reset.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ +#include +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(nrf_cloud_mqtt_fota, + CONFIG_NRF_CLOUD_MQTT_FOTA_SAMPLE_LOG_LEVEL); + +#define RESET_NODE DT_NODELABEL(nrf52840_reset) +#define HAS_RECOVERY_MODE (DT_NODE_HAS_STATUS(RESET_NODE, okay)) + +int nrf52840_reset_api(void) +{ + int err; + const struct gpio_dt_spec reset_pin_spec = GPIO_DT_SPEC_GET(RESET_NODE, gpios); + + if (!device_is_ready(reset_pin_spec.port)) { + LOG_ERR("Reset device not ready"); + return -EIO; + } + + /* Configure pin as output and initialize it to inactive state. */ + err = gpio_pin_configure_dt(&reset_pin_spec, GPIO_OUTPUT_INACTIVE); + if (err) { + LOG_ERR("Pin configure err: %d", err); + return err; + } + + /* Reset the nRF52840 and let it wait until the pin is inactive again + * before running to main to ensure that it won't send any data until + * the H4 device is setup and ready to receive. + */ + err = gpio_pin_set_dt(&reset_pin_spec, 1); + if (err) { + LOG_ERR("GPIO Pin set to 1 err: %d", err); + return err; + } + + /* Wait for the nRF52840 peripheral to stop sending data. + * + * It is critical (!) to wait here, so that all bytes + * on the lines are received and drained correctly. + */ + k_sleep(K_MSEC(10)); + + /* We are ready, let the nRF52840 run to main */ + err = gpio_pin_set_dt(&reset_pin_spec, 0); + if (err) { + LOG_ERR("GPIO Pin set to 0 err: %d", err); + return err; + } + + LOG_DBG("Reset Pin %d", reset_pin_spec.pin); + + return 0; +} diff --git a/samples/cellular/nrf_cloud_mqtt_fota/src/smp_reset.h b/samples/cellular/nrf_cloud_mqtt_fota/src/smp_reset.h new file mode 100644 index 000000000000..c02978b803bd --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/src/smp_reset.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef _SMP_RESET_H_ +#define _SMP_RESET_H_ + +/** + * Reset the nRF52840 for SMP FOTA. + */ +int nrf52840_reset_api(void); + +#endif /* _SMP_RESET_H_ */ diff --git a/samples/cellular/nrf_cloud_mqtt_fota/sysbuild.conf b/samples/cellular/nrf_cloud_mqtt_fota/sysbuild.conf new file mode 100644 index 000000000000..b04cca742ab9 --- /dev/null +++ b/samples/cellular/nrf_cloud_mqtt_fota/sysbuild.conf @@ -0,0 +1,8 @@ +# +# Copyright (c) 2025 Nordic Semiconductor +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +SB_CONFIG_BOOTLOADER_MCUBOOT=y +SB_CONFIG_SECURE_BOOT_APPCORE=y