Early in the Dread Year of 2020, I realised that if I was going to be stuck inside to avoid the plague, I’d need a regular form of exercise that I could do without leaving the house. Ideally, it’d be something I could do without thinking, and while focusing on other work. While I considered the prospect of a treadmill standing desk, I wasn’t quite ready for that level of commitment; instead, I bought an under-desk cycle.

The Deskcycle 2 is a solid exercise machine. A decent amount of resistance from a magnetic flywheel design, close enough to the ground that my knees didn’t get close to hitting my desk. But the crappy little LCD display that attached to it? Absolute garbage. Barely readable under the wrong lighting conditions, unreliable plastic button. I have no idea why the component you’re going to spend the most time staring at seems to have gotten the least consideration.

Luckily, the connection between the cycle itself and the display is just a 3.5mm jack, and the communication interface is just the two pins being shorted once per revolution. Connecting the pins up to a Pi Zero W was relatively straightforward. I used Node-RED to prototype it, with the core being this:

Pin 3 is set to 0, and Pin 5 set to an input with a pullup resistor. Once per revolution, the two pins are briefly shorted; the rising edge on Pin 5 is considered a completed cycle, and the timestamp at which it occurs is added to the day’s CSV.

It’s similarly straightforward to estimate revolutions per minute from the interval between each revolution, smoothing the value across several revolutions makes the output less noisy, and letting the value drop when a period passes with no revolutions allows the value to return to 0 after stopping.

At the start of a new day, we finish by writing the final number of revolutions to the old CSV, and start the new CSV at 0 revolutions. I also check whether I cycled above a goal distance; if I did, that goal distance increases by 30 revolutions for the subsequent day, otherwise it reduces by 3. This control system keeps bumping the goal upwards for every day that I meet it.

It’s useful to be able to access the files remotely through web requests; in addition, CSV values from the past shouldn’t change, so any files other than the one for the current day can be cached for a very long time. This minimises the amount of data that needs to be transmitted - which, when running on a Pi Zero and with many weeks’ worth of data, makes a substantial difference.

I really like how graphical coding languages let you lay out functions visually like this. Anyway, enough about the backend - the frontend’s a website that looks like this:

Up the top is a live plot of RPM and progress towards the daily target over time, plotted using Cubism.js. I had to patch in some fixes since the library hadn’t been updated in close to a decade and by default doesn’t use timeouts for its web requests, which could sometimes result in no data coming through and not recovering without refreshing the page. The output’s a horizon chart which is a nice, compact way of representing values that can vary substantially while not needing too much area.

Down the bottom is a plot of cycling progress over the current day in red, and over the past week in blue, with more recent days being thicker and more opaque lines. It’s directly plotted in D3.js; if I were re-implementing I’d have used a library like Plotly which is slightly higher-level, since I didn’t really need the full flexibility of D3. Maybe I’ll do that eventually.

The top slider is the minimum speed setting: if the RPM drops below that value, the page repeatedly beeps. When this value’s nonzero, it gives time estimates for how long to cycle at that speed to meet the day’s target; if that target’s already achieved, it gives a time estimate to have cycled the most out of every day of the current week; if it’s already the highest of the current week, it gives an estimate of how long to the most cycling of all time. It also shows the present position across all historical data - so here, I’m number 96 out of all my historical cycling records - and also how long until I overtake that position, and go up another rank.

The slider directly underneath the top one is a “bumper” - if I want to take a break, I can set the top slider to 0, and the beeping will stop. However, once I return to cycling and the RPM increases rather than decreases, if I’m not yet past 100% for the day, it will automatically bump the minimum speed up to that setting. The third slider’s just volume, for if I want to reduce the volume of the beeping relative to e.g. a video that I’m watching. The Pause 10 Mins button drops the volume down to 0, and in 10 minutes if the volume is still set to 0 it returns it back to the previous setting; this can be interrupted early by manually setting the volume again. The 5 Min Timeout checkbox automatically drops the minimum speed to 0 if no cycling is detected for more than 5 minutes.

I show a pie chart as the favicon using Piecon:

And when the RPM is nonzero, it gets added to the favicon using favico.js:

When I hit 100% for the day, a chime goes off and fireworks shoot across the screen, with the fireworks code pulled from this codepen.

It’s a useful project that I still tweak and add to as I think of new features. For example, when I have it meet the goal I post to a Discord channel using a webhook, and I’d like eventually to push the cycling information to Google Fit. I might eventually open-source the code, but I should probably rewrite it not to use Node RED, since it’s only being used because I inherited it from the prototyping stage.

Oh, and I use a Deskcycle Ellipse now.

Addenda

The Deskcycle V2 and Ellipse, aside from their displays, are really good! They have a lot of resistance at their maximum setting, move smoothly at their lowest, and are comfortably worth their price tag. The V2 needs a lot of legroom at your desk, works best when your chair is at a specific height, and the pedals aren’t the comfiest to use barefoot. Unlike a Treadmill desk, nothing forces you to stay at a constant speed, which is why I have the beeper in the UI at the lower end of the speed. After a while, “hear beep > cycle faster” becomes a Trigger-Action Plan and becomes automatic without requiring conscious attention. And unlike a treadmill, there’s no risk of falling off, and if you want to go faster than the lower limit it doesn’t require any manual adjustment, you can just pedal faster.

The Ellipse is much comfier to use barefoot, doesn’t require anywhere near as much legroom to use, and is usable at pretty much any chair height. You can move your feet towards the front or back of the pedals to vary intensity and muscles used, and even just placing your feet on them after you sit down is easy since they’re so large (whereas slipping your feet into the V2 requires more attention). One downside is that it does need more regular cleaning: the closer edge of the Ellipse rolls across the ground on a small wheel, which can get dust and gunk built up on it that makes the otherwise smooth motion a little bumpy. That said, I use mine daily and only find I need to clean it once every 3 weeks; if you’re using it wearing shoes that are tracking dirt in from The Outside World then you’d probably have to do it more frequently. The uncomfortable pedals of the original Deskcycle can also be counteracted by wearing shoes, and there’s moving parts for that dirt to build up and impede the effectiveness, which might make it better if you’re going to use it without de-shoeing. To make cleaning more convenient, I 3D printed a brush holder to sit where the LCD display would otherwise be anchored.

Cycling with either machine is low-impact exercise so they’re easy on your joints; unlike a treadmill, there’s also no maximum weight limit or speed limit since your weight is mostly being supported by your chair and there’s no motors with a maximum torque. If you’re expecting to get sweaty while using it, it’s important to use a suitably breathable chair. I use a Zeus mesh chair from Empire Office Furniture; it’s more commonly sold elsewhere under the name Scroll. (Not affiliate links, though I did have a positive experience with Empire.) Not entirely sure of the original manufacturer or if the chair has a canonical name. It’s a good design for me because unlike most other mesh-based office chairs you’re 100% supported by the mesh with no rigid plastic parts that can make exercising uncomfortable. Downside is it’s not adjustable beyond height so if it’s uncomfortable for you then an alternative’d be better. Just make sure whatever you pick doesn’t have any foam that can absorb sweat and get gross.