Creating a Custom Profile

This chapter describes how the user can create customizable functionality over the Bluetooth Low Energy Host Stack by defining profiles and services. The Temperature Profile, used by the Temperature Sensor and Collector applications, is used as a reference to explain the steps of building custom functionality.

Defining custom UUIDs

The first step when defining a new service included in a profile is to define the custom 128-bit UUID for the service and the included characteristics. These values are defined in gatt_uuid128.h, which is located in the application folder. For example, the Temperature Profile uses the following UUID for the service:

/* Temperature */
UUID128(uuid_service_temperature, 0xfb ,0x34 ,0x9b ,0x5f ,0x80 ,0x00
,0x00 ,0x80 ,0x00 ,0x10 ,0x00 ,0x02 ,0x00 ,0xfe ,0x00 ,0x00)

The definition of the services and characteristics are made in gattdb.h, as explained in Creating GATT database. For more details on how to structure the database, see Application Structure.

Parent topic:Creating a Custom Profile

Creating service functionality

All defined services in the SDK have a common template which helps the application to act accordingly.

The service locally stores the device identification for the connected client. This value is changed on subscription and non-subscription events.

/*! Temperature Service - Subscribed Client*/
**static** deviceId_t mTms_SubscribedClientId;

The service is initialized and changed by the application through a service configuration structure. It usually contains the service handle, initialization values for the service (for example, the initial temperature for the Temperature Service) and in some cases user-specific structures that can store saved measurements (for example, the Blood Pressure Service). Below is an example for the custom Temperature Service:

/*! Temperature Service - Configuration */
**typedef** **struct** tmsConfig_tag
{
    uint16_t serviceHandle ;
    int16_t initialTemperature ;
} tmsConfig_t ;

The initialization of the service is made by calling the start procedure. The function requires as input a pointer to the service configuration structure. This function is usually called when the application is initialized. It resets the static device identification for the subscribed client and initializes both dynamic and static characteristic values. An example for the Temperature Service (TMS) is shown below:

bleResult_t **Tms\_Start** ( tmsConfig_t *pServiceConfig)
{
    mTms_SubscribedClientId = gInvalidDeviceId_c;
    **return** Tms_RecordTemperatureMeasurement (pServiceConfig-> serviceHandle ,
                                                    pServiceConfig-> initialTemperature );
}

The service subscription is triggered when a device connects to the server. It requires the peer device identification as an input parameter to update the local variable. On disconnect, the unsubscribe function is called to reset the device identification. For the Temperature Service:

bleResult_t **Tms\_Subscribe** ( deviceId_t deviceId)
{
    mTms_SubscribedClientId = deviceId;
    **return** gBleSuccess_c;
}
bleResult_t **Tms\_Unsubscribe** (void)
{
    mTms_SubscribedClientId = gInvalidDeviceId_c;
    **return** gBleSuccess_c;
}

Depending on the complexity of the service, the API implements additional functions. For the Temperature Service, there is only a temperature characteristic that is notifiable by the server. The API implements the record measurement function which saves the new measured value in the GATT database and send the notification to the client device if possible. The function needs the service handle and the new temperature value as input parameters:

bleResult_t **Tms\_RecordTemperatureMeasurement** ( uint16_t serviceHandle, int16_t temperature)
{
    uint16_t handle;
    bleResult_t result;
    bleUuid_t uuid = Uuid16(gBleSig_Temperature_d);
    /* Get handle of Temperature characteristic */
    result = GattDb_FindCharValueHandleInService(serviceHandle,
        gBleUuidType16_c, &uuid, &handle);
    **if** (result != gBleSuccess_c)
        **return** result;
    /* Update characteristic value */
    result = GattDb_WriteAttribute(handle, **sizeof**( uint16_t ), ( uint8_t *)&temperature);
    **if** (result != gBleSuccess_c)
        **return** result;
    Hts_SendTemperatureMeasurementNotification(handle);
    **return** gBleSuccess_c;
}

To accommodate some use cases where the service is reset, the stop function is called. The reset also implies a service unsubscribe. Below is an example for the Temperature Service:

bleResult_t **Tms\_Stop** ( tmsConfig_t *pServiceConfig)
{
    **return** Tms_Unsubscribe();
}

Parent topic:Creating a Custom Profile

GATT client interactions

The client side of the service, which includes the service discovery, notification configuration, attribute reads and others are left to be handled by the application. The application calls the GATT client APIs and reacts accordingly. The only exception for this rule is that the service interface declares the client configuration structure. This structure usually contains the service handle and the handles of all the characteristic values and descriptors discovered. Additionally it can contain values that the client can use to interact with the server. For the Temperature Service client, the structure is as follows:

/*! Temperature Client - Configuration */
**typedef** **struct** tmcConfig_tag
{
    uint16_t                 hService;
    uint16_t                 hTemperature ;
    uint16_t                 hTempCccd ;
    uint16_t                 hTempDesc ;
    gattDbCharPresFormat_t   tempFormat ;
} tmcConfig_t ;

Parent topic:Creating a Custom Profile