BLE application with nRF51822: Advertising data

One of the feature (let’s say most important feature) brought by BLE specifications is the advertisement principle. BLE advertising allows a device to periodically broadcast data to every devices around it. Between each advertisement (the interval time can be set from 20ms to 10 seconds) the device can go in sleep mode, that helps to drastically reduce the power consumption. Before going deeper with advertisement, it’s important to explain some BLE vocabularies. A BLE device can be either a Peripheral or a Central. The Peripheral is mostly the battery powered up device that needs to keep its power consumption low such as a temperature sensor or a heart rate monitor. Since its power consumption has to remains low, it needs to send data to a much powerful device (a smartphone, computer etc…) to process the data, called the Central. BLE advertising is by definition unidirectional that means only the Peripheral can transmit data to the Central. If the Central needs to transmit data to the Peripheral as well, a connection needs to be done between both devices.

Let’s start by creating a new project based on sd_app project. We will call it sd_adv_app.

        $ cd nrf51/sd_app
        $ make clean
        $ cd ..
        $ cp -r sd_app/ sd_adv_app

Update the Makefile with the new project name:

# Project name
PROJECT_NAME = sd_adv_app

To support BLE advertisement in our code we also need to add tow more flags in the Makefile. USE_BLE_ADV to add corresponding advertisement code and USE_DRV_PSTORAGE to support the persistent storage, needed by BLE advertising.

# Drivers
USE_DRV_NOSD := n
USE_LIB_SCHEDULER := y
USE_SD110 := y
USE_BLE_COMMON := y
USE_LIB_TRACE := y
USE_DRV_BLE_FLASH := y
USE_LIB_TIMER := y
USE_BLE_ADV := y
USE_DRV_PSTORAGE := y

If you try to compile your code right now, you’ll get the following error:

../sdk/nRF51_SDK_9.0.0_2e23562/components/drivers_nrf/pstorage/pstorage.h:28:31: fatal error: pstorage_platform.h: No such file or directory
 #include "pstorage_platform.h";
                               ^
compilation terminated.
make: *** [../sdk/nRF51_SDK_9.0.0_2e23562/components/drivers_nrf/pstorage/pstorage.o] Error 1

As you can see pstorage_platform.h is missing, this file has to be added as a project dependent include containing the infromation where the application data is stored in flash. This module is really useful if you need to store persistent data in flash memory.

Create a folder named include in sd_adv_app/ and then copy pstorage_platform.h from an example code provided by Nordic.

        $ cd nrf51/sd_adv_app
        $ mkdir include
        $ cp ../sdk/nRF51_SDK_9.0.0_2e23562/examples/ble_peripheral/ble_app_template/config/pstorage_platform.h include/

The compilation should now works well. And we are now ready to code our first BLE app. To do that open main.c file, we’ll start by enabling Softdevice:

#include "softdevice_handler.h";
...
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
    // Catch BLE event here
}

static void softdevice_init(void)
{
    uint32_t err_code;

    // Initialize the SoftDevice handler module.
    SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, NULL);

    // Enable BLE stack.
    ble_enable_params_t ble_enable_params;
    memset(&ble_enable_params, 0, sizeof(ble_enable_params));

    err_code = sd_ble_enable(&ble_enable_params);
    APP_ERROR_CHECK(err_code);

    // Subscribe for BLE events.
    err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
    APP_ERROR_CHECK(err_code);
}

int main(void){
    softdevice_init();
...

The code above initialize the BLE stack Softdevice and setup the callback function needed to catch BLE events.

Once Softdevice initialized we can setup the GAP (Generic Access Profile) parameters (name of the device, security, connection parameters, etc…).

#define APP_ADV_NAME   "SD_ADV_APP";
...
static void gap_params_init(void)
{
    uint32_t                err_code;
    ble_gap_conn_sec_mode_t sec_mode;

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);

    err_code = sd_ble_gap_device_name_set(&sec_mode,
                                          (const uint8_t *)APP_ADV_NAME,
                                          strlen(APP_ADV_NAME));
    APP_ERROR_CHECK(err_code);
}

