Quantcast
Channel: Recent Questions - Stack Overflow
Viewing all articles
Browse latest Browse all 12111

BLE Not Properly Notifying Two Characteristics in Kotlin Compose App

$
0
0

I'm trying to notify two characteristics, voltsValue and ampsValue, from a BLE-enabled device to my Android 11 phone. The BLE operations work fine for scanning, connecting and notifying the voltsValue into a screen text field, but that's only when I'm reading a single characteristic. When I add ampsValue as a second Service UUID/Characteristic UUID, my app displays two readings, but both text fields are displaying the same value, i.e. the voltsValue. Debug and logcat indicate the ampsValue is being set to the same value as voltsValue, so it's not an issue with the UI. The nRF app displays voltsValue and ampsValue as expected, so the device is working properly.

Logcat indicates onServicesDiscovered() is discovering the relevant UUIDs. However, I suspect there's a sequencing/timing issue with the asynchronous operations between onServicesDiscovered() and the onCharacteristicChanged() callback, inclusive. The amps characteristic either isn't being read/updated properly, and/or it's being overwritten by the volts characteristic. I'm not experienced enough to know why this is happening. My research indicates queuing BLE operations is required, but I'm not sure where and how to do that in my code. Can anyone provide some guidance? Note: This is my first mobile app and my first question on Stack Overflow, so please excuse any newbie errors.

The readCharacteristic() method is separated into voltsValue and ampsValue respectively:

@SuppressLint("MissingPermission")        override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {            if (status == BluetoothGatt.GATT_SUCCESS) {                Log.d("Frank", "Services discovered.")                val voltsServiceUuid = VOLTS_SERVICE_UUID                val voltsCharacteristicUuid = VOLTS_CHARACTERISTIC_UUID                val voltsService = gatt?.getService(voltsServiceUuid)                val voltsCharacteristic = voltsService?.getCharacteristic(voltsCharacteristicUuid)                voltsCharacteristic?.let {                    gatt.readCharacteristic(it)                }                val ampsServiceUuid = AMPS_SERVICE_UUID                val ampsCharacteristicUuid = AMPS_CHARACTERISTIC_UUID                val ampsService = gatt?.getService(ampsServiceUuid)                val ampsCharacteristic = ampsService?.getCharacteristic(ampsCharacteristicUuid)                ampsCharacteristic?.let {                    Log.d("Frank", "Reading Amps Characteristic")                    gatt.readCharacteristic(it)                }            } else {                Log.d("Frank", "Service discovery failed with status: $status")            }        }

The two characteristics are also separate in onCharacteristicRead():

```    @Deprecated("Deprecated in Java")            override fun onCharacteristicRead(                gatt: BluetoothGatt?,                characteristic: BluetoothGattCharacteristic?,                status: Int            ) {                super.onCharacteristicRead(gatt, characteristic, status)                if (status == BluetoothGatt.GATT_SUCCESS) {                    characteristic?.value?.let { value ->                        when (characteristic.uuid) {                            VOLTS_CHARACTERISTIC_UUID -> {                                val voltsFloatValue =                                    ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).float                                CoroutineScope(Dispatchers.IO).launch {                                    voltsValue = voltsFloatValue                                    Log.d("Frank-onRead", "voltsValue: $voltsFloatValue")                                }                                enableVoltsCharacteristicNotifications(gatt)                            }                            AMPS_CHARACTERISTIC_UUID -> {                                val ampsFloatValue =                                    ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).float                                CoroutineScope(Dispatchers.IO).launch {                                    ampsValue = ampsFloatValue                                    Log.d("Frank-onRead", "ampsValue: $ampsFloatValue")                                }                                enableAmpsCharacteristicNotifications(gatt)                            }                        }                    }                }            }```

Notifications is split into the two characteristics:

```    @SuppressLint("MissingPermission")            private fun enableVoltsCharacteristicNotifications(gatt: BluetoothGatt?) {                val voltsCharacteristic =                    gatt?.getService(VOLTS_SERVICE_UUID)?.getCharacteristic(VOLTS_CHARACTERISTIC_UUID)                if (voltsCharacteristic != null) {                    gatt.setCharacteristicNotification(voltsCharacteristic, true)                    val descriptor =                        voltsCharacteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))                    descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE                    gatt.writeDescriptor(descriptor)                }            }            @SuppressLint("MissingPermission")            private fun enableAmpsCharacteristicNotifications(gatt: BluetoothGatt?) {                val ampsCharacteristic =                    gatt?.getService(AMPS_SERVICE_UUID)?.getCharacteristic(AMPS_CHARACTERISTIC_UUID)                if (ampsCharacteristic != null) {                    gatt.setCharacteristicNotification(ampsCharacteristic, true)                    val descriptor =                        ampsCharacteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"))                    descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE                    gatt.writeDescriptor(descriptor)                }            }```

And finally onCharacteristicChanged():

```@Deprecated("Deprecated in Java")        override fun onCharacteristicChanged(            gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?        ) {            super.onCharacteristicChanged(gatt, characteristic)            characteristic?.value?.let { value ->                val voltsFloatValue = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).float                CoroutineScope(Dispatchers.IO).launch {                    voltsValue = voltsFloatValue                    Log.d("Frank - voltsFloatValue", "voltsValue: $voltsFloatValue")                }            }            super.onCharacteristicChanged(gatt, characteristic)            characteristic?.value?.let { value ->                val ampsFloatValue = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN).float                CoroutineScope(Dispatchers.IO).launch {                    ampsValue = ampsFloatValue                    Log.d("Frank - ampsFloatValue", "ampsValue: $ampsFloatValue")                }            }        }    }```

Viewing all articles
Browse latest Browse all 12111

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>