6
votes

We have identified the wheel and crank sensor Gatt characteristic measurement data which we have got in our application as per the split ups in the link below. " https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.csc_measurement.xml "

For instance we have tried the following,

Hex Data : 0x03 6D010000 FC7E 2C01 F87E

Flag-03 ->0000 0011 -> 8bits so both are true hence we can get the wheel and crank's respective values.

Cumulative Wheel Revolutions- 6D 01 00 00 -> 32bits so reversing the bluetooth values in little endian i.e 00 00 01 6D and converting it in decimal we get-365

Last Wheel Event Time- FC 7E -> 16bits so reversing the bluetooth values in little endian i.e 7E FC and converting it in decimal we get-32508

Cumulative Crank Revolutions- 2C 01 -> 16bits so reversing the bluetooth values in little endian i.e 01 2C and converting it in decimal we get-300

Last Crank Event Time- F8 7E -> 16bits so reversing the bluetooth values in little endian i.e 7E F8 and converting it in decimal we get-32504

Here we used only the wheel and crank revolution value for our application so we are facing a problem that the last value of the crank or wheel is repeating even after stopping the cycle and the value continues from the previous event value Some times the values are very high and aberrant. Here I suspect Last Wheel and Crank event times will play a major role. But I am not sure about the functionality and purpose behind those time bytes. The main wheel and crank value are unit less hence we don't know what type of conversion we have to do. How can we get the right real time value for calculating the RPM and speed of the cycle when it is in motion. How should we use the last event time in our C# coding ? How can we incorporate the time event in our logic ? Kindly guide us through this process of parsing. The device I am using is the SunDing515 cycling speed and cadence sensor with Bluetooth low energy. strong text

1
Ever resolve this issue @santosh? Seeing something similarMannie

1 Answers

2
votes

I'm developing an app for a BLE unit which transmits GATT CSC measurements. What I describe below are not C# or Java specific concepts but they are generic.

  1. The revolution readings of wheel and crank are unit-less as you mention. This is because it's a simple count of revolutions. The sensor is usually fixed to the fork or onto the axle while there's a counterpart rotating simple magnet bit on the wheel or spoke and the sensor counts each time the magnet passes the detector. For my device the cumulative revolutions monotonically increase as expected.
  2. The event time data may look erratic at first glance. However when you take a closer look at the csc_measurement.xml file you can see that the unit is 1/1024 seconds. So you have to divide the values you mentioned (32508 and 32504) to get the pure second reading. However since this reading is transmitted as an UInt16, that also means that this variable overflows every 64 seconds. I don't know why this GATT characteristic was designed this way, they could have used an UInt32 (or UInt24) here just like with the wheel rotation, but this is what we have.

So it's your task to calculate the instantaneous cadence values from this data. When you also factor-in that sometimes riders may pedal with a 20-30 crank rotations per minute it's also clear that I'd need more than just a few seconds of sample data to calculate a cadence with an acceptable precision.

  1. Devices may send readings multiple times per second. You'd need to handle the cases when the last event time and / or the revolution count stays the same. You can discard that packet or maybe even deduct if it signals a workout pause event.
  2. I'm pushing the readings into a LIFO queue. This will provide a dynamic length sliding window for calculating the display cadence.
  3. Along that I set a constant of 30 seconds, which will be the size of the sliding window in time.
  4. With each reading I look at if the readings in the queue span more than 30 seconds, and if that's true I remove the oldest readings.
  5. You must handle the case when the event time value overflows, which will happen every 64 seconds. This is just a simple check when I calculate the cadence reading: if the newest reading's time value is smaller than the oldest, then I temporarily offset the newest with 64 seconds. I also have to mention that this simple logic would not work if your sliding window time gets greater than 64 seconds, because then the timer could overflow multiple times.
  6. It's not specified if the cumulative revolutions will be preserved between workouts or not. So far what I've seen is no, but I'm dealing with indoor bicycles, and it's very easy to listen to the first reading at the workout, and use this as an offset.
  7. If you are lucky then your device may supply an instantaneous or average cadence reading through the 2AD2 "Indoor Bike Data" GATT characteristic of the 1826 GATT Service. In that case you can save yourself from all of these shenanigans here.

With all that being said, if your device still supplies erratic data for event time: double check the 1024 division and overflow logic and also the sliding window. You still have a last resort to fall back to your own measured time stamps, but that backfired in my case: when the phone got into locked mode during a workout and then got unlocked again, all of the measurements flooded into the app at once rendering my timestamps invalid and my calculations became outrageous. So try to stick to the device's readings if possible.