资讯 小学 初中 高中 语言 会计职称 学历提升 法考 计算机考试 医护考试 建工考试 教育百科
栏目分类:
子分类:
返回
空麓网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
空麓网 > 计算机考试 > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: 计算机考试归档 最新发布

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

平台:AB1565M

SDK版本:V2.11.0

开发环境:windows10

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

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


#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);

#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);

#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);

#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。


#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");

#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值来实现其订阅与否的状态管理。


typedef struct {
   const bt_uuid_t *uuid_ptr; 
   bt_atts_rec_perm_t perm;   
   uint8_t value_len;         
} 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描述对象。通常,对于一个服务下面的不同属性,可以共享同一个CCCD对象,而不必为每个属性都定义一个CCCD,通过在CCCD的回调函数中以不同的句柄来区分用户操作的是哪个属性。

 

转载请注明:文章转载自 http://www.konglu.com/
本文地址:http://www.konglu.com/it/1024743.html
免责声明:

我们致力于保护作者版权,注重分享,被刊用文章【络达开发---自定义BLE服务(一):相关数据结构讲解】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理,本文部分文字与图片资源来自于网络,转载此文是出于传递更多信息之目的,若有来源标注错误或侵犯了您的合法权益,请立即通知我们,情况属实,我们会第一时间予以删除,并同时向您表示歉意,谢谢!

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2023 成都空麓科技有限公司

ICP备案号:蜀ICP备2023000828号-2