As part of our recent moves toward better fitness, my partner & I picked up a secondhand exercise bike in the fall. Nice enough bike, but batteries were left in the display module when it went into storage, so it was dead when we got it. Enter the Arduino nerd; I set out to build a replacement module.
First task was to figure out the cadence sensor. On this bike it's connected via a 3.5mm TS plug. I overthought this one an awful lot; I was thinking some sort of magnetic-sensing coil or 1-wire hall-effect sensor or something; so I did a bunch of awkward trial and error with my multimeter before I found mention of bikes using a magnetic reed switch. So simple! Magnet on the flywheel and a reed switch in the housing; at a certain point in the rotation the switch closes. Dead simple.
In firmware, I set the cadence pin mode to INPUT_PULLUP and attach an interrupt handler on the falling edge. The handler sets a global variable to the current timestamp and starts the rotation timer. In the main loop, we check that global flag; if it's set then we copy the value to a local variable and clear the global, then do our “on rotation” stuff: increment the distance, calculate the time for a rotation and invert that to get the rpm, and we EMA the new rpm into our moving average.
The first prototype hardware was a little over complicated; I used an external pull-down resistor for the cadence sensor; in the current revision I use the Arduino's internal pull-up.
I used Fritzing to design the perfboard layout. There are some differences between the Fritzing and real-life layouts; the real board only allows one wire per hole, so there are some nasty big solder bridges in real life, where the design shows nicely-tidy joints like the nest of ground connections near the middle.
Perfboard is kind of nasty to work with – you end up making a lot of solder busses, and if you use both sides you can end up with surface-mount sadness mixed in.
Because of space constraints, it wasn't practical to mount the Arduino right on the board. What I settled on was a wiring harness to a pair of 8-pin Dupont headers. I had some trouble with the headers for the input wires, and currently they are bare socket headers shoved onto the pins, while the lower header still has its 8-pin shroud. If this proves unstable and wiggles loose, my next plan is to take the headers off the wires and the nano, and solder the harness in directly.
Currently, the display shows all available information (current rpm & speed, riding time, and distance) at once, but I'm thinking to make separate screens with larger (and more readable) text, using the front panel button to toggle through like the original unit did. I'm also planning a kmh/mph toggle (long-press the button to switch).
I've published the code and Fritzing files On GitLab; I welcome any feedback, suggestions, or questions. If you build your own, let me know!