Introduction: Cycflix: Exercise Powered Entertainment
Cycflix: Exercise Powered Entertainment © Ronan Byrne July 2017
Now that the cheesy title has pulled you(Fitflix was taken and I didn't want to be sued) I'll tell you about the project, it uses a stationary exercise bike connected to an Arduino Nano to control the streaming of Netflix on a PC, I started this project hoping I could get it working on just a Raspberry Pi but ran into a few problems I couldn't overcome which I'll discuss at the end. But the main gist of the project at the click of a button and some user input, Firefox will open and play Netflix and monitor your speed for the Workout routine you designed, if you are going below the speed for too long, Netflix will be paused until you get back up to speed. If you want to use some other streaming service, it'll only take a few changes.
The main reason I did this project was to learn some python and finally use my Raspberry Pi(which didn't get used in the end) so the code won't be perfect as I was learning as I went but thought I might as well publish the project.
- USB Cable
- 10K resistor
- Copper Strip board
- Stationary Exercise Bike(Can't guarantee this will work with your bike)
- 2 Nuts and Bolts
Step 1: Speed Calculation
Before I started this project, I've never tried to hack into an exercise bike but I assumed it would output a variable frequency square wave or an analog voltage proportional to the speed, well it looks like my first guess was correct. But I thought it would be a rotary encoder which would be a high frequency due to the number of slots on the encoder but from my testing which I'll describe below, the frequency of the square wave was very slow, at slower speeds the period of the wave was greater than a second. Either this is a very low resolution encoder, high gear ratio or something else, but it doesn't really matter whats in the black box once we know how to use it .
I found out the output was a square wave with varying frequency from hooking up my Bitscope to the speed input of the display(which the manufacturers were nice enough to clearly label) and started pedaling. With the help of a friend, I took multiple screenshots of the waveforms at different speeds. To get the period of the square wave, measure the time between two rising or two falling edges(use TB in bottom left of window not Time/Div). From these waveforms, I found a formula to convert period of waveform to speed on the bike(these pictures are shown above). I did this by plotting the points on a graph and getting the equation from the treadline. You of course draw your own line of best fit, pick two point off the graph and solve:
y = m*x+C
Where y = Speed(Km), m is slope(Km/ms), x is Period(ms) and C is the offset (Km)
I rounded the slope and offset to this: Speed=Period*-0.0384+60.372
I did run into a problem that I couldn't recorded the lower speed(around 10Km) because the frequency was so slow that the Bitscope couldn't display the whole period. From my testing it looks accurate enough between 20Km and 40Km but below and above can be inaccurate making me think either the output is none linear, the display isn't accurate or my formula is accurate. But it gives me close enough answers to the real value for most speeds, if I took more points, the equation would be more accurate but this will do.
Step 2: Circuit and Code
The circuit is very simple, its just a pull down resistor and two wires connecting to the speed input and ground of the bike circuit. There was four holes already in the back of the casing so I soldered my circuit to some copper strip board, drilled two holes in it and attached it to the casing with two nuts and bolts. If you do use copper strip board, make sure the traces are cut in between the Arduino pins and that the bolt doesn't touch and of the copper.
And that's it! See the first few pics for the schematic and my circuit
The code is here on my Github(clone the whole repo and do not change directory structure) but I'll briefly go through bits of the code here.
There isn't too much to the Arduino code, it first waits for the PC to send something to establish a connection. After that it starts looping and reading the analog pin to see if its high or low and calculates the speed by measuring the period of the square wave. I couldn't use interrupts as the voltage from the bike is roughly 1V(when connected to the Arduino) so less than the logic level of the Arduino, you could amplify it but it was easier to fix in code than in circuitry(I have op amp circuits on my other Instructables and on my blog if you want to use them, an optocoupler would work too). Because the shortest period of the waveform is going to be between 100's of milliseconds(Unless you are insert-famous-cyclist-here) we didn't need the speed of interrupts. Every half second a timer interrupt is triggered and if PC has requested the current speed, the Arduino will send it. If the speed hasn't been updated after a few seconds, the speed will be reset to zero.
For the python code, you will of course have the appropriate packages and geckodriver downloaded. This project uses Python3 and I used pip3 to install the packages but you could also use easy_install
pip3 install selenium (Linux users would need sudo)
For the geckodriver, download the one for your system from here and extract it and follow below:
Place the geckdriver.exe in the Cycflix folder, you can place it somewhere else and add to your PATH if you like
Place the geckodriver in /usr/local/bin and type 'geckodriver in you terminal to test it if works
After both of them are setup you can do a test of the script, below is the basic flow of the code(pictures for most steps are above, I show the terminal output for the extra info)
- Run the python script(running the script in your terminal or IDLE will show some extra information)
- A popup window will show you all the Serial ports connected to your PC, pick the one connected to your Arduino
- Another window will popup(I went a bit tkinter crazy with the windows :) ) while you PC waits for a message from your Arduino. Press the reset button on the Arduino if you're waiting too long
- Firefox will now start and open the sign in page of Netflix(it could take a few seconds for FireFox to start). If you have you login details saved in the "login.txt", you will be signed in automatically but if you don't, you will be prompted to enter you sign in details with the option to save them for next time
- You will be prompted to enter in the details for for workout such as work time, work speed, sets, time until pause etc
- Now the script will wait for you to choose a Programme/Movie to watch and your workout will start
- During the workout you will see a window in the top left corner of your screen with your current round details such as your speed, time to next round, nominal speed and round. When a round ends, you will see a popup with the details for the next round(this closes after 5 seconds automatically). If you go below the nominal speed for the current round for half the pause time, you will get a warning saying you are going too slow. If you are still going too slow for the whole pause time, Netflix will be pause until you get back up to speed for a few seconds
- Once your workout if finished, the script will close but Netflix will remain playing
You shouldn't go fullscreen when watching Netflix as you won't be able to see you current round details.
I did most of my development and testing on Linux Mint, but from doing some quick tests on Windows, it all worked fine
Step 3: Conclusion
Why I Couldn't Use a Raspberry Pi
As I said at the start I couldn't get this to work on a Raspberry Pi, the reasons for this was the browsers and selenium, I couldn't get a browser that could be controlled by selenium that could also stream Netflix. The problem with Firefox was you need the Widevine extension to enable 'Play DRM' which is required to stream Netflix, this extension isn't supported on the Pi. I also tried Chromium and after a while of searching I did find a version of Chromium which could stream Netflix on the Pi but Google stopped supporting Arm for the Chromedriver so I couldn't control it with selenium, I did find an old version of Chromedriver but couldn't get it working.
There is of course a lot of extras I could put in this such as stopping the user unpausing Netflix, adding sound when switching rounds, saving workout details and a few others but if I kept adding these features, the project would never be "done" and right now it has enough functionality for me and after all, it was to help me learn some python and maybe(a big maybe) get me using the exercise bike.
If you would like to add some features or improve the code, open a pull request on the repo and I'll have a look when I get the chance.