1. BLE 协议栈概览:架构、角色与分层模型

做 BLE 开发这些年,我见过太多人一上来就翻芯片手册、调 API,结果连广播包都发不出去。为什么?说白了,就是没搞懂协议栈到底长什么样。这一章,咱们先把地图摊开,看看 BLE 协议栈的骨架。

1.1 协议栈长什么样?

BLE 协议栈不是一块铁板,它分了好几层。每层只管自己的事,层与层之间通过标准接口通信。这种分层设计,说白了就是为了解耦——你换芯片、换蓝牙版本,只要接口不变,上层代码基本不用动。

我个人习惯把协议栈分成三块:

  • Controller(控制器):负责物理层、链路层,管射频收发、时序、跳频。这部分通常固化在芯片硬件或 ROM 里。
  • Host(主机):负责逻辑链路控制、属性协议、安全管理、GATT/GAP 等。这部分跑在 CPU 上,你可以改。
  • Application(应用层):你写的业务代码,比如心率计、温湿度采集、OTA 升级。

嗯,这里要注意:有些芯片把 Host 和 Controller 做在同一颗 SoC 里(比如 Nordic nRF5 系列),有些则分开(比如 PC 端用 USB 蓝牙适配器,Host 跑在操作系统里)。

核心认知:BLE 协议栈的分层边界,就是 HCI(Host Controller Interface)。Controller 和 Host 之间通过 HCI 命令/事件通信。你写应用层代码时,基本碰不到 HCI,但调试底层问题时,HCI 日志是救命稻草。

BLE 协议栈分层架构 Application(应用层) 你的业务代码:心率、温湿度、OTA、Beacon Host(主机) GAP(通用访问规范) 广播、扫描、连接、角色管理 GATT(通用属性规范) 服务、特征、属性读写、通知 SMP(安全管理) 配对、绑定、加密 L2CAP 数据封装、多路复用 ATT(属性协议) 属性读写、通知、指示 HCI(主机控制器接口) Controller(控制器) 物理层(PHY)→ 链路层(LL)→ 射频

1.2 两个核心角色:GAP 和 GATT

很多初学者搞混 GAP 和 GATT,觉得都是蓝牙规范,有什么区别?我打个比方你就明白了:

  • GAP 管的是「谁在说话、谁在听、怎么搭上线」——相当于社交礼仪。
  • GATT 管的是「搭上线之后,具体聊什么、怎么聊」——相当于聊天内容。

我在项目中遇到过好几次,设备能连上但读不到数据,查了半天发现是 GATT 服务没注册对。嗯,这就是 GAP 和 GATT 没配合好。

GAP 的核心概念

角色 说明 常见场景
Broadcaster 只发广播,不连接 Beacon、信标
Observer 只扫描广播,不连接 扫描仪、寻址器
Peripheral 可被连接,通常作为从设备 手环、传感器
Central 主动发起连接,通常作为主设备 手机、网关

避坑提示:我曾经在做一个多连接网关时,Central 同时连了 6 个 Peripheral,结果广播扫描参数没调好,导致连接不稳定。后来把扫描窗口和扫描间隔的比例调到 1:2 才解决。GAP 参数不是随便填的。

GATT 的核心概念

GATT 定义了一种树形数据结构:

  • Service(服务):一组功能的集合,比如「电池服务」、「设备信息服务」。
  • Characteristic(特征):具体的数据点,比如「电池电量百分比」。
  • Descriptor(描述符):对特征的补充说明,比如「电量百分比的范围是 0-100」。

每个 Service 和 Characteristic 都有一个 UUID 来标识。标准服务用 16-bit UUID(比如 0x180F 是电池服务),自定义服务用 128-bit UUID。

注意:GATT 的读写操作有四种模式:Read、Write、Notify、Indicate。Notify 不需要应答,速度快但不可靠;Indicate 需要应答,可靠但慢。选错了,要么丢数据,要么功耗爆炸。

