未完待续
前言
本文框架主要分为以下几部分:
- BLE协议栈框架简述
- BLE-GATT层相关概念简单介绍,包括profile、service等
- 结合代码分析GATT层的概念
- 思考与拓展
BLE协议栈框架
暂略
BLE-GATT层相关概念
GATT(Generic Attribute Profile)定义了一种用于数据交换的分层结构,包括Profile(通用属性配置文件)、Service(服务)、Characteristic(特征值)、Descriptor(描述符)等。
一个BLE设备可以有多个服务,每个服务可以包含多个特征值,每个特征值可以有零个或多个特征描述符。
例如,一个健康监测设备可能有一个心率服务,该服务有一个心率测量特征值,这个特征值可能有一个描述符来指示测量结果的格式。
特征声明是特征值的描述和定义。它包含了特征值的属性(如读、写、通知)、权限、以及可能的描述符等信息,定义如何访问和使用特征值。
Profile(配置文件)
Profile,即配置文件,定义了BLE设备的功能和数据交换规则。
包括关键概念有:服务、特征值、特征描述符、属性、配置文件角色、数据交换格式、安全和配对等。
Service(服务)
Service(服务)是一组特征值的组合,每个服务都有一个唯一的标识符UUID,使得客户端可以标准化识别服务。
服务可以包含多个特征值,特征值可以包含多个特征描述符(Descriptors)。
服务本身没有直接的访问权限,但它们包含的特征值可以具有不同的访问权限,如只读、可写、可通知等。
句柄: 服务句柄(Service Handle)是一个用于唯一标识服务的数字。每个服务、特征值(Characteristic)、以及特征描述符(Descriptor)在GATT数据库中都有一个唯一的句柄值。
- 在GATT数据库,即
profile_data
中,句柄值通常会从1开始,然后依次增加。 - 服务句柄通常与其他属性的句柄一起定义了一个服务的范围,即服务的第一个和最后一个属性句柄标识了服务的开始和结束。
- 服务句柄与服务的UUID相关联,UUID用于定义服务的类型和功能,而句柄用于在GATT数据库中标识服务的位置。
主服务: 主服务是GATT数据库中的顶级服务,它作为GATT层次结构的起点,包含多个特征值(Characteristics)和次级服务(Secondary Services)。
一个主服务可以包含多个次级服务,而次级服务也可以进一步包含其他次级服务或特征值。
主服务可以被客户端设备发现,客户端设备通过扫描和查询来识别服务器端提供的服务
在BLE设备配对和连接过程中,客户端设备首先发现主服务,然后才能进一步发现和访问服务内的特征值
主服务在GATT数据库中定义了一个服务范围,由服务的起始句柄(Start Handle)和结束句柄(End Handle)标识
Characteristic(特征值)
每个特征值都有一个唯一的UUID(Universally Unique Identifier),用于在GATT数据库中标识不同的特征值。
特征值是客户端和服务器之间数据交换的基本单元。其表示BLE设备中的一个数据项,可以是温度传感器的读数、设备的电池电量、或者其它用户自定义的数据设置通道。
客户端可以读取服务器的特征值,或者在特征值上注册通知,以接收数据更新。
其定义了一系列特征属性(Properties),这些属性描述了特征值的行为,例如是否可读、可写、可通知(Notify)、可指示(Indicate)等。
特征值可以有零个或多个特征描述符(Descriptor),这些描述符提供了关于特征值的额外信息,例如用户友好的描述、配置参数等。
特征值具有访问权限,定义了哪些操作(如读取、写入)是被允许的。这些权限可以用于实现安全措施,如认证和加密。
特征值可以是动态的,其值随设备状态变化而变化;也可以是静态的,其值在设备使用期间保持不变。
UUID(唯一标识符)
蓝牙低功耗(BLE)的GATT(Generic Attribute Profile)框架中,服务(Service)、特征声明(Characteristic Declaration)、特征值(Characteristic Value)和特征描述符(Descriptor)都有各自的UUID(Universally Unique Identifier,通用唯一识别码)
BLE的服务和特征值都是通过UUID唯一标识的,并且可以通过UUID查找相关的服务和特征值
唯一性:UUID提供了一个全球唯一的标识符,确保了不同设备或开发者定义的服务、特征值和描述符不会发生冲突。
标准化:使用UUID有助于标准化,使得不同制造商生产的设备能够通过标准化的接口进行通信和交互。
等等
Client and Server
Server(服务器端): 比如蓝牙耳机
- 提供服务,广播服务信息,被其它设备发现
- 存储特征值数据,如设备参数、状态信息或者用户自定义的数据
- 响应客户端的数据交互,或者发送通知/指示到客户端
Client(客户端): 比如手机
- 发现服务,请求服务信息,连接到Server访问特征值
- 根据实际应用,可以读取Server的特征值数据,或者写入数据到特征值
- 也可以订阅特征值通知,当特征值数据发生变化时,会收到通知。
- 一个通信会话包括一个 Server 和一个 Client
- 设备可以同时充作为 Server 和 Client
- BLE支持一对多通信模式,一个Server可以与多个Client建立通信
BLE通信流程
发现阶段:Client扫描周围环境中的Server,通过广播包发现服务。
连接阶段:Client发起连接请求,Server接受连接后,两者建立通信链路。
服务和特征值发现:Client通过读取操作获取Server上的服务列表,进一步发现服务内的特征值。
数据交互:Client根据需要读取或写入特征值,Server响应这些请求并提供数据或确认。
通知和指示:Server可以通过特征值向Client发送通知或指示,Client接收并处理这些更新。
GATT层概念小结
BLE定义了一系列的标准服务,其可以看作是特定应用场景下的profile,如Heart Rate Service、Battery Service等都是标准服务
标准服务和特征值:
标准服务和特征值的 UUID 通常为 16 位。
16位 UUID 通常用于表示标准的服务和特征值。这种 UUID 的格式为 0xXXXX,其中 XXXX 是一个四位的十六进制数。
16位 UUID 的完整形式为 0000XXXX-0000-1000-8000-00805F9B34FB。
例如,Battery Service 的 UUID 为 0x180F,其中包含的 Battery Level 特征值的 UUID 为 0x2A19。
自定义服务和特征值:
自定义服务和特征值的 UUID 可以为 16 位或 128 位。
例如,一个自定义的服务 UUID 可能是 128 位的,而该服务下的特征值也可以是 128 位的 UUID,或者其中一个为 16 位,另一个为 128 位。
128位 UUID 通常用于表示自定义的服务和特征值。这种 UUID 的格式为一组 32 位的十六进制数字,以连字号分为五段。
128位 UUID 的格式为 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX。
嵌入式BLE之业务应用层开发
以下为一项目范例BLE广播及连接示意
BLE的广播包设置及分析
0x02010609FF00008AE088E655540505DAF58F010201061409736F756E64636F72652053656C656374203253
LEN | TYPE | VALUE |
---|---|---|
2 | 0x01 | 0x06 |
9 | OxFF | 0x00008AE088E65554 |
5 | 0x05 | 0xDAF58F01 |
2 | 0x01 | 0x06 |
20 | 0x09 | 0x736F756E64636F72652053656C656374203253 |
LEN: 0x02 (2字节) TYPE: 0x01 (Flags) VALUE: 0x06
这通常表示设备的发现模式,其中0x06通常表示设备支持 BLE 仅连接模式。LEN: 0x09 (9字节) TYPE: 0xFF (Manufacturer Specific Data) VALUE: 0x00008AE088E65554
这是制造商特定的数据,通常包含公司标识符和产品特定的信息LEN: 0x05 (5字节)TYPE: 0x05 (Service Data)VALUE: 0xDAF58F01(低位在前)
TYPE 0x05: 表示广播数据包的类型为 Service UUID;VALUE 0xDAF58F01: 表示一个服务的 UUID。LEN: 0x02 (2字节) TYPE: 0x01 (Flags) VALUE: 0x06
重复了,可能是设置错误?
LEN: 0x14 (20字节) TYPE: 0x09 (Local Name) VALUE: 0x736F756E64636F72652053656C656374203253
设备的本地名称,解码为字符串
soundcore Select 2S
。
Profile_data解析
profile_data[]
是一个 二进制格式的GATT(通用属性配置文件)描述数组,用于定义BLE设备的服务(Service)、特征(Characteristic)及权限。
每个条目对应一个属性(Attribute)
,按顺序存储以下信息:
属性句柄(Handle):2字节,唯一标识属性的地址。
属性类型(Type):2字节,表示属性类型(如服务、特征、描述符)。
属性值(Value):变长数据,具体内容由属性类型决定。
句柄递增规则: 主服务、特征声明、特征值、CCCD的句柄按顺序递增
字段逐字节解析(以第一个服务声明为例)
1 | 0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x18 |
字节位置 | 值(十六进制) | 含义 |
---|---|---|
0 | 0x0a | 条目总长度:10字节(后续9字节需+1,因长度本身占1字节)。 |
1-2 | 0x00 0x02 | 属性句柄:小端序,值为 0x0002。 |
3-4 | 0x00 0x28 | 属性类型:小端序,0x2800 表示 主服务声明(Primary Service)。 |
5-6 | 0x00 0x01 | 服务UUID长度/标志:此处为16位UUID,值为 0x0001(无关,通常忽略)。 |
7-8 | 0x00 0x18 | 服务UUID:小端序,0x1800 对应 GAP服务(通用访问)。 |
数据结构原理
- 层级结构
服务(Service) 包含多个 特征(Characteristic)。
特征 包含值、权限及可选的 描述符(Descriptor)(如CCCD)。 - 句柄分配
句柄按顺序递增,确保唯一性。
例如,服务声明句柄为 0x0002,其下特征值句柄为 0x0003,依此类推。 - 动态值(DYNAMIC)
若特征值标记为 DYNAMIC,实际数据由应用层通过回调函数实时提供,而非硬编码在配置中。 - 小端序存储
UUID、句柄等16位值均按小端序(低位在前)存储。例如 0x1800 存储为 0x00 0x18。
以下为一profile_data示例:
1 | static const uint8_t profile_data[] = { |
服务类型与结构如下:
- 主服务声明(Primary Service)
属性类型:0x2800(固定值)。
属性值:服务UUID(16位或128位)。1
2// 示例:声明UUID为0x1800的GAP服务
0x0a, 0x00, [句柄], 0x00, 0x28, [UUID低字节], [UUID高字节] - 特征声明(Characteristic Declaration)
属性类型:0x2803(固定值)。
属性值:包含特征权限、值句柄和特征UUID。1
2
3
4
5// 示例:声明一个可读、可通知的特征(UUID 0xFFC2)
0x0d, 0x00, [句柄], 0x03, 0x28,
0x12, // 权限:READ(0x02) + NOTIFY(0x10) = 0x12
0x08, 0x00, // 特征值句柄:0x0008
0xC2, 0xFF // 特征UUID:0xFFC2(小端序) - 特征值(Characteristic Value)
属性类型:特征UUID(如 0x2A00 表示设备名称)。
属性值:实际数据或动态生成标志(DYNAMIC)。1
2// 示例:特征值动态生成(由应用层提供)
0x08, 0x00, [句柄], 0x01, 0x2A, 0x00, 0x00 // 0x2A00为设备名称特征 - 客户端特征配置描述符(CCCD)
属性类型:0x2902(固定值)。
属性值:配置通知(Notify)或指示(Indicate)的开关。1
2// 示例:CCCD描述符,句柄为0x0009
0x0a, 0x00, [句柄], 0x02, 0x29, 0x00, 0x00 // 0x2902为CCCD类型
初始化、服务及回调注册
根据业务需求,定义好profile_data
后,调用ble_profile_init
函数进行初始化,并注册服务及回调函数。
业务数据处理
当设备收到数据,会在相应回调中触发 case 相应的事件枚举。该枚举即对应相应的profile_data句柄。
att_server_init(multi_profile_data, att_read_callback, att_write_callback);
思考与拓展
服务句柄、特征值句柄、特征值值句柄、CCCD句柄?
在程序中通过case各种句柄事件,从而执行相应的操作
特征值句柄,用于读取元数据
特征值值句柄用于读写实际数据
CCCD句柄:特征描述符相关的标识,可选
客户端不与服务器端建立连接,可以读取特征值数据吗?
广播跟GATT层有什么联系?广播数据包必须包含哪些数据?必须与UUID关联?配置广播有什么标准要求吗?
扫描到的广播名称,即与0x09字段的绑定
广播数据包结构:
广播数据包包含了一个或多个字段,每个字段由长度、类型和数据组成。
类型字段定义了数据的含义,如设备名称、服务 UUID 等。
广播数据包可以是可连接的 (Connectable) 或不可连接的 (Non-connectable)。