Android Question BLE2.SetNotify() NullPointer exception

microfan

Member
I am trying to interface a smartphone to a BLE server using the BLE2 library.

After a confusing start, I am now able to connect, discover etc. I am able to send data (it was very easy) and receive data (less easy).

The problem is that I don't want to poll the device, I want to be notified when the data (characteristic) varies.

So, I try to use SetNotify() after connecting:

B4X:
Sub theble_Connected (services As List)
    Log("Connected")
    For Each st As String In services
        Log("  service " & st)
        If st="96ded887-592d-48e1-9fe4-903046afff16" Then
            ' theble.ReadData2(st, "bb7b236e-eeef-42c5-ab0d-3305051c5c49")
        End If
    Next
    theble.SetNotify("96ded887-592d-48e1-9fe4-903046afff16", "bb7b236e-eeef-42c5-ab0d-3305051c5c49", True)
End Sub

When calling SetNotify() the following exception is reported in the log:
Error occurred on line: 87 (B4XMainPage)
java.lang.NullPointerException
at anywheresoftware.b4a.objects.BleManager2.setNotify(BleManager2.java:355)
at anywheresoftware.b4a.objects.BleManager2.SetNotify(BleManager2.java:344)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at anywheresoftware.b4a.shell.Shell.runVoidMethod(Shell.java:777)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:354)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:157)
at anywheresoftware.b4a.BA$2.run(BA.java:395)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5476)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
at dalvik.system.NativeStart.main(Native Method)

I can not understand what I am missing here. the UUIDS for service and characteristics are correct, in fact I can send and receive.

Is there something else to do before using SetNotify()? I tried also to invoke it in the DataAvailable event handler, but the result is the same.
 

JordiCP

Expert
Licensed User
Longtime User
Perhaps you already know, but just in case: not all characteristics can throw notifications or indications. It is the same peripheral device which implements them as read-only, write-only, read-notify, or other combinations. So, you will only be able to set .SetNotify() to a characteristic that allows this
 
Upvote 0

microfan

Member
Perhaps you already know, but just in case: not all characteristics can throw notifications or indications. It is the same peripheral device which implements them as read-only, write-only, read-notify, or other combinations. So, you will only be able to set .SetNotify() to a characteristic that allows this
TY. It happens that I also wrote the other end, i.e. the characteristic. And it has (or it should) have notify. Now, I should understand where the problem is. Given that the char. had not notify, is it normal to have an exception? It seems strange to me...

Is there another way to test this?
 
Upvote 0

JordiCP

Expert
Licensed User
Longtime User
You could check it (if the given characteristic does indeed allow notifications) with Nordic nRF Connect app, or any other similar one.

In case it is already there, you can subscribe to it (in the same app) and check that you are receiving updated data -> if this is the case, we know that your device is behaving as expected.

Regarding the crash I don't know if this is the cause, it could be. I've seen some posts where it is advised to add a Sleep() before the .SetNotify(). In some of my apps I do only perform a Read when connected, and when the DataAvailable event is raised, after checking that the data comes from that service and characteristic, only then I do the . SetNotify() (checking that it is only raised once per connection, perhaps this later filter is not needed).
Hope it helps.
 
Upvote 0

microfan

Member
You could check it (if the given characteristic does indeed allow notifications) with Nordic nRF Connect app, or any other similar one.

In case it is already there, you can subscribe to it (in the same app) and check that you are receiving updated data -> if this is the case, we know that your device is behaving as expected.

Regarding the crash I don't know if this is the cause, it could be. I've seen some posts where it is advised to add a Sleep() before the .SetNotify(). In some of my apps I do only perform a Read when connected, and when the DataAvailable event is raised, after checking that the data comes from that service and characteristic, only then I do the . SetNotify() (checking that it is only raised once per connection, perhaps this later filter is not needed).
Hope it helps.
TY, I will check better as you say, but the null pointer exception still is awful. I will update this thread with news.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
The exception happens at this codeblock in the BLE2 library:
Ble2 code snippet:
/* https://github.com/AnywhereSoftware/B4A/blob/fa934475f597d2a8ea61296cad3b052fabef78f5/Libs_BLE2/src/anywheresoftware/b4a/objects/BleManager2.java#L341C2-L371C3 */
    public boolean SetNotify(String Service, String Characteristic, boolean Notify) {
        BluetoothGattService ser = getService(Service);
        BluetoothGattCharacteristic chr = getChar(ser, Characteristic);
        return setNotify(chr, Notify, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    }
    
    @Hide
    public boolean setNotify(BluetoothGattCharacteristic chr, boolean Notify, byte[] descriptorValue) {
        if (!gatt.setCharacteristicNotification(chr, Notify))
            throw new RuntimeException("Error changing notification state: " + chr.getUuid());
        else {
            BluetoothGattDescriptor descriptor = chr.getDescriptor(notifyDescriptor);
            boolean res;
            if (Notify) {
                res = descriptor.setValue(descriptorValue);
                BA.Log("Setting descriptor. Success = " + res);
            }
            else {
                res = descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
            }
            if (res) {
                boolean res2 = gatt.writeDescriptor(descriptor);
                BA.Log("writing descriptor: " + res2);
                return res2;
            }
            else {
                return false;
            }

        }
    }

Line 355 in your stack trace is line 16 above (344 is line 5). The cause for this is line 13, where the library attempts to get the "notify descriptor" for that particular characteristic. The value for notifyDescriptor is set on line 77 as
BLE2 code snippet #2:
public UUID notifyDescriptor = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");

Looking at Android documentation, for the getDescriptor method of a BluetoothGattCharacteristic, we get the following:

getDescriptor ( https://developer.android.com/refer...tCharacteristic#getDescriptor(java.util.UUID) )
Added in API level 18

public BluetoothGattDescriptor getDescriptor (UUID uuid)

Returns a descriptor with a given UUID out of the list of descriptors for this characteristic.

Parameters
uuid UUID

Returns
BluetoothGattDescriptor GATT descriptor object or null if no descriptor with the given UUID was found.
I bolded the relevant part. It is Android's library that returns the Null value. The BLE2 code then tries to call a "setValue" method on that Null object, at which point you end up with the exception you posted above.
 
Upvote 0
Top