QCC300x学习笔记:自定义一个GATT client

Posted NiceBT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QCC300x学习笔记:自定义一个GATT client相关的知识,希望对你有一定的参考价值。

为了方便大家学习,现与我爱蓝牙网联合推出【QCC300x/CSR867x/QCC30xx/QCC51xx开发板】

技术交流QQ群号:743434463
开发板会员QQ群号:725398389(凭订单号入群,赠独家学习资料)
——————————正文分割线———————————–

1. 引言

这篇文章是【CSR8675学习笔记:新建一个GATT server】的兄弟篇。在前述文章中讲述了如何在CSR8675上运行一个可由手机访问的自定义GATT服务器。

本文讲述了如何在QCC3003上运行一个自定义的GATT客户端,使其能如手机一样去访问运行在其他设备上的自定义的GATT服务器。

常见的自定义GATT client的应用场景是使得QCC3003能够连接一个BLE PTT遥控器,遥控器的按键可打开或关闭蓝牙对讲。

本文介绍的方法与【QCC3003项目实战:BlueMotor6 AGHFP CVC 蓝牙对讲耳机】一起实施,可以组建一个短距无线控制的对讲设备。

2. 什么是GATT client

GATT client是一个软件概念,用于指代蓝牙BLE GATT协议中的客户端,通常作为访问者登入一个GATT server,获取服务内容,简单的模型如下:

上图中,手机端运行GATT client,嵌入式设备端运行GATT server。client向server发送请求或命令,读写server中的特征值,达到获取设备状态和操控设备的目的。

类似地,当QCC300x与PTT建立BLE连接后,QCC300x上运行的ptt GATT client即可从PTT遥控器上运行的ptt GATT server获取按键的状态。

3. 我需要怎样的GATT client

首先明确我们需要通过GATT client获取怎样的服务。在本文中以一个PTT遥控器为例,我们需要获取遥控器上的按键状态。当按键数量不超过8个,单个uint8型的特征值即足够使用。

再考虑按键事件是用户的重要输入,应以最快的速度响应,同时以尽可能低的功耗维持对用户事件的关切,因此notify机制是较合适的。即当有按键事件触发时,GATT server主动向GATT client发送通知,而非由GATT client轮询。

4. 自定义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库
    • 在lib路径下创建gatt_ptt_client文件夹,将gatt_battery_client文件夹中所有代码复制到此文件夹
    • 文件和代码全部改成以ptt为关键字,如gatt_battery_client.h改为gatt_ptt_client.h
    • 修改完成后,rebuild VM,漫长等待后,会生成libgatt_ptt_client.a
  • 修改VM工程属性
    • 查看VM工程属性,确保与BLE GATT相关的编译选项是使能的
    • 修改VM工程属性,在libraries栏加入gatt_ptt_client,在DEFINE栏加入GATT_PTT_CLIENT
  • 修改VM代码
    • 仿照sink_gatt_battery_client.c/h创建sink_gatt_ptt_client.c/h,加入到VM工程
    • 修改sink_gatt_client.c,sink_ble.c等文件,添加ptt库的初始化,消息处理等代码调用
  • 修改ADK config tool配置
    • 使能ble central功能(BLE Max Central Connection设为1)
    • 添加start ble bonding消息对应的按键
  • 重新编译VM工程,烧录到目标板

5. 连接并使用BLE PTT遥控器

目标板开机后自动初始化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));

6. 总结

实测PTT按键操控QCC300x进入退出对讲的延迟很短(<50ms),用户几乎感知不到。

原理上我们可以在创建的GATT service中定义更多字节数的特征值来支持更多的按键,或是主动读写GATT service来对LED/PIO进行控制。

当前PTT按键对BLE的带宽利用较低,后续有机会可尝试开发BLE串口透传功能。

以上是关于QCC300x学习笔记:自定义一个GATT client的主要内容,如果未能解决你的问题,请参考以下文章

QCC300x学习笔记:自定义HFP AT command

QCC300x学习笔记:自定义HFP AT command

QCC300x学习笔记:自定义HFP AT command

CSR8670学习笔记:自定义ADK configuration tool配置项

CSR8670学习笔记:自定义ADK configuration tool配置项

CSR8670学习笔记:自定义ADK configuration tool配置项