1

I use following code to enable notification on android device using kotlin: with this code notifications get enabled, but i am not recieving notification even when data changes on the device. I am not recieving any error, i am so confused as i am developing my first ble app. I don't know what more details i could include

    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)
    
                        }}
    
    
    //which is writing descriptor as follows:
    
    
      override fun onDescriptorWrite(
                gatt: BluetoothGatt,
                descriptor: BluetoothGattDescriptor,
                status: Int
            ) {
                with(descriptor) {
                    when (status) {
                        BluetoothGatt.GATT_SUCCESS -> {
                            Timber.i("Wrote to descriptor $uuid | value: ${value.toHexString()}")
                            if (isCccd()) {
                                onCccdWrite(gatt, value, characteristic)
                            } else {
                                listeners.forEach {
                                    it.get()?.onDescriptorWrite?.invoke(
                                        gatt.device,
                                        this
                                    )
                                }
                            }
                        }
                        BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> {
                            Timber.e("Write not permitted for $uuid!")
                        }
                        else -> {
                            Timber.e("Descriptor write failed for $uuid, error: $status")
                        }

and then on characteristic changed the following code get used:

      override fun onCharacteristicChanged(
                gatt: BluetoothGatt,
                characteristic: BluetoothGattCharacteristic
            ) {
                with(characteristic) {
                    super.onCharacteristicChanged(gatt, characteristic)
    
                    Timber.i("Characteristic $uuid changed | value: ${value.toHexString()}")
                    listeners.forEach {
                        it.get()?.onCharacteristicChanged?.invoke(gatt.device, this)
                    }
                }
    
            }

As above code is written in manager class, I am calling this with following code from activity which need to recieve notification:

      val characteristicNotify = BluetoothGattCharacteristic(
            char_uuid, BluetoothGattCharacteristic.PROPERTY_NOTIFY ,
            1
        )
    Manager.enableNotifications(device, characteristicNotify)
Mr R
  • 754
  • 7
  • 19
  • Hi @HarsimranSandhu - welcome to Stack Overflow - I suspect others may ask you for more details ... I can see you are going `listeners.forEach` to send the changes - but where are you adding the listeners? And you overridethe functions - but don't they still need to be registered [this is a non-expert comment, just a that's how that sort of stuff tends to be done ... – Mr R Apr 15 '21 at 05:47
  • Please correct your question title – Michael Rovinsky Apr 15 '21 at 07:55

2 Answers2

1

The code which subscribes to notifications looks good, because it finds available characteristic from BluetoothGatt, checks if it supports subscription, calls gatt.setCharacteristicNotification and then gatt.writeDescriptor.

It's difficult to say from your part of code, but I guess these lines don't do what you expect, because this is how you should create characteristic on Peripheral side, not Central:

// Central should get the existing characteristic from BluetoothGatt
// but this code creates a new characteristic
val characteristicNotify = BluetoothGattCharacteristic(
        char_uuid, BluetoothGattCharacteristic.PROPERTY_NOTIFY ,
        1
    )
Manager.enableNotifications(device, characteristicNotify)

Central should use characteristics provided by the discovered BluetoothGattService (available after BluetoothGattCallback.onServicesDiscovered), but not create new characteristics.

This example of subscribing to indications may be useful: BLEProof: MainActivity.kt:351 on github

Also I suggest to ensure that the Peripheral you are connecting to is sending notifications properly - using one of these Android apps: LightBlue, BLE Scanner, you can manually scan for your Peripheral, discover services and subscribe to your characteristic, just to be sure that Peripheral works as expected.

alexander.cpp
  • 747
  • 6
  • 15
  • I found out that, i receive the notification when the write operation get performed from the application: for example if i want to check voltage, i request the device by "write characteristic", then i get notified that this characteristic changed, but i want to get notified and update value on opened application layout when the voltage get changed from device side. Currently, it working as: when the voltage get changed from device, voltage layout opened on application does not get updated automatically, so to check the current voltage , i need to go back and open activity layout again – Harsimran Sandhu Apr 16 '21 at 02:25
  • Your approach works (write and wait for notification), but typically BLE applications use only Read in this case. So Peripheral has **only one characteristic which supports Read and Notify**. Central reads the characteristic (when it needs it "rigth now"), and subscribes to notifications to receive updates. Note: when Central sends Read request, Peripheral has 30 seconds to deliver the value - [here](https://stackoverflow.com/questions/54898592/bluetoothgattserver-is-always-disconnecting-after-30-seconds/54919186#54919186) is an answer about those 30 seconds. – alexander.cpp Apr 17 '21 at 12:37
  • @Harsimran Sandhu any luck? I suspect you're using the same code from the PunchThrough Ultimate BLE guide. I too am using it and facing the same problem of not being able to receive notifications on the TX Characteristic of my BLe device that is used in receiving data on my app. I only get notified when writing to the device. So any luck? Have you managed to resolve your problem? – Mohamed El Kayal Feb 03 '22 at 10:42
  • @MohamedElKayal have a look at my response below – paolo Feb 27 '23 at 19:07
0

With API level 33 (Android 13) some new bluetooth apis were added. For example the following method on BluetoothGattCallback:

public void onCharacteristicChanged (BluetoothGatt gatt, 
            BluetoothGattCharacteristic characteristic, 
            byte[] value)

If you override this method in your ble callback and run your app on an older device (i.e on android 11) you won't be notified of characteristic changes.

paolo
  • 340
  • 3
  • 7