络达开发---自定义BLE服务(一):相关数据结构讲解

平台:AB1565M

SDK版本:V2.11.0

开发环境:windows10

一、BLE服务相关的几个结构定义

理解Airoha的SDK对BLE的支持,就必须要理解下面这些结构的功能,因为定义BLE服务,离不开这些结构的使用。


/**
* @brief This macro creates a 128-bit primary service.
* @param[in] name               is the name of the record.
* @param[in] uuid128_value      is the 128-bit primary service UUID.
*/
#define BT_GATTS_NEW_PRIMARY_SERVICE_128(name, uuid128_value)        \
    static const bt_gatts_primary_service_128_t name = {     \
    .rec_hdr.uuid_ptr = &BT_GATT_UUID_PRIMARY_SERVICE,      \
    .rec_hdr.perm = BT_GATTS_REC_PERM_READABLE,      \
    .rec_hdr.value_len = 16,                                \
    .uuid128 = uuid128_value                                 \
}

该结构用来定义一个主服务变量,比如你的设备支持3个自定义服务,那么就需要用该宏定义三个不同UUID的主服务变配置以便被注册到SDK中。该宏只需要一个名称和服务的UIID,注意:该UUID需要是128-bit的UUID。

比如笔者定义的服务:

#define GHP_SERVICE_UUID                \
    {{0x45, 0x4C, 0x42, 0x61, 0x68, 0x6F, 0x72, 0x69, \
      0x41, 0x03, 0xAB, 0x2D, 0x4D, 0x49, 0x19, 0x73}}

BT_GATTS_NEW_PRIMARY_SERVICE_128(ble_ghp_primary_service, GHP_SERVICE_UUID);

该结构用来定义一个主服务变量,比如你的设备支持3个自定义服务,那么就需要用该宏定义三个不同UUID的主服务变配置以便被注册到SDK中。该宏只需要一个名称和服务的UIID,注意:该UUID需要是128-bit的UUID。

比如笔者定义的服务:

#define GHP_SERVICE_UUID                \

    {{0x45, 0x4C, 0x42, 0x61, 0x68, 0x6F, 0x72, 0x69, \

      0x41, 0x03, 0xAB, 0x2D, 0x4D, 0x49, 0x19, 0x73}}


BT_GATTS_NEW_PRIMARY_SERVICE_128(ble_ghp_primary_service, GHP_SERVICE_UUID);

/**
* @brief This macro creates a 128-bit readable characteristic.
* @param[in] name              is the name of the record.
* @param[in] prop              specifies the properties of the characteristic.
* @param[in] _handle           is the handle of the characteristic value.
* @param[in] uuid              is the 128-bit characteristic UUID.
*/
#define BT_GATTS_NEW_CHARC_128(name, prop, _handle, uuid)    \
    static const bt_gatts_characteristic_128_t name = {     \
    .rec_hdr.uuid_ptr = &BT_GATT_UUID_CHARC,       \
    .rec_hdr.perm = BT_GATTS_REC_PERM_READABLE,      \
    .rec_hdr.value_len = 19,                                \
    .value.properties = prop,                               \
    .value.handle = _handle,                                 \
    .value.uuid128 = uuid                                   \
    }

我们知道一个服务可以包含若干个属性,该宏即用来定义服务的属性配置,其中参数:

Prop ------ 用来定义属性的权限;

_handle --- 是一个16-bit的值,用来定义该属性年关联的值描述对象的ID,注意:该值必须是其属性值的描述对 象在服务数组中 的索引+该服务的起始句柄的值(即starting_handle的值);

uuid------- 为128-bit的数组值;从该宏的定义是看不出来其与主服务的关系,这个关系是通过一个bt_gatts_service_rec_t 的结构来实现关联的。

如笔者定义了两个属性:

#define GHP_RX_CHAR_UUID                \
    {{0x45, 0x4C, 0x42, 0x61, 0x68, 0x6F, 0x72, 0x69, \
 0x41, 0x32, 0xAB, 0x2D, 0x52, 0x41, 0x19, 0x73}}
 
