BLE的GATT架构适用于小的固定数据(每个特性最多20个字节)。但在某些情况下,您最终希望“流式传输”一些任意长度的数据,即大于20个字节。例如,固件升级,即使您知道它很慢。
我很好奇其他人曾经使用过哪种方案来“流式传输”BLE特征的数据(即使是小而慢)。
到目前为止,我使用了两种不同的方案:
一个是使用控制特性,其中接收设备通知发送设备已经接收了多少数据,然后发送设备使用它来触发下一次写入(我同时执行了with_response和without_response)特性
我最近做的另一个方案是基本上将数据块化为19个字节的段,其中第一个字节表示当它达到0时要遵循的数据包的数量,这表明接收器可以连接所有最近的更新并作为单个数据包处理。
我正在寻找的那种答案,概述了有经验的人如何实现这样做的体面模式。并且可以证明为什么他们所做的是最好的(或至少更好的)解决方案。
答案 0 :(得分:14)
在对现有协议进行一些审查之后,我最终设计了一个用于无线更新BLE外设的协议。
1表示2-5,6表示性能要求,7表示优化,8表示可移植性。
在发现服务并阅读一些只读特征以检查设备与要上传的图像的兼容性后,所有上传都发生在两个特征之间:
整个固件映像通过有效负载特性以块的形式发送。
Payload是一个20字节的特性:4字节的块偏移量加上16字节的数据块。
状态通知告知是否存在错误条件,以及下一个预期的有效负载块偏移量。这样,上传者可以判断它是否可以继续推测,从其自己的偏移量发送其块,或者是否应该从状态通知中找到的偏移量恢复。
发送状态更新有两个主要原因:
Receiver期望所有块按顺序排列,它不会重新排序。如果一个块出现故障,它就会被丢弃,并且会推送一个错误状态通知。
当状态进入时,它会隐式确认所有具有较小偏移量的块。
最后,发送方有一个发送窗口,许多成功的确认飞行允许发送方放大其窗口(在匹配确认之前发送更多块)。如果发生错误,窗口会减少,丢弃的块可能是因为队列在某处溢出。
使用“单向”PDU(没有响应和通知写入)是为了避免6.以上,因为ATT协议明确告知已确认的PDU(写入,指示)不能被流水线化(即,在收到之前,您可能不会发送下一个PDU响应)。
状态,包含最后收到的块,缓和5。
要遵守2.和3.有效载荷是20字节的特征写入。 4 + 16有许多优点,一个是偏移验证,一个16字节的块只涉及移位,另一个是块总是在目标闪存中页面对齐(最好是7。)。
要处理4.在接收状态更新之前发送了多个块,推测它将被正确接收。
此协议具有以下功能:
某些参数不属于此协议:
使用:
我平均每个连接事件得到3.8个数据包,即在数据包丢失,协议开销等之后有效负载约为6 kB / s。
这样,在不到10秒的时间内完成60 kB图像的上传,整个过程(连接,发现,传输,图像验证,解压缩,闪烁,重启)在20秒内完成。
答案 1 :(得分:1)
这取决于你拥有什么样的中央设备。 通常,Write Without Response是通过BLE传输数据的方式。 不应该发生无序接收的数据包,因为BLE的链路层永远不会在前一个数据包被确认之前发送下一个数据包。
对于Android来说,它非常简单:只需使用Write Without Response即可一个接一个地发送所有数据包。获得onCharacteristicWrite后,您将发送下一个数据包。这样Android就会自动对数据包进行排队,它也有自己的流量控制机制。当所有缓冲区都被填满时,onCharacteristicWrite将在再次出现空间时被调用。
然而,iOS并不那么聪明。如果您发送大量Write Without Response数据包并且内部缓冲区已满,iOS将默默地丢弃新数据包。有两种解决方法,要么为外设实现一些(可能是复杂的)协议,通知传输状态,如Nipos答案。然而,更简单的方法是将每个第10个数据包发送为“响应时写入”,其余部分作为“无响应写入”发送。这样iOS将为您排队所有数据包而不会丢弃Write Without Response数据包。唯一的缺点是Write With Response数据包需要一次往返。然而,该方案应该为您提供高吞吐量。