4. 连接间隔优化:连接间隔概念、连接间隔对延迟和功耗的影响、动态调整连接间隔策略

连接间隔,说白了就是两个设备之间约定好多久握一次手。在 BLE 的世界里,主机和从机不会一直说话,而是每隔一段时间交换一次数据包。这个时间间隔,就是连接间隔。

我刚开始做 BLE 项目时,总觉得这个参数随便设一个就行。直到有一次,一个穿戴设备因为连接间隔设得太短,电池撑不过半天。另一个项目又因为设得太长,用户按一下遥控器要等半秒才有反应。嗯,从那以后,我再也不敢小看这个参数了。

核心概念:连接间隔(Connection Interval)是 BLE 连接中两个连续连接事件之间的时间间隔,单位是 1.25ms 的整数倍。范围从 7.5ms(6 个单元)到 4s(3200 个单元)。

4.1 连接间隔的工作原理

每次连接事件,主机和从机都会在同一个物理信道上交换数据。连接事件结束后,双方进入休眠状态,直到下一个连接事件到来。

你想想看,这就像两个人约好每 10 分钟通一次电话。通话期间把该说的事情说完,然后挂断,各自忙各自的。如果事情多,可以多聊一会儿(多个数据包);如果没事,说一句「没事了」就挂断(空包)。

连接间隔时序图 连接事件 连接间隔 连接事件 连接间隔 连接事件 休眠 休眠 休眠 说明:连接事件期间双方交换数据,之后进入休眠。连接间隔越短,休眠时间越短,功耗越高。 连接间隔越长,休眠时间越长,但数据延迟也越大。

4.2 连接间隔对延迟的影响

延迟,就是数据从发送端到接收端需要的时间。在 BLE 里,这个时间主要由连接间隔决定。

举个例子。假设连接间隔是 50ms。从机在连接事件结束后产生了一个数据,它必须等到下一个连接事件才能发送出去。平均等待时间就是连接间隔的一半,也就是 25ms。再加上传输时间,总延迟大约在 30ms 左右。

如果连接间隔缩短到 10ms,平均延迟就降到 5ms 左右。这对实时性要求高的应用,比如游戏手柄、无线鼠标,就非常关键。

应用场景 推荐连接间隔 典型延迟 说明
游戏手柄 7.5ms - 15ms 5 - 10ms 用户能感知的延迟阈值约 20ms
无线鼠标 10ms - 20ms 8 - 15ms 快速移动时延迟必须低
健康监测 100ms - 500ms 60 - 300ms 心率数据不需要实时
传感器数据采集 500ms - 2s 300ms - 1.2s 温度、湿度等慢变数据

我的经验:做游戏外设时,我习惯把连接间隔设在 10ms 以内。但要注意,连接间隔越短,射频占空比越高,干扰风险也越大。我曾经在一个展会现场,因为周围蓝牙设备太多,手柄频繁断连。后来把连接间隔从 7.5ms 调到 12.5ms,配合跳频算法,问题就解决了。

4.3 连接间隔对功耗的影响

功耗问题,说白了就是电池能用多久。连接间隔对功耗的影响非常直接。

每次连接事件,设备都要唤醒射频模块,完成同步、收发数据、确认等操作。这个过程大约需要 2-5ms 的时间,电流在 5-15mA 之间。连接间隔越短,单位时间内唤醒次数越多,平均电流就越大。

我算过一笔账。一个纽扣电池供电的传感器,如果连接间隔设为 20ms,平均电流大约 200μA,电池能用 3 个月。如果把连接间隔拉到 500ms,平均电流降到 20μA,电池能用 2 年以上。差距就是这么明显。

连接间隔 每秒连接事件数 平均电流(估算) CR2032 续航
7.5ms 133 ~800μA ~15 天
30ms 33 ~200μA ~2 个月
100ms 10 ~60μA ~6 个月
500ms 2 ~15μA ~2 年
1s 1 ~8μA ~3 年

注意:以上数据基于典型 BLE 芯片(如 nRF52832、CC2640)的实测估算。实际功耗还受发射功率、数据包长度、连接事件内数据包数量等因素影响。不要直接套用,一定要在自己的硬件上实测。

4.4 动态调整连接间隔策略

既然连接间隔对延迟和功耗的影响是相反的,那有没有办法两全其美?答案是:动态调整。

说白了,就是在需要低延迟的时候用短间隔,在空闲的时候用长间隔。BLE 规范提供了连接参数更新请求(Connection Parameter Update Request),允许从机主动向主机申请改变连接间隔。

我常用的策略有三种:

  1. 基于数据量的调整:当发送缓冲区有数据待发时,缩短连接间隔;缓冲区空了,就拉长间隔。
  2. 基于用户交互的调整:检测到用户操作(如按键、触摸)时,立即切换到短间隔;用户停止操作一段时间后,逐渐拉长。
  3. 基于场景的调整:比如运动传感器在静止时用长间隔,检测到运动后切换到短间隔。
// 伪代码示例:基于数据量的动态间隔调整
void connection_interval_adaptive(uint8_t *buffer, uint16_t len) {
    static uint16_t current_interval = 100; // 初始 100ms
    
    if (len > 0) {
        // 有数据待发,缩短间隔
        if (current_interval > 20) {
            current_interval = 20; // 切换到 20ms
            ble_gap_conn_param_update(conn_handle, current_interval, 0, 0);
        }
    } else {
        // 无数据,拉长间隔
        if (current_interval < 500) {
            current_interval = 500; // 切换到 500ms
            ble_gap_conn_param_update(conn_handle, current_interval, 0, 0);
        }
    }
}

关键点:动态调整不是越快越好。频繁发送连接参数更新请求会增加协议开销,甚至被主机拒绝。我建议每次调整后至少等待 3-5 个连接间隔再发起下一次调整。

4.5 避坑指南

做动态调整时,有几个坑我踩过,分享给你:

  • 主机可能拒绝请求:iOS 和 Android 对连接间隔有各自的限制。iOS 要求连接间隔必须是 15ms 的整数倍,且不能低于 20ms。Android 相对宽松,但不同厂商的兼容性差异很大。
  • 不要频繁请求:我曾经在一个项目里,每 100ms 就发一次参数更新请求,结果主机直接断开连接。后来改成每 5 秒最多请求一次,就稳定了。
  • 考虑从机延迟:从机延迟(Slave Latency)允许从机跳过若干个连接事件而不丢失连接。适当设置从机延迟,可以在不增加延迟的情况下进一步降低功耗。
  • 实测验证:连接参数调整后,一定要用抓包工具(如 Ellisys、nRF Sniffer)确认实际生效的参数。有些主机会忽略请求,保持原有参数。

我的习惯:做产品原型时,我会先固定一个保守的连接间隔(比如 50ms),确保功能稳定。等所有功能调通后,再逐步优化连接参数。这样能避免因为连接问题而浪费大量调试时间。

连接间隔优化,说白了就是在延迟和功耗之间找平衡。没有通用的最优值,只有最适合你应用场景的值。动态调整策略给了我们两全其美的可能,但实现时一定要考虑主机的兼容性和稳定性。