GATT&ATT处于蓝牙结构里主机(host)这一层级里
ATT基本属性:用通俗的话来说,ATT相当于设备的数据库。比如LED灯的状态。
GATT属性:定义了两个角色,GATTS(服务器)和GATTC(客户端),GATT独立与GAP角色,GATTS提供数据,客户端访问数据。一个设备可以同时充当服务器和客户端。
GATT独立于GAP,如何理解,下面是解释:GATT全称是通用属性配置文件,它的主要作用将设备各种数据组织成服务和特征,并通过协议进行读写、写入属性值、以方便设备间的信息通信,比如心率检测仪通过GATT将心率数据提供给其他设备。GAP全称是通用访问配置文件,它的主要作用是管理蓝牙设备的连接、设备发现以及设备角色的定义和管理,决定了设备如何进行广播、扫描、发起连接、和接收连接等操作,以及连接过程中扮演的角色,如中心设备和外围设备。简单来说,GAP负责蓝牙设备连接,GATT则负责数据的传输
GATT数据交互过程可以这样理解,GATT可以提供一个数据交换的通道,ATT是设备的属性,那如何去获取这个状态的数据呢?这时可以将LED灯的状态当成ATT,这个ATT信息存储在GATTS中,同时再搭建一个GATTC,此时客户端就可以通过请求GATTS服务,GATTS接收到请求后会发出GATT响应,将ATT里的内容,也就是LED的状态信息发送给GATTC。这个流程可以类似对比HTTP的流程,但是二者协议本质上是有差别的。
仍然以ESP32作为示例,打开官方demo进行进一步理解,相应示例位于该文件目录下
红框是具体要实现的功能:通过一块ESP32点亮另外一块ESP32的LED灯。蓝框是用于分析和参考,也就是一些结构体的定义规范需要参考该文件。
首先了解一下什么是SPP:指的是串口仿真协议(serial port profile),它是蓝牙协议中的一种,用于蓝牙设备之间模拟串口通信。其核心功能是允许蓝牙设备之间像传统串口线连接一样进行数据传输,它将蓝牙链路抽象成一个虚拟的串口,使得设备可以通过虚拟串口收发数据,就好像他们有物理串口连接一样。比如stm32串口挂了一个zigbee的集成模块,可以直接进行设备数据的交互,这个spp就充当了串口的功能,只不过是虚拟的,物理层面是不存在的。
现在我们来看SPP示例中初始化的流程和SPP的运行流程
图1 SPP初始化流程
1-6步都是之前文章提过的,是一个标准的ESP32蓝牙初始化流程。
图2 SPP运行流程
为什么是先从GATT APP注册开始?直接跳到了示例初始化的第八步?是因为在APP注册后,会产生GATT注册回调,GATT注册回调又会产生GAP回调,所以流程是没问题的。图中流程并非完全线性,而是一个不断跳转的过程。
图3 GATTS&GAP回调过程
这是GATTS&GAP回调的过程,绿色代表事件,蓝色代表执行的动作。SPP流程从注册APP开始,在开始注册时便会触发GATTS事件回调,对应图1中的第八步,在配置好广播数据之后,便触发了“设置广播数据”事件,此时就会触发GAP事件回调,这就解释了图2中流程为什么是从APP注册开始,而在图1中却处于第八步而不是第六步。在完成“配置广播数据”后,会进行“属性表创建”,同时也会开额外一个线程,产生并发,进行“开始广播操作”。“创建属性表”再次触发GATTS事件回调,然后开始进行“开始服务”
【补充说明:
属性表:类似于数据库,当客户端与服务器建立连接后,服务器会为每个服务分配一个属性句柄,并将其告知客户端,蓝牙内的所有成员(服务、特征、描述符)均有唯一的句柄,类似于指针,可以通过句柄去快速访问某一属性,并对属性进行操作
服务:一组相关特征的分类,由特征、UUID、服务属性句柄组成,它是对设备提供服务功能或数据上一种逻辑上的分组和抽象,每个服务都有一个唯一的UUID和服务属性句柄,用于区分不同类型的服务,例如,常见的心率服务,其 UUID 是一个特定的值,它包含了与心率监测相关的各种特征,如心率测量值、心率传感器状态等。
特征:由特征值、特征属性、描述符、特征UUID、属性句柄构成,类似于结构体,结构体名称叫服务,成员是一个或者多个特征、每个特征内含五个成员,一个特征值,一个特征属性,一个描述符、一个特征UUID,一个属性句柄,其中特征值是具体的数值a,特征属性是指对于该数值a的操作权限,如可写,可读等,描述符是指该数值a的数据格式、取值范围等,特征UUID是指设定一个唯一的编号定义该特征,以区分不同特征,属性句柄同上。UUID和属性句柄起互补作用,UUID主要用于查询搜索,句柄主要用于实际的操作
这样说太抽象了,举个例子吧,dht11采集温湿度,那么这个dht11就是一个服务,这个服务由两个特征组成,分别是温度和湿度,以温度特征为例,它记录了温度数据(特征值)、数据是否可读可写(特征属性)、数据类型或取值范围(描述符)、dht11的定位编号(特征UUID)、特定属性句柄
结构大概就是这样,可以参考一下
图4 GATTC回调过程
图5 GAP回调过程
GATT流程总结:GATTC向GATTS发送GATT请求,GATTC发送目标UUID,GATTS根据该UUID去寻找对应的服务,并将该服务的UUID和句柄范围返回到GATTC,随后GATTC可以发起特征发现请求,GATTS会在该句柄范围内去进行遍历,寻找目标特征,然后将目标特征的UUID和句柄返回到GATTC,GATTC拿到特定句柄后,便可直接通过句柄对特征值进行读写操作了。一般来说,UUID用于搜索,句柄用于特定操作。
先理清属性表、服务、特征的结构,再理清通信流程,多借助豆包或deepseek可能会更好理解。
下一章开始上ESP32蓝牙代码