Creating static GATT database

To define a GATT Database at compile-time, several macros are provided by the GATT_DB API. These macros expand in many different ways at compilation, generating the corresponding Attribute Database on which the Attribute Protocol (ATT) may operate.

This is the default way of defining the database.

The GATT Database definition is written in two files that are required to be added to the application project together with all macro expansion files:

  • gatt_db.h - contains the actual declaration of Services and Characteristics.

  • gat_uuid128.h – contains the declaration of Custom UUIDs (16-byte wide); these UUIDs are given a user-friendly name that is used in gatt_db.h file instead of the entire 16-byte sequence.

Declaring custom 128-bit UUIDs

All Custom 128-bit UUIDs are declared in the required file gatt_uuid128.h.

Each line in this file contains a single UUID declaration. The declaration uses the following macro:

  • UUID128 (name, byte1, byte2, …, byte16)

Thename parameter is the user-friendly handle that references this UUID in the gatt_db.hfile.

The 16 bytes are written in the LSB-first order each one using the 0xZZ format.

Note: On some occasions, it is desired to reuse an 128-bit UUID declared in gatt_uuid128.h. The 16 byte array is available through its friendly name and be accessed by including gatt_db_handles.h in the application. It is strongly advised to use it only in read-only operations. For example:


(gatt_uuid128.h)
UID128(uuid_service_wireless_uart, 0xE0, 0x1C, 0x4B, 0x5E, 0x1E, 0xEB, 0xA1, 0x5C, 0xEE, 0xF4, 0x5E, 0xBA, 0x00, 0x01, 0xFF, 0x01)
(app.c)
#include "gatt_db_handles.h"
........
/* Start Service Discovery*/                   
BleServDisc_FindService(peerDeviceId, gBleUuidType128_c, (bleUuid_t*) &uuid_service_wireless_uart);

Parent topic:Creating static GATT database

Declaring a service

There are two types of Services:

  • Primary Services

  • Secondary Services - these are only to be included by other Primary or Secondary Services

The Service declaration attribute has one of these UUIDs, as defined by the Bluetooth SIG:

  • 0x2800 a.k.a. <<Primary Service>> - for a Primary Service declaration

  • 0x2801 a.k.a. <<Secondary Service>> - for a Secondary Service declaration

The Service declaration attribute permissions are read-only and no authentication required. The Service declaration attribute value contains the Service UUID. The Service Range starts from the Service declaration and ends at the next service declaration. All the Characteristics declared within the Service Range are considered to belong to that Service. For a more comprehensive list of SIG defied UUID values, check ble_sig_defines.h.

Service declaration macros

The following macros are to be used for declaring a Service:

  • PRIMARY_SERVICE (name, uuid16)

    • Most often used.

    • The name parameter is common to all macros; it is a universal, user-friendly identifier for the generated attribute.

    • The uuid16 is a 2-byte SIG-defined UUID, written in 0xZZZZ format.

  • PRIMARY_SERVICE_UUID32 (name, uuid32)

    • This macro is used for a 4-byte, SIG-defined UUID, written in 0xZZZZZZZZ format.

  • PRIMARY_SERVICE_UUID128 (name, uuid128)

    • The uuid128 is the friendly name given to the custom UUID in the gatt_uuid128.h file.

  • SECONDARY _SERVICE (name, uuid16)

  • SECONDARY_SERVICE_UUID32 (name, uuid32)

  • SECONDARY _SERVICE_UUID128 (name, uuid128)

    • All three are similar to Primary Service declarations.

Parent topic:Declaring a service

Include declaration macros

Secondary Services are meant to be included by other Services, usually by Primary Services. Primary Services may also be included by other Primary Services. The inclusion is done using the Include declaration macro:

  • INCLUDE (service_name)

    • The service_name parameter is the friendly name used to declare the Secondary Service.

    • This macro is used only for Secondary Services with a SIG-defined, 2-byte, Service UUID.

  • INCLUDE_CUSTOM (service_name)

    • This macro is used for Secondary Services that have either a 4-byte UUID or a 16-byte UUID.

The effect of the service inclusion is that the including Service is considered to contain all the Characteristics of the included Service.

Parent topic:Declaring a service

Parent topic:Creating static GATT database

Declaring a characteristic

A Characteristic must only be declared inside a Service. It belongs to the most recently declared Service, so the GATT Database must always begin with a Service declaration.

The Characteristic declaration attribute has the following UUID, as defined by the Bluetooth SIG:

  • 0x2803 a.k.a. <<Characteristic>>

The Characteristic declaration attribute value contains:

  • the Characteristic UUID

  • the Characteristic Value ’s declaration handle

  • the Characteristic Properties – Read, Write, Notify, and so on. (1 byte of flags)

The Characteristic Rangestarts from the Characteristic declaration and ends before a new Characteristic or a Service declaration.

After the Characteristic declaration these follow:

  • A Characteristic Value declaration (mandatory; immediately after the Characteristic declaration).

  • Zero or more Characteristic Descriptor declarations.

Characteristic declaration macros

The following macros are used to declare Characteristics:

  • CHARACTERISTIC (name, uuid16, properties)

  • CHARACTERISTIC_UUID32 (name, uuid32, properties)

  • CHARACTERISTIC _UUID128 (name, uuid128, properties*)***

See Service declaration for uuidXXX parameter explanation.

The properties parameter is a bit mask. The flags are defined in the gattCharacteristicPropertiesBitFields_t.

Parent topic:Declaring a characteristic

Declaring characteristic values

The Characteristic Value declaration immediately follows the Characteristic declaration and uses one of the following macros:

  • VALUE (name, uuid16, permissions, valueLength, valueByte1, valueByte2, …)

  • VALUE_UUID32 (name, uuid32, permissions, valueLength, valueByte1, valueByte2, …)

  • VALUE _UUID128(name, uuid128, permissions, valueLength, valueByte1, valueByte2, …)

    • See Declaring a service for description of the uuidXXX parameter.

    • The permissions parameter is a bit mask, whose flags are defined in gattAttributePermissionsBitFields_t .

    • The valueLength is the number of bytes to be allocated for the Characteristic Value. After this parameter, exactly [valueLength ] bytes follow in 0xZZ format, representing the initial value of this Characteristic.

These macros are used to declare Characteristic Values of fixed lengths.

Some Characteristics have variable length values. For those, the following macros are used:

  • VALUE_VARLEN (name, uuid16, permissions, maximumValueLength, initialValueLength, valueByte1, valueByte2, …)

  • VALUE_UUID32_VARLEN (name, uuid32, permissions, maximumValueLength, initialValueLength, valueByte1, valueByte2, …)

  • VALUE_UUID128_VARLEN (name, uuid128, permissions, maximumValueLength, initialValueLength, valueByte1, valueByte2, …)

    • The number of bytes allocated for this Characteristic Value is maximumValueLength.

    • The number of valueByteXXX parameters shall be equal to initialValueLength.

Obviously, initialValueLength is, at most, equal to maximumValueLength.

Parent topic:Declaring a characteristic

Declaring characteristic descriptors

Characteristic’s Descriptors are declared after the Characteristic Value declaration and before the next Characteristic declaration.

The macros used to declare Characteristic Descriptors are very similar to those used to declare fixed-length Characteristic Values:

  • DESCRIPTOR (name, uuid16, permissions, descriptorValueLength, descriptorValueByte1, descriptorValueByte2, …)

  • DESCRIPTOR_UUID32 (name, uuid32, permissions, descriptorValueLength, descriptorValueByte1, descriptorValueByte2, …)

  • DESCRIPTOR_UUID128(name, uuid128, permissions, descriptorValueLength, descriptorValueByte1, descriptorValueByte2, …)

A special Characteristic Descriptor that is used very often is the Client Characteristic Configuration Descriptor (CCCD). This is the descriptor where clients write some of the bits to activate Server notifications and/or indications. It has a reserved, 2-byte, SIG-defined UUID (0x2902), and its attribute value consists of only 1 byte (out of which 2 bits are used for configuration, the other 6 are reserved).

Because the CCCD appears very often in Characteristic definitions for standard Bluetooth Low Energy profiles, a special macro is used for CCCD declaration:

  • CCCD (name)

This simple macro is basically equivalent to the following Descriptor declaration:

* DESCRIPTOR \(name, *
    *0x2902, *
    (gGattAttPermAccessReadable_c
    | gGattAttPermAccessWritable_c),
    2, 0x00, 0x00)

Parent topic:Declaring a characteristic

Parent topic:Creating static GATT database

Static GATT database definition examples

The GAP Service must be present on any GATT Database. It has the Service UUID equal to 0x1800, <<GAP Service>>, and it contains three read-only Characteristics, no authentication required: *Device Name, Appearance,*and Peripheral Preferred Connection Parameters. These also have well defined UUIDs in the SIG documents.

Most of the demos also include the optional GATT Security Levels characteristic, which defines the highest security requirements of the GATT server when operating in a LE connection.

The definition for this Service is shown here:

**PRIMARY\_SERVICE\(service\_gap, 0x1800\) **
    **CHARACTERISTIC\(char\_device\_name, 0x2A00, \(gGattCharPropRead\_c\) \)** 
            **VALUE\(value\_device\_name, 0x2A00, \(gGattAttPermAccessReadable\_c\), ** **6, “Sensor”\)** 
    **CHARACTERISTIC\(char\_appearance, 0x2A01, \(gGattCharPropRead\_c\) \)** 
            **VALUE\(value\_appearance, 0x2A01, \(gGattAttPermAccessReadable\_c\), 2, 0xC2, 0x03\)** 
    **CHARACTERISTIC\(char\_ppcp, 0x2A04, \(gGattCharPropRead\_c\) \)** 
            **VALUE\(value\_ppcp, 0x2A04, \(gGattAttPermAccessReadable\_c\), ****8, 0x0A, 0x00, 0x10, 0x00, 0x64, 0x00, 0xE2, 0x04\)**
    **CHARACTERISTIC\(char\_security\_levels, gBleSig\_GattSecurityLevels\_d, \(gGattCharPropRead\_c\) \)
            VALUE\(value\_security\_levels, gBleSig\_GattSecurityLevels\_d, \(gPermissionFlagReadable\_c\), 2, 0x01, 0x01\)**

Another often encountered Service is the Scan Parameters Service:

**PRIMARY\_SERVICE\(service\_scan\_parameters, 0x1813\)** 
    **CHARACTERISTIC\(char\_scan\_interval\_window, 0x2A4F, \(gGattCharPropWriteWithoutRsp\_c\) \)** 
        **VALUE\(value\_scan\_interval\_window, 0x2A4F, \(gGattAttPermAccessWritable\), ** **4, 0x00, 0x00, 0x00, 0x00\)** 
    **CHARACTERISTIC\(char\_scan\_refresh, 0x2A31, \(gGattCharPropRead\_c \| gGattCharPropNotify\_c\) \)** 
        **VALUE\(value\_scan\_refresh, 0x2A31, \(gGattAttPermAccessReadable\_c\), 1, 0x00\)** **CCCD\(cccd\_scan\_refresh\)**

Note: All “user-friendly” names given in declarations are statically defined as enum members, numerically equal to the attribute handle of the declaration. This means that one of those names can be used in code wherever an attribute handle is required as a parameter of a function if gatt_db_handles.h is included in the application source file. For example, to write the value of the Scan Refresh Characteristic from the application-level code, use these instructions:

#include "gatt_db_handles.h"
...
uint8_t scan_refresh_value = 0x12;
*GattDb\_WriteAttribute\(char\_scan\_refresh, 1, &scan\_refresh\_value\);*

For static database declarations, the ‘attribute handle’ is equal to the line number in the gatt_fb.h file, where the attribute is defined.

Parent topic:Creating static GATT database

Parent topic:Creating GATT database