上文中实现了一个GATT服务,其实就是将NUS改了改名字,本文深入到ble_yqs代码中,梳理它的实现脉络。
(1)UUID
作为一个自定义服务,一定会有一个128-bit的UUID。
在ble_yqs.c中,定义了一个基础UUID(YQS_BASE_UUID):
#define YQS_BASE_UUID {{0x9D, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E}} /**< Used vendor specific UUID. */
注意倒数第三、四位是0x0000,这两个字节为占位符,运行时将根据Service UUID和Characteristic UUID进行填充。
初始化时候,要先将这个基础UUID写入协议栈:
sd_ble_uuid_vs_add(&yqs_base_uuid, &p_yqs->uuid_type);
同时定义了双字节的Service和Characteristic的UUID:
#define BLE_UUID_YQS_SERVICE 0x0001 /**< The UUID of the Nordic UART Service. */
#define BLE_UUID_YQS_RX_CHARACTERISTIC 0x0002 /**< The UUID of the RX Characteristic. */
#define BLE_UUID_YQS_TX_CHARACTERISTIC 0x0003 /**< The UUID of the TX Characteristic. */
最终生成的UUID如下:
Service UUID={0x9D, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x01, 0x00, 0x40, 0x6E}
RX Char UUID={0x9D, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x02, 0x00, 0x40, 0x6E}
TX Char UUID={0x9D, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x03, 0x00, 0x40, 0x6E}
(2)添加服务
sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,&ble_uuid,&p_yqs->service_handle);
传入参数为服务的类型和UUID,最后一个参数为传出参数,记录这个服务的句柄。
在该服务下添加特征都将围绕这个服务句柄(service_handle)进行。
(3)添加特征
sd_ble_gatts_characteristic_add(p_yqs->service_handle,&char_md,&attr_char_value,&p_yqs->rx_handles);
传入参数为服务句柄、特征的特性以及特征值,最后一个参数为传出参数,记录这个特征的句柄。
特征的相关参数比较繁杂,至少需要了解以下知识点:
- write/write_no_response/notify/indicate/read
- CCCD
- authorization required or not
- encryption with MITM or not
- value length and offset
- MTU size
解释清楚这些细节需要一篇专门的文章,这里略过。
而在ble_yqs.c中,RX Characteristic实现了write和write no reponse两个写属性,表示主机(Central)可以对该特征进行写操作。TX Characteristic实现了notify的属性,表示主机可以接收该特征发出的数据。
(4)接收数据
YQS接收数据是指主机向RX Characteristic发送数据,将产生BLE_GATTS_EVT_WRITE事件,进一步触发on_write()回调函数。
在回调函数中,根据特征句柄的不同,处理两件事:
- 处理CCCD使能信号
- 处理用户write数据
用户的write数据进一步产生BLE_YQS_EVT_RX_DATA事件,触发外部回调事件。
(5)发送数据
发送数据即执行notify操作。主机端使能notify以后,才能够执行notify。
notify采用API:sd_ble_gatts_hvx
函数,值得注意的是,发送数据的长度要小于MTU Size,否则将触发NRF_ERROR_INVALID_PARAM错误。
所有数据发送完毕触发BLE_GATTS_EVT_HVN_TX_COMPLETE事件。
(6)声明YQS
从SDK 14起,各种服务都称为一个个“观察者(Observer)”,声明观察者的操作都是宏的形式,如下:
BLE_YQS_DEF(m_yqs);
它需要放在全局变量位置。
(7)初始化YQS
所有的GATT服务都放在services_init()
中初始化。
初始化时候要指定事件处理的回调函数,这里是yqs_data_handler
。
(8)事件处理
在回调函数yqs_data_handler
中,处理在ble_yqs.c中定义好的事件,从而告诉主程序数据收发的节点。
(完)
1 Response
[…] 解读自定义GATT服务 2018-03-17 […]