2. 连接建立流程:广播、扫描、发起连接、连接参数协商

大家好,我是你们的老朋友。今天我们来聊聊蓝牙GATT连接是怎么建立起来的。很多刚入行的工程师,遇到连接断开的Bug,第一反应就是查信号、查天线。但说实话,连接建立阶段的参数协商,才是很多诡异断连问题的根源。

我个人习惯把连接建立拆成四个阶段:广播 → 扫描 → 发起连接 → 参数协商。咱们一个一个来过。

核心观点:连接参数协商是GATT连接的“合同签署”环节。合同签得不好,后面执行起来全是坑。

2.1 广播阶段 —— 设备在喊“我在这里”

广播,说白了就是外设(Peripheral)在不停地喊:“我在这儿,我能连,我的名字是XXX,我支持这些服务……”

广播包分两种:

  • 广播包(Advertising Packet):必发,包含设备地址、广播类型、部分数据。
  • 扫描响应包(Scan Response Packet):可选,当主机扫描时,外设可以回复更多信息。

我记得有一次调试一个手环项目,设备死活连不上手机。抓空包一看,广播间隔设成了20ms,但广播信道太拥挤,导致手机收不到完整的广播包。后来我把广播间隔调到100ms,问题就解决了。嗯,这里要注意:广播间隔太短,功耗高且容易丢包;太长,连接响应慢。

参数 典型值 说明
广播间隔 20ms ~ 10.24s 步进0.625ms,实际项目中常用100ms~1s
广播信道 37/38/39 三个专用信道,避免与Wi-Fi冲突
广播类型 可连接/不可连接/可扫描 决定设备能否被连接

实战技巧:如果你在做低功耗产品,广播间隔建议设在200ms以上。我曾经见过一个项目,为了追求“秒连”,把广播间隔压到30ms,结果电池两天就耗光了。

2.2 扫描阶段 —— 主机在“听”

主机(Central)这边,会定期打开接收窗口,监听广播信道。扫描也分两种模式:

  • 被动扫描:只听,不发请求。省电,但信息有限。
  • 主动扫描:听到广播后,发送扫描请求,获取扫描响应包。信息更全,但功耗高。

你想想看,如果主机扫描窗口太小,或者扫描间隔太长,就可能漏掉外设的广播包。这就是为什么有时候手机靠近设备,却搜不到——不是设备没广播,而是手机没在听。

避坑指南:我曾经遇到一个案例,外设广播间隔100ms,主机扫描间隔1.28s,扫描窗口只有11.25ms。结果主机平均要等3~5个广播周期才能抓到一次。这种配置下,连接建立延迟高达数秒,用户体验极差。

2.3 发起连接 —— 握手瞬间

当主机扫描到目标外设后,会发送一个连接请求(CONNECT_REQ)。这个请求里,包含了主机希望使用的连接参数。

连接请求包含的关键信息:

  • 发起者的物理地址
  • 接收者的物理地址
  • 连接参数(我们下一节细讲)
  • 信道映射
  • 跳频算法增量

外设收到连接请求后,如果接受,就会回复一个空包(Empty PDU)。至此,连接建立成功。但注意,此时连接参数还是主机单方面提出的,外设可以后续要求修改。

2.4 连接参数协商 —— 真正的“谈判桌”

连接建立后,外设可以通过连接参数更新请求(Connection Parameter Update Request),向主机提出新的参数。主机可以选择接受、拒绝或提出折中方案。

这里涉及三个核心参数:

参数 单位 范围 说明
Connection Interval 1.25ms 6 ~ 3200 两次连接事件之间的时间间隔。值越小,延迟越低,功耗越高。
Slave Latency 次数 0 ~ 499 从设备可以跳过多少个连接事件而不响应。值越大,从设备越省电。
Supervision Timeout 10ms 10 ~ 3200 如果在这个时间内没有收到任何包,连接被视为断开。

这三个参数之间有一个硬约束:

Supervision Timeout > (1 + Slave Latency) × Connection Interval × 2

为什么?因为从设备最多可以跳过Slave Latency个连接事件,再加上当前事件,最坏情况下要等 (1 + Slave Latency) × Connection Interval 的时间才能收到一个包。Supervision Timeout必须大于这个值的两倍,否则连接会被误判为断开。

我在项目中遇到过最典型的案例:一个传感器节点,为了省电把Slave Latency设成100,Connection Interval设成100ms,但Supervision Timeout只设了5秒。算一下:(1+100)×100ms×2 = 20.2秒,远超5秒。结果设备每隔几分钟就断连一次。查了三天才找到原因。

黄金法则:Supervision Timeout 至少是 (1 + Slave Latency) × Connection Interval × 3,留出余量。

2.5 连接参数协商的完整流程

下面这张图,是我自己画的一个流程总结。你可以把它当作调试时的参考地图。

连接建立与参数协商流程图 外设广播 主机扫描 广播包 主机发起连接请求 CONNECT_REQ 连接建立 外设发起参数更新请求 主机接受/拒绝 注意:参数协商可以在连接生命周期的任意时刻发起,不限于连接建立阶段 但连接建立后的第一次协商最为关键,直接影响后续通信质量

2.6 参数选择的实际考量

讲完流程,咱们聊聊实际项目中怎么选参数。我总结了一个简单的对照表:

应用场景 Connection Interval Slave Latency Supervision Timeout
高实时性(如鼠标、键盘) 7.5ms ~ 15ms 0 100ms ~ 500ms
中等实时性(如手环通知) 30ms ~ 50ms 2 ~ 5 1s ~ 3s
低功耗传感器(如温湿度计) 100ms ~ 500ms 10 ~ 100 5s ~ 20s

个人经验:如果你不确定怎么配,先从保守值开始:Connection Interval 50ms,Slave Latency 0,Supervision Timeout 2s。等产品稳定了,再逐步优化。我曾经在一个医疗项目中,因为一开始就追求极致功耗,结果参数配得太激进,导致连接频繁断开,差点延误了项目进度。

2.7 常见问题与排查思路

最后,我列几个我在项目中遇到过的典型问题,供你参考:

  • 问题1:连接建立慢 → 检查广播间隔和扫描窗口是否匹配。广播间隔100ms,扫描窗口至少11.25ms,扫描间隔不超过1s。
  • 问题2:连接频繁断开 → 先算 Supervision Timeout 是否满足约束。90%的断连问题出在这里。
  • 问题3:数据延迟高 → 检查 Connection Interval 是否太大。如果对实时性有要求,建议不超过30ms。
  • 问题4:功耗异常高 → 检查 Slave Latency 是否设为0。适当增加 Slave Latency 可以显著降低从设备功耗。

重要提醒:连接参数协商不是一次性的。外设可以在任何时候发起参数更新请求。但注意,频繁发起参数更新会导致连接不稳定。我建议只在必要时发起,比如设备状态发生重大变化时(从充电状态切换到工作状态)。

好了,关于连接建立流程和参数协商,今天就聊到这里。这些内容看起来简单,但实际调试中,每一个参数都可能成为坑。希望你能把这些知识用到实际项目中,少走一些弯路。