OTA Options for NCP-Based Designs

Introduction

This article lays out the different ways of doing device firmware updates (DFU), especially over-the-air updates (OTA), when using NCP Mode in your design. We will use the NCP-target-empty example project as the starting point. Normally the NCP empty example just acts as a bridge between the NCP host and the BLE stack running inside an EFR32 device. In these cases, OTA logic has been included in the NCP-target-empty firmware.

Adding a Bootloader to Example Projects

When starting with software examples in Simplicity Studio, the project will not contain a bootloader by default. To get the bootloader, either flash an NCP target - Empty demo or create and flash a Gecko Bootloader example project for your part.

Creating Upgrade Images of your Project

SDK examples provide scripts, which generate .gbl-files, create_bl_files.bat/.sh for Windows and Linux/Mac respectively. The images (application.gbl, full.gbl and possible secure variants) are created into output_gbl folder.

OTA Client

For methods other than the UART DFU, we will use the Blue Gecko mobile app as the OTA client to perform the upgrade. A guide to using the app can be found here: Using Blue Gecko Mobile App for OTA DFU.

Firmware Update Methods

UART DFU

This is probably the most used firmware update method in NCP-mode. A Gecko Bootloader image (.GBL file) containing the new firmware is written to target device using UART and BGAPI binary protocol. The target device needs to be programmed with the Gecko Bootloader as BGAPI UART DFU Bootloader.

The default configuration, suitable for testing with Wireless Starter Kit, is as follows: |UART Option|Default| |:---|:---| |USART|USART0| |Baud rate|115200| |TX pin|PA0| |RX pin|PA1| |HW flow control|OFF| |UART enable port|PA5 (VCOM_ENABLE)| |||

The UART DFU process follows these basic steps:

  1. Boot the target device into DFU mode (dfu_reset(1)).
  2. Wait for DFU boot event.
  3. Send command DFU Flash Set Address to start the upgrade (set as zero, offsets are calculated automatically).
  4. Send the whole Gecko Bootloader image (.GBL) using DFU flash upload.
  5. After sending image, the host sends command DFU upload finish.
  6. Finally, the host resets target to normal mode dfu_reset(0).

An NCP host example is provided with the SDK at <StudioPath>\<version X>\developer\sdks\gecko_sdk_suite\<version>\app\bluetooth\examples_ncp_host\uart_dfu. For more information about compiling and developing host examples, see AN1042. The host example compiles to an executable, which takes the COM port number, baud rate and full.gbl file as arguments.

Testing UART DFU

  1. Create a new NCP-empty-target project for your part. Here a BGM13P22 module is used.

  2. Create a new BGAPI UART DFU Bootloader project for your part.

  3. Flash the <projectname>-combined.s37 file of the bootloader project to your part first.

  4. Build and flash the default NCP-empty-target project to your part. At this point, you should be able to connect to your part with e.g. BGTool to verify that it's running. You should not see any advertisements by default.

  5. Next, we'll add functionality to the NCP-target project, by including the following code in function local_handle_event in main.c:

    // In the switch-statement
     case gecko_evt_system_boot_id:
       {
         // Start advertising automatically at boot (just to make testing easier)
         gecko_cmd_le_gap_start_advertising(0, le_gap_general_discoverable, le_gap_connectable_scannable);
       }
       break;

    This will make the device start advertising with name "BLE Device" by default.

  6. Build the project again and run the create_bl_files script.

  7. Navigate to the uart-dfu host example directory mentioned above and compile the host project with e.g. mingw32-make.

  8. Copy the full.gbl image to the \exe directory, which now also contains the executable program.

  9. Run the program from using a command like utility.

    uart-dfu.exe <your port> 115200 full.gbl

  10. After the upgrade is finished, you should see "BLE Device" from, for example, Blue Gecko mobile app Bluetooth Browser. -Success!

AppLoader

The Apploader is a simple application with a minimal Bluetooth stack, that handles the upload process. The Apploader uses the API of the Gecko Bootloader to decode the GBL files and to store the application image in the flash.

For NCP projects, AppLoader should be linked in the project C/C++ Build->Settings->GNU ARM C Linker->Miscellaneous->$ and copied there from the $/protocol/bluetooth/lib/<partname>/GCC/ SDK directory. The Silicon Labs OTA service should also be added to your GATT either via the GATT Configurator or gatt.xml file.

