Search code examples
androidbluetoothbluetooth-lowenergykotlin-coroutineskotlin-sharedflow

Bluetooth LE - Multiple Characteristic Behavior


I've written an Kotlin Compose Coroutine SharedFlow app that connects my phone to a BLE-enabled sensor device. The app is exhibiting a strange behavior. If I simply connect to the device, the voltsValue from the sensor is properly read and notified on my single screen. However, my device is notifying two characteristics, i.e. an ampsValue as well. The ampsValue is neither read per logcat nor displayed on my screen. The amps characteristic UUID is discovered, but not the amps characteristic value.

If I swap the characteristic UUIDs of volts and amps respectively, the ampsValue begin to notify in logcat and display on my screen. Of course, the respective sensor readings are displayed in the wrong text fields, but if I swap the UUIDs back, then both volts and amps display correctly. Another problem occurs when I power down the device and power it back up. My app will reconnect, but it reverts back to displaying only the voltsValue. The only way I can get it to read/notify both values is to swap the characteristic UUIDs. Does this behavior sound familiar to anyone? Note that nRF reads and displays both notifying values correctly.

This is the onServicesDiscovered() callback in which the UUIDs are read:

@SuppressLint("MissingPermission")
        override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
            when (status) {
                BluetoothGatt.GATT_SUCCESS -> {
                    // Service discovery successful
                    val service = gatt?.getService(SERVICE_UUID)
                    if (service != null) {
                        // Read volts characteristic
                        val voltsCharacteristic =
                            service.getCharacteristic(VOLTS_UUID)
                        voltsCharacteristic?.let {
                            gatt.readCharacteristic(it)
                            Log.d("Frank - read voltsCharacteristic", "Volts Characteristic = $it")
                        } ?: run {
                            Log.e("Frank", "Volts characteristic not found")
                        }

                        // Read amps characteristic
                        val ampsCharacteristic = service.getCharacteristic(AMPS_UUID)
                        ampsCharacteristic?.let {
                            gatt.readCharacteristic(it)
                            Log.d("Frank - read ampsCharacteristic", "Amps Characteristic = $it")
                        } ?: run {
                            Log.e("Frank", "Amps characteristic not found")
                        }
                    } else {
                        Log.e("Frank", "Service not found")
                    }
                }

                else -> {
                    // Service discovery failed
                    Log.e("Frank", "Service discovery failed with status: $status")
                }
            }

Solution

  • You may only have one outstanding GATT operation per BluetoothGatt object at a time. Please wait until you get the onReadCharacteristic callback until you send the next request (readCharacteristic).