Android BLE特征值如何传输兆字节级数据?1MB以上数据传输方案
结论
不需要额外创建多个BluetoothGattCharacteristic,这个方案解决不了单包传输上限的问题,还会平白增加服务发现、连接管理的额外开销,属于完全没必要的无效方案。
问题本质
你碰到的单次发送数据量受限,是BLE协议原生机制决定的,和特征数量没关系:
- BLE默认ATT MTU(最大传输单元)是23字节,扣掉3字节的ATT协议头,默认单次写入最多只能传20字节有效载荷
- 就算主动协商MTU,安卓端常规支持的最大协商值也就512字节,扣完协议头单次最多传509字节有效数据,离1MB的单次传输差了好几个量级
- 你现在直接把全量数据丢给
setValue再调writeCharacteristic的写法,超过MTU的部分会被底层直接截断,甚至直接返回写入失败,根本传不完整。
标准实现方案:分包流控传输
单特征完全可以支撑MB级数据传输,这也是行业里BLE传大数据的通用做法,具体按下面的步骤做就行:
- 先协商更高的MTU
BLE连接成功之后,第一时间调用btGatt.requestMtu(512)发起MTU协商,监听onMtuChanged回调拿到最终协商成功的MTU值,算出来单包最大可发的有效载荷长度 = 实际MTU - 3,用这个值当分包的单包大小,能少发很多包,传输效率高很多。如果你的Arduino硬件支持BLE 4.2以上的数据长度扩展(DLE),记得把这个特性开了,空口单包载荷最高能到251字节,速度还能再提一截。 - 给大文件做分包
把要传的1MB完整数据,按前面算出来的单包最大长度拆成连续的有序包,建议每个包头加2字节的序号,方便Arduino端排序、查丢包;最后一包可以带上总数据长度、CRC校验值,接收端拼完可以校验下数据有没有坏。 - 带流控逐包发送
安卓原生BLE栈没有内置写入队列,绝对不能写个循环连续调writeCharacteristic,不然必丢包,到最后大概率只有最后一次写入能成功。你自己维护一个发送队列就行:- 从队头取第一包数据调写入方法
- 等
onCharacteristicWrite回调返回写入成功,再取下一包发 - 想要更快的话,把特征的写入类型设成
WRITE_TYPE_NO_RESPONSE,这个模式不需要等外设每包回ACK,速度比默认的需要响应的写入快3-4倍,只要每隔几十包让Arduino回一次当前收到的最大包序号,缺的包重传就行,稳定性也够。
- 接收端重组数据
Arduino那边开个足够大的接收缓存,按收到的包序号逐包拼,等所有包收齐、CRC校验过了,就能拿到完整的1MB数据了。
为什么多特征方案没用
同一个BLE连接下的所有特征,走的是同一个物理通道,共享同一个MTU限制,你就算加100个特征,每个特征单次写入还是受MTU的长度卡着,根本提升不了传输能力,反而要多维护一堆特征的句柄、写入回调,纯纯给自己加活。
内容的提问来源于stack exchange,提问作者tonykrjhc
相关产品推荐
相关产品推荐

