This guide will show you, step by step, how to add Device Firmware Update (DFU) support to your application using the nRF Connect SDK. First it will demonstrate how to add BLE DFU support to the Peripheral LBS sample and how to perform a DFU using the nRF Connect Device Manager app. The next section will show you how to add Serial DFU support to the Hello World sample and how to perform a DFU from a computer using mcumgr. The code snippets used in this guide are inspired by the SMP Server sample.
Check out the source code for the mcumgr management library for more information about the library and the underlying protocols. It contains a README explaining mcumgr and a README explaining the Simple Management Protocol (SMP).
Before starting, make sure you have installed the nRF Connect SDK and are familiar with nRF Connect for VS code. Check out the nRF Connect for VS Code series on YouTube for help with this. The west command is used to build and flash the samples, but if you're not familiar with the command line, you can build and flash the sample using nRF Connect for VS Code instead.
The samples in this guide are tested with nRF Connect SDK v1.8.0-rc1 and the nRF52840 DK.
Table of Contents
DFU over Bluetooth
Modify the Peripheral LBS sample
1. In nRF Connect for VS Code, create a new application from sample, like explained in nRF Connect for VS Code, 2: Create an application. Choose the peripheral_lbs
sample and give it the name ble_dfu_peripheral_lbs
.
In the above image, Linux is used. If you're using Windows, make sure the nRF Connect Toolchain points to the folder <ncs location>/<ncs version>/toolchain
2. Add the following lines to the prj.conf
file
2a. If you are using NCS v2.5.0 or newer, adding FOTA update to your application is simplified. You only need to add these into prj.conf#Enable MCUBOOT bootloader build in the application
CONFIG_BOOTLOADER_MCUBOOT=y
#Include MCUMGR and the dependencies in the build
CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU=y
You can jump straight to Testing section
You can read more about CONFIG_NCS_SAMPLE_MCUMGR_BT_OTA_DFU here. The configuration basically add all you need for DFU over BLE, including adding FOTA service into your Bluetooth attribute table.
2b. If you are using NCS v2.4.x or newer: add the following lines to the prj.conf
file, found in ble_dfu_peripheral_lbs/prj.conf
# Enable mcumgr.
CONFIG_MCUMGR=y
# Enable most core commands.
CONFIG_MCUMGR_CMD_IMG_MGMT=y
CONFIG_MCUMGR_CMD_OS_MGMT=y
# Ensure an MCUboot-compatible binary is generated.
CONFIG_BOOTLOADER_MCUBOOT=y
- MCUMGR: enables the mcumgr management library
- MCUMGR_CMD_IMG_MGMT: enables the command handlers for image management
- MCU_MGR_CMD_OS_MGMT: enables the command handlers for OS management.
- BOOTLOADER_MCUBOOT: includes MCUboot as a child image.
The image management command handlers makes it possible to upload, list, test and confirm the image(s), while the OS management command handlers makes it possible to reset the chip (and run the echo command). Since the DFU support is added to the application, the chip needs to be reset, to give control to MCUboot, which will validate the new uploaded image and swap it with the old image.
3. Add the following lines to the prj.conf
file as well
# Allow for large Bluetooth data packets.
CONFIG_BT_L2CAP_TX_MTU=252
CONFIG_BT_BUF_ACL_RX_SIZE=256
# Enable the Bluetooth (unauthenticated) and shell mcumgr transports.
CONFIG_MCUMGR_SMP_BT=y
CONFIG_MCUMGR_SMP_BT_AUTHEN=n
# Some command handlers require a large stack.
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
- BT_L2CAP_TX_MTU: increase the L2CAP MTU
- BT_BUF_ACL_RX_SIZE: increase the ACL size
- MCUMGR_SMP_BT: enable the Bluetooth mcumgr SMP transport
- MCUMGR_SMP_BT_AUTHEN: disable the encrypted and authenticated connection requirements to send messages over the BLE SMP transport layer
- SYSTEM_WORKQUEUE_STACK_SIZE: increase the work queue stack size to avoid stack overflow
4. Add the lines below to the top of the main.c
file, found in ble_dfu_peripheral_lbs/src/main.c
4a. If you use nRF Connect SDK v2.3 or newer:
#include <zephyr/mgmt/mcumgr/transport/smp_bt.h>
4b. If you use nRF Connect SDK v2.0 to v2.2:
#include <zephyr/mgmt/mcumgr/smp_bt.h>
#include "os_mgmt/os_mgmt.h"
#include "img_mgmt/img_mgmt.h"
4c. If you use nRF Connect SDK v1.9 or earlier
#include <mgmt/mcumgr/smp_bt.h>
#include "os_mgmt/os_mgmt.h"
#include "img_mgmt/img_mgmt.h"
5. Remove the LBS Service UUID from the scan response packet, and replace it with the SMP service UUID:
BT_DATA_BYTES(BT_DATA_UUID128_ALL,
0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86,
0xd3, 0x4c, 0xb7, 0x1d, 0x1d, 0xdc, 0x53, 0x8d),
This is to make the advertising visible for the nRF Connect Device Manager app. The app will filter out all the devices that don't advertise with the SMP service UUID.
6. Lastly, add these lines right before bt_conn_cb_register
6a. If you use nRF Connect SDK v2.3 or newer:
printk("build time: " __DATE__ " " __TIME__ "\n"); smp_bt_register();
6b. If you use nRF Connect SDK v2.2 or earlier:
printk("build time: " __DATE__ " " __TIME__ "\n"); os_mgmt_register_group(); img_mgmt_register_group(); smp_bt_register();
Here, we register the OS and Image group and the SMP Bluetooth service. A printk()
with the build date and time is added to make it easier to confirm that an update was successful.
If you've followed the above steps correctly, the sample should look like this: https://github.com/simon-iversen/sdk-nrf/tree/ble_dfu_peripheral_lbs/samples/bluetooth/peripheral_lbs
Testing
1. Connect your board to the computer, then build ble_dfu_peripheral_lbs
and flash it to the board
west build -b nrf52840dk_nrf52840 -p && west flash --erase
2. Open a serial terminal, reset the chip and check the date and time that is output.
For example:
...
*** Booting Zephyr OS build v2.7.0-ncs1-rc1 ***
Starting Bluetooth Peripheral LBS example
build time: Nov 30 2021 17:14:25
...
3. Build the sample again, to create the file used for the DFU update
west build -b nrf52840dk_nrf52840 -p
Due to the printk statement with the build date and time, a new and different binary will be created
- Transfer the generated file peripheral_lbs/build/zephyr/app_update.bin to your phone
- Install the nRF Connect Device Manager app on your phone
- If you open the app, the advertisement from the peripheral LBS sample should be found. Click on it
4. Next, click on the download button, then SELECT FILE and find the app_update.bin
file you just transferred to the phone
5. Click on START, then select Test and Confirm and start the firmware update
The app will then transfer the image binary, and UPLOAD COMPLETE should be displayed when the whole binary is uploaded
The nRF Connect Device Manager app will reset the chip after completing the transfer. If the date and time output on the terminal is different from the one seen earlier, it means a different image is running, and the update was successful
For example:
...
*** Booting Zephyr OS build v2.7.0-ncs1-rc1 ***
Starting Bluetooth Peripheral LBS example
build time: Nov 30 2021 17:18:53
...
The new image will get confirmed from the app, so it should not revert back on the next reset.
Serial DFU
Modify the Hello World sample
1. In nRF Connect for VS Code, create a new application from sample, like explained in nRF Connect for VS Code, 2: Create an application. Choose the hello_world
sample and give it the name serial_dfu_hello_world
.
In the above image, Linux is used. If you're using Windows, make sure the nRF Connect Toolchain points to the folder <ncs location>/<ncs version>/toolchain
2. Add the following lines to the prj.conf
file
2a. If you are using NCS v2.5.0 or newer, add the following configurations to prj.conf:
# Enable MCUBoot.
CONFIG_BOOTLOADER_MCUBOOT=y
# Enable flash operations.
CONFIG_FLASH=y
# Enable MCUmgr and dependencies.
CONFIG_MCUMGR=y
CONFIG_NET_BUF=y
CONFIG_ZCBOR=y
CONFIG_CRC=y
CONFIG_STREAM_FLASH=y
CONFIG_FLASH_MAP=y
# Enable most core commands.
CONFIG_IMG_MANAGER=y
CONFIG_MCUMGR_GRP_IMG=y
CONFIG_MCUMGR_GRP_OS=y
# Enable the serial mcumgr transport.
CONFIG_MCUMGR_TRANSPORT_UART=y
CONFIG_BASE64=y
CONFIG_CONSOLE=y
# Disable UART Console and enable the RTT console
CONFIG_UART_CONSOLE=n
CONFIG_RTT_CONSOLE=y
CONFIG_USE_SEGGER_RTT=y
# Some command handlers require a large stack.
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
That's all you need in your application if you are using NCS v2.5.0 and newer to support UART DFU, you now can jump to Testing section
2.b If you are using NCS earlier than v2.5.0
Add the following lines to the prj.conf
file, found in serial_dfu_hello_world/prj.conf.
# Enable mcumgr.
CONFIG_MCUMGR=y
# Enable most core commands.
CONFIG_MCUMGR_CMD_IMG_MGMT=y
CONFIG_MCUMGR_CMD_OS_MGMT=y
# Ensure an MCUboot-compatible binary is generated.
CONFIG_BOOTLOADER_MCUBOOT=y
Check out step 2 in Modify the Peripheral LBS sample for an explanation of these configs.
3. Add the following lines to the prj.conf
file as well.
# Enable the serial mcumgr transport. CONFIG_MCUMGR_SMP_UART=y # Disable UART Console and enable the RTT console CONFIG_UART_CONSOLE=n CONFIG_RTT_CONSOLE=y CONFIG_USE_SEGGER_RTT=y # Some command handlers require a large stack. CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
- MCUMGR_SMP_UART: enabling the serial mcumgr SMP transport
Next, we use RTT for logging instead of UART, so that it doesn't interfere with the transfer (see Nordic nRF5x Segger J-Link -> RTT Console). Another option is to use the configs from smp_svr/overlay-serial-console.config to enable the UART console with mcumgr pass through.
4. Add the lines below to the top of the main.c
file
4a. If you use nRF Connect SDK v.2.2 or earlier:
#include "os_mgmt/os_mgmt.h" #include "img_mgmt/img_mgmt.h"
4b. If you use nRF Connect SDK v.2.3 or newer, no lines are required.
5. Add the lines shown below to the start of the main()
function.
5a. If you use nRF Connect SDK v2.3 or newer:
printk("build time: " __DATE__ " " __TIME__ "\n");
5b. If you use nRF Connect SDK v2.2 or earlier:
printk("build time: " __DATE__ " " __TIME__ "\n"); os_mgmt_register_group(); img_mgmt_register_group();
We don't need to run any functions related to the SMP serial transport. This is because smp_uart_init() will run before main()
and set up an UART mcumgr ISR that handles the incoming mcumgr messages.
If you've followed the above steps correctly, the sample should look like this: https://github.com/simon-iversen/sdk-zephyr/tree/serial_dfu_hello_world/samples/hello_world
Testing
Before testing the sample, make sure mass storage is disabled. If not, the upload command might stall at 0%
1. Follow the steps in Disabling the Mass Storage Device functionality to disable mass storage
2. Build serial_dfu_hello_world and flash it to your board
west build -b nrf52840dk_nrf52840 -p && west flash --erase
3. Disconnect and connect the micro USB from the computer.
Use the USB connector connected to the Interface MCU, not the USB connector connected to the USB peripheral. This is because UART 0 is used for mcumgr UART, which is by default connected to the Interface MCU for the nRF52840 DK in nRF Connect SDK.
4. Install and open a software that can display the RTT logs. You can check out Nordic nRF5x Segger J-Link --> RTT Console, or use the integrated RTT terminal that comes with nRF Connect for VS Code
5. When you've gotten the RTT logs working, check the date and time that is output
For example:
*** Booting Zephyr OS build v2.7.0-ncs1-rc1 ***
Hello World! nrf52840dk_nrf52840
build time: Nov 30 2021 17:39:13
6. Build the sample again to create the file used for the DFU update
west build -b nrf52840dk_nrf52840 -p
7. Install mcumgr by following the instructions in the section MCUmgr -> Command-line Tool, and add it to the path
8. Get the development kit’s serial port name (for example, "/dev/ttyACM0"
), by running nrfjprog --com
../home$ nrfjprog --com
683416774 /dev/ttyACM0 VCOM0
9. Open the command line and run the following command
mcumgr --conntype serial --connstring "/dev/ttyACM0,baud=115200" echo hello
The string "hello" should be returned, if not the communication is not working properly
10. Open a terminal in ../zephyr/samples/hello_world/build/zephyr (where the signed binary app_update.bin is located) and type in the following command to start the upload
mcumgr --conntype serial --connstring "/dev/ttyACM0,baud=115200" image upload -e app_update.bin
11. When the transfer has been completed run the following command:
mcumgr --conntype serial --connstring "/dev/ttyACM0,baud=115200" image list
This will list all the images located on the chip, and if the upload has been successful, you should see an image at slot 1, similar to the one below:
Images:
image=0 slot=0
version: 0.0.0
bootable: true
flags: active confirmed
hash: f8ebf4778aa17de10c24ea1a8fd7b3877672a934448d5193b89a0a60c37699cd
image=0 slot=1
version: 0.0.0
bootable: true flags:
hash: 31cc459b1ec1ac8e19f092caa860db8e377a7ab66234012bdb6a1dd522807269
Split status: N/A (0)
12. In order for mcuboot to swap the images on reset, the flag of the image in slot-1 has to be "pending". This can be done by running the mcumgr test command, with the hash of slot-1 image. For example:
mcumgr --conntype serial --connstring "/dev/ttyACM0,baud=115200" image test 31cc459b1ec1ac8e19f092caa860db8e377a7ab66234012bdb6a1dd522807269
The image in slot-1 should now be in a "pending" state
13. For the swap to start, the chip needs to reset. This can be done remotely through mcumgr:
mcumgr --conntype serial --connstring "/dev/ttyACM0,baud=115200" reset
14. Check the log output, and if the date and time output is different from the one seen earlier, the update was successful
For example:
*** Booting Zephyr OS build v2.7.0-ncs1-rc1 ***
Hello World! nrf52840dk_nrf52840
build time: Nov 30 2021 17:42:56
- To make the swap permanent, run the confirm command
mcumgr --conntype serial --connstring "/dev/ttyACM0,baud=115200" image confirm
Other DFU samples
Serial DFU using external flash
This sample is similar to the one in Serial DFU, just that the secondary slot is located at the nRF52840 DK's onboard flash instead of the internal flash
HTTP DFU using external flash
This sample will download an app_update.bin file from a remote server and write it to the external flash. The nRF9160 DK needs to be used
Bluetooth DFU from chip
With this sample you will be able to perform a DFU from one nRF52840DK to another nRF52840DK
https://github.com/simon-iversen/ncs_samples/tree/master/central_smp_client_dfu
Serial DFU of app core and net core on the nRF5340
This sample demonstrates how to perform DFU on the nRF5340, specifically app core updates (main application and mcubot) and network core updates
https://github.com/simon-iversen/ncs_samples/tree/master/update_mcuboot_app_and_netcore
Top Comments