平台: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服务(二):功能实现

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

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

    络达开发--自定义BLE服务(一) 一.目录和工程的配置 本文讲解如何在该SDK中添加用户自居定义的BLE服务.该服务的源码可以存放在自己希望的位置,但为符合工程目录的合理性,建议放在工程所在的目录下 ...

  2. 络达开发---自定义Timer的实现

    平台:AB1565M SDK版本:V2.11.0 开发环境:windows10 络达SDK是在FreeRTOS的基础上进行构建的,因此我们可以使用该RTOS的机制来建设Timer任务. 需要在C文件中 ...

  3. 络达开发---UI定义+自定义按钮事件

    平台:AB1565M SDK版本:V2.11.0 开发环境:windows10 络达的SDK中有默认的参考工程,其中包含默认的按钮的事件定义.这些定义是基于官方的评估板进行设计的.通常用户在开发自己的 ...

  4. 络达开发-AB15XX实时查看Log方法

    源自:络达开发-AB15XX实时查看Log方法 - 知乎络达开发板为例. 开发过程中,难免要查看系统运行的实时输出的日志,在此表述如何查看. 1.需要的工具:USB转RS232-TTL,如下图所示: ...

  5. 络达开发---串口日志日志过滤

    平台:AB1565M SDK版本:V2.11.0 开发环境:windows10 采用官方ATK中的日志工具可以通过硬件物理串口来实时查看芯片中软件的运行日志.如下图所示,其实该ATK工具为一若干个工具 ...

  6. android双模蓝牙,Android和双模蓝牙设备:与BR / EDR(经典)配对时未找到自定义BLE服务...

    我使用Silicon Labs的BT 121构建了双模蓝牙设备 . 该设备通过经典连接实现SPP . 经典节点的设备名称是"XYZ Classic" . 它还在BLE中实现自定义服 ...

  7. 络达开发----如何手工实时调整ANC和PassThrough的增益

    芯片型号:AB1565 功能模块:ANC和PassThrough 功能描述:ANC和PassThrough支持动态调整其增益,比如ANC的消噪强度,或者PassThourgh的透传比例:PassTho ...

  8. 络达开发----RACE指令之---DSP中AudioLoopbackTest如何触发执行

    芯片型号:AB1565 功能模块:RACE之AudioLoopbackTest 功能描述:DSP中音频流的处理 一.指令介绍 AB1565的SDK支持串口指令的调试和控制,在SDK中叫作RACE模块, ...

  9. 络达开发-MCU中添加用户自定义功能模块

    该示例把用户自定义的功能模块放在目录:mcu\middleware\third_party\wuYinTec下面: 文件结构如下: 该示例把用户自定义的功能模块放在目录:mcu\middleware\ ...

最新文章

  1. java 贝叶斯抠图_贝叶斯抠图
  2. 支付宝的架构到底有多牛逼!还没看完我就跪了!
  3. 在Ubuntu11.10下构建hadoop实验环境笔记
  4. leetcode最小路径和 (动态规划)python
  5. SAP Spartacus CurrentProductService返回的null对象
  6. HTML期末作业-宠物网
  7. Android访问php webservice
  8. byte数组存的是什么_字节跳动为什么有字节2个字,因为程序的真谛:字节
  9. MTK 一个分支兼容多客户开机Logo方案
  10. vcpkg安装boost的一些问题,
  11. python utf编码 查询_python数据库查询中文乱码
  12. 计算机组织与体系结构课程实验心得体会
  13. 三菱plc控制步进电机实例_「PLC案例」三菱FX3U与威纶通HMI的步进定位控制,附程序图~...
  14. JS里给日期增加n个月的方法
  15. Python常用库汇总
  16. 精心整理了50个数据源网站(建议收藏)
  17. 面:【1】笔试. mgj 再菜也能做出来,,,有点信心好不好
  18. BSCI认证培训,BSCI验厂费用最终需要和审核机构来确认
  19. 金蝶软件 CITRIX无法打印解决方案
  20. 宁波诺丁汉计算机学院,宁波诺丁汉大学学子帝国理工计算机录取

热门文章

  1. MVC编程实例----简易电子商务网站(一)
  2. 关于opencv获取摄像头数据时报错问题
  3. postgresql源码学习(51)—— 提交日志CLOG 原理 用途 管理函数
  4. 力扣剑指offer53-||0~n-1中缺失的数字
  5. frida工具Jnitrace | Objection | r0tracer
  6. 校园O2O商铺平台-开发准备
  7. iOS 稳定性问题治理:卡死崩溃监控原理及最佳实践
  8. 通过CubeMX实现STM32的USB支持
  9. 并行工程的本质分析(转)
  10. “守正创新”——金融科技迈向2.0阶段