NordicSemiconductor / Android-BLE-Library

A library that makes working with Bluetooth LE on Android a pleasure. Seriously.
BSD 3-Clause "New" or "Revised" License
1.98k stars 413 forks source link

I stoped receiving notifications even when i have still not get all data #554

Closed KevinMartinezC closed 4 months ago

KevinMartinezC commented 4 months ago

I am currently using the Android-BLE-library for Kotlin, trying to get the data for switches and APs. When I try to connect a switch, the notifications keep listening, and I get the firmware, serial number, and key. However, when I try it with the AP, I only get the firmware and serial number because it seems the notification stops before getting the key.

These are my logs from the switch:

Reading characteristic 00002a25-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a25-0000-1000-8000-00805f9b34fb)
onConnectionUpdated() - Device=FC:B9:23:80:01:59 interval=36 latency=0 timeout=500 status=0
Connection parameters updated (interval: 45.0ms, latency: 0, timeout: 5000ms)
Read Response received from 00002a25-0000-1000-8000-00805f9b34fb, value: (0x) 62-63-62-39-32-33-38-30-30-31-35-38
Reading characteristic 00002a26-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a26-0000-1000-8000-00805f9b34fb)
get serial number bcb923800158
Read Response received from 00002a26-0000-1000-8000-00805f9b34fb, value: (0x) 32-2E-31-64
Writing characteristic ba04c4b2-892b-43be-b69c-5d13f2195392 (WRITE REQUEST)
gatt.writeCharacteristic(ba04c4b2-892b-43be-b69c-5d13f2195392, value=0x0010, WRITE REQUEST)
get firmware 2.1d
Data written to ba04c4b2-892b-43be-b69c-5d13f2195392
Notification received from 0734594a-a8e7-4b1a-a6b1-cd5243059a57, value: (0x) 01-01-74-41-41-41-41-42-33-4E-7A-61-43-31-79-63-32-45-41-41-41-41-44-41-51-41-42-41-41-41-42-41-51-43-35-6F-6C-7A-79-30-62-46-78-35-6E-61-63-77-34-50-6C-66-50-44-78-36-75-76-6D-47-6F-2F-35-30-77-42-6A-66-2B-49-58-35-44-5A-30-76-68-53-69-6A-34-4C-7A-46-47-38-35-2F-49-65-67-75-68-50-66-34-38-4B-47-32-6B-4F-7A-44-39-57-5A-34-6B-31-38-74-72-41-61-43-75-54-76-44-57-4D-41-61-50-31-77-6C-63-38-78-74-63-67-34-32-6A-54-6E-56-78-76-51-76-64-70-55-47-67-6B-33-51-31-75-2B-4C-56-46-45-35-6E-36-35-38-38-39-32-6F-4F-37-67-42-30-75-6E-69-4B-61-55-58-30-39-47-44-54-53-59-73-78-6E-50-36-4F-6B-69-2B-42-52-73-6F-65-73-68-61-58-30-68-44-41-63-54-2F-58-44-4A-78-68-67-33-41-51-63-69-42-6D-50-6E-55-53-49-64-69-46-7A-57-4B-75-48-6D-64
LED turned OFF
Button released
Credits sent successfully
Value 240
RXData 240
Notification received from 0734594a-a8e7-4b1a-a6b1-cd5243059a57, value: (0x) 45-38-41-38-46-44-59-37-73-4B-6D-6C-67-70-35-37-48-51-4E-6E-73-55-63-50-41-63-38-51-36-67-37-56-65-41-4B-62-48-6F-79-6A-6B-39-4E-4A-6D-4E-4C-4B-55-44-42-72-36-75-35-69-37-4D-6C-78-41-45-35-66-53-57-67-48-2F-56-4E-34-51-6C-78-42-5A-68-57-6F-34-33-7A-58-6A-62-44-41-55-55-42-47-74-4D-53-6D-76-46-54-73-36-75-30-63-70-73-67-59-2F-50-47-38-38-62-4F-64-52-47-61-42-54-38-59-44-75-2F-50-44-31-74-2B-6B-64-77-44-03-00-01-00-03-00-01-04
Value 143
RXData 383
gatt.setCharacteristicNotification(0734594a-a8e7-4b1a-a6b1-cd5243059a57, false)
setCharacteristicNotification() - uuid: 0734594a-a8e7-4b1a-a6b1-cd5243059a57 enable: false
Disabling notifications and indications for 0734594a-a8e7-4b1a-a6b1-cd5243059a57
gatt.writeDescriptor(00002902-0000-1000-8000-00805f9b34fb, value=0x00-00)
get pupkey AAAAB3NzaC1yc2EAAAADAQABAAABAQC5olzy0bFx5nacw4PlfPDx6uvmGo/50wBjf+IX5DZ0vhSij4LzFG85/IeguhPf48KG2kOzD9WZ4k18trAaCuTvDWMAaP1wlc8xtcg42jTnVxvQvdpUGgk3Q1u+LVFE5n658892oO7gB0uniKaUX09GDTSYsxnP6Oki+BRsoeshaX0hDAcT/XDJxhg3AQciBmPnUSIdiFzWKuHmdE8A8FDY7sKmlgp57HQNnsUcPAc8Q6g7VeAKbHoyjk9NJmNLKUDBr6u5i7MlxAE5fSWgH/VN4QlxBZhWo43zXjbDAUUBGtMSmvFTs6u0cpsgY/PG88bOdRGaBT8YDu/PD1t+kdwD
AAAAB3NzaC1yc2EAAAADAQABAAABAQC5olzy0bFx5nacw4PlfPDx6uvmGo/50wBjf+IX5DZ0vhSij4LzFG85/IeguhPf48KG2kOzD9WZ4k18trAaCuTvDWMAaP1wlc8xtcg42jTnVxvQvdpUGgk3Q1u+LVFE5n658892oO7gB0uniKaUX09GDTSYsxnP6Oki+BRsoeshaX0hDAcT/XDJxhg3AQciBmPnUSIdiFzWKuHmdE8A8FDY7sKmlgp57HQNnsUcPAc8Q6g7VeAKbHoyjk9NJmNLKUDBr6u5i7MlxAE5fSWgH/VN4QlxBZhWo43zXjbDAUUBGtMSmvFTs6u0cpsgY/PG88bOdRGaBT8YDu/PD1t+kdwD
Data written to descr. 00002902-0000-1000-8000-00805f9b34fb
onConnectionUpdated() - Device=FC:B9:23:80:01:59 interval=396 latency=5 timeout=620 status=0
Connection parameters updated (interval: 495.0ms, latency: 5, timeout: 6200ms)

