Sibles GATT Service

GATT

The Generic Attribute Profile (GATT) uses the Attribute Protocol (ATT) to define a service framework for transmitting data. GATT defines two roles: client and server.

  • The server builds a service database according to ATT data format and responds to commands and requests from the client.

  • The client discovers the server’s database and sends commands and requests to the server.

  • GATT defines procedures for discovering, reading, writing, notifying, and indicating between client and server to exchange data.

An attribute data consists of 4 parts:

  • Attribute handle: the index that specifies the attribute. The value ranges from 0x0001 to 0xFFFF.

  • Attribute type: description of the attribute handle through 16-bit/128-bit UUID. The UUID can be SIG-assigned or user-defined.

  • Attribute value: the data of the attribute.

  • Attribute permissions: indicate whether the attribute can be read or written.

Based on attributes, the service framework hierarchy is: profile, service, included service, and characteristic.

  • A profile is a high-level concept consisting of one or more services. It defines the behavior for accessing services.

  • A service consists of several included services and characteristics. It defines the data format and related behaviors.

  • An included service refers to other service definitions that exist on the server.

  • A characteristic describes detailed data format and behavior. It contains characteristic declaration, characteristic value, and characteristic descriptors.

The following is an example of the Battery Service. The Battery Service is to notify clients of battery changes. Therefore, this service has a battery level characteristic with readable and notify properties. The client can write the Client Characteristic Configuration Descriptor (CCCD) to enable notifications. Then the Battery Service will notify battery level.

Implementing GATT Service

Sibles GATT service provides formatted APIs that can be generated by tools. Users can also customize GATT services through APIs following these steps:

  1. Build GATT service and register to SIBLEs GATT service. The service will automatically save the allocated GATT handle of the user GATT service from the Bluetooth stack.

  2. Register set/get callbacks to respond to client commands. SIBLEs GATT service will notify users of client requests with the specified handle.

The following is an example of Battery Service:


// Battery Service Attributes Indexes which are mapped to GATT handles.
enum
{
    BAS_IDX_SVC,

    BAS_IDX_BATT_LVL_CHAR,
    BAS_IDX_BATT_LVL_VAL,
    BAS_IDX_BATT_LVL_NTF_CFG,

    BAS_IDX_NB,
};

typedef enum
{
    BASS_STATE_IDLE,
    BASS_STATE_READY,
    BASS_STATE_BUSY,
} ble_bass_state_t;



// Full BAS Database Description - Used to add attributes into the database
const struct attm_desc bas_att_db[BAS_IDX_NB] =
{
    // Battery Service Declaration
    [BAS_IDX_SVC]                  =   {ATT_DECL_PRIMARY_SERVICE,  PERM(RD, ENABLE), 0, 0},

    // Battery Level Characteristic Declaration
    [BAS_IDX_BATT_LVL_CHAR]        =   {ATT_DECL_CHARACTERISTIC,   PERM(RD, ENABLE), 0, 0},
    // Battery Level Characteristic Value
    [BAS_IDX_BATT_LVL_VAL]         =   {ATT_CHAR_BATTERY_LEVEL,    PERM(RD, ENABLE) | PERM(NTF, ENABLE), PERM(RI, ENABLE), 0},
    // Battery Level Characteristic - Client Characteristic Configuration Descriptor
    [BAS_IDX_BATT_LVL_NTF_CFG]     =   {ATT_DESC_CLIENT_CHAR_CFG,  PERM(RD, ENABLE) | PERM(WRITE_REQ, ENABLE), 0, 0},
};


typedef struct
{
    ble_bass_callback callback;
    sibles_hdl handle;
    uint8_t state;
    uint8_t cccd_enable;
    uint8_t bas_lvl;
} ble_bass_env_t;

static ble_bass_env_t g_bass_env_t;

static ble_bass_env_t *ble_bass_get_env(void)
{
    return &g_bass_env_t;
}

// Read callback for specified index.
static uint8_t *ble_bass_get_cbk(uint8_t conn_idx, uint8_t idx, uint16_t *len)
{
    ble_bass_env_t *env = ble_bass_get_env();
    switch (idx)
    {
    case BAS_IDX_BATT_LVL_VAL:
    {
        *len = sizeof(uint8_t);
        if (env->callback)
            env->bas_lvl = env->callback(conn_idx, BLE_BASS_GET_BATTERY_LVL);
        rt_kprintf("battery lvl %d", env->bas_lvl);
        return &env->bas_lvl;
        break;
    }
    default:
        break;
    }
    *len = 0;
    return NULL;
}

// Write callback for specified index.
static uint8_t ble_bass_set_cbk(uint8_t conn_idx, sibles_set_cbk_t *para)
{
    ble_bass_env_t *env = ble_bass_get_env();
    switch (para->idx)
    {
    case BAS_IDX_BATT_LVL_NTF_CFG:
    {
        rt_kprintf("bas enable %d", *(para->value));
        env->cccd_enable = *(para->value);
        break;
    }
    default:
        break;
    }
    return 0;
}

int8_t ble_bass_notify_battery_lvl(uint8_t conn_idx, uint8_t lvl)
{
    ble_bass_env_t *env = ble_bass_get_env();
    uint8_t ret = -1;
    if (env->state == BASS_STATE_READY)
    {
        if (env->bas_lvl != lvl)
        {
            env->bas_lvl = lvl;
            sibles_value_t value;
            value.hdl = env->handle;
            value.idx = BAS_IDX_BATT_LVL_VAL;
            value.len = sizeof(uint8_t);
            value.value = &env->bas_lvl;
            int ret = sibles_write_value(conn_idx, &value);
            ret = 0;
        }
        ret = -2;
    }
    return ret;
}



void ble_bass_init(ble_bass_callback callback, uint8_t battery_lvl)
{
    ble_bass_env_t *env = ble_bass_get_env();

    if (env->state == BASS_STATE_IDLE)
    {
        sibles_register_svc_t svc;
        
        // Provide battery database.
        svc.att_db = (struct attm_desc *)&bas_att_db;
        svc.num_entry = BAS_IDX_NB;
        svc.sec_lvl = PERM(SVC_AUTH, NO_AUTH);
        svc.uuid = ATT_SVC_BATTERY_SERVICE;
        env->handle = sibles_register_svc(&svc);
        if (env->handle)
            sibles_register_cbk(env->handle, ble_bass_get_cbk, ble_bass_set_cbk);  // Register read/write callback to respond to client access.
        env->state = BASS_STATE_READY;
    }
    env->bas_lvl = battery_lvl;
    env->callback = callback;
}


Message Flow

  • Read response in real-time

  • Read response preset

  • Write response

  • Indication