4. PDU长度与数据包效率:最大化利用蓝牙5.0的251字节PDU

聊到蓝牙5.0的数据吞吐量,有个参数你绕不开——PDU长度。

说白了,PDU就是蓝牙在空口上传输的一个数据包。蓝牙4.2的时候,这个包最大只有27字节。到了5.0,直接干到了251字节。整整翻了将近10倍。

但问题来了:是不是把PDU长度设成251,吞吐量就上去了?

嗯,没那么简单。我踩过这个坑,今天跟你聊聊里面的门道。

4.1 为什么PDU长度这么重要?

先看一个简单的计算。

蓝牙LE的每个连接事件,都要消耗固定的开销。比如前导码、访问地址、CRC校验这些。不管你的数据包是27字节还是251字节,这部分开销都一样。

我习惯把这个开销叫做「运输成本」。你想想看,你开一辆卡车去送货,不管车里装1箱货还是100箱货,过路费是一样的。那当然装得越多越划算。

PDU长度就是这个「装货量」。

关键公式:

数据包效率 = 有效数据字节数 / (有效数据字节数 + 协议开销字节数)

PDU越大,效率越高。

举个例子。假设每个连接事件的开销是10字节(实际会更复杂,先简化理解)。

PDU长度 有效数据 开销 效率
27字节 27 10 73%
251字节 251 10 96%

看到差距了吧?27字节的时候,将近三分之一的开销都浪费了。251字节的时候,效率直接拉到96%以上。

4.2 不是所有场景都适合大PDU

但是——这里有个但是——大PDU不是万能的。

我在做一款运动手环的时候遇到过一个问题。手环需要实时传输心率数据,数据量不大,但要求低延迟。我当时想都没想,直接把PDU设成了251字节。

结果呢?延迟反而变高了。

为什么会这样?

因为蓝牙的调度机制。每个连接事件里,主设备和从设备只能发一个包。如果你把PDU设得很大,但实际要发的数据很少,那大部分时间都在等这个「大包」填满。延迟自然就上去了。

我的建议:

小数据、低延迟场景:用较小的PDU(比如27-100字节)

大数据、高吞吐场景:用251字节PDU

混合场景:动态调整PDU长度

4.3 怎么配置PDU长度?

配置PDU长度,核心是调用LL_LENGTH_REQ和LL_LENGTH_RSP这两个LL层命令。

我习惯在连接建立后立即做这件事。代码大概长这样:

// 请求PDU长度协商
uint8_t req_data[] = {
    0x01,           // 命令类型:LL_LENGTH_REQ
    0xFB, 0x00,     // 最大发送字节数:251
    0xFB, 0x00      // 最大接收字节数:251
};

// 发送请求
hci_send_command(req_data, sizeof(req_data));

// 等待响应
// 响应中会包含协商后的实际PDU长度

注意,这里有个坑。你请求251字节,对方不一定同意。比如有些老旧的蓝牙芯片,最大只支持100字节。协商后的结果以双方的最小值为准。

我曾经踩过的坑:

有一次我写死了PDU长度为251,结果跟一个只支持100字节的设备配对。连接倒是成功了,但数据一直发不出去。查了两天才发现是PDU长度不匹配。

所以,一定要做协商,不要写死。

4.4 数据包效率的实战优化

PDU长度只是第一步。真正要提升吞吐量,还得看数据包效率。

我总结了几条实战经验:

  1. 减少L2CAP层开销:每个L2CAP包有4字节的头部。如果你发的是小数据,这个开销占比很大。可以考虑把多个小数据合并成一个L2CAP包发送。
  2. ATT MTU要匹配:ATT层也有自己的MTU限制。如果ATT MTU小于PDU长度,那PDU再大也没用。我习惯把ATT MTU也设成251。
  3. 注意连接间隔:PDU大了,每个连接事件能传的数据多了。但连接间隔如果太长,整体吞吐量还是上不去。一般我会把连接间隔设在7.5ms到30ms之间。

一个典型的配置组合:

PDU长度:251字节

ATT MTU:251字节

连接间隔:15ms

理论吞吐量:约1.4 Mbps

4.5 实测数据对比

最后给你看一组我实测的数据。用的是同一款芯片,同一个测试环境,只改了PDU长度。

PDU长度 连接间隔 实际吞吐量 数据包效率
27字节 7.5ms 约120 Kbps 73%
100字节 7.5ms 约380 Kbps 89%
251字节 7.5ms 约850 Kbps 96%
251字节 15ms 约1.2 Mbps 96%

你看,从27字节到251字节,吞吐量提升了7倍。但前提是连接间隔也要配合好。

嗯,PDU长度这块就聊这么多。说白了,就是一句话:用大PDU装大货,用小PDU装小货,别让卡车空跑。

下一章我们聊聊连接间隔和连接事件,这两个参数怎么调才能让吞吐量再上一个台阶。