These are for the AP:

Reading characteristic 00002a25-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a25-0000-1000-8000-00805f9b34fb)
Read Response received from 00002a25-0000-1000-8000-00805f9b34fb, value: (0x) 62-63-62-39-32-33-30-30-63-34-65-38
Reading characteristic 00002a26-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a26-0000-1000-8000-00805f9b34fb)
get serial number bcb92300c4e8
Read Response received from 00002a26-0000-1000-8000-00805f9b34fb, value: (0x) 32-2E-30-6A
Writing characteristic ba04c4b2-892b-43be-b69c-5d13f2195392 (WRITE REQUEST)
gatt.writeCharacteristic(ba04c4b2-892b-43be-b69c-5d13f2195392, value=0x0010, WRITE REQUEST)
get firmware 2.0j
Data written to ba04c4b2-892b-43be-b69c-5d13f2195392
Notification received from 0734594a-a8e7-4b1a-a6b1-cd5243059a57, value: (0x) 01-01-74-41-41-41-41-42-33-4E-7A-61-43-31-79-63-32-45-41-41
LED turned OFF
Button released
Credits sent successfully
Value 20
RXData 20
onConnectionUpdated() - Device=B9:BC:00:23:EB:C4 interval=36 latency=0 timeout=500 status=16
onConnectionUpdated received status: 16, interval: 36, latency: 0, timeout: 500
Connection parameters update failed with status 16 (interval: 45.0ms, latency: 0, timeout: 5000ms)
onPhyUpdate() - status=0 address=B9:BC:00:23:EB:C4 txPhy=2 rxPhy=2
onConnectionUpdated() - Device=B9:BC:00:23:EB:C4 interval=36 latency=0 timeout=500 status=0
PHY updated (TX: LE 2M, RX: LE 2M)
Connection parameters updated (interval: 45.0ms, latency: 0, timeout: 5000ms)

