为了方便大家学习,现与我爱蓝牙网联合推出【QCC300x/CSR867x/QCC30xx/QCC51xx开发板】。
技术交流QQ群号:743434463
开发板会员QQ群号:725398389(凭订单号入群,赠独家学习资料)
——————————正文分割线———————————–
这篇文章是【CSR8675学习笔记:新建一个GATT server】的兄弟篇。在前述文章中讲述了如何在CSR8675上运行一个可由手机访问的自定义GATT服务器。
本文讲述了如何在QCC3003上运行一个自定义的GATT客户端,使其能如手机一样去访问运行在其他设备上的自定义的GATT服务器。
常见的自定义GATT client的应用场景是使得QCC3003能够连接一个BLE PTT遥控器,遥控器的按键可打开或关闭蓝牙对讲。
本文介绍的方法与【QCC3003项目实战:BlueMotor6 AGHFP CVC 蓝牙对讲耳机】一起实施,可以组建一个短距无线控制的对讲设备。
GATT client是一个软件概念,用于指代蓝牙BLE GATT协议中的客户端,通常作为访问者登入一个GATT server,获取服务内容,简单的模型如下:
上图中,手机端运行GATT client,嵌入式设备端运行GATT server。client向server发送请求或命令,读写server中的特征值,达到获取设备状态和操控设备的目的。
类似地,当QCC300x与PTT建立BLE连接后,QCC300x上运行的ptt GATT client即可从PTT遥控器上运行的ptt GATT server获取按键的状态。
首先明确我们需要通过GATT client获取怎样的服务。在本文中以一个PTT遥控器为例,我们需要获取遥控器上的按键状态。当按键数量不超过8个,单个uint8型的特征值即足够使用。
再考虑按键事件是用户的重要输入,应以最快的速度响应,同时以尽可能低的功耗维持对用户事件的关切,因此notify机制是较合适的。即当有按键事件触发时,GATT server主动向GATT client发送通知,而非由GATT client轮询。
ADK原生支持的GATT client有很多,battery client是必不可少的,其功能是读取目标设备的当前电量或接收目标设备电量变化的通知。本文提到的GATT PTT client的原型即是GATT Battery client。
GATT Battery client的源码在ADK的src/lib/gatt_battery_client路径中。为了创建GATT PTT client,需要按如下步骤操作:
目标板开机后自动初始化GATT client库,但还不会自动搜索PTT遥控器,需按键触发start ble bonding消息,QCC300x即进入central模式,开始快速扫描周围是否有包含PTT server的BLE peripheral,相关log如下:
initialise GATT Manager - with servers
Calculate memory size for server tasks:
+GATT=[3]
+GAP=[6]
+Optional=[6]
GATT Server registered
GATT GAP Server initialised
GAP new state=[IDLE]
GAP Bonded to private device=[0]
GattPtt: Add PTT advertising filter
GAP new event=[POWER_ON] state=[IDLE]
GAP new event=[POWER_ON] state=[IDLE]
GAP new event=[BONDABLE] state=[IDLE]
GAP gapStartBonding
GAP new state=[BONDED_SCAN_ADV]
The Max Configuration for [CENTRAL] role is 1
GAP gapStartBondableConnectionTimer timeout=[60 s]
GAP new event=[CENTRAL_CONNECT_ATTEMPT] state=[BONDED_SCAN_ADV]
QCC300x扫描到包含PTT server的设备后,开始尝试发起连接:
GAP Central conn attempt=[0]
GAP Central conn attempt=[0]
GAP new state=[BONDABLE_CONNECTING]
GAP gapStopBondableConnectionTimer
GAP Connect To Any Device
GAP connect attempt whitelist=[0]:
connect_addr=[(0) 2:5b:1529]
current_addr=[(0) 2:5b:1529]
permanent_addr=[(0) 2:5b:1529]
GAP Scanning fast=[1]
GAP Set Master Conn Attempt =[1]
CL_DM_LOCAL_NAME_COMPLETE
Set advertising data bondable=[0]
GAP new event=[CANCELLED_ADV] state=[BONDABLE_CONNECTING]
GAP event ignored in state
GAP new event=[BOND_CONN_TIMEOUT] state=[BONDABLE_CONNECTING]
GAP event not handled in state
GAP new event=[CENTRAL_CONNECT_ATTEMPT] state=[BONDABLE_CONNECTING]
GAP event not handled in state
GAP new event=[SET_ADV_COMPLETE] state=[BONDABLE_CONNECTING]
GAP event not handled in state
The Max Configuration for [CENTRAL] role is 1
GAP Set Master Conn Attempt =[1]
Mem Alloc (Gatt Client - Services): size[15] addr[1b00]
Mem Alloc (Gatt Client - Discovery): size[27] addr[2100]
GATT_EXCHANGE_MTU_CFM
QCC300x发现PTT遥控器支持的所有首要服务,记录服务句柄,留作后续分别获取每个服务的次要服务以及特征值,可以看出获取到GATT/PTT/BAS服务:
(Gatt Client - Connection): addr[1c1b]
GATT_DISCOVER_ALL_PRIMARY_SERVICES_CFM
cid[0xc0] Start[0x1] End[0x7] more[1]
(Gatt Client - Connection): addr[1c1b]
Gatt Client Store Discovered Service, uuid 0x1800
GATT_DISCOVER_ALL_PRIMARY_SERVICES_CFM
cid[0xc0] Start[0x8] End[0x8] more[1]
(Gatt Client - Connection): addr[1c1b]
Gatt Client Store Discovered Service, uuid 0x1801
Gatt Client Storing GATT Service handles
Mem Re-Alloc (Gatt Client - Services): size[20] addr[1b00]
GATT_DISCOVER_ALL_PRIMARY_SERVICES_CFM
cid[0xc0] Start[0x9] End[0xc] more[1]
(Gatt Client - Connection): addr[1c1b]
Gatt Client Store Discovered Service, uuid 0x5696
Gatt Client Storing PTT Service handles
Mem Re-Alloc (Gatt Client - Services): size[27] addr[1b00]
GATT_DISCOVER_ALL_PRIMARY_SERVICES_CFM
cid[0xc0] Start[0xd] End[0xffff] more[0]
(Gatt Client - Connection): addr[1c1b]
Gatt Client Store Discovered Service, uuid 0x180f
Gatt Client Storing BAS Service handles
Mem Re-Alloc (Gatt Client - Services): size[34] addr[1b00]
(Gatt Client - Connection): addr[1c1b]
(Gatt Client - Size of Services): size[5] addr[1b0e]
Add gatt client service; success[0] cid[0xc0] start[0x8] end[0x8]
gattClientProcessSecurityRequirements: is_security_required = 0
获取所有次要服务和特征值,完成notify事件的注册:
GATT Discovered Service: cid[0xc0] index[0]
service[6] start[0x8] end[0x8]
(Gatt Client - Connection): addr[1c1b]
GATT Discovered Service: cid[0xc0] index[1]
service[9] start[0x9] end[0xc]
(Gatt Client - Connection): addr[1c1b]
(Gatt Client - Size of Services): size[12] addr[1b13]
GATT_PTT_CLIENT_INIT_CFM status[0]
GATT_PTT_CLIENT_READ_BTN_VAL_CFM status[2] value[0]
GATT_PTT_CLIENT_SET_NOTIFICATION_ENABLE_CFM status[0]
GATT Discovered Service: cid[0xc0] index[2]
service[1] start[0xd] end[0xffff]
(Gatt Client - Connection): addr[1c1b]
(Gatt Client - Size of Services): size[19] addr[1b1a]
GATT_BATTERY_CLIENT_INIT_CFM status[0]
GATT_PTT_CLIENT_READ_DESCRIPTOR_CFM status[3] uuid[10498] size[0]
]GATT_BATTERY_CLIENT_READ_LEVEL_CFM status[0] level[100]
GATT Battery RC level cache=[100]
GATT_BATTERY_CLIENT_SET_NOTIFICATION_ENABLE_CFM status[0]
GATT Primary Services Initialised
GATT Client Discovery Complete
Mem Free (Gatt Client): addr[2100]
(Gatt Client - Connection): addr[1c1b]
GAP new event=[CENTRAL_CONNECT_COMPLETE] state=[BONDABLE_CONNECTING]
GAP Set Master Conn Attempt =[0]
The Max Configuration for [CENTRAL] role is 1
GAP new state=[SCAN_ADV]
The Max Configuration for [PERIPHERAL] role is 0
GAP new state=[SCAN_ADV]
GATT_BATTERY_CLIENT_LEVEL_IND level[100]
GATT Battery RC level cache=[100]
GATT_BATTERY_CLIENT_READ_DESCRIPTOR_CFM status[0] uuid[10498] size[2]
[0x1][0x0]
当有PTT按键触发后,QCC300x可从notify事件中得知按键值的变化:
GATT_PTT_CLIENT_BTN_VAL_IND value[4]
GATT Ptt RC value cache=[4]
GATT_PTT_CLIENT_BTN_VAL_IND value[12]
GATT Ptt RC value cache=[12]
GATT_PTT_CLIENT_BTN_VAL_IND value[8]
GATT Ptt RC value cache=[8]
GATT_PTT_CLIENT_BTN_VAL_IND value[12]
GATT Ptt RC value cache=[12]
对应到VM代码中,当收到此IND消息后,可发送系统消息以打开/关闭对讲:
static void gattPttButtonValueInd(const GATT_PTT_CLIENT_BTN_VAL_IND_T *ind)
{
GATT_PTT_CLIENT_INFO(("GATT_PTT_CLIENT_BTN_VAL_IND value[%u]\n", ind->button_value));
if (ind->button_value == 0x04)
{
MessageSend(&theSink.task,EventSysIntercomOnOffSwitch,NULL);
}
gattPttClientSetCachedValue(ind->button_value, gattPttClientFindCid(ind->ptt_client));
}
实测PTT按键操控QCC300x进入退出对讲的延迟很短(<50ms),用户几乎感知不到。
原理上我们可以在创建的GATT service中定义更多字节数的特征值来支持更多的按键,或是主动读写GATT service来对LED/PIO进行控制。
当前PTT按键对BLE的带宽利用较低,后续有机会可尝试开发BLE串口透传功能。