0

我尝试在给定(自定义)特征上写入 >20 字节的数据。在以下日志中,我尝试写入 85 个字节:

代码:

connectionObservable
                .flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(
                        wChar.uuid(),
                        wChar.bytes()))
                .observeOn(mainThread())
                .subscribe(
                        bytes -> wChar.success(),
                        this::onWriteFailure
                );

结果:在服务器端(nrf52)我可以看到 EXEC_WRITE 但只发送了前 20B。

这是日志猫:

D/RxBle#ClientOperationQueue: 排队 ConnectOperation(17461182) D/RxBle#ClientOperationQueue: 已启动 ConnectOperation(17461182) D/RxBle#ClientOperationQueue: 排队 ConnectOperation(218660306) D/RxBle#ClientOperationQueue: 已启动 ConnectOperation(218660306) D/RxBle#BluetoothG onConnectionStateChange newState=2 status=0 D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0 D/RxBle#ClientOperationQueue: FINISHED ConnectOperation(218660306) D/RxBle#ClientOperationQueue: FINISHED ConnectOperation(17461182) D/RxBle#ConnectionOperationQueue: QUEUED ServiceDiscoveryOperation (125599796) D/RxBle#ConnectionOperationQueue: 已启动 ServiceDiscoveryOperation(125599796) D/RxBle#BluetoothGatt: onServicesDiscovered status=0 D/RxBle#ConnectionOperationQueue:QUEUED CharacteristicReadOperation(2626026) D/RxBle#ConnectionOperationQueue: FINISHED ServiceDiscoveryOperation(125599796) D/RxBle#ConnectionOperationQueue: STARTED CharacteristicReadOperation(2626026) D/RxBle#BluetoothGatt: onCharacteristicRead 特性=0000fa03-0278-03be-4447-0dfe 状态RxBle#ConnectionOperationQueue: 已完成 CharacteristicReadOperation(2626026) D/RxBle#ClientOperationQueue: QUEUED ConnectOperation(158692575) D/RxBle#ClientOperationQueue: 已启动 ConnectOperation(158692575) D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0 D/RxBle#ClientOperationQueue:已完成 ConnectOperation(158692575) D/RxBle#ConnectionOperationQueue: 排队 ServiceDiscoveryOperation(20778996) D/RxBle#ConnectionOperationQueue: 已启动 ServiceDiscoveryOperation(20778996) >D/RxBle#BluetoothGatt:onServicesDiscovered 状态=0
D/RxBle#ConnectionOperationQueue: QUEUED CharacteristicWriteOperation(51009974) D/RxBle#ConnectionOperationQueue: FINISHED ServiceDiscoveryOperation(20778996) D/RxBle#ConnectionOperationQueue: STARTED CharacteristicWriteOperation(51009974)
> D/RxBle#BluetoothGatt: onCharacteristicWrite 特性=00020fa444-7883 -091eba91df8e 状态=0

D/RxBle#ConnectionOperationQueue:完成 CharacteristicWriteOperation(51009974)

我还尝试使用长 rxAndroidBlewrite 过程:

connectionObservable
                .flatMap(rxBleConnection -> {
                            rxBleConnection.setupNotification(wChar.uuid()); 
                            return rxBleConnection.createNewLongWriteBuilder()
                                    .setCharacteristicUuid(wChar.uuid()) 
                                    .setBytes(array)
                                    .build();
                        }
                )
                .subscribe(
                        bytes -> wChar.success(),
                        this::onWriteFailure
                );

它发送了几个连续的写命令,但它不是长写过程(带有 n ATT_prepare 和 1 ATT_exec),它是独立的写:

D/RxBle#ConnectionOperationQueue: QUEUED CharacteristicLongWriteOperation(74131396) D/RxBle#ConnectionOperationQueue: FINISHED ServiceDiscoveryOperation(250008320) D/RxBle#ConnectionOperationQueue: STARTED CharacteristicLongWriteOperation(74131396)

D/RxBle#BluetoothGatt:onCharacteristicWrite 特性=0000fa04-0278-03be-4447-091eba91df8e 状态=0

D/RxBle#BluetoothGatt:onCharacteristicWrite 特性=0000fa04-0278-03be-4447-091eba91df8e 状态=0

D/RxBle#BluetoothGatt:onCharacteristicWrite 特性=0000fa04-0278-03be-4447-091eba91df8e 状态=0

D/RxBle#BluetoothGatt:onCharacteristicWrite 特性=0000fa04-0278-03be-4447-091eba91df8e 状态=0

D/RxBle#BluetoothGatt:onCharacteristicWrite 特性=0000fa04-0278-03be-4447-091eba91df8e 状态=0

D/RxBle#ConnectionOperationQueue:已完成 CharacteristicLongWriteOperation(74131396)

当然我可以设法在服务器上重建或修改 MTU,但我想使用 BLE 排队写入,这通常由我的中央 (rxandroidble) 和我的外围设备 (nrf52) 支持

4

3 回答 3

1

