I was able to get the valid "Indoor Bike Data" values by using "nRF Connect for Mobile".
- Instantaneous Speed in (km/h)
- Instantaneous Cadence in (/min)
- Resistance Level in (unitless)
- Instantaneous Power in (W)
Issue is when I try to get "Indoor Bike Data" values using web bluetooth, I get the data in a DataView format which I am not sure how to parse the understandable values from.
I read some other stack overflow answers and did some random guesses and was able to get the "Resistance Level" by using below code
dataView.getInt16(6, true)
Not sure why using 6 and true was able to get the "Resistance Level"
I tried random numbers but was not able to get valid looking number for
- Instantaneous Speed in (km/h)
- Instantaneous Cadence in (/min)
- Instantaneous Power in (W)
Can I get help parsing above three numbers by parsing the dataView input that I am getting from indoor bike BLE device?
Thanks!
Below is the code for how I got dataView from indoor bike BLE device.
const FITNESS_MACHINE_SERVICE_UUID = "00001826-0000-1000-8000-00805f9b34fb";
const INDOOR_BIKE_DATA_UUID = "00002ad2-0000-1000-8000-00805f9b34fb";
const handleClick = async () => {
const indoorBikeDevice = await navigator.bluetooth.requestDevice({
filters: [{ name: "MG03" }],
optionalServices: [FITNESS_MACHINE_SERVICE_UUID],
});
if (!indoorBikeDevice.gatt) return;
const server = await indoorBikeDevice.gatt.connect();
const service = await server.getPrimaryService(FITNESS_MACHINE_SERVICE_UUID);
const characteristic = await service.getCharacteristic(INDOOR_BIKE_DATA_UUID);
characteristic.addEventListener(
"characteristicvaluechanged",
async (event) => {
const dataView = (event.target as any).value as DataView;
console.log("dataView: ", dataView);
const resistanceLevel = dataView.getInt16(6, true);
console.log("resistanceLevel: ", resistanceLevel);
}
);
characteristic.startNotifications();
};
BELOW IS AFTER LOOKING AT RESPONSE FROM @Michael Kotzjan
I looked at the link @Michael Kotzjan provided and after few trials I was able to get flags by running code below
// GATT_Specification_Supplement_v8.pdf
// 3.124.1 Flags field: The bits of this field are defined below.
for (let i = 0; i < 16; i++) {
console.log("flags[" + i + "] = " + !!((flags >>> i) & 1));
}
console.log looked like below:
// flags[0] = false
// flags[1] = false
// flags[2] = true (Instantaneous Cadence present)
// flags[3] = false
// flags[4] = false
// flags[5] = true (Resistance Level present)
// flags[6] = true (Instantaneous Power present)
// flags[7] = false
// flags[8] = false
// flags[9] = false
// flags[10] = false
// flags[11] = false
// flags[12] = false
// ...
It seems like above true flag values are telling me that Instantaneous Cadence present, Resistance Level present, and Instantaneous Power present are available.
My issue was getting the value of those field and matching the value to the data from "nRF Connect for Mobile".
I blindly guessed numbers without any understanding and was able to match output numbers to "nRF Connect for Mobile" with the code below
characteristic.addEventListener(
"characteristicvaluechanged",
async (event) => {
const dataView = (event.target as any).value as DataView;
const instantaneousCadence = dataView.getUint16(3, true) / 512;
const resistanceLevel = dataView.getUint8(6);
const instantaneousPower = dataView.getInt16(8, true);
console.log(
[instantaneousCadence, resistanceLevel, instantaneousPower].join("|")
);
}
);
Even if I got the desired number, I still want to know why it worked?
For example, for the cadence: dataView.getUint16(3, true) / 512 why is the byte offset: 3 and I need to divided by 512? to get the rev/min?
byte offsets for resistance level and power are 6 and 8 and I am not sure where and how to get byte offsets?
You can search for your service in the Assigned Numbers Document provided by the Bluetooth SIG. Your
INDOOR_BIKE_DATA_UUIDis a standard UUID with the 16-Bit representation of 0x2ad2. The Assigned Numbers Document shows this UUID as Indoor Bike Data, which is part of the Fitness Machine Service. The service specification contains a section regarding Indoor Bike Data:That means you need to read out the flags field to figure out which data fields are present on your bike and handle them accordingly. All information about the types and lengths of the data fields can be found in the documentation.