Nordic BLE Advertising Part I

Nordic has a good tutorial Bluetooth low energy Advertising, a beginner’s tutorial, and two guides of GAP advertising Part I and Part II. In this post, we will go through some of advertising features in details.

Device Name

In every Nordic example, the device name is a defined by define DEVICE_NAME in the code. There has been many posts (here) about how to change the device name. However, the simplest I found was using the advertising module from this post, only two steps:

  1. Set the new device name with sd_ble_gap_device_name_set(),
  2. Call ble_advertising_advdata_update() to update the name part of the advertising packet.

The write permissions are set to the variable sec_mode using the BLE_GAP_CONN_SEC_MODE_SET_OPEN() macro. You many want to stop advertising first to be safe, however, I have found that there’s no issue to just do it with stop/restart. To make it even simpler, let advdata and srdata be global variable, so you can update it anywhere including inside advertising_init() and don’t need to rebuild either data structure. There are, however, limitations on the length of the device name.

TX Power

There are two aspects of TX power, one is for advertising to show TX power value, the other is to change the TX radio power. Though they are aligned in most cases, they have to be set individually.

To set advertising TX power, set m_advdata.p_tx_power_level = &m_tx_power in advertising_init() or anywhere you wish to change. If it’s not in initialization, then call ble_advertising_advdata_update() again so it will appear in the advertising packet. It comes handy if you already set advdata to global so the internal buffer is there to be easily modified.

To set TX power of the radio, call sd_ble_gap_tx_power_set function with a handle as described in this post:

  1. For advertising BLE_GAP_TX_POWER_ROLE_ADV, call advertising_int() first, so the adv_handle is not empty:
sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, m_advertising.adv_handle, level);
  1. For scanning BLE_GAP_TX_POWER_ROLE_SCAN_INIT, the connection handle is ignored:
sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, BLE_CONN_HANDLE_INVALID, level);
  1. For connection BLE_GAP_TX_POWER_ROLE_CONN, use the specific connection handle from the connected event, which can be found in on_ble_evt() function.
sd_ble_gap_tx_power_set(BLE_GAP_TX_POWER_ROLE_ADV, p_ble_evt->evt.gap_evt.conn_handle, level);

If nothing changed when a connection is established, the TX power during connection will inherit the power used in advertising or scanning.

UUID

In the service tutorial there’s a good section about the Universally Unique Identifier (UUID). There are two types, a short 16-bit Bluetooth SIG predefined UUID and a 128-bit vendor specific UUID for custom services and characteristics. One way to create a custom one is to use a random generator. In Nordic SDK, these two types are BLE_UUID_TYPE_BLE and BLE_UUID_TYPE_VENDOR_BEGIN.

You can add multiple services in the services_init() method, we can also include one (so that other services can be discovered once connected) or multiple UUIDs (predefined or vendor specific) in the advertising packet. One thing to pay attention to is the order they’ve been added, otherwise the advertising services may get mixed up (see here and here).

Inside the function services_init(), each service initialized and added to the stack by sd_ble_uuid_vs_add(), which normally resides in each service file. Then, the main calls the advertising_init() to add each service UUIDs to the advertising packet. For the advertised UUIDs correspond to the actual services, you have to make sure each UUID points to the right sercie in the stack, e.g., if you have three services DFU, our_service, and NUS initialized in this order in services_init(), you will need to specify them correspondingly:

static ble_uuid_t m_adv_uuids[] =
{
  {BLE_UUID_OUR_SERVICE, BLE_UUID_TYPE_VENDOR_BEGIN+1},
  {BLE_UUID_NUS_SERVICE, BLE_UUID_TYPE_VENDOR_BEGIN+2},
  {BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE}
};

The TYPE is to tell if it’s a predefined or a vendor specific UUID, but they all point to the first base UUID. In this case the first one is DFU, the second is OUR_SERVICE and the third is NUS_SERVICE, thus BLE_UUID_TYPE_VENDOR_BEGIN+1 and BLE_UUID_TYPE_VENDOR_BEGIN+2 respectively.