可以通过 BLE 执行的大多数操作都是异步的,需要一些时间才能完成。设置通知也不例外 - 这是一个两步过程:
- 设置本地通知
- 编写一个希望从中获取通知的特征的客户端特征配置描述符
如果您的外围设备首先设置为在通知准备好由中央接收之前发送通知,则某些数据可能会在通知设置过程中丢失。
是否有某种方法可以执行 setupNotification(),然后编写特征来告诉设备开始发送通知?
当然(这通常是处理类似场景的方式)——有多种可能的实现方式。其中一个可能如下所示:
device.establishConnection(false) // establish the connection
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(log_uuid) // once the connection is available setup the notification
.flatMap(logDataObservable -> Observable.merge( // when the notification is setup start doing three things at once
rxBleConnection.writeCharacteristic(pword_uuid, pword).ignoreElements(), // writing the `pword` but ignore the result so the output of this .merge() will contain only log data
rxBleConnection.writeCharacteristic(mode_uuid, mode).ignoreElements(), // same as the line above but for `mode`
logDataObservable // observing the log data notifications
))
)
.subscribe(
bytes -> System.out.println(">>> data from device " + bytesToHex(bytes)),
throwable -> {
System.out.println("error");
System.out.println(throwable);
}
);
编辑:
正如下面评论中提到的那样——在设置模式和写入密码之前,外设不允许任何 BLE 交互。正如我在上面所写的那样,设置通知是一个两步过程,一个是本地步骤,一个是远程(在外围设备上执行),一个在上述代码片段中的模式/密码之前执行。可以通过使用模式和稍后手动NotificationSetupMode.COMPAT
编写来分离这两个步骤:Client Characteristic Configuration Descriptor
UUID clientCharacteristicConfigDescriptorUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
device.establishConnection(false) // establish the connection
.flatMap(
RxBleConnection::discoverServices, // once the connection is available discover the services of the peripheral
(rxBleConnection, rxBleDeviceServices) -> // and when we have the connection and services
rxBleDeviceServices.getCharacteristic(log_uuid) // we get the log characteristic (on which we will setup the notification and write the descriptor)
.flatMap(logDataCharacteristic -> // once the log characteristic is retrieved
rxBleConnection.setupNotification(logDataCharacteristic, NotificationSetupMode.COMPAT) // we setup the notification on it in the COMPAT mode (without writing the CCC descriptor)
.flatMap(logDataObservable -> Observable.merge( // when the notification is setup start doing four things at once
rxBleConnection.writeCharacteristic(pword_uuid, pword).ignoreElements(), // writing the `pword` but ignore the result so the output of this .merge() will contain only log data
rxBleConnection.writeCharacteristic(mode_uuid, mode).ignoreElements(), // same as the line above but for `mode`
rxBleConnection.writeDescriptor(logDataCharacteristic.getDescriptor(clientCharacteristicConfigDescriptorUuid), BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE).ignoreElements(), // and we write the CCC descriptor manually
logDataObservable // observing the log data notifications
))
)
)
.flatMap(observable -> observable) // flatMap to get the raw byte[]
.subscribe(
bytes -> System.out.println(">>> data from device " + bytesToHex(bytes)),
throwable -> {
System.out.println("error");
System.out.println(throwable);
}
);
rxBleConnection.discoverServices()
UUID
如果我们知道日志特征服务和使用rxBleConnection.writeDescriptor(UUID serviceUuid, UUID characteristicUuid, UUID descriptorUuid
函数,调用可以省略。
UUID clientCharacteristicConfigDescriptorUuid = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
device.establishConnection(false) // establish the connection
.flatMap(rxBleConnection -> rxBleConnection.setupNotification(log_uuid, NotificationSetupMode.COMPAT) // once the connection is available setup the notification w/o setting Client Characteristic Config Descriptor
.flatMap(logDataObservable -> Observable.merge( // when the notification is setup start doing three things at once
rxBleConnection.writeCharacteristic(pword_uuid, pword).ignoreElements(), // writing the `pword` but ignore the result so the output of this .merge() will contain only log data
rxBleConnection.writeCharacteristic(mode_uuid, mode).ignoreElements(), // same as the line above but for `mode`
rxBleConnection.writeDescriptor(log_service_uuid, log_uuid, clientCharacteristicConfigDescriptorUuid).ignoreElements(), // same as the above line but for writing the CCC descriptor
logDataObservable // observing the log data notifications
))
)
.subscribe(
bytes -> System.out.println(">>> data from device " + bytesToHex(bytes)),
throwable -> {
System.out.println("error");
System.out.println(throwable);
}
);
编辑2:
由于版本1.8.0
有一个新的NotificationSetupMode.QUICK_SETUP
,它首先打开内部通知,然后写入 CCC 描述符值。
rxBleConnection.setupNotification(log_uuid, NotificationSetupMode.QUICK_SETUP)
优点:
Observable<byte[]>
在写入描述符之前发出,允许从头开始观察通知(如果开头正在写入描述符)
缺点: