ATT(Attribute Protocol)属性层是GATT和GAP的基础,它定义了BLE协议栈上层的数据结构和组织方式。

属性(Attribute)概念是ATT层的核心,ATT层定义了属性的内容,规定了访问属性的方法和权限。以编程的眼光来看,属性是一个数据结构,它包括了数据类型和数据值,就如同C语言结构体的概念,开发者可以设计独特的结构,来描述外部世界实体。

属性包括三种类型:服务项特征值描述符。三者之间存在树状包含关系,服务项包含一个或多个特征值,特征值包含一个或多个描述符,多个服务项组织在一起,构成属性规范(Attribute Profile)。对于常用的属性规范,比如体重计、心率计,SIG(蓝牙技术联盟)做了具体定义,这样的话,只要BLE主从设备均遵守某个Profile来进行设计,那么二者就能够优雅的通信。

ATT层相关的东西与开发者比较近,易于理解,但是章节内容图表较少,阐述偏多。

一. 属性的组成(数据结构)

属性主要由以下四部分组成:属性句柄(Attribute Handler)、属性类型(Attribute Type)、属性值(Attribute Value)、属性权限(Attribute Permissions)。

1.1 属性句柄

属性句柄(Attribute Handle)犹如指向属性实体的指针,对端设备可通过属性句柄来访问该属性,它是一个2字节长度的十六进制码,起始于0x0001,在系统初始化时候,各个属性的句柄逐步加一,最大不超过0xFFFF。

1.2 属性类型

作用是用以区分当前属性是服务项或是特征值等,它用UUID来表示。UUID(universally unique identifier,通用唯一识别码)是一个软件构建标准,并非BLE独有的概念,一个合法的UUID,一定是随机的、全球唯一的,不应该出现两个相同的UUID(出现了,就说明它们俩是同一个UUID)。标准的UUID是一串16字节十六进制字符串,如f6257d37-34e5-41dd-8f40-e308210498b4,在网上可以方便的生成一个UUID。
BLE的属性类型是有限的,有四个大类:

  • Primary Service(首要服务项)
  • Secondary Service(次要服务项)
  • Include(包含服务项)
  • Characteristic(特征值)

这些属性类型分别对应了指定的UUID,BLE对这些UUID与属性类型的映射关系做了规定:

  • 0x1800 – 0x26FF :服务项类型
  • 0x2700 – 0x27FF :单位
  • 0x2800 – 0x28FF :属性类型
  • 0x2900 – 0x29FF :描述符类型
  • 0x2A00 – 0x7FFF :特征值类型

假如UUID=0x1800,就表示它是一个首要服务项。

UUID是16个字节的字符串,为什么这里只使用了2字节?

因为这些是常用的UUID,为了减少传输的数据量,BLE协议做了一个转换约定,给定一个固定的16字节模板,只设置2个字节为变化量,其他为常量,2字节的UUID在系统内部会被替换,进而转换成标准的16字节UUID。UUID模板为:

0000XXXX----00805F9B34FB

其中从左数第3、4个字节“XXXX”就是变化位,其他为固定位。如:UUID=0x2A00在系统内部会转换成00002A00-0000-1000-8000-00805F9B34FB。

反之,如果一个特征值的UUID是16字节的,在系统内部它的属性类型也可能写成第3、4字节组成的双字节,比如UUID=1234ABCD-0000-1000-8000-00805F9B34FB,它的属性类型在内部表示为ABCD。主机端扫描到该属性类型,会将其当做是“用户自定义”的类型,然后从其他位置获取该UUID的真实值。

1.3 属性值

用于存放数据。如果该属性是服务项类型或者是特征值声明类型,那么它的属性值就是UUID等信息。如果是普通的特征值,则属性值是用户的数据。属性值需要预留空间以保存用户数据。为了方便理解,我们可以将属性值的空间看做I2C的数据空间,操作特征值里的用户数据,就是对那块内存空间进行读写。

1.4 属性权限

属性权限主要有以下四种:

  • 访问权限(Access Permission)- 只读、只写、读写
  • 加密权限(Encryption Permission) – 加密、不加密
  • 认证权限(Authentication Permission) – 需要认证、无需认证
  • 授权权限(Authorization Permission) – 需要授权、无需授权

访问(Access)权限好理解,如果是只读权限,就不能对其写数据,其他类似。

加密(Encryption)权限也好理解,就是对数据进行加密。

认证(Authentication)是指相互确认对方身份。完成认证流程的两个设备,双方建立信任关系,二者之间的通信通道即可以认为是安全的。BLE中,“认证”过程就是配对。

授权(Authorization)是指对授信设备开放权利。

认证和授权功能容易混淆,其英文拼写也很相似。从上面的概念上看,授权要求设备必须是可信任的,因此授权的管控等级要高于认证——认证的设备未必被授权,授权的设备一定是认证的。理解二者关系,需要引入一个概念:Trusted Device(可信任设备)一个没有经过认证的设备,被称为Unknown Device(未知设备);经过了认证该设备会在绑定信息中被标记为Untrusted,被称为Untrusted Device(不可信设备);经过了认证,并且在绑定信息中被标记为Trusted的设备被称为Trusted Device(可信设备)。

授权要求设备为Trusted Device(可信任设备)。在实际使用中,经过配对以后设备即为Untrusted Device——认证,在代码中调用API可以设置设备为Trusted Device——授权。

二. 属性的种类和分组(属性的层级)

属性大致可以分为三种类型:服务项特征值描述符。它们的层级关系为:最顶级为Profile, 下面是多个服务项(Service), 服务项下面是多个特征值(Characteristic), 特征值下面是多个描述符(Descriptor)。

每个设备都包含以下必要的特征值和服务项:

PROFILE

  • Generic Access Service(Primary Service)

    • Device Name(Characteristic)
    • Appearance(Characteristic)
  • Generic Attribute Service(Primary Service)
    • Service Changed(Characteristic)

      • CCCD(Descriptor)

服务项这种类型本身并不包含数据,仅仅相当于是一个容器,用来容纳特征值。特征值用于保存用户数据,但它也有自己的UUID, 有点像C语言中的变量int var=0xFF,整形变量var携带了用户数据0xFF,但是它自身还有地址信息(&var),因此在使用时需要先定义再赋值两个步骤。类似的,在处理特征值所携带的用户数据之前,需要先对特征值自身进行声明。

特征值在系统中的表达形式是:声明 + 特征值属性。比如Device Name,它在GATT数据库中表示方式是:

Characteristic 声明: 0x0002, 0x2803, access_property, 0x2A00
Characteristic 项: 0x0003, 0x2A00, access_property, data

其中第一列双字节数代表句柄,第二列双字节数代表属性类型(UUID),0x2803表示该项为“特征值的声明”,0x2A00表示该特征值是Device Name。在第一行特征值声明中,最后一项的属性值就是该特征值的UUID。可以注意到,声明里面的属性值为0x2A00,特征值自己的属性类型也是0x2A00,显然信息冗余了。原因是,假如特征值的UUID为自定义的16字节UUID,在特征值的属性类型中,只能存放2个字节,而UUID的真实值,就存放在特征值声明的属性值项中。

BLE的属性体系在系统中以GattDB表示,即属性数据库。打开CyBle_gatt.c文件,找到cyBle_gattDB变量,如下图

const CYBLE_GATTS_DB_T cyBle_gattDB[0x10u] = {
{ 0x0001u, 0x2800u /* Primary service */, 0x00000001u /* */, 0x0007u, {{0x1800u, NULL}} },
{ 0x0002u, 0x2803u /* Characteristic */, 0x00000201u /* rd */, 0x0003u, {{0x2A00u, NULL}} },
{ 0x0003u, 0x2A00u /* Device Name */, 0x00000201u /* rd */, 0x0003u, {{0x0009u, (void *)&cyBle_attValuesLen[]}} },
{ 0x0004u, 0x2803u /* Characteristic */, 0x00000201u /* rd */, 0x0005u, {{0x2A01u, NULL}} },
{ 0x0005u, 0x2A01u /* Appearance */, 0x00000201u /* rd */, 0x0005u, {{0x0002u, (void *)&cyBle_attValuesLen[]}} },
{ 0x0006u, 0x2803u /* Characteristic */, 0x00000201u /* rd */, 0x0007u, {{0x2A04u, NULL}} },
{ 0x0007u, 0x2A04u /* Peripheral Preferred Connection Par */, 0x00000201u /* rd */, 0x0007u, {{0x0008u, (void *)&cyBle_attValuesLen[]}} },
{ 0x0008u, 0x2800u /* Primary service */, 0x00000001u /* */, 0x000Bu, {{0x1801u, NULL}} },
{ 0x0009u, 0x2803u /* Characteristic */, 0x00002201u /* rd,ind */, 0x000Bu, {{0x2A05u, NULL}} },
{ 0x000Au, 0x2A05u /* Service Changed */, 0x00002201u /* rd,ind */, 0x000Bu, {{0x0004u, (void *)&cyBle_attValuesLen[]}} },
{ 0x000Bu, 0x2902u /* Client Characteristic Configuration */, 0x00000A04u /* rd,wr */, 0x000Bu, {{0x0002u, (void *)&cyBle_attValuesLen[]}} },
{ 0x000Cu, 0x2800u /* Primary service */, 0x00000001u /* */, 0x0010u, {{0xCBBBu, NULL}} },
{ 0x000Du, 0x2803u /* Characteristic */, 0x00001A01u /* rd,wr,ntf */, 0x0010u, {{0xCBB1u, NULL}} },
{ 0x000Eu, 0xCBB1u /* Custom Buffer */, 0x00011A04u /* rd,wr,ntf */, 0x0010u, {{0x00C8u, (void *)&cyBle_attValuesLen[]}} },
{ 0x000Fu, 0x2901u /* Custom Descriptor */, 0x00010001u /* */, 0x000Fu, {{0x001Cu, (void *)&cyBle_attValuesLen[]}} },
{ 0x0010u, 0x2902u /* Client Characteristic Configuration */, 0x00010A04u /* rd,wr */, 0x0010u, {{0x0002u, (void *)&cyBle_attValuesLen[]}} },
};