This is my code:

    @OptIn(ExperimentalUnsignedTypes::class)
    private fun sendCredits() {
        rxCredits = 0x1000
        val data =
            ubyteArrayOf((rxCredits and 0xff).toUByte(), (rxCredits ushr 8).toUByte()).toByteArray()

        writeCharacteristic(txCtlChar, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
            .with { device, data ->
                Log.i("BlinkyManagerImpl", "Credits sent successfully")
                rxCredits = 0
            }
            .fail { device, status ->
                Log.e("BlinkyManagerImpl", "Failed to send credits, status: $status")
            }
            .enqueue()
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    override fun initialize() {
        // Enable notifications for the button characteristic.
        val flow: Flow<ButtonState> = setNotificationCallback(firmwareRevisionCharacteristic)
            .asValidResponseFlow()

        // Forward the button state to the buttonState flow.
        scope.launch {
            flow.map { it.state }.collect { _buttonState.tryEmit(it) }
        }

        setNotificationCallback(readChar).with { device, data ->
            val value = data.value
            if (value != null) {
                collectDataKey(value)
            }
        }

        enableNotifications(readChar).done {
            sendCredits()
        }.enqueue()

        setNotificationCallback(txCtlChar)
        enableNotifications(txCtlChar).enqueue()

        // Read the initial value of the write characteristic.
        readCharacteristic(writeChar).enqueue()

        enableNotifications(firmwareRevisionCharacteristic).enqueue()

        // Read the initial value of the button characteristic.
        readCharacteristic(firmwareRevisionCharacteristic).with(buttonCallback).enqueue()

        // Read the initial value of the LED characteristic.
        readCharacteristic(serialNumberCharacteristic).with(ledCallback).enqueue()

        readCharacteristic(serialNumberCharacteristic).with { device, data ->
            val serialNumber = data.getStringValue(0)
            serialNumberValue = serialNumber
            Log.wtf("kevin", "get serial number $serialNumber")
            // Handle serial number value
        }.enqueue()

        readCharacteristic(firmwareRevisionCharacteristic).with { device, data ->
            val firmwareRevision = data.getStringValue(0)
            firmware = firmwareRevision
            Log.wtf("kevin", "get firmware $firmwareRevision")
            // Handle firmware revision value
        }.enqueue()
    }
philips77 commented 4 months ago

Hello, Here are the results of my investigation.

Main issue

I think the main issue is with the MTU. Seems like your switch is requesting high MTU on it's own from the peripheral side. Have a look at this log entry:

Notification received from 0734594a-a8e7-4b1a-a6b1-cd5243059a57, value: (0x) 01-01-74-41-41-41-41-42-33-4E-7A-61-43-31-79-63-32-45-41-41-41-41-44-41-51-41-42-41-41-41-42-41-51-43-35-6F-6C-7A-79-30-62-46-78-35-6E-61-63-77-34-50-6C-66-50-44-78-36-75-76-6D-47-6F-2F-35-30-77-42-6A-66-2B-49-58-35-44-5A-30-76-68-53-69-6A-34-4C-7A-46-47-38-35-2F-49-65-67-75-68-50-66-34-38-4B-47-32-6B-4F-7A-44-39-57-5A-34-6B-31-38-74-72-41-61-43-75-54-76-44-57-4D-41-61-50-31-77-6C-63-38-78-74-63-67-34-32-6A-54-6E-56-78-76-51-76-64-70-55-47-67-6B-33-51-31-75-2B-4C-56-46-45-35-6E-36-35-38-38-39-32-6F-4F-37-67-42-30-75-6E-69-4B-61-55-58-30-39-47-44-54-53-59-73-78-6E-50-36-4F-6B-69-2B-42-52-73-6F-65-73-68-61-58-30-68-44-41-63-54-2F-58-44-4A-78-68-67-33-41-51-63-69-42-6D-50-6E-55-53-49-64-69-46-7A-57-4B-75-48-6D-64

On the other hand, the same notification that you get from the AP is only 20 bytes long. 20 bytes is the packet length with the default MTU=23 (max length is 20 bytes). Because of that, the firmware may experience some kind of issue (you try to send a lot more) and it stops.

Solution

Either make sure you set the same MTU value on both switch and AP or request it from Android. Add the following:

    override fun initialize() {
         requestMtu(517).enqueue()
         // [...]

Other issues

  1. The requests enqueued in the initialize() method are first added to the initialization queue before they are started being executed. However, in your code you have the following:
        enableNotifications(readChar).done {
            sendCredits()
        }.enqueue()
       // other requests...

    The sendCredits() also enqueues a request, but it will be performed AFTER the initialization is complete. It's not a part of the initialization, as it's enqueued outside of initiazlize(). It does work but placing it inside that method and in the middle may be confusing. I would recommend just moving it after the last readCharacteristic or, if you want to make sure it only happens when notifications are enabled, at least adding some comment.

  2. Just a reminder to rename the callbacks. You're handling serial number with LED callback and firmware revision with button callback:
       readCharacteristic(serialNumberCharacteristic).with(ledCallback).enqueue()
KevinMartinezC commented 4 months ago

I was using the android library and i want to update it to use nordic for a better user but i am still facing the issue with the AP

gatt.requestMtu(508)
configureMTU() - device: XX:XX:XX:XX:EB:C4 mtu: 508
onConfigureMTU() - Device=B9:BC:00:23:EB:C4 mtu=517 status=0
MTU changed to: 517
Reading characteristic 00002a26-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a26-0000-1000-8000-00805f9b34fb)
Read Response received from 00002a26-0000-1000-8000-00805f9b34fb, value: (0x) 32-2E-30-6A
Reading characteristic 00002a25-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a25-0000-1000-8000-00805f9b34fb)
Read Response received from 00002a25-0000-1000-8000-00805f9b34fb, value: (0x) 62-63-62-39-32-33-30-30-63-34-65-38
Reading characteristic 00002a25-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a25-0000-1000-8000-00805f9b34fb)
Read Response received from 00002a25-0000-1000-8000-00805f9b34fb, value: (0x) 62-63-62-39-32-33-30-30-63-34-65-38
Reading characteristic 00002a26-0000-1000-8000-00805f9b34fb
gatt.readCharacteristic(00002a26-0000-1000-8000-00805f9b34fb)
get serial number bcb92300c4e8
Read Response received from 00002a26-0000-1000-8000-00805f9b34fb, value: (0x) 32-2E-30-6A
gatt.setCharacteristicNotification(0734594a-a8e7-4b1a-a6b1-cd5243059a57, true)
setCharacteristicNotification() - uuid: 0734594a-a8e7-4b1a-a6b1-cd5243059a57 enable: true
Enabling notifications for 0734594a-a8e7-4b1a-a6b1-cd5243059a57
gatt.writeDescriptor(00002902-0000-1000-8000-00805f9b34fb, value=0x01-00)
get firmware 2.0j
Request timed out
Disconnecting...
gatt.disconnect()
cancelOpen() - device: XX:XX:XX:XX:EB:C4
onClientConnectionState() - status=0 clientIf=7 device=B9:BC:00:23:EB:C4
[Callback] Connection state changed with status: 0 and new state: 0 (DISCONNECTED)
Disconnected
gatt.close()
close()
unregisterApp() - mClientIf=7
conextion fail XX:XX:XX:XX:EB:C4 and status -1
KevinMartinezC commented 4 months ago
 override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
        val switchService = gatt.getService(BlinkySpec.SWITCH_SERVICE)
        firmwareRevisionCharacteristic = switchService?.getCharacteristic(BlinkySpec.FIRMWARE_CHARACTERISTIC)
        serialNumberCharacteristic = switchService?.getCharacteristic(BlinkySpec.SERIAL_CHARACTERISTIC)

        val sppService = gatt.getService(SPP_UUID)
        readChar = sppService?.getCharacteristic(READ_UUID)
        txCtlChar = sppService?.getCharacteristic(TX_CTL_UUID)
        writeChar = sppService?.getCharacteristic(WRITE_UUID)

        return switchService != null && firmwareRevisionCharacteristic != null && serialNumberCharacteristic != null &&
                sppService != null && readChar != null && txCtlChar != null && writeChar != null
    }
