BLE协议栈核心原理:GAP与GATT角色、广播与扫描机制、连接参数详解

做蓝牙开发,尤其是低功耗蓝牙,绕不开两个核心协议:GAP(通用访问规范)和 GATT(通用属性规范)。

我刚开始接触 BLE 时,也分不清它们到底管什么。后来踩了几个坑才明白——GAP 管的是“设备怎么找到对方、怎么连上”,GATT 管的是“连上之后数据怎么传”。一个管连接前,一个管连接后。

GAP:设备发现与连接的角色定义

GAP 定义了四种角色。你在项目中选哪个角色,直接决定了设备的行为逻辑。

角色 典型设备 核心行为
Broadcaster(广播者) iBeacon 信标、传感器标签 只发广播,不接收连接
Observer(观察者) 扫描器、信标接收器 只扫描广播,不发起连接
Peripheral(外设) 心率带、温湿度计 广播并等待连接,可被连接
Central(中心) 手机、网关、ESP32 主设备 扫描并发起连接

我做过一个 iBeacon 项目,信标只做 Broadcaster,不连任何设备。但后来客户要求能远程修改广播间隔——这就得把角色升级成 Peripheral,允许中心设备连进来写参数。你看,角色选错了,后面全得改。

关键点:ESP32 可以同时支持多种角色。比如一边做 iBeacon 广播者,一边做中心扫描其他设备。但要注意,多角色并发会增加协议栈负担,广播间隔和扫描窗口需要协调好。

广播与扫描机制:设备怎么“打招呼”

广播和扫描,说白了就是设备之间的“打招呼”过程。

广播:外设(或广播者)在三个固定信道(37/38/39)上周期性地发送数据包。每个广播包最多 31 字节有效数据。如果你要发更多数据,可以用 Scan Response(扫描响应)或者扩展广播(LE Extended Advertising)。

扫描:中心设备(或观察者)在三个广播信道上轮流监听。扫描分两种:

  • 被动扫描:只收广播包,不发任何请求。
  • 主动扫描:收到广播包后,发送扫描请求,外设回复扫描响应数据。

我记得有个项目,设备广播了温湿度数据,但手机 App 老是收不到。排查了半天,发现是扫描窗口设得太短,刚好错过了广播包。你想想看,广播间隔 100ms,扫描窗口只有 30ms,那漏包的概率确实很高。

经验值:广播间隔和扫描窗口的比例建议在 1:3 以上。比如广播间隔 100ms,扫描窗口至少 300ms,才能保证较高的捕获率。

连接参数详解:连上之后怎么“聊天”

设备连上之后,通信质量就看三个参数: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 比较安全。
避坑指南:我曾经在一个项目中,为了省电把 Supervision Timeout 设成了 10s。结果设备断开后,App 要等 10 秒才能发现,用户体验极差。建议不要超过 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 通信流程中的位置和作用:

BLE协议栈核心逻辑图 BLE 协议栈核心逻辑图 GAP 层(通用访问规范) 设备发现 · 连接建立 · 角色定义 Broadcaster / Observer / Peripheral / Central 广播与扫描机制 37/38/39 信道 · 广播间隔 · 扫描窗口 · 主动/被动扫描 连接参数 Connection Interval · Slave Latency · Supervision Timeout GATT 层(通用属性规范) Service · Characteristic · Read / Write / Notify / Indicate 客户端-服务器模型 · 数据交换 GAP 管连接前,GATT 管连接后。两者配合完成完整的 BLE 通信流程。

实际项目中的参数调优建议

说了这么多理论,最后给几个实战建议:

  1. iBeacon 广播项目:广播间隔设 100ms 到 900ms。100ms 响应快但费电,900ms 省电但手机扫描要等更久。我一般取 300ms 作为平衡点。
  2. 传感器数据上报:连接间隔 100ms,Slave Latency 设 4,Supervision Timeout 设 3s。这样外设可以休眠 400ms,功耗降低不少。
  3. 遥控器/实时控制:连接间隔 7.5ms 到 15ms,Slave Latency 设 0,Supervision Timeout 设 1s。延迟低,响应快。
  4. 多设备并发:ESP32 做中心时,连接间隔不要全部设成最小值,否则协议栈处理不过来。我习惯把不同设备的连接间隔错开,比如一个设 30ms,另一个设 50ms。
调试技巧:用 nRF Connect 或 LightBlue App 可以查看当前连接的参数。如果发现连接间隔不是你设的值,说明设备协商失败了,检查一下参数范围是否在对方接受的范围内。

嗯,GAP 和 GATT 这部分内容,说白了就是 BLE 的“交通规则”和“数据格式”。你只要把角色选对、广播参数调好、连接参数算清楚,项目就成功了一大半。剩下的,就是代码层面的实现了。