wordpress 全站静态化,在线网站免费网站入口,海口建站模板,wordpress中文后台前言
蓝牙作为一个庞大的知识体系#xff0c;其学习和运用对于初学者来说显得有些复杂且凌乱。我整理了这段时间的学习笔记#xff0c;涵盖了协议栈、工作流程、参数等内容。在实际应用中#xff0c;我们主要使用 GAP 和 GATT#xff0c;协议栈中的其他部分只需了解即可。…前言
蓝牙作为一个庞大的知识体系其学习和运用对于初学者来说显得有些复杂且凌乱。我整理了这段时间的学习笔记涵盖了协议栈、工作流程、参数等内容。在实际应用中我们主要使用 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 摘要BriefThe classification of BluetoothBluetooth terminologyCore ArchitectureBluetooth RolesBluetooth Network ConfigurationBluetooth Connection Process Bluetooth StackGAPRoles when Operating over BR/EDR Physical TransportRoles when Operating over an LE Physical TransportDefines compliance requirementsDevice Connection Establishment Process GATTConfiguration and RolesGATT Profile HierarchyServiceService DeclarationService IncludeInclude Declaration CharacteristicCharacteristic DeclarationCharacteristic Value DeclarationCharacteristic Descriptor DeclarationsCharacteristic Extended PropertiesCharacteristic User DescriptionClient Characteristic ConfigurationServer Characteristic ConfigurationCharacteristic Presentation FormatCharacteristic Aggregate Format GATT Profile Attribute Types GATT Send Command GATT ServerGATT Server Work FlowAdvertisingMTUPDU Advertising FlowAdvertising StateUndirected AdvertisingDirected Advertising Scanning StatePassive ScanningActive Scanning Callback ParametersAdvertising And Scanning Response Data Formatgatts_table_creat_demo GATT ClientGATT Client Work Flow ConnectionSupervision TimeoutPeripheral LatencyMTU Data ExchangeWrite DataNotification for ServerNotification 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)调制方式GFSKGFSKπ/4 DQPSK8DPSK数据速率LE 2M PHY2 Mb/sLE 1M PHY1 Mb/sLE Coded PHY (S2)500 Kb/sLE Coded PHY (S8)125 Kb/sEDR PHY (8DPSK)3 Mb/sEDR PHY (π/4 DQPSK)2 Mb/sBR PHY (GFSK)1 Mb/s发射功率≤ 100 mW (20 dBm)≤ 100 mW (20 dBm)接收灵敏度LE 2M PHY≤-70 dBmLE 1M PHY≤-70 dBmLE Coded PHY (S2)≤-75 dBmLE Coded PHY (S8)≤-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 三个信道同事广播相同的消息 主机端扫描 扫描是在一定范围内用来寻址其他 BLE 设备的过程扫描者在扫描过程中会使用广播信道但扫描并没有严格的时间定义和信道规则其过程按照主机设定的扫描定时参数进行 被动扫描 扫描者仅仅监听广播包而不向广播者发送任何数据 在扫描过程中如果控制器接收到符合过滤策略或其他规则的的广播包, 则向主机发送一个报告时间其中包括广播者的设备地址、广播包中的数据、信号接收强度 主动扫描 Central 在扫描捕获广播包的同时捕获扫描响应包并作出区分 广播包主要用于设备发现和提供基本信息适合所有设备接收扫描响应包用于增强广播包满足需要获取更多信息的主动扫描设备需求 主机端连接 连接过程如下 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)同一信道上连续传输包之间的时间间隔 ACKACK在通信中是接收方对发送方的反馈用于确认数据包已成功接收如果没有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和蓝牙低功耗BLEBluedroid 已被广泛应用于 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 作为 DUTDevice Under Test用 UART 作为 IO 接口接上认证测试的 PC 机即可完成认证 IDF 中的 host、controller 目录其中存放有不同芯片型号相应的库文件及接口等 路径idf 安装路径\v5.3.1\esp-idf\components\bt ESP-IDF 的 component/bt/host/bluedroid 目录说明
子文件夹说明apiAPI 目录所有的 API除 Controller 相关都在此目录下bta蓝牙适配层适配一些主机层底层协议的接口btc蓝牙控制层控制主机上层协议包括规范以及杂项的处理common协议栈的通用头文件config为协议栈配置一些参数device与蓝牙设备控制相关的如控制器设备的配置的 HCI CMD 流程等external与蓝牙自身无关但又要使用的代码如 SBC codec 设备程序等hciHCI 层协议Kconfig.inMenuconfig 文件main主程序目录主要为启动、关闭流程stack主机底层协议栈GAP/ATT/GATT/SDP/SMP 等
GAP
蓝牙协议栈中 GAP 和 GATT 部分各知识点之间联系 原图较大由于网站压缩图像画质可能较差原图见Github_CodeFlashier
本部分的全部内容详见 Bluetooth Core Specification v5.0 Vol3 PartC这里只做必要内容的介绍
通用访问配置文件Generic Access ProfileGAP的目的如下
引入与运输和应用配置文件使用的模式和访问程序相关的定义、建议和通用要求描述设备在待机和连接状态下的行为以保证蓝牙设备之间始终能够建立连接和通道并且支持多配置文件操作特别关注设备发现、链路建立和安全程序规定用户界面方面的要求主要是编码方案和过程与参数的命名以确保用户体验 该配置文件的目的是描述
配置文件角色可发现模式和程序连接模式和程序安全模式和程序 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
设备连接过程 #mermaid-svg-f0XZ5qPUkMJ6Z856 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .error-icon{fill:#552222;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .marker.cross{stroke:#333333;}#mermaid-svg-f0XZ5qPUkMJ6Z856 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .cluster-label text{fill:#333;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .cluster-label span{color:#333;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .label text,#mermaid-svg-f0XZ5qPUkMJ6Z856 span{fill:#333;color:#333;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .node rect,#mermaid-svg-f0XZ5qPUkMJ6Z856 .node circle,#mermaid-svg-f0XZ5qPUkMJ6Z856 .node ellipse,#mermaid-svg-f0XZ5qPUkMJ6Z856 .node polygon,#mermaid-svg-f0XZ5qPUkMJ6Z856 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .node .label{text-align:center;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .node.clickable{cursor:pointer;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .arrowheadPath{fill:#333333;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .cluster text{fill:#333;}#mermaid-svg-f0XZ5qPUkMJ6Z856 .cluster span{color:#333;}#mermaid-svg-f0XZ5qPUkMJ6Z856 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-f0XZ5qPUkMJ6Z856 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Sending data Return data A Standby Advertising waiting Connection B Standby Scanning Initiation Connection 设备 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 ProfileGATT通过使用属性协议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 DeclarationService IncludeCharacteristic 01Characteristic 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它们定义了服务的范围所包含服务的 UUIDAttribute Permission 应为只读并且不要求身份验证或授权 包含声明结构 图源《Bluetooth Core Specification v5.0》 当 UUID 是 16 位 Bluetooth UUID 时服务 UUID 必须被直接指定 GATTGeneric 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特性 UUIDAttribute Permission 应为可读并且不要求身份验证或授权
特性声明的 Attribute Value 在服务器与任何客户端建立信任关系时不得更改 特征声明结构 图源《Bluetooth Core Specification v5.0》 Attribute Value
Attribute ValueSizeDescriptionCharacteristic Properties1 octets1 个 8 位数据特性属性的位域Characteristic Value Handle2 octets包含此特性值的属性的句柄Characteristic UUID2 or 16 octets16 位 Bluetooth UUID 或 128 位 UUID用于特性值
一个服务可以有多个具有相同特征 UUID的特征定义
在服务定义中某些特征可能是必需的这些特性应位于包含声明include declarations之后、任何可选特性之前
客户端不应假设服务定义中必需特性或可选特性的顺序
尽可能地并且在前述要求范围内使用 16 位 Bluetooth UUID 的特性声明应当按顺序分组在一起即按顺序列出而使用 128 位 UUID 的特性声明应当分组在一起 Characteristic Properties 特性属性位域Characteristic Properties决定了特性值如何使用或者如何访问特性描述符可以设置多个 特性属性
这些位应根据该特性所允许的程序进行设置并由更高层规范定义而不考虑安全要求
PropertiesValue描述Broadcast0x01如果设置则允许使用服务器特性配置描述符广播特性值如果设置必须存在服务器特性配置描述符Read0x02如果设置则允许使用第 4.8 节中定义的程序读取特性值Write Without Response0x04如果设置则允许使用第 4.9.1 节中定义的程序写入特性值且不要求响应Write0x08如果设置则允许使用第 4.9.3 或第 4.9.4 节中定义的程序写入特性值并要求响应Notify0x10如果设置则允许在没有确认的情况下使用第 4.10 节中定义的程序通知特性值如果设置则必须存在客户端特性配置描述符Indicate0x20如果设置则允许在有确认的情况下使用第 4.11 节中定义的程序指示特性值如果设置则必须存在客户端特性配置描述符Authenticated Signed Writes0x40如果设置则允许使用第 4.9.2 节中定义的程序向特性值写入签名Extended Properties0x80如果设置则在第 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 FieldAttribute Permissions 应为可读不需要身份验证和授权 特征扩展属性结构 图源《Bluetooth Core Specification v5.0》 特性扩展属性位域描述了如何使用特性值Characteristic Value 或如何访问特性描述符 如果表中定义的位被设置则表示允许执行相应的操作可以设置多个 特性属性
PropertiesValueDescriptionReliable Write0x0001如果设置则允许使用第 4.9.5 节中定义的程序进行可靠的特性值写入Writable Auxiliaries0x0002如果设置则允许写入第 3.3.3.2 节中定义的特性描述符Reserved for Future Use0xFFFC保留供未来使用 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》 客户端特性配置位定义
ConfigurationValueDescriptionNotification0x0001特性值将被通知。只有在特性属性设置了通知位时此值才可以设置Indication0x0002特性值将被指示。只有在特性属性设置了指示位时此值才可以设置Reserved for Future Use0xFFFC保留供未来使用
客户端特性配置描述符值 的默认值应为 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》 服务器特性配置位定义
ConfigurationValue描述Broadcast0x0001当服务器处于广播过程并且广告数据资源可用时特性值将被广播只有在特性属性设置了广播位时此值才可以设置Reserved for Future Use0xFFFE保留供未来使用 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 NameValue SizeDescriptionFormat1 octet该特性值的格式Exponent1 octet指数字段用于确定该特性值如何进一步格式化Unit2 octets该特性的单位如在《Assigned Number Specification》中定义Name Space1 octet描述的命名空间如在《Assigned Number Specification》中定义Description2 octets该特性的描述如在更高层配置文件中定义 位序 Characteristic Format descriptor 使用的位序应该是 小端序little-endian(低位字节存储在低地址而高位字节存储在 高地址,即数据的 最低有效字节LSB 放在最前面) 格式 格式Format 字段决定了 特性值Characteristic Value 中单个值的格式
如果格式不是一个完整的字节数则数据应以能够容纳该值的最小字节数进行存储数据应占用每个字节的全部内容除了最后一个字节的最高有效位最后一个字节的其他位应预留供未来使用
带符号整数应使用二进制补码表示法two’s-complement representation
定义的格式值
FormatShort NameDescriptionExponent Value0x00rfuReserved for Future UseNo0x01booleanunsigned 1-bit; 0 false, 1 trueNo0x022bitunsigned 2-bit integerNo0x03nibbleunsigned 4-bit integerNo0x04uint8unsigned 8-bit integerYes0x05uint12unsigned 12-bit integerYes0x06uint16unsigned 16-bit integerYes0x07uint24unsigned 24-bit integerYes0x08uint32unsigned 32-bit integerYes0x09uint48unsigned 48-bit integerYes0x0Auint64unsigned 64-bit integerYes0x0Buint128unsigned 128-bit integerYes0x0Csint8signed 8-bit integerYes0x0Dsint12signed 12-bit integerYes0x0Esint16signed 16-bit integerYes0x0Fsint24signed 24-bit integerYes0x10sint32signed 32-bit integerYes0x11sint48signed 48-bit integerYes0x12sint64signed 64-bit integerYes0x13sint128signed 128-bit integerYes0x14float32IEEE-754 32-bit floating pointNo0x15float64IEEE-754 64-bit floating pointNo0x16SFLOATIEEE-11073 16-bit SFLOATNo0x17FLOATIEEE-11073 32-bit FLOATNo0x18duint16IEEE-20601 formatNo0x19utf8sUTF-8 stringNo0x1Autf16sUTF-16 stringNo0x1BstructOpaque structureNo0x1C – 0xFFrfuReserved for Future UseNo
当编码 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 TypeUUIDDescriptionPrimary Service0x2800Primary Service DeclarationSecondary Service0x2801Secondary Service DeclarationInclude0x2802Include DeclarationCharacteristic0x2803Characteristic DeclarationCharacteristic Extended Properties0x2900Characteristic Extended PropertiesCharacteristic User Description0x2901Characteristic User Description DescriptorClient Characteristic Configuration0x2902Client Characteristic Configuration DescriptorServer Characteristic Configuration0x2903Server Characteristic Configuration DescriptorCharacteristic Format0x2904Characteristic Format DescriptorCharacteristic Aggregate Format0x2905Characteristic Aggregate Format Descriptor GATT Send Command 命令代码 输入参数 授权 GATT Server
BLE 通信中 Server 和 Client 工作流程图解 原图较大由于网站压缩图像画质可能较差原图见Github_CodeFlashier
我们把存有数据即属性的设备叫做服务器 (Server)⽽将获取别⼈设备数据的设备叫做客户端 (Client) GAP与GATT的关系
维度GAPGATT层级控制层决定设备可见性和连接方式数据层定义连接后的数据传输规则工作阶段连接前广播、发现、配对连接后数据读写、服务交互依赖关系GATT依赖GAP建立的连接GAP为GATT提供通信基础典型应用设备配对、广播信息传感器数据读取、设备控制 GATT Server Work Flow
此过程指的是初始化和启动 GATT 服务器所涉及的步骤其中包括设置GATT服务、特征和描述符等任务并使服务器准备好处理来自 GATT 客户端的请求 #mermaid-svg-Ol80huvTcF7YlxEU {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Ol80huvTcF7YlxEU .error-icon{fill:#552222;}#mermaid-svg-Ol80huvTcF7YlxEU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ol80huvTcF7YlxEU .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Ol80huvTcF7YlxEU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ol80huvTcF7YlxEU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ol80huvTcF7YlxEU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ol80huvTcF7YlxEU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ol80huvTcF7YlxEU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ol80huvTcF7YlxEU .marker.cross{stroke:#333333;}#mermaid-svg-Ol80huvTcF7YlxEU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ol80huvTcF7YlxEU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ol80huvTcF7YlxEU .cluster-label text{fill:#333;}#mermaid-svg-Ol80huvTcF7YlxEU .cluster-label span{color:#333;}#mermaid-svg-Ol80huvTcF7YlxEU .label text,#mermaid-svg-Ol80huvTcF7YlxEU span{fill:#333;color:#333;}#mermaid-svg-Ol80huvTcF7YlxEU .node rect,#mermaid-svg-Ol80huvTcF7YlxEU .node circle,#mermaid-svg-Ol80huvTcF7YlxEU .node ellipse,#mermaid-svg-Ol80huvTcF7YlxEU .node polygon,#mermaid-svg-Ol80huvTcF7YlxEU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ol80huvTcF7YlxEU .node .label{text-align:center;}#mermaid-svg-Ol80huvTcF7YlxEU .node.clickable{cursor:pointer;}#mermaid-svg-Ol80huvTcF7YlxEU .arrowheadPath{fill:#333333;}#mermaid-svg-Ol80huvTcF7YlxEU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ol80huvTcF7YlxEU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ol80huvTcF7YlxEU .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Ol80huvTcF7YlxEU .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Ol80huvTcF7YlxEU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ol80huvTcF7YlxEU .cluster text{fill:#333;}#mermaid-svg-Ol80huvTcF7YlxEU .cluster span{color:#333;}#mermaid-svg-Ol80huvTcF7YlxEU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ol80huvTcF7YlxEU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} init flash controller bluedriod app_main GATT Callback GAP Callback GATT App Set MTU gatts_event_handler gap_event_handler GAP Callback 处理客户端的连接、扫描等事件 GATT Callback 在客户端连接成功后底层协议栈会发送 GATT 事件到上层程序进行处理从而完成客户端对服务端数据的读写 GATT 客户端在与 GATT 服务器初次建立通信时会从 GATT 服务器拉取属性表中的元信息从而获取 GATT 服务器上可用的服务以及数据特征这一过程被称为服务发现 (Service Discovery) 属性表中包含
参数作用auto_rsp控制谁处理读/写操作的响应如果设置为 ESP_GATT_RSP_BY_APP则由应用程序负责生成响应如果设置为 ESP_GATT_AUTO_RSP则 GATT 协议栈会自动生成响应。uuid_lengthUUID 的长度以字节为单位*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)337-39用于发送广播数据包和扫描响应数据包数据信道 (Data Channel)370-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 0x08001.28 秒。时间 N * 0.625 毫秒时间范围20 毫秒到 10.24 秒。 */uint16_t adv_int_max; /*! 无定向广播和低占空比定向广播的最大广播间隔。范围0x0020 到 0x4000默认值N 0x08001.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)22有效负载 (Payload)0-37PDU 头中含有较多信息可以分为以下六个部分
序号名称比特位数备注1PDU 类型 (PDU Type)42保留位 (Reserved for Future Use, RFU)13通道选择位 (Channel Selection Bit, ChSel)1标记广播者是否支持 LE Channel Selection Algorithm #2 通道选择算法4发送地址类型 (Tx Address, TxAdd)10/1 分别表示公共地址/随机地址5接收地址类型 (Rx Address, RxAdd)10/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)12数据类型 (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 ALE 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 CompleteLE Set Scan Enable (Enable)Command CompleteAdvertSCAN_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_mainesp_ble_gatts_app_register () 注册 profile ID返回 ESP_GATTS_REG_EVTgatts_profile_event_handler当触发 ESP_GATTS_REG_EVT设置设备名称、原始广播数据触发 ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVTgatts_profile_event_handleresp_ble_gap_config_scan_rsp_data_raw() GATT设置广播数据、扫描响应数据触发 ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVTgap_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 #mermaid-svg-FMFfNflorRwA0War {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FMFfNflorRwA0War .error-icon{fill:#552222;}#mermaid-svg-FMFfNflorRwA0War .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FMFfNflorRwA0War .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-FMFfNflorRwA0War .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FMFfNflorRwA0War .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FMFfNflorRwA0War .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FMFfNflorRwA0War .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FMFfNflorRwA0War .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FMFfNflorRwA0War .marker.cross{stroke:#333333;}#mermaid-svg-FMFfNflorRwA0War svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FMFfNflorRwA0War .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FMFfNflorRwA0War .cluster-label text{fill:#333;}#mermaid-svg-FMFfNflorRwA0War .cluster-label span{color:#333;}#mermaid-svg-FMFfNflorRwA0War .label text,#mermaid-svg-FMFfNflorRwA0War span{fill:#333;color:#333;}#mermaid-svg-FMFfNflorRwA0War .node rect,#mermaid-svg-FMFfNflorRwA0War .node circle,#mermaid-svg-FMFfNflorRwA0War .node ellipse,#mermaid-svg-FMFfNflorRwA0War .node polygon,#mermaid-svg-FMFfNflorRwA0War .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FMFfNflorRwA0War .node .label{text-align:center;}#mermaid-svg-FMFfNflorRwA0War .node.clickable{cursor:pointer;}#mermaid-svg-FMFfNflorRwA0War .arrowheadPath{fill:#333333;}#mermaid-svg-FMFfNflorRwA0War .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FMFfNflorRwA0War .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FMFfNflorRwA0War .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-FMFfNflorRwA0War .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-FMFfNflorRwA0War .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FMFfNflorRwA0War .cluster text{fill:#333;}#mermaid-svg-FMFfNflorRwA0War .cluster span{color:#333;}#mermaid-svg-FMFfNflorRwA0War div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FMFfNflorRwA0War :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} init flash controller bluedriod app_main GAP Callback GATT Callback GATT App Set MTU esp_gap_cb esp_gattc_cb GATT Client 工作流程
app_main 中初始化 NVSapp_main 释放经典蓝牙模式app_main 初始化蓝牙控制器app_main 使能蓝牙控制器app_main 初始化 bluedroidapp_main 使能 bluedroidapp_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 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 --gt;MTU-3--D[ESP_GATTS_WRITE_EVT]
C --gt;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-notification0x0002-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]