Implementing a nRF52 DFU Bootloader over UART

Introduction

In our previous article, you have learnt about general concepts of Device Firmware Update (DFU) with nRF52. To add DFU capability to your nRF52 product, you need to develop a bootloader. The bootloader’s role is to receive the new firmware image, validate and activate it. In this post, you will learn how to build and test a DFU bootloader over serial link (UART), meaning the new firmware image will be sent from a host computer to the target nRF52 chip by using UART module.

To compile and run your code on real nRF52 hardware, it is recommended that you have a nRF52 development kit such as

Affiliate Disclosure: When you click on links in this section and make a purchase, this may result in this site earning a commission at no extra cost to you.

  • In this post, we will use PCA10040 development board
  • Software:
    • nRF5 SDK 17.1
    • gcc-arm-none-eabi-9-2020-q2-update
    • nRF Command Line Tools
    • nrfutil version 6.1.3

Installing some software components are described in our previous post. Here I will start by describing how to install nrfutil which is not covered in the previous article.

Install nrfutil

nrfutil is the critical component when working with Nordic DFU as it is used to generate keys, update package and also used to transfer update package to the target chip via UART. Unfortunately, installing nrfutil might not be straight-forward for some systems. If you run into trouble when installing nrfutil, try googling your specific error. This is probably the most time-consuming step if something does not go well. In some cases, you might need to try installing from source code.

If you are using MacOS, nrfutil can be installed by opening a terminal and type

python3 -m pip install nrfutil

If you get an error like this

you will need to update pip first by running

python3 -m pip install --upgrade pip

After upgrading pip, run the above command to install nrfutil again. After nrfutil is installed, you will see a warning message in the terminal saying the installed directory is not on PATH, which you will need to add. Depending on your shell terminal, you might need to modify .zprofile in order to add nrfutil to path variable.

Once installed correctly, you can check nrfutil by running

nrfutil version

Generate keys using nrfutil

After nrfutil is installed, you will use it to generate public and private key pair to encrypt and validate the firmware image’s signature. To generate keys, type the following command in a terminal

nrfutil keys --help

You will see instructions in the terminal about what are the parameters. To generate a private key, use the command

nrfutil keys generate private.pem

To generate the public key corresponding to the private key, use the following command

nrfutil keys display --key pk private.pem --format code --out_file dfu_public_key.c

We will need to use dfu_public_key.c in the next step.

Compile micro-ecc

We will use micro-ecc in our project to decrypt the new firmware image’s signature to verify if the firmware is coming from a trusted source. Open a terminal and go to micro-ecc folder in nRF5 SDK.

cd ~/nrf52projects/nRF5_SDK_17.1.0_ddde560/external/micro-ecc

Run the following command to remove unwanted character in build_all.sh

sed -i -e 's/\r$/' build_all.sh

Then run build_all.sh script.

./build_all.sh

This will compile the library and add micro_ecc_lib_nrf52.a which is required in the next step.

Create a new bootloader project

In this step, you need to create a new project based on secure bootloader example in nRF5 SDK 17.1. Create a new folder at ~/nrf52projects/bl_uart_debug, then open the folder nRF5_SDK_17.1.0_ddde560/examples/dfu/secure_bootloader/, you will see the following files:

nRF5_SDK_17.1.0_ddde560/examples/dfu/secure_bootloader/
|---main.c
|---nrf_crypto_allocator.h
|---pca10040_uart_debug
    |---armgcc
        |---Makefile
        |---secure_bootloader_gcc_nrf52.ld
    |---config
        |---sdk_config.h

We will build a project based on the pca10040_uart_debug example. This is a project example for PCA10040 board. If you are using custom board, you can also use this example as a starting point and modify the UART TX and RX pin accordingly. _debug means this project is configured for printing out log messages using NRF_LOG module. Check out our previous article about NRF_LOG if you are unfamiliar with it. Since the NRF_LOG module is enabled, its size is larger than the non debug version pca10040_uart (104 kB vs 24 kB). You can see the difference in the linker scripts. The debug version has bootloader start address at 0x64000, while the non debug version uses bootloader start address at 0x78000. Our aim is to understand how the bootloader works, so we use the debug version. In production, you should use the non debug version and disable all logging feature to optimise the size of the bootloader.

Now do the following steps:

  • Copy main.c, nrf_crypto_allocator.h, Makefile, secure_bootloader_gcc_nrf52.ld and sdk_config.h to our project folder ~/nrf52projects/bl_uart_debug.
  • Copy dfu_public_key.c and private.pem that you generated previously to ~/nrf52projects/bl_uart_debug/keys.

Open Makefile and make the following modifications

  • Change SDK_ROOT variable to point to your installation folder of nRF5 SDK.
  • In the SRC_FILES list, change $(PROJ_DIR)/../dfu_public_key.c \ to $(PROJ_DIR)/keys/dfu_public_key.c \
  • Delete ../config \ in the INC_FOLDERS list.

