1

Question: Phrased another way. My Intel Bluetooth controller accepts data from my Polar Bluetooth LE device and places the data on the D-bus system bus. How do I use the bluez API and D-Bus to read my Polar sensors heart rate data?

In an attempt to at least see the Polar sensor, I ran c code written by Parthiban Nallathambi at www.linumiz.com: https://www.linumiz.com/bluetooth-list-devices-using-gdbus/. Providing this for credit and background.

The code accurately displayed the Polar sensor attributes, but no data. FYI, the 1st few executions it actually did display ManufacturerData:

        Address : D2:9C:2A:C8:F9:CA
        AddressType : random
        Name : Polar H9 ADAC102E
        Alias : Polar H9 ADAC102E
        Appearance : Other
        Paired : 1
        Trusted : 1
        Blocked : 0
        LegacyPairing : 0
        Connected : 0
        UUIDs : 
                00001800-0000-1000-8000-00805f9b34fb
                00001801-0000-1000-8000-00805f9b34fb
                0000180a-0000-1000-8000-00805f9b34fb
                0000180d-0000-1000-8000-00805f9b34fb
                0000180f-0000-1000-8000-00805f9b34fb
                0000181c-0000-1000-8000-00805f9b34fb
                0000feee-0000-1000-8000-00805f9b34fb
                6217ff4b-fb31-1140-ad5a-a45545d7ecf3
        Adapter : Other
        ServicesResolved : 0 

Then I ran bluetoothctl to display vendor data in ManufacturerData:

steven@DEVELOPMENT-JETSON:~$ bluetoothctl
[NEW] Device D2:9C:2A:C8:F9:CA Polar H9 ADAC102E
[NEW] Primary Service
        /org/bluez/hci0/dev_D2_9C_2A_C8_F9_CA/service0045
        0000feee-0000-1000-8000-00805f9b34fb
        Polar Electro Oy
[NEW] Characteristic
        /org/bluez/hci0/dev_D2_9C_2A_C8_F9_CA/service000e/char000f
        00002a37-0000-1000-8000-00805f9b34fb
        Heart Rate Measurement
[bluetooth]# **connect D2:9C:2A:C8:F9:CA**
Attempting to connect to D2:9C:2A:C8:F9:CA
[CHG] Device D2:9C:2A:C8:F9:CA Connected: yes
Connection successful
[CHG] Device D2:9C:2A:C8:F9:CA ServicesResolved: yes

[Polar H9 ADAC102E]# scan on
Discovery started

[CHG] Device D2:9C:2A:C8:F9:CA RSSI: -67
[CHG] Device D2:9C:2A:C8:F9:CA ManufacturerData Key: 0x006b
[CHG] Device D2:9C:2A:C8:F9:CA ManufacturerData Value: 33 1e 33 33        3.33

I'm just baffled, I can't find any examples of c code that does the following (pseudo code):

  1. Pair to device given device ID or address
  2. Iteratively/continually read ManufacturerData where key = 0x006b
  3. Pull out heart rate data from array

Not looking for someone to write the code, but for someone to point me at the bluez/dbus functions or code if you have it :-), that will accomplish this. Thanks for you time. I'm just stumped.

I have already looked at the Bluetooth for Linux Developers Study Guide, but its in Python and I'm looking for a C guide.

Steven
  • 65
  • 5

1 Answers1

0

If you followed the example in here then you should have a function named bluez_property_value, in here you need to compare the key param with "ManufacturerData" and then extract that information using GVariant in this case is an a{qv} an array of dictionaries containing a q id and a v variant where this variant is an array of bytes ay.

void bluez_property_value(const gchar *key, GVariant *value)
{
    if (g_strcmp0(key, "ManufacturerData") != 0) 
        return;

    const guint16 id;
    GVariant *manufacturing_data = NULL;
    GVariantIter *iter;

    g_variant_get(value, "a{qv}", &iter);
    while (g_variant_iter_loop(iter, "{qv}", &id, &manufacturing_data))
    {
        const guint8 byte;
        GVariantIter *bytes;

        g_variant_get(manufacturing_data, "ay", &bytes);
        while (g_variant_iter_next(bytes, "y", &byte))
            fprintf(stdout, "%02X", byte);
        fflush(stdout)
    }
}

/**
 * @brief read a float from unsigned int, 
 * Check if the first bit is 1, if it is the number is negative
 * so we need to negate the number with the ~ operator and extract
 * a the subset of the bits from the number.
 * 
 * @param number the number to read from
 * @return double, the negative or positive value
 */
double read_negative_value(int number)
{
    if ((number & 0x8000) > 0)
        return -(double) ((~number) & 0x7FFF);
    return (double) number;
}

EDIT: Fix code format.

EDIT2: Added a function to read negative values from manufacturer data.

Zyro
  • 16
  • 1
  • 6
  • Zyro, Thanks! The code you provided exactly steered me in the correct direction. I had a few syntax changes: I added "!" on the "if" to flip the test since strcmp0 returns a zero (false) on a match, There were a few missing parentheses - I added. In addition I added #include for the fprintf, and the code worked well. Two questions I have is, why didn't you use g_print instead of fprintf? How does one convert the variable byte to a "C" type integer. I need to pass this to GTK for printing on a linux screen. Any ideas? – Steven Jul 26 '22 at 22:50
  • I used fprintf instead of g_print, because they do the same, I could also use printf instead. To convert to type integer is more complicated, you need to read a uint8 you only need `data[0]` but if you want to read a uint16 you need to shift and mask the values `((data[1] << 8) | (data[2] & 0xFF))` – Zyro Jul 27 '22 at 08:27
  • I added a new function to explain how to read negative values, this function should be called like this `double value = read_negative_value((data[1] << 8) | (data[2] & 0xFF))` – Zyro Jul 27 '22 at 10:01