The standard OTA DFU sequence implemented in the AppLoader goes as follows:

  1. App is running.
  2. Device is reset into DFU mode. In this mode the bootloader will start the Apploader instead of the Application.
  3. The Apploader advertises and waits for a Bluetooth connection.
  4. The AppLoader waits for the OTA start command (ota_control characteristic).
  5. The Apploader starts receiving the GBL file (using ota_data characteristic).
  6. The headers of the GBL file are stored in Slot 0 to be parsed.

  7. After parsing the headers, the rest of the GBL file is decoded on-the-fly, and the application is copied right to the application area, overwriting the old application.

  8. The Apploader receives the OTA end command and restarts the device in normal mode.

To implement this and test it, you need to capture the write request to ota_control in local event-handling loop to get to step 2. Connection closed event needs to be handled as well.

/* Check if the user-type OTA Control Characteristic was written.
* If ota_control was written, boot the device into Device Firmware Upgrade (DFU) mode. */
// static uint8_t boot_to_dfu = 0; defined at the top
case gecko_evt_gatt_server_user_write_request_id:

  if(evt->data.evt_gatt_server_user_write_request.characteristic == gattdb_ota_control) {
      /* Set flag to enter to OTA mode */
      boot_to_dfu = 1;
      /* Send response to Write Request */
    gecko_cmd_gatt_server_send_user_write_response(evt->data.evt_gatt_server_user_write_request.connection,
                                              gattdb_ota_control,
                                              bg_err_success);
    /* Close connection to enter to DFU OTA mode */
    gecko_cmd_le_connection_close(evt->data.evt_gatt_server_user_write_request.connection);
  }
  break;

  /*-----------------------------------------------*/
  // In connection_closed event to enter OTA mode
  if (boot_to_dfu) {gecko_cmd_system_reset(2);}

This is implemented in the attached Apploader_OTA_main.c file. The procedure is also described in AN1086 section 3.6.

For testing, flash the modified NCP-empty project to your kit, make another example project for the same part, e.g. SoC-Thermometer and generate the GBL files with the script as described in the beginning sections of this article. Now you should be able to perform OTA with Blue Gecko app and see that the new example is running.

Application-level OTA

In addition to the basic UART and OTA DFU solutions discussed above, it is possible to implement the firmware update functionality completely in the user application.

The main difference here compared to AppLoader based OTA above is that there is no separate DFU mode and step 2 in the above list is not needed at all. The GBL file is uploaded to the target device under the control of the user application. In this example, the file upload is done using the Silicon Labs proprietary OTA service (as specified in AN1086), but it is possible to use a customer specific service and protocol to handle the file upload.

Some of the benefits of this approach are:

* Sleep and BLE encryption/bonding are not supported by AppLoader to reduce the flash footprint.

An obvious disadvantage of this solution is the requirement to have dedicated flash space available to be used as the download area.

To use this approach, your device needs to be programmed with a suitable Gecko bootloader. When creating the bootloader project select Internal Storage Bootloader (single image on 512kB/1MB device), depending on the size of internal flash in your device.

Gecko bootloader has a key part in the OTA update. After GBL file is uploaded, it is Gecko bootloader that takes care of parsing the GBL file and installing the new firmware. The bootloader is also needed during the OTA file upload.

Gecko bootloader has an application interface exposed through a function table in the bootloader. This means that the user application can call functions implemented in the bootloader, even though the application is running in normal mode. In other words, the bootloader API functions are called from the user application context, but the implementation is in the Gecko bootloader project. As a practical example, the user application can call function bootloader_writeStorage to write data into the download area, without even knowing where the download area is physically located.

A list of the key bootloader API calls needed to implement OTA update is found in AN1086, Chapter 4. The commands are also shown below in short.

// Bring in needed bootloader functions
#include "btl_interface.h"
#include "btl_interface_storage.h"

// Erase download slot before update started
bootloader_eraseStorageSlot(0);
// Write 0 to ota_control characteristic -> init
bootloader_init();'
// Write received bytes to download area
bootloader_writeStorage(...);
// When 3 is written to ota_control, the update ends and you verify the image
bootloader_verifyImage(0, NULL);
//  Specify slot of new image
bootloader_setImageToBootload(0);
// Reboot and perform update
bootloader_rebootAndInstall();

