2. Custom Database Creation Process

This section analyzes an application example which demonstrates creating a custom Bluetooth service. The example is based on the ble_peripheral sample code found in the SDK. It adds an additional custom 128-bit BLE service in the internal BLE Framework.

2.1. Working on a Custom BLE Service

This section goes through the steps required to create a custom 128-bit ATT Service. For demonstration purposes, let’s create a custom service which exhibits the following features:

  • One Service Declaration of type Primary
  • One Characteristic Declaration with write-read-notify access permissions
  • Two Descriptors, one of type Client Characteristic Configuration and one of type Characteristic User Description.

In general, it’s good practice to separate the source code of Bluetooth service implementation from the application itself. With this approach, a Bluetooth service is portable to any application just by defining a few callback functions as well as invoking an initialization function in application context.

  1. Declare callback functions. These callbacks will be called following specific BLE events to exchange data between the application task and the Bluetooth service itself. The number of callback functions depends on the number of characteristics as well as their associated permissions. For demonstration purposes let’s define two callbacks: one triggered upon read requests and one triggered upon write requests. Please note that these callbacks will be triggered from application context.
/* Callback functions */
typedef struct {

        /* Hanlder for read requests - Triggered on application context */

        /* Handler for write requets - Triggered on application context */

} my_custom_service_cb_t;
  1. Define a structure associated with the Bleutooth service. This structure should contain a variable of type ble_service_t, the previously defined callback functions, as well as all the attribute handles of Bluetooth service.
/* Service related variables */
typedef struct {
        ble_service_t svc;

        // Callback functions


        // Attribute handles of Bluetooth Service

} mc_service_t;
  1. Define the total number of attribute handles of Bluetooth service:
/*
 * 0 --> Number of Included Services
 * 1 --> Number of Characteristic Declarations
 * 2 --> Number of Descriptors
 */
 num_attr = ble_gatts_get_num_attr(0, 1, 2);
  1. Declare all the attributes of type Service (either Primary or/and Secondary). Please note that all the custom Bluetooth services should exhibit a 128-bit UUID, while the Bluetooth SIG ones a 16-bit UUID.
/* Service declaration */
ble_uuid_from_string("00000000-1111-2222-2222-333333333333", &uuid);
ble_gatts_add_service(&uuid, GATT_SERVICE_PRIMARY, num_attr);

Note

Special care must be taken when defining a 128-bit UUID. If the UUID is not defined correctly , an assertion will be issued.


  1. Declare all the attributes of type Characteristic:
/* Characteristic declaration */
ble_uuid_from_string("11111111-0000-0000-0000-111111111111", &uuid);
ble_gatts_add_characteristic(&uuid, GATT_PROP_READ | GATT_PROP_NOTIFY | GATT_PROP_WRITE,
                              ATT_PERM_RW, 1, GATTS_FLAG_CHAR_READ_REQ, NULL,
                                                        &mcs->mc_char_value_h);

Note

The GATTS_FLAG_CHAR_READ_REQ flag, is mandatory when enabling read permissions.


  1. Declare all the attributes of type Characteristic Descriptors.
/* Descriptor declaration - Client Characteristic Configuration (CCC) */
ble_uuid_create16(UUID_GATT_CLIENT_CHAR_CONFIGURATION, &uuid);
ble_gatts_add_descriptor(&uuid, ATT_PERM_RW, 2, 0,
                                        &mcs->mc_char_value_ccc_h);

/* Descriptor declaration - Characteristic User Description (CUD) */
ble_uuid_create16(UUID_GATT_CHAR_USER_DESCRIPTION, &uuid);
ble_gatts_add_descriptor(&uuid, ATT_PERM_READ,
                  sizeof(char_user_descriptor_val), 0, &char_user_descriptor_h);

Note

For all the available GATT descriptor UUIDs see at sdk/ble/include/ble_uuid.h.


  1. Register the newly created service in the ATT database. The first input parameter should be the first attribute handle of the service whereas the last input parameter should be zero. In this step, all the attribute handles should be registered, so that the BLE manager can update them automatically upon Bluetooth events.
/*
 * Register all the attribute handles so that they can be updated
 * by the BLE manager automatically.
 */
 ble_gatts_register_service(&mcs->svc.start_h, &mcs->mc_char_value_h,
                     &mcs->mc_char_value_ccc_h, &char_user_descriptor_h , 0);
  1. Calculate the last attribute handle of the service.
/* Calculate the last attribute handle of the BLE service */
mcs->svc.end_h = mcs->svc.start_h + num_attr;
  1. When necessary, declare predefined attribute values.
/* Set default attribute values */
ble_gatts_set_value(mcs->mc_char_value_h, 1, variable_value);
ble_gatts_set_value(char_user_descriptor_h,  sizeof(char_user_descriptor_val),
                                                    char_user_descriptor_val);
  1. Register the service in Dialog Bluetooth framework.
/* Register the BLE service in BLE framework */
ble_service_add(&mcs->svc);
  1. Define handlers for specific Bluetooth events.
/* Declare handlers for specific BLE events */
mcs->svc.read_req  = handle_read_req;
mcs->svc.write_req = handle_write_req;
mcs->svc.cleanup  = cleanup;
  1. Upon a write request, that is BLE_EVT_GATTS_WRITE_REQ, identify the attribute handle. In case of invalid attribute handles, the appropriate error code should be sent back to the peer device.
/* Handler for write requests, that is BLE_EVT_GATTS_WRITE_REQ */
static void handle_write_req(ble_service_t *svc,
                                    const ble_evt_gatts_write_req_t *evt)
{
        mc_service_t *mcs = (mc_service_t *) svc;
        att_error_t status = ATT_ERROR_WRITE_NOT_PERMITTED;

        /*
         * Identify for which attribute handle the write request has been sent to
         * and call the appropriate function.
         */
}
'Write Request Event Flow Chart'

Fig. 9 Write Request Event Flow Chart


  1. Upon a read request, that is BLE_EVT_GATTS_READ_REQ, identify the attribute handle. In case of invalid attribute handles, the appropriate error code should be sent back to the peer device.
/* Handler for read requests, that is BLE_EVT_GATTS_READ_REQ */
static void handle_read_req(ble_service_t *svc, const ble_evt_gatts_read_req_t *evt)
{
        mc_service_t *mcs = (mc_service_t *) svc;

        /*
         * Identify for which attribute the read request has been sent to
         * and call the appropriate function.
         */

}
'Read Request Event Flow Chart'

Fig. 10 Read Request Event Flow Chart

  1. In case of notifications/indications, how an attribute value is notified or indicated, is not defined in the core Bluetooth specifications. Thus, a custom process should be declared in order to signal all the connected peer devices about the updated characteristic value (given that notifications for that specific attribute are enabled).
'Read Request Event Flow Chart'

Fig. 11 Read Request Event Flow Chart