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
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.