【发布时间】:2018-03-27 09:03:43
【问题描述】:
我正在更新一个应用程序以使用 RxAndroidBLE,并且正在努力将我现有的回调模式转换为 Rx 模式。特别是,我需要根据接收到的数据以不同的方式响应特征通知,并将特定的写入命令发送回设备(这将导致特征被循环更新)。
这背后的基本原理是,我正在集成的 BLE 设备具有特殊的自定义特性,我们可以向其发送不同的命令,然后侦听返回的数据。
我已经阅读了很多关于使用 RxBLE 链接命令的内容,但似乎没有一个能够解决我的特定查询,即如何在观察更改通知时将命令发送回设备(因为连接本身似乎已断开当我们到达可观察块时的范围)。这样做的“Rx 方式”是什么?
为了清楚起见,这是我的 BLE 服务的整个流程:
- 使用我们的自定义特征过滤器扫描设备
- 连接到找到的设备
- 读取几个标准特征(字符串),并将它们存储在我们的数据模型中
- 当且仅当其中一个特征与字符串数组中的一个匹配时,继续执行 5。否则,处理连接。
- 订阅我们的自定义“控制”特征 (“CC”) 以获取更改通知
- 向 CC 发送命令 1。这应该会触发答案 1 在 CC 中设置,因此我们的处理程序被调用
- 对答案 1 执行一些计算并保存到我们的模型中。将命令 2(包括这些修改后的值,因此我们无法在编译时确定)发送到 CC。这应该会触发 CC 中的答案 2。
- 收到答案 2 后,发送命令 3,这将触发答案 3。
- 在收到答案 3 时,解析为一个 int 值。如果答案 3 == 0,则处理连接 - 我们完成了。
- 答案 3 > 0,所以发送命令 4。这将触发答案 4。
- 对答案 4 执行一些计算并将结果存储在我们的模型中
- 然后发送命令 5,这实际上会触发答案 3(命令 5 和 3 都会触发答案 3)。由于我们已经订阅了答案 3,这将带我们回到上面的第 9 步。我们一直循环直到答案 3 为 0(即,我们已经保存了所有数据)。
编辑:我讨厌共享代码,因为我很清楚以下方法不可能起作用 - 但我希望它描述了我正在尝试做的事情,即使语法甚至不会编译:
connectedDevice.connectionDisposable = connectedDevice.getRxDevice().establishConnection(false)
.observeOn(AndroidSchedulers.mainThread())
.flatMapSingle(rxBleConnection -> rxBleConnection.readCharacteristic(BATTERY_CHARACTERISTIC_UUID))
.doOnNext(bytes -> {
//store the battery info in our model here
})
.flatMapSingle(rxBleConnection -> rxBleConnection.readCharacteristic(SERIAL_NUMBER_CHARACTERISTIC_UUID))
.doOnNext(bytes -> {
//store the serial number info in our model here
//TODO: how do we only proceed to the subscription if serialNumber is correct?
}
)
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(CUSTOM_CHARACTERISTIC_UUID))
.doOnNext(notificationObservable -> {
// Notification has been set up
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_1); //we can't do this because rxBleConnection is out of scope!
})
.flatMap(notificationObservable -> notificationObservable) // <-- Notification has been set up, now observe value changes.
.subscribe(
bytes -> {
// Given characteristic has been changes, here is the value.
switch(commandFromBytes(bytes)){
case answer1:
int newCommand = doSomethingWith(bytes);
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_2 + newCommand);
break;
case answer2:
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_3);
break;
case answer3:
if(bytes <= 0){
connectedDevice.connectionDisposable.dispose();
}
else{
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_4);
}
break;
case answer4:
doSomethingLongWindedWith(bytes);
//then
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_5);
//command 5 will cause answer3 to be notified, so we loop back above
break;
}
},
throwable -> {
// Handle an error here.
}
);
编辑2:在玩了一段时间括号探戈之后,我想我已经接近解决方案了:
connectedDevice.connectionDisposable = connectedDevice.getRxDevice().establishConnection(false)
.observeOn(AndroidSchedulers.mainThread())
.flatMapSingle(rxBleConnection -> rxBleConnection.readCharacteristic(BATTERY_CHARACTERISTIC_UUID)
.doOnNext(bytes -> {
connectedDevice.setBatLevel(bytes);
})
.flatMapSingle(rxBleConnection2 -> rxBleConnection.readCharacteristic(SERIAL_NUMBER_CHARACTERISTIC_UUID))
.doOnNext(bytes -> {
connectedDevice.setSerialNum(bytes);
//we also notify a singleton listener here
}
)
.flatMap(rxBleConnection3 -> {
if (serialNumberIsCorrect(connectedDevice.getSerialNum())) {
rxBleConnection.setupNotification(CUSTOM_CHARACTERISTIC_UUID).subscribe(
bytes -> {
// Given characteristic has been changes, here is the value.
switch (commandFromBytes(bytes)) {
case answer1:
int newCommand = doSomethingWith(bytes);
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_2 + newCommand);
break;
case answer2:
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_3);
break;
case answer3:
if (bytes <= 0) {
//we also notify a singleton listener here
connectedDevice.connectionDisposable.dispose();
} else {
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_4);
}
break;
case answer4:
doSomethingLongWindedWith(bytes);
//then
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_5);
//command 5 will cause answer3 to be notified, so we loop back above
break;
}
},
throwable -> {
// Handle an error here.
}
);
} else {
connectedDevice.connectionDisposable.dispose();
}
}
.doOnNext(notificationObservable -> {
// Notification has been set up
if (serialNumberIsCorrect(connectedDevice.getSerialNum())) {
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_1);
}
})
));
【问题讨论】:
-
你有没有尝试过?随意展示您的代码
-
@DariuszSeweryn 我已经尝试了很多东西,但我无法真正编译:) 一次尝试就更新了我的问题
-
rxBleConnection.writeCharacteristic(CUSTOM_CHARACTERISTIC_UUID, COMMAND_1);你确实需要订阅这个操作。 -
@Christopher 获准,但我如何从该订阅块内将命令写回连接?
-
RxBleConnection默认情况下可以在其范围内访问。因此,在RxBleDevice.establishConnection()之后的第一个.flatMap()中,您可以构造取决于连接的流部分。在第 7 步中你写了perform some calculations on answer 1 and save to our model. send command 2 (which includes these modified values, so we can't determine this at compile time)——这是否意味着COMMAND_2不是一个静态的最终常量?哪些命令不是静态的?
标签: android bluetooth-lowenergy rx-android rxandroidble