5

我正在尝试使用在 Android 手机上运行的 rxBleAndroid 以及使用 Android Things 的 Raspberry Pi 与 BLE 数据记录器/传感器进行通信。

但是,我目前遇到一个问题,我的应用程序从未收到最多大约 5 个第一条通知。

我已验证 BLE 设备实际上已成功发送所有预期的通知。我已经通过 nRF Connect 应用程序做到了这一点,并且一切都按预期工作。

当我通过 nRF Connect 应用程序执行此操作时,我采取的步骤如下:

  1. 写入密码特征以解锁设备
  2. 写入模式特征以将设备置于正确模式
  3. 订阅通知(通知立即开始工作)

通过 RxAndroidBle 执行此操作时,我怀疑可能是 .subscribe() 的设置速度不够快。

是否有某种方法可以执行 setupNotification(),然后编写特征来告诉设备开始发送通知?

这是我当前的代码:

rxBleClient = RxBleClient.create(this);
RxBleDevice device = rxBleClient.getBleDevice(mac_address);

device.establishConnection(false)
        .flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(pword_uuid, pword)
                .flatMap(ignored1 -> rxBleConnection.writeCharacteristic(mode_uuid, mode))
                .flatMap(ignored2 -> rxBleConnection.setupNotification(log_uuid))
        )
        .flatMap(notificationObservable -> notificationObservable)
        .subscribe(
                bytes -> {
                    System.out.println(">>> data from device " + bytesToHex(bytes));
                },
                throwable -> {
                    System.out.println("error");
                    System.out.println(throwable);
                });
4

1 回答 1

6

可以通过 BLE 执行的大多数操作都是异步的,需要一些时间才能完成。设置通知也不例外 - 这是一个两步过程:

  1. 设置本地通知
  2. 编写一个希望从中获取通知的特征的客户端特征配置描述符

如果您的外围设备首先设置为在通知准备好由中央接收之前发送通知,则某些数据可能会在通知设置过程中丢失。

是否有某种方法可以执行 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[]>在写入描述符之前发出,允许从头开始观察通知(如果开头正在写入描述符)

缺点:

  • 无法判断描述符何时被写入。
于 2017-09-13T09:05:06.400 回答