如果您指的是排队写入,那么在 Android API 上它似乎被称为可靠写入。此 API当前未在 中实现RxAndroidBle,您需要通过使用native 的快捷方式实现RxBleCustomOperationAPI来实现。即便如此,在这件事上,原生 Android API 似乎也没有完全发挥作用BluetoothGattCallback

RxAndroidBle写入不是使用准备好的写入,而是使用多个标准写入。这实际上可以在 Javadoc 中更好地描述......

关于长写到底是什么,人们的看法不一。@Emil在这个问题上的出色回答很好地阐明了这一点

我使用 nRF51822 和 SDK 8.1.0 中的 Softdevice S110 执行了一些测试。

看起来,在引擎盖下,长写只是一个准备好的写——Android 为用户管理它。

在外围设备方面,实现起来似乎更棘手,因为 Softdevice 通知应用程序准备写入已完成并且数据已准备好解析(它未附加到写入 BLE 事件本身)。数据的解析属于应用程序逻辑,因为长写入和准备/可靠写入之间似乎没有区别,这可能会考虑一次写入多个特征,并且可能存在一些与业务逻辑相关的一致性问题(是否应该接受一组特定的写入)。

结论: Android vanilla API(和RxAndroidBle)确实支持所谓的开箱即用长写入,方法是在后台进行多次准备/排队写入。正确处理它取决于外围设备的固件

于 2018-01-11T23:15:19.847 回答
1

对于客户端(Android),只需使用标准的 Write 过程,你就可以了。在内部,它会将其拆分为多个准备写入请求,然后是执行写入请求。也就是说,使用您的第一种方法。

为了帮助解决混淆,有两层:GATT 和 ATT。ATT 层定义了一个称为“队列写入”的概念,它由准备写入请求/响应和执行写入请求/响应组成。这个想法是所有准备好的写入(每个都限制在 MTU-5 大小,具有偏移参数和 ATT 句柄)都放在外围设备的队列中。在执行写入发生之前,外设不会提交写入。(执行写入有一个标志,也可以用来取消整个队列。)

在 GATT 层,我们有一个叫做“Write Long Characteristic Values”的东西。当应写入的特性值超过单个写入请求中可以放入的值时,将使用此过程。这个过程被定义为使用 ATT 中的“Queued Writes”功能,因此它将值分成多个段,将它们全部发送到 Prepared Write 数据包中,最后发送 Execute Write 数据包。应该不惜一切代价避免“写入长特征值”,因为它的开销很大(每个数据包往返一次,响应数据包很长,因为它们包含值的副本,并且需要一个最终的执行写入请求)。相反,将 MTU 增加到最大值会更好,因为如果幸运的话,它可以在单个连接事件中发送所有内容。

Reliable Writes 也是 GATT 层的一个特性,它使用了 ATT 中的“Queued Writes”特性。这个想法是,用户应该能够在一个操作中对潜在的多个特征执行多个原子写入。可靠这个词来自客户端应该验证值是否正确发送。每个 Prepared Write Response 都包含从 Prepared Write Request 接收到的值,客户端应该比较这些值并查看它们是否相等,如果不相等,则中止。现在在 Android 中,当前的 API + 实现不可能执行此检查,所以虽然操作仍然有效,但它确实不像它应该的那样“可靠”(但无论如何你对所有 BLE 数据包都有 CRC,所以我不要认为会有问题)。

在外围设备方面,不可能真正知道传入的准备写入请求是“写入长特征值”操作还是“可靠写入”操作的一部分。但无论如何,大多数 BLE 堆栈不会组合检索到的部分,然后在收到执行写入时提供单个写入。在我看来,它们反而暴露了一个相当低级的 API;通常只是或多或少地转发 ATT 数据包。

对于 Nordic Semiconductor 的软设备,他们拥有的最简单的方法是使用“GATTS Queued Writes:堆栈处理,没有属性需要授权” http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s132.api.v5。 0.0/组___b_l_e___g_a_t_t_s___q_u_e_u_e_d___w_r_i_t_e___b_u_f___n_o_a_u_t_h___m_s_c.html。这样,它将所有准备好的写入排队到应用程序提供的缓冲区中,并在执行写入到达时通知应用程序。然后,应用程序应解析堆栈已放入缓冲区的所有(或多或少)原始 ATT Prepared Write Request 数据包。这个结构在这里定义http://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.s132.api.v5.0.0%2Fgroup___b_l_e___g_a_t_t_s___q_u_e_u_e_d___w_r_i_t_e_s___u_s_e_r___m_e_m.html&cp=2_3_1_1_0. 请注意,缓冲区是一个列表(这些结构连接在一起的数组),而不是单个值。该列表以包含 BLE_GATT_HANDLE_INVALID 作为句柄的项目终止。我认为您的错误是您仅解析此列表中的第一项。

于 2018-01-24T22:47:44.087 回答
1

包括引入 BLE 的蓝牙 4.0 规范指出,在给定特性上一次最多可以传输 20 个字节。如果您需要发送更多数据,则必须在某种类型的循环中一次发送 20 个字节。

所以事实上,这不是 RxAndroidBle 的问题,只是技术的限制。

见这里:https ://stackoverflow.com/a/38914831/4321774

于 2018-01-11T22:48:51.277 回答