Here is today's installment of "What Colin did in lockdown today".
I was teaching someone to program yesterday, and we made a bike computer using a Pixl.js to display speed and distance on the Pixl display using an sensor from an old broken wired Cateye bike computer we had. This sensor is a reed switch and a magnet.
I thought "I have a Puck.js with Bluetooth and magnetometer" so had a go at making a sensor for my phone using the Puck.
When running this code the Puck appears as a bluetooth cycling sensor ("CSC profile") to use the jargon and can be read by apps on a mobile phone. I tried "Jepster" on Android and the nRF toolbox app.
The theory is that the Puck could be mounted on the front fork of a bicycle, with a magnet on a spoke. When you bring the magnet nearby the LED on the Puck lights, and a reading is sent to the phone. When the magnet rotates away the LED goes off.
There is a commented out bit of code that uses setWatch so the same code could run with a bike computer sensor - this probably gives longer battery life than using the magnetometer.
I did discover if you alter the time between pulses the app seems quite happy to believe I can cycle at 80,000 km/h - take that, competitive Strava users :-)
I haven't actually tested this on a bike, but I think it works. Posting here in case it is of interest to anyone!
/*
Espruino code for Bluetooth Low Energy CSC (Cycling and Cadence Sensor) profile
https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.csc_measurement.xml
Data is returned via the Measurement characteristic:
flags = 0x01 = Wheel data; 0x02 = Crank data; OR together for both
uint32 = count of wheel revolutions;
uint16 = last wheel event time in units of 1/1024 seconds
uint16 = count of crank revs
uint16 = crank event time
Usage:
csc=create_csc_service()
At each wheel revolution
csc(time_in_seconds)
...each call to this will notify the phone etc
*/
function create_csc_service() {
// Dataview and buffer to hold the measurement data
var buffer = new ArrayBuffer(11);
var dv = new DataView(buffer, 0);
var revs=0;
// Given # wheel revs, time in seconds, format the buffer
// for the CSC measurement characteristic.
function format_csc(revs,wtime) {
var time_csc_units = wtime*1024; // Convert to units of 1024th of a second
dv.setInt8(0, 0x01); // Only wheel data is available => 01
dv.setUint32(1, revs, little_endian=true);
dv.setUint16(5, time_csc_units, little_endian=true);
dv.setUint16(7, 0); // Crank revs not used
dv.setUint16(9, 0); // Crank time not used
return buffer;
}
// Update the characteristic. Call once per revolution passing
// time in seconds. This is our "public" interface
function revolution(time_secs) {
revs+=1;
format_csc(revs,time_secs);
// Now notify...
NRF.updateServices({
0x1816 : { // CSC UUID
0x2A5B: { // Measurement UUID
value : buffer,
readable : true,
notify : true,
}
}
});
}
// Start - advertise that we're a CSC
NRF.setServices({
0x180F : { // Battery service UUID
0x2A19: { // Battery value UUID
value: [90], // Battery level percentage
readable : true,
},
},
0x1816 : { // CSC UUID
0x2A5B: { // Measurement UUID
value : format_csc(0,0),
readable : true,
notify : true,
},
0x2A5C: { // Feature UUID - need to fully understand this
value: [ 0x01,0x01],
readable: true,
}
}
}, { advertise: [ '180F', '1816' ], uart: false});
return revolution;
}
// Version with ex bike computer magnet+reed sensor on GPIO pin D13
//pinMode(D13,"input_pullup");
//setWatch(function(e) {
// csc(e.time);
//},D13,{edge: 'falling', repeat:true, debounce:20});
// Version for Puck.js using its in-built magnetometer
// Two values for hysteresis. Determine mag reading for magnet near and far.
// Might need to change this depending on magnet or bicycle positioning
MAGNET_NEAR_VALUE=150000;
MAGNET_FAR_VALUE=130000;
function on_mag(reading) {
reading.x -= mag_zero.x;
reading.y -= mag_zero.y;
reading.z -= mag_zero.z;
// Pythagoras theorem, but no need to
// sqrt as we're only interested in
// change
var magnitude = reading.x*reading.x
+ reading.y*reading.y
+ reading.z*reading.z;
// Magnet detected near?
if (magnitude>MAGNET_NEAR_VALUE && !magnet_near) {
magnet_near=true;
LED.write(1);
csc(getTime()); // Update the bluetooth service
}
// Magnet moved away?
if (magnitude<MAGNET_FAR_VALUE && magnet_near) {
magnet_near=false;
LED.write(0);
}
}
function onInit() {
csc = create_csc_service();
mag_zero = Puck.mag(); // Get base case for magnetometer reading
magnet_near = false;
Puck.on('mag', on_mag);
Puck.magOn(80); // 80Hz sample rate
}
Espruino is a JavaScript interpreter for low-power Microcontrollers. This site is both a support community for Espruino and a place to share what you are working on.
Hello,
Here is today's installment of "What Colin did in lockdown today".
I was teaching someone to program yesterday, and we made a bike computer using a Pixl.js to display speed and distance on the Pixl display using an sensor from an old broken wired Cateye bike computer we had. This sensor is a reed switch and a magnet.
I thought "I have a Puck.js with Bluetooth and magnetometer" so had a go at making a sensor for my phone using the Puck.
When running this code the Puck appears as a bluetooth cycling sensor ("CSC profile") to use the jargon and can be read by apps on a mobile phone. I tried "Jepster" on Android and the nRF toolbox app.
The theory is that the Puck could be mounted on the front fork of a bicycle, with a magnet on a spoke. When you bring the magnet nearby the LED on the Puck lights, and a reading is sent to the phone. When the magnet rotates away the LED goes off.
There is a commented out bit of code that uses setWatch so the same code could run with a bike computer sensor - this probably gives longer battery life than using the magnetometer.
I did discover if you alter the time between pulses the app seems quite happy to believe I can cycle at 80,000 km/h - take that, competitive Strava users :-)
I haven't actually tested this on a bike, but I think it works. Posting here in case it is of interest to anyone!