How to update advertising data dynamically using BLE Advertising library

Introduction

The question of how to update advertising data dynamically using the BLE Advertising library has been raised many times by our forum-goers, especially after the release of nRF5 SDK v15.2.0, in which the ble_advertising_advdata_update function was added. The usage of this function changed with the release of nRF5 SDK v16.0.0, and this blog post aims to shed light on this change, and make it clear how to use this function with SDK versions from both before and after the change.

Intention and revision

Prior to the release of nRF5 SDK v15.2.0 the primary way to change advertising data at runtime was to first stop the advertising, configure with new data, and then restart the advertising. This is a sure-fire but tedious way of changing the advertising data. The intention behind the introduction of the ble_advertising_advdata_update function was that the user would no longer have to stop-configure-start the advertising every time the advertising data should change, but rather just provide the new data and be done with it. While the initial release of the function in nRF5 SDK v15.2.0 achieved this, it was not specified thoroughly in the API Reference that the advertising data buffer provided to the ble_advertising_advdata_update function needed to be a new buffer - i.e it would not succeed if the user kept passing the the same data buffer, even though its contents had been updated. This meant that the user would have to do double buffering manually, and alternate between which advertising data buffer that was currently in use, and which could be updated.
This was a point of confusion for many users, which led to the changed implementation in nRF5 SDK v16.0.0.
In nRF5 SDK v16.0.0 the ble_advertising_advdata_update function was augmented to take care of the double-buffering issue.

This change can be seen in the following snippets from the functions source code.

Initial ble_advertising_advdata_update function as part of nRF5 SDK v15.2.0 and v15.3.0:
ret_code_t ble_advertising_advdata_update(ble_advertising_t  * const p_advertising,
                                          ble_gap_adv_data_t * const p_new_advdata_buf,
                                          bool                       permanent)
{
    if (permanent)
    {
        memcpy(&p_advertising->adv_data, p_new_advdata_buf, sizeof(p_advertising->adv_data));
        p_advertising->p_adv_data = &p_advertising->adv_data;
    }
    else
    {
        p_advertising->p_adv_data = p_new_advdata_buf;
    }

    return sd_ble_gap_adv_set_configure(&p_advertising->adv_handle,
                                        p_advertising->p_adv_data,
                                        NULL);
}


Revised ble_advertising_advdata_update function as part of nRF5 SDK v16.0.0 and v17.0.2:
ret_code_t ble_advertising_advdata_update(ble_advertising_t   * const p_advertising,
                                          ble_advdata_t const * const p_advdata,
                                          ble_advdata_t const * const p_srdata)
{
    VERIFY_PARAM_NOT_NULL(p_advertising);
    if (p_advertising->initialized == false)
    {
        return NRF_ERROR_INVALID_STATE;
    }

    if ((p_advdata == NULL) && (p_srdata == NULL))
    {
        return NRF_ERROR_NULL;
    }

    ble_gap_adv_data_t new_adv_data;
    memset(&new_adv_data, 0, sizeof(new_adv_data));

    if (p_advdata != NULL)
    {
        new_adv_data.adv_data.p_data =
            (p_advertising->p_adv_data->adv_data.p_data != p_advertising->enc_advdata[0]) ?
             p_advertising->enc_advdata[0] : p_advertising->enc_advdata[1];
        new_adv_data.adv_data.len = adv_set_data_size_max_get(p_advertising);

        ret_code_t ret = ble_advdata_encode(p_advdata,
                                            new_adv_data.adv_data.p_data,
                                            &new_adv_data.adv_data.len);
        VERIFY_SUCCESS(ret);
    }

    if (p_srdata != NULL)
    {
        new_adv_data.scan_rsp_data.p_data =
            (p_advertising->p_adv_data->scan_rsp_data.p_data != p_advertising->enc_scan_rsp_data[0]) ?
             p_advertising->enc_scan_rsp_data[0] : p_advertising->enc_scan_rsp_data[1];
        new_adv_data.scan_rsp_data.len = adv_set_data_size_max_get(p_advertising);

        ret_code_t ret = ble_advdata_encode(p_srdata,
                                            new_adv_data.scan_rsp_data.p_data,
                                            &new_adv_data.scan_rsp_data.len);
        VERIFY_SUCCESS(ret);
    }

    memcpy(&p_advertising->adv_data, &new_adv_data, sizeof(p_advertising->adv_data));
    p_advertising->p_adv_data = &p_advertising->adv_data;

    return sd_ble_gap_adv_set_configure(&p_advertising->adv_handle,
                                        p_advertising->p_adv_data,
                                        NULL);
}

Notice how the updated function takes care of the double-buffering issue of its predecessor by always populating a new data buffer before encoding and configuring the data.

Super short summary

nRF5 SDK v15.2.0 and v15.3.0:

Create two advertising data buffers, and alternate between which is passed to ble_advertising_advdata_update and which is updated.
To continuously update the advertising data follow the steps below:

1. Create two advertising data buffers
2. Fill the first data buffer with contents, and use it to configure and start advertising.
3. Update the second advertising data buffer with new advertising data.
4. Pass the newly updated second advertising data buffer to the ble_advertising_advdata_update function.
5. Update the first advertising data buffer with new advertising data.
6. Pass the newly updated first advertising data buffer to the ble_advertising_advdata_update function.
7. Repeat steps 3 - 6.

nRF5 SDK v16.0.0 and v17.0.2:

1. Create a single advertising data buffer.
2. Fill the data buffer with contents, and use it to configure and start advertising.
3. Update the advertising data buffer with the new advertising data.
4. Pass the updated advertising data buffer to the ble_advertising_advdata_update function.
5. Repeat steps 3 - 4.


Simple demonstration

Lastly, a simple project is attached to the end of the blogpost that demonstrates how the advertising data can be changed dynamically. The demonstration is made for a nRF52840 DK for use with the S140 SoftDevice and nRF5 SDK v.17.0.2. The included project is based on the BLE peripheral template example from the nRF5 SDK v17.0.2, and is modified to change its advertising data every 5th second. The device advertises as ‘Nordic_template’ and starts out with the default advertising payload from the example, before changing its advertising payload to contain a single letter in its manufacturer specific data field. I will leave the creative part of populating this manufacturer specific data with something cool to you!

Try it out yourself to see how it works, and please do not hesitate to open a ticket on DevZone if you should encounter any issues or questions! :)

ble_app_update_advertising_data.zip
Anonymous