Implementing the device manager

Implementing the device manager

In this tutorial we will cover how to implement pairing and bonding with a BLE enabled device using the device manager library for the nRF52 preview development kit. It is also possible to follow this tutorial for the nRF51 DK with some minor adjustments.

Required tools

To follow this tutorial you will need

Introduction

This tutorial is intended to be accessed after the tutorials on:

The tutorial will use the example ble_app_template(an advertising and pairing example) and modify it to use the device manager to store and restore bond information.

Pairing is the initial exchange of security features for each BLE device creating a temporary encryption. After a device has been paired it can optionally continue to bond. Bonding is the exchange of long term security keys which are stored for later re-connection and encryption.

The device manager does this for you, utilizing the persistent storage module, pstorage, to store the bond information in flash, so that it is persistent even after reboot. When you have reached the maximum amount of bonds(you can define this yourself, up to a max of 8) the device manager will assert and you will be able to handle this in your application. For an example of handling this see the github-example ble-peripheral-bond-handling.

Start off by:

  • Flashing SoftDevice S132 to your kit

  • Compiling and flashing the ble_app_template example

The example can be found in the SDK at ..sdk_0.9\examples\ble_peripheral\ble_app_template\pca10036\s132\arm5_no_packs

Next verify that everything is working correctly, observe that LED_1 is blinking to indicate advertising. You can also check that you are able to see Nordic_template advertising in Master Control Panel. Note that since the nRF52 DK uses a different pin configuration from the nRF51 DK, you will need to change the header file used if you want to follow this tutorial for the nRF51, for help with this see the tutorial on the Board Support Package.

Adding the device manager library

There are a few steps required to include the device manager in your project, these are:

Include the header file in main.c

#include "device_manager.h"

Add .c files to project

image description

  1. Click "File extensions, Books and Environment..."

  2. Click "Add files..."

  3. Add either device_manager_central.c or device_manager_peripheral.c, these can be found in "..\sdk_0.9\components\ble\device_manager" for this tutorial we will use the peripheral version

For most cases the version you chose is determined like this:

S110 -> peripheral

S120 -> central

S13x_peripheral -> peripheral

S13x_central -> central

Bonding as both peripheral and central at the same time is beyond the scope of this tutorial. It is not straightforward, and we do not support it out of the box.

Include the header path

image description

  1. Click "Options for target..."

  2. Select "C/C++"

  3. Click "..."

  4. Add the paths:

..\..\..\..\..\..\components\ble\device_manager

and ..\..\..\..\..\..\components\ble\device_manager\config

In this last path you will find the file device_manager_cnfg.h, this file contains configurations for the device manager, here you can set max amount of bonds, connections etc. the file is well commented, so I will leave it for you to configure.


Next we need to configure some dependencies, for this project we need the pstorage module, include it like this:

#include "pstorage.h"

If you want to do this for a custom example you may also have to include some paths, these are:

..\..\..\..\..\..\components\drivers_nrf\pstorage

and

..\..\..\..\..\..\components\drivers_nrf\pstorage\config

Here you will find the pstorage_platform.h file which contains configurations for the persistent storage module, more information on this can be found in the SDK.

Code initialization

We need to add pstorage to the system event dispatcher. A dispatcher is an event handler that decides where to, and in which order to dispatch an event. Change sys_evt_dispatch from:

static void sys_evt_dispatch(uint32_t sys_evt)
{
    ble_advertising_on_sys_evt(sys_evt);
}

to:

static void sys_evt_dispatch(uint32_t sys_evt)
{
    pstorage_sys_event_handler(sys_evt); //Add this line
    ble_advertising_on_sys_evt(sys_evt);
}

Next we will initialize the device manager module, add this code to your project, just above main.
static dm_application_instance_t         m_app_handle;                              /**< Application identifier allocated by device manager */


/**@brief Function for handling the Device Manager events.
 *
 * @param[in] p_evt  Data associated to the device manager event.
 */
static uint32_t device_manager_evt_handler(dm_handle_t const * p_handle,
                                           dm_event_t const  * p_event,
                                           ret_code_t        event_result)
{
    APP_ERROR_CHECK(event_result);

    return NRF_SUCCESS;
}


/**@brief Function for the Device Manager initialization.
 *
 * @param[in] erase_bonds  Indicates whether bonding information should be cleared from
 *                         persistent storage during initialization of the Device Manager.
 */