1.3 协议栈分层模型详解

咱们从下往上捋一遍,每层到底干了什么。

物理层(PHY)

工作在 2.4GHz ISM 频段,有 40 个信道(37 个数据信道 + 3 个广播信道)。BLE 5.0 之后支持 1Mbps、2Mbps、500kbps(长距离模式)。说白了,物理层就是决定「信号怎么发、发多快」。

链路层(LL)

链路层管的是「什么时候发、发什么、发完怎么确认」。它负责:

  • 广播和扫描的时序
  • 连接建立和断开
  • 数据包重传和确认
  • 跳频算法

我记得有一次调试连接超时问题,发现是链路层的 supervision timeout 设得太短,设备稍微离远一点就断连。这个参数要根据实际应用场景来调。

HCI(主机控制器接口)

HCI 是 Host 和 Controller 之间的桥梁。它定义了命令、事件、数据包的格式。如果你用双芯片方案(比如 Host 跑在 Linux 上,Controller 是 USB 蓝牙适配器),HCI 就是通过 USB 或 UART 传输的。

L2CAP(逻辑链路控制与适配协议)

L2CAP 负责把上层数据封装成链路层能处理的包,还支持多路复用——多个应用可以同时使用同一个蓝牙连接。它还有个重要功能:MTU 协商。默认 MTU 是 23 字节,但你可以协商到更大的值(比如 247 字节),这样一次能传更多数据。

ATT(属性协议)

ATT 定义了客户端-服务器模型。GATT 服务器(通常是 Peripheral)维护一组属性,GATT 客户端(通常是 Central)可以读写这些属性。ATT 的操作包括:

  • Read Request / Response
  • Write Request / Response
  • Notification(服务器主动推数据,不需要应答)
  • Indication(服务器主动推数据,需要应答)

SMP(安全管理协议)

SMP 负责配对、绑定和加密。配对方式有三种:Just Works(无输入)、Passkey Entry(输入 PIN 码)、Out of Band(NFC 等带外方式)。

经验之谈:我做过一个医疗设备项目,要求传输数据必须加密。当时选了 Just Works 配对,结果安全审计没过——因为 Just Works 不提供中间人攻击防护。后来改成 Passkey Entry 才通过。选配对方式时,别图省事。

1.4 数据流全景

咱们看一个完整的数据流:手机(Central)读取手环(Peripheral)的心率值。

  1. GAP 层:手环广播「我是心率设备」,手机扫描到广播,发起连接。
  2. 链路层:建立连接,协商连接参数(连接间隔、延迟、超时)。
  3. L2CAP:为 ATT 协议分配信道,协商 MTU。
  4. GATT 客户端(手机):发送 Read Request,UUID 指向心率特征。
  5. ATT 层:封装请求,通过 L2CAP 传到对端。
  6. GATT 服务器(手环):解析请求,读取心率值,返回 Response。
  7. 数据原路返回:ATT → L2CAP → 链路层 → 物理层 → 手机。

整个过程看起来复杂,但每层各司其职。你写应用层代码时,通常只需要关心 GAP 和 GATT 的 API,底层的事协议栈帮你做了。

调试建议:如果你遇到「连得上但读不到数据」的问题,先确认 GATT 服务有没有注册成功。我常用的方法是:用 nRF Connect 或 LightBlue 这类工具扫描设备,看看能不能看到服务和特征。看不到?那就是 GATT 注册有问题。能看到但读不了?那就是 ATT 权限或 MTU 的问题。

1.5 本章小结

BLE 协议栈的分层模型,说白了就是「各管各的,接口清晰」。GAP 管连接,GATT 管数据,底层管射频。你不需要记住每一层的细节,但必须知道:

  • 问题出在哪一层,就去哪一层找原因。
  • GAP 参数影响连接稳定性,GATT 结构影响数据交互效率。
  • HCI 是 Host 和 Controller 的分界线,调试底层问题时是重要线索。