rxandroidble write 只发送前 20B
rxandroidble write only sends the first 20B
我尝试在给定(自定义)特征上写入 >20 字节的数据。在下面的日志中,我尝试写了85个字节:
代码:
connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(
wChar.uuid(),
wChar.bytes()))
.observeOn(mainThread())
.subscribe(
bytes -> wChar.success(),
this::onWriteFailure
);
结果:
在服务器端 (nrf52) 我可以看到 EXEC_WRITE 但只发送了前 20B。
这是 logcat:
D/RxBle#ClientOperationQueue: QUEUED ConnectOperation(17461182)
D/RxBle#ClientOperationQueue: STARTED ConnectOperation(17461182)
D/RxBle#ClientOperationQueue: QUEUED ConnectOperation(218660306)
D/RxBle#ClientOperationQueue: STARTED ConnectOperation(218660306)
D/RxBle#BluetoothGatt: 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: STARTED 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 characteristic=0000fa03-0278-03be-4447-091eba91df8e status=0
D/RxBle#ConnectionOperationQueue: FINISHED CharacteristicReadOperation(2626026)
D/RxBle#ClientOperationQueue: QUEUED ConnectOperation(158692575)
D/RxBle#ClientOperationQueue: STARTED ConnectOperation(158692575)
D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0
D/RxBle#ClientOperationQueue: FINISHED ConnectOperation(158692575)
D/RxBle#ConnectionOperationQueue: QUEUED ServiceDiscoveryOperation(20778996)
D/RxBle#ConnectionOperationQueue: STARTED ServiceDiscoveryOperation(20778996) > D/RxBle#BluetoothGatt:onServicesDiscovered status=0
D/RxBle#ConnectionOperationQueue: QUEUED CharacteristicWriteOperation(51009974)
D/RxBle#ConnectionOperationQueue: FINISHED ServiceDiscoveryOperation(20778996)
D/RxBle#ConnectionOperationQueue: STARTED CharacteristicWriteOperation(51009974)
> D/RxBle#BluetoothGatt: onCharacteristicWrite characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#ConnectionOperationQueue: FINISHED 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
characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#BluetoothGatt: onCharacteristicWrite
characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#BluetoothGatt: onCharacteristicWrite
characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#BluetoothGatt: onCharacteristicWrite
characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#BluetoothGatt: onCharacteristicWrite
characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#ConnectionOperationQueue: FINISHED
CharacteristicLongWriteOperation(74131396)
当然我可以设法在服务器上重建或修改 MTU,但我想使用 BLE 排队写入,这通常由我的中央设备 (rxandroidble) 和外围设备 (nrf52) 支持
包括 BLE 引入的蓝牙 4.0 规范指出,一次最多可以根据给定特征传输 20 个字节。如果您需要发送更多数据,则必须在某种类型的循环中一次发送 20 个字节。
所以其实这不是RxAndroidBle的问题,只是技术的限制。
看这里:
如果参考queued writes then on the Android API it seems to be referenced as reliable write. This API is not currently implemented in the RxAndroidBle
and you would need to do that by implementing RxBleCustomOperation
API using a shortcut to native BluetoothGattCallback
. Even then it appears that the native Android API is not fully functional in this matter.
The RxAndroidBle
long write 没有使用准备好的写入,而是使用多个标准写入。这实际上可以在 Javadoc 中得到更好的描述...
有mixed opinions on what a Long Write really is. @Emil's .
我使用带有来自 SDK 8.1.0 的 Softdevice S110 的 nRF51822 进行了一些测试。
似乎在幕后,长写只是准备好的写- Android 为用户管理它。
在外围设备方面,实现起来似乎更棘手,因为软设备通知应用程序准备写入已完成并且数据已准备好进行解析(它未附加到写入 BLE 事件本身)。数据的解析属于应用程序逻辑,因为长写入和 Prepared/Reliable 写入之间似乎没有区别,后者可能考虑一次写入多个特征,并且可能存在一些业务逻辑相关的一致性问题(是否应接受一组特定的写入)。
结论: Android vanilla API(和 RxAndroidBle
)确实通过制作多个 Prepared/Queued 写在引擎盖下。由外围设备的固件来正确处理它
对于客户端 (Android),只需使用标准的 Write 程序就可以了。在内部,它会将其拆分为多个准备写入请求,然后是执行写入请求。也就是说,使用您的第一种方法。
为了帮助解决混淆,有两层:GATT 和 ATT。 ATT层定义了一个叫做"Queued Writes"的概念,它由Prepare WriteRequest/Response和Execute WriteRequest/Response组成。这个想法是所有准备好的写入(每一个都限制在 MTU-5 的大小,有一个偏移参数和一个 ATT 句柄)被放入外围端的队列中。在执行写入发生之前,写入不会在外设上提交。 (Execute Write 有一个标志,也可以用来取消整个队列。)
在 GATT 层上,我们有一个叫做 "Write Long Characteristic Values" 的东西。当应写入的特征值超过单个写入请求中可以放入的值时,将使用此过程。该程序定义为使用ATT中的"Queued Writes"特性,因此它将值分成多个段,将它们全部发送到Prepared Write数据包中,最后发送Execute Write数据包。 "Write Long Characteristic Values" 应该不惜一切代价避免,因为它的开销很大(每个数据包往返一次,响应数据包很长,因为它们包含值的副本,并且需要一个最终的执行写入请求)。相反,将 MTU 增加到最大值要好得多,因为如果幸运的话,它可以在单个连接事件中发送所有内容。
Reliable Writes 也是一个 GATT 层特性,它使用了 ATT 中的 "Queued Writes" 特性。这个想法是用户应该能够在一次操作中对可能不止一个特征执行多次原子写入。可靠一词来自于客户端应该验证值是否已正确发送。每个 Prepared Write Response 都包含从 Prepared Write Request 接收到的值,客户端应该比较这些值并查看它们是否相等,如果不相等,则中止。现在在 Android 中,当前的 API+ 实现不可能执行此检查,因此虽然操作仍然有效,但它确实不像预期的那样 "reliable"(但是无论如何,您在所有 BLE 数据包上都有 CRC,所以我认为不会有问题)。请注意,如果您遵循 GATT 规则,您只能对特征(而非描述符)执行可靠写入,并且只能对声明此 属性.
的特征执行
在外设端,不可能真正知道传入的准备好的写入请求是 "Write Long Characteristic Values" 操作还是 "Reliable Write" 操作的一部分。但无论如何,大多数 BLE 堆栈 不会 组合检索到的部分,然后在收到执行写入时传送单个写入。在我看来,他们反而暴露了一个相当低的水平 API;通常只是或多或少地转发了 ATT 数据包。
对于Nordic Semiconductor的softdevice,他们最简单的方法就是使用"GATTS Queued Writes: Stack handled, no attributes require authorization" http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s132.api.v5.0.0/group___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. This way it queues up all Prepared Writes in your application-provided buffer and notifies the app when the Execute Write arrives. The app should then parse all (more or less) raw ATT Prepared Write Request packets the stack has put in the buffer. This structure is defined here 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_2_4_5。请注意,缓冲区是一个列表(将这些结构连接在一起的数组)而不是单个值。该列表以包含 BLE_GATT_HANDLE_INVALID 作为句柄的项目终止。我认为你的错误是你只解析了这个列表中的第一项。
我尝试在给定(自定义)特征上写入 >20 字节的数据。在下面的日志中,我尝试写了85个字节:
代码:
connectionObservable
.flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(
wChar.uuid(),
wChar.bytes()))
.observeOn(mainThread())
.subscribe(
bytes -> wChar.success(),
this::onWriteFailure
);
结果: 在服务器端 (nrf52) 我可以看到 EXEC_WRITE 但只发送了前 20B。
这是 logcat:
D/RxBle#ClientOperationQueue: QUEUED ConnectOperation(17461182) D/RxBle#ClientOperationQueue: STARTED ConnectOperation(17461182) D/RxBle#ClientOperationQueue: QUEUED ConnectOperation(218660306) D/RxBle#ClientOperationQueue: STARTED ConnectOperation(218660306) D/RxBle#BluetoothGatt: 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: STARTED 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 characteristic=0000fa03-0278-03be-4447-091eba91df8e status=0 D/RxBle#ConnectionOperationQueue: FINISHED CharacteristicReadOperation(2626026) D/RxBle#ClientOperationQueue: QUEUED ConnectOperation(158692575) D/RxBle#ClientOperationQueue: STARTED ConnectOperation(158692575) D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0 D/RxBle#ClientOperationQueue: FINISHED ConnectOperation(158692575) D/RxBle#ConnectionOperationQueue: QUEUED ServiceDiscoveryOperation(20778996) D/RxBle#ConnectionOperationQueue: STARTED ServiceDiscoveryOperation(20778996) > D/RxBle#BluetoothGatt:onServicesDiscovered status=0
D/RxBle#ConnectionOperationQueue: QUEUED CharacteristicWriteOperation(51009974) D/RxBle#ConnectionOperationQueue: FINISHED ServiceDiscoveryOperation(20778996) D/RxBle#ConnectionOperationQueue: STARTED CharacteristicWriteOperation(51009974)
> D/RxBle#BluetoothGatt: onCharacteristicWrite characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0D/RxBle#ConnectionOperationQueue: FINISHED 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 characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#BluetoothGatt: onCharacteristicWrite characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#BluetoothGatt: onCharacteristicWrite characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#BluetoothGatt: onCharacteristicWrite characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#BluetoothGatt: onCharacteristicWrite characteristic=0000fa04-0278-03be-4447-091eba91df8e status=0
D/RxBle#ConnectionOperationQueue: FINISHED CharacteristicLongWriteOperation(74131396)
当然我可以设法在服务器上重建或修改 MTU,但我想使用 BLE 排队写入,这通常由我的中央设备 (rxandroidble) 和外围设备 (nrf52) 支持
包括 BLE 引入的蓝牙 4.0 规范指出,一次最多可以根据给定特征传输 20 个字节。如果您需要发送更多数据,则必须在某种类型的循环中一次发送 20 个字节。
所以其实这不是RxAndroidBle的问题,只是技术的限制。
看这里:
如果参考queued writes then on the Android API it seems to be referenced as reliable write. This API is not currently implemented in the RxAndroidBle
and you would need to do that by implementing RxBleCustomOperation
API using a shortcut to native BluetoothGattCallback
. Even then it appears that the native Android API is not fully functional in this matter.
The RxAndroidBle
long write 没有使用准备好的写入,而是使用多个标准写入。这实际上可以在 Javadoc 中得到更好的描述...
有mixed opinions on what a Long Write really is. @Emil's
我使用带有来自 SDK 8.1.0 的 Softdevice S110 的 nRF51822 进行了一些测试。
似乎在幕后,长写只是准备好的写- Android 为用户管理它。
在外围设备方面,实现起来似乎更棘手,因为软设备通知应用程序准备写入已完成并且数据已准备好进行解析(它未附加到写入 BLE 事件本身)。数据的解析属于应用程序逻辑,因为长写入和 Prepared/Reliable 写入之间似乎没有区别,后者可能考虑一次写入多个特征,并且可能存在一些业务逻辑相关的一致性问题(是否应接受一组特定的写入)。
结论: Android vanilla API(和 RxAndroidBle
)确实通过制作多个 Prepared/Queued 写在引擎盖下。由外围设备的固件来正确处理它
对于客户端 (Android),只需使用标准的 Write 程序就可以了。在内部,它会将其拆分为多个准备写入请求,然后是执行写入请求。也就是说,使用您的第一种方法。
为了帮助解决混淆,有两层:GATT 和 ATT。 ATT层定义了一个叫做"Queued Writes"的概念,它由Prepare WriteRequest/Response和Execute WriteRequest/Response组成。这个想法是所有准备好的写入(每一个都限制在 MTU-5 的大小,有一个偏移参数和一个 ATT 句柄)被放入外围端的队列中。在执行写入发生之前,写入不会在外设上提交。 (Execute Write 有一个标志,也可以用来取消整个队列。)
在 GATT 层上,我们有一个叫做 "Write Long Characteristic Values" 的东西。当应写入的特征值超过单个写入请求中可以放入的值时,将使用此过程。该程序定义为使用ATT中的"Queued Writes"特性,因此它将值分成多个段,将它们全部发送到Prepared Write数据包中,最后发送Execute Write数据包。 "Write Long Characteristic Values" 应该不惜一切代价避免,因为它的开销很大(每个数据包往返一次,响应数据包很长,因为它们包含值的副本,并且需要一个最终的执行写入请求)。相反,将 MTU 增加到最大值要好得多,因为如果幸运的话,它可以在单个连接事件中发送所有内容。
Reliable Writes 也是一个 GATT 层特性,它使用了 ATT 中的 "Queued Writes" 特性。这个想法是用户应该能够在一次操作中对可能不止一个特征执行多次原子写入。可靠一词来自于客户端应该验证值是否已正确发送。每个 Prepared Write Response 都包含从 Prepared Write Request 接收到的值,客户端应该比较这些值并查看它们是否相等,如果不相等,则中止。现在在 Android 中,当前的 API+ 实现不可能执行此检查,因此虽然操作仍然有效,但它确实不像预期的那样 "reliable"(但是无论如何,您在所有 BLE 数据包上都有 CRC,所以我认为不会有问题)。请注意,如果您遵循 GATT 规则,您只能对特征(而非描述符)执行可靠写入,并且只能对声明此 属性.
的特征执行在外设端,不可能真正知道传入的准备好的写入请求是 "Write Long Characteristic Values" 操作还是 "Reliable Write" 操作的一部分。但无论如何,大多数 BLE 堆栈 不会 组合检索到的部分,然后在收到执行写入时传送单个写入。在我看来,他们反而暴露了一个相当低的水平 API;通常只是或多或少地转发了 ATT 数据包。
对于Nordic Semiconductor的softdevice,他们最简单的方法就是使用"GATTS Queued Writes: Stack handled, no attributes require authorization" http://infocenter.nordicsemi.com/topic/com.nordic.infocenter.s132.api.v5.0.0/group___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. This way it queues up all Prepared Writes in your application-provided buffer and notifies the app when the Execute Write arrives. The app should then parse all (more or less) raw ATT Prepared Write Request packets the stack has put in the buffer. This structure is defined here 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_2_4_5。请注意,缓冲区是一个列表(将这些结构连接在一起的数组)而不是单个值。该列表以包含 BLE_GATT_HANDLE_INVALID 作为句柄的项目终止。我认为你的错误是你只解析了这个列表中的第一项。