GATT详细介绍

我们经常看到下图,读图时候我们会讲一个GATT Profile包含多个服务,一个服务包含多个特征,一个特征包含多个描述符,但是真正落到细节层面上,又有些模棱两可,比如为什么特征里面有个Declaration,又有个Value。当我们发数据时候是发给Declaration还是发给Value?

本文介绍GATT的技术细节。

1. Attribute

BLE 中的GATT 指Generic Attribute, ATT 指Attribute。

BLE 协议栈有ATT层和GATT层,ATT 层定义了一套基础数据结构,GATT 层基于ATT 层定义了一套通信交互规范。蓝牙协议规范的Vol 3的Part F和Part G有详尽介绍。

Attribute不能翻译成属性或特征,会与Property和Characteristic冲突,有的文档将其翻译成特性,读起来总觉词不达意,这里尽可能保持使用Attribute或其简写形式(ATT)。

ATT 是BLE 协议对现实世界实体的数据描述形式,不同的BLE 设备,其实就是它们的ATT 定义不同。

一个ATT 包含以下元素:

  • 类型(UUID)
  • 句柄(Handle)
  • 权限(Permission)
  • 值(Value)

UUID是一个16字节的唯一码,如果两个ATT 拥有相同的UUID,就认为它们是同一类ATT,因此使用UUID 来表征ATT 的类型。

蓝牙协议规范设定了一个UUID 模板(0000xxxx-0000-1000-8000-00805f9b34fb),第三四字节为占位符,这样只要拿一个双字节数填充占位符,即可产生一个16字节UUID。于是我们看到了许多双字节UUID。

常见的ATT 有:服务(Primary Service)、特征(Characteristic)和描述符。

他们拥有各自固定的UUID,比如服务的UUID为0x2800,特征的UUID为0x2803。

等下!特征的UUID不是用户自己定义的吗,为什么是固定的0x2803?

2. Declaration of Attribute

特征其实是个集合,特征包含以下子元素:

  • 特征声明(Characteristic Declaration)
  • 特征值声明(Characteristic Value Declaration)
  • 特征描述符声明(Characteristic Descriptor Declaration)

其中描述符是可选项,可能包含一个或多个描述符,也可能不包含描述符。

我们通常说:

BLE 的特征是BLE 设备与外界通信的接口,当手机与BLE 设备通信,其实都是与某个具体的特征进行读写。

这句话中的“特征”其实是指特征值。

特征值也是一种Attribute,所以特征值也有Value元素(Characteristic Value also has a value)。

在绝大多数情况下,我们说特征其实就是说特征值。 所以我们说特征的UUID,其实是特征值的UUID,它是用户自定义的。上面说特征的UUID为固定的0x2803,它其实是特征声明。

类似的,服务也是一个集合,服务包含:

  • 服务声明(Service Declaration)
  • 特征

上面说服务的UUID是固定的0x2800,其实是指服务声明(Service Declaration)的UUID。

3. Value of Attribute

服务声明的Value包含了服务的UUID。

所以,服务声明作为一个Attribute,它自己的UUID 是0x2800。它的Value 中包含一个UUID 值,这个UUID 是整个服务的UUID。这个UUID 可以是双字节,也可以是16字节。

特征声明的Value 包含特征值的属性、Handle 和特征值的UUID。

属性的可选项主要包括:Read(0x02), Write_no_resp(0x04), Write(0x08), Notify(0x10), Indicate(0x20),还有几个非常少见,这里略过。

一个特征可以有多个属性,通过或运算组合起来,比如Read + Write 属性,就是0x02 | 0x08 = 0x10。

句柄比较简单,无论服务还是特征,他们的句柄都是依次加1的。

这里特征值的UUID 也就是开发时候设定的UUID。

特征值的Value里面其实就是通信时候的传输的数据。

特征值的Value的数据长度是可变的,但是有个最大值限制,我们将这个限制称为MTU(Maximum Transmission Unit),特征值的Value数据长度最大为MTU – 3。如果MTU = 23,特征值的Value数据长度最大是23,如果MTU=247,则最大是244。

这个MTU也不是胡乱设定,如果链路层(Link Layer)支持长包特性,Data Length = 251,那么MTU可以取247,否则MTU就算取很大,在链路层仍然是以20字节小包拆开发送。

描述符的Value要根据描述符的类型,对于最常用的CCCD,如果Notify和Indicate都被禁用,它就是0x0000。

4. Permission of Attribute

当我们提起权限,总是会想起:只读权限、只写权限和读写权限。它们三个也是最常用的选项。

有个极端选项:NO_ACCESS,表示完全不能读写。

如果BLE 设备要求配对绑定,则读写权限需要与以下几种选项产生搭配:

  1. No_Encryption
  2. Encryption_no_MITM
  3. Encryption_with_MITM

No_Encryption 表示不在乎配对与否。

Encryption_no_MITM 表示设备需要建立配对关系,但是不强求MITM。它对应采用Just Works配对方式。

Encryption_with_MITM 表示要求设备建立MITM的配对关系。它对应采用Passkey配对方式。

5. 示例

往nRF52开发板中下载一个ble_app_blinky 的固件,用手机连接它,它的GATT服务长这个样子:

它包含三个服务,前两个服务是默认的服务,这里只关注第三个服务。

在芯片内存中,第三个服务的各项数据均保存下来,以表格的形式列出来:

GATT AttributeHandleUUIDValuePermission
Primary Service Declaration0x000B0x280000001523-1212-EFDE-1523-785FEABCD123Read
Characteristic Declaration0x000C0x28030x12, 0x000D, 00001524-1212-EFDE-1523-785FEABCD123Read
Characteristic Value0x000D00001524-1212-EFDE-1523-785FEABCD123User DataRW
Descriptor (CCCD)0x000E0x29020x0000RW
Characteristic Declaration 0x000F0x28030x0A, 0x0010, 00001524-1212-EFDE-1523-785FEABCD123Read
Characteristic Value 0x001000001525-1212-EFDE-1523-785FEABCD123 User DataRW

第二行是服务声明,它的Handle是0x000B,因为第一个服务的Handle是0x0001,依次递增下来就是它。它的类型UUID是0x2800,表明它是一个Primary Service。从Value中可以读出这个服务的自定义UUID,它是一个Blinky的UUID。

第三行到第五行是Button特征,第六行是LED特征。

第三行是特征声明,观察它的Value,0x12等于(0x04 | 0x08),他表示该特征具有Read(0x04)和Write(0x08)两个属性。第二个数0x000D表示特征值的Handle,它比特征声明的Handle大1。第三个数是特征值的UUID。

第五行是一个CCCD(Client Characteristic Configuration Descriptor)描述符,它是Notify和Indicate属性必须的描述符。因为它太常见、所以它的UUID(0x2902)值得记住,能在许多时候帮助我们快速定位到它。

(完)