深入BLE低功耗蓝牙

写在前面
最近在做蓝牙相关的工作,也终于有机会能由浅入深的把蓝牙的工作流程梳理一遍。梳理整个流程时,顺便写下这篇博客。
总的来说,蓝牙是工作在 2.4GHz ISM 频段上的一种无线通信协议。经典蓝牙和低功耗蓝牙分别有79个和40个信道可用,本文只关注低功耗蓝牙,仅涉及经典蓝牙中与BLE共同之处。
基础协议栈
如果只关注蓝牙在应用层的实现,往往只需要关注蓝牙的广播、服务即可,但是如果需要关注蓝牙的广播包是怎么发的?收发双方是如何跳频的,都需要更深入的了解 Conyroller 控制层中的 物理层、链路层。想要了解这两个层级,就需要先从蓝牙发送的数据空口包出发。
BLE 空口包结构

Preamble 前导码 通俗讲就是告诉接收方“我开始发数据了,请从这里开始接收”。在低功耗蓝牙BLE中,大部分情况下前导码都是 0x55 或 0xAA, 它是由 Access Address 最低位决定的,当最低位为 0 时,前导码是 0x55,当最低位是 1 时,前导码是 0xAA。0x55 和 0xAA 这两种前导码的设计是结合工程中的实际问题确定的,在实际的环境中可能会出现RF极性翻转等问题,导致接收到的数据解调后可能是 0x55 或 0xAA, 但是接收时通过 Access Address 最低位就能确定这个究竟是“真的”前导码,还是误触发的或者随机出现的。Preamble 的设计相当于设计两道门槛,只允许“大概率是真实数据包”的数据进入,把有限的资源分配给真正需要的数据。
Access Address 访问地址 在BLE中只保留两种,一个是广播通道,一个是数据通道。广播通道固定为 0x8E89BED6, 数据通道的 Access Address 是随机的,只要生成的不是特殊的即可(特殊的包括 全0、全1、与广播通道一致等)。在实际的接收方视角下,接收的数据可能是包含噪声的,所以需要在其中定位 Preamble 和 Access Address ,达到数据筛选的目的。同时,广播通道的访问地址是固定的,但是数据通道的访问地址是随机生成的,这就需要了解到在建立数据连接之前,这个访问地址已经通过 CONNECT_IND 类型的方式携带,这一步就告诉接收方,你连接后需要接收 Access Address 是 xxx 数据。
1 | |
PDU Header
PDU Header 结构
接下来就是在开发中关注最多的PDU部分,这部分可以简单的拆解为 PDU Header 和 Payload 两部分,其中 Header 有 2 字节,两字节的结构如下
1 | |
PDU Type
其中 PDU Type 决定了这整个PDU包是什么语义、进入什么状态机,下面是所有的 PDU Type 类型
| 分类 | PDU Type | 方向 | 语义 | 是否可连接 | 是否可扫描响应 | 状态机作用 |
|---|---|---|---|---|---|---|
| 广播 | ADV_IND | 广播 → 所有 | 可连接广播(通用设备发现) | ✔ | ✔ | Advertising |
| 广播 | ADV_DIRECT_IND | 广播 → 指定设备 | 定向连接广播 | ✔ | ❌ | Advertising → Connect |
| 广播 | ADV_SCAN_IND | 广播 → Scanner | 可扫描广播 | ❌ | ✔ | Advertising |
| 广播 | ADV_NONCONN_IND | 广播 → 所有 | 不可连接广播(Beacon) | ❌ | ❌ | Advertising |
| 分类 | PDU Type | 方向 | 语义 | 作用 |
|---|---|---|---|---|
| 扫描 | SCAN_REQ | Scanner → Advertiser | 请求更多广播信息 | 扫描交互 |
| 扫描 | SCAN_RSP | Advertiser → Scanner | 补充广播数据 | 扫描交互响应 |
| 分类 | PDU Type | 方向 | 语义 | 核心作用 |
|---|---|---|---|---|
| 连接建立 | CONNECT_IND / CONNECT_REQ | Central → Peripheral(广告信道) | 建立连接 | 切换到 Data Channel |
| 连接建立 | AUX_CONNECT_REQ (BLE 5) | Central → Peripheral | 扩展广播连接建立 | 扩展广告 |
| 分类 | PDU Type | 方向 | 语义 | 状态 |
|---|---|---|---|---|
| 数据通道 | DATA PDU | 双向 | 已建立连接的数据传输 | Connection State |
| 分类 | PDU Type | 方向 | 语义 | 说明 |
|---|---|---|---|---|
| 扩展广播 | AUX_ADV_IND | 广播 → 扫描器 | BLE 5 扩展广播入口 | Secondary Channel |
| 扩展扫描 | AUX_SCAN_REQ | Scanner → Advertiser | 扩展扫描请求 | BLE 5 |
| 扩展扫描 | AUX_SCAN_RSP | Advertiser → Scanner | 扩展扫描响应 | BLE 5 |
RFU 预留
RFU 有 1 个字节,SIG将其暂时作为保留位置,等待后续扩展。
ChSel
ChSel Channel Section 表示是否支持 Channel Selection Algorithm #2 算法,当 ChSel 为 0 时表示不支持(默认支持 CSA #1),只有当其为 1 时才表示支持。
TxAdd 和 RxAdd
TxAdd 和 RxAdd 分别表示发送、接收方的地址类型,当为 0 时表示随机地址,当为 1 时表示公有地址。这只时一个标志位,表示公有还是随机。当表示公有时,这个地址是IEEE为厂家分配的,当表示随机时,这个地址是随机生成的。
Length
Payload Lenghth 表示后续 payload 的长度,目的是告诉接收方从我开始接收指定长度的数据。当广播时,payload 长度最大是 37 所以 Payload Length 是 0x25。按理说 6 bits 最大能够表示的 payload length 是 64 个字节,就是 0x40 ,但是在广播中只允许 payload 以最大 37 个字节的长度发送,这一点需要注意。
PDU Payload
PDU Payload 的结构取决于 PDU Type,下面列举几个不同PDU Type 下 Payload 的示例
ADV_IND 广播
1 | |
其中 AdvData 是由多个 AD Structure 组成,下面是其结构
1 | |
比如一个广播的 AdvData 数据是
1 | |
那么其中 AD Structure 是
1 | |
SCAN_RSP
1 | |
使用 SCAN_RSP 可以让广播携带更多附加信息,相当于对 ADV_IND 的补充,比如在一个应用中需要过滤某服务ID为 xxx-xxx-xxxx 的设备,则可以通过这个将这部分数据广播出去。
CONNECT_IND
这是连接发起时重要的一次广播,他会携带,它的结构是
1 | |
我们之前谈论的 Access Address 就在其 LLData 中,这次之后接收方就知道发送方每次发送的 Access Address。
1 | |
CRC
CRC 这部分是 CRC24, 包含 PDU Header + PDU Payload,不包含 Preamble 和 Access Address。需要注意的是,在广播信道中 CRC 初始寄存器的值是 0x555555,再此基础上计算。
1 | |
以上就是BLE低功耗蓝牙的广播通道和数据通道的从基本数据流、格式。