#define GHP_TX_CHAR_UUID                \
    {{0x45, 0x4C, 0x42, 0x61, 0x68, 0x6F, 0x72, 0x69, \
 0x41, 0x31, 0xAB, 0x2D, 0x52, 0x41, 0x19, 0x73}}
 
BT_GATTS_NEW_CHARC_128(ble_ghp_rx_char, BT_GATT_CHARC_PROP_WRITE_WITHOUT_RSP | BT_GATT_CHARC_PROP_WRITE, GHP_RX_CHAR_VALUE_HANDLE, GHP_RX_CHAR_UUID);
BT_GATTS_NEW_CHARC_128(ble_ghp_tx_char, BT_GATT_CHARC_PROP_NOTIFY, GHP_TX_CHAR_VALUE_HANDLE, GHP_TX_CHAR_UUID);

/**
* @brief This macro creates a characteristic value with callback.
* @param[in] name              is the name of the record.
* @param[in] uuid              is the UUID of the characteristic value.
* @param[in] _perm             is the permission of the characteristic value.
* @param[in] call              is the callback to handle the characteristic value read and write request.
*/
#define BT_GATTS_NEW_CHARC_VALUE_CALLBACK(name, uuid, _perm, call)   \
    static const bt_gatts_characteristic_t name = {                 \
    .rec_hdr.uuid_ptr = &uuid,                                      \
    .rec_hdr.perm = _perm,                                          \
    .rec_hdr.value_len = 0,                                         \
    .value.callback = call                                          \
    }

该宏用来定义一个支持写操作的属性的控制方式,如写的权限特性,写时的回调函数。其中uuid是一个bt_uuid_t类型的值而非一个类似主服务的UUID一样的宏定义;_perm为写的权限类型,包含:

BT_GATTS_REC_PERM_WRITABLE
BT_GATTS_REC_PERM_WRITABLE_ENCRYPTION
BT_GATTS_REC_PERM_WRITABLE_AUTHENTICATION
BT_GATTS_REC_PERM_WRITABLE_AUTHORIZATION

call为客户端写该属性时的回调函数。

如笔者的定义:

const bt_uuid_t GHP_RX_CHAR_UUID128 = GHP_RX_CHAR_UUID;
BT_GATTS_NEW_CHARC_VALUE_CALLBACK(ble_ghp_rx_char_value, GHP_RX_CHAR_UUID128, BT_GATTS_REC_PERM_WRITABLE, ble_ghp_rx_write_char_callback);

/**
* @brief This macro creates a const uint8 characteristic value.
* @param[in] name              is the name of the record.
* @param[in] uuid              is the UUID of the characteristic value.
* @param[in] _perm             is the permission of the characteristic value.
* @param[in] _value            is uint8 characteristic value.
*/
#define BT_GATTS_NEW_CHARC_VALUE_UINT8(name, uuid, _perm, _value)     \
    static const bt_gatts_characteristic_t name = {                 \
    .rec_hdr.uuid_ptr = &uuid,                                      \
    .rec_hdr.perm = _perm,                                           \
    .rec_hdr.value_len = 1,                                         \
    .value.value_uint_8 = _value                                     \
    }

该宏用于定义一个属性的值该值通常具有读的属性,且该值是个UINT8类型,其它类型的可以在bt_gatts.h有对应的宏。其中uuid也是一个bt_uuid_t类型的值,而非一个宏定义,_value为值的初始值。

如笔者的定义:

const bt_uuid_t GHP_TX_CHAR_UUID128 = GHP_TX_CHAR_UUID;
BT_GATTS_NEW_CHARC_VALUE_UINT8(ble_ghp_tx_char_value, GHP_TX_CHAR_UUID128, BT_GATTS_REC_PERM_READABLE, 0);

该值的配置的名称叫ble_ghp_tx_char_value。