KevinMartinezC commented 4 months ago

The part to get the key stop working on AP while it works on the switch . In AP it works sometimes in android 14 but it does not work in android 12 while switch works in both

KevinMartinezC commented 4 months ago

@philips77 could you provide me an example how it would be the correct way to enable the notifications and read and write properties correctly please in this case i just want to keep listened the notifications from readChar

philips77 commented 4 months ago

I commented your code. It should work. If it works with the switch, but doesn't work with AP, you have an issue on the AP side, not here.

    @OptIn(ExperimentalCoroutinesApi::class)
    override fun initialize() {
        requestMtu(517).enqueue() // <- 517 instead of 508, as this is the value requested by Android 14 (it actually ignores the parameter)
        // Enable notifications for the button characteristic.
        setNotificationCallback(txCtlChar)
        enableNotifications(txCtlChar).enqueue()

        readCharacteristic(readChar).enqueue() // <- Do you need to do this? You ignore the received value here
        // Read the initial value of the write characteristic.
        readCharacteristic(writeChar).enqueue() // <- Same here.

        enableNotifications(firmwareRevisionCharacteristic).enqueue() // <- You never set notification callback for this char.

        // Read the initial value of the button characteristic.
        readCharacteristic(firmwareRevisionCharacteristic).with(buttonCallback).enqueue() // <- button callback?

        // Read the initial value of the LED characteristic.
        readCharacteristic(serialNumberCharacteristic).with(ledCallback).enqueue() // <- LED callback?

        readCharacteristic(serialNumberCharacteristic).with { device, data -> // <- you read serial again, this time with another callback
            val serialNumber = data.getStringValue(0)
            serialNumberValue = serialNumber
            Log.wtf("kevin", "get serial number $serialNumber")
            // Handle serial number value
        }.enqueue()

        readCharacteristic(firmwareRevisionCharacteristic).with { device, data -> // <- You read this char for the second time, now with a different callback
            val firmwareRevision = data.getStringValue(0)
            firmware = firmwareRevision
            Log.wtf("kevin", "get firmware $firmwareRevision")
            // Handle firmware revision value
        }.enqueue()

        setNotificationCallback(readChar).with { device, data ->
            val value = data.value
            Log.wtf("kevin","get data value $value")
            if (value != null) {
                collectDataKey(value)
            }
        }

        enableNotifications(readChar)
            .fail { device, status ->
                Log.wtf("kevin","connection fail $device and status $status")
            }
            .done {
                sendCredits() // <- Mind, that you'll get `onDeviceReady()` before the result to this method, as it's called after initialization is complete
            }.enqueue()

     sendCredits() // <- You could move the method here, so it's part of the initialization
    }
