具有BLE GATT特征的流数据的方案

时间:2016-05-11 01:26:29

标签: bluetooth-lowenergy gatt

BLE的GATT架构适用于小的固定数据(每个特性最多20个字节)。但在某些情况下,您最终希望“流式传输”一些任意长度的数据,即大于20个字节。例如,固件升级,即使您知道它很慢。

我很好奇其他人曾经使用过哪种方案来“流式传输”BLE特征的数据(即使是小而慢)。

到目前为止,我使用了两种不同的方案:

一个是使用控制特性,其中接收设备通知发送设备已经接收了多少数据,然后发送设备使用它来触发下一次写入(我同时执行了with_response和without_response)特性

我最近做的另一个方案是基本上将数据块化为19个字节的段,其中第一个字节表示当它达到0时要遵循的数据包的数量,这表明接收器可以连接所有最近的更新并作为单个数据包处理。

我正在寻找的那种答案,概述了有经验的人如何实现这样做的体面模式。并且可以证明为什么他们所做的是最好的(或至少更好的)解决方案。

2 个答案:

答案 0 :(得分:14)

在对现有协议进行一些审查之后,我最终设计了一个用于无线更新BLE外设的协议。

设计假设

  1. 我们无法预测堆栈行为(协议将与我们的所有产品一起使用,无论使用哪种芯片和供应商堆栈,无论是在外围还是在中心,可能还未知),
  2. 使用标准GATT服务,
  3. 避免L2CAP碎片,
  4. 假设数据包在TX之前排队,
  5. 假设可能有一些丢弃的数据包(即使堆栈不应该),
  6. 避免不必要的数据包往返,
  7. 将代码复杂性放在中心位置,
  8. 假设4.2增强功能不可用。
  9. 1表示2-5,6表示性能要求,7表示优化,8表示可移植性。

    总体设计

    在发现服务并阅读一些只读特征以检查设备与要上传的图像的兼容性后,所有上传都发生在两个特征之间:

    • 有效负载(只写,无响应),
    • 身份(须予公布)。

    整个固件映像通过有效负载特性以块的形式发送。

    Payload是一个20字节的特性:4字节的块偏移量加上16字节的数据块。

    状态通知告知是否存在错误条件,以及下一个预期的有效负载块偏移量。这样,上传者可以判断它是否可以继续推测,从其自己的偏移量发送其块,或者是否应该从状态通知中找到的偏移量恢复。

    发送状态更新有两个主要原因:

    • 当一切顺利(有效载荷按顺序飞入)时,以给定的速率(如4Hz,而不是每个数据包),
    • 出现错误(无序,经过一段时间没有收到有效负载等),具有相同的给定速率(不是每个错误的数据包)。

    Receiver期望所有块按顺序排列,它不会重新排序。如果一个块出现故障,它就会被丢弃,并且会推送一个错误状态通知。

    当状态进入时,它会隐式确认所有具有较小偏移量的块。

    最后,发送方有一个发送窗口,许多成功的确认飞行允许发送方放大其窗口(在匹配确认之前发送更多块)。如果发生错误,窗口会减少,丢弃的块可能是因为队列在某处溢出。

    讨论

    使用“单向”PDU(没有响应和通知写入)是为了避免6.以上,因为ATT协议明确告知已确认的PDU(写入,指示)不能被流水线化(即,在收到之前,您可能不会发送下一个PDU响应)。

    状态,包含最后收到的块,缓和5。

    要遵守2.和3.有效载荷是20字节的特征写入。 4 + 16有许多优点,一个是偏移验证,一个16字节的块只涉及移位,另一个是块总是在目标闪存中页面对齐(最好是7。)。

    要处理4.在接收状态更新之前发送了多个块,推测它将被正确接收。

    此协议具有以下功能:

    • 它适应无线电条件,
    • 它适应发送方的队列,
    • 目标没有任何状态泛滥,
    • 队列保持填充,这允许整个中央堆栈使用每个可能的TX机会。

    某些参数不属于此协议:

    • central应强制执行短连接间隔(尝试在更新程序应用程序中强制执行);
    • slave PHY应该具有良好的从属延迟(YMMV,测试供应商的堆栈);
    • 你应该压缩你的有效载荷以减少转移时间。

    编号

    使用:

    • 15%压缩,
    • 与connectionInterval = 10ms连接的设备,
    • 主PHY将每个连接事件限制为4-5个TX数据包,
    • 平均无线电条件。

    我平均每个连接事件得到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数据包需要一次往返。然而,该方案应该为您提供高吞吐量。