Rants from a Computer Engineer

BLE in Android

As state in my previous post, I have been playing around with the PSoC 4 BLE 4.1 Pioneer Kit. BLE in Android is more of an art than a science. The Bluetooth stack is notoriously flaky, even in recent versions of Android. Problems like GATT Error 133 and bad BLE drives in the KitKat version of Android have cause numerous headaches for developers that want to use the BLE stack. After repeatably scouring the internet and smashing my head on my desk here are a few pointers if you want to use BLE on Android.

  1. Set API version to 21 or above – Seriously it is not worth the headache. Anything at or below Android 4.4.4 is not worth the amount of pain you have to go through, especially if you do not want to use a 3rd party library. If you absolutely need to target an older SDK, you might want to follow set 2.
  2. Use a third party library – There are many good libraries on Github where other developers have gone through the painstaking process of dealing with the various quarks of BLE in Android and have provided easy to use APIs. RxAndroidBle is by far the most popular library.
  3. Use the TRANSPORT_LE parameter when calling the bluetoothDevice.connectGatt() function. – Depending on the target device, many users have reported that adding this parameter will clear up any GATT Error 133 they recieve.
  4. Use the UI thread when interacting with the BLE API – Anytime you call a BLE GATT function, ensure that you are running on the UI thread. If called on another thread, but the calls in the runOnUIThread.

Ok now that we have that out of the way lets begin by implementing the BluetoothGattCallback class with our hooks into our functions.

// Implements callback methods for GATT events that the app cares about.  For example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        Timber.d("onConnectionStateChange() received - status: %s, state %d", stringifyGattStatus(status), newState);

        if (status == BluetoothGatt.GATT_SUCCESS) {
            switch (newState) {
                case BluetoothProfile.STATE_CONNECTED:
                    Timber.i("Connecting to GATT server.");
                    synchronized (mGattCallback) {
                        mConnectionState = STATE_CONNECTED;
                    }
                    break;
                case BluetoothProfile.STATE_DISCONNECTED:
                    Timber.i("Disconnected from GATT server.");
                    synchronized (mGattCallback) {
                        mConnectionState = STATE_DISCONNECTED;
                    }
                    break;
                case BluetoothProfile.STATE_CONNECTING:
                    Timber.i("Connecting to GATT server.");
                    synchronized (mGattCallback) {
                        mConnectionState = STATE_CONNECTING;
                    }
                    break;
                case BluetoothProfile.STATE_DISCONNECTING:
                    Timber.i("Disconnecting from GATT server.");
                    synchronized (mGattCallback) {
                        mConnectionState = STATE_DISCONNECTING;
                    }
                    break;
                default:
                    break;
            }
        } else {
            Timber.e("onConnectionStateChange() error with status: %d", status);
        }

        mEventBus.post(
            new ConnectionStateChangeEvent(
                gatt.getDevice(),
                status,
                newState
            )
        );
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        Timber.d("onServicesDiscovered() received - status: %s", stringifyGattStatus(status));
        mEventBus.post(new ServicesDiscoveredEvent(gatt.getDevice(), status));
    }

    @Override
    public void onCharacteristicRead(BluetoothGatt gatt,
                                     BluetoothGattCharacteristic characteristic,
                                     int status) {
        Timber.d("onCharacteristicRead() received - status: %s", stringifyGattStatus(status));
        mEventBus.post(new CharacteristicReadEvent(gatt.getDevice(), characteristic, status));
    }

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
        Timber.d("onCharacteristicWrite() received - status: %s", stringifyGattStatus(status));
        super.onCharacteristicWrite(gatt, characteristic, status);
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt,
                                        BluetoothGattCharacteristic characteristic) {
        Timber.d("onCharacteristicChanged() received");
        mEventBus.post(new CharacteristicChangedEvent(gatt.getDevice(), characteristic));
    }

    @Override
    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        Timber.d("onDescriptorWrite() received - status: %s", stringifyGattStatus(status));
        mEventBus.post(new DescriptorWriteEvent(gatt.getDevice(), descriptor, status));
    }

    @Override
    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
        Timber.d("onDescriptorRead() received - status: %s", stringifyGattStatus(status));
        mEventBus.post(new DescriptorReadEvent(gatt.getDevice(), descriptor, status));
    }

    @Override
    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
        Timber.d("onReliableWriteCompleted() received - status: %s", stringifyGattStatus(status));
        mEventBus.post(new ReliableWriteCompletedEvent(gatt.getDevice(), status));
    }

    @Override
    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
        Timber.d("onReadRemoteRssi() received - status: %s", stringifyGattStatus(status));
        super.onReadRemoteRssi(gatt, rssi, status);
    }

    @Override
    public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
        Timber.d("onMtuChanged() received - status: %s", stringifyGattStatus(status));
        super.onMtuChanged(gatt, mtu, status);
    }

    @Override
    public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
        Timber.d("onPhyUpdate() received - status: %s", stringifyGattStatus(status));
        super.onPhyUpdate(gatt, txPhy, rxPhy, status);
    }

    @Override
    public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
        Timber.d("onPhyRead() received - status: %s", stringifyGattStatus(status));
        super.onPhyRead(gatt, txPhy, rxPhy, status);
    }
};

 

Quadcopter 2.0

GEPRC GEP-VX Series 250mm Carbon Fiber Frame Kit We are going for a freestyle/cruiser build here so a decently large frame is […]

Leave a comment

Your email address will not be published. Required fields are marked *