如何在RxAndroidBle中接收所有通知

时间:2017-09-13 08:28:12

标签: rxandroidble

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

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

我已经确认BLE设备实际上已成功发送所有预期的通知。我已经通过nRF Connect应用程序完成了这一切,一切都按预期通过那里工作。

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

  1. 写入密码特性以解锁设备
  2. 写入模式特性以使设备处于正确模式
  3. 订阅通知(并立即开始工作通知)
  4. 当通过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);
                    });
    

1 个答案:

答案 0 :(得分:3)

人们可以对BLE执行的大多数操作都是异步的,需要一些时间才能完成。设置通知也不例外 - 它分为两步:

  1. 设置本地通知
  2. 编写想要从
  3. 获取通知的特征的客户端特征配置描述符

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

      

    是否有某种方法可以进行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);
                    }
            );