本文介绍了调用onCharacteristicWrite和onNotificationSent的速度太快-如何获取实际的传出数据速率?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经使用外围设备上的两个ATT特性实现了双工BLE通信(实质上是串行TX/RX电缆之类的东西)-一个是可写的,另一个可通过通知读取.除报告的写入速度外,其他所有操作均按预期进行.

I have implemented duplex BLE communication (essentially, something like serial TX/RX cable) using two ATT characteristics on the peripheral - one is writable, the other is readable with notifications. Everything works as expected, except reported write speed.

当我调用以下代码时:

txCharacteristic.setValue(data);
boolean queuedOk = 
   connectedPeripheralGatt.writeCharacteristic(txCharacteristic);

onCharacteristicWrite回调几乎立即被触发,然后我重复上面的代码片段以在特征上应用下一个数据片段.从onCharacteristicWriteRequest中收集的时间戳开始,接收方报告正确的速度(大约10 KB/s)时,发送方报告的速度令人难以置信(例如50-300 KB/s),并且报告已完成推动所有操作.将数据发送到writeCharacteristic,尽管接收方仍在接收传入的数据.

the onCharacteristicWrite callback is being triggered almost immediately, and then I repeat the code fragment above to apply next data fragment on the characteristic. While the receiving side is reporting correct speed (about 10 KB/s), as measured from timestamps collected in onCharacteristicWriteRequest, the sending side reports unbelievable speeds (like 50 - 300 KB/s) and it reports that it has finished pushing all the data to writeCharacteristic, although the receiving side still is receiving the incoming data.

因此,很显然,在大多数数据仍在传送至目标设备的途中,正在调用onCharacteristicWriteRequest.

So, clearly onCharacteristicWriteRequest is being called while most of the data is still on its way to the target device.

当我从外围设备向中央设备发送数据时,会发生同样的事情,仅这次外围设备上的onNotificationSent处于其速度附近而中央设备上的onCharacteristicChanged正在报告正确的速度.

The same thing happens when I send the data from the peripheral to the central device, only this time onNotificationSent on the peripheral is lying about its speed and onCharacteristicChanged on the central is reporting correct speed.

是否存在或多或少可靠的方法来近似测量发件人端的传出数据速率而无需打开ACK(出于性能原因,我不想这样做)?

数据本身完整无缺,我正在使用0.5-5兆字节大小的图像文件进行测试,并且图像总是在接收端正确解码.

The data itself arrives intact, I'm testing it with image files of 0.5 - 5 megabytes of size and the images are always decoded correctly on the receiving side.

为使吞吐量最大化,可写特性设置为不可靠,如下所示:

To maximize the throughput, the writable characteristic was set up to be non-reliable as follows:

    BluetoothGattCharacteristic rxCharacteristic = new BluetoothGattCharacteristic(UUID.fromString(RX_CHARACTERISTIC_UUID),
            BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
            BluetoothGattCharacteristic.PERMISSION_WRITE);
    rxCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);

我不确定我使用的是PROPERTY_WRITE_NO_RESPONSEWRITE_TYPE_NO_RESPONSE,但是从某些网站上的某些文章中,模糊地看来,同时指定这两种方法是一种很好的做法.如果我错了,请纠正我.

I'm not sure of my use of PROPERTY_WRITE_NO_RESPONSE and WRITE_TYPE_NO_RESPONSE but from some articles on some websites it vaguely seemed that it's a good practice to specify both; correct me if I'm wrong.

我知道,通过这种设置,我不能期望接收方对数据进行真正的确认,但是我仍然希望onCharacteristicWriteonNotificationSent本身至少可以提供现实的时间来检测何时实际发送数据源设备的名称.取而代之的是,似乎有一个相当大的缓存,该缓存消耗了我的数据并报告几乎立即发送了该数据.

I understand that with such setup I cannot expect real acknowledgements of the data on the receiving side, but still I expected that onCharacteristicWrite and onNotificationSent themselves would provide at least realistic timing to detect when the data was actually sent out of the source device. Instead, it seems, there is a pretty large cache which consumes my data and reports it being sent almost immediately.

推荐答案

您是正确的.

