coyodha
coyodha

Reputation: 135

Android BLE : Notification from multiple characteristic under one service

PROBLEM

I am trying to get notification from more than one characteristic through BLE, I've seen some solution on the internet that I need to wait until the onDescriptorWrite() callback is finished (Which i think i have done here?), but I can't do onDescriptorWrite() for the second time for FILE_TX (code down there) notification. All of this is performed under onServicesDiscovery() - when I established BLE connection.

Is there anything that I am doing wrong here?

You can only have one outstanding gatt operation at a time. In this case you do two writeDescriptor calls before waiting until the first has completed. You must wait for https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html#onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int) until you can send the next one.

This is the best answer I can find but how can you tell that the onDescriptorWrite has been completed?

I tried putting Thread.sleep(500) in between for a work around but i doesn't work too.

Under onServicesDiscovery - gattCallback

for (gattCharacteristic in gattCharacteristics) {
                        uuid = gattCharacteristic.uuid
                        // // Log.d("GATT", "$uuid")

                        if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_READ_FILE_TX)) {
                            gatt.setCharacteristicNotification(gattCharacteristic, true)
                            val descriptorfile: BluetoothGattDescriptor = gattCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID) ?: error("Required Client Characteristic Configuration not found")
                            descriptorfile.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
                            isSuccess = gatt.writeDescriptor(descriptorfile)
                            Log.d("tagfile", "FILE_TX Successful ? " + isSuccess)
                            gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
                            Log.d("tagfile", "Found Transparent service File Tx characteristics")
                        }

                        else if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_TX)) {
                            gatt.setCharacteristicNotification(gattCharacteristic, true)
                            val descriptor: BluetoothGattDescriptor = gattCharacteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID) ?: error("Required Client Characteristic Configuration not found")
                            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)
                            isSuccess = gatt.writeDescriptor(descriptor)
                            Log.d("tagfile", "TX Successful ? " + isSuccess)
                            gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
                            Log.d("tagfile", "Found Transparent service Tx characteristics")
                        }

                        if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_RX)) {
                            gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
                            Log.d("tagfile", "Found Transparent service Rx characteristics")
                        }

                        else if (gattCharacteristic.uuid.equals(UUID_CHARACTERISTIC_READ_FILE_RX)) {
                            gattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE)
                            Log.d("tagfile", "Found Transparent service File Rx characteristics")
                        }
}

Under onDescriptorWrite - gattCallback

override fun onDescriptorWrite(
            gatt: BluetoothGatt?,
            descriptor: BluetoothGattDescriptor?,
            status: Int
        ) {

            Log.d("tagfile", "Status of gatt : " + status + "       GATT FAILURE : " + BluetoothGatt.GATT_FAILURE)
        }

RESULTS :

2020-01-24 09:41:51.359 8565-8587/com.example.ricco_ble D/tagfile: TX Successful ? true
2020-01-24 09:41:53.359 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service Tx characteristics
2020-01-24 09:41:53.360 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service Rx characteristics
2020-01-24 09:41:53.371 8565-8587/com.example.ricco_ble D/tagfile: FILE_TX Successful ? false
2020-01-24 09:41:53.371 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service File Tx characteristics
2020-01-24 09:41:53.372 8565-8587/com.example.ricco_ble D/tagfile: Found Transparent service File Rx characteristics
2020-01-24 09:41:53.424 8565-8587/com.example.ricco_ble D/tagfile: Status of gatt : 0       GATT FAILURE : 257

Upvotes: 0

Views: 2215

Answers (2)

HarryQ
HarryQ

Reputation: 1423

I found it useful to follow two points when working with the Android BLE stack.

  1. In any Gatt callbacks, only perform result copying, and schedule the next task to perform (not to execute). So that the callback function can be released as soon as possible.
  2. Perform operation sequentially. Don't perform the next operation until you receive the callback from the previous operation.

From the code post above, you are nesting writing descriptors in serviceDiscovery callback. Also, you write the next descriptor before receiving the prior callback.

To have a more stable/predictable performance, you can refactor your code into something like

BluetoothGattCallback(){
    onServiceDiscovery(gatt){
        MainHandler.post((gatt)->setFirstNotification(gatt)) //do the scheduling, not direct execution here.
    }

    onDescriptorWrite(gatt){
         if(the first descriptor write success) {
        MainHandler.post((gatt)->setSecondNotification(gatt)) //do the scheduling
         } else if (the second descriptor write success) {
        MainHandler.post((gatt)->otherLogic(gatt)) //do the scheduling
        }
    } 
}

fun setFirstNotification(gatt){
    //your code to set descriptor value
}


fun setSecondNotification(gatt){
    //your code to set descriptor value
}

fun otherLogic(gatt){
    //your other code
}

This is roughly the idea how you would approach it if you want to build your communication app directly with the Android stack.

Upvotes: 1

ChillBroDev
ChillBroDev

Reputation: 312

Instead of trying to build the system yourself, I recommend using the Android BLE Library from Nordic SemiConductor. I spent the better half of 4 months trying to do it myself. This library allows you to execute your calls using async with defined callbacks and not have to worry about issues like this.

Upvotes: 1

Related Questions