tl;dr - I’ve got a GitHub Repo that contains scripts for getting the cadence sensor on my exercise bike to control video games.

So, I’ve posted before about setting up my exercise bike in front of a PlayStation. It does two things:

  • Makes my exercise a bit more fun.
  • stops me gaming until 4am like a teenager and thus wrecking my job, relationships, and entire life.

I’ve got an alarm on my watch that beeps if my heartbeat drops below a target and I’ve been working my way through the best games of, like, 2007.

However, something I’ve always wanted was a system that mapped the speed of the pedals to something in the game. This weekend I got around to putting an old cadence sensor on the spin bike and writing a quick script that took the cadence sensor information and used it to control the keyboard, and thus control a simple online game.

Here’s the proof of concept:

You can see the basic code here(note: that’s a link to the commit rather than the up-to-date state) and the particular code shown in the video is this one:

from bleak import BleakClient
import asyncio
import viciouscycle
import bleak
import keyboard

def go_forward(sender, data): 
    print("Here")
    cadence=viciouscycle.decode_and_handle_measurement(sender,data) 
    if cadence is None:
        print("None") 
        return
    if cadence > 60: 
        if cadence <150: 
            print("Doing great!")         
            keyboard.hold_key('up',2) 

    else:
        print("No, do better") 



async def play():
    async with BleakClient(viciouscycle.sensor_address) as client:
        print("Start to play") 
        await client.start_notify(viciouscycle.CSC_MEASUREMENT_UUID, go_forward)
        await asyncio.sleep(60)
        await client.stop_notify(viciouscycle.CSC_MEASUREMENT_UUID)
        print("Stopped notifications")

if __name__ == "__main__":
    print("Entering Warm up") 
    print("Seeking Sensor")  
    try: 
        asyncio.run(play())
    except bleak.exc.BleakDeviceNotFoundError: 
        print("Device was NOT found")  

It’s my plan that I’ll come back to this shortly with some more complex code and, much more importantly, a setup where I’ve been able to play some games using the pedals for a few hours. I don’t expect to get as far as Mario Kat 8, but it would be nice to have something with that is low-overhead and fun.

JUNE UPDATE

Since making the proof of concept I rebuilt the overall setup so that it will be a lot easier to do things like ‘fit the laptop on the desk’ and so on. I also bought a Retrode on the basis that would be a nice way in to doing some bike controlled gaming with simple games.

JULY UPDATE

I successfully used my cadence sensor to play Road Rash 2 via the Retrode and a cheap USB gamepad.

Playing Road Rash 2

I created a test system using my Retrode and Road Rash 2 (1992) (I played this game as a child, still have a copy I can put into the Retrode, and know that it’s played by having the accelerator down for the whole race, which is fairly easy to do with my script). I’m using RetroArch to actually play via the Retrode⁰

It was successful in the sense that I am much more tired by the workout that I would have been with my other gaming setup. I used some extremely simple logic (If cadence is above 80, then hold down the accelerate button) and relived my youth a little.

While doing it, I rebuild the code in general, and investigated what might be possible with the device on other games (It won’t be needed for a while, Road Rash is wearing me out at the moment). Presumably the setup of ‘hold down the accelerate button’ will work for similar retro games like Mario Kart.

I wrote up some notes on the sensor itself on this post. It sends one data pulse every 0.76 seconds or so (and quite a lot get lost, so the average time between received packets about 0.9 seconds) I get the impression that I get more regular updates the higher the cadence, but I don’t have particularly hard data on that and it certainly doesn’t get below 0.7 seconds or so.

That eliminates the ‘direct control’ option immediately. 0.7 seconds plus the sensor lag (it will take a while before it can tell I’ve sped up) is far to long for any sort of gaming response. There are other problems as well but I talk though those in the other post.

The bottom line is that these cadence sensors are good for their central use case (measuring cadence for cyclists over reasonably long periods) but far from great for my use-case (super-fast reaction times in video gaming).

Sidenote: in the 15 years or so since I last properly looked at retro games emulators they have been very much enhanced. Here’s a before and after of me finding the controls for graphic enhancement:

Comparing graphics

March update

I injured my knee at Christmas so there has been very little cycling, but I have built a much more accurate speedometer for future gaming.

Next Steps

  • Reimplement Road Rash with the new Speedometer.
  • Test Mario Kart 64.

Daydream Goals

  • More ‘aware’ controls that recognise the game state and adapt automatically.
  • The python code directly controlling the PS4 via emulating the controller. ✅ Porting the code to a Raspberry Pi (or whatever is current these days) so I don’t have to lug around my laptop ✅ More advanced crank tracking using a bunch of magnets(like this but with more magnets and on a stationary bike) (Update: I was thinking about magnets and a hall effect sensor, but I actually think it would be a lot easier to have a dynamo and a voltmeter…)

⁰ OpenEmu is more popular, but as detailed here really doesn’t work well for accepting inputs from python.