4、影响吞吐量的关键因素:PHY速率、连接间隔、PDU大小、MTU大小

吞吐量测试做到一定阶段,你会发现一个现象:明明两边设备都支持高速率,实际测出来的数据就是上不去。我刚开始做BLE吞吐优化时也遇到过这个问题,折腾了好几天,最后发现是几个基础参数没配对。

说白了,BLE的吞吐量不是由某一个参数决定的,而是由四个关键因素共同制约。就像水管出水,管径、水压、阀门开度、出水口大小,哪个环节卡住,流量都上不去。

核心公式(简化版):

吞吐量 ≈ (PDU有效载荷 × 每连接事件包数) / 连接间隔

这个公式虽然粗糙,但能帮你快速定位瓶颈在哪。

BLE 吞吐量四大影响因素 BLE 吞吐量 PHY 速率 连接间隔 PDU 大小 MTU 大小 四个因素相互制约,最短的那块板决定最终吞吐量

4.1 PHY速率:物理层的硬门槛

PHY(Physical Layer)是BLE的物理层,它决定了无线信号调制的速率。目前主流的有三种:1M PHY、2M PHY和LE Coded PHY。

PHY 类型 理论速率 实际有效吞吐 适用场景
1M PHY 1 Mbps 约 300-500 kbps 兼容性优先,旧设备
2M PHY 2 Mbps 约 700-1200 kbps 高速数据传输
LE Coded PHY (S=2) 500 kbps 约 100-200 kbps 远距离、抗干扰
LE Coded PHY (S=8) 125 kbps 约 30-80 kbps 超远距离、穿墙

我个人习惯,只要设备支持,优先选2M PHY。为什么?因为它的速率翻倍,但功耗增加并不明显。我在一个穿戴设备项目里,从1M切到2M后,吞吐量直接翻了一番,电池续航只少了不到10%。

小技巧: 在nRF Connect或LightBlue里,连接后可以查看当前使用的PHY。如果显示的是1M,而你的设备支持2M,记得在连接参数里主动请求切换。

4.2 连接间隔:时间窗口的博弈

连接间隔(Connection Interval)是主机和从机之间交换数据的周期。单位是1.25ms的倍数,范围从7.5ms到4s。

你想想看,连接间隔越小,数据交换越频繁,吞吐量自然就高。但代价是什么?功耗也上去了。因为设备要更频繁地醒来收发数据。

我在一个医疗设备项目里遇到过这种情况:客户要求数据实时上传,但设备要用纽扣电池撑一个月。最后我们把连接间隔设在15ms,配合较大的PDU,才勉强平衡了吞吐和功耗。

连接间隔 每连接事件包数 理论吞吐(2M PHY) 功耗
7.5 ms 1-2 ~1.2 Mbps 高
15 ms 3-4 ~800 kbps 中
30 ms 5-6 ~500 kbps 低
50 ms 6-8 ~300 kbps 极低

注意: 连接间隔不是越小越好。iOS和Android对连接间隔有最低限制,通常不能低于15ms。强行设置过小的间隔,可能导致连接不稳定甚至断开。

4.3 PDU大小:数据包的容量

PDU(Protocol Data Unit)是BLE协议栈里实际传输的数据单元。在BLE 4.2之前,PDU最大只有27字节。4.2之后引入了LE Data Length Extension(DLE),可以把PDU扩展到251字节。

这个提升是巨大的。我举个例子:

  • 没有DLE:每次最多发27字节,其中还有3字节是L2CAP头,实际有效载荷只有24字节。
  • 启用DLE:每次最多发251字节,L2CAP头还是3字节,有效载荷248字节。

算一下,同样是发100KB数据,没有DLE需要发约4200个包,启用DLE后只需要约410个包。包数量少了,空中碰撞的概率也低了,吞吐量自然就上去了。

关键点: DLE需要在连接建立后由主机或从机主动发起请求。不是所有协议栈都默认开启的,你得在代码里显式调用API。

我曾经在一个固件升级项目里,忘记开启DLE,结果升级一个200KB的固件花了将近3分钟。后来加上DLE,时间缩短到40秒。嗯,这个坑我踩过,所以现在每次做吞吐测试,第一件事就是检查DLE是否启用。

4.4 MTU大小:协议栈的缓冲区

MTU(Maximum Transmission Unit)是L2CAP层的最大数据单元。它和PDU大小是两回事,但很多人容易搞混。

简单来说:

  • PDU大小:物理层一次能发多少数据(由DLE控制,最大251字节)。
  • MTU大小:应用层一次能提交给协议栈多少数据(由ATT_MTU协商,最大517字节)。

MTU协商是在连接建立后,通过Exchange MTU Request/Response完成的。如果MTU设置得小,即使PDU再大,应用层也只能一次发一点点数据,协议栈会帮你分包。

举个例子:

// MTU协商示例(基于Zephyr)
// 发起MTU请求,建议设为247(兼容大多数设备)
bt_gatt_exchange_mtu(conn, NULL);

// 获取协商后的MTU值
uint16_t mtu = bt_gatt_get_mtu(conn);
printk("Negotiated MTU: %d\n", mtu);

我建议你在做吞吐测试时,先把MTU设到247或更高。为什么是247?因为这是大多数BLE芯片默认支持的最大值,再高的话,有些老设备可能不兼容。

MTU 大小 单包有效载荷 适用场景
23(默认) 20 字节 兼容性测试,低数据量
247 244 字节 大多数BLE设备,推荐
517 514 字节 高性能设备,需双方支持

避坑指南: 我曾经在调试时发现,MTU协商成功了,但实际发送数据还是被截断。后来查了半天,发现是应用层的发送缓冲区没调大。记住,MTU协商只是告诉协议栈“我能收这么大”,但你的发送代码也得配合。

4.5 四个因素的协同配置

这四个因素不是孤立的,它们互相影响。我画了一张图帮你理解它们的关系:

四因素协同配置流程 1. 选择 PHY 速率 2. 协商 MTU 大小 3. 启用 DLE 4. 调整连接间隔 配置建议 • 先选2M PHY(如果设备支持) • MTU 协商到 247 或更高 • 启用 DLE(Data Length Extension) • 连接间隔根据功耗需求调整,建议 15-30ms

实际测试时,我建议你按这个顺序来排查:

  1. 先确认PHY:是不是2M?如果不是,能不能切?
  2. 再查MTU:协商后的MTU是多少?有没有被限制?
  3. 检查DLE:PDU大小是不是251?有些协议栈默认不开。
  4. 最后调连接间隔:在功耗允许范围内,尽量缩小间隔。

这四个因素都调对了,吞吐量基本不会差。我见过太多人只盯着一个参数调,结果折腾半天没效果。记住,短板效应在这里特别明显。