Now run make to compile the bootloader project. You should see successful messages printed on the console.

Flash the bootloader

Up to this point, you have compiled the bootloader successfully. The next step would be flashing the bootloader to the PCA10040 board. You will need to open a terminal and type the following commands

make erase
make flash_mbr
make flash

These commands will perform 3 operations:

  • Erase everything in the internal flash memory
  • Flash the master boot record which is required when a bootloader is used and no softdevice is present.
  • Flash the bootloader hex.

Testing the bootloader and DFU

Now your bootloader has been flashed successfully, we will test if the bootloader is able to perform the DFU functionality over UART. Because we are using the debug version of bootloader example, NRF_LOG module is enabled (see our previous post about enabling NRF_LOG module here) and you will see log messages printed on RTT client. Open two terminals and type the following commands

JLinkExe -if SWD -device nrf52 -speed 4000 -autoconnect 1
JLinkRTTClient

You will see the following log messages on RTT Client:

From these debug messages, you can infer how the bootloader works and you can trace back to each of the functions in the source code. For instance, you can see that on startup, it calls nrf_bootloader_init() and will check bootloader settings parameters to see if there is an application that needs to be activated, then check if there is a valid app to boot. If it detects no valid application, it will stay in bootloader mode waiting for a new application image.

In the next steps, we will:

  • Use nrfutil to generate a new firmware image to do DFU
  • Use nrfutil to transfer the firmware package to target chip via UART

Generate a new firmware image

In order to test the bootloader, we need a new firmware image. We will generate a firmware image in zip format using nrfutil tool. We will use the blinky example in the nRF5 SDK examples nRF5_SDK_17.1.0_ddde560/examples/peripheral/blinky.

Open blinky_gcc_nrf52.ld and change the FLASH start address to 0x1000. Your memory map would look like this

MEMORY
{
  FLASH (rx) : ORIGIN = 0x1000, LENGTH = 0x80000
  RAM (rwx) :  ORIGIN = 0x20000000, LENGTH = 0x10000
}

The original hex file won’t work because the application start address is placed at 0x0. Since we are using a bootloader, we need a master boot record and it occupies the first 4 kB at the bottom of the flash address, as explained in our previous article. We need to move the application’s start address to above the MBR. If you are using a softdevice, the application will need to stay further up above the softdevice region.

Now compile the project by running make and copy nrf52832_xxaa.hex in the _build folder to ~/nrf52projects/bl_uart_debug/hex and rename it as blinky.hex. Our project folder will look like this

~/nrf52projects/bl_uart_debug
|---hex
    |---blinky.hex
|---keys
    |---dfu_public_key.c
    |---private.pem
|---main.c
|---Makefile
|---nrf_crypto_allocator.h
|---sdk_config.h
|---secure_bootloader_gcc_nrf52.ld

To generate a firmware update package using nrfutil, use the following command

nrfutil pkg generate --hw-version 52 --sd-req 0x00 --application ./hex/blinky.hex --application-version 1 --key-file ./keys/private.pem dfu_uart_package.zip

dfu_uart_package.zip is the update package ready to send to the target nRF52 chip.

Transfer the update package over UART using nrfutil

To transfer the firmware package to the nRF52 chip, use the following command

nrfutil dfu serial -pkg dfu_uart_package.zip -p [PORT] -b 115200 -fc 1

where [PORT] is the serial port and can be determined by ls /dev/tty*. If everything is working fine, you will see the message indicating the transfer is successful.

  [####################################]  100%
Device programmed.

This means the firmware has been transferred to the nRF52. Upon receiving this firmware package, the bootloader will do a couple of things:

  • Verify the image by calculating the hash of the image using public key and compare it with the hash it receives in the init packet.
  • If verification is successful, it will reboot the bootloader.
  • Once the bootloader reboots, it will check if there is a pending application to be activated. It will activate the new application and boot it up.
  • One the application boots up, you should see the LEDs blink sequentially as expected.
  • To enter the DFU mode again to update new firmware, restart the board and hold the button 4.

Wrapping Up

Phew! That’s quite a bit of steps to follow to build and test DFU over UART. If you have come to here, conguratulations. You have successfully built a serial DFU bootloader based on the secure bootloader example in the nRF5 SDK. In the next article, we will learn about how to build a DFU bootloader over BLE link. Thanks for reading.

1 thought on “Implementing a nRF52 DFU Bootloader over UART”

  1. Thank you for the most comprehensive guide towards the nrf52 uart bootloader. Following your steps, I could successfully use the debug version of the bootloader. Then I switched to the non-debug version bootloader, and after the uart transfer, the app did not start as the LEDs were not blinking(LEDs were all off). Since it is not the debug version, without the debug message, I could not figure out what went wrong. Any idea would be appreciated.

Leave a Comment