Implementing and Testing Application-level OTA

Changes made in the NCP-target-empty example:

In GATT Configurator (.isc):

New files and include paths that need to be added into the project (versus having just binapploader.o linked):

Changes in main.c: The NCP empty example includes a skeleton for adding customized event handling code, it is the function local_handle_event. This function is called whenever an event is caught in the main loop. The default implementation is empty, it is merely a placeholder for user code.

All the changes in main.c are in the local_handle_event(), with some additional includes and definitions added just before this function definition.

To support OTA in the NCP firmware, the following event handlers are implemented:

See attached example in file app_level_OTA_main.c.

How It Works

The implementation follows the same idea that is described in application note AN1086. The boot event handler initializes GPIOs and checks the state of button PB0. If button is pressed at the time of reboot, then the download area is erased by calling bootloader_eraseStorageSlot(0);. The boot event also sets the device name dynamically and starts advertising automatically so that OTA can be tested without any NCP host at all.

Soft timer is only used for LED blinking. A soft timer is started if the download area is erased. The soft timer handler will then simply toggle the LED pin state.

The user write request event handler is where most of the OTA work is done. Note that the type of ota_data characteristic was changed to type user in GATT configurator. Writes to ota_control characteristic will start and terminate the OTA update. Only control values 0 and 3 need to be handled, no other control values are supported by this example.

When OTA image data is received (write to ota_data), the event handler writes the data directly to the internal flash memory using Gecko bootloader API (bootloader_writeStorage).

When client writes the value 3 to ota_control indicating that all the data has been sent, the application verifies the received GBL file by calling bootloader_verifyImage. GBL file includes checksum which makes it possible to check if the file was corrupted.

After successful GBL image upload, the disconnect event triggers the actual firmware update. If the verification was successful, the new GBL image is installed by calling:

If the image verification failed, the application does not try to install or reboot. Instead, it restarts advertising and includes the error code from the verify function in the device name.

To test the verify feature, you can try OTA update with a corrupted file. The file truncated.gbl is a GBL file from the project that has been intentionally modified so that it does not pass the check (some data has been cut from the end of the file). If you try OTA update with this file, the expected result is that the application will not change and it will restart advertising with name “err XXXX”. You can then erase the download area and try again with a valid GBL file.

How to Test It

Preparations:

First, verify the device starts advertising with name “NCP APP 1”. You can also use BGTool to test that the device is responding to BGAPI commands.

To test OTA, you need to manually erase the download area. To do this, keep PB0 pressed and reset the kit. LED1 turns on and after a short delay it starts blinking. The delay is because of the flash erase (~256kB area) which takes time to complete.

The device should now advertise with name “NCP APP 1*”.

Now you can run OTA using the Blue Gecko app. OTA is performed using one single file (fullv1.gbl). In the OTA options, use the partial update which asks you to specify only one GBL file. Select file fullv2.gbl to change the application from version 1 to version 2.

After the OTA upload is complete, you press END button on the mobile app. At this point, the connection is closed and the NCP firmware completes the firmware update. Gecko bootloader is used to install the new image from the download slot.

Installation of the new image takes about 8 seconds and then the device reboots with new firmware installed. At this point, you should see it advertising with a different name “NCP APP 2”.

You can repeat the process and do another update to the original version by using file fullv1.gbl.

Remember that you need to erase the download area before each OTA update.

Bluetooth in-place OTA DFU

The download area must not overlap with the user application and therefore the above DFU solution is not applicable to devices or modules based on the EFR32xG1, as they have smaller flash size (256 kB). For EFR32xG1, the Bluetooth in-place OTA DFU Bootloader configuration is used as a default. In this configuration, the upper half of the main flash, normally used to hold the Bluetooth application, is repurposed as a storage area while a Bluetooth stack upgrade is downloaded. The flash layout is illustrated in Fig. 3.1. in AN1086 Section 3.2. For these parts, you can follow the same workflow as described in the Apploader section, but make sure to flash the Bluetooth in-place OTA DFU Bootloader example project (or demo) first.

Further Reading

Source Files