Android Question BLE2 - How to read a Bluetooth characteristic descriptor?

emexes

Well-Known Member
Licensed User
The 0x2902 descriptor is written to by SetNotify, and that is working beautifully.

But there are other descriptors (per https://www.bluetooth.com/specifications/gatt/descriptors ) and I cannot work out how to read descriptor 0x2904 aka "Characteristic Presentation Format". The descriptor shows up in a BLE Analyser app, alongside the 0x2902 notify descriptor for the same characteristic.

The descriptor is of fixed format and length (7 bytes). It describes the value returned by the characteristic, eg: what it is, and the units and scaling ("exponent"). In my case currently, it is a pressure measurement in PSI, ie 0x27a5 per https://www.bluetooth.com/specifications/assigned-numbers/units

Previously I've just hard-coded this information into my apps, but what's happened now is that the sensor manufacturer has updated the device and my readings with the new sensors are a tenth of what they should be. It turns out the scaling of the pressure reading has been changed. I didn't see that coming but, to be fair to the manufacturer, the new devices are indeed compatible with the old devices, provided that the scaling is read from the device. I have temporarily worked around the issue by identifying old/new sensors using their serial number, but I just know that's going to bite me on future device updates.
 
Last edited:

emexes

Well-Known Member
Licensed User
That was an impressively quick response! Sadly: already done, several times. Plenty of discussion about writing descriptor 0x2902 (solved by addition of SetNotify to the library) but nothing about reading other descriptors. Is there a specific post in that thread that explains reading descriptors (not characteristics) that I've missed?

There was a similar query thread previously:

https://www.b4x.com/android/forum/t...w-and-read-descriptor-in-charateristic.83690/

but it seems abandoned, and because it was a multi-pronged query, I thought it would be clearer to begin afresh.
 

emexes

Well-Known Member
Licensed User
My problem is with trying to read a characteristic descriptor, not a characteristic value. A characteristic can have multiple descriptors, and ReadData only returns the characteristic values, not characteristic descriptors.

It looks like people had a similar problem reading 0x2803, which is now done using BLE2:GetCharacteristicProperties. Likewise writing 0x2902, which is now done using BLE2:SetNotify. I would like to read 0x2904, but I cannot find a function in BLE2 that will do it. I am hoping that there is an internal/unpublished function in BLE2 that is used by GetCharacteristicProperties that I could use, if only I knew its name.

Once I have this app done, I have to port/rewrite it using B4i, so it would be great if the solution worked similarly there as well.
 

Erel

Administrator
Staff member
Licensed User
Get the BluetoothGattCharacteristic object:
B4X:
Dim jo As JavaObject = BleManager1
Dim ser As JavaObject = jo.RunMethod("getService", Array(ServiceId))
Dim chrc As JavaObject = jo.RunMethod("getChar", Array (ser, CharacteristicId))
Read the descriptors:
B4X:
Dim descriptors As List = chrc.RunMethod("getDescriptors", Null)
For Each desc As JavaObject in descriptos
  Log("UUID: " & desc.RunMethod("getUuid", Null))
  Dim value() As Byte = desc.RunMethod("getValue", Null)
 'use bc.HexFromBytes to log it  
Next
 

emexes

Well-Known Member
Licensed User
Bonza! Thanks for that, and I'll get back to you on Tuesday when I have a chance to test it.
 

emexes

Well-Known Member
Licensed User
The JavaObject code above got me tantalisingly close to the descriptor: I can see its UUID returned by the getUuid method, but the getValue method seems to be coming back empty - accessing the Value byte array generates an "uninitialised array" error dump, and if I log the method result directly, the method appears to be returning "null".

I think it is something to do with not having done this beforehand:

https://developer.android.com/refer...or(android.bluetooth.BluetoothGattDescriptor)

I will dig around the forum and see if I can derive a magical incantation by myself (but am wide open to hints from anybody more fluent in Java than me, ie, most everyone ;-)
 
Last edited:

emexes

Well-Known Member
Licensed User
Breaking news: I think we may be on the home stretch here, I've just got Value.Length = 7, which is the expected number of bytes.

gatt = gatt.GetField("gatt") made me laugh... I never would have gatt that by myself, you've just saved me hours of frustration.

Righto, now to find out if that magical incantation has indeed actually worked.
 

emexes

Well-Known Member
Licensed User
Super, can read the descriptor now, although admittedly I don't understand half of how that code works (yet). Takes 1 to 3 seconds to read, am using a try..catch in a 100 ms loop until getValue returns something.

From log:

DeviceId = D3:CC:CB:F7:AC:09
ServiceId = cc4a6a80-51e0-11e3-b451-0002a5d5c51b
CharacteristicId = 835ab4c0-51e4-11e3-a5bd-0002a5d5c51b
DescriptorId =00002904-0000-1000-8000-00805f9b34fb
Value.Length = 7
Value = 0EFFA527010000

where descriptor 0x2904 aka "Characteristic Presentation Format" describes the sensor's pressure characteristic:

0E = reading is a signed 16-bit integer
FF = exponent is -1 = scale/multiply 16-bit reading by 10^-1 (ie, reading is in 0.1 PSI)
A527 = 0x27A5 = reading is in org.bluetooth.unit.pressure.pound_force_per_square_inch
010000 = description "unknown"
 
Last edited:
Top