0

i am experimenting in reading bluetooth characteristics values from my indoor bike trainer. I can connect and i get callbacks on the CharacteristicValueChanged but after a lot of trying i cannot retrieve meaningful data from the byte array the value holds.

I am aware there are bluetooth specifications but shoot me, i am unable to retrieve readable data.

Can anyone assist me how to get real values that are defined here https://github.com/oesmith/gatt-xml/blob/master/org.bluetooth.characteristic.indoor_bike_data.xml from a byte array ?

Things i tried:

private static void Chars_CharacteristicValueChanged(object sender, GattCharacteristicValueChangedEventArgs e)
    {
        var flags = e.Value[0];
        var test = BitConverter.ToSingle(e.Value, 4);
        Console.WriteLine(flags);
        Console.WriteLine(test);
        Console.WriteLine(e.Value.GetValue(4));
        //for (var i = 0; i < 13; i++)
        //{
        //    Console.WriteLine(i + ": " + BitConverter.ToBoolean(e.Value, i));
        //}

        //Console.WriteLine("Instant Speed: " + BitConverter.ToUInt16(e.Value, 14));
        //Console.WriteLine("Average Speed: " + BitConverter.ToUInt16(e.Value, 15));
        //Console.WriteLine("Instantious Cadence: " + BitConverter.ToUInt16(e.Value, 16));
        //Console.WriteLine("Average Cadence: " + BitConverter.ToUInt16(e.Value, 17));
        //Console.WriteLine("Total Distance: " + BitConverter.ToInt32(e.Value, 18));
        //Console.WriteLine("Resistance Level: " + BitConverter.ToUInt16(e.Value, 19));
        //Console.WriteLine("Instantaneous Power: " + BitConverter.ToUInt16(e.Value, 20));
        //Console.WriteLine("Average Power: " + BitConverter.ToUInt16(e.Value, 21));
        //Console.WriteLine("Unknown22: " + BitConverter.ToUInt16(e.Value, 22));
        //Console.WriteLine("Unknown23: " + BitConverter.ToUInt16(e.Value, 23));
        //Console.WriteLine("Unknown24: " + BitConverter.ToUInt16(e.Value, 24));
        //Console.WriteLine("Unknown25: " + BitConverter.ToUInt16(e.Value, 25));
        //Console.WriteLine("Elapsed Time: " + BitConverter.ToUInt16(e.Value, 26));
        //Console.WriteLine("Unknown27: " + BitConverter.ToUInt16(e.Value, 27));
        //Console.WriteLine("Unknown28: " + BitConverter.ToUInt16(e.Value, 28));
        //distance = Convert.ToUInt16(e.Value[10]);
        //Console.WriteLine("Distance= " + distance);

But no data that looks real life...

For example:

e.Value.GetValue(0) returns 254 e.Value.GetValue(1) returns 31 the others mostly 0 ...

Already a big thanks upfront!

EDIT After searching and trying to convert other code samples in other languages I found, i came up with something like this:

private static void Chars_CharacteristicValueChanged(object sender, GattCharacteristicValueChangedEventArgs e)
        {
            byte[] distanceBytes = new byte[2];
            Array.Copy(e.Value, 10, distanceBytes, 0, distanceBytes.Length);
            distanceBytes = PadBytes(distanceBytes, 4);

            var distance = Convert.ToDouble(BitConverter.ToUInt32(distanceBytes, 0).ToString());

                Console.WriteLine("Real Trainer: " + distance + "m");
}

It looks like a realistic value and most importantly, it does not go back to zero after passing the 254 value.

It seems that because of the uint24 the data is, we should take two bytes of the complete byte array (starting from index position 10).

It feels like some kind of magic, so if someone can enlighten me how to do all this correctly, i would be very grateful

Verthosa
  • 1,671
  • 1
  • 15
  • 37
  • I notice when i spin the wheel, the value at e.Value.GetValue(10) contains a number that may look like the distance, but still not sure – Verthosa Feb 07 '23 at 08:30
  • Which Bluetooth profile/service does your indoor bike trainer implement? – Risto Feb 07 '23 at 09:42
  • Hey thanks for your comment. The characteristics UUID is 2AD2 for primary service 1826 – Verthosa Feb 07 '23 at 09:50
  • 1
    Please have a look in the corresponding documentation: [GATT Specification Supplement](https://www.bluetooth.com/de/specifications/specs/gatt-specification-supplement/). On the one hand, the data structure is dynamic and does not contain all possible elements, on the other hand, most data have a size of `uint16_t` or even `uint24_t`. You have to take this into account in your deserialisation and the XML document you referenced does not seem very clear to me. – Risto Feb 07 '23 at 12:33
  • If you would show us the contents of the "characteristic", we might be able to help you with the deserialisation/interpretation of the data. – Risto Feb 07 '23 at 12:39
  • I quickly checked out the document and maybe you have seen my edit i made a couple of minutes ago. If i take the flag field into account and see that each previous property before the distance takes two bytes, this can clarify why i should start looking at byte 10. I think i should fix it and take the bytes 10 to 13 (as size seems like 13). This document helped me to check this further, big thanks! – Verthosa Feb 07 '23 at 12:41
  • Yes, I saw it after I sent my comment, apparently we were working in parallel. :-) – Risto Feb 07 '23 at 12:59

0 Answers0