I want to use Kotlin's coroutines to handle BLE's asynchronous callbacks. Connecting to a BLE device takes a callback object, something like:
connectToBle(Context, Boolean, GattCallback)
The result returns asynchronously, in a method onConnectionStateChanged
of GattCallback object. I used suspendCoroutine<BluetoothGatt>
to implement this as detailed in the documentation here .
Now onConnectionStateChanged
returns a BluetoothGatt object which I have to save as a global variable and use to call other methods such as discoverServices
, readCharacteristic
, writeCharacteristic
etc., all of which return asynchronously in different callback methods of the GattCallback object such as onServicesDiscovered
, onCharacteristicRead
, onCharacteristicWrite
and so on.
Here's the code using suspendCoroutine
:
suspend fun BluetoothDevice.connectToBleDevice(
context: Context,
autoConnect: Boolean = false
) = suspendCoroutine<BluetoothGatt?> { cont ->
connectGatt(context, autoConnect, object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
super.onConnectionStateChange(gatt, status, newState)
Timber.d("onConnectionStateChange: ")
if (status != BluetoothGatt.GATT_SUCCESS) cont.resume(null) else cont.resume(gatt)
// save gatt instance here if success
}
override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
super.onServicesDiscovered(gatt, status)
if (status != BluetoothGatt.GATT_SUCCESS) cont.resume(null) else cont.resume(gatt)
// return list of services if success
}
override fun onCharacteristicRead(
gatt: BluetoothGatt?,
characteristic: BluetoothGattCharacteristic?,
status: Int
) {
super.onCharacteristicRead(gatt, characteristic, status)
if (status != BluetoothGatt.GATT_SUCCESS) cont.resume(null) else cont.resume(gatt)
// return read value if success
}
})
}
Methods called on the saved gatt instance:
fun discoverServices() {
gatt?.discoverServices() // result received in onServicesDiscovered
}
fun readCharacteristic(serviceUUID: UUID, characteristicUUID: UUID) {
gatt?.apply {
val characteristic =
getService(serviceUUID).getCharacteristic(characteristicUUID)
readCharacteristic(characteristic) // result received in onCharacteristicRead
}
}
If I want to write "sequential code" as below:
val gatt = connectToBle(context, false, gattCallback) // suspend until onConnectionStateChanged returns successfully
discoverServices() // suspend until discoverServices returns successfully
writeCharacteristic(characteristic, valueToWrite) // suspend until value is written successfully
val valueRead = readCharacteristic(characteristic) // suspend until the value is read successfully
disconnect()
What changes do I have to make? Should I use something other than the suspendCoroutine?
Sounds like Kable might fit your needs? It provides an API similar to what you've described.
For example, you can connect, write/read to/from a characteristic, then disconnect, with the following code:
val characteristic = characteristicOf(
service = "00001815-0000-1000-8000-00805f9b34fb",
characteristic = "00002a56-0000-1000-8000-00805f9b34fb"
)
// todo: Use scanner (provided by Kable) to obtain `peripheral`.
peripheral.apply {
connect()
write(characteristic, byteArrayOf(1, 2, 3))
val valueRead: ByteArray = read(characteristic)
disconnect()
}
Disclaimer: I'm a contributor to Kable , so this is a bit of a biased self plug. ♂️
if someone still looking for the answer, it can be achieved using coroutine channels (or maybe callbackFlow as well)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.