async function connect(props) { const device = await navigator.bluetooth.requestDevice({ filters: [{ services: ["cycling_speed_and_cadence"] }], acceptAllDevices: false, }); console.log(`%c\nšŸ‘©šŸ¼ā€āš•ļø`, "font-size: 82px;", "Starting CSC...\n\n"); const server = await device.gatt.connect(); const service = await server.getPrimaryService("cycling_speed_and_cadence"); const char = await service.getCharacteristic("csc_measurement"); char.oncharacteristicvaluechanged = props.onChange; char.startNotifications(); return char; } function parseCSC({ value }) { const flags = value.getUint8(0, true); const hasWheel = !!(flags & 0x01); const hasCrank = !!(flags & 0x02); let index = 1; const res = { wheelRevs: null, wheelTime: null, crankRevs: null, crankTime: null, }; if (hasWheel) { res.wheelRevs = value.getUint32(index, true); index += 4; res.wheelTime = value.getUint16(index, true); index += 2; } if (hasCrank) { res.crankRevs = value.getUint16(index, true); index += 2; res.crankTime = value.getUint16(index, true); index += 2; } console.log("CSC", res); const pre = document.createElement("pre"); pre.style.border = "1px solid red"; pre.textContent = JSON.stringify(res, null, 2); document.body.appendChild(pre); return res; } function revsToRPM(t1, t2) { const deltaRevs = t2.revs - t1.revs; if (deltaRevs === 0) { // no rotation return 0; } let deltaTime = (t2.time - t1.time) / 1024; if (deltaTime < 0) { // time counter wraparound deltaTime += Math.pow(2, 16) / 1024; } deltaTime /= 60; // seconds to minutes const rpm = deltaRevs / deltaTime; return rpm; } document.querySelector("#ble-connect").addEventListener("click", () => connect({ onChange: parseCSC, }).catch(console.error) );