本文章将介绍在面向连接的蓝牙模式中,ATT(attribute protocol
,属性协议)和GATT(generic attribute profile
,通用属性配置文件)这两个重要的协议层,它与蓝牙的数据传输密切相关。
1 设备之间如何建立连接(Gap层)
若BLE设备之间要进行数据传输,就需要形成一个通信通道。GAP(通用访问配置文件)来负责这个通道的形成和维护。
GAP指示两台将要连接和通信的设备的角色,一台作为Central
(一般为智能手机这种功能强大的设备),另一台作为Peripheral
(一般指需要较少功率的设备)。设备之间建立连接的流程如下:
(1)当作为Peripheral
的设备准备好连接时,它必须进入广播状态并在主广播通道(37、38和39)上广播数据包
(2)作为Central
的设备必须转换到Scanning
状态,并在这三个通道上监听广播包。
(3)当检测到广播包时,Central
通过发送连接请求包来向Peripheral
发起连接请求。其中,Central
设备负责管理连接,可以直接决定连接的一些参数,其中一些参数为:
Connection Interval
:连接的间隔时间Peripheral Latency
:指定Peripheral
可以忽略多少的连接事件Channel Map
:指定将使用37个数据传输通道中的哪一个进行传输
Central
决定这些参数,以连接请求包的方式将这些参数发送到Peripheral
,此时就创建了连接。接着,Central
将在一个Connection Interval
后向Peripheral
发送数据包。如果Peripheral
接收到数据包并向Central
返回一个数据包,则认为连接已经建立。
设备建立连接后,设备之间就可以进行双向数据传输。那么,数据传输到底是如何完成的呢?这就是ATT和GATT要做的事了。
2 ATT
2.1 角色
ATT协议定义了一些角色,每个角色都有它特定的功能。这些角色都是基于Client-Server架构的。
- 服务器(Server):作为一个数据库,存储可被客户端读写的数据
- 客户端(Client):请求服务器上的数据
但有一个误区:Central
设备将作为ATT的客户端,Peripheral
设备将作为ATT的服务端。但实际上Central
和Peripheral
都既可以做服务端,也可以做客户端。比如,一个可以记录步数、心率等信息的蓝牙智能手环。当手环想向手机传输这些信息的时候,它作为服务端,而手机作为客户端;手环上可以显示当前的时间,这是由手机传过去的,此时手机作为服务端,而手环作为客户端。
2.2 作为服务端时ATT的属性
服务端设备作为一个数据库来存储需要分享给客户端的数据。ATT服务端和Mysql、Oracle这些数据库一样,需要实现:存储和组织数据的方法、访问数据的标准机制。所以,ATT协议负责为服务端设备提供客户端能读写的指定格式化的数据,以及客户端访问、写入和读取数据的机制。
1、Attributes
ATT协议定义了一个叫attribute
的数据结构,该数据结构由下列四个字段组成:
Attribute Handle
:用于给客户端查找一个服务端attribute
的uint16_t
类型的唯一标识符,也就是给attribute一个“地址”Attribute Type
(UUID) :全局唯一的2或16字节的UUID标识符,定义储存于Attribute Value
中的数值的具体类型和含义,UUID分为两种:Service UUID
和Characteristic UUID
Attribute Value
:属性值Attribute Permissions
:它表示访问Attribute Value
的权限,或者说设置一个属性的安全等级。
在服务端,数据以如下Attribute表格形式存储:
一个BLE服务端的所有Attributes都通过增加Attribute handle
的值存储在它的数据库中。
- 句柄之间可以有空隙,只要保证句柄值是按照递增顺序存储的即可
2、Attribute访问方式
ATT协议还定义了读写属性的方式。具体的方式取决于发起属性访问过程的是客户端还是服务端。
①客户端发起属性访问,有两种操作——Write
和Read
- 客户端使用
Read
从服务器读取属性的值,服务器响应属性的值。 - 客户端使用
Write
将一个属性的值写入服务器,服务器响应写操作是否成功。
②服务端发起属性访问,有两种操作——Notification
和Indication
Notification
:当Attribute发生改变时,服务端使用该种方式向客户端发送更新后的属性值,客户端收到后不响应此操作Indication
:与Notification
类似,但是客户端必须发送是否正确收到该Attribute的响应
3、Attribute访问权限
访问权限决定客户端是否可以读、写或读写其中某个属性值,可选权限如下:
- None:没有读写权限
- Readable:可读
- Writable:可写
- Readable and writable:可读写
注意:Attribute handle
和Attribute type
是公共信息,而Attribute Value
和Attribute Permissions
是私有信息。为了让客户端可以访问这两个私有信息,服务端需要进行如下操作:
- 读或写的身份验证
- 读或写的授权
- 读或写的加密和配对
从上面的分析可知,ATT协议通过增加属性的句柄数,以线性方式将数据作为Attribute存储在表中。但是,如果我们想要知道存储在服务器上的数据之间的关系,该怎么办呢?
- 关系:类似于在电脑中文件和文件夹的关系,我们直观地理解同一文件夹中的文件是相关的,这将使得在复杂的关系中的查找变得更加容易。
GATT层负责定义分层数据结构,该结构演示了ATT服务器中存储的数据之间的关系或连接。
3 GATT
GATT协议层定义了一个框架,在该框架中,服务端数据库上的Attribute可以被组织起来而有一定的层次关系。GATT层定义了一个4层树形框架,其中根节点为Profile
(配置),它有不同的Services
(服务),不同的服务有不同的Characteristics
(特征),不同的特征通过一个具体的Value
(值)或者Descriptor
(描述符)来定义,如下图所示。
1、Characteristic
Characteristic
是一个基本的存储单元,每个Characteristic
都是由value
和Descriptor(描述符)
组成的。
Characteristic Value
:实际存储的应用数据Descriptor
:该字段可选,它会存储更多关于Characteristic
的信息,比如Characteristic
的范围(如整数类型,定义其范围只能在0~100间)、Characteristic
单位等。- 一个非常常用的描述符是
Client Characteristic Configuration Descriptor (CCCD)
,它用来使/失能GATT服务端某个Characteristic
的Indications
和Notifications
功能。当使能了某个Characteristic
(CCCD
的UUID固定为0x2902)的Indications
和Notifications
功能后,每当服务端的该Characteristic
值改变的时候,客户端都可以收到通知。
- 一个非常常用的描述符是
2、Services
Services
主要用来组织数据而不是存储数据。一个Service
可以包含多个Characteristics
,比如对于蓝牙手环来说,有两个Services
:
①心率:包含两个Characteristics
,一个是心率值,一个是CCCD
②设备信息:包含一个Characteristics
,即设备信息
3、Profile
Profile
是上面分层结构的最顶层,它包含了服务端支持的特定服务。有些服务是可选的,而有些服务是必须实现的,这是为了确保两个有相同GATT profile
的设备能够相互通信。
服务端的Attribute Table
的一个例子如下图所示,每个Services
和Characteristics
都是使用ATT中的Attributes
数据结构描述的,他们都需要先声明。
同时,GATT层可以响应ATT requests
,从而实现读写服务端的数据、获得GATT服务器的primary services
和订阅/取消订阅服务端某个characteristic
的notifications
/indications
等功能。
4 总结
- ATT协议负责管理设备之间的数据存储。它为服务端提供了一种客户端可以进行读写的
Attribute
数据结构,并为客户端提供了访问、写入和读取数据的机制(访问方法和权限)。 - GATT层定义了一个层次化的数据结构,它有助于理解存储在服务端中数据(
GATT Profile
)之间的关系。