如果您使用带有响应的写入",则在远程发送回写入响应之后,将调用onCharacteristicWrite回调.但是,如果您使用无响应写入",则Android具有流控制机制来缓冲您写入的数据.它是这样的:

If you use "write with response" then the onCharacteristicWrite callback will be called after the remote sends back a write response. If you use "write without response" however, Android has a flow control mechanism which buffers the data you write. It works like this:

该应用会写入一个数据包.蓝牙堆栈将此数据包放入其内部缓冲区.如果使此数据包排队后缓冲区中还有更多空间,则将调用onCharacteristicWrite回调,因此您可以立即写入另一个数据包.当缓冲区已满时,它将等待调用onCharacteristicWrite回调,直到缓冲区中现在有50%的空间(如果我没记错的话)可用.

The app writes a packet. The Bluetooth stack puts this packet in its internal buffer. If there is still more space in the buffer after enqueuing this packet, the onCharacteristicWrite callback is called, so you can immediately write another packet. When the buffer is full, it waits to call the onCharacteristicWrite callback until there is now 50% space (if I remember correctly) available in the buffer.

只要内部缓冲区中有数据包,它就会尝试将其写入Bluetooth控制器,该控制器也有数量有限的可以缓存的数据包.蓝牙控制器通过HCI将已完成数据包数量"事件发送回Android的蓝牙堆栈,该事件指示远程设备的链路层已确认数据包. (这并不表示远程应用程序尚未收到它,而只是表明远程蓝牙控制器已收到它.)如果Bluetooth控制器中没有可用空间,它将等待直到有空间.

As long as there are packets in the internal buffer, it tries to write them to the Bluetooth controller, which also has a limited number of packets that can be buffered. The Bluetooth controller sends a "number of packets complete" event back to Android's Bluetooth stack over HCI which indicates that the Link Layer of the remote's device has acknowledged a packet. (This doesn't indicate that the remote app hasn't received it; only that the remote Bluetooth controller received it.) If there is no space available in the Bluetooth controller, it will wait until there is space.

这比在iOS上的工作方式聪明得多.如果您发送大量带有响应的写入"数据包,则在内部缓冲区已满的情况下,甚至在发送之前它们都将被丢弃. (这可以通过在第10个数据包左右发送带有响应的写入"来解决).

This is much smarter than how it works on iOS. There if you send a lot of "write with response" packets, they will be discarded before they are even sent if the internal buffer is full. (This can be solved by sending "write with response" each 10th packet or so).

不幸的是(对于您的情况),当远程链接层已确认数据包时,Android的蓝牙堆栈不会将回调发送给应用程序,因此您必须基于onCharacteristicWrite回调来提高速度.但是,您可以每隔大约10个数据包向另一个方向发送状态通知,我认为这会给您带来良好的效果.如果Android的蓝牙堆栈在收到链路层确认时转而发送onCharacteristicWrite回调,则会将速度降低到每个连接事件仅一个数据包.

Unfortunately (for your case), Android's Bluetooth stack does not send a callback to the app when a packet has been acknowledged by the remote Link Layer, so you will have to base your speed on the onCharacteristicWrite callback. You could however send status notifications in the other direction each 10th packet or so, which I think will give you good results. If Android's Bluetooth stack would instead send the onCharacteristicWrite callback when the Link Layer acknowledgement is received, that would reduce the speed to only one packet per connection event.

如果有时由于监督超时而导致链接断开连接,那么您应该知道我前一段时间发布的错误报告: https://issuetracker.google.com/issues/37121017 .

If the link sometimes get disconnected due to supervision timeout, you should be aware of the bug report I posted some time ago: https://issuetracker.google.com/issues/37121017.

关于您的有关物业/许可的问题,本来可以是一样的,但是蓝牙团队决定将这些问题分开.权限类型与ATT协议有关,告诉蓝牙堆栈允许客户端执行什么操作.这些属性只是另一端可以读取以了解其特征类型的某些属性.

About your questions with properties/permissions, that could have been the same thing but the Bluetooth team decided to separate those. The permissions kind of relate to the ATT protocol, telling the Bluetooth stack what a client is allowed to do. The properties are just some properties that the other side can read in order to know what type of characteristic it is.

这篇关于调用onCharacteristicWrite和onNotificationSent的速度太快-如何获取实际的传出数据速率?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-30 20:21