Queued data for Bluetooth LE devices on Android

02 Oct 2016

Cross platform development is sometimes really frustrating. Especially when dealing with hardware. In this case, communication with a Bluetooth Low Energy device. On Android, some of the communication was just lost.

Our use case was to send a lot of commands to some Bluetooth device. The commands consisted of short strings. In some state in our application, we sent about 10 consecutive commands to the device. Our application had to run on iOS and Android. Dealing with Bluetooth devices is only possible through classes of either OS, so we made 2 implementations. On iOS, the consecutive sending of commands just worked. On Android also, at least we thought it did. On one device, we had some issues. It seemed not all commands were received by the Bluetooth device. We used a library that was made by the supplier of the Bluetooth chip, which was something like:

public class BluetoothLeService extends Service {

    private BluetoothGatt mBluetoothGatt;
    private BluetoothGattCharacteristic mNotifyCharacteristic;

    ....

    public void writeValue(String strValue) {
        mNotifyCharacteristic.setValue(writeQueue.poll().getBytes());
        mBluetoothGatt.writeCharacteristic(mNotifyCharacteristic);
    }

    ....
}

So we called this method multiple times to send the commands. But after some searching on the web we found out that you have to wait for the Bluetooth device to affirm that the message is received, otherwise messages can get lost. Because we sent a lot of commands, the problem Android device just discarded some of the messages. There was a way to deal with this, by using a queue. The implementation is quite simple:

import java.util.Queue;
import java.util.LinkedList;

public class BluetoothLeService extends Service {

    private BluetoothGatt mBluetoothGatt;
    private BluetoothGattCharacteristic mNotifyCharacteristic;
    private Queue writeQueue = new LinkedList();
    private boolean isWriting = false;

    ....

    public void writeValue(String strValue) {
        writeQueue.add(strValue);
        writeNextValueFromQueue();
    }

    private void writeNextValueFromQueue() {
        if (isWriting) {
            return;
        }
        if (writeQueue.size() == 0) {
            return;
        }
        isWriting = true;

        mNotifyCharacteristic.setValue(writeQueue.poll().getBytes());
        mBluetoothGatt.writeCharacteristic(mNotifyCharacteristic);
    }

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            isWriting = false;
            writeNextValueFromQueue();
        }
        ....
    }
}

In our service we created an instance of BluetoothGattCallback, which reports back when messages are received (which is not the only callback though). The original implementation just had an empty onCharacteristicWrite method. The only thing we had to put in was a isWriting boolean and our method that checked whether the queue is empty and, if not, send the next message. With this little change, we fixed the problem device without any changes to outside code.

back to blog
comments powered by Disqus