GATT(Generic Attribute Profile)是BLE协议栈中比较靠上的一层,它基于ATT层的属性(Attribute),对BLE连接的主从双方具体的通信行为进行规范。GATT规定了主机如何给从机发消息,以及从机如何给主机发消息。
前面ATT层里面介绍了属性分组,即一个Profile下面有多个服务项,一个服务项下面有多个特征值,一个特征值下面有多个描述符,特征值是承载用户数据的载体,描述符协助特征值描绘用户数据。ATT层的这个设计是一个抽象的设计,理论上可以被其他协议借鉴或扩展,GATT就是将这个设计给具体实现。以编程的视角看,如同ATT层设计了一个结构体类型,GATT就是一个结构体变量。
GATT层主要任务有三个:
- 发现流程:客户端如何发现服务器的属性
- 客户端发起通信:客户端向服务器发数据
- 服务端发起通信:服务器向客户端发数据
GATT的操作都是围绕GATT DB(GATT数据库)进行的,GATT DB本身会涉及到ATT属性的Property(也译为“属性”)、权限、值长度等,其中权限(Permission)跟GAP层的Security Manager紧密相关。可以说GATT、GAP和ATT三层密不可分。
发现流程
使用BLE调试软件nRF Connect连接一个BLE设备,会随即列出BLE设备的服务项、特征值和描述符的信息。BLE主机获取从机的服务项等信息的过程,称为“发现(Discovery)”。
发现流程分三个步骤:
发现服务项
发现服务有三种途径:发现所有的首要服务、按照服务项UUID发现指定的首要服务,查找包含服务。
第一种途径,BLE主机向从机发送一个“按组类型读取请求(Read By Group Type Request)”,从机收到后,返回一个响应,包含了一个服务项的起始句柄和终止句柄。服务项中包含多个特征值和描述符,这些属性排列在GATT DB中,句柄值依次增加,所以起始句柄其实就是服务项自身的句柄,而终止句柄为该服务项下面的最后一个属性的句柄,越过该句柄,就是下一个服务项的领域。然后主机端再发送这样一个请求,从机再返回一个响应,等全部服务项都响应完毕,则返回错误。这样主机端就能够获取从机端的全部首要服务。
第二种途径,BLE主机发送一个“按类型值查找请求(Find By Type Value Request)”,其实就是将服务项的UUID传给从机端,从机收到后,返回该UUID对应的服务项的起始句柄和终止句柄。这对于已知服务项UUID的情况,使用这种方法处理起来显然比第一种要快。
第三种途径使用较少,这里不进行介绍。
发现特征值
发现特征值一定是发现某个服务项领域内的特征值。与前面类似,BLE主机端向从机发送一个“按类型读取请求(Read By Type Request)”,从机端返回一个响应,包含了特征值信息。在实际操作中,开发这还会根据服务项的起始和终止句柄框定特征值的查找范围,这样多次执行,就可以获得该服务项下的全部特征值的句柄信息。
发现描述符
BLE主机端向从机发送一个“查找信息请求(Find Info Request)”,从机端返回一个响应,包含了描述符信息。根据服务器的首尾句柄和特征值的句柄,可以推算出特征值的句柄范围,再通过查找信息请求,获得全部的描述符信息。显然,在固件程序中,获取全部以上信息,需要多轮循环操作,经历多次主从握手通信,即费时又易出错。Anroid开发的SDK中,将发现流程精简为一个API,一旦调用,则能获得全部的从机属性信息。有了从机端的属性结构信息,主机就可以向指定的句柄发送信息了,也可以知道从机发送过来的消息是哪个句柄触发的。
客户端发起通信
有四种类型操作:
读取特征值(Read)
读取特征值就是从机将GATT DB中的特征值数据读出来并发送给主机,所以这个操作可以分解成两部分,先从GATT DB将数据读出,再发送给主机。读取特征值数据要考虑特征值长度问题。默认的情况下MTU=23,这时读取的特征值数据长度不要超过22,否则会涉及到读取长数据。(Write属性值长度不应超过MTU-3,而Read属性值长度不应超过MTU-1,参考这个文章)
使用UUID读取特征值
BLE主机如果知道特征值的UUID,可以在不知道从机的属性结构的情况下,直接向从机发送读取指定UUID的特征值的请求,这样省却了发现的流程,快捷许多。
写入特征值(Write)
与读取特征值类似。
其他
可靠写入(Reliable Write)和签名写入(Signed Write)看介绍很厉害,但是我从未使用过,这里不做多余介绍。
服务端发起通信
只有两类:
Notification可以在任何时候从BLE从机发给主机,主机端如果有足够的缓存空间,则一直接收,否则则丢弃。Indication则在发消息后,需要等待主机给出一个确认消息,在确认之前,不能再次发出,这样保证了通信的可靠。可以看出,这些内容与ATT层的属性协议是完全对应的。
(完)
原作者:CY大象,原链接已失效