Merge Nordic Buttonless DFU with NUS

Nordic provides good examples, but mostly independent from each other. To make a BLE UART service with DFU support, we’ll need to merge the two examples:

ble_app_buttonless_dfu, and ble_app_uart. We start with either one and add another.

1. Adding NUS to Buttonless DFU

Copy and rename ble_app_buttonless_dfu_nus_pca10056_s140, change the solution and project name accordingly in .emProject file.

All the changes can be done either in SES -> options -> common or by directly editing the .emProject file as followings.

1.1. Add BLE UART example required libraries.

Find section c_user_include_directories and add:

../../../../../../components/ble/ble_services/ble_nus
../../../../../../components/ble/ble_services/ble_nus_c
../../../../../../components/ble/ble_link_ctx_manager
../../../../../../components/libraries/fifo
../../../../../../components/libraries/uart

1.2. Add source files

Find the section nRF_Libraries and add:

<folder Name="nRF_Libraries">
......
<file file_name="../../../../../../components/libraries/fifo/app_fifo.c" />
<file file_name="../../../../../../components/libraries/uart/app_uart_fifo.c" />
<file file_name="../../../../../../components/libraries/uart/retarget.c" />
</folder>

Find the section nRF_BLE and add:

<folder Name="nRF_BLE">
......
<file file_name="../../../../../../components/ble/ble_link_ctx_manager/ble_link_ctx_manager.c" />
......
</folder>

Find the section nRF_BLE_Services and add:

<folder Name="nRF_BLE_Services">
  <file file_name="../../../../../../components/ble/ble_services/ble_nus/ble_nus.c" />
</folder>

1.3. Make changes to RAM

Find section linker_section_placement_macros

RAM_START=0x20002280
RAM_SIZE=0x3dd80

1.4. Make changes to sdk_config.h

Add to nRF_BLE_Services

// </h>
//==========================================================

// <h> nRF_BLE_Services

//==========================================================
// <q> BLE_NUS_C_ENABLED  - ble_nus_c - Nordic UART Central Service


#ifndef BLE_NUS_C_ENABLED
#define BLE_NUS_C_ENABLED 0
#endif

// <e> BLE_NUS_ENABLED - ble_nus - Nordic UART Service
//==========================================================
#ifndef BLE_NUS_ENABLED
#define BLE_NUS_ENABLED 1
#endif
// <e> BLE_NUS_CONFIG_LOG_ENABLED - Enables logging in the module.
//==========================================================
#ifndef BLE_NUS_CONFIG_LOG_ENABLED
#define BLE_NUS_CONFIG_LOG_ENABLED 0
#endif
// <o> BLE_NUS_CONFIG_LOG_LEVEL  - Default Severity level

// <0=> Off
// <1=> Error
// <2=> Warning
// <3=> Info
// <4=> Debug

#ifndef BLE_NUS_CONFIG_LOG_LEVEL
#define BLE_NUS_CONFIG_LOG_LEVEL 3
#endif

// <o> BLE_NUS_CONFIG_INFO_COLOR  - ANSI escape code prefix.

// <0=> Default
// <1=> Black
// <2=> Red
// <3=> Green
// <4=> Yellow
// <5=> Blue
// <6=> Magenta
// <7=> Cyan
// <8=> White

#ifndef BLE_NUS_CONFIG_INFO_COLOR
#define BLE_NUS_CONFIG_INFO_COLOR 0
#endif

// <o> BLE_NUS_CONFIG_DEBUG_COLOR  - ANSI escape code prefix.

// <0=> Default
// <1=> Black
// <2=> Red
// <3=> Green
// <4=> Yellow
// <5=> Blue
// <6=> Magenta
// <7=> Cyan
// <8=> White

#ifndef BLE_NUS_CONFIG_DEBUG_COLOR
#define BLE_NUS_CONFIG_DEBUG_COLOR 0
#endif

// </e>

// </e>

You can add other services if you want, here are a few extras I added.

// <q> BLE_CTS_C_ENABLED  - ble_cts_c - Current Time Service Client


#ifndef BLE_CTS_C_ENABLED
#define BLE_CTS_C_ENABLED 0
#endif

// <q> BLE_DIS_ENABLED  - ble_dis - Device Information Service


#ifndef BLE_DIS_ENABLED
#define BLE_DIS_ENABLED 0
#endif


// <q> BLE_TPS_ENABLED  - ble_tps - TX Power Service


#ifndef BLE_TPS_ENABLED
#define BLE_TPS_ENABLED 0
#endif

Add to nRF_Libraries

Add APP_FIFO

// </h>
//==========================================================

// <h> nRF_Libraries

//==========================================================
// <q> APP_FIFO_ENABLED  - app_fifo - Software FIFO implementation


