A few months ago my legs started objecting to my running routine and I decided to purchase an exercise bike. Never having owned such a device in the past I wasn’t sure what to look for or what to expect. Somewhat naively I assumed that the bike would be similar to a treadmill, with various exercise programs to alleviate boredom and provide some variety.
As it turns out, the bike I purchased is as simple as they come. The lacklustre monochrome display shows the current speed, distance and estimated calories burnt. The resistance is controlled via a knob connected to a screw, which in turn pushes down on a magnet bar hovering over the flywheel. An exercise program is therefore a completely manual affair – use a timer on your watch/smartphone to beep after a certain interval and then rotate the knob. Not cool.
Recovering from my disappointment I decided to turn this bike into what I expected it to be.
The exercise bike is a fairly simple device – there is just one “input” (the rotational speed) and one “output” (the resistance). The first task was to figure out how the input worked. Two wires come out from the pedal area and connect to the console. An oscilloscope showed that the signal carried by these wires is digital rather than analog – it goes to 0 every time the pedals reach a certain position, marking a single rotation. The rotational speed thus needs to be calculated by measuring the time it takes to complete a single rotation. This is not very accurate and poses problems when stopping, starting and changing speeds. The distance and linear speed can also be calculated from the number of rotations and the time, though these measurements are just an extrapolation of how far and how fast a real bicycle would go under similar conditions. I followed the same approximation used by the console that came with the bike, which translates 4 RPM to 1 KM/H. On the plus side, the simplicity of the input means that it only needs to be connected to two standard GPIO pins, one configured as an input and one ground.
Controlling the resistance of the bike turned out to be a much harder problem to solve. I spent a lot of time trying to mimic the manual operation of the screw that pushes down on the magnet bar and relies on a powerful spring to bring the bar back up when the screw is rotated the other way. After many failed attempts I decided to do something else. The rig consists of
- 3/8″ threaded rod, slotted so that it doesn’t turn;
- 3D-printed hinge connecting the rod to the magnet bar;
- 3D-printed bevel gear which turns a 3/8″ nut, causing the threaded rod to move up and down;
- 12v low-speed, high-torque geared motor;
- L298 H-bridge to change the direction of the motor.
Since the motor cannot be controlled accurately, I made two holes in the gear, positioned an infrared LED below and and infrared receiver above. This lets the controller know when the gear has completed half a turn, which is taken to be a single level of resistance.
The next step was to replace the original console. The bike is controlled by a RaspberryPi 4B with 4GB of RAM, which is probably an overkill for what is required in this case. The display is a 7″ 1024×600 LCD. Both are mounted on a piece of plywood attached to the bike. The display is covered by a sheet of Lexan, which also holds the buttons used to select, start and stop an exercise, as well as to manually change the resistance level if needed.
The RaspberryPi is powered by QNX, with drivers I wrote for GPIOs, the frame buffer, and button controls. The bike-specific software is divided into two components:
- The controller resource manager
- The console user interface
This division allows for a different UI to be used instead of the default one, and I have a plan (or, rather, my son does) to program games that use the input from the bike for the controls.
The resource manager has one thread for handling input (e.g., read sensor information) and output (e.g., set resistance level) messages, and a high priority thread for acting on the various GPIOs.
The user interface program is based on SDL, hacked to use the frame buffer driver for the display, and the four buttons from the bike’s console for the “keyboard”. It loads exercise programs from the SD card. Each program is a text file with a simple representation of the exercise as a set of intervals, each consisting of a duration and a resistance level.
The code for both components is available here.
To say that this was a fun project would be a serious understatement. I enjoyed tremendously all parts of it, from milling the threaded rod (donations to buy a real milling machine would be appreciated) to printing the gears, experimenting with the IR LED and receiver and writing the software. The fact that it works wonderfully well as a programmable exercise bike is an added bonus.