static void device_manager_init(bool erase_bonds)
{
    uint32_t               err_code;
    dm_init_param_t        init_param = {.clear_persistent_data = erase_bonds};
    dm_application_param_t register_param;

    // Initialize persistent storage module.
    err_code = pstorage_init();
    APP_ERROR_CHECK(err_code);

    err_code = dm_init(&init_param);
    APP_ERROR_CHECK(err_code);

    memset(&register_param.sec_param, 0, sizeof(ble_gap_sec_params_t));

    register_param.sec_param.bond         = SEC_PARAM_BOND;
    register_param.sec_param.mitm         = SEC_PARAM_MITM;
    register_param.sec_param.io_caps      = SEC_PARAM_IO_CAPABILITIES;
    register_param.sec_param.oob          = SEC_PARAM_OOB;
    register_param.sec_param.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
    register_param.sec_param.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
    register_param.evt_handler            = device_manager_evt_handler;
    register_param.service_type           = DM_PROTOCOL_CNTXT_GATT_SRVR_ID;

    err_code = dm_register(&m_app_handle, &register_param);
    APP_ERROR_CHECK(err_code);
}

For some explanation of the security parameters, see this infocenter link.

We also need to initialize this in our main() function. We will also initialize the local variable erase_bonds, this lets us add support for erasing all bonds by pressing a button or something similar.

bool erase_bonds;
device_manager_init(erase_bonds);

And we need to register the device manger event handler with the BLE dispatcher, change ble_evt_dispatch() from:

static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
    on_ble_evt(p_ble_evt);
    ble_conn_params_on_ble_evt(p_ble_evt);
    ble_advertising_on_ble_evt(p_ble_evt);

    /*
    YOUR_JOB: Add service ble_evt handlers calls here, like, for example:
    ble_bas_on_ble_evt(&m_bas, p_ble_evt);
    */
}

to:

static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
    on_ble_evt(p_ble_evt);
    ble_conn_params_on_ble_evt(p_ble_evt);
    ble_advertising_on_ble_evt(p_ble_evt);

    /*
    YOUR_JOB: Add service ble_evt handlers calls here, like, for example:
    ble_bas_on_ble_evt(&m_bas, p_ble_evt);
    */
    dm_ble_evt_handler(p_ble_evt); //Add this line
}

Let's open Master Control Panel(MCP) and check the functionality of our new device managed connection. After compiling and flashing our project we can see that LED1 is blinking, this indicates advertising. We can also connect to it using MCP, and observe that LED1 changes to a solid lit state. But when we try to bond with the device we're in for some trouble, it will refuse the bonding and go back to advertising.

Because the original code already has implemented manual pairing we are effectively doing the same thing twice, once manually and once with the device manager, this is causing an error. Since we want pairing and bonding to be handled by the device manager we can change the on_ble_evt handler to:

static void on_ble_evt(ble_evt_t * p_ble_evt)
{
    uint32_t                         err_code;
    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
            APP_ERROR_CHECK(err_code);
            m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
            break;
        
        default:
            // No implementation needed.
            break;
    }
}

Now we can connect and bond freely with Nordic_template using either MCP or a BLE enabled phone. Note that when not connected LED1 is blinking to indicate advertising, and is permanently lit when connected.

Notice that if you pair and bond with the development kit, and then disconnect and reconnect again, you will not have to do bonding again, you have already got an encrypted connection. That is the beauty of the device manager.

Final notes and further reading

The full project files are attached here, they are provided as is. These files should be added to the peripheral examples folder of SDK 0.9, e.g. ..sdk_0.9\examples\ble_peripheral\.

Feedback is much appreciated, post a comment here or send me a personal message if you have any questions or input.

Anonymous
  • Hi, Thanks for the well-written article. I'm working with SDK versions 12.3 and higher, trying to add bonding to the UART example, and I'm unable to find any file named 'device_manager.c'. Does this file no longer exist in later versions of the SDK ? Has it been replaced by something else ? Regards.

  • FormerMember
    FormerMember over 1 year ago

    As i was working on this code using sdk9,keil versio 5,i got an error as ._build\nrf51422_xxac_s110.axf: error: L6050U: The code size of this image (38220 bytes) exceeds the maximum allowed for this version of the linker. can u please help me out....

  • Thanks for this article, I have a few questions : I can build and download the project on my custom board based on nRF51822 soft device 130 V2 with SDK 11. I think this board is quite similar with nRF boards and I linked the custom bsp file.

    I can bond with nRF toolbox for android, but then when I unbond by using the "delete bond information option" I cannot bon again. Even when I reset or unplug the board. Then I realize that I can bond again only after the device goes to standby and I press the advertising button. I understand some bond information in flash is not reset when the central unbonds and something has to be done from the peripheral that is only done in the bsp as the main includes something like this // Initialize. timers_init(); buttons_leds_init(&erase_bonds); ble_stack_init(); device_manager_init(erase_bonds);

    So I'll look after this erase_bonds params, but I think there is some explanations missing here about the way to delete bond info.

    Besides this please note that the app_template example in SDK 11 is already implementing the device manager. Looks like the author of the tutorial just commited his changes to the repository...

  • Thank you for explaining in detail about pstorage and device manager. Can you please specify how to retrieve the data again from pstorage. Thank you.