int main(void){
    softdevice_init();
    gap_params_init();
...

This code allows to setup the connection security in open mode (anyone can initiate a connection to the device) and to set the device name (the name we will see on a host during a BLE scan).

Then we can initialize the data that will be broadcasted during BLE advertising. We’ll setup the advertisement behaviour at the same time (advertisement interval, timeout, type, ect…)

#include "ble_advdata.h";
#include "ble_advertising.h";
...
#define APP_ADV_INTERVAL                320        // 320 * 0.625ms = 200ms
...
static void advertisement_evt_cb(ble_adv_evt_t ble_adv_evt)
{
	// Catch Advertisement events here
}
...
static void advertising_init(void)
{
    uint32_t      err_code;
    ble_advdata_t advdata;
    memset(&advdata, 0, sizeof(advdata));

    // advertise the the full device name
    advdata.name_type = BLE_ADVDATA_FULL_NAME;
    //LE General Discoverable Mode, BR/EDR not supported.
    advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;

    ble_adv_modes_config_t options = {0};

    // enable fast advertisement
    options.ble_adv_fast_enabled  = BLE_ADV_FAST_ENABLED;
    // set advertisement interval to 200 ms, 320 * 0.625ms
    options.ble_adv_fast_interval = APP_ADV_INTERVAL;
    // set advertisement timeout to infinite
    options.ble_adv_fast_timeout  = BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED;

    err_code = ble_advertising_init(&advdata, NULL, &options, advertisement_evt_cb, NULL);
    APP_ERROR_CHECK(err_code);
}

int main(void){
    softdevice_init();
    gap_params_init();
    advertising_init();
...

Finally we just need to call ble_advertising_start function to start advertisement:

int main(void){
    softdevice_init();
    gap_params_init();
    advertising_init();
    // start fast advertisement
    ble_advertising_start(BLE_ADV_MODE_FAST);

Compile your code and flash it on your dev board:

        $ cd nrf51/
        $ JLinkExe sh/nrf51_load_sd.sh
        J-Link>loadbin sd_adv_app/sd_adv_app.bin 0x18000
        J-Link>r
        J-Link>g

The green led should blink and now thanks to LightBLue Explorer app (ios/mac) you should be able to see your device advertising device name data:

LightBlue on Mac:

bluetooth low energy advertisement nrf51

LightBlue on ios:

bluetooth low energy advertisement nrf51

For Android users you can use a similar application called BLE Scanner.

If you click on SD_ADV_APP you’ll open a BLE connection with the device. Then if you click on Show advertisement data you’ll see the data advertised by the device.

bluetooth low energy advertisement nrf51

So far only the local name is broadcasted. We can add some data thanks to the manufacturer specific data field in an advertisement packet. To do that we just need to add our data in advdata.p_manuf_specific_data field in main.c file:

#define APP_ADV_CUSTOM_DATA     "hello world!";
...
static void advertising_init(void)
{
    uint32_t err_code;
    ble_advdata_t advdata;
    ble_advdata_manuf_data_t manfdata;

    memset(&advdata, 0, sizeof(advdata));

    // advertise the the full device name
    advdata.name_type = BLE_ADVDATA_FULL_NAME;
    // LE General Discoverable Mode, BR/EDR not supported.
    advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;

    // Set the manufacturer specific data below
    memset(&manfdata, 0, sizeof(manfdata));

    manfdata.company_identifier = 0x1234;
    manfdata.data.size = strlen(APP_ADV_CUSTOM_DATA);
    manfdata.data.p_data = (uint8_t *)APP_ADV_CUSTOM_DATA;

    advdata.p_manuf_specific_data = &manfdata;

    ble_adv_modes_config_t options = {0};
...

The result on LightBlue Explorer application should be as below:

bluetooth low energy advertisement nrf51

Here we are! We are now able to advertise data over BLE, in the next post we’ll see how to transfert data from our smartphone to the device in order to control remotely the LED.

Links:

LightBlue Explorer

BLE Scanner

You can retrieve the source code on the following github link: github

Advertisements

BLE application with nRF51822: Softdevice

One advantage of using Nordic Semiconductor BLE solution is Softdevice. Softdevice is a BLE stack running on the nRF51 chip, allowing you to develop a custom BLE application really easily. Sofdevice binary is provided free of charge with the nRF51 chip, a bunch of different public APIs are provided to let your application set up and communicate with it. Different declinations of Sofdevice are available depending the features you need for your project. In our case we will use S110 (BLE stack only). To get BLE working with our current project set up, we need to create a new linker script since Softdevice has to be stored at a specific memory location.

  • nRF51 Softdevice memory map:

nrf51822 bluetooth softdevice s110 memory map

To work properly Softdevice needs a part of the flash memory and a part of the RAM memory as well. So we will create a new linker script based on the first one we already developed:

        $ cd nrf51/linker_script/
        $ cp nrf51_ld_script.ld nrf51_sd_ld_script.ld

We now have to do some modifications in nrf51_sd_ld_script.ld file in order to share the memory. In Softdevice specifications document we can retrieve information regarding the memory usage when S110 is enabled.

nrf51822 bluetooth stack softdevice memory usage

As described below S110 + MBR need 96kB of Flash memory and 8kB of RAM memory. So the RAM starting point for our application is now 0x20002000 (0x20000000 + 8kB), therefore, the total RAM available for our application is not 16kB anymore but 8kB. Same for the Flash memory, the new starting point for our application is now 0x00180000 (0x00000000 + 96kB), so, the amount of Flash available is now 160kB. Below the modifications to do accordingly the specifications:

        MEMORY
        {
             RAM (xrw) : ORIGIN = 0x20000000 + 8k, LENGTH = 16k - 8k
             FLASH (rx) : ORIGIN = 0x00000000 + 96k, LENGTH = 256k - 96k
        }

That’s it for the linker script. We’re gonna create a new project based on the led blinking sources.

        $ cd nrf51/
        $ cd led_blinking/
        $ make clean         // In order to do a clean copy, remove all the object files 
        $ cd ../
        $ cp -r led_blinking/ sd_app

We need to do some modifications in the Makefile to call the correct ld script, change the project name and add few mandatory flags.

# linker script directory
LDSCRIPT = ../linker_script/nrf51_sd_ld_script.ld   
# Project name
PROJECT_NAME = sd_app
# Drivers
USE_DRV_NOSD := n
USE_LIB_SCHEDULER := y
USE_SD110 := y
USE_BLE_COMMON := y
USE_LIB_TRACE := y
USE_DRV_BLE_FLASH := y
USE_LIB_TIMER := y
...
CPFLAGS = -mcpu=cortex-m0 ...
CPFLAGS += -std=gnu99
...
# Define target processor
CPFLAGS += -D$(TARGET)
CPFLAGS	+= -DBLE_STACK_SUPPORT_REQD 

Now, compile your project and look at the generated sd_app.map file, you will see that the “.isr_vector” section is now starting at 0x180000, same for the “.bss” section which is now starting at 0x20002000.

Some modifications have been done in sdk.mk as well, in order to fix few include issues. You can find all these modifications at the following link:

https://github.com/jocelynmass/nrf51/blob/master/sdk/sdk.mk

Now, we need to flash Softdevice S110 (the MBR section is already included in S110) in the nrf51 flash memory. Start by copying the stack hex file in a new directory named stack_img.

        $ cd nrf51/
        $ mkdir stack_img
        $ cp sdk/nRF51_SDK_9.0.0_2e23562/components/softdevice/s110/hex/s110_softdevice.hex stack_img/

Then we’re gonna create a new JLink script file (based on nrf51_erase.sh script) that will help us to upload Sofdevice in flash memory.

        $ cd nrf51/sh/
        $ cp nrf51_erase.sh nrf51_load_sd.sh

In nrf51_load_sd.sh add the following lines:

loadfile stack_img/s110_softdevice.hex 0
r 

Finally we’ll check that our blinking led application is still working with Sofdevice loaded in the flash memory. Be careful to correctly load your application at address 0x18000, if not you’ll most likely corrupt Sofdevice.

        $ cd nrf51/
        $ JLinkExe sh/nrf51_load_sd.sh
        J-Link>loadbin sd_app/sd_app.bin 0x18000
        J-Link>r
        J-Link>g

You should see the green led blinking now. I know, nothing really exciting yet but that means we can now start using BLE ;-). In the next post we’ll see how to advertise some data with BLE.