3

First of all, I am using RxAndroidBLE library to manage my BLE connections.

I have two SensorTag devices and I want to read temperature from both at the same time. For example, I'd like to read the temperature from both devices exactly every 500ms and display it to user in two TextViews.

My app currently successfully connects to both BLE devices like this:

@OnClick(R.id.connectButton1)
public void connectFirstSensorTag(Button b) {
    if (!isDeviceConnected(sensorTag1)) {
        connectionObservable1 = sensorTag1.establishConnection(getApplicationContext(), false).compose(new ConnectionSharingAdapter());
    }

    connectionObservable1.subscribe(new Subscriber<RxBleConnection>() {
        @Override
        public void onCompleted() {

        }

        @Override
        public void onError(Throwable e) {
            updateStatus(statusTextView1, "SensorTag not found");
        }

        @Override
        public void onNext(RxBleConnection rxBleConnection) {
            updateStatus(statusTextView1, "Connected");
            enableSensorTagTemperatureSensor(connectionObservable1);
        }
    });
}

@OnClick(R.id.connectButton2)
public void connectSecondSensorTag(Button b) {
    if (!isDeviceConnected(sensorTag2)) {
        connectionObservable2 = sensorTag2.establishConnection(getApplicationContext(), false).compose(new ConnectionSharingAdapter());
    }

    connectionObservable2.subscribe(new Subscriber<RxBleConnection>() {
        @Override
        public void onCompleted() {

        }

        @Override
        public void onError(Throwable e) {
            updateStatus(statusTextView2, "SensorTag not found");
        }

        @Override
        public void onNext(RxBleConnection rxBleConnection) {
            updateStatus(statusTextView2, "Connected");
            enableSensorTagTemperatureSensor(connectionObservable2);
        }
    });
}

Now I'm looking for the best way to read temperature from both at the same time every 500ms.

Right now, I'm doing something like this:

connectionObservable1
                .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(uuidFromShortCode("AA01")))
                .subscribe(bytes -> {

                    // first temperature was successfully read here

                    connectionObservable2
                            .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(uuidFromShortCode("AA01")))
                            .subscribe(bytes -> {

                                // second temperature was successfully read here

                            }, error -> {
                                updateStatus(error.toString());
                            });
                }, error -> {
                    updateStatus(error.toString());
                });

And this block of code is inside a runnable that gets called every 500ms.

I feel like this an extremely inefficient way to do it. Could someone please let me know if there is a better way to do this?

Guy
  • 6,414
  • 19
  • 66
  • 136
  • You should probably parallelize the read, no? Also note that there's no reason to have two anonymous "new Subscriber(" (extract this as an inner class and pass `statusTextView1/2` by the constructor) –  Aug 14 '16 at 10:00

2 Answers2

2

First of all you cannot make truly parallel reads or any other operations on the BLE as you have only one radio and operations need to be sequential. The best you can do is to fire them as soon one after another as possible. Using the RxAndroidBle you are getting the serialization managed for you.

The way to doing what you want I see this way:

    RxBleDevice sensorTag0 = // your first SensorTag
    RxBleDevice sensorTag1 = // your second SensorTag
    UUID characteristicUuid = uuidFromShortCode("AA01");

    Subscription flowSubscription = Observable.combineLatest(
            sensorTag0.establishConnection(this, false), // establishing connections
            sensorTag1.establishConnection(this, false),
            Pair::new // merging them together
    )
            .flatMap(connections -> Observable
                    .interval(500, TimeUnit.MILLISECONDS) // every 500 ms
                    .flatMap(aLong -> Observable.combineLatest(
                            connections.first.readCharacteristic(characteristicUuid), // performing reads
                            connections.second.readCharacteristic(characteristicUuid),
                            Pair::new // and merging the results
                    )))
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                    readings -> {
                        updateUISensorTag0(readings.first); // updating the UI
                        updateUISensorTag1(readings.second);
                    },
                    throwable -> updateStatus(throwable.toString()) // or showing the error
            );

Hope this helps you.

Best Regards.

Dariusz Seweryn
  • 3,212
  • 2
  • 14
  • 21
  • Thanks! This is exactly the kind of answer I was looking for. One additional question though. How did you know that RxAndroidBle is capable of doing this? I've read through all the documentation that is available for this library (and there's not much of it) and there was no example that would show anything like this. Did you dig into the source code to find out? Or is there something else that I missed when learning about this library? I am genuinely interested because I often stumble upon an issue that I can't resolve even after digging through documentation a lot. – Guy Aug 16 '16 at 18:59
  • I am one of authors of the RxAndroidBle library. The library tries to conform to Observable contract and the rest is combining that with rxJava knowledge. In the above example I only use two library methods: `establishConnection(Context, boolean)` and `readCharacteristic(UUID)` - the rest is pure rxJava. Actually there is the Example #5 https://github.com/Polidea/RxAndroidBle/blob/master/sample/src/main/java/com/polidea/rxandroidble/sample/example5_rssi_periodic/RssiPeriodicExampleActivity.java where a similar flow is being used with `readRssi()` instead of `readCharacteristic(UUID)` – Dariusz Seweryn Aug 16 '16 at 19:36
  • If I may ask - what information would you expect? And where? I will try to update available documentation. – Dariusz Seweryn Aug 16 '16 at 20:50
  • Actually I didn't know that this is mostly just RxJava. I am very new to RxJava as well (pretty much never used it before this), so that's probably the reason why I couldn't use this library in a more "advanced" way. I guess what I would expect from documentation is to show and possibly explain some more advanced usage examples like the one in your answer, because a lot of people like me meet RxJava for the first time when they want to use a library like yours. But either way, great library, I love it! It made BLE extremely easy to use for me :) – Guy Aug 17 '16 at 06:43
0

I would suggest that you start two threads. Thread 1 checks the first device, thread 2 checks the second. This ensure that both are being read at the same time. In order to proceed with the code only after both have completed, I would do a blocking join loop.

//Threads array has been created, with 0 checking the first 
//device and 1 checking the second
for(i = 0; i < threads.length(); i++)
    threads[i].join();
meedz
  • 132
  • 13
  • Hey, thanks for your answer. Could you elaborate on the for loop you mentioned? I don't think I fully understand how it would help me know when both threads finished. – Guy Aug 14 '16 at 13:45
  • `thread.join` allows you to wait for a thread to end before completion. Now, if you loop over all threads an ensure that they're all done, then that's exactly what you want! – meedz Aug 16 '16 at 18:22
  • That makes sense, thanks! I accepted another answer since it is more viable for my example, but your information helped too. – Guy Aug 16 '16 at 18:56