新闻  |   论坛  |   博客  |   在线研讨会
BLE 5协议栈-通用属性规范层(GATT
电子禅石 | 2021-05-15 13:48:14    阅读:8312   发布文章

BLE 5协议栈-通用属性规范层(GATT) - yeshenmeng - 博客园 (cnblogs.com)


文章转载自:http://www.sunyouqun.com/2017/04/page/2/

通用属性规范GATT(Generic Attribute Profile)将ATT层定义的属性打包成不同的属性实体,包括服务项、特征项和描述符,这些属性实体组合在一起组成规范,即GATT规范。GATT规范是服务项的集合,服务项是特征项的集合,特征项携带了属性参数和数据,描述符协助特征项描述特征值的形式和功能。

GATT层按照命令的传输方向将设备分成GATT客户端和GATT服务端。客户端发起命令,服务端发出数据。GATT规范定义了客户端设备发现服务端设备的服务项的方法,建立连接以后,客户端设备可以通过发现方法检索服务端设备的GATT服务项和特征项,进而发送命了或数据。

服务端向客户端发送数据以通知和指示的形式发送,客户端收到指示信息需要返回确认信息。

服务端可以向客户端发送通知和指示,客户端按需返回响应。

1. 属性1.1 GATT角色

客户端:设备发起命令、请求并接受响应、通知和指示。

服务端:设备接收命令、 请求并发出响应、通知和指示。

设备可以同时属于客户端和服务端。

GATT角色与执行过程相关,它不与设备绑定。设备在执行一个过程时,根据发起命令或接收命令而决定它是服务端还是客户端,该过程结束后就释放GATT角色。

GATT角色不与链路层的主机和从机角色绑定。一个链路层的主机,通常担任GATT客户端角色,也可以担任GATT服务端角色。

1.2 属性PDU

属性实体PDU如下:

字段Attribute HandleAttribute TypeAttribute ValueAttribute Permissions
长度2 octets2 or 16 octetsvariableimplementation specific

一个属性包含四个字段:属性句柄、属性类型、属性值和属性权限。

属性句柄用于指定具体的属性。属性句柄有效范围为0x0000-0xFFFF,属性句柄按步进1的增序排列,但有时可能会出现空缺。

属性类型为2字节、4字节或16字节的UUID。 如果是4字节UUID,在封装成属性PDU时根据蓝牙基础UUID转换成16字节标准UUID。

属性值字段包含了属性的具体数据。

属性权限决定了属性是否可读或可写。

1.3 属性协议PDU

两个设备属性层之间根据属性协议传输数据,属性协议包括几种类型:命令、请求、响应、通知、指示和确认。

属性协议PDU如下:

字段OpcodeAttribute ParametersAuthentication Signature
长度1 octetvariable12 octets

操作码Opcode决定了该PDU的操作过程类型。另外,操作码中包含一个认证标志位。

属性参数中包含了命令或请求的参数,或响应的数据。

最后字段的认证签名为可选字段,仅用于带签名的写操作,当操作码的认证标志位为1,则需要认证签名字段,否则不需要改字段。

1.4 属性缓存

客户端与服务端建立连接后,执行发现过程,以获取服务端所携带的全部属性。属性缓存功能用于保存服务端设备的属性句柄,使下一次重新连接时无需执行发现过程。

一般情况下,服务端设备的属性不会改变,但是执行固件升级则可以改变设备的属性。

如果改变设备的属性,将从Service Changed characteristic发出一个指示PDU,告知客户端设备服务端设备的属性发生了改变。该指示PDU中包含了发生改变的属性句柄范围,客户端设备收到该指示,重新执行发现过程,获取更新后的服务端设备的属性句柄。

如果设备的属性确定不能发生改变,则无需增加Service Changed characteristic属性。

如果两端设备完成绑定,则属性缓存信息一直有效,直到收到了Service Changed characteristic发出的指示。如果在服务端的属性在断开后发生了改变,则服务端在下次重连时候发送指示给客户端设备重新缓存属性句柄。

1.5 属性分组

GATT定义了三种属性分组:主要服务、次要服务和特征项。

一个属性分组包括声明和定义。

主要服务和次要服务可以使用“按组类型读取”请求获得,特征项不可以。

1.6 属性结构

蓝牙协议中包含了许多种GATT规范,每个规范适配一种用户案例,比如FindMe规范适配查找物件的场景,心率传感器规范适配心率测量场景。

每个规范均中均有若干服务项和特征项,服务项和特征项都属于属性实体,它们携带了通信中传输的数据。

服务项分为主要服务和次要服务,主要服务可以引用(Include)另一个主要服务或次要服务,客户端设备可以通过“主要服务发现过程”获取主要服务信息。

特征项包括一个声明、配置、数据和描述符。描述符用于描述特征项的数据如何被访问和展示。

规范、服务项和特征项之间有明确的包含关系,一个GATT规范中可以包括多个服务项,一个服务项中可以包括多个特征项。

GATT的规范结构框图如下:

GATT_Profile_Structure

2. 属性类型

属性的类型由UUID表示,协议栈预留了一些16-bit的UUID来表示常用的属性类型。

2.1 服务项

服务项必须包含一个服务项声明,可选地包含多个其他服务项和特征项。所包含的其他服务项和特征项均是该服务项的一部分。

服务项的声明格式如下:

GATT_Service_Declaration

服务项可以是主要服务项(UUID=0x2800)或次要服务项(UUID=0x2801)。

主要服务项可以独立使用,次要服务项一定要被其他服务项包含引用。

协议栈文档中对次要服务项的使用场景解释有限,在绝大多数情况下均可以不使用次要服务项,仅使用主要服务。

2.2 包含

包含现了一个引用机制,比如需要扩展一个现有的服务项,可以在新的服务项中引用该服务项。

假如服务项中包含了其他服务项,则需要加入包含的声明(UUID=0x2802)。

协议栈文档中对包含的使用场景解释有限,在绝大多数情况下均可以不使用包含功能。

2.2 特征项

特征项是GATT数据的载体。

特征项包括:特征项的声明(UUID=0x2803),特征值的声明,以及若干描述符。特征值也是一个属性,它的句柄和UUID在特征项的声明中给出。

特征项始于该特征项的声明,结束语下一个特征项的声明。

特征项的声明数据格式如下:

GATT_Characteristic_Format

其中属性值字段包括了特征值功能特性(Characteristic Properties),特征值的属性句柄和特征值的UUID。

特征值功能特性如下表所示:

特征值功能值描述
Broadcast0x01允许广播该特征值
Read0x02允许读该特征值
Write Without Response0x04允许写该特征值,不需要Response
Write0x08允许写该特征值,需要Response
Notify0x10允许该特征值发送通知
Indicate0x20允许该特征值发送指示
Authenticated Signed Writes0x40允许带认证签名的写该特征值
Extended Properties0x80扩展特性

其中,Broadcase(0x01)、Notify(0x10)和Indicate(0x20)要求该特征值具有服务端特征项配置描述符(CCCD)。

特征项的声明中属性字段的特征值UUID跟特征值的声明中的UUID一致。

特征值的声明中包含了特征值所携带的数据内容,其格式如下:

GATT_Characteristic_Value_Format

2.3 描述符

描述符也是一种属性,它是特征项的一部分,用以提供特征值的额外信息。协议栈定义了6种不同的描述符,如下:

属性类型UUID描述
«Characteristic Extended Properties»0x2900特征项的扩展描述符
«Characteristic User Description»0x2901特征项的用户描述符
«Client Characteristic Configuration»0x2902客户端特征项配置描述符
«Server Characteristic Configuration»0x2903服务端特征项配置描述符
«Characteristic Format»0x2904特征项数据格式描述符
«Characteristic Aggregate Format»0x2905聚合特征项数据格式描述符

0x2900 扩展性描述符,用于Reliable Write和Writable Auxiliaries这两类写属性。

0x2901 用户描述符,用于给出该特征值的文字描述。

0x2902 客户端特征项配置描述符,简称为CCCD,客户端设备通过一个标志参数,设置该特征值能否发送通知和指示。如果该标志参数为0x0001,表示该特征值允许发送通知;如果该标志参数为0x0002,表示该特征值允许发送指示。如果该标志参数为0x0000,表示该特征值不能发送通知和指示。

每个特征项最多能包含一个CCCD,对于具有Broadcast、Notify和Indicate功能的特征项,必须拥有一个CCCD。在两个建立了绑定的设备中,断开连接不会丢失CCCD信息。

0x2903 服务端特征项配置描述符,服务端设备通过一个标志参数,设置该特征值是否在广播中发出。如果该标志参数为0x0001,则广播消息中应该包含该特征值;如果该标志参数为0x0000,则广播消息中不包含该特征值。

0x2904 特征值格式描述符,用于提供特征值的数据格式。可选的数据类型包括:Boolean、1/4字节、1/2字节、1字节、2字节、3字节、4字节、8字节、16字节、带符号整数、无符号整数、浮点数、字符串、结构体等。还可以指定数据的指数、单位、名字空间、描述信息等。

0x2905 聚合特征项格式描述符,专用于聚合特征项。所谓聚合特征值,是指多个特征值共同组合成一个数值,每个特征值仅是该聚合数值的一部分。

3. GATT功能

GATT规范实现了以下功能:

  1. Server Configuration

  2. Primary Service Discovery

  3. Relationship Discovery

  4. Characteristic Discovery

  5. Characteristic Descriptor Discovery

  6. Reading a Characteristic Value

  7. Writing a Characteristic Value

  8. Notification of a Characteristic Value

  9. Indication of a Characteristic Value

  10. Reading a Characteristic Descriptor

  11. Writing a Characteristic Descriptor

这些功能利用了“深入BLE协议栈 —— 属性协议”中的属性协议PDU一节中的多种读写属性PDU。

下面具体分析。

3.1 服务端配置

该功能呢包含一个子功能:交换两端设备的ATT_MTU。

客户端设备发送Exchange MTU Request,其中包含了该设备的ATT_MTU,服务端设备返回Exchange MTU Response,其中包含了该设备的ATT_MTU,取二者的较小值作为协商的ATT_MTU值。

3.2 发现主要服务项

该功能包含两个子功能:发现全部主要服务项,按UUID发现主要服务项。

发现全部主要服务项

该功能向服务端设备发送Read By Group Type Request,起始句柄为0x0001,结束句柄为0xFFFF,属性类型为0x2800(主要属性的UUID),查找全部符合条件的首要服务项。

服务端返回Read By Group Type Response,响应中包含多个属性信息组成的列表,单个属性信息包含三个参数:元素长度、属性组首尾句柄、属性的UUID。

因为该列表长度不能超过一个属性层的PDU长度,所以需要多次执行请求和响应,直到服务端设备返回“未找到属性项”或到达结束句柄,才能获取全部的主要服务项,如下图所示:

GATT_Discover_All_Primary_Services

观察上图,客户端第一次发起请求,查找主要服务项,首末句柄分别是0x0001和0xFFFF。

服务端返回响应中包含三个元素,每个元素代表一个首要服务项。每个元素的长度为0x06。第一个首要服务项的属性句柄为0x0001,类型为UUID1,末尾句柄为0x000F。第二个服务项的属性句柄为0x0010,类型为UUID2,末尾句柄为0x0017。第三个服务项的属性句柄为0x0100,类型为UUID3,末尾句柄为0x01FF。

客户端发起第二次请求,起始句柄设为0x2000。

服务端返回响应中仍然包含三个元素,每个元素的长度为0x06。三个元素分别表示三个首要服务项,其UUID分别为UUID4、UUID5和UUID6,UUID6的末尾句柄为0x04FF。

客户端接着发起第三次请求,起始句柄设为0x0500。

服务端返回错误,错误原因是未找到属性。客户端根据该错误原因,判断已经获取服务端设备的全部主要服务项。

按UUID发现主要服务项

该功能向服务端设备发送Read By Group Type Request,起始句柄为0x0001,结束句柄为0xFFFF,属性类型为0x2800(主要属性的UUID),指定的UUID为xxxx,查找全部符合条件的主要服务项。

具体的操作步骤与“发现全部主要服务”一致。

通常具有指定UUID的服务项仅有一个。

3.3 发现关系

该功能呢包含一个子功能:查找包含的服务项。

该功能向服务端设备发送Read By Type Request,起始句柄为0x0001,结束句柄为0xFFFF,属性类型为0x2802(包含的声明UUID),查找全部符合条件的被包含服务项。

服务端返回响应,包含了满足条件的服务项的句柄和属性值。

3.4 发现特征项

该功能包含两个子功能:发现服务项下的全部特征项,按UUID发现特征项。

发现服务项下的全部特征项

该功能向服务端设备发送Read By Type Request,设置已知的服务项首末句柄,属性类型为0x2803(特征项声明的UUID),查找全部符合条件的特征项。

服务端返回Read By Type Response,响应中包含多个“属性句柄 – 值”元素组成的列表。单个元素包含三个参数:元素长度、特征项声明的句柄和特征值参数。特征值参数包括特征值的功能特性、特征值句柄和UUID。

因为该列表长度不能超过一个属性层的PDU长度,所以需要多次执行请求和响应,直到服务端设备返回“未找到属性项”或到达结束句柄,才能获取全部的特征项,如下图所示:

GATT_Discovery_Characteristic

观察上图,客户端第一次发起请求,查找特征项,首末句柄分别是0x0200和0x0214。

服务端返回响应中包含两个元素,每个元素代表一个特征项。每个元素的长度为0x07。第一个特征项的声明句柄为0x0203,特征值的功能特性为0x02,即具有Read功能,特征值的句柄为0x0204,特征值的UUID为UUID1。第二个特征项的声明句柄为0x0210,特征值的功能特性为0x02,即具有Read功能,特征值的句柄为0x0212,特征值的UUID为UUID2。

由于每个元素的长度为7,表明该两个特征值的UUID均是2字节UUID,如果是16字节UUID,则每个元素的长度应该为0x15。

按UUID发现特征项

该功能根据已知的特征项UUID和首末句柄范围,查找满足条件的 特征项。

具体与“发现服务项下的全部特征项”完全一致。

3.5 发现描述符

该功能包含一个子功能:发现全部描述符。

该功能向服务端设备发送Find Information Request,设置已知的特征项首末句柄,查找全部符合条件的描述符。

服务端返回Find Information Response,响应中包含多个“属性句柄 – 值”元素组成的列表。单个元素包含三个参数:UUID格式、特征值的句柄和描述符的UUID。如果UUID格式参数等于1,表示描述符的UUID为2字节UUID,如果等于2,表示描述符的UUID为16字节UUID。

因为该列表长度不能超过一个属性层的PDU长度,所以需要多次执行请求和响应,直到服务端设备返回“未找到属性项”或到达结束句柄,才能获取全部的描述符,如下图所示:

GATT_Discovery_Descriptor

观察上图,服务端的响应数据第一个参数0x01表示UUID1和UUID2均为2字节UUID,第二个参数0x0205表示该描述符上级的特征值的句柄。

3.6 读特征值

该功能包含四个子功能:读特征值,按UUID读特征值,读长包特征值,读多个特征值。

读特征值

客户端已知特征值句柄,向服务端发送Read Request读取该句柄的特征值。

服务端返回指定句柄的特征值。该特征值长度应小于等于(ATT_MTU-1),如果大于该限制,则仅返回前(ATT_MTU-1)个数据。

下图为一次读取过程:

GATT_Read_Request

按UUID读特征值

客户端已知特征值的UUID,不知道其句柄,向服务端发送Read By Type Request读取该特征值。

具体操作与“按UUID发现特征项”一致。

读长包特征值

客户端已知特征值的句柄,但是特征值的长度大于(ATT_MTU-1),向服务端发送Read Request以读取前(ATT_MTU-1)个字节,然后发送Read Blob Request并设置合适的偏移量,以读取随后的(ATT_MTU-1)个字节,重复执行Read Blob Request直到服务端的Read Blob Response内容小于(ATT_MTU-1),表明该特征值完全被读取。

具体步骤如下:

GATT_Read_Blob_Request

读多个特征值

客户端已知多个特征值的句柄,向服务端发送Read Multiple Request,参数为多个特征值句柄。

服务端返回Read Multiple Response,包含了多个指定的特征值数据。

3.7 写特征值

该功能包含五个子功能:写命令,带签名的写命令,写请求,写长包请求,可靠的写请求。

写命令

客户端已知特征值句柄,向服务端发送Write Command,写入指定数据。

数据长度不能超过(ATT_MTU-3)字节,如果超过,仅写入前(ATT_MTU-3)个字节。

该命令无需服务端返回响应。

带签名的写命令

客户端已知特征值句柄,且链接没有经过认证,向服务端发送Write Command,并设置签名认证标志位,实现带签名的写命令。

数据长度不能超过(ATT_MTU-3-12)字节,其中12表示认证签名的长度,如果超过,仅写入前(ATT_MTU-3-12)个字节。

该命令无需服务端返回响应。

写请求

客户端已知特征值句柄,向服务端发送Write Request,写入指定数据。

数据长度不能超过(ATT_MTU-3)字节,如果超过,仅写入前(ATT_MTU-3)个字节。

该命令需要服务端返回响应Write Response。

写长包请求

客户端已知特征值句柄,但待写入数据长度过长,向服务端发送Prepare Write Request,设置适当的偏移量,将数据发送至服务端缓存起来,数据发送完毕后,项服务端发送Execute Write Request执行写请求。

待写数据总长度不受限制,但是分步发送数据每次数据长度不得超过(ATT_MTU-3)。

两种请求均需要对应的服务端响应。

一个写长包请求流程如下:

GATT_Long_Write_Request

可靠的写请求

客户端已知特征值句柄,希望一次性写入多字节的数据,或者要求数据的每个字节都必须被安全写入服务端设备,向服务端发送Prepare Write Request,偏移量永远等于0,一次性只发送一个数据,带多字节数据缓存完毕,再发送Execute Write Request执行写请求。

具体的操作与“写长包请求”完全一致。

3.8 通知

该功能包含一个子功能:通知。

服务端执行Handle Value Notification,参数为特征值句柄和通知数据,向客户端推送通知。

执行通知前,该特征值需要已经使能通知,并且将通知数据写入该特征值。

该命令无需客户端返回响应。

3.9 指示

该功能包含一个子功能:指示。

服务端执行Handle Value Indication,参数为特征值句柄和指示数据,向客户端推送指示。

执行指示前,该特征值需要已经使能指示,并且将指示数据写入该特征值。

该命令需要客户端返回响应Handle Value Confirmation。

3.10 读写描述符

该功能包含四个子功能:读描述符,读长包描述符,写描述符,写长包描述符。

读描述符与读特征值一致。

读长包描述符与读长包特征值一致。

写描述符与写请求一致。

写长包描述符与写长包请求一致。

4. 与L2CAP层互操作

GATT使用的L2CAP固定信道传输属性数据。

GATT的PDU长度限制ATT_MTU默认大小为23,它与L2CAP层的MTU值保持一致。

(完)


*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
属于自己的技术积累分享,成为嵌入式系统研发高手。
推荐文章
最近访客