前言
蓝牙作为一个庞大的知识体系,其学习和运用对于初学者来说显得有些复杂且凌乱。我整理了这段时间的学习笔记,涵盖了协议栈、工作流程、参数等内容。在实际应用中,我们主要使用 GAP 和 GATT,协议栈中的其他部分只需了解即可。因此,如果只是简单使用蓝牙,只要理清逻辑,学习难度其实并不高。
希望这篇笔记能够帮助你快速入门蓝牙技术。你可以结合 ESP-IDF 提供的 gatt_server_service_table
和 gatt_client
示例进行学习。同时,附件中还提供了协议栈和设备工作流程的思维导图,或许通过图示化的方式能更直观地理解这些概念。
需要注意的是,笔记中的某些内容(如特征值扩展部分)可能在初级使用中用不到,可以暂时跳过。
如果发现任何错误或是提出改进建议,欢迎与我联系。
AI 摘要
这篇笔记详细介绍了蓝牙技术的核心概念和实现,包括蓝牙网络的拓扑结构(如微微网、散射网和 Mesh 网络)、蓝牙连接流程(广播、扫描、连接建立等)、以及蓝牙协议栈的分层结构(GAP 和 GATT)。文中还深入解析了 BLE 的数据传输机制、MTU 配置、广播数据格式和特性描述符等内容,并结合 ESP32 的实际开发,提供了丰富的技术细节。
参考资料:
ESP-IDF 编程指南
蓝牙技术网站
Bluetooth - Wikipedia
Bilibili-Michael_ee
Assigned Numbers
BLE User Guide — Apache Mynewt latest documentation
附件链接:
Github_CodeFlashier
文章目录
- 前言
- AI 摘要
- Brief
- The classification of Bluetooth
- Bluetooth terminology
- Core Architecture
- Bluetooth Roles
- Bluetooth Network Configuration
- Bluetooth Connection Process
- Bluetooth Stack
- GAP
- Roles when Operating over BR/EDR Physical Transport
- Roles when Operating over an LE Physical Transport
- Defines compliance requirements
- Device Connection Establishment Process
- GATT
- Configuration and Roles
- GATT Profile Hierarchy
- Service
- Service Declaration
- Service Include
- Include Declaration
- Characteristic
- Characteristic Declaration
- Characteristic Value Declaration
- Characteristic Descriptor Declarations
- Characteristic Extended Properties
- Characteristic User Description
- Client Characteristic Configuration
- Server Characteristic Configuration
- Characteristic Presentation Format
- Characteristic Aggregate Format
- GATT Profile Attribute Types
- GATT Send Command
- GATT Server
- GATT Server Work Flow
- Advertising
- MTU
- PDU
- Advertising Flow
- Advertising State
- Undirected Advertising
- Directed Advertising
- Scanning State
- Passive Scanning
- Active Scanning
- Callback Parameters
- Advertising And Scanning Response Data Format
- gatts_table_creat_demo
- GATT Client
- GATT Client Work Flow
- Connection
- Supervision Timeout
- Peripheral Latency
- MTU
- Data Exchange
- Write Data
- Notification for Server
- Notification for Client
Brief
蓝牙是一种支持设备短距离通信的无线通信技术,能够实现在短距离范围内实现信息的自由分享和传输,具有安全性高、自由连接等特性,工作在 2.4GHz ISM(工业、科学、医学) 频段
蓝牙可分为经典蓝牙和低功耗蓝牙
从整体结构上,蓝⽛可分为控制器 (Controller) 和主机 (Host) 两⼤部分:控制器包括了 PHY、Baseband、Link Controller、Link Manager、Device Manager、HCI 等模块,⽤于硬件接⼝管理、链路管理等等;主机则包括了 L2CAP、SMP、SDP、ATT、GATT、GAP 以及各种规范,构建了向应⽤层提供接⼝的基础,⽅便应⽤层对蓝⽛系统的访问。主机可以与控制器运⾏在同⼀个宿主上,也可以分布在不同的宿主上。
The classification of Bluetooth
Classic Bluetooth
经典蓝牙(BR/EDR)泛指支持蓝牙协议在 4.0 以下的模块,一般用于大数据量的传输(如语音、音乐),其协议包含个人局域网的各种规范,如:
Advanced Audio Distribution Profile (A2DP)
适用于音频Hands-Free Profiles/Hand-Set Profiles (HFP/HSP)
适用于免提设备Serial Port Profiles (SPP)
适用于文本串口透传(可用于蓝牙调试)HFP/HSP
、HID
等
Bluetooth Low Energy
一种超低功耗无线通信技术,主要针对低成本、低复杂度的无线体域网和无线各域网设计
来源:Bluetooth 技术概述 |Bluetooth® 技术网站
特性 | 低功耗蓝牙 (LE) | 经典蓝牙 (Bluetooth Classic) |
---|---|---|
频段 | 2.4GHz ISM 频段 (2.402 – 2.480 GHz 使用) | 2.4GHz ISM 频段 (2.402 – 2.480 GHz 使用) |
频道 | 40 个频道,2 MHz 间隔(3 个广播频道/37 个数据频道) | 79个频道,1 MHz间隔 |
频道使用 | 频率跳变扩频 (FHSS) | 频率跳变扩频 (FHSS) |
调制方式 | GFSK | GFSK,π/4 DQPSK,8DPSK |
数据速率 | LE 2M PHY:2 Mb/s LE 1M PHY:1 Mb/s LE Coded PHY (S=2):500 Kb/s LE Coded PHY (S=8):125 Kb/s | EDR PHY (8DPSK):3 Mb/s EDR PHY (π/4 DQPSK):2 Mb/s BR PHY (GFSK):1 Mb/s |
发射功率 | ≤ 100 mW (+20 dBm) | ≤ 100 mW (+20 dBm) |
接收灵敏度 | LE 2M PHY:≤-70 dBm LE 1M PHY:≤-70 dBm LE Coded PHY (S=2):≤-75 dBm LE Coded PHY (S=8):≤-82 dBm | ≤-70 dBm |
数据传输 | 异步面向连接 同步面向连接 异步无连接 同步无连接 等时无连接 | 异步面向连接 同步面向连接 |
通信拓扑 | 点对点(包括个人区域网络(Piconet微微网)) 广播 Mesh(网状) | 点对点(包括个人区域网络(Piconet 微微网)) |
定位功能 | 存在:广告 方向:测向(AoA/AoD) 距离:RSSI,信道探测 | 无 |
Bluetooth terminology
Core Architecture
蓝牙的核心系统
Host 主机
实现各种业务场景需求,大部分的开发工作基于此Controller 控制器
用于蓝牙报文的收发、蓝牙物理连接等功能,由芯片厂商负责实现Host Controller Interface(HCI) 主机控制接口
Host 和 Controller 通过 HIC 进行通信
Bluetooth Roles
蓝牙通信是指两个或多个蓝牙设备之间的通信,通信双方必须一个是主机,另一个是从机,从机和从机之间不能直接通信
Master
工作在 Master 模式的设备,可以与一个 Slave 进行连接,在此模式下可以对周围的设备进行搜索并选择需要的 Slave 进行连接
Slave
Slave 模式下的设备只能被 Master 搜索,不能主动搜索
Bluetooth Network Configuration
根据拓扑结构,蓝牙网络可分为
Piconet
微微网Scatternet
散射网Mesh
Piconet 微微网
每次建立的蓝牙无线链路,都处于微微网中,一个微微网由两个或更多占用相同物理信道的设备组成(表示这些设备是按照共用时钟和跳频序列进行同步的),其拓扑结构如下:
从机3// //从机1 -------------------- 主机\\\\ \从机2
拓扑结构(Topology Structure)是指系统中各个节点及其连接关系的组织方式或布局。它广泛应用于网络、工程、数学等领域,用来描述元素之间的连接模式,而不关注实际的物理位置或距离
Scatternet 散射网
如果多个网存在重叠的区域,就可以构成散射网,其中各个 Piconet仍然具有自己的主机,但一个 Piconet 的主机可以同时是另一个 Piconet 的从机,这样该设备就具有双重身份
如下图,此时左侧的主机同时也是右侧的从机
从机1.3 从机2.1/ // // // /
从机1.1 ---------- 主机/从机 ---------- 主机\ \\ \\ \\ \从机1.2 从机2.2
Mesh
蓝牙 4.0 之后诞生了蓝牙 Mesh,用于建立多对多设备通信的低能耗蓝牙网络,允许创建基于多个设备的大型网络,可以包含数十台、数百台甚至数千台蓝牙 Mesh 设备,设备之间可以相互进行数据传输
Bluetooth Connection Process
蓝牙首先需要通过广播或扫描发现周围设备,其次创建连接,最终组建网络传输数据
- 从机端广播
- 在大部分情况下,外围设备 (Peripheral,从机) 通过广播自己来让中心设备 (Central,主机) 发现自己,并建立 GATT (通用属性配置文件)连接,从而进行更多的消息交换
- 也有些情况是不需要连接的,只需要外围设备(外设)广播自己的信息即可,这种方式的主要目的是让外设把自己的消息发送给多个中心设备
- Peripheral 在进行广播时不断发送广播包,每发送一次广播包称为广播事件;每一次发送之间有长度为 t 的广播间隔,也称为广播事件间隔,每次广播事件都会持续一段时间,只有在此期间蓝牙芯片才会打开射频模块发送广播包,功耗较高,其余时间都处于空闲待机状态
- 当广播事件时,每一个事件包含三个广播包,分别在 37、38、39 三个信道同事广播相同的消息
- 在大部分情况下,外围设备 (Peripheral,从机) 通过广播自己来让中心设备 (Central,主机) 发现自己,并建立 GATT (通用属性配置文件)连接,从而进行更多的消息交换
- 主机端扫描
- 扫描是在一定范围内用来寻址其他 BLE 设备的过程,扫描者在扫描过程中会使用广播信道,但扫描并没有严格的时间定义和信道规则,其过程按照主机设定的扫描定时参数进行
被动扫描
扫描者仅仅监听广播包,而不向广播者发送任何数据- 在扫描过程中,如果控制器接收到符合过滤策略或其他规则的的广播包, 则向主机发送一个报告时间,其中包括广播者的设备地址、广播包中的数据、信号接收强度
主动扫描
Central 在扫描捕获广播包的同时捕获扫描响应包,并作出区分广播包
主要用于设备发现和提供基本信息,适合所有设备接收扫描响应包
用于增强广播包,满足需要获取更多信息的主动扫描设备需求
- 扫描是在一定范围内用来寻址其他 BLE 设备的过程,扫描者在扫描过程中会使用广播信道,但扫描并没有严格的时间定义和信道规则,其过程按照主机设定的扫描定时参数进行
- 主机端连接
- 连接过程如下
- Peripheral(外设,外围设备)开始广播,在发送完一个广播包的T_IFS内,开启射频窗口接收来自中心设备的数据包
- Central 接收到广播,在接收后的 T_IFS 内如果开启了 Central 的扫描回复,则 Central 将向 Peripheral 发送回复
- Peripheral 收到中心设备的回复后,做好接收准备并返回 ACK 包
- 如果 Peripheral 发送的 ACK 没有被中心设备接收到,则 Central 将一直发送回复知道超时为止,在此期间内只要外设返回过一次 ACK 就算连接成功
- 开始建立通信,Central 以接收到外设广播的时间为原点,以连接间隔为周期向 Peripheral 数据包,数据包用于同步两个设备的时钟和建立主从模式的通信,其过程如下:
同步两个设备的时钟
Peripheral 每收到 Central 发送的一个数据包,就会重新设置自己的书序原点,以便于 Central 同步- BLE 通信在建立成功后变成主从模式,Central 变成主机模式,Peripheral 变成从机,从机只能在 Central 向它发送一个数据包后才能在规定时间内把自己的数据回传给 Central
- 连接建立成功
- 外设自动停止广播,其他设备无法再查找到此外设
- 在中心设备发送数据包的间隔内,外设可以发送多个数据包
- 连接过程如下
T_ISF 、 ACK 和同步时钟
T_ISF(Inter Frame Space)
同一信道上连续传输包之间的时间间隔
ACK
ACK在通信中是接收方对发送方的反馈,用于确认数据包已成功接收;如果没有ACK,发送方无法知道数据是否到达目标,从而无法保障通信的可靠性ACK机制还可检测数据错误并触发重传,确保数据传输完整无误 同步时钟同步时钟在通信中用来协调双方的时间基准,确保数据的发送与接收按预期时序进行。如果没有同步时钟,数据可能因时序错乱而丢失或解析错误。在 BLE 中,同步时钟还能优化设备能耗,让设备在需要时准确唤醒,保持高效运行
断开连接
- 在需要断开连接时,只要 Central 停止连接(停止发送数据包)即可
重新连接
- 中心设备可以将外设的 MAC 地址写入 Flash 或是 SRAM 等存储器件,并保持监听此 MAC 地址,当再次收到 Peripheral 发送的广播包时就可以建立通信
- 从机为了省电,当一段时间没有数据要发送时,可以不在发送广播包,双方就会因为连接超时 (Connection Timeout) 而断开,此时需要 Central 启动监听
MAC地址(Media Access Control Address)是一个设备的唯一硬件地址,用于标识蓝牙设备。每个蓝牙设备在出厂时都会分配一个全球唯一的MAC地址,一个48位(6字节)的数字,通常用16进制表示,格式为 XX:XX:XX:XX:XX:XX,例如 A1:B2:C3:D4:E5:F6
Bluetooth Stack
蓝牙 API - ESP32 - — ESP-IDF 编程指南 latest 文档
ESP-IDF 目前支持两个主机堆栈(蓝牙协议栈),基于 Bluedroid 的堆栈(默认)支持传统蓝牙和低功耗蓝牙 (Bluetooth® LE),而基于 Apache NimBLE 的堆栈仅支持低功耗蓝牙
- 对于同时涉及传统蓝牙和低功耗蓝牙的用例,应该选用 Bluedroid
- 对于仅涉及低功耗蓝牙的用例,建议选用 NimBLE,在代码占用和运行时,NimBLE 对内存的要求较低,因此适用于此类场景
有关功能支持状态,详见 docs.espressif
Bluedroid 是 Android 平台和 ESP32 上常用的蓝牙协议栈。它提供了完整的蓝牙主机和控制器功能,支持蓝牙经典(BR/EDR)和蓝牙低功耗(BLE);Bluedroid 已被广泛应用于 Android 设备和 ESP32 系统,能够通过提供标准的 HCI 接口与硬件控制器通信;Bluedroid 是一个稳定的协议栈,但由于它具有较大的代码库和相对较高的资源需求,可能不适合资源有限的嵌入式设备
Apache MyNewt NimBLE 是一个轻量级、灵活、高度可配置且符合 Bluetooth® SIG 认证的蓝牙低功耗 (Bluetooth LE) 协议栈,提供主机和控制器功能,专注于低功耗蓝牙设备的高效能实现,与 Bluedroid 不同,NimBLE 更加简化,旨在提供更好的性能和更少的内存占用,适用于嵌入式应用和低功耗设备
ESP-IDF 支持专为 ESP32 平台和 FreeRTOS 移植的 NimBLE 主机栈,底层控制器与 Bluedroid 中使用的相同,提供 VHCI 接口
ESP-IDF 支持大多数 NimBLE 特性,包括蓝牙低功耗网状网络 (Bluetooth Low Energy Mesh),通过保留 NimBLE 所有现有 API 并提供一个统一的 ESP-NimBLE API 用于初始化,移植层变得更简洁,从而简化了应用开发者的工作
图源:NimBLE-based Host APIs - ESP32 - — ESP-IDF 编程指南 latest 文档
BLE User Guide — Apache Mynewt latest documentation
BLE 协议栈
-
应用层 (Application Layer)
-
主机层 (Host Layer)
-
控制器层 (Controller Layer)
-
Controller
用于硬件接口管理、链路管理等功能-
Physical Layer(PHY) 物理层
指定低功耗蓝牙所用的无线频段、调制解调方式等 -
Link Layer(LL) 链路层
负责数据发送和接收,但不负责数据解析,是蓝牙协议栈的核心
-
-
Host Controller Interface(HCI) 主机控制接口
Host 和 Controller 之间的通信接口,可以是物理形式的(UART、USB 等,常见于双芯片架构),也可以用 API 实现(常见于单芯片架构) -
Host
构建了向应用层提供接口的基础,方便应用层对蓝牙系统的访问-
Logical Link Control and Adaptation Protocol(L2CAP) 逻辑链路控制和适配协议
向上层协议(协议复用、分段、重组操作)提供连接导向和无连接的数据服务,并按通道进行流量控制和重传 -
Attribute Protocol(ATT) 属性协议
用来定义用户命令以及命令操作的数据(如读取或写入);BLE 协议栈引入了 Attribute 概念,用于描述一条条的数据;ATT 除了定义数据,也定义该数据可以使用的 ATT 命令 -
Security Manager Protocol(SMP) 安全管理器协议
负责管理 BLE 连接的加密和安全,在保证连接安全的同时不影响用户的体验 -
Generic Attribute Profile(GATT) 通用属性配置文件
规范 Attribute 中的数据内容,并运用分组 (Group) 的概念对 Attribute 进行分类管理 -
Generic Access Profile(GAP) 通用访问配置文件
对 LL 的有效数据包进行了一些规范和定义,是解析 LL 负载数据最简单的一种方式,一次 GAP 的功能及其有限,主要用来进行广播、扫描和发起连接等
-
作为应用开发者,在开发过程中主要使用主机层提供的 API 接口
BLUEDROID 内部⼤致分为 2 层:BTU 层和 BTC 层(除去 HCI ),每个层都有对应的任务来处理。BTU 层主要负责蓝⽛主机底层协议栈的处理,包括 L2CAP、GATT/ATT、SMP、GAP 以及部分规范等,并向上提供以“bta”为前缀的接⼝; BTC 层主要负责向应⽤层提供接⼝⽀持、处理基于 GATT 的规范、处理杂项等,并向应⽤ 层提供以“esp”为前缀的接⼝。所有的 API 都在 ESP_API 层,开发者应当使⽤“esp”为前缀的蓝⽛ API(特殊的除外)。
蓝牙核心规范 (Core Specification) 允许主机层和控制器层在物理上分离,此时 HCI 体现为物理接口,包括 SDIO、USB 以及 UART 等
图源:esp32_bluetooth_architecture_cn
通过软硬件分层的设计,底层不变,可以方便的切换上层程序(应用层软件)
场景一(ESP-IDF 默认)
在 ESP32 的系统上,选择 Bluedroid 为蓝牙主机,并通过 VHCI(软件实现的虚拟 HCI 接口)接口,访问控制器。此场景下,Bluedroid 和控制器都运行在同一宿主上(即 ESP32 芯片),不需要额外连接运行蓝牙主机的 PC 或其它主机设备
场景二
在 ESP32 上运行控制器(此时设备将单纯作为蓝牙控制器使用),外接一个运行蓝牙主机的设备(如运行 BlueZ 的 Linux PC、运行 Bluedroid 的 Android 等)
此场景下,控制器和主机运行在不同宿主上,与手机、PAD、PC 的使用方式比较类似
场景三
此场景与场景二类似,特别之处在于,在 BQB(或其它认证)的控制器测试下,可以将 ESP32 作为 DUT(Device Under Test),用 UART 作为 IO 接口,接上认证测试的 PC 机,即可完成认证
IDF 中的 host、controller 目录,其中存放有不同芯片型号相应的库文件及接口等
路径:
idf 安装路径\v5.3.1\esp-idf\components\bt
ESP-IDF 的 component/bt/host/bluedroid 目录说明
子文件夹 | 说明 |
---|---|
api | API 目录,所有的 API(除 Controller 相关)都在此目录下 |
bta | 蓝牙适配层,适配一些主机层底层协议的接口 |
btc | 蓝牙控制层,控制主机上层协议(包括规范)以及杂项的处理 |
common | 协议栈的通用头文件 |
config | 为协议栈配置一些参数 |
device | 与蓝牙设备控制相关的,如控制器设备的配置的 HCI CMD 流程等 |
external | 与蓝牙自身无关,但又要使用的代码,如 SBC codec 设备程序等 |
hci | HCI 层协议 |
Kconfig.in | Menuconfig 文件 |
main | 主程序目录(主要为启动、关闭流程) |
stack | 主机底层协议栈(GAP/ATT/GATT/SDP/SMP 等) |
GAP
蓝牙协议栈中 GAP 和 GATT 部分各知识点之间联系
原图较大,由于网站压缩,图像画质可能较差,原图见Github_CodeFlashier
本部分的全部内容详见: Bluetooth Core Specification v5.0 Vol3 PartC,这里只做必要内容的介绍
通用访问配置文件(Generic Access Profile,GAP)的目的如下:
- 引入与运输和应用配置文件使用的模式和访问程序相关的定义、建议和通用要求
- 描述设备在待机和连接状态下的行为,以保证蓝牙设备之间始终能够建立连接和通道,并且支持多配置文件操作;特别关注设备发现、链路建立和安全程序
- 规定用户界面方面的要求,主要是编码方案和过程与参数的命名,以确保用户体验
该配置文件的目的是描述:
- 配置文件角色
- 可发现模式和程序
- 连接模式和程序
- 安全模式和程序
GAP 与蓝牙底层架构的关系
图源:Bluetooth Core Specification v5.0
Roles when Operating over BR/EDR Physical Transport
BR/EDR 是 Bluetooth Radio/Enhanced Data Rate 的缩写,指的是蓝牙技术中的两种主要通信模式:
BR (Basic Rate)
基本速率,是蓝牙 1.x 版本中使用的传输速率,最高为 1 MbpsEDR (Enhanced Data Rate)
增强数据传输速率,是蓝牙 2.x 版本及更高版本中引入的,提供更高的传输速率,最高为 3 Mbps
BR/EDR 是蓝牙经典(Bluetooth Classic)模式的一部分,适用于需要较高数据传输速率的应用,如音频传输(例如蓝牙耳机)和文件传输
在 GAP 协议中,为了描述发生在 BR/EDR GAP 角色的两个设备之间的蓝牙通信,使用以下两个通用符号描述设备:
A-party A 方
链路建立时的分页设备(发起连接请求的设备)B-party B 方
接收连接请求的设备
在蓝牙的 GAP 协议中,“分页设备”(paging device)是指在进行蓝牙设备配对或连接时,发起连接请求的设备;分页设备通过广播其存在来寻找其他设备,并尝试建立一个连接
具体而言,“分页”(paging)指的是一种低功耗的搜索过程,设备会扫描特定的信道,并等待另一方设备响应以建立连接
该配置文件涵盖了由一个设备(A)发起,针对另一个设备(B)的程序,后者可能已经建立了蓝牙连接,也可能没有
图源:《Bluetooth Core Specification v5.0》
发起者和接受者通常根据此配置文件或引用此配置文件的其他配置文件执行通用程序;如果接受者同时根据多个配置文件操作,则此配置文件提供了处理这种情况的通用机制
Roles when Operating over an LE Physical Transport
LE 指的是 Bluetooth Low Energy(蓝牙低功耗)技术
GAP 中共定义了三种设备的连接状态以及五种不同的设备角色,如下
- 空闲 (Idle)
- 此时设备无角色,处于就绪状态 (Standby)
- 设备发现 (Device Discovery)
- 广播者 (Advertiser)
- 扫描者 (Scanner)
- 连接发起者 (Initiator)
- 连接 (Connection)
- 外围设备 (Peripheral)
- 中央设备 (Central)
Bluetooth LE GAP 协议层采⽤ API 调⽤和事件 (Event) 返回的设计模式,通过事件返回来获取 API 在协议栈的处理结果。当对端设备主动发起请求时,也是通过事件返回获取对端设备的状态。Bluetooth LE 设备定义了四类 GAP ⻆⾊:
• ⼴播者 (Broadcaster):处于这种⻆⾊的设备通过发送⼴播 (Advertising) 让接收者发现⾃⼰。这种⻆⾊只能发⼴播,不能被连接。
• 观察者 (Observer):处于这种⻆⾊的设备通过接收⼴播事件并发送扫描 (Scan) 请求。这种⻆⾊只能发送扫描请求,不能被连接。
• 外围设备 (Peripheral):当⼴播者接受了观察者发来的连接请求后就会进⼊这种⻆ ⾊。当设备进⼊了这种⻆⾊之后,将会作为从设备 (Slave) 在链路中进⾏通信。
• 中央设备 (Central):当观察者主动进⾏初始化,并建⽴⼀个物理链路时就会进⼊这种⻆⾊。这种⻆⾊在链路中同样被称为主设备 (Master)。
同时在多个 GAP 角色下运行
如果控制器支持,设备可以同时在多个GAP角色下运行,主机应在使用任何程序或模式之前,先读取控制器支持的链路层状态和状态组合
Defines compliance requirements
在 LE 物理传输上操作时,每个 GAP 角色的物理层和链路层功能的 GAP 遵从性要求
图源:Bluetooth Core Specification v5.0
符号释义
M
强制支持的功能O
可选支持的功能C
条件支持的功能E
在配置文件角色中排除的功能N/A
不适用的功能C1
如果支持被动扫描,则主动扫描是可选的,否则主动扫描是强制的C2
如果支持连接参数请求过程,则必选,否则可选
Device Connection Establishment Process
设备连接过程
- 设备 A、B 进入空闲状态
- 在上层软件控制下,A 进入广播状态,向外界进入广播状态,B 进入扫描状态
- 当 B 扫描到数据后,进入初始化状态,发送请求连接的数据给 A
- 设备 A 返回数据给 B,进入连接状态
- B 收到返回的数据后,进入连接状态
- 两个设备相互交换数据
对于部分设备,可能不需要连接(如 BLE Beacon、BLE Sniffer)
GATT
蓝牙协议栈中 GAP 和 GATT 部分各知识点之间联系
原图较大由于网站压缩,图像画质可能较差,原图见Github_CodeFlashier
本部分的全部内容详见: Bluetooth Core Specification v5.0 Vol3 PartD,这里只做必要内容的介绍
通用属性配置文件(Generic Attribute Profile,GATT)通过使用属性协议(Attribute Protocol)定义了一个服务框架,该框架定义了服务及其特性的程序和格式,所定义的程序包括发现、读取、写入、通知和指示特性,以及配置特性广播
ATT 属性协议规定了在 Bluetooth LE 中的最⼩数据存储单位,⽽ GATT 规范则定义了如何 ⽤特性值和描述符表示⼀个数据,如何把相似的数据聚合成服务 (Service),以及如何发现对端设备拥有哪些服务和数据。
ATT 与 GATT 的关系
ATT 提供了一个基础的传输框架和操作机制(例如,读取、写入和通知),而 GATT 则是 ATT 的上层协议,定义了如何组织这些属性,使得设备能够通过结构化的方式进行通信
- GATT 提供了一套服务(services)和特征(characteristics)的规范,指导如何通过 ATT 协议进行数据交换
- GATT 使用 ATT 协议的读、写和通知操作来访问设备的属性
ATT 是 GATT 的数据传输载体,而 GATT 是 ATT 的应用框架,二者结合为蓝牙低功耗设备的通信提供了完整的协议支持
Configuration and Roles
GATT 将蓝牙设备分为两种:
Client 客户端
发起命令和请求的设备,向服务器发送命令并接收服务器发送的响应、指示和通知Server 服务端
接受来自客户端的命令和请求的设备,并向客户端发送响应、指示和通知- 这些角色不是固定分配给设备的,角色是在设备发起定义的程序时确定的,并在程序结束时释放
此配置文件涵盖以下场景:
- 配置交换
- 设备上服务和特性的发现
- 读取特性值
- 写入特性值
- 特性值通知
- 特性值指示
数据以 profile(配置文件(规范))的形式存储在服务端
GATT 配置文件指定了配置文件数据交换的结构,该结构定义了配置文件中使用的基本元素,如服务(Service)和特性(Characteristic);所有这些元素都包含在属性中,属性协议中使用的属性是承载配置文件数据的容器
层次结构的顶层是配置文件,配置文件由一个或多个服务组成,这些服务是为了实现某个使用场景所必需的,一个服务由特性或对其他服务的引用组成;每个特性包含一个值,并且可能包含关于该值的可选信息
GATT Profile Hierarchy
GATT 配置文件的基本结构如下
Profile 配置文件(规范)
配置文件(规范)是最高层次,通常由一个或多个服务组成,它定义了完成某个特定使用场景所必需的所有元素Service 服务
服务由一个或多个特性组成,并且可能包含对其他服务的引用; 服务是一个逻辑集合,用于组织和管理特性;在 GATT 配置文件中,服务是为了满足特定功能需求而设计的Characteristic 特性
特性是服务的基本组成部分,每个特性通常包含一个值,并且可能包含与该值相关的描述符(如用户描述符、客户端配置描述符等);这些特性承载着实际的配置文件数据Descriptor 描述符
描述符是与特性相关的附加信息,用于进一步解释特性的值或控制特性的行为,常见的描述符包括用户描述符、客户端配置描述符等
规范是一个预定义的服务集合,实现了某规范中所定义的所有服务的设备即满足该规范。例如 Heart Rate Profile 规范由 Heart Rate Service 和 Device Information Service 两个服务组成,那么可以称实现了 Heart Rate Service 和 Device Information Service 服务的设备符合 Heart Rate Profile 规范
GATT Profile 文件层次结构
图源:《Bluetooth Core Specification v5.0》
Service
Server 服务是一组数据和相关行为的集合,用于实现特定的功能或特性,在 GATT 中,服务由其服务定义(service definition)来定义
服务定义可以包含
- 引用的服务
- 必需的特性
- 可选的特性
为了保持与早期客户端的向后兼容,服务定义的后续版本只能添加新的引用服务或可选特性,不能修改以前版本中的行为
Primary Service 主服务
是暴露设备主要功能的服务,主服务可以被其他服务包含,并且可以通过主服务发现程序进行发现Secondary Service 次服务
仅用于从主服务、另一个次服务或其他更高层规范中引用,次服务只在引用它的实体的上下文中相关
是否将一个服务定义为主服务或次服务,可能由更高层的规范进行规定,服务可以在一个或多个更高层的规范中使用,以实现特定的使用场景
Service 数据结构
- Service Declaration
- Service Include
- Characteristic 01
- Characteristic xx
特点
- 服务定义在下一个服务声明之前结束,或者在达到最大属性句柄时结束;服务定义在服务器上按属性句柄的顺序出现
- 服务定义必须包含一个服务声明(Service Declaration),并且可以包含包含定义和特性定义
- 服务定义中包含的所有包含定义和特性定义都视为服务的一部分,所有包含定义必须紧跟在服务声明之后,并且在任何特性定义之前
- 所有特性定义必须紧跟在最后一个包含定义之后,如果没有包含定义,则紧跟在服务声明之后
- 一个服务定义可以有零个或多个包含定义和特性定义,包含定义和特性定义没有上限
- 服务器上的所有属性要么包含服务声明,要么存在于服务定义内
- 服务器中包含的服务定义可以按任意顺序出现,客户端不应假设服务器上服务定义的顺序
所有的定义均以属性 Attribute (ATT 属性协议)的形式存在,内容包括
Attribute Handle 属性句柄
2 OctetsAttribute Type 属性类型
2 or 16 OctetsAttribute Value 属性值
variable lengthAttribute Permissions 属性权限
implementation specific(工具自定义)
属性的类型由 UUID 表示,可以分为 16 位、32 位与 128 位 UUID 三类。 16 位 UUID 由蓝牙技术联盟 (Bluetooth Special Interest Group, Bluetooth SIG) 统一定义,可以在其公开发布的 Assigned Numbers 文件中查询;其他两种长度的 UUID 用于表示厂商自定义的属性类型,其中 128 位 UUID 较为常用
Service Declaration
服务声明是一个属性(Attribute),
Attribute Type 属性类型
设置为 Primary Service 主服务 或 Secondary Service 次服务 的 UUIDAttribute Value 属性值
应为服务的 16 位或 128 位 UUID(参见 Assigned_Numbers.pdf),称为服务 UUID,客户端必须支持 16 位和 128 位 UUID 的使用,客户端可以忽略任何具有未知服务 UUID 的服务定义(未知服务 UUID 是指不支持的服务的 UUID)- SIG 定义的 16 位 UUID
- 厂商自定义的 128 位 UUID (在 SIG 官方提供的 Assigned Numbers 标准文件中,给出了一些常用特征数据和服务的 UUID)
Attribute Permission 属性权限
应为只读,并且不要求身份验证或授权
UUID(通用唯一标识符) 是一种 128 位的标识符,广泛用于标识不同的对象、服务或数据元素,确保它们在全球范围内的唯一性
在蓝牙技术中,UUID 被用于区分和标识不同的服务、特性和描述符
UUID 可以是 16 位或 128 位格式,其中 16 位 UUID 常用于标准服务,而 128 位 UUID 通常用于自定义服务,UUID 的使用确保了设备间的兼容性和通信的准确性
Bluetooth UUID 是一种特定于蓝牙协议的 UUID,用于标识蓝牙设备中的服务、特性和描述符。与通用的 UUID(通用唯一标识符)相比,Bluetooth UUID 通常采用 16 位、32 位 或 128 位 格式,其中 16 位 UUID 用于标准蓝牙服务
规范-分配的编号-蓝牙®技术网站
服务声明结构
图源:《Bluetooth Core Specification v5.0》
当存在多个服务时,使用不同位数 UUID 的服务定义应当分组在一起(使用 16 位 Bluetooth UUID 的服务定义分组在一起(即按顺序列出),使用 128 位 UUID 的服务定义分组在一起)
一个设备或更高层规范可以有多个服务定义,且可以有多个服务定义使用相同的服务 UUID
Service Include
包含服务是一种将服务器上存在的另一个服务定义引用到当前定义的服务中的方法
要包含另一个服务,在服务定义的开始部分使用包含定义(include definition)
特点
- 通过包含定义引用的服务的整个服务定义将成为新服务定义的一部分,这包括所有包含的服务和该服务的特性
- 被包含的服务仍然是独立的服务
- 一个被包含的服务不应因包含或包含的服务而被修改,服务定义中对包含定义或嵌套包含的数量没有限制
Include Declaration
一个 包含定义(include definition) 应仅包含一个包含声明(include declaration)
Attribute Type
设置为 Include 的 UUIDAttribute Value
- 包含服务的属性句柄
- 结束组句柄(每个服务或服务的一部分都有一个 开始句柄(Start Handle,即 Attribute Handle) 和 结束句柄(End Handle),它们定义了服务的范围)
- 所包含服务的 UUID
Attribute Permission
应为只读,并且不要求身份验证或授权
包含声明结构
图源:《Bluetooth Core Specification v5.0》
当 UUID 是 16 位 Bluetooth UUID 时,服务 UUID 必须被直接指定
GATT(Generic Attribute Profile) 中,当一个服务使用 16 位 Bluetooth UUID 时,服务 UUID 必须在服务的定义中明确指定
在某些情况下,如某些包含(Include)服务,UUID 可能通过引用的其他服务的 Attribute Handle 来隐式获得,但这并不等同于“未指定”UUID
服务定义中的包含声明不能指向自己,这适用于包含定义引用的每个服务(这被称为 循环引用(circular reference));如果客户端检测到循环引用或检测到嵌套包含声明超过其预期的层次,客户端应终止 ATT 承载通道(ATT Bearer)
Characteristic
Characteristic 特性是服务中使用的值,伴随有关于如何访问该值的属性和配置信息,以及关于如何显示或表示该值的信息
一个特性定义(Characteristic Definition)应包含
- 特性声明(Characteristic Declaration)
- 特性值声明(Characteristic Value Declaration)
并且可能包含
- 特性描述符声明(Characteristic Descriptor Declarations)
特点
- 特性值声明应紧随其后放置在特性声明之后,任何可选的特性描述符声明应放置在特性值声明之后,可选的特性描述符声明的顺序没有限制
- 特性定义的结束位置是下一个特性声明、服务声明的开始,或者是最大 Attribute Handle,特性定义在服务器的服务定义中按 Attribute Handle 顺序出现
- 特性定义可以通过将多个特性值合并为一个单独的聚合特性值来进行定义,这可以用于通过读取和写入单个聚合特性值来优化多个特性值的读取和写入;这类特性定义与普通的特性定义相同,特性声明应使用唯一的特性 UUID 来标识该聚合特性定义;聚合特性定义还可以包含一个特性聚合格式描述符,用于描述聚合特性值的显示格式
Characteristic Declaration
一个 特性声明(Characteristic Declaration) 是一个 Attribute
Attribute Type
设置为特性的 UUIDAttribute Value
- 特性属性(Characteristic Properties)
- 特性值属性句柄(Characteristic Value Attribute Handle)
- 特性 UUID
Attribute Permission
应为可读,并且不要求身份验证或授权
特性声明的 Attribute Value 在服务器与任何客户端建立信任关系时不得更改
特征声明结构
图源:《Bluetooth Core Specification v5.0》
Attribute Value
Attribute Value | Size | Description |
---|---|---|
Characteristic Properties | 1 octets(1 个 8 位数据) | 特性属性的位域 |
Characteristic Value Handle | 2 octets | 包含此特性值的属性的句柄 |
Characteristic UUID | 2 or 16 octets | 16 位 Bluetooth UUID 或 128 位 UUID,用于特性值 |
一个服务可以有多个具有相同特征 UUID的特征定义
在服务定义中,某些特征可能是必需的,这些特性应位于包含声明(include declarations)之后、任何可选特性之前
客户端不应假设服务定义中必需特性或可选特性的顺序
尽可能地,并且在前述要求范围内,使用 16 位 Bluetooth UUID 的特性声明应当按顺序分组在一起(即按顺序列出),而使用 128 位 UUID 的特性声明应当分组在一起
Characteristic Properties
特性属性位域(Characteristic Properties)决定了特性值如何使用,或者如何访问特性描述符,可以设置多个 特性属性
这些位应根据该特性所允许的程序进行设置,并由更高层规范定义,而不考虑安全要求
Properties | Value | 描述 |
---|---|---|
Broadcast | 0x01 | 如果设置,则允许使用服务器特性配置描述符广播特性值 如果设置,必须存在服务器特性配置描述符 |
Read | 0x02 | 如果设置,则允许使用第 4.8 节中定义的程序读取特性值 |
Write Without Response | 0x04 | 如果设置,则允许使用第 4.9.1 节中定义的程序写入特性值且不要求响应 |
Write | 0x08 | 如果设置,则允许使用第 4.9.3 或第 4.9.4 节中定义的程序写入特性值并要求响应 |
Notify | 0x10 | 如果设置,则允许在没有确认的情况下使用第 4.10 节中定义的程序通知特性值 如果设置,则必须存在客户端特性配置描述符 |
Indicate | 0x20 | 如果设置,则允许在有确认的情况下使用第 4.11 节中定义的程序指示特性值 如果设置,则必须存在客户端特性配置描述符 |
Authenticated Signed Writes | 0x40 | 如果设置,则允许使用第 4.9.2 节中定义的程序向特性值写入签名 |
Extended Properties | 0x80 | 如果设置,则在第 3.3.3.1 节中定义的特性扩展属性描述符中定义附加特性属性 如果设置,则必须存在特性扩展属性描述符 |
见《Bluetooth Core Specification v5.0》 P2236
Characteristic Value Handle
特性值属性句柄字段(Characteristic Value Attribute Handle) 是指包含特性值(Characteristic Value) 的属性的属性句柄(Attribute Handle)
Characteristic UUID
特性 UUID 字段 是一个 16 位 Bluetooth UUID 或 128 位 UUID,用于描述特性值(Characteristic Value) 的类型。客户端必须支持使用 16 位 和 128 位 特性 UUID
如果客户端遇到一个具有未知 特性 UUID 的特性定义(不支持的特性所使用的 UUID),可以忽略该特性
Characteristic Value Declaration
特性值声明(Characteristic Value Declaration) 包含特性值,它是特性声明之后的第一个属性,所有特性定义必须具有特性值声明
特性值声明是一个 Attribute
Attribute Type
设置为在特性声明中使用的 16 位 Bluetooth UUID 或 128 位 UUID,用于标识特性值Attribute Value
设置为特性值Attribute Permissions
由服务指定,或者如果没有特别指定,则可以由实现决定
ESP32 IDF ⾥⾯规定 MTU 可以设置的范围是 23~517 字节,对属性值的总⻓度没有做限制
特征值声明结构
图源:《Bluetooth Core Specification v5.0》
Characteristic Descriptor Declarations
特性描述符(Characteristic Descriptors)用于包含与特性值(Characteristic Value)相关的信息
GATT 配置文件定义了一组标准的特性描述符,可以供更高层配置文件使用,更高层的配置文件可能会定义额外的特性描述符,这些描述符是特定于该配置文件的
每个特性描述符由特性描述符 UUID 标识,客户端必须支持使用 16 位 和 128 位 特性描述符 UUID;如果客户端遇到一个具有未知特性描述符 UUID 的特性描述符声明,可以忽略该描述符
如果特性描述符存在于特性定义中,它们应跟随特性值声明之后;特性描述符声明可以按任意顺序出现在特性定义中,客户端不应假设特性描述符声明在特性值声明之后的顺序
特性描述符声明的权限由更高层的配置文件定义,或者如果未指定,则由实现决定
客户端不应假设所有的特性描述符声明都是可读的
Characteristic Extended Properties
特性扩展属性(Characteristic Extended Properties)声明是一个描述符,用于定义附加的特性属性;如果特性属性中的扩展属性位被设置,则该特性描述符必须存在
在一个特性定义中只能有一个特性扩展属性声明
特性描述符包含在一个 Attribute 中
Attribute Type
应设置为 “特性扩展属性(Characteristic Extended Properties)” 的 UUIDAttribute Value
应设置为特性扩展属性位域(Characteristic Extended Properties Bit Field)Attribute Permissions
应为可读,不需要身份验证和授权
特征扩展属性结构
图源:《Bluetooth Core Specification v5.0》
特性扩展属性位域描述了如何使用特性值(Characteristic Value) 或如何访问特性描述符
如果表中定义的位被设置,则表示允许执行相应的操作;可以设置多个 特性属性
Properties | Value | Description |
---|---|---|
Reliable Write | 0x0001 | 如果设置,则允许使用第 4.9.5 节中定义的程序进行可靠的特性值写入 |
Writable Auxiliaries | 0x0002 | 如果设置,则允许写入第 3.3.3.2 节中定义的特性描述符 |
Reserved for Future Use | 0xFFFC | 保留供未来使用 |
Characteristic User Description
特性用户描述(Characteristic User Description) 声明是一个可选的特性描述符,用于定义一个可变大小的 UTF-8 字符串,它是对特性值(Characteristic Value) 的用户文本描述;如果特性属性(Characteristic Properties) 中的可写辅助位(Writable Auxiliary) 被设置,则该特性描述符可以被写入
在一个特性定义中只能有一个特性用户描述声明
特性描述符包含在一个 Attribute 中
Attribute Type
应设置为 “特性用户描述(Characteristic User Description)” 的 UUIDAttribute Value
应设置为特性用户描述的 UTF-8 字符串Attribute Permissions
由配置文件指定,或者如果未特别指定,则可以由实现决定
特征用户描述结构
图源:《Bluetooth Core Specification v5.0》
Client Characteristic Configuration
客户端特性配置(Client Characteristic Configuration) 声明是一个可选的特性描述符,用于定义特性如何由特定客户端进行配置
客户端特性配置描述符的值应在已配对设备之间的连接中保持持久性;在与非配对设备的每次连接中,客户端特性配置描述符的值应设置为默认值
该特性描述符值是一个 位域(bit field),当某个位被设置时,相应的操作将被启用,否则将不使用
在一个特性定义中只能有一个客户端特性配置声明
客户端可以写入此配置描述符来控制服务器上该特性的配置,每个客户端都有自己独立的客户端特性配置 实例,读取客户端特性配置仅显示该客户端的配置,写入仅影响该客户端的配置
写入配置描述符时,服务器可能需要身份验证和授权
客户端特性配置声明应为可读和可写,该特性描述符包含在一个 Attribute 中
Attribute Type
应设置为 “客户端特性配置(Client Characteristic Configuration)” 的 UUIDAttribute Value
应设置为特性描述符值Attribute Permissions
由配置文件指定,或如果未特别指定,则可以由实现决定
客户端特性配置结构
图源:《Bluetooth Core Specification v5.0》
客户端特性配置位定义
Configuration | Value | Description |
---|---|---|
Notification | 0x0001 | 特性值将被通知。只有在特性属性设置了通知位时,此值才可以设置 |
Indication | 0x0002 | 特性值将被指示。只有在特性属性设置了指示位时,此值才可以设置 |
Reserved for Future Use | 0xFFFC | 保留供未来使用 |
- 客户端特性配置描述符值 的默认值应为 0x0000
- 如果 GATT 服务器 支持来自同一设备的多个 ATT 承载通道(ATT bearers),则每个 ATT 承载通道应视为具有独立的 GATT 客户端实例,因此,每个 GATT 客户端应拥有独立的 客户端特性配置
Notification 通知
是一种无确认的数据传输方式,服务器发送数据后无需等待客户端的响应;这使得通知在需要高效、频繁更新数据的场景中非常适用,但不能保证数据已经被客户端成功接收
Indication 指示
则是一种有确认的传输方式,服务器发送数据后会等待客户端的确认响应;这确保了客户端已经成功接收数据,因此适用于那些需要可靠传输和确保数据到达的场景,但相较通知,指示会增加一定的延迟和开销
Server Characteristic Configuration
服务器特性配置(Server Characteristic Configuration) 声明是一个可选的特性描述符,用于定义如何为服务器配置特性
特性描述符值是一个 位域(bit field),当某个位被设置时,相应的操作将被启用,否则将不使用
在一个特性定义中只能有一个服务器特性配置声明
服务器特性配置声明应为可读和可写,客户端可以写入该配置描述符来控制服务器上所有客户端的特性配置;对于所有客户端来说,服务器特性配置是单一实例,读取服务器特性配置显示的是所有客户端的配置,而写入则会影响所有客户端的配置
服务器可能需要身份验证和授权才能写入配置描述符
该特性描述符包含在一个 Attribute 中
Attribute Type
应设置为 “服务器特性配置(Server Characteristic Configuration)” 的 UUIDAttribute Value
应设置为特性描述符值Attribute Permissions
由配置文件指定,或者如果没有特别指定,则可以由实现决定
服务器特性配置结构
图源:《Bluetooth Core Specification v5.0》
服务器特性配置位定义
Configuration | Value | 描述 |
---|---|---|
Broadcast | 0x0001 | 当服务器处于广播过程并且广告数据资源可用时,特性值将被广播 只有在特性属性设置了广播位时,此值才可以设置 |
Reserved for Future Use | 0xFFFE | 保留供未来使用 |
Characteristic Presentation Format
特性展示格式(Characteristic Presentation Format) 声明是一个可选的特性描述符,用于定义特性值(Characteristic Value) 的格式,即如何解析特征值
如果一个特性定义中存在多个特性展示格式声明(Characteristic Presentation Format declarations),则必须存在特性聚合格式声明(Characteristic Aggregate Format declaration) 作为该特性定义的一部分
特性格式值由五个部分组成:
- 格式(format)
- 指数(exponent)
- 单位(unit)
- 命名空间(name space)
- 描述(description)
该特性描述符包含在一个Attribute 中
Attribute Type
应设置为 “特性格式(Characteristic Format)” 的 UUIDAttribute Value
应设置为特性描述符值Attribute Permissions
应为只读,并且不需要身份验证或授权
特征展示格式属性结构
图源:《Bluetooth Core Specification v5.0》
特性展示格式描述符属性值字段的定义
Field Name | Value Size | Description |
---|---|---|
Format | 1 octet | 该特性值的格式 |
Exponent | 1 octet | 指数字段,用于确定该特性值如何进一步格式化 |
Unit | 2 octets | 该特性的单位,如在《Assigned Number Specification》中定义 |
Name Space | 1 octet | 描述的命名空间,如在《Assigned Number Specification》中定义 |
Description | 2 octets | 该特性的描述,如在更高层配置文件中定义 |
位序
Characteristic Format descriptor 使用的位序应该是 小端序(little-endian)(低位字节存储在低地址,而高位字节存储在 高地址,即数据的 最低有效字节(LSB) 放在最前面)
格式
格式(Format) 字段决定了 特性值(Characteristic Value) 中单个值的格式
如果格式不是一个完整的字节数,则数据应以能够容纳该值的最小字节数进行存储;数据应占用每个字节的全部内容,除了最后一个字节的最高有效位;最后一个字节的其他位应预留供未来使用
带符号整数应使用二进制补码表示法(two’s-complement representation)
定义的格式值
Format | Short Name | Description | Exponent Value |
---|---|---|---|
0x00 | rfu | Reserved for Future Use | No |
0x01 | boolean | unsigned 1-bit; 0 = false, 1 = true | No |
0x02 | 2bit | unsigned 2-bit integer | No |
0x03 | nibble | unsigned 4-bit integer | No |
0x04 | uint8 | unsigned 8-bit integer | Yes |
0x05 | uint12 | unsigned 12-bit integer | Yes |
0x06 | uint16 | unsigned 16-bit integer | Yes |
0x07 | uint24 | unsigned 24-bit integer | Yes |
0x08 | uint32 | unsigned 32-bit integer | Yes |
0x09 | uint48 | unsigned 48-bit integer | Yes |
0x0A | uint64 | unsigned 64-bit integer | Yes |
0x0B | uint128 | unsigned 128-bit integer | Yes |
0x0C | sint8 | signed 8-bit integer | Yes |
0x0D | sint12 | signed 12-bit integer | Yes |
0x0E | sint16 | signed 16-bit integer | Yes |
0x0F | sint24 | signed 24-bit integer | Yes |
0x10 | sint32 | signed 32-bit integer | Yes |
0x11 | sint48 | signed 48-bit integer | Yes |
0x12 | sint64 | signed 64-bit integer | Yes |
0x13 | sint128 | signed 128-bit integer | Yes |
0x14 | float32 | IEEE-754 32-bit floating point | No |
0x15 | float64 | IEEE-754 64-bit floating point | No |
0x16 | SFLOAT | IEEE-11073 16-bit SFLOAT | No |
0x17 | FLOAT | IEEE-11073 32-bit FLOAT | No |
0x18 | duint16 | IEEE-20601 format | No |
0x19 | utf8s | UTF-8 string | No |
0x1A | utf16s | UTF-16 string | No |
0x1B | struct | Opaque structure | No |
0x1C – 0xFF | rfu | Reserved for Future Use | No |
- 当编码 IPv4 地址 时,应使用 uint32 格式类型
- 当编码 IPv6 地址 时,应使用 uint128 格式类型
- 当编码 蓝牙 BD_ADDR 时,应使用 uint48 格式类型
- duint16 是由两个 uint16 值连接在一起组成的
Exponent
指数(Exponent) 字段与整数数据类型一起使用,用于确定值如何进一步格式化
指数字段仅在 格式字段(format field) 中指定为整数格式类型时使用,是二进制补码有符号整数
实际值 = 特性值 × 1 0 指数 \text{实际值} = \text{特性值} \times 10^{\text{指数}} 实际值=特性值×10指数
实际值是特性值 与 10 的 指数(Exponent) 次方的组合
Unit
单位(Unit) 是一个 UUID,如在 分配编号文档(Assigned Numbers document) 中定义
Name Space
命名空间(Name Space) 字段用于标识 负责定义描述字段枚举值的组织,该组织在分配编号文档中有定义
Description
描述(Description) 是一个枚举值,如在 分配编号文档(Assigned Numbers document)中定义,来自 命名空间字段(Name Space) 中标识的组织
Characteristic Aggregate Format
特性聚合格式(Characteristic Aggregate Format) 声明是一个可选的特性描述符,用于定义聚合的特性值(Characteristic Value) 的格式
在一个特性定义中只能有一个特性聚合格式声明
特性聚合格式值由特性展示格式声明(Characteristic Presentation Format declarations) 的属性句柄列表(Attribute Handles) 组成,每个属性句柄指向一个特性展示格式声明,Attribute Permissions 应为只读,并且不需要身份验证或授权
属性句柄列表是多个 16 位属性句柄(Attribute Handle) 值连接成一个 Attribute Value,该列表至少应包含两个特性展示格式声明(Characteristic Presentation Format declarations) 的属性句柄
特性值应根据每个由属性句柄指向的特性展示格式声明进行解构,列表中属性句柄的顺序是有意义的
如果在一个特性定义中存在多个特性展示格式声明,则必须有一个特性聚合格式声明;该声明应在属性句柄列表中包含特性定义中的每个特性展示格式声明;其他特性定义中的特性展示格式声明也可以被使用
特性聚合格式属性结构
图源:《Bluetooth Core Specification v5.0》
GATT Profile Attribute Types
Attribute Type | UUID | Description |
---|---|---|
Primary Service | 0x2800 | Primary Service Declaration |
Secondary Service | 0x2801 | Secondary Service Declaration |
Include | 0x2802 | Include Declaration |
Characteristic | 0x2803 | Characteristic Declaration |
Characteristic Extended Properties | 0x2900 | Characteristic Extended Properties |
Characteristic User Description | 0x2901 | Characteristic User Description Descriptor |
Client Characteristic Configuration | 0x2902 | Client Characteristic Configuration Descriptor |
Server Characteristic Configuration | 0x2903 | Server Characteristic Configuration Descriptor |
Characteristic Format | 0x2904 | Characteristic Format Descriptor |
Characteristic Aggregate Format | 0x2905 | Characteristic Aggregate Format Descriptor |
GATT Send Command
命令代码 + 输入参数 + 授权
GATT Server
BLE 通信中 Server 和 Client 工作流程图解
原图较大由于网站压缩,图像画质可能较差,原图见Github_CodeFlashier
我们把存有数据(即属性)的设备叫做服务器 (Server),⽽将获取别⼈设备数据的设备叫做客户端 (Client)
GAP与GATT的关系
维度 | GAP | GATT |
---|---|---|
层级 | 控制层(决定设备可见性和连接方式) | 数据层(定义连接后的数据传输规则) |
工作阶段 | 连接前(广播、发现、配对) | 连接后(数据读写、服务交互) |
依赖关系 | GATT依赖GAP建立的连接 | GAP为GATT提供通信基础 |
典型应用 | 设备配对、广播信息 | 传感器数据读取、设备控制 |
GATT Server Work Flow
此过程指的是初始化和启动 GATT 服务器所涉及的步骤,其中包括设置GATT服务、特征和描述符等任务,并使服务器准备好处理来自 GATT 客户端的请求
GAP Callback 处理客户端的连接、扫描等事件
GATT Callback 在客户端连接成功后,底层协议栈会发送 GATT 事件到上层程序进行处理,从而完成客户端对服务端数据的读写
GATT 客户端在与 GATT 服务器初次建立通信时,会从 GATT 服务器拉取属性表中的元信息,从而获取 GATT 服务器上可用的服务以及数据特征,这一过程被称为服务发现 (Service Discovery)
属性表中包含:
参数 | 作用 |
---|---|
auto_rsp | 控制谁处理读/写操作的响应 如果设置为 ESP_GATT_RSP_BY_APP ,则由应用程序负责生成响应;如果设置为 ESP_GATT_AUTO_RSP ,则 GATT 协议栈会自动生成响应。 |
uuid_length | UUID 的长度(以字节为单位) |
*uuid_p | 指向 UUID 值的指针 |
perm | 属性的权限,由 esp_gatt_perm_t 定义 |
max_length | 属性值的最大长度 |
length | 属性值的当前长度 |
*value | 指向属性值数组的指针 |
创建 GATT Profile 实例,用于后续功能实现
struct gatts_profile_inst // GATT Profile 实例
{ esp_gatts_cb_t gatts_cb; // GATT 事件回调函数 uint16_t gatts_if; // GATT 接口 uint16_t app_id; // Application ID uint16_t conn_id; // 连接 ID uint16_t service_handle; // 服务句柄 esp_gatt_srvc_id_t service_id; // 服务 ID uint16_t char_handle; // 特征值句柄 esp_bt_uuid_t char_uuid; // 特征值 UUID esp_gatt_perm_t perm; // 特征值权限 esp_gatt_char_prop_t property; // 特征值属性 uint16_t descr_handle; // 描述符句柄 esp_bt_uuid_t descr_uuid; // 描述符 UUID};
BLE Server 设置主要步骤
- 定义服务和特征值:通过
gatt_db
属性表 - 创建服务:通过
esp_ble_gatts_create_attr_tab
- 启动服务:通过
esp_ble_gatts_start_service
- 注册服务:通过
esp_ble_gatts_app_register
- 处理事件:通过
gatts_event_handler
- 设置广播:通过
esp_ble_gap_config_adv_data
或esp_ble_gap_config_adv_data_raw
Advertising
在 Bluetooth LE 4.2 标准中, RF 信道分为两种类型,如下
类型 | 数量 | 编号 | 作用 |
---|---|---|---|
广播信道 (Advertising Channel) | 3 | 37-39 | 用于发送广播数据包和扫描响应数据包 |
数据信道 (Data Channel) | 37 | 0-36 | 用于发送数据通道数据包 |
广播者在广播时,会在 37-39 这三个广播信道中进行广播数据包的发送
在三个广播信道的广播数据包均发送完毕后,可以认为一次广播结束,广播者会在下一次广播时刻到来时重复上述过程
广播数据包的最外层包含四个部分,分别是
序号 | 名称 | 字节数 | 功能 |
---|---|---|---|
1 | 预置码 (Preamble) | 1 | 特殊的比特序列,用于设备时钟同步 |
2 | 访问地址 (Access Address) | 4 | 标记广播数据包的地址 |
3 | 协议数据单元 (Protocol Data Unit, PDU) | 2-39 | 有效数据的存放区域 |
4 | 循环冗余校验和 (Cyclic Redundancy Check, CRC) | 3 | 用于循环冗余校验 |
广播参数配置结构体:
typedef struct {uint16_t adv_int_min; /*!< 无定向广播和低占空比定向广播的最小广播间隔。范围:0x0020 到 0x4000,默认值:N = 0x0800(1.28 秒)。时间 = N * 0.625 毫秒,时间范围:20 毫秒到 10.24 秒。 */uint16_t adv_int_max; /*!< 无定向广播和低占空比定向广播的最大广播间隔。范围:0x0020 到 0x4000,默认值:N = 0x0800(1.28 秒)。时间 = N * 0.625 毫秒,时间范围:20 毫秒到 10.24 秒。 */esp_ble_adv_type_t adv_type; /*!< 广播类型 */esp_ble_addr_type_t own_addr_type; /*!< 本地蓝牙设备地址类型 */esp_bd_addr_t peer_addr; /*!< 目标设备的蓝牙设备地址 */esp_ble_addr_type_t peer_addr_type; /*!< 目标设备的蓝牙设备地址类型,仅支持公共地址类型和随机地址类型 */esp_ble_adv_channel_t channel_map; /*!< 广播信道映射 */esp_ble_adv_filter_t adv_filter_policy; /*!< 广播过滤策略 */
} esp_ble_adv_params_t;---static esp_ble_adv_params_t adv_params = { .adv_int_min = 0x20, // 广播间隔最小值,单位为 0.625ms .adv_int_max = 0x40, .adv_type = ADV_TYPE_IND, // 使用 ADV_IND 为蓝牙5.0以下的 PDUs .own_addr_type = BLE_ADDR_TYPE_PUBLIC, // 自身地址类型,此处为公共地址 .channel_map = ADV_CHNL_ALL, // 广播通道,使用所有通道 .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, // 广播过滤策略,允许扫描和连接
};
MTU
MTU ( Maximum Transmission Unit 最大传输单元)指定发送方在通道上能够接收的最大SDU(服务数据单元)大小
服务器和客户端之间的交互操作都是通过上述的消息 ATT PDU 实现的。每个设备可以指定⾃⼰设备⽀持的最⼤ ATT 消息⻓度,我们称之为 MTU。ESP32 IDF ⾥⾯规定 MTU 可以设置的范围是 23~517 字节,对属性值的总⻓度没有做限制
如果⽤户需要发送的数据包⻓度⼤于 (MTU-3)*,则需要调⽤准⼊写⼊请求 (Prepare Write Request) 来完成数据的写操作。同理,在读取⼀个数据时候,如果数据的⻓度超过 (MTU- 1),则需要通过⼤对象读取请求 (Read Blob Request) 来继续读取剩余的值。
其类型为 0x01,负载长度为 2 个八位字节(octet),并携带两个八位字节的 MTU 大小值;与 B 帧长度字段不同,I 帧长度字段可以大于配置的 MTU,因为它包括了控制字段、L2CAP SDU 长度(如果存在)、帧校验序列字段以及信息字节的长度
MTU 选项格式
图源:《Bluetooth Core Specification v5.0》
MTU不是协商值,它是一个信息性参数,每个设备可以独立指定;它向远程设备表明,当前设备能够在该通道中接收比最低要求更大的MTU
MTU 的默认值为 23 字节,恰为 Bluetooth LE 4.2 之前单个数据 PDU 的最大可承载 ATT 数据字节数
MTU 可以设定为更大的值,例如 140 字节。在 Bluetooth LE 4.2 以前,由于有效负载中最多只有 23 字节可以承载 ATT 数据,所以必须将完整的一包 ATT 数据包拆分成若干份,分散到多个数据 PDU 中。在 Bluetooth LE 4.2 以后,单个数据 PDU 最多可以承载 247 字节 ATT 数据,所以 MTU 为 140 字节时仍然可以使用单个数据 PDU 承载
Broadcast Frame B 帧
用于传输控制信息或链路管理数据,包含控制字段和帧检查序列等额外的管理信息,不直接传输应用数据
Information Frame I 帧
用于传输实际的应用数据,包含有效负载和控制信息,它通常包括L2CAP SDU长度、帧校验序列等,用于数据传输
L2CAP SDU (L2CAP Service Data Unit)
是L2CAP(逻辑链路控制和适配协议)中的一种数据单位,它指的是 L2CAP 协议层所处理的完整数据单元;L2CAP 用于蓝牙设备之间的通信,负责数据分段和重组、流量控制、错误检测等功能
在响应配置请求时,指定通道的 MTU 大小时应遵循以下规则:
- 如果请求中指定的 MTU 大于或等于通道的最小 MTU ,则应接受该请求
- 如果请求中指定的 MTU 小于通道的最小MTU,则该请求可能会被拒绝
PDU
PDU 段为有效数据存放的区域,其结构如下
序号 | 名称 | 字节数 |
---|---|---|
1 | 头 (Header) | 2 |
2 | 有效负载 (Payload) | 0-37 |
PDU 头中含有较多信息,可以分为以下六个部分 |
序号 | 名称 | 比特位数 | 备注 |
---|---|---|---|
1 | PDU 类型 (PDU Type) | 4 | |
2 | 保留位 (Reserved for Future Use, RFU) | 1 | |
3 | 通道选择位 (Channel Selection Bit, ChSel) | 1 | 标记广播者是否支持 LE Channel Selection Algorithm #2 通道选择算法 |
4 | 发送地址类型 (Tx Address, TxAdd) | 1 | 0/1 分别表示公共地址/随机地址 |
5 | 接收地址类型 (Rx Address, RxAdd) | 1 | 0/1 分别表示公共地址/随机地址 |
6 | 有效负载长度 (Payload Length) | 8 |
PDU 类型位反映了设备的广播行为。在蓝牙标准中,共有以下三对广播行为
- 可连接 (Connectable) 与 不可连接 (Non-connectable)
- 是否接受其他设备的连接请求
- 可扫描 (Scannable) 与 不可扫描 (Non-scannable)
- 是否接受其他设备的扫描请求
- 不定向 (Undirected) 与 定向 (Directed)
- 是否发送广播数据至指定设备
上述广播行为可以组合成以下四种常见的广播类型,对应四种不同的 PDU 类型
- 是否发送广播数据至指定设备
可连接? | 可扫描? | 不定向? | PDU 类型 | 作用 |
---|---|---|---|---|
是 | 是 | 是 | ADV_IND | 最常见的广播类型 |
是 | 否 | 否 | ADV_DIRECT_IND | 常用于已知设备重连 |
否 | 否 | 是 | ADV_NONCONN_IND | 作为信标设备,仅向外发送广播数据 |
否 | 是 | 是 | ADV_SCAN_IND | 作为信标设备,一般用于广播数据包长度不足的情况,此时可以通过扫描响应向外发送额外的数据 |
PDU 有效负载也分为两部分
序号 | 名称 | 字节数 | 备注 |
---|---|---|---|
1 | 广播地址 (Advertisement Address, AdvA) | 6 | 广播设备的 48 位蓝牙地址 |
2 | 广播数据 (Advertisement Data, AdvData) | 0-31 | 由若干广播数据结构 (Advertisement Data Structure) 组成 |
蓝牙地址,可以分为
类型 | 说明 |
---|---|
公共地址 (Public Address) | 全球范围内独一无二的固定设备地址,厂商必须为此到 IEEE 组织注册并缴纳一定费用 |
随机地址 (Random Address) | 随机生成的地址 |
随机地址又根据用途分为两类
类型 | 说明 |
---|---|
随机静态地址 (Random Static Address) | 可以随固件固化于设备,也可以在设备启动时随机生成,但在设备运行过程中不得变更;常作为公共地址的平替 |
随机私有地址 (Random Private Address) | 可在设备运行过程中周期性变更,避免被其他设备追踪 |
若使用随机私有地址的设备要与其他受信任的设备通信,则应使用身份解析秘钥 (Identity Resolving Key, IRK) 生成随机地址,此时其他持有相同 IRK 的设备可以解析并得到设备的真实地址,此时,随机私有地址又可以分为两类
类型 | 说明 |
---|---|
可解析随机私有地址 (Resolvable Random Private Address) | 可通过 IRK 解析得到设备真实地址 |
不可解析随机私有地址 (Non-resolvable Random Private Address) | 完全随机的地址,仅用于防止设备被追踪,非常少用 |
广播数据
一个广播数据结构的格式定义如下
序号 | 名称 | 字节数 | 备注 |
---|---|---|---|
1 | 数据长度 (AD Length) | 1 | |
2 | 数据类型 (AD Type) | n | 大部分数据类型占用 1 字节 |
3 | 数据 (AD Data) | (AD Length - n) |
Advertising Flow
设备在 2.4 GHz ISM 频段广播数据
2.4 GHz ISM 频段是一个全球可用的免费无线电频段,不被任何国家以军事用途等理由管控,也无需向任何组织支付许可费用,因此该频段的可用性极高,且没有任何使用成本
这也意味着 2.4 GHz ISM 频段非常拥挤,可能会与其他无线通信协议发生数据冲突,如 2.4 GHz WiFi
Bluetooth LE 应用了自适应跳频技术 (Adaptive Frequency Hopping, AFH) ,该技术可以判断 RF 信道的拥挤程度,通过跳频避开拥挤的 RF 信道,以提高通信质量
Bluetooth LE ⼴播主要有 5 种类型,分别为:可连接可扫描⾮定向⼴播 (Connectable scannable undirected mode)、⾼占空⽐定向⼴播 (High duty cycle directed event type)、可扫描⾮定向⼴播 (Scannable undirected mode)、不可连接⾮定向⼴播 (Non-connectable undirected mode)、可连接低占空⽐定向⼴播 (Connectable low duty cycle directed mode)。
Advertising State
Undirected Advertising
设备通过启用广播进入广播状态,在此之前必须先配置好广播参数
不定向广播
图源:《Bluetooth Core Specification v5.0》
Host A
蓝牙设备 A 的上层(主机)
LL A
蓝牙设备 A 的链接层(link layer)
Advertising Flow
LE Set Advertising Parameters
Host 发送广播参数命令到 LLCommand Complete
LL 设置成功后返回命令完成 Event 到 HostLE Read Advertising Channel Tx Power
Host 读取广播 Tx 的功率Command Complete
LL 返回命令完成 Event 到 HostLE Set Advertising Data
Host 设置广播数据Command Complete
LL 返回命令完成 Event 到 HostLE Set Scan Response Data
Host 设置扫描应答数据Command Complete
LL 返回命令完成 Event 到 HostLE Set Advertising Enable(Enable)
Host 发送使能命令Command Complete
LL 返回命令完成 Event 到 HostAdvert
LL 开始广播数据(从 LL B 到 LL A)LE Set Advertising Enable(Disable)
Host 发送停止广播命令Command Complete
LL 返回命令完成 Event 到 Host
属性 | SAMPLE_DEVICE_NAME | 广播数据中的名字 |
---|---|---|
设置方式 | esp_ble_gap_set_device_name() | 在广播数据中通过 0x09 字段设置 |
作用范围 | GAP 名称,全局标识设备 | 广播包中的本地名称,广播时可见 |
是否必须 | 是,设备必须有一个 GAP 名称 | 否,广播包中可以不包含名字字段 |
是否可以不同 | 不依赖广播数据,可以与广播数据中的名字不同 | 可以与 GAP 名称不同 |
显示位置 | 客户端扫描设备时显示 | 客户端解析广播包时显示 |
Directed Advertising
设备可以使用定向广播允许发起方与其连接
高占空比定向广播在控制器中是时间有限的,因此在建立连接之前可能会失败
低占空比定向广播也必须启用才能进入广播状态,设备在此之前还应配置广播参数
高占空比循环定向广播失败情况
图源:《Bluetooth Core Specification v5.0》
低占空比循环定向广播失败情况
图源:《Bluetooth Core Specification v5.0》
占空比
指的是在一个周期内,信号处于“活动”状态的时间比例。它通常用百分比表示,计算公式为:占空比 = 活动时间 总周期时间 × 100 % \text{占空比} = \frac{\text{活动时间}}{\text{总周期时间}} \times 100\% 占空比=总周期时间活动时间×100%
高占空比意味着信号在大部分时间内处于“活动”状态,只有少部分时间处于“空闲”状态
- 发送的信号较强,持续时间较长,适合需要高接入概率的场景
- 由于广播持续时间较长,设备的功耗较高,因此适用于短时间内需要建立连接的情况
低占空比意味着信号的“活动”时间较短,大部分时间处于“空闲”状态
- 广播时间较短,信号的持续时间较低,适用于功耗敏感的场景
- 这种方式会减少设备的功耗,但建立连接的概率较低,因为广播的时间较短,接收方可能错过广播
Scanning State
Passive Scanning
设备可以使用被动扫描来查找区域内的广播设备
该设备将接收来自对等设备的广播数据包,并将这些数据报告给主机
被动扫描
图源:《Bluetooth Core Specification v5.0》
Scanning Flow
LE Set Scan Parameters (Passive Scanning)
Host 设置被动扫描的扫描参数Command Complete
LL 返回命令完成 Event 到 HostLE Set Scan Enable (Enable)
Host 设置扫描允许命令Command Complete
LL 返回命令完成 Event 到 HostAdvert
LL A 收到来自 LL B 的扫描LE Advertising Report
LL 上报收到的广播数据LE Set Scan Enable (Disable)
Host 发送停止扫描命令Command Complete
LL 返回命令完成 Event 到 Host
Active Scanning
设备可以使用主动扫描获取更多关于设备的信息,这些信息可能对填充用户界面有帮助
主动扫描涉及更多的链路层广播消息
主动扫描
图源:《Bluetooth Core Specification v5.0》
Active Scanning Flow
LE Set Scan Parameters (Passive Scanning)
Command Complete
LE Set Scan Enable (Enable)
Command Complete
Advert
SCAN_REQ
LL A 发送扫描请求到 LL B (广播数据最大只有 32 个 Bytes,更多的数据需要放在扫描应答中)SCAN_RSP
LL B 返回扫描请求应答数据LE Advertising Report
上报广播数据和扫描应答数据LE Set Scan Enable (Disable)
Command Complete
Callback Parameters
GATT Server Callback Parameters 详见 esp-idf_GATT Server
GAP Callback Parameter 详见 esp-idf_GATT Client
Advertising And Scanning Response Data Format
广播数据和扫描响应数据的格式由两个部分组成:重要部分和非重要部分
- 重要部分
- 包含一系列
AD 结构
,每个 AD 结构包含两个字段长度字段
1 字节,表示数据的长度,即后面的长度不能超过 0xff数据字段
长度由长度字段决定,数据字段的第一个字节是 AD 类型字段,剩余的长度 - 1 字节是 AD 数据,其内容取决于 AD 类型字段的值
- 包含一系列
- 非重要部分
- 当需要时,用来扩展广播和扫描响应数据
- 必须包含全零字节
广播数据和扫描响应数据的格式
图源:《Bluetooth Core Specification v5.0》
长度字段如果设置为零,则数据字段没有字节,这仅在需要提前终止广告或扫描响应数据时发生
只有重要部分的广告或扫描响应数据应通过无线信号发送
广播和扫描响应数据在广播事件中发送:
- 广播数据放置在 ADV_IND、ADV_NONCONN_IND、ADV_SCAN_IND、AUX_ADV_IND、AUX_CHAIN_IND 和 AUX_SYNC_IND 数据包的 AdvData 字段中
- 扫描响应数据放置在 SCAN_RSP 数据包的 ScanRspData 字段中,或者放置在 AUX_SCAN_RSP 数据包的 AdvData 字段中
如果完整的广播或扫描响应数据无法容纳在 AUX_ADV_IND 或 AUX_SCAN_RSP 数据包中,则使用 AUX_CHAIN_IND 数据包传送剩余的数据
AD 类型数据的格式和含义在 Core Specification Supplement 的 A 部分中定义,AD 类型标识符值在 Assigned_Numbers.pdf 文档中 2.3 Common Data Types 部分定义
对于蓝牙 5.0 及以下,广播可以携带 0~31 个字节
对蓝牙 5.0 及以上,广播可以携带 0~254 个字节,通过分组技术,可以携带更多数据(不超过 1650Bytes)
详见:Bluetooth Core Specification Vol6 PartB 2.3
These PDUs are sent by the Link Layer in the Advertising State and received by a Link Layer in the Scanning State or Initiating State. The ADV_IND, ADV_DIRECT_IND, ADV_NONCONN_IND, and ADV_SCAN_IND PDUs are called “legacy advertising PDUs”. The ADV_EXT_IND, AUX_ADV_IND, AUX_SYNC_IND, and AUX_CHAIN_IND PDUs are called “extended advertising PDUs”. Advertising events using legacy advertising PDUs are called “legacy advertising events”
来源:Bluetooth Core Specification v5.0, 页面 2,570
gatts_table_creat_demo
GATT APP 的工作流程:
- (app_main)
esp_ble_gatts_app_register ()
注册 profile ID,返回ESP_GATTS_REG_EVT
- (gatts_profile_event_handler)当触发
ESP_GATTS_REG_EVT
,设置设备名称、原始广播数据,触发ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT
- (gatts_profile_event_handler)
esp_ble_gap_config_scan_rsp_data_raw()
(GATT)设置广播数据、扫描响应数据,触发ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT
- (gap_event_handler)当触发
ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT
事件如果广播数据和扫描响应数据都设置了,则 (GAP) 调用esp_ble_gap_start_advertising
开始广播,触发ESP_GAP_BLE_ADV_START_COMPLETE_EVT
事件 - 设备广播数据
- 可以调用
esp_ble_gap_stop_advertising()
停止广播,返回ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT
可以在 LightBlue 中读到广播数据
应用程序处理客户端读取特征值事件
case ESP_GATTS_READ_EVT: // 读事件,当客户端读取特征值时触发,如果设置为 AUTO_RSP,协议栈会自动处理,否则需要应用程序处理 ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_READ_EVT"); esp_gatt_rsp_t rsp; // 定义变量 memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); // 初始化变量 rsp.attr_value.handle = param->read.handle; // 设置 handle rsp.attr_value.len = 4; // 设置长度 rsp.attr_value.value[0] = 0xde; // 设置值 rsp.attr_value.value[1] = 0xed; rsp.attr_value.value[2] = 0xbe; rsp.attr_value.value[3] = 0xef; esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp); // 返回数据至客户端 break;
GATT Client
GATT Client Work Flow
GATT Client 工作流程
- (app_main) 中初始化 NVS
- (app_main) 释放经典蓝牙模式
- (app_main) 初始化蓝牙控制器
- (app_main) 使能蓝牙控制器
- (app_main) 初始化 bluedroid
- (app_main) 使能 bluedroid
- (app_main) 注册 GAP 回调函数
- (app_main) 注册 GATT 回调函数
- (app_main) 注册 GATT APP ID,触发
ESP_GATTC_REG_EVT
事件 - (gattc_profile_event_handler)设置扫描参数,触发
ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT
事件 - (esp_gap_cb)当触发
ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT
事件时,开始扫描,触发ESP_GAP_BLE_SCAN_START_COMPLETE_EVT
事件 - (esp_gap_cb)扫描结果触发
ESP_GAP_BLE_SCAN_RESULT_EVT
事件,它有两个子事件:ESP_GAP_SEARCH_INQ_RES_EVT
和ESP_GAP_SEARCH_INQ_CMPL_EVT
- (esp_gap_cb)扫描到每一个蓝牙 Service 时,触发
ESP_GAP_SEARCH_INQ_RES_EVT
,对扫描到的数据进行解析,如果扫描到的设备名和目标设备名相同,则停止扫描,打开 GATT 连接,触发ESP_GATTC_CONNECT_EVT
、ESP_GATTC_OPEN_EVT
事件 - (esp_gap_cb)扫描完所有的蓝牙 Service 时,触发
ESP_GAP_SEARCH_INQ_CMPL_EVT
- (esp_gap_cb)扫描到每一个蓝牙 Service 时,触发
- (gattc_profile_event_handler)当触发
ESP_GATTC_CONNECT_EVT
事件时,打印连接 ID,发送 MTU 请求,触发ESP_GATTC_DIS_SRVC_CMPL_EVT
、ESP_GATTC_CFG_MTU_EVT
事件 - (gattc_profile_event_handler)当触发
ESP_GATTC_CFG_MTU_EVT
事件时,检查 MTU 设置是否成功 - (gattc_profile_event_handler)当触发
ESP_GATTC_DIS_SRVC_CMPL_EVT
事件时,搜索服务,当找到服务时,会触发ESP_GATTC_SEARCH_RES_EVT
事件;当搜索完所有服务时,会触发ESP_GATTC_SEARCH_CMPL_EVT
事件 - (gattc_profile_event_handler)当触发
ESP_GATTC_SEARCH_RES_EVT
事件时,保存服务的起始句柄和结束句柄 - (gattc_profile_event_handler)当触发
ESP_GATTC_SEARCH_CMPL_EVT
事件,获取服务的特征数量和服务特征,注册通知,触发ESP_GATTC_REG_FOR_NOTIFY_EVT
事件 - (gattc_profile_event_handler)当触发
ESP_GATTC_REG_FOR_NOTIFY_EVT
事件,写入描述符,触发ESP_GATTC_WRITE_DESCR_EVT
事件 - (gattc_profile_event_handler)当触发
ESP_GATTC_WRITE_DESCR_EVT
事件,,输出写入成功或失败 - (gattc_profile_event_handler)如果 Service 发送通知,触发
ESP_GATTC_NOTIFY_EVT
特征读取函数
esp_ble_gattc_read_char(gattc_if, // 特征接口 p_data->search_cmpl.conn_id, // 连接 ID char_elem_result[0].char_handle, // 特征句柄 ESP_GATT_AUTH_REQ_NONE); // 无需授权 // 触发 ESP_GATTC_READ_CHAR_EVT 事件
特征输出函数
case ESP_GATTC_READ_CHAR_EVT: ESP_LOGI(GATTC_TAG, "ESP_GATTC_READ_CHAR_EVT, len %d, value:", p_data->read.value_len); esp_log_buffer_hex(GATTC_TAG, p_data->read.value, p_data->read.value_len);
break;
Connection
当扫描者在某一个广播信道接收到一个广播数据包时,若该广播者是可连接的,那么扫描者可以在同一广播信道发送连接请求 (Connection Request)
对广播者,它可以设置 接受列表 (Filter Accept List) 以过滤不受信任的设备,或接受任一扫描者的连接请求
随后,广播者转变为外围设备,扫描者转变为中央设备,两者之间可以在数据信道进行双向通信
在连接中,中央设备与外围设备会周期性地进行数据交换,这个数据交换的周期被称为连接间隔 (Connection Interval)
- 连接间隔作为连接参数之一,在连接请求中被首次确定,后续也可以进行修改
- 连接间隔的取值步长 (Step Size) 为 1.25 ms ,取值范围为 7.5 ms (6 steps) - 4.0 s (3200 steps)
一次数据交换的过程被称为连接事件 (Connection Event)
一次连接事件中,存在一次或多次数据包交换(数据量比较大时需分包发送)
- 一次数据包交换的过程是,中央设备先给外围设备发送一个数据包,随后外围设备给中央设备发送一个数据包
- 即便连接中任意一方在连接间隔开始时无需发送数据,也必须发送空数据包以维持连接
- 若一次连接事件中需要发送的数据很多,导致连接事件时长超过了连接间隔,那么必须将一次连接事件拆分成多次连接事件(假如连接间隔的剩余时间不足以完成下一次数据包交换,那么下一次数据包交换必须等到下一次连接间隔开始时才能进行)
Supervision Timeout
连接超时参数 (Supervision Timeout) 规定了两次成功连接事件之间的最长时间
若在一次成功的连接事件之后,经过了连接超时时间却仍没有完成另一次成功的连接事件,则可以认为连接已断开
Peripheral Latency
外围设备延迟 (Peripheral Latency) 规定了外围设备在无需发送数据的前提下,最多可忽略的连接事件数量(即可以根据实际情况动态调整连接间隔,低间隔则高能耗,高间隔则高延迟)
MTU
详见本文MTU部分
Data Exchange
由客户端发起的操作有以下三种
- 读 (Read)
- 从 GATT 服务器上拉取某一特征数据的当前值
- 写 (Write)
- 普通的写操作要求 GATT 服务器在收到客户端的写请求以及对应数据以后,进行确认响应
- 写(无需响应) (Write without response)
- 快速写操作则不需要服务器进行确认响应
由服务器发起的操作分两种
- 通知 (Notify)
- 通知是 GATT 服务器主动向客户端推送数据的操作,不需要客户端回复确认响应
- 指示 (Indicate)
- 与通知相似,区别在于指示需要客户端回复确认,因此数据推送速度比通知慢
虽然通知和指示都是由服务器发起的操作,但是服务器发起操作的前提是,客户端启用了通知或指示
所以,本质上 GATT 的数据交换过程总是以客户端请求数据开始
Write Data
flowchart TB
A[Client] --write data--> B[Server]
B --> C{Data}
C --<=MTU-3-->D[ESP_GATTS_WRITE_EVT]
C -->MTU-3-->D[ESP_GATTS_WRITE_EVT]
C -->MTU-3-->E[ESP_GATTS_EXEC_WRITE_EVT]
当接收到的数据 > MTU - 3 时,会将超出的数据放入一个 buffer 中,接受完所有数据后,再进行处理
- 客户端发起写入请求
- 服务器接收并存储到属性数据库
- 服务器应用逻辑读取该值
- 服务器处理该值
Notification for Server
可以用于在触发 ESP_GATTS_WRITE_EVT 事件后回复 client
Notification for Client
- 客户端查找服务事件
ESP_GATTC_SEARCH_CMPL_EVT
,即蓝牙客户端在查找完服务端后,会上报上层程序,如果找到了目标服务,则会调用esp_ble_gattc_get_attr_count()
函数,取得属性数目 - 如果取得的数目 >0,则会调用
esp_ble_gattc_get_char_by_uuid()
函数,取得所用对应特征值的设置,同时取得通知值的设置,从而判断是 notification 还是 indication - 之后调用
esp_ble_gattc_register_for_notify()
注册通知,触发ESP_GATTC_REG_FOR_NOTIFY_EVT
事件 - 通过
esp_ble_gattc_get_descr_by_char_handle()
取得 describe 位 - 通过
esp_ble_gattc_write_char_descr()
函数将notify_en
参数(0x0001-notification,0x0002-indication)写入 Server,触发ESP_GATTC_WRITE_DESCR_EVT
事件(在服务端触发ESP_GATTS_WRITE_EVT
) - 检查写入是否成功,如果成功,则进行下一步流程
- Server 发送 notification,触发客户端的
ESP_GATTC_NOTIFY_EVT
事件
flowchart TDA[Register] --esp_ble_gattc_register_for_notify()--> B[ESP_GATTC_REG_FOR_NOTIFY_EVT]B --esp_ble_gattc_get_descr_by_char_handle(), esp_ble_gattc_write_char_descr() -->C[ESP_GATTC_WRITE_DESCR_EVT]