2. BLE与WiFi协议栈协同基础

做物联网嵌入式开发,尤其是搞WiFi配网这一块,BLE几乎是绕不开的坎。我最早接触BLE时,看着那三层架构——Controller、Host、Application——说实话,有点懵。后来调了几个月的蓝牙配网,才慢慢摸清楚门道。

这一章,咱们就把BLE协议栈的底裤扒开看看。不扯太深的理论,重点讲清楚你写代码时真正需要关心的东西。

2.1 BLE协议栈架构:三层分工

BLE协议栈分三层,说白了就是:底层管收发,中间层管逻辑,上层管业务。我习惯把这三层想象成一个快递公司:

  • Controller(控制器):相当于快递员。负责把数据包发出去、收回来。它处理的是射频、跳频、时序这些物理层的东西。
  • Host(主机):相当于分拣中心。负责把快递员的包裹拆开、重组、分类。它处理的是连接管理、数据封装、安全加密。
  • Application(应用层):相当于收件人。你只关心包裹里是什么东西,不关心快递员怎么送的。

实际芯片里,Controller和Host可能在同一颗芯片上(比如Nordic的nRF52832),也可能分开(比如有些方案用两颗芯片)。嗯,这里要注意:如果你用双芯片方案,Controller和Host之间走的是HCI(Host Controller Interface)协议,通常是UART或SPI。

核心要点:做WiFi配网时,我们主要工作在Host和Application层。Controller层由芯片厂商的SDK封装好了,你基本不用碰。

BLE 协议栈三层架构 Application(应用层) 用户业务逻辑:WiFi配网、数据传输、设备控制 你写的代码就在这里 Host(主机层) GAP · GATT · SMP · L2CAP · ATT 连接管理、数据封装、安全加密 Controller(控制器层) 物理层(PHY)· 链路层(LL)· 直接测试模式 射频收发、跳频、时序控制 💡 实际开发中,Controller 由 SDK 封装,我们主要操作 Host 和 Application 层

2.2 GAP:通用访问协议——设备怎么“打招呼”

GAP(Generic Access Profile)定义了BLE设备之间怎么发现对方、怎么建立连接。你想想看,两个蓝牙设备第一次见面,总得有个规矩吧?GAP就是干这个的。

GAP定义了两种角色:

角色 说明 配网场景
Broadcaster(广播者) 周期性发送广播包,不连接 WiFi设备上电后广播自身信息
Observer(观察者) 扫描广播包,不连接 手机扫描周围WiFi设备
Peripheral(外设) 发送广播,接受连接 WiFi模块作为从设备等待手机连接
Central(中心) 扫描并发起连接 手机作为主设备连接WiFi模块

我在项目中遇到过一个问题:广播包里的数据放太多,导致手机扫描不到。BLE广播包最大31字节,你塞个WiFi SSID进去基本就满了。后来我学乖了,广播包只放设备类型和状态,具体数据走连接后的GATT通信。

实战技巧:广播间隔建议设在100ms~200ms之间。太快了费电,太慢了手机扫描响应慢。我一般用152.5ms,这是BLE规范推荐的值。

2.3 GATT:通用属性协议——数据怎么“传”

GAP管的是“谁是谁”,GATT管的是“数据怎么传”。GATT基于客户端-服务器模型:

  • GATT Server:持有数据的一方(通常是WiFi模块)
  • GATT Client:读取/写入数据的一方(通常是手机)

GATT的数据结构分三层:

  1. Service(服务):一组相关功能的集合。比如“WiFi配网服务”
  2. Characteristic(特征):具体的数据项。比如“WiFi SSID特征”、“WiFi密码特征”
  3. Descriptor(描述符):特征的额外说明。比如“客户端特征配置描述符(CCCD)”

说白了,Service就像文件夹,Characteristic就像文件,Descriptor就像文件的备注。你写代码时,主要就是定义这些Characteristic。

注意:每个Characteristic都有UUID。标准服务用16位UUID(如0x180A),自定义服务用128位UUID。配网服务建议用自定义UUID,避免和标准服务冲突。

2.4 广播、扫描、连接、数据传输流程

整个BLE通信流程,我总结为四步走:

步骤 谁在做 干了什么
1. 广播 Peripheral(WiFi模块) 周期性发送广播包,包含设备名、UUID、状态
2. 扫描 Central(手机) 扫描周围广播包,筛选目标设备
3. 连接 Central发起,Peripheral响应 建立连接,协商连接参数(间隔、延迟、超时)
4. 数据传输 Client/Server双向 通过GATT读写Characteristic,完成配网数据交换

这里有个坑,我曾经踩过:连接参数没调好,导致数据传输断断续续。连接间隔(Connection Interval)设得太小,模块频繁唤醒,功耗高;设得太大,数据延迟大。配网场景我建议用30ms~50ms的连接间隔,兼顾速度和功耗。

BLE 通信四步流程 ① 广播 WiFi模块发送 广播包 ② 扫描 手机扫描 发现设备 ③ 连接 手机发起连接 参数协商 ④ 数据传输 GATT读写 配网数据 各步骤关键参数 ① 广播间隔:100~200ms,广播数据:31字节 ② 扫描窗口:30~60ms,扫描间隔:100~200ms ③ 连接间隔:30~50ms,延迟:0,超时:4~6秒 ④ MTU大小:默认23字节,可协商至247字节 ⚠ 我曾经踩过的坑:连接间隔设成10ms,模块发热严重;设成100ms,配网超时。 最终妥协方案:配网阶段用30ms,配网完成后切回100ms省电。

2.5 配网场景下的关键参数

做BLE辅助WiFi配网,有几个参数你必须心里有数:

参数 推荐值 说明
广播间隔 152.5ms BLE规范推荐,兼顾功耗和发现速度
连接间隔 30~50ms 配网阶段用30ms,完成后切回100ms
MTU大小 协商至247字节 默认23字节,协商后提升传输效率
广播数据 ≤31字节 只放关键信息,详细数据走GATT
扫描响应 ≤31字节 可选,用于回复扫描请求

我的习惯:广播包里只放3样东西——设备类型(1字节)、配网状态(1字节)、自定义UUID(16字节)。剩下的空间留给厂商自定义数据。这样手机扫描时就能快速判断是不是目标设备。