1

I am trying to read all the records stored in a glucometer using rxAndroidBle library. According to several resources that I have found the process consist of three main steps after pairing/bonding and connecting to the device:

  1. Setup indications in the Record Access Control Point characteristic (RACP)
  2. Setup notifications on the Glucose measurement characteristic
  3. Write on the RACP characteristic two bytes 0x01, 0x01.

Then the notifications should flow if there are any records.

Now, this flow has worked fine some times in an LG G5 with android 7.0, but on other phones that I have access to it just won't work. It will throw a gruesome GATT_INTERNAL_ERROR (status 129), which is kind of ambiguous. I found this article which describes kind of what I may be facing.

My concern is that this works but it may be firmware related, which is weird because I've seen it work flawlessly on other application that connects to the glucometer without issue in any device.

Here's what how my test code for this seems like right now:

fun loadRecords(rxBleDevice: RxBleDevice){
...
...
rxBleDevice.establishConnection(false)
   .flatMap { rxBleConnection: RxBleConnection ->
        rxBleConnection.setupIndication(racpUUID)
            .flatMapSingle {
                Single.just(rxBleConnection)
            }
    }
    .flatMap { rxBleConnection ->
        writeAndReadOnNotification(racpUUID, 
                                   glucoseUUID, 
                                   byteArrayOf(0x01, 0x01), 
                                   false, 
                                   rxBleConnection)
    }
    .subscribe(
        { it:ByteArray ->
            decodeReading(it)
        },Logger::logException)
}

private fun writeAndReadOnNotification(writeTo: UUID, readOn: UUID,
                                           bytes: ByteArray,
                                           isIndication: Boolean,
                                    rxBleConnection:RxBleConnection)
: Observable<ByteArray> {
    val notifObservable = if (isIndication)
            rxBleConnection.setupIndication(readOn)
        else
            rxBleConnection.setupNotification(readOn)
    return notifObservable.flatMap { notificationObservable ->
            Observable.combineLatest(
                    notificationObservable,
                    rxBleConnection.writeCharacteristic(writeTo, bytes).toObservable(),
                    BiFunction { readBytes: ByteArray, writeBytes: ByteArray -> readBytes })
        }
    }

and here's what the log looks like for that piece of code:

18:28:58.058 D/BluetoothGatt: connect() - device: E0:7D:EA:FF:38:AB, auto: false
18:28:58.058 D/BluetoothGatt: registerApp()
18:28:58.058 D/BluetoothGatt: registerApp() - UUID=cca42db0-a88f-4b1c-acd0-f7fbe7be536d
18:28:58.065 D/BluetoothGatt: onClientRegistered() - status=0 clientIf=7
18:28:58.518 D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=7 device=E0:7D:EA:FF:38:AB
18:28:58.527 D/BluetoothGatt: discoverServices() - device: E0:7D:EA:FF:38:AB
18:28:58.532 D/BluetoothGatt: onSearchComplete() = Device=E0:7D:EA:FF:38:AB Status=0
18:28:58.873 D/BluetoothGatt: setCharacteristicNotification() - uuid: 00002a52-0000-1000-8000-00805f9b34fb enable: true
18:28:58.965 D/BluetoothGatt: setCharacteristicNotification() - uuid: 00002a18-0000-1000-8000-00805f9b34fb enable: true
18:28:59.057 D/BluetoothGatt: setCharacteristicNotification() - uuid: 00002a18-0000-1000-8000-00805f9b34fb enable: false
18:28:59.061 D/BluetoothGatt: setCharacteristicNotification() - uuid: 00002a52-0000-1000-8000-00805f9b34fb enable: false
18:28:59.066 E/None: com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException: GATT exception from MAC='XX:XX:XX:XX:XX:XX', status 129 (GATT_INTERNAL_ERROR), type BleGattOperation{description='CHARACTERISTIC_WRITE'}. (Look up status 0x81 here https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-5.1.0_r1/stack/include/gatt_api.h)
        at com.polidea.rxandroidble2.internal.connection.RxBleGattCallback.propagateErrorIfOccurred(RxBleGattCallback.java:243)
        at com.polidea.rxandroidble2.internal.connection.RxBleGattCallback.access$800(RxBleGattCallback.java:35)
        at com.polidea.rxandroidble2.internal.connection.RxBleGattCallback$2.onCharacteristicWrite(RxBleGattCallback.java:125)
        at android.bluetooth.BluetoothGatt$1$7.run(BluetoothGatt.java:438)
        at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:770)
        at android.bluetooth.BluetoothGatt.access$200(BluetoothGatt.java:39)
        at android.bluetooth.BluetoothGatt$1.onCharacteristicWrite(BluetoothGatt.java:433)
        at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:137)
        at android.os.Binder.execTransact(Binder.java:731)
18:28:59.067 D/BluetoothManager: getConnectionState()
18:28:59.067 D/BluetoothManager: getConnectedDevices
18:28:59.074 D/BluetoothGatt: cancelOpen() - device: E0:7D:EA:FF:38:AB
18:28:59.080 D/BluetoothGatt: onClientConnectionState() - status=0 clientIf=7 device=E0:7D:EA:FF:38:AB
18:28:59.083 D/BluetoothGatt: close()
18:28:59.084 D/BluetoothGatt: unregisterApp() - mClientIf=7
18:28:59.507 V/FA: Inactivity, disconnecting from the service

Did I missed something in my code? Why does it work on some phones?

Danlos
  • 25
  • 5

1 Answers1

0

I managed to solve this myself. Looking into the logs I posted earlier I saw that both the indications and the notifications were being disabled right after setting them up, so I made a deep exploration into the library to see, why it was doing so. I turns out that you should not set indications before setting up notifications in whatever characteristic (even though they are two different characteristics). So the main steps to read this should be in this order:

  1. Set up Glucose characteristic notifications and keep observing.
  2. Set up indications on the RACP.
  3. Write 0x01, 0x01 into the RACP.
  4. Profit

Also I found this note in the library code:

/*
*NOTE: due to stateful nature of characteristics if one will setupIndication() before setupNotification()
* the notification will not be set up and will emit an BleCharacteristicNotificationOfOtherTypeAlreadySetException
*/

which led me to move the notification part before the indication part. Here is what it looks like right now:

fun loadRecords(rxBleDevice: RxBleDevice){
...
//Do stuff
...
    rxBleDevice.establishConnection(false)
        .flatMap { rxBleConnection: RxBleConnection ->

    rxBleConnection.setupNotification(glucoseUUID,
                                      NotificationSetupMode.QUICK_SETUP)
        .flatMapSingle {
            Single.just(Pair(it, rxBleConnection))
        }
    }
    .flatMap { (observable, rxBleConnection)  ->
        writeAndReadOnNotification(racpUUID, 
                                   racpUUID, 
                                   byteArrayOf(0x01, 0x01),
                                   true, 
                                   rxBleConnection).subscribe()
        observable
    }
    .subscribe(
        {
            decodeReading(it)
        },Logger::logException)
}

I know it looks ugly and it needs polishing.

Danlos
  • 25
  • 5