KevinMartinezC commented 4 months ago

Thank you! I will update it with the comments you added and test it

KevinMartinezC commented 4 months ago

@philips77 I get this error message when i try to connect an AP using the nRf CONNECT APP Screenshot_20240411-085954

KevinMartinezC commented 4 months ago

Setting the value of the mtu requestMTU(517).enqueue() it makes it works on android 14 and 13 but it does not work on android 12

KevinMartinezC commented 4 months ago

Do i need to do a different implementation for android 12?

philips77 commented 4 months ago

No, it should work the same way for any Android 5+ version.

Regarding that error, seems like Android tries to do service discovery or connection param update using Group Type which your device may not support. Perhaps that may be ignored..?

KevinMartinezC commented 4 months ago

Is it a way to disable it? or that would be an issue ?

KevinMartinezC commented 4 months ago

When I try to connect with Android 12 i get these logs:

unregisterApp() - mClientIf=8
Connecting...
gatt = device.connectGatt(autoConnect = false, TRANSPORT_LE, LE 1M)
connect() - device: B9:BC:00:23:1F:14, auto: false
registerApp()
registerApp() - UUID=cfba8dbc-6cd7-476b-976b-c1d8c9f0801a
onClientRegistered() - status=0 clientIf=8
onClientConnectionState() - status=0 clientIf=8 device=B9:BC:00:23:1F:14
[Callback] Connection state changed with status: 0 and new state: 2 (CONNECTED)
Connected to B9:BC:00:23:1F:14
wait(300)
Discovering services...
gatt.discoverServices()
discoverServices() - device: B9:BC:00:23:1F:14
Request timed out
Disconnecting...
gatt.disconnect()
cancelOpen() - device: B9:BC:00:23:1F:14
onClientConnectionState() - status=0 clientIf=8 device=B9:BC:00:23:1F:14
[Callback] Connection state changed with status: 0 and new state: 0 (DISCONNECTED)
Disconnected
gatt.close()
close()
unregisterApp() - mClientIf=8
KevinMartinezC commented 4 months ago

I solved it! I increased the connection timeout for Android 12 version. I really appreciate your comments and help

philips77 commented 4 months ago

Great to hear that!

KevinMartinezC commented 4 months ago

@philips77 I have a question for example I have two devices and I want to connect each one when I click but they are in the same ViewModel so it will not navigate to another screen. So, when I connect the first one, it works perfect but when I try to connect the second one after finishing adding the first device, I am not able to connect even if after setting up the first one I disconnect it. Should I stop the scan and start doing another scan again?

