1 year ago

#29974

test-img

Mohamed El Kayal

How to enable notifications on exactly 2 characteristics in Android?

I want to be able to receive notifications on exactly 2 characteristics. Said characteristics are RX and TX from the perspective of the BLE device I am communicating with. I have succeeded in doing it for the RX, but apparently I need to do the same for the TX.

   override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
        with(gatt) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                val services = gatt.services
                Timber.w("Discovered ${services.size} services for ${device.address}.")
                printGattTable()
                requestMtu(device, GATT_MAX_MTU_SIZE)
                val characteristic: BluetoothGattCharacteristic = this.getService(XpressStreamingServiceUUID).getCharacteristic(peripheralRX)
                this.setCharacteristicNotification(characteristic, true)
                setBleCharacteristic(characteristic)
                listeners.forEach { it.get()?.onConnectionSetupComplete?.invoke(this) }
            } else {
                Timber.e("Service discovery failed due to status $status")
                teardownConnection(gatt.device)
                disconnect()
            }
        }
        if (pendingOperation is Connect) {
            signalEndOfOperation()
        }
    }

how do I go about changing that? Would a function like this suffice?:

 private fun setNotification(
    characteristic: BluetoothGattCharacteristic,
    enabled: Boolean
) {
    bluetoothGattRef.let { gatt ->
        gatt.setCharacteristicNotification(characteristic, enabled)

        if (peripheralTX == characteristic.uuid) {
            val descriptor = characteristic.getDescriptor(UUID.fromString(CCC_DESCRIPTOR_UUID))
            descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
            gatt.writeDescriptor(descriptor)
        } else if (peripheralRX == characteristic.uuid) {
            val descriptor = characteristic.getDescriptor(UUID.fromString(CCC_DESCRIPTOR_UUID))
            descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
            gatt.writeDescriptor(descriptor)
        }
        else{
            Timber.i("Neither characteristic RX nor TX detected")
        }
    }
}

I basically want to be notified by either one of them when send or receiving data

UPDATE: would this be a proper queuing mechanism?

 @Synchronized
private fun enqueueOperation(operation: BleOperationType) {
    operationQueue.add(operation)
    if (pendingOperation == null) {
        doNextOperation()
    }
}


@Synchronized
private fun signalEndOfOperation() {
    Timber.d("End of $pendingOperation")
    pendingOperation = null
    if (operationQueue.isNotEmpty()) {
        doNextOperation()
    }
}
    @Synchronized
private fun doNextOperation() {
    if (pendingOperation != null) {
        Timber.e("doNextOperation() called when an operation is pending! Aborting.")
        return
    }

    val operation = operationQueue.poll() ?: run {
        Timber.v("Operation queue empty, returning")
        return
    }
    pendingOperation = operation

    // Handle Connect separately from other operations that require device to be connected
    if (operation is Connect) {
        with(operation) {
            Timber.w("Connecting to ${device.name} | ${device.address}")
            device.connectGatt(context, false, gattCallback)
            isConnected.value = true
        }
        return
    }

    // Check BluetoothGatt availability for other operations
    val gatt = deviceGattMap[operation.device]
        ?: this@BleConnectionManager.run {
            Timber.e("Not connected to ${operation.device.address}! Aborting $operation operation.")
            signalEndOfOperation()
            return
        }

    // TODO: Make sure each operation ultimately leads to signalEndOfOperation()
    // TODO: Refactor this into an BleOperationType abstract or extension function
    when (operation) {
        is Disconnect -> with(operation) {
            Timber.w("Disconnecting from ${device.address}")
            gatt.close()
            deviceGattMap.remove(device)
            listeners.forEach { it.get()?.onDisconnect?.invoke(device) }
            signalEndOfOperation()
            setConnectionStatus(STATE_DISCONNECTED)
            isConnected.value = false
        }
        is CharacteristicWrite -> with(operation) {
            gatt.findCharacteristic(characteristicUUID)?.let { characteristic ->
                characteristic.writeType = writeType
                characteristic.value = payLoad
                gatt.writeCharacteristic(characteristic)
            } ?: this@BleConnectionManager.run {
                Timber.e("Cannot find $characteristicUUID to write to")
                signalEndOfOperation()
            }
        }
        is CharacteristicRead -> with(operation) {
            gatt.findCharacteristic(characteristicUUID)?.let { characteristic ->
                gatt.readCharacteristic(characteristic)
            } ?: this@BleConnectionManager.run {
                Timber.e("Cannot find $characteristicUUID to read from")
                signalEndOfOperation()
            }
        }
        is DescriptorWrite -> with(operation) {
            gatt.findDescriptor(descriptorUUID)?.let { descriptor ->
                descriptor.value = payLoad
                gatt.writeDescriptor(descriptor)
            } ?: this@BleConnectionManager.run {
                Timber.e("Cannot find $descriptorUUID to write to")
                signalEndOfOperation()
            }
        }
        is DescriptorRead -> with(operation) {
            gatt.findDescriptor(descriptorUUID)?.let { descriptor ->
                gatt.readDescriptor(descriptor)
            } ?: this@BleConnectionManager.run {
                Timber.e("Cannot find $descriptorUUID to read from")
                signalEndOfOperation()
            }
        }
        is EnableNotifications -> with(operation) {
            gatt.findCharacteristic(characteristicUUID)?.let { characteristic ->
                val cccdUuid = UUID.fromString(CCC_DESCRIPTOR_UUID)
                val payload = when {
                    characteristic.isIndicatable() ->
                        BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
                    characteristic.isNotifiable() ->
                        BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
                    else ->
                        error("${characteristic.uuid} doesn't support notifications/indications")
                }

                characteristic.getDescriptor(cccdUuid)?.let { cccDescriptor ->
                    if (!gatt.setCharacteristicNotification(characteristic, true)) {
                        Timber.e("setCharacteristicNotification failed for ${characteristic.uuid}")
                        signalEndOfOperation()
                        return
                    }

                    cccDescriptor.value = payload
                    gatt.writeDescriptor(cccDescriptor)
                } ?: this@BleConnectionManager.run {
                    Timber.e("${characteristic.uuid} doesn't contain the CCC descriptor!")
                    signalEndOfOperation()
                }
            } ?: this@BleConnectionManager.run {
                Timber.e("Cannot find $characteristicUUID! Failed to enable notifications.")
                signalEndOfOperation()
            }
        }
        is DisableNotifications -> with(operation) {
            gatt.findCharacteristic(characteristicUUID)?.let { characteristic ->
                val cccdUuid = UUID.fromString(CCC_DESCRIPTOR_UUID)
                characteristic.getDescriptor(cccdUuid)?.let { cccDescriptor ->
                    if (!gatt.setCharacteristicNotification(characteristic, false)) {
                        Timber.e("setCharacteristicNotification failed for ${characteristic.uuid}")
                        signalEndOfOperation()
                        return
                    }

                    cccDescriptor.value = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
                    gatt.writeDescriptor(cccDescriptor)
                } ?: this@BleConnectionManager.run {
                    Timber.e("${characteristic.uuid} doesn't contain the CCC descriptor!")
                    signalEndOfOperation()
                }
            } ?: this@BleConnectionManager.run {
                Timber.e("Cannot find $characteristicUUID! Failed to disable notifications.")
                signalEndOfOperation()
            }
        }
        is MtuRequest -> with(operation) {
            gatt.requestMtu(mtu)
        }
    }
}

android

kotlin

bluetooth-lowenergy

characteristics

0 Answers

Your Answer

Accepted video resources