通过注释可以看到,每个特征值都有声明和特征值项两部分组成。

由于各个属性的句柄是递增的,因此属性的声明顺序会影响句柄的计算。

描述符是特征值的补充信息,挂载在特征值之下,它可以开辟一段数据空间以携带数据,客户端可以像操作特征值一样对其进行读写,但是描述符弱于特征值,它不具备Notify/Write等读写属性,远不如特征值灵活。有一种描述符叫CCCD(Client Characteristic Configuration Description),当对特征值设置Notify或Indication时,用以控制Notify和Indication的使能情况。

关于gattDB,值得注意的一点是,gattDB是BLE协议栈在内存中开辟的一段专有区域,它会在特定的时候写入Flash进行保存,并在启动时读取出来回写到内存中去。并非所有的BLE数据通信是操作gattDB!比如手机向BLE设备发送一串字符串,从机收到这串字符串并打印出来,这个过程中,这个字符串并没有写入gattDB。那么,即使开发者限制某个特征值为认证、只读等权限,仍然不能阻止这个字符串的发送和打印,因为特征值的权限,都是针对gattDB而言的。进一步,假如BLE从机收到字符串后,希望写入gattDB进行保存,那么就会触发权限问题。以往有开发者说为什么我勾选了属性需要认证,但是配对失败后仍然能够看到手机发来的数据,原因就在这里,就是手机端传来的数据没有涉及到gattDB,当尝试写入gattDB的时候,就会发现报错了。

三. ATT PDU(属性协议)

在ATT层协议框架内,拥有一组属性的设备称为服务端(Server),读写该属性值的设备称为客户端(Client),Server和Client通过ATT PDU进行交互。属性协议共有6种:

属性PDU 方向 触发响应
Command Client -> Server
Request Client -> Server Response
Response Server -> Client
Notification Server -> Client
Indication Server -> Client Confirmation
Confirmation Client -> Server

它们的区别如下:

客户端发送Request,服务器需要返回一个Response,表明服务器收到了。

服务器发送Indication,客户端需要返回一个Confirmation,表明客户端收到了。

以上两种方式,均是单线程操作,即下一个Request/Indication操作需要在上一个操作收到Response/Confirmation之后才能开始。

客户端发送Command,服务器无需任何返回。

服务器发送Notification,客户端无需任何返回。

因此Command和Notification是不可靠的通信。当通信环境不佳,客户端频繁发送Command,可能发生服务器接收不到或丢弃的情况,Notification也类似。

PDU的具体格式定义如下:

参数说明:

Opcode:
bit -:操作属性的方法
bit :Command 标识位
bit :Authentication Signature标识位
Attribute Parameters:
如果Attribute Opcode中身份验证签名标记位为0,则X = ;
如果Attribute Opcode中身份验证签名标记位为1,则X = ;
Authentication Signature:
属性操作码和属性参数的可选身份验证签名

参考链接:

1. 蓝牙BLE实用教程

2. BLE协议栈-ATT

3. BLE ATT和GATT小结

4. BLE 5 协议栈-属性协议层(ATT)

蓝牙BLE: ATT协议层中属性(Attribute)的更多相关文章

  1. 蓝牙BLE ATT剖析(一)

    一.概述 The attribute protocol allows a device referred to as the server to expose a set of attributes ...

  2. 蓝牙BLE ATT剖析(二)-- PDU

    一.Error Handling Error Response The Error Responseis used to state that a given request cannot be pe ...

  3. 蓝牙的OBEX协议

    1.概述     OBEX为Object Exchange,用于在蓝牙设备间传数据对象,来源于红外定义的协议,后被蓝牙采用.OBEX在蓝牙协议层中的位置如下图(在之前的OBEX版本中,OBEX是通过R ...

  4. BLE 5协议栈-属性协议层(ATT)

    文章转载自:http://www.sunyouqun.com/2017/04/page/2/ 属性协议(Attribute Protocol)简称ATT. ATT层定义了属性实体的概念,包括UUID. ...

  5. 蓝牙 BLE 协议学习: 001-BLE协议栈整体架构

    背景 在深入BLE协议帧之前,我们先看一下BLE协议栈整体架构. 转载自:<深入浅出低功耗蓝牙(BLE)协议栈> 架构 如上图所述,要实现一个BLE应用,首先需要一个支持BLE射频的芯片, ...

  6. BLE 5协议栈-逻辑链路控制与适配协议层(L2CAP)

    文章转载自:http://www.sunyouqun.com/2017/04/page/2/ 逻辑链路控制与适配协议通常简称为L2CAP(Logical Link Control and Adapta ...

  7. Objective-C中属性及其特质@property、attribute

    属性: 属性@property和属性attribute不同,@property在OC里有自己的一套专对实例变量的处理机制.attribute我们可以特指属性所具有或遵循的特质. 使用属性,编译器就会自 ...

  8. [Tips]ASP.NET MVC 发布到服务器后Model中属性相关的Attribute失效

    Asp.net MVC4 开发的项目,发布到真实环境环境后Model中的一个属性设置的了Attribute,但是这些Attribute都失效. 经过对比和坚持代码发现控制长度的Attribute使用错 ...

  9. layer弹出层中H5播放器全屏出错解决 & 属性poster底图占满<video>的方法

    1. 在layer弹窗组件中 如果使用了flash播放器,全屏是正常的 但若使用了HTML5的播放器,全屏失效 举个栗子 <!DOCTYPE html> <html> < ...

随机推荐

  1. 爬虫之selenium模块;无头浏览器的使用

    一,案例 爬取站长素材中的图片:http://sc.chinaz.com/tupian/gudianmeinvtupian.html import requests from lxml import ...

  2. Odoo搜素视图过滤器之筛选与分组

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10826168.html 一:过滤器 搜索视图还可以包含<filter>元素,定制过滤器. 过滤器 ...

  3. Httpd服务入门知识-Httpd服务安装

    Httpd服务入门知识-Httpd服务安装 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Httpd概述 1>.Httpd介绍 20世纪90年代初,国家超级计算机应用中心 ...

  4. zabbix--微信告警

    zabbix 微信告警机制 zabbix 告警机制有很多,比如邮件.微信.电话.短信等等.很多,但是像电话和短信都是有钱人玩的,我们这些穷屌丝玩玩 微信 邮件 就可以了. 参考:https://git ...

  5. php中的Exception

    如果定制的EXCEPTION搞定了,默认的,就自然不在话下罗. 直接上最曲折的过程. InvalidIdException.php <?php namespace Bookstore\Excep ...

  6. Python库的使用之-Sys模块

    Python标准库之sys模块使用详解,讲解了使用sys模块获得脚本的参数.处理模块. 使用sys模块操作 模块搜索路径.使用sys模块查找内建模块.使用sys模块查找已导入的模块,重定向输出以及重定 ...

  7. TCP server 为什么一个端口可以建立多个连接?

    https://segmentfault.com/q/1010000003101541 如果是tcp client用同一个本地端口去连不同的两个服务器ip,连第二个时就会提示端口已被占用.但服务器的监 ...

  8. Optional类的基本使用(没怎么看)

    参考:https://www.runoob.com/java/java8-optional-class.html java8中引入了一个新类:Optional,用于日常编码中对空指针异常进行限制和处理 ...

  9. PHP:ThinkCMFX任意文件包含漏洞

    前言:最近爆出来的漏洞,ThinkCmfX版本应该是通杀的,基于3.X Thinkphp开发的 代码下载地址:https://gitee.com/thinkcmf/ThinkCMFX/releases ...

  10. 开发(二) ardunio批量固件上传地址

    https://blog.csdn.net/Naisu_kun/article/details/84958561 批量烧录固件到模块中上面讲了如何编写上传程序,接下来讲讲如何量产.相比<Ardu ...