0
votes

i'm having difficulty figuring out the unexpected behavior using RxAndroidBle.

the short form of the issue is that i need to receive in-order notification from two characteristics of a particular device. in my example below, i scan for SERVICE_UUID, and setup notification (in order) for CHARACTERISTIC_FOO_UUID and CHARACTERISTIC_BAR_UUID. i need to do something based on the responses from both characteristics --- in my example, i simply store the byte[] in member variables.

the issue i'm having is that the first characteristic reports back but not the second. if i flip the order, it is still whichever characteristic is first in the chain. below, i present the debug output, which shows that the notification call occurs for both, including the low-level descriptor write, but for some reason the second one does not report back. (assume i subscribe to the Observable.)

i have been able to get this to work without RxAndroidBle. i also have one version employing RxAndroidBle that works but is setup in a different manner using ConnectionSharingAdapter and several subscriptions. the example below is an attempt at a cleaner approach but, as i said, doesn't seem to work.

rxBleClient.scanBleDevices(SERVICE_UUID)
    .first()
    .flatMap(rxBleScanResult -> {
        return Observable.just(rxBleScanResult.getBleDevice());
    })
    .flatMap(rxBleDevice -> {
        return rxBleDevice.establishConnection(context, IS_AUTO_CONNECT);
    })
    .flatMap(rxBleConnection -> 
        rxBleConnection.setupNotification(CHARACTERISTIC_FOO_UUID)
        .flatMap(observable -> observable)
        .flatMap(new Func1<byte[], Observable<RxBleConnection>>() {
            @Override
            public Observable<RxBleConnection> call(final byte[] notificationBytes) {
                mFooBytes = notificationBytes;
                return Observable.just(rxBleConnection);
            }
        })
    )
    .flatMap(rxBleConnection -> 
        rxBleConnection.setupNotification(CHARACTERISTIC_BAR_UUID)
        .flatMap(observable -> observable)
        .flatMap(new Func1<byte[], Observable<RxBleConnection>>() {
            @Override
            public Observable<RxBleConnection> call(final byte[] notificationBytes) {
                mBarBytes = notificationBytes;
                return Observable.just(rxBleConnection);
            }
        })
    )

here is the RxBle debug output --- i redacted the actual uuid with "CHARACTERISTIC_FOO_UUID" in the output.

12-22 12:13:43.322 12074-12074/com.foo.example D/RxBle#Radio:   QUEUED RxBleRadioOperationScan(217963087)
12-22 12:13:43.322 12074-12281/com.foo.example D/RxBle#Radio:  STARTED RxBleRadioOperationScan(217963087)
12-22 12:13:43.412 12074-12281/com.foo.example D/RxBle#Radio: FINISHED RxBleRadioOperationScan(217963087)
12-22 12:13:43.682 12074-12074/com.foo.example D/RxBle#Radio:   QUEUED RxBleRadioOperationConnect(37012551)
12-22 12:13:43.682 12074-12281/com.foo.example D/RxBle#Radio:  STARTED RxBleRadioOperationConnect(37012551)
12-22 12:13:44.052 12074-12085/com.foo.example D/RxBle#BluetoothGatt: onConnectionStateChange newState=2 status=0
12-22 12:13:44.092 12074-12558/com.foo.example D/RxBle#Radio:   QUEUED RxBleRadioOperationServicesDiscover(72789039)
12-22 12:13:44.092 12074-12281/com.foo.example D/RxBle#Radio: FINISHED RxBleRadioOperationConnect(37012551)
12-22 12:13:44.092 12074-12281/com.foo.example D/RxBle#Radio:  STARTED RxBleRadioOperationServicesDiscover(72789039)
12-22 12:13:45.232 12074-12086/com.foo.example D/RxBle#BluetoothGatt: onServicesDiscovered status=0
12-22 12:13:45.262 12074-12558/com.foo.example D/RxBle#Radio:   QUEUED RxBleRadioOperationDescriptorWrite(8700606)
12-22 12:13:45.262 12074-12281/com.foo.example D/RxBle#Radio: FINISHED RxBleRadioOperationServicesDiscover(72789039)
12-22 12:13:45.262 12074-12281/com.foo.example D/RxBle#Radio:  STARTED RxBleRadioOperationDescriptorWrite(8700606)
12-22 12:13:45.342 12074-12085/com.foo.example D/RxBle#BluetoothGatt: onDescriptorWrite descriptor=00002902-0000-1000-8000-00805f9b34fb status=0
12-22 12:13:45.362 12074-12281/com.foo.example D/RxBle#Radio: FINISHED RxBleRadioOperationDescriptorWrite(8700606)
12-22 12:13:46.172 12074-12086/com.foo.example D/RxBle#BluetoothGatt: onCharacteristicChanged characteristic=CHARACTERISTIC_FOO_UUID
12-22 12:13:46.192 12074-12558/com.foo.example D/RxBle#Radio:   QUEUED RxBleRadioOperationDescriptorWrite(179103302)
12-22 12:13:46.192 12074-12281/com.foo.example D/RxBle#Radio:  STARTED RxBleRadioOperationDescriptorWrite(179103302)
12-22 12:13:46.272 12074-12201/com.foo.example D/RxBle#BluetoothGatt: onDescriptorWrite descriptor=00002902-0000-1000-8000-00805f9b34fb status=0
12-22 12:13:46.272 12074-12281/com.foo.example D/RxBle#Radio: FINISHED RxBleRadioOperationDescriptorWrite(179103302)

