-->

Using RxAndroidBle, how do I subscribe to response

2019-01-27 05:13发布

问题:

The BLE device that I'm connecting to emits bytes on one of its GATT characteristics in response to writes to the characteristic. Clients are supposed to enable notifications on that characteristic, and to interpret change bytes on the characteristic. (The behavior I'm controlling is turning on a scanning service for nearby wireless networks, then listening to the service output.)

I'm using RxAndroidBle and following the examples. I have an active connection Observable. The characteristic I want to observe has a UUID called AP_SCAN_DATA. It's supposed to emit 0xFE in response to receiving a written 0xFF.

How do I call setupNotification and set up an Observer on it to catch emitted byte[]s, then write a value to the characteristic, so that I can catch the response?

My best effort so far:

connectionObservable.observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Observer<RxBleConnection>() {
                @Override
                public void onCompleted() { // ignore...
                }

                @Override
                public void onError(Throwable e) { // ignore...
                }

                @Override
                public void onNext(final RxBleConnection connection) {
                    Observable.just(connection)
                              .flatMap(new Func1<RxBleConnection, Observable<Observable<byte[]>>>() {
                                  @Override
                                  public Observable<Observable<byte[]>> call(RxBleConnection connection) {
                                      return connection.setupNotification(AP_SCAN_DATA);
                                  }
                            })
                            .doOnNext(new Action1<Observable<byte[]>>() {
                                @Override
                                public void call(Observable<byte[]> observable) {
                                    Log.i(TAG, "notification has been set up");
                                    // This code logs on DEBUG that a write was made, but no response ever arrives 
                                    connection.writeCharacteristic(AP_SCAN_DATA, CharacteristicValue.RESET.asBytes())
                                            .observeOn(AndroidSchedulers.mainThread())
                                            .subscribe();

                                }
                            })
                            .flatMap(new Func1<Observable<byte[]>, Observable<byte[]>>() {
                                @Override
                                public Observable<byte[]> call(Observable<byte[]> observable) {
                                    return observable;
                                }
                            })
                            .doOnNext(new Action1<byte[]>() {
                                @Override
                                public void call(byte[] bytes) {
                                    Log.i(TAG, "want to read response bytes here, but I don't... " + HexString.bytesToHex(bytes));
                                }
                            })
                            .subscribe();
                }
            });

回答1:

There is already a topic in which you may find some insight -> RxAndroidBle keeping a persistant connection + Write/Notification handling

This is how you could achieve the same result while using only a single .subscribe().

    connectionObservable
            .flatMap( // when the connection is available...
                    rxBleConnection -> rxBleConnection.setupNotification(AP_SCAN_DATA), // ... setup the notification...
                    (rxBleConnection, apScanDataNotificationObservable) -> Observable.combineLatest( // ... when the notification is setup...
                            rxBleConnection.writeCharacteristic(AP_SCAN_DATA, writeValue), // ... write the characteristic...
                            apScanDataNotificationObservable.first(), // ... and observe for the first notification on the AP_SCAN_DATA
                            (writtenBytes, responseBytes) -> responseBytes // ... when both will appear return just the response bytes...
                    )
            )
            .flatMap(observable -> observable) // ... flatMap the result as it is Observable<byte[]>...
            .first() // ... and finish after first response is received to cleanup notifications
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                    responseBytes -> { /* consume the response here */ },
                    throwable -> { /* handle exception */ }
            );

FYI - you should handle errors in every .subscribe() unless you're 100% sure that the Observable does not emit errors.



回答2:

For those readers not using a Java version that supports lambdas, here's my implementation of @s_noopy's answer.

connectionObservable
    .flatMap(new Func1<RxBleConnection, Observable<Observable<byte[]>>>() {
            @Override
            public Observable<Observable<byte[]>> call(RxBleConnection connection) {
                return connection.setupNotification(AP_SCAN_DATA);
            }             
        }, new Func2<RxBleConnection, Observable<byte[]>, Observable<byte[]>>() {
            @Override
            public Observable<byte[]> call(RxBleConnection connection, Observable<byte[]> apScanDataNotificationObservable) {
                return Observable.combineLatest(
                    connection.writeCharacteristic(AP_SCAN_DATA, CharacteristicValue.RESET.asBytes()),
                    apScanDataNotificationObservable.first(),
                    new Func2<byte[], byte[], byte[]>() {
                        @Override
                        public byte[] call(byte[] writtenBytes, byte[] responseBytes) {
                                    return responseBytes;
                                }
                            }
                        );
                    }
                }
            ).flatMap(new Func1<Observable<byte[]>, Observable<byte[]>>() {
                @Override
                public Observable<byte[]> call(Observable<byte[]> observable) {
                    return observable;
                }
            })
            .first()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Action1<byte[]>() {
                @Override
                public void call(byte[] bytes) {
                    Log.i(TAG, "notification response...." + HexString.bytesToHex(bytes));
                }
            }, new Action1<Throwable>() {
                @Override
                public void call(Throwable throwable) {
                    logError(throwable);
                }
            });