BLE协议栈核心原理:GAP与GATT角色、广播与扫描机制、连接参数详解
做蓝牙开发,尤其是低功耗蓝牙,绕不开两个核心协议:GAP(通用访问规范)和 GATT(通用属性规范)。
我刚开始接触 BLE 时,也分不清它们到底管什么。后来踩了几个坑才明白——GAP 管的是“设备怎么找到对方、怎么连上”,GATT 管的是“连上之后数据怎么传”。一个管连接前,一个管连接后。
GAP:设备发现与连接的角色定义
GAP 定义了四种角色。你在项目中选哪个角色,直接决定了设备的行为逻辑。
| 角色 | 典型设备 | 核心行为 |
|---|---|---|
| Broadcaster(广播者) | iBeacon 信标、传感器标签 | 只发广播,不接收连接 |
| Observer(观察者) | 扫描器、信标接收器 | 只扫描广播,不发起连接 |
| Peripheral(外设) | 心率带、温湿度计 | 广播并等待连接,可被连接 |
| Central(中心) | 手机、网关、ESP32 主设备 | 扫描并发起连接 |
我做过一个 iBeacon 项目,信标只做 Broadcaster,不连任何设备。但后来客户要求能远程修改广播间隔——这就得把角色升级成 Peripheral,允许中心设备连进来写参数。你看,角色选错了,后面全得改。
广播与扫描机制:设备怎么“打招呼”
广播和扫描,说白了就是设备之间的“打招呼”过程。
广播:外设(或广播者)在三个固定信道(37/38/39)上周期性地发送数据包。每个广播包最多 31 字节有效数据。如果你要发更多数据,可以用 Scan Response(扫描响应)或者扩展广播(LE Extended Advertising)。
扫描:中心设备(或观察者)在三个广播信道上轮流监听。扫描分两种:
- 被动扫描:只收广播包,不发任何请求。
- 主动扫描:收到广播包后,发送扫描请求,外设回复扫描响应数据。
我记得有个项目,设备广播了温湿度数据,但手机 App 老是收不到。排查了半天,发现是扫描窗口设得太短,刚好错过了广播包。你想想看,广播间隔 100ms,扫描窗口只有 30ms,那漏包的概率确实很高。
连接参数详解:连上之后怎么“聊天”
设备连上之后,通信质量就看三个参数:Connection Interval、Slave Latency、Supervision Timeout。
这三个参数由中心设备在连接请求中下发,外设可以协商修改。但很多开发者忽略了协商机制,导致连接不稳定或者功耗过高。
Connection Interval(连接间隔)
连接间隔是两个连接事件之间的时间,单位是 1.25ms。取值范围是 6 到 3200,对应 7.5ms 到 4s。
- 间隔越小:数据延迟低,但功耗高。
- 间隔越大:功耗低,但数据延迟高。
我建议:对实时性要求高的场景(如遥控器),用 7.5ms 到 30ms;对传感器数据上报,用 100ms 到 500ms 就够。
Slave Latency(从设备延迟)
这个参数允许外设跳过若干个连接事件而不发数据。说白了,就是让外设“偷懒”。
- 取值范围:0 到 499,但不能让跳过的总时间超过 Supervision Timeout。
- 典型值:对于温湿度传感器,设 4 到 10 很常见。
我曾经遇到一个坑:外设设了 Slave Latency = 10,连接间隔 100ms,结果手机端以为设备掉线了。为什么?因为 Supervision Timeout 只设了 1s,而外设最长可以 1.1s 不发数据(100ms × (10+1) = 1100ms),刚好超时。嗯,这里要注意:Slave Latency 的最大跳过时间必须小于 Supervision Timeout。
Supervision Timeout(监督超时)
这是连接保活的最后一道防线。如果在这个时间内没有收到任何数据包(包括空包),连接就会断开。
- 取值范围:10 到 3200,单位 10ms,即 100ms 到 32s。
- 典型值:2s 到 6s 比较安全。
连接参数关系速查表
| 参数 | 单位 | 范围 | 典型值 | 影响 |
|---|---|---|---|---|
| Connection Interval | 1.25ms | 6 ~ 3200 | 80 (100ms) | 延迟 & 功耗 |
| Slave Latency | 次数 | 0 ~ 499 | 4 | 外设休眠时间 |
| Supervision Timeout | 10ms | 10 ~ 3200 | 200 (2s) | 连接保活 |
GATT:连接后的数据交换规范
GATT 定义了数据怎么组织、怎么读写。它基于客户端-服务器模型:
- GATT Server:提供数据的一方(如传感器设备)。
- GATT Client:读取或写入数据的一方(如手机 App)。
数据通过 Service(服务)和 Characteristic(特征值)来组织。一个服务包含多个特征值,每个特征值有对应的属性(读、写、通知、指示)。
我习惯把 Service 比作“文件夹”,Characteristic 比作“文件”。手机 App 通过 UUID 找到对应的 Service,再操作里面的 Characteristic。
- Read:客户端主动读取特征值。
- Write:客户端写入特征值。
- Notify:服务器主动推送数据,客户端不需要确认。
- Indicate:服务器主动推送数据,客户端必须确认。
Notify 比 Indicate 快,但可能丢包。Indicate 可靠,但速度慢。我通常在传输传感器数据时用 Notify,在传输关键指令时用 Indicate。
BLE协议栈核心逻辑图
下面这张图展示了 GAP 和 GATT 在整个 BLE 通信流程中的位置和作用:
实际项目中的参数调优建议
说了这么多理论,最后给几个实战建议:
- iBeacon 广播项目:广播间隔设 100ms 到 900ms。100ms 响应快但费电,900ms 省电但手机扫描要等更久。我一般取 300ms 作为平衡点。
- 传感器数据上报:连接间隔 100ms,Slave Latency 设 4,Supervision Timeout 设 3s。这样外设可以休眠 400ms,功耗降低不少。
- 遥控器/实时控制:连接间隔 7.5ms 到 15ms,Slave Latency 设 0,Supervision Timeout 设 1s。延迟低,响应快。
- 多设备并发:ESP32 做中心时,连接间隔不要全部设成最小值,否则协议栈处理不过来。我习惯把不同设备的连接间隔错开,比如一个设 30ms,另一个设 50ms。
嗯,GAP 和 GATT 这部分内容,说白了就是 BLE 的“交通规则”和“数据格式”。你只要把角色选对、广播参数调好、连接参数算清楚,项目就成功了一大半。剩下的,就是代码层面的实现了。