below is a simplified example of a version using RxAndroidBle that does work but with multiple subscriptions and a ConnectionSharingAdapter. in both versions, the Observable will get subscribed to elsewhere, so i'm trying to avoid all the bookkeeping of the multiple subscriptions --- the CompositeSubscription here and the other subscription(s) elsewhere. the approach above, which is having problems, seems more functional. in my actual application, what i'm doing is more complicated, so the version above actually become easier to follow, where in this simplified version it might seem like a bit more code.

CompositeSubscription bleConnectionSubscriptions = new CompositeSubscription();

Observable<RxBleConnection> bleConnectionObservable =
    bleConnectionObservable = bleDevice.establishConnection(context, IS_AUTO_CONNECT)
        .compose(new ConnectionSharingAdapter());

Subscription subscription =
    bleConnectionObservable
        .flatMap(rxBleConnection ->
                 rxBleConnection.setupNotification(CHARACTERISTIC_FOO_INGREDIENTS))
        .flatMap(observable -> observable)
        .subscribe(notificationBytes -> mFooBytes = notificationBytes);

bleConnectionSubscriptions.add(subscription);

subscription =
    bleConnectionObservable
        .flatMap(rxBleConnection ->
                 rxBleConnection.setupNotification(CHARACTERISTIC_BAR_INGREDIENTS))
        .flatMap(observable -> observable)
        .subscribe(notificationBytes -> mBarBytes = notificationBytes);

bleConnectionSubscriptions.add(subscription);
1
Your code should work (though it will probably start to misbehave if more than one value will be notified from the FOO characteristic) and judging from the logs the first notification is delivered though the second one is not visible anywhere. Are there any BLE logs after the moment you have pasted? Is there another BLE connection at the same time to different device? (I do not know if Android OS limit for characteristic notifications is shared among connections or not)Dariusz Seweryn
@s_noopy thanks. there will only be one notification from FOO uuid, so i should be safe, but thanks for pointing out the potential issue. as for the logs, what i pasted is everything reported. if i leave the connection alone, after a while additional logs will be generated when the connection auto-disconnects.Steve Yohanan
It seems that your device is not sending any notifications at all on the BAR characteristic. It is possible that the BAR characteristic value is changed before the notification is set up.Dariusz Seweryn
i updated the original post to include a simplified working version that uses multiple subscriptions. to answer a couple of additional questions posed in the comments... (1) there are no other BLE connections at the same time on the same device; (2) if i use separate subscriptions, both FOO and BAR report back, and if i switch the order in the version that doesn't work, BAR reports but FOO doesn't, so i don't think it has to do with BAR changing before the notification is setup (though i will verify next week when i get a chance). thanks @s_noopySteve Yohanan

1 Answers

1
votes

As you can see in the logs both notifications are being properly set but the second one does not receive any values. The suspicion is that the BAR value is emitted before the notification is setup.

You can setup both notifications at the very beginning of the connection using code like below:

rxBleClient.scanBleDevices(SERVICE_UUID)
        .first() // subscribe to the first device that is available...
        .flatMap(rxBleScanResult -> rxBleScanResult.getBleDevice().establishConnection(context, IS_AUTO_CONNECT)) // ...establish the connection...
        .flatMap(rxBleConnection -> Observable.combineLatest( // ...when connection is established we combine latest results from...
                rxBleConnection.setupNotification(CHARACTERISTIC_FOO_INGREDIENTS) // ...setup notification on FOO...
                        .flatMap(observable -> observable) // ...flatMap it to values...
                        .first(), // ...take the first value so the notification will be disposed...
                rxBleConnection.setupNotification(CHARACTERISTIC_BAR_INGREDIENTS) // ...setup notification on BAR...
                        .flatMap(observable -> observable) // ...flatMap it to values...
                        .first(), // ...take the first value so the notification will be disposed...
                ((fooBytes, barBytes) -> {
                    mFooBytes = fooBytes; 
                    mBarBytes = barBytes;
                    return true; // return whatever
                })
        ))
        .first(); // after the first returned value the connection will be disconnected