#ifndef APP_FIFO_ENABLED
#define APP_FIFO_ENABLED 1
#endif

Add APP_UART

// </h>
//==========================================================

// </e>

// <e> APP_UART_ENABLED - app_uart - UART driver
//==========================================================
#ifndef APP_UART_ENABLED
#define APP_UART_ENABLED 1
#endif
// <o> APP_UART_DRIVER_INSTANCE  - UART instance used

// <0=> 0

#ifndef APP_UART_DRIVER_INSTANCE
#define APP_UART_DRIVER_INSTANCE 0
#endif

// </e>

Add RETARGET

// <q> RETARGET_ENABLED  - retarget - Retargeting stdio functions


#ifndef RETARGET_ENABLED
#define RETARGET_ENABLED 1
#endif

Change defines

#define NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED 0
#define NRF_FPRINTF_DOUBLE_ENABLED 1
#define NRF_LOG_BACKEND_RTT_ENABLED 1
#define NRF_LOG_BACKEND_UART_ENABLED 0
#endif

increase the UUID count

#define NRF_SDH_BLE_VS_UUID_COUNT 2

1.5. Make changes in main.c

Add headers

#include "ble_nus.h"
#include "app_uart.h"

#if defined (UART_PRESENT)
#include "nrf_uart.h"
#endif
#if defined (UARTE_PRESENT)
#include "nrf_uarte.h"
#endif

Add defines

#define DEVICE_NAME                     "Nordic_Buttonless_NUS"   

#define NUS_SERVICE_UUID_TYPE           BLE_UUID_TYPE_VENDOR_BEGIN                  /**< UUID type for the Nordic UART Service (vendor specific). */
#define UART_TX_BUF_SIZE                256                                         /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE                256                                         /**< UART RX buffer size. */

BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT);

static uint16_t m_ble_nus_max_data_len  = BLE_GATT_ATT_MTU_DEFAULT - 3;  

Copy BLE UART functions over

static void nus_data_handler(ble_nus_evt_t * p_evt)
static void services_init(void)
{
    ble_nus_init_t            nus_init;

    // Initialize NUS.
    memset(&nus_init, 0, sizeof(nus_init));

    nus_init.data_handler = nus_data_handler;

    err_code = ble_nus_init(&m_nus, &nus_init);
    APP_ERROR_CHECK(err_code);
    ......
}
void uart_event_handle(app_uart_evt_t * p_event)

static void uart_init(void)

Finally add uart_init() and printf

int main(void)
{
  ......
  err_code = ble_dfu_buttonless_async_svci_init();
  APP_ERROR_CHECK(err_code);

  uart_init();

  ......

  printf("\r\nUART started.\r\n");

  ......
}

2. Adding Buttoneless DFU to NUS

This post has a guide to add buttonless dfu services to ble_app_uart example. We will need

nrf_dfu_types.h
nrf_bootloader_info.h

2.1. Changes in .emProject

Add to c_user_include_directories

../../../../../../components/libraries/bootloader
../../../../../../components/libraries/bootloader/ble_dfu
../../../../../../components/libraries/bootloader/dfu
../../../../../../components/libraries/svc

Find section c_preprocessor_definitions or in SES -> Options -> Common -> Preprocessor -> Preprocessor Definitions and add the definitions below:

BL_SETTINGS_ACCESS_ONLY
NRF_DFU_SVCI_ENABLED
NRF_DFU_TRANSPORT_BLE=1

Make changes to linker_section_placement_macros

RAM_START=0x20002AF8
RAM_SIZE=0x3D508

Add source files in folder nRF_SVC

<folder Name="nRF_SVC">
  <file file_name="../../../../../../components/libraries/bootloader/dfu/nrf_dfu_svci.c" />
</folder>

Add source files in folder nRF_DFU

<folder Name="nRF_DFU">
  <file file_name="../../../../../../components/ble/ble_services/ble_dfu/ble_dfu.c" />
  <file file_name="../../../../../../components/ble/ble_services/ble_dfu/ble_dfu_bonded.c" />
  <file file_name="../../../../../../components/ble/ble_services/ble_dfu/ble_dfu_unbonded.c" />
</folder>

2.3. Changes in sdk_config.h

In section nRF_Libraries, set

#define NRF_LOG_ENABLED 1
#define NRF_SDH_BLE_VS_UUID_COUNT 2

In section nRF_DFU, set

#define BLE_DFU_ENABLED 1

Note, this is not used anymore as stated here:

the macro BLE_DFU_ENABLED is not used in the bootloader library. It is simply an artifact from a previous SDK version.

2.4. Changes in main.c

Add header

#include "ble_dfu.h"
#include "nrf_bootloader_info.h"
#include "nrf_dfu_ble_svci_bond_sharing.h"
#include "nrf_svci_async_function.h"
#include "nrf_svci_async_handler.h"

Copy functions over

static void advertising_config_get(ble_adv_modes_config_t * p_config)
static void disconnect(uint16_t conn_handle, void * p_context)
static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event)
static void services_init(void)
{
    ......
    ble_dfu_buttonless_init_t   dfus_init = {0};

    // Initalize buttonless DFU
    dfus_init.evt_handler = ble_dfu_evt_handler;

    err_code = ble_dfu_buttonless_init(&dfus_init);
    APP_ERROR_CHECK(err_code);
    ......
}
static void sleep_mode_enter(void)
{
    ......
    // err_code = sd_power_system_off();

    err_code = nrf_sdh_disable_request();
    APP_ERROR_CHECK(err_code);
}
static void advertising_init(void)
{
    .....
    // init.config.ble_adv_fast_enabled  = true;
    // init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
    // init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
    advertising_config_get(&init.config);
    ......
}
int main(void)
{
    ......
    // Initialize the async SVCI interface to bootloader before any interrupts are enabled.
    err_code = ble_dfu_buttonless_async_svci_init();
    APP_ERROR_CHECK(err_code);

This post has a nice explanation of the functions used in buttonless DFU services

2.5. Add Pear Manager

You will need link all required files to solve the messy errors related to Peer Manager

<folder Name="nRF_Libraries">
......
<file file_name="../../../../../../components/libraries/fstorage/nrf_fstorage.c" />
<file file_name="../../../../../../components/libraries/fstorage/nrf_fstorage_sd.c" />
<file file_name="../../../../../../components/libraries/fds/fds.c" />
</folder>
<folder Name="nRF_BLE">
  ......
  <file file_name="../../../../../../components/ble/peer_manager/auth_status_tracker.c" />
  <file file_name="../../../../../../components/ble/peer_manager/gatt_cache_manager.c" />
  <file file_name="../../../../../../components/ble/peer_manager/gatts_cache_manager.c" />
  <file file_name="../../../../../../components/ble/peer_manager/id_manager.c" />
  <file file_name="../../../../../../components/ble/peer_manager/peer_data_storage.c" />
  <file file_name="../../../../../../components/ble/peer_manager/peer_database.c" />
  <file file_name="../../../../../../components/ble/peer_manager/peer_id.c" />
  <file file_name="../../../../../../components/ble/peer_manager/peer_manager.c" />
  <file file_name="../../../../../../components/ble/peer_manager/peer_manager_handler.c" />
  <file file_name="../../../../../../components/ble/peer_manager/pm_buffer.c" />
  <file file_name="../../../../../../components/ble/peer_manager/security_dispatcher.c" />
  <file file_name="../../../../../../components/ble/peer_manager/security_manager.c" />
</folder>

Add to sdk_config.h

#define PEER_MANAGER_ENABLED 1

#define FDS_ENABLED 1
#define NRF_FSTORAGE_ENABLED 1
#include "peer_manager.h"
#include "peer_manager_handler.h"

changes in main.c static void peer_manager_init()

3. Remarks

3.1. You may encounter errors

<warning> nrf_sdh_ble: Insufficient RAM allocated for the SoftDevice.
<warning> nrf_sdh_ble: Change the RAM start location from 0x20002270 to 0x20002AF8.
<warning> nrf_sdh_ble: Maximum RAM size for application is 0x3D508.

Change RAM size accordingly

3.2. Missing function advertising_config_get?

it is implemented directly inside advertising_init.

3.3. What is app_shutdown_handler doing?

See this link for exaplaination of actions in buttonless_dfu; NRF_PWR_MGMT_HANDLER_REGISTER(app_shutdown_handler, 0);

3.4. nrf_sdh_disable_request and gpregret2?

See links here and here for detailed explanation. buttonless_dfu_sdh_state_observer NRF_SDH_STATE_OBSERVER(m_buttonless_dfu_state_obs, 0)

3.5. Enter sleep mode

ble NUS sd_power_system_off(); ble Buttonless DFU nrf_sdh_disable_request();

3.6. NRF_ERROR_NO_MEM error

See link here and here.

3.8. Customized UUID advertised incorrectly

SoftDevice needs to know where the customized UUID started, by setting BLE_UUID_TYPE_VENDOR_BEGIN correctly corresponding to the service initialized in services_init(), e.g, if the customized service initialized after a special one, then

static ble_uuid_t m_adv_uuids[] =
{
    {BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE + 1},
    {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}
};

The first UUID in the m_adv_uuids array will be advertised service.

3.7. Others: