4. Create a Custom Profile Characteristic

4.1. Getting started

Having covered the basics of the Bluetooth Attribute Protocol and Generic Attribute Profile, we can now build our custom service and characteristic, and use it in our application.

Go to the SDK 6.0.12 folder that you have downloaded on your hard drive and navigate to the \projects\target_apps\ble_examples\ble_app_profile\Keil_5 folder. In there open the ble_app_profile.uvprojx Keil project. This a template project that you can use to build your own profile around it. You can also use the \projects\target_apps\ble_examples\ble_app_peripheral as a reference to a more extended implementation.

You can see the project file structure on the left edge of your IDE.

ble_app_profile

Figure 5 The BLE Profile Application project

4.2. Project structure

You can see that there are two types of folders included in the project, some that their name begins with “sdk_” and some that begin with “user_”. User code is developed under the folders beginning with “user_” and all of our modifications will be on these files, but we will also take a look at some SDK functions when this will benefit our understanding. These folders are:

  • user_config/, contains most configuration files.

  • da1458x_config_advanced.h holds advanced configuration settings.

  • da1458x_config_basic.h holds basic configuration settings.

  • user_callback_config.h appoints the callback functions that handle various events or operations. More on this will be discussed later.

  • user_profiles_config.h defines which BLE profiles (Bluetooth SIG adopted or custom ones) will be included in user’s application. Each macro denotes the respective BLE profile that will be included.

  • user_modules_config.h defines which application modules are included or excluded from the user’s application. Define each macro to 0 if it’s included, or to 1 if it’s excluded. An important point to note is that these settings have no effect if the respective module is a BLE profile that it is not used in the user’s application.

  • user_config.h holds advertising parameters, connection parameters, etc.

  • user_periph_setup.h defines hardware related settings relative to the used Development Kit.

  • user_platform/, contains the implementation of target related functions and especially configuration and initialization of peripheral devices in file user_periph_setup.c.

  • user_custom_profile/, contains our BLE server’s database declaration and configuration parameters.

  • user_app/, holds the user’s implementation of handling BLE events and any additional application code.

In the following figure you see the message flow diagram between the Server Application entity, the Custom Profile Service and the Remote Client.

Custom Service message flow diagram

Figure 6 Custom Service message flow diagram

When our device boots, the Custom Profile app initiates the service database creation of the Custom Profile. The creation of the database is confirmed and awaits for a client to connect. When the Remote Client connects, the service gets enabled and the Remote Client can now initiate requests to our server. In the above use case, the Remote Client requests to write to characteristic 1. This is indicated by the Custom Profile to the user application, which updates the value of characteristic 1. Finally our remote client disconnects, and this disables the Custom Profile Service, which in turn indicates that to the user application.

4.3. Adding a characteristic step by step

The following steps will guide you on how to create your custom BLE Profile on your DA14531 or DA14585/DA14586 device.

  1. Navigate to file da1458x_config_advanced.h in the folder user_config/ and change the default Bluetooth Device Address. This address has to be unique in a BLE network.

#define CFG_NVDS_TAG_BD_ADDRESS             {0x03, 0x00, 0x70, 0xCA, 0xEA, 0x80}
  1. In file user_module_config.h, check and define the DLG_CUST1 module.

#define EXCLUDE_DLG_SUOTAR          (1)   //This module gets _excluded_
#define EXCLUDE_DLG_CUSTS1          (0)   //This module gets _included_
  1. Activate the custom profile in your application by defining the respective macro in the user_profiles_config.h file.

#define CFG_PRF_DISS
#define CFG_PRF_CUST1
  1. Now navigate to file user_config.h and change the advertising device name.

/// Device name
#define USER_DEVICE_NAME        "DLG-PRPH"
  1. Change the Advertising Data. More information can be found in the BLE Advertising Concepts Tutorial for DA14531 and DA14585/DA14586 devices.

/// Advertising data
#define USER_ADVERTISE_DATA      ("\x03"\
                                 ADV_TYPE_COMPLETE_LIST_16BIT_SERVICE_IDS\
                                 ADV_UUID_DEVICE_INFORMATION_SERVICE\
                                 "\x11"\
                                 ADV_TYPE_COMPLETE_LIST_128BIT_SERVICE_IDS\
                                 "\x59\x5A\x08\xE4\x86\x2A\x9E\x8F\xE9\x11\xBC\x7C\x98\x43\x42\x18")

Note

Your custom Service UUID must be written in little-endian format. You should expect to see the Service UUID as “184243987C…5A59”.

  1. Optionally, choose your default sleep mode. For low power consumption, you can set it to ARCH_EXT_SLEEP_ON, but bear in mind that when the debugger is attached you won’t be able to monitor the DA14531’s or DA14585/DA14586’s ultra low power consumption.

static const sleep_state_t app_default_sleep_mode = ARCH_SLEEP_OFF;

4.3.1. The Custom Profile Service

Note

Some of the implementation of the Custom Profile functions have been moved to ROM for DA14531 devices. The next steps on this tutorial are the same for both device families, but you can refer to the SDK Porting Guide if you want to find more about the related changes.

Having made our device’s basic configuration, along with setting up the advertising parameters, we can now dive deeper into our GATT server’s implementation details.

1. Open the file user_custs1_def.h in folder user_custom_profile/. This header file is not shown in the Keil project structure. You can either locate it in your file browser, or open the user_custs1_def.c file, right-click on the header’s file include directive and select “Open document user_custs1_def.h”. In there you will find the UUID definitions for the service and their characteristics, their lengths and other constants.

Open header file in Keil IDE

Figure 7 Open header file in Keil IDE

  1. Define our custom Service’s UUID. This has to match the Service UUID that you defined in the Advertising Data; see step 5 in the previous section.

#define DEF_SVC1_UUID_128                {0x59, 0x5a, 0x08, 0xe4, 0x86, 0x2a, 0x9e, 0x8f, 0xe9, 0x11, 0xbc, 0x7c, 0x98, 0x43, 0x42, 0x18}

Note

This tutorial provides an example of a 128-bit UUID number. Before releasing a product to the market the user will need to define a different 128-bit number than used in the example to avoid conflicts. The user can select any number and this does not need to be registered at the Bluetooth SIG.

  1. Add the service’s Control Point UUID.

#define DEF_SVC1_CTRL_POINT_UUID_128     {0x20, 0xEE, 0x8D, 0x0C, 0xE1, 0xF0, 0x4A, 0x0C, 0xB3, 0x25, 0xDC, 0x53, 0x6A, 0x68, 0x86, 0x2D}
  1. Add Control Point’s data length.

#define DEF_SVC1_CTRL_POINT_CHAR_LEN     1
  1. Add Control Point’s description name as a string.

#define DEF_SVC1_CONTROL_POINT_USER_DESC     "Control Point"
  1. As discussed, an attribute has also properties, value, and optionally some descriptors. Our Control Point’s implementation uses the “Characteristic User Description” descriptor. Add your custom Service database Control Point characteristic, value, and user description to define an enumeration that will be used for the server’s database.

/// Custom1 Service Data Base Characteristic enum
enum
{
 // Custom Service 1
 SVC1_IDX_SVC = 0,

 SVC1_IDX_CONTROL_POINT_CHAR,
 SVC1_IDX_CONTROL_POINT_VAL,
 SVC1_IDX_CONTROL_POINT_USER_DESC,
 [...]
  1. Open the file user_custs1_def.c in folder user_custom_profile. Declare and assign the custom server’s attribute value. Use also a variable that will hold the Control Point’s descriptor UUID.

static const uint8_t SVC1_CTRL_POINT_UUID_128[ATT_UUID_128_LEN]       = DEF_SVC1_CTRL_POINT_UUID_128;

static const uint16_t att_desc_user_desc = ATT_DESC_CHAR_USER_DESCRIPTION;
  1. Add your characteristic declaration, value and description in custom server’s database attributes.

// Control Point Characteristic Declaration
[SVC1_IDX_CONTROL_POINT_CHAR]      = {(uint8_t*)&att_decl_char, ATT_UUID_16_LEN, PERM(RD, ENABLE),
                                        0, 0, NULL},

// Control Point Characteristic Value
[SVC1_IDX_CONTROL_POINT_VAL]       = {SVC1_CTRL_POINT_UUID_128, ATT_UUID_128_LEN, PERM(WR, ENABLE) | PERM(WRITE_REQ, ENABLE),
                                        DEF_SVC1_CTRL_POINT_CHAR_LEN, 0, NULL},

// Control Point Characteristic User Description
[SVC1_IDX_CONTROL_POINT_USER_DESC] = {(uint8_t*)&att_desc_user_desc, ATT_UUID_16_LEN, PERM(RD, ENABLE),
                                        sizeof(DEF_SVC1_CONTROL_POINT_USER_DESC) - 1, sizeof(DEF_SVC1_CONTROL_POINT_USER_DESC) - 1,
                                        (uint8_t *) DEF_SVC1_CONTROL_POINT_USER_DESC},

At this point you have created your first custom Profile Characteristic, the Control Point Characteristic. This Characteristic is mainly used for user configuration. In the next section we’ll add one more Characteristic, as also a Service for accessing it and modifying it.