KevinMartinezC commented 4 months ago

this is my current implementation, should i add another method to be able to disconnect and connect a new device ?

override fun release() {
        // Cancel all coroutines.
        scope.cancel()

        val wasConnected = isReady
        // If the device wasn't connected, it means that ConnectRequest was still pending.
        // Cancelling queue will initiate disconnecting automatically.
        // If the device was connected, we have to disconnect manually.
        if (wasConnected) {
            disconnect().enqueue()
        }
    }
KevinMartinezC commented 4 months ago

for the seconde device when i get into here detect it as connect it and it does complete the process. It is like still has notifications of the previous one

    setNotificationCallback(readChar).with { device, data ->
            val value = data.value
            if (value != null) {
                collectDataKey(value, address = device.address)
            }
        }
    private fun collectDataKey(value: ByteArray?, address: String) {
        value?.size?.let {
            rxCredits += it
        }
        if (value != null) {
            rxData += value
            Timber.tag("ALTA-CHANGED-KEY").d("Value ${value.size}")
        }
        Timber.tag("ALTA-CHANGED-KEY").d("RXData ${rxData.size}")
        if (rxData.size < THREE_INT) return

        val total: Int =
            rxData.copyOfRange(ONE_INT, THREE_INT).fold(ZER0_INT) { a, b -> (a shl 8) + b.toInt() }

        if (rxData.size < total + THREE_INT) return
        val pktType = rxData[ZER0_INT]
        val payload = rxData.copyOfRange(THREE_INT, THREE_INT + total)
        rxData = rxData.copyOfRange(THREE_INT + total, rxData.size)

        when (pktType.toUByte()) {
            ALTA_PUB_KEY -> {
                pubKey = payload
                val pubKeyStr = pubKey.decodeToString()

                pupkey = pubKeyStr
                Timber.tag(ALTA_BLE_TAG).wtf("get pup key %s", pubKeyStr)
                if (pubKey.isNotEmpty()) {
                    pubKeyValue.value = pubKeyStr
                }
                Timber.tag("ALTA-CHANGED-KEY").d(pubKeyStr)
            }

            ALTA_STATE -> {
                val state = payload[ZER0_INT].toInt()
                val inet = if (state and (ONE_INT shl ZER0_INT) != ZER0_INT) INET else EMPTY_STRING
                val mesh = if (state and (ONE_INT shl ONE_INT) != ZER0_INT) MESH else EMPTY_STRING
                val cloud = if (state and (ONE_INT shl TWO_INT) != ZER0_INT) CLOUD else EMPTY_STRING
                val setup =
                    if (state and (ONE_INT shl THREE_INT) != ZER0_INT) SET_UP else EMPTY_STRING
                if (state and (ONE_INT shl THREE_INT) != ZER0_INT) {
                    Log.wtf("kevin","completed")
                    isConnected.value = true
                }
                Timber.tag("ALTA-STATUS").d("state: $inet $mesh $cloud $setup")
            }

            else -> {}
        }
    }

but i am resetting the values before connect into another device

 override fun resetValues (){
        disableNotifications(readChar).enqueue()
        rxCredits = 0x1000
        pupkey = null
        txData = ByteArray(0)
        pubKey = ByteArray(0)
        isConnected.value = false
        firmwareVersion.value = null
        serialNumber.value = null
        pubKeyValue.value = null
        onServicesInvalidated()
        release()
    }
override fun onServicesInvalidated() {
        serialNumberCharacteristic = null
        firmwareRevisionCharacteristic = null
        txCtlChar = null
        readChar = null
        writeChar = null
    }`
KevinMartinezC commented 4 months ago

@philips77 do i need to use another method to clear the notifications?

philips77 commented 4 months ago

Instead of reseting values, like you do, call:

disconnect().enqueue()

This will disconnect the device and will call onServicesInvalidated() automatically when done. There you may reset your values. I think you're now only disconnecting when the ViewModel gets closed, so when you leave the screen, not when you want to connect to another device.

Also, no need to disable notifications, as you're disconnecting after all.

KevinMartinezC commented 4 months ago

@philips77 I solved it. Thanks. I have another question using that library is able to connect to multiple device at the same time when it is discover by the scanner one by one ?

philips77 commented 4 months ago

Yes, it is possible. You need to have one BleManager instance per device. I think modern phones support up to 6 simultaneous connections.

KevinMartinezC commented 4 months ago

Thank you. I will try it!