Search code examples
bluetooth-lowenergyandroid-bluetoothrxandroidble

Read multiple characteristics from an Android device using library RxAndroidBle


I am using the library RxAndroidBle in order to scan devices and then connect to one specific device and read 4 GATT characteristics.

I can read one characteristic (Battery Level) ith this code :

scanSubscription = rxBleClient.scanBleDevices(
            new ScanSettings.Builder()
                    .build()
    )
            .observeOn(AndroidSchedulers.mainThread())
            .doOnNext(
                    scanResult -> {
                        if(scanResult.getBleDevice().getName() != null){
                            if(scanResult.getBleDevice().getName().equals("NODE 1")){
                                Log.e("BLE SCAN", "SUCCESS");
                                Log.e("BLE SCAN", scanResult.getBleDevice().getName());
                                Log.e("BLE SCAN", scanResult.getBleDevice().getMacAddress());
                                scanSubscription.unsubscribe();

                                RxBleDevice device = scanResult.getBleDevice();
                                subscription = device.establishConnection(false) // <-- autoConnect flag
                                        .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb")))
                                        .subscribe(
                                                characteristicValue -> {
                                                    Log.e("Characteristic", characteristicValue[0]+"");
                                                },
                                                throwable -> {
                                                    Log.e("Error", throwable.getMessage());
                                                }
                                        );
                            }
                        }
                    }
            )
            .subscribe();

I can read two by using :

.flatMap(rxBleConnection -> Observable.combineLatest( // use the same connection and combine latest emissions
                rxBleConnection.readCharacteristic(aUUID),
                rxBleConnection.readCharacteristic(bUUID),
                Pair::new
        ))

But I don't understand how to do that with 4 characteristics for example.

Thank you


Solution

  • The above example is just fine you would only need some data object that would accept more values than Pair. For instance something like:

    class ReadResult {
        final byte[] aValue;
        final byte[] bValue;
        final byte[] cValue;
        final byte[] dValue;
    
        ReadResult(byte[] aValue, byte[] bValue, byte[] cValue, byte[] dValue) {
            this.aValue = aValue;
            this.bValue = bValue;
            this.cValue = cValue;
            this.dValue = dValue;
        }
    }
    

    And then the example could look like this:

    disposable = rxBleClient.scanBleDevices(
            new ScanSettings.Builder().build(),
            new ScanFilter.Builder().setDeviceName("NODE 1").build() // one can set filtering by name here
    )
            .take(1) // take only the first result and then the upstream will get unsubscribed (scan will end)
            .flatMap(scanResult -> scanResult.getBleDevice().establishConnection(false)) // connect to the first scanned device that matches the filter
            .flatMapSingle(rxBleConnection -> Single.zip( // once connected read all needed values
                    rxBleConnection.readCharacteristic(aUUID),
                    rxBleConnection.readCharacteristic(bUUID),
                    rxBleConnection.readCharacteristic(cUUID),
                    rxBleConnection.readCharacteristic(dUUID),
                    ReadResult::new // merge them into a single result
            ))
            .take(1) // once the result of all reads is available unsubscribe from the upstream (connection will end)
            .subscribe(
                    readResult -> Log.d("Characteristics", /* print the readResult */),
                    throwable -> Log.e("Error", throwable.getMessage())
            );
    

    Original/Legacy solution for RxAndroidBle based on RxJava1:

    subscription = rxBleClient.scanBleDevices(
            new ScanSettings.Builder().build(),
            new ScanFilter.Builder().setDeviceName("NODE 1").build() // one can set filtering by name here
    )
            .take(1) // take only the first result and then the upstream will get unsubscribed (scan will end)
            .flatMap(scanResult -> scanResult.getBleDevice().establishConnection(false)) // connect to the first scanned device that matches the filter
            .flatMap(rxBleConnection -> Observable.combineLatest( // once connected read all needed values
                    rxBleConnection.readCharacteristic(aUUID),
                    rxBleConnection.readCharacteristic(bUUID),
                    rxBleConnection.readCharacteristic(cUUID),
                    rxBleConnection.readCharacteristic(dUUID),
                    ReadResult::new // merge them into a single result
            ))
            .take(1) // once the result of all reads is available unsubscribe from the upstream (connection will end)
            .subscribe(
                    readResult -> Log.d("Characteristics", /* print the readResult */),
                    throwable -> Log.e("Error", throwable.getMessage())
            );