/**
* @brief This macro creates a characteristic str16 user description descriptor.
*  IMPORTANT: The length(val_len) of val_name should be less than or equal to 16.
* @param[in] name              is the name of the record.
* @param[in] _perm             is the permission of the record.
* @param[in] val_len           is the length of the record.
* @param[in] val_name          is the name of the buffer to hold the record value.
*/
#define BT_GATTS_NEW_CHARC_USER_DESCRIPTION_STR16(name, _perm, val_len, val_name)      \
    static const bt_gatts_characteristic_user_description_str16_t name = {                 \
    .rec_hdr.uuid_ptr = &BT_GATT_UUID_CHARC_USER_DESCRIPTION,   \
    .rec_hdr.perm = _perm,                                           \
    .rec_hdr.value_len = val_len,                                        \
    .str = val_name                                                     \
    }

该宏用来给属性定义一个易读的名称,以字符串来表示,因为通过UUID实在不好识别属性。参数 val_len为字符串的长度,包含结尾符;val_name为字符的值。定义好该描述对象后,在服务的列表中排在属性描述对象的后面,可参见笔者下面的定义;

笔者的工程的的定义如下所示:

BT_GATTS_NEW_CHARC_USER_DESCRIPTION_STR16(blt_ghp_rx_desc, BT_GATTS_REC_PERM_READABLE, 3,"Rx");
BT_GATTS_NEW_CHARC_USER_DESCRIPTION_STR16(blt_ghp_tx_desc, BT_GATTS_REC_PERM_READABLE, 3,"Tx");

/**
* @brief This macro creates a client characteristic configuration descriptor.
* @param[in] name              is the name of the record.
* @param[in] _perm             is the permission of the record.
* @param[in] _callback         is the callback to handle the record read and write request.
*/
#define BT_GATTS_NEW_CLIENT_CHARC_CONFIG(name, _perm, _callback)          \
    static const bt_gatts_client_characteristic_config_t name = {       \
    .rec_hdr.uuid_ptr = &BT_GATT_UUID_CLIENT_CHARC_CONFIG,     \
    .rec_hdr.perm = _perm,                                               \
    .rec_hdr.value_len = 0,                                             \
    .callback = _callback                                                \
    }

每一个BLE服务都应该有一个对应的CCCD,用来管理服务下各个属性的诸如订阅等状态,该宏就是用来声明支持该工作的一个对象。通常_perm应该支持读和写,即:BT_GATTS_REC_PERM_READABLE|BT_GATTS_REC_PERM_WRITABLE,以便客户端可以读取当前和状态和修改状态。 如笔者的定义:

BT_GATTS_NEW_CLIENT_CHARC_CONFIG(ble_ghp_tx_client_config, BT_GATTS_REC_PERM_READABLE|BT_GATTS_REC_PERM_WRITABLE, ble_ghp_tx_char_cccd_callback);

其中回调函数ble_ghp_tx_char_cccd_callback就可以用来依据每个属性的ID值来实现其订阅与否的状态管理。


/**
 *  @brief Record structure of the GATTS.
 */
typedef struct {
   const bt_uuid_t *uuid_ptr; /**< A pointer to the UUID of attribute type. */
   bt_atts_rec_perm_t perm;   /**< The read or write permission and security requirement of the attribute, #BT_GATTS_REC_PERM_READABLE, #BT_GATTS_REC_PERM_WRITABLE. */
   uint8_t value_len;         /**< The length of the attribute value. If the value_len is 0, the record has a callback #bt_gatts_rec_callback_t(), followed by this structure. GATTS will call the record's callback to read or write the attribute value.*/
} bt_gatts_service_rec_t;

该结构用来定义一完整的BEL服务序列,前面有提到主服务和属特的关联就是用该结构来实现的。

通常一个服务会用该结构通过定义一个数组来实现,如笔者的代码:

static const bt_gatts_service_rec_t *ble_ghp_service_rec[] = {
    (const bt_gatts_service_rec_t*) &ble_ghp_primary_service, //starting_handle=0xA401
    (const bt_gatts_service_rec_t*) &ble_ghp_rx_char,         //0xA402
    (const bt_gatts_service_rec_t*) &ble_ghp_rx_char_value,   //0xA403
    (const bt_gatts_service_rec_t*) &blt_ghp_rx_desc,         //0xA404
 
    (const bt_gatts_service_rec_t*) &ble_ghp_tx_char,         //0xA405
    (const bt_gatts_service_rec_t*) &ble_ghp_tx_char_value,   //0xA406
    (const bt_gatts_service_rec_t*) &blt_ghp_tx_desc,         //0xA407
    (const bt_gatts_service_rec_t*) &ble_ghp_tx_client_config,//ending_handle=0xA408
};

该数组中的每个描述结象都有一个对应的16bit的句柄值,从数据的第一个索引依据递增;第一个索引的名柄的值应该是bt_gatts_service_t

所定义中的starting_handle的值,且最后一个索引的值也必须为ending_hadle的值,如下代码所示。所以这里也解释了上面的GHP_RX_CHAR_VALUE_HANDLE的值为什么是0xA403,因为ble_ghp_rx_char属性对应的值的描述对象的句柄的值也为0xA403。

const bt_gatts_service_t ble_ghp_service = {
    .starting_handle = 0xA401,
    .ending_handle   = 0xA408,
    .required_encryption_key_size = 0,
    .records = ble_ghp_service_rec
};

二、BLE服务与属性的相关定义

一个服务通常包含如下几个定义:

属性值(Characteristic Value)

data value of the characteristic

属性描述(Characteristic Declaration)

descriptor storing the properties, location, and type of the characteristic value

用户描述字符串(an ASCII string describing the characteristic)

an ASCII string describing the characteristic[该描述符用来给属性定义一个易理解的字符串名称].

CCCD(Client Characteristic Configuration)

a configuration that allows the GATT server to configure the characteristic to be notified (send message asynchronously) or indicated (send message asynchronously with acknowledgment)

如下图是一个服务及其所包含特性属性的定义:

 

因此,上文提到的结构 bt_gatts_service_rec_t所定义的数组,其定义的顺序通道是这样的:

因此,上文提到的结构 bt_gatts_service_rec_t所定义的数组,其定义的顺序通道是这样的:
bt_gatts_service_rec_t xxx[]={
	服务描述符,
	属性1描述符,
	属性1的值描述符,
	属性1的用户字符串描述符[可选],
	属性1的CCCD描述符[可选],
	属性2描述符,
	属性2的值描述符,
	属性2的CCCD描述符[可选],
	属性3描述符,
	属性3的值描述符,
	属性3的CCCD描述符[可选],
	......
}

如笔者所定义的代码:

如笔者所定义的代码:
static const bt_gatts_service_rec_t *ble_ghp_service_rec[] = {
    (const bt_gatts_service_rec_t*) &ble_ghp_primary_service, //starting_handle=0xA401
    (const bt_gatts_service_rec_t*) &ble_ghp_rx_char,         //0xA402
    (const bt_gatts_service_rec_t*) &ble_ghp_rx_char_value,   //0xA403
    (const bt_gatts_service_rec_t*) &ble_ghp_rx_desc,         //0xA404
    
    (const bt_gatts_service_rec_t*) &ble_ghp_tx_char,         //0xA405
    (const bt_gatts_service_rec_t*) &ble_ghp_tx_char_value,   //0xA406
    (const bt_gatts_service_rec_t*) &ble_ghp_tx_desc,         //0xA407
    (const bt_gatts_service_rec_t*) &ble_ghp_tx_client_config,//ending_handle=0xA408
};

其中ble_ghp_rx_char并不支持notify,所以也就没有添加CCCD相关的描述对象;但ble_ghp_tx_char是支持订阅的,所以在该属性的后面追加有CCCD描述对象。


络达开发---自定义BLE服务(二):功能实现

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值