49152
Posts: 10
Joined: Mon Mar 14, 2016 8:16 pm

PiCycling – bike trainer controlled video

Wed Mar 16, 2016 2:31 am

If pedaling your bike trainer while waiting for better weather has become tedious, you might consider using your Raspberry Pi, a $2 sensor, a 25 cent magnet, and bits of wire, tape & glue to control the speed of a point-of-view video.
picycling.jpg
picycling.jpg (58.53 KiB) Viewed 14278 times
POV videos, homemade or from YouTube, put you on the bike seat. Watching one while exercising is better than staring at the wall, but when pedaling controls the speed of the video, the experience changes. I found myself trying to pass the rider in front of me. This, of course, is impossible.

You’re not limited to biking videos. I pedaled a train across Norway in the driver's seat, thanks to Norwegian TV’s Bergen to Oslo program. Train enthusiasts have uploaded many driver’s view videos. You can pedal a cog train up the Swiss Alps past the Matterhorn.

This project was an experiment, but works well enough that I thought others might be interested. There are limitations. There is no adjustment for different trainer roller sizes. By happy coincidence, the 2” dia. roller on my trainer, with one magnet attached, controls video playback speed quite well. As my speed increases from 0 to about 15 mph (on the bike speedometer), the video speed increases from still-frame to a little faster than normal. There’s a small difference between 25 fps and 30 fps video, but both work. Pedaling faster will not increase the video speed further, so these are ‘recreational’ rides. There are no menus, but the command line shouldn’t bother most Pi users.

Hardware:
(Raspberry Pi: Model B rev.2 tested)
Bike with nominally 26” wheels; trainer with 2” roller. With this combination, one bike wheel revolution produces slightly less than 13 trainer roller revolutions.

Hall Effect Sensor:
Honeywell SS445P Unipolar Hall-Effect Digital Position Sensor with Built-in Pull-up Resistor, TO-92 package (Digi-Key 480-5197-ND)
Many other units will work, but it’s important to pick a non-latching, digital switch, not a latching or analog sensor. It also needs to operate with a 3V3 supply. South pole, north pole and bipolar units are available. The recommended unit has a built-in pullup resistor. Other units require an external 10K resistor between output terminal and V+. Note which side of the sensor faces the magnet – usually the side with printing.
(http://sensing.honeywell.com/index.php? ... me=SS345PT)

Magnet:
Small neodymium disc magnet, 1/4” x 1/16” or similar, to fit trainer flywheel
(https://www.kjmagnetics.com/proddetail.asp?prod=D41-N52)

Capacitor: .01uF (value not critical) between sensor supply and ground pins.
sensor.jpg
sensor.jpg (40.91 KiB) Viewed 14278 times
3-conductor cable from trainer sensor to Pi. A polarized connector is recommended so the sensor can be disconnected/reconnected without causing a wiring error. (An old modular phone extension cord was used at the trainer end). At the Pi end, splice the cable to 3 jumper wires, prewired with header connectors, such as: https://www.adafruit.com/products/794

Small perf-board to hold sensor and capacitor (& pullup resistor, if needed)

Appropriate materials to attach and adjust sensor board position – Velcro, hot glue, double sided tape, etc.
pi_connections.jpg
pi_connections.jpg (59.27 KiB) Viewed 14278 times
Note: The Pi’s GPIO circuits are not protected. A wiring error can cause permanent damage. BE VERY CAREFUL. There are many GPIO reference diagrams available, such as the one at http://pinout.xyz. Raspberry Pi uses several pin numbering systems. This project uses the BCM system.

Wiring: (See your Hall Effect sensor’s data sheet and the Pi’s connector pinout diagram)

Connect the sensor ground pin (GND) to the Pi’s pin 6 (ground)
Connect the sensor power supply pin (VS, VCC, V+ - label varies with manufacturer) to the Pi’s pin 1 (3V3 power)
Connect the sensor output pin (out, open collector, open drain - label varies with manufacturer) to the Pi’s BCM18 (physical pin 12 on the Model B)

If your sensor does not have a built-in pullup resistor (output is called open collector or open drain) you will need to connect a 10K resistor between the output pin and the power pin.

To suppress noise, connect a .01uF (or similar) between the sensor ground and power pins.

Attaching to trainer:

Using double-sided tape, hot glue, Velcro or whatever works and be easily adjusted, attach the sensor board near the trainer flywheel. The sensor needs to be about 1/4” from the magnet as it passes by. Check to make sure the correct side of the sensor faces the magnet.

Attachment the magnet to the flywheel. Again, tape, glue or whatever works to keep the magnet attached. I also attached a washer 180 degrees opposite to avoid unbalancing the flywheel – probably not needed.

Software:
[Edit: Software described here is for the first version of piCycle. For newer versions, see later post]
A few lines of Python are used to link existing Pi programs and modules. Omxplayer, included in Raspbian, can be paused (‘p’ on the keyboard). Once paused, pressing the ‘v’ key will step forward, one frame at a time. Working together, two modules allow the trainer’s magnetic sensor to ‘virtually’ type.

RPi.GPIO, also pre-installed (https://pypi.python.org/pypi/RPi.GPIO), gives control of the Pi’s GPIO pins via Python. We will set one pin as an input that watches for a high-to-low logic transition. When detected, a second module, Python-uinput (http://tjjr.fi/sw/python-uinput/) simulates a ‘v’ key press, typing “vvvvvv...” from 0 to 30+ times per second, as the bike wheel spins.

In Raspbian Jessie, install Python-uinput.

With an internet connection:

Code: Select all

sudo pip install python-uinput

sudo apt-get install libudev-dev
Set uinput to start at boot:

Code: Select all

sudo nano /etc/modules
Add “uinput” (no quotes) on a new line and save the file.

Create a text file named gpio.py with the following contents, including indentation:

Code: Select all

#gpio to keypress
import RPi.GPIO as GPIO
import uinput

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down = GPIO.PUD_UP)
device = uinput.Device([uinput.KEY_V,])

while True:
    	GPIO.wait_for_edge(18, GPIO.FALLING)
     device.emit_click(uinput.KEY_V)
   
GPIO.cleanup()
Save the file to your ‘Videos’ folder.

Raspbian Preferences – graphical interface:

In the System tab, set ‘Boot’ to ‘CLI’. If this is a new Raspbian system install and you haven’t used the ‘Expand Filesystem’ option on this menu, do so. This will make space for your videos.

Under the ‘Performance’ tab is the option to boost processor speed. With the Pi Model B (rev2), I’ve set the speed to 900MHz.

Videos:

Any video playable by Omxplayer will work, but 24, 25 or 30 fps videos work best. Higher frame rates, 50 or 60 fps, have smoother motion but will not reach full speed, even with fast pedaling (at least on the original Pi). Copy your videos to the Videos folder.

Use:

After booting to the command line, change directory to ‘Videos’. Listing, you should see your videos and the gpio.py file. Enter:

Code: Select all

sudo python gpio.py &  
(& runs the program in background)
You will need to start gpio.py after each (re)boot, but not between videos. Test by slowly moving your bike’s wheel. You should see a string of ‘v’s running across the screen. Delete the ‘v’s before starting your video.

To start a video:

Code: Select all

omxplayer --no-osd [yourvideo].mp4
The “—no-osd” option disables on-screen-display of frame counts.
Use the ‘p’ key to pause where you want to begin your ride.

RIDE !

During your ride, you can use the arrow keys to skip ahead or back. End the video with the ‘q’ key. If you keep pedaling after the video finishes, the system may appear to ‘hang’ for a few seconds. Wait, and the prompt should return, or press Control-C twice.
Last edited by 49152 on Sun Mar 05, 2017 10:20 pm, edited 1 time in total.

MrBulp
Posts: 11
Joined: Wed Dec 07, 2016 2:45 am

Re: PiCycling – bike trainer controlled video

Tue Dec 27, 2016 1:47 pm

hi, would it easy to put the speed details into the video?

& my raspberry pi now blank... black screen... how to reset to normal? :(

49152
Posts: 10
Joined: Mon Mar 14, 2016 8:16 pm

Re: PiCycling – bike trainer controlled video

Fri Feb 17, 2017 12:12 am

I recently noticed a post by forum member Gadgetguy about the cross-platform mpv player, now with video acceleration for the Raspberry Pi:
Using omxplayer, the first piCycle simply step-frames from about 0-35fps. It works well enough, but can't play 50/60fps video at full speed or include a speed overlay. Mpv player's playback rate, and other settings, can be controlled by software so it seemed like a good time to try another approach.

PiCycle can now play 50/60fps video and includes an on-screen speedometer & odometer, a file-selector to reduce typing, and the ability to save the current position of your video when you quit. It works with both my Pi3 and a Pi 1B, although the 1B can’t play 1080p60 as fast as the Pi3.

For this project, mpvplayer must be compiled from source. This requires patience, especially on an older pi, but using Gadgetguy’s instructions I’ve done it several times with no problems. He does offer binaries on his website, but they are slightly older versions that do not work with piCycle.

The hardware is the same as previously described, but with an additional magnet on the roller flywheel, 180 degrees opposite the first. Make sure the correct side of the magnet faces the sensor.
_____________________________________________________
Starting with a new Raspbian installation:

Code: Select all

sudo apt-get update 
sudo apt-get upgrade 
[Edit: Due to changes in newer mpv player and/or Raspbian versions, the build instructions described here no longer work. See the alternative installation that follows.]
Old- Download & compile mpv player, following the “Build it yourself” instructions, near the bottom of the webpage at:
(Note: Do not use the pre-compiled '.deb' packages listed under 'Install'. These are for an older version of mpv player and will NOT work with piCycle.)
New: Since compiling mpv from source has become more difficult, I have uploaded a compiled version. At least for a while, it is available here:
After downloading, change to your download folder and unarchive the file. The result is a single executable file named ' mpv '. Then:

Code: Select all

sudo mv mpv /usr/local/bin/mpv
sudo chown root:root /usr/local/bin/mpv
sudo apt-get install libluajit-5.1
mpv --version
After the last line you should see information about the installed mpv player application, starting with 'mpv 0.23.0-git-10a005d (C) 2000-2016.."

Install python-dialog:

Code: Select all

sudo apt-get install python3-dialog

Install python-evdev:

Code: Select all

sudo pip3 install evdev 
Install mpv-python-ipc:

Code: Select all

git clone https://github.com/siikamiika/mpv-python-ipc.git 
cd mpv-python-ipc 
sudo python3 setup.py install 
Not needed for piCycle, but enables mpv player audio:

Code: Select all

sudo apt-get install oss-compat 
In system preferences:

Code: Select all

gpu_mem=256 (for 1GB models)
gpu_mem=160 (for 512MB models)
Edit (or create) the file with the line:

Code: Select all

 /home/pi/.config/mpv/mpv.conf
 rpi-background=yes
The file selector font size can be enlarged by editing:

Code: Select all

 /etc/default/console-setup 
FONTFACE="TerminusBold" 
FONTSIZE="12x24"
In the pyCycle python script:
Choose km/h or mph speedometer settings.
Enter your roller’s diameter.
___________________________________________________
Optional settings:
The on-screen odometer is close but not completely accurate, when compared to a calibrated bike speedometer. The error varies with Pi models and processor speed, but can be corrected by editing the line " odocal = 1.00 # small increments above/below 1.00 ". For example, my Pi3 is set 'odocal = .976, and my Pi1b (turbo) is 'odocal = .926'.

When riding at "12mph/19km/h", video plays at about normal x1 speed. This can be adjusted by editing the line " vidspeedcal = 1.00 # small increments above/below 1.00 ". For example, with a setting of .75, you have to pedal at 25km/h for normal play speed. With 1.25, normal video speed is at 15km/h. There is a cap on how fast video plays, especially at higher framerates or with slower Pi models.

Run piCycle from the command line (not a desktop terminal window).
python3 piCycle.py [Edit: newer version in later post]

Code: Select all

# PiCycle  15Feb17

# uncomment preferred system:
speed_label = " km/h"
#speed_label = " mph"

# enter trainer roller diameter in mm
roller_diameter = 50.45

# optional: calibrate odometer, default = 1.00
odocal = 1.00   # small increments above/below 1.00

# optional: adjust video speed to bike speed
# default - normal x1 video at about 12mph (19km/h)
vidspeedcal = 1.00   # small increments above/below 1.00

####################################
from time import perf_counter
import RPi.GPIO as GPIO
from mpv_python_ipc import MpvProcess
from dialog import Dialog
import evdev
from evdev import *
from select import select

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down = GPIO.PUD_UP)

defaultVidFolder = "/home/pi/Videos/"
vidFolder = defaultVidFolder
d = Dialog(dialog="dialog")
tag = "nil"
mp = MpvProcess()
stopmpv = 0
bikespeed = int(1)
speedometer = 1
odopulse = int(0)
distance = 0
save = 0
vidspeedadj = .03 * vidspeedcal
atenthnum = 63662 * odocal

if speed_label == " km/h":
    roller_speed = (88.419/roller_diameter)
    dist_label = " km"
    atenth = (atenthnum/roller_diameter)
elif speed_label == " mph":
    roller_speed = (88.419/roller_diameter) * 1.609344
    dist_label = " m"
    atenth = (atenthnum/roller_diameter) * 1.609344

devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()]
eventlist = []
for device in devices:
    eventlist.append(device.fn)
devices = map(InputDevice, eventlist)
devices = {dev.fd: dev for dev in devices}

GPIO.add_event_detect(18, GPIO.FALLING)

while save == 0:
    if tag[-17:] == ("Videos_on_SD_card"):
        vidFolder = defaultVidFolder
    code, tag = d.fselect(vidFolder,20,70,)
    print("\033[H\033[J")
    if tag == "":     # cancel button ends program
        break
    mp.commandv("loadfile", tag)
    vidFolder = tag
    mp.commandv("set", "audio", "no")
    mp.commandv("set", "cache", "no")
    mp.commandv("set", "framedrop", "no")
    mp.set_property("fullscreen", "yes")

    while mp.get_property_native("idle-active") == 0 and save == 0:
        r, w, x = select(devices, [], [], 0)
        for fd in r:
            if r:
                for event in devices[fd].read():
                    if (event.type == ecodes.EV_KEY and
                            categorize(event).keystate == 1):
                        if categorize(event).scancode == 105:    # left: back small
                            mp.commandv("keypress", "LEFT")
                        elif categorize(event).scancode == 106:  # right: forward small
                            mp.commandv("keypress", "RIGHT")
                        elif categorize(event).scancode == 103:  # up: forward large
                            mp.commandv("keypress", "UP")
                        elif categorize(event).scancode == 108:  # down: back large
                            mp.commandv("keypress", "DOWN")
                        elif categorize(event).scancode == 32:   # d: video progress
                            mp.commandv("osd-bar", "show-progress")
                        elif categorize(event).scancode == 19:   # r: reset odometer
                            distance = 0
                            odopulse = 0
                        elif categorize(event).scancode == 31:   # s: speedo on/off
                            speedometer = -speedometer
                        elif categorize(event).scancode == 17:   # w: quit, save position
                            mp.set_property("pause", "yes")
                            save = 1
                        elif categorize(event).scancode == 16:   # q: stop video
                            mp.commandv("stop")

        if speedometer == 1:
            speedo = round((bikespeed - 1)/roller_speed)
            speedoform = ("{:0>2d}".format(speedo))
            distanceform = ("{:.1f}".format(distance));
            speedostr = (speedoform + speed_label + "  " +
                                    distanceform + dist_label)
            mp.commandv("show-text", speedostr)

        mp.commandv("set", "speed", (bikespeed * vidspeedadj))
        if (bikespeed == 1):
            mp.set_property("pause", "yes")
        elif (bikespeed != 1):
            mp.set_property("pause", "no")

        endtime = perf_counter() + .5
        bikespeed = 1
        while perf_counter() <= endtime:
            if GPIO.event_detected(18):
                bikespeed += 1
                odopulse += 1
                if odopulse >= atenth:
                    distance += .1
                    odopulse = 0

    mp.set_property("fullscreen", "no")

if save == 1:
    mp.commandv("quit-watch-later")
elif save == 0:
    mp.commandv("quit")
print("\033[H\033[J")
GPIO.cleanup()
In the file selector, use arrow keys and space bar to select, then the enter key.
The 'cancel' button ends the program.

During the video:
right arrow = small jump forward
left arrow = small jump backward
up arrow = large jump forward
down arrow = large jump backward
s = speedometer display on/off
r = reset odometer
d = brief display of video progress
q = stop video, return to file selector
w = save current video position, exit program
bridge.jpg
bridge.jpg (23.32 KiB) Viewed 13826 times
Last edited by 49152 on Sun Mar 05, 2017 11:11 pm, edited 2 times in total.

MrBulp
Posts: 11
Joined: Wed Dec 07, 2016 2:45 am

Re: PiCycling – bike trainer controlled video

Thu Feb 23, 2017 4:12 am

hi 49152,

thank you very much for sharing the codes.. but its empty.. nothing

id saw the mpv in task manager running.. but nothing on screen..

49152
Posts: 10
Joined: Mon Mar 14, 2016 8:16 pm

Re: PiCycling – bike trainer controlled video

Thu Feb 23, 2017 6:26 pm

Hi MrBulp,

I'm sorry you're having problems. You say you can see mpv in the task manager. Are you running the program from the Raspbian desktop? It must be run from the linux command line, not with the desktop "run" popup or terminal window.

Before running piCycle, try testing the player. At the command line: mpv 'yourvideo.mp4'
"q" to quit.

MrBulp
Posts: 11
Joined: Wed Dec 07, 2016 2:45 am

Re: PiCycling – bike trainer controlled video

Thu Feb 23, 2017 6:44 pm

Hi 49512,

i did try the command mpv "videoname" from Terminal & its run smoothly..

yes.. im using Raspbian Desktop..

now i know.. have to click CLI to be in linux command line without entering desktop mode..

anyway its still dark.. nothing run when i select the videos after entering command python piCycle.py

this is correct?

Code: Select all

 rpi-background=yes
or this?

Code: Select all

--vo=rpi:background=yes

thanks for the help

49152
Posts: 10
Joined: Mon Mar 14, 2016 8:16 pm

Re: PiCycling – bike trainer controlled video

Thu Feb 23, 2017 8:23 pm

"i select the videos after entering command python piCycle.py "
Did you mean "python3 piCycle.py"? The program will not run with just python (python2).

"rpi-background=yes" is correct. The mpv player developers have been changing Raspberry Pi options, and the other method doesn't work now.

Something might be crashing piCycle. To see what it is, try this:
Modify the mpv.conf file to:

Code: Select all

#rpi-background=yes
geometry=50%+700+100
Then, about half-way through the piCycle.py script, make this modification:

Code: Select all

   # mp.set_property("fullscreen", "yes")
These changes will shrink the video and let you see error messages.
testscreen.JPG
testscreen.JPG (39.79 KiB) Viewed 13574 times
This might help find the problem.

MrBulp
Posts: 11
Joined: Wed Dec 07, 2016 2:45 am

Re: PiCycling – bike trainer controlled video

Thu Feb 23, 2017 8:51 pm

My bad.. typo there.. python3 piCycle.py

id follow your guide just now & the video shrink to the exact location but i still got the blue screen with dialog to choose videos on the background.. no rpm text on the leftside.

49152
Posts: 10
Joined: Mon Mar 14, 2016 8:16 pm

Re: PiCycling – bike trainer controlled video

Thu Feb 23, 2017 9:36 pm

Hi MrBulp,
A puzzle. When the video appears:
- Is it running or in still-frame?
- Does it respond to pressing the arrow keys?
- Can you stop the video and return to the selector by pressing 'q"?

MrBulp
Posts: 11
Joined: Wed Dec 07, 2016 2:45 am

Re: PiCycling – bike trainer controlled video

Fri Feb 24, 2017 1:31 am

Hi 49512,

the video start to play immidiately.
i cant quit even stop when i press the Q key or any arrows keys.
i need to wait till the video end or just press control alternate delete.

49152
Posts: 10
Joined: Mon Mar 14, 2016 8:16 pm

Re: PiCycling – bike trainer controlled video

Fri Feb 24, 2017 3:09 am

I have been trying to "break" the program in a way that produces what you are seeing, but without success so far. Meanwhile, please let me know the results of the following command, typed in the terminal:

Code: Select all

mpv --version

MrBulp
Posts: 11
Joined: Wed Dec 07, 2016 2:45 am

Re: PiCycling – bike trainer controlled video

Fri Feb 24, 2017 3:51 am

this is the result

mpv 0.20.0-git-c226bc7 (C) 2000-2016 mpv/MPlayer/mplayer2 projects
built on Sat Oct 1 00:38:38 UTC 2016
ffmpeg library versions:
libavutil 55.28.100
libavcodec 57.48.101
libavformat 57.41.100
libswscale 4.1.100
libavfilter 6.47.100
libswresample 2.1.100
ffmpeg version: n3.1.3

49152
Posts: 10
Joined: Mon Mar 14, 2016 8:16 pm

Re: PiCycling – bike trainer controlled video

Fri Feb 24, 2017 5:32 am

I think you have found the source of the problem. This is the version on my system:
mpv 0.23.0-git-10a005d (C) 2000-2016 mpv/MPlayer/mplayer2 projects
built on Sat Feb 4 16:23:55 PST 2017
ffmpeg library versions:
libavutil 55.34.100
libavcodec 57.64.101
libavformat 57.56.100
libswscale 4.2.100
libavfilter 6.65.100
libswresample 2.3.100
ffmpeg version: n3.2.2
It appears that you installed the pre-compiled binaries from Gadgetguy's website. As I mentioned in the instructions, these ".deb" packages of mpv 0.20.0 do not work with the piCycle program. I did try to use them for one installation, but had symptoms much like yours. (As just a player, they worked fine.)

The most likely solution is to compile from source, as Gadgetguy described toward the bottom of his webpage, under "Build it yourself". This pulls the current source files from github, compiles and installs them. It takes hours on an older pi, but I did this several times and had no problems. If you choose to do this, you will first need to remove your current versions of mpv_0.20.0_armhf and ffmpeg_3.1.3-1_armhf, using the dpkg installer. I can't remember the exact options, but there are 'man' and '--help' files.

I checked, and the current mpv player is at v0.24.0. There don't appear to be any major changes, but I can't guarantee that something else didn't break. For convenience, here are Gadgetguy's compile instructions:
https://nwgat.ninja/quick-easy-compilin ... pberry-pi/
Build it yourself

sudo apt-get install -y gperf bison flex autoconf automake make texinfo help2man libtool libtool-bin ncurses-dev git yasm mercurial cmake cmake-curses-gui libfribidi-dev checkinstall libfontconfig1-dev libgl1-mesa-dev libgles2-mesa-dev gnutls-dev libsmbclient-dev libpulse-dev libbluray-dev libdvdread-dev libluajit-5.1-dev libjpeg-dev libv4l-dev libcdio-cdda-dev libcdio-paranoia-dev
git clone https://github.com/mpv-player/mpv-build.git
cd mpv-build
echo --enable-mmal >> ffmpeg_options
./use-mpv-release
./use-ffmpeg-release
./update
./rebuild -j4
sudo ./install
I apologize if my instructions were confusing.
Good luck!

49152
Posts: 10
Joined: Mon Mar 14, 2016 8:16 pm

Re: PiCycling – bike trainer controlled video

Sun Mar 05, 2017 11:50 pm

Here is an improved version of the piCycle program. Video playback is more responsive to changes in bike speed and the odometer is more accurate. The odometer can now be toggled between 'trip' and 'total' mileage, which is automatically saved when the program ends. Total mileage is displayed followed by a ' . '

A different gpio library is used. Pigpio is already installed in Raspbian, but must be started before running piCycle with: ' sudo pigpiod '. It will stay running until reboot or can be set to automatically start at boot.
python3 piCycle.py

Code: Select all

#! /usr/bin/env python3

####### PiCycle  3Mar17
#start pigpiod before running piCycle: ' sudo pigpiod '

speed_label = " km/h"
#speed_label = " mph" #uncomment for distance in miles

# enter trainer roller diameter in mm
roller_diameter = 50.45

# optional: adjust video speed to bike speed
# default - normal x1 video at about 12mph (19km/h)
vidspeedcal = 1.00   # small increments above/below 1.00

####################################
from time import perf_counter
import pigpio
from mpv_python_ipc import MpvProcess
from dialog import Dialog
import evdev
from evdev import *
from select import select

defaultVidFolder = "/home/pi/Videos/"
vidFolder = defaultVidFolder
d = Dialog(dialog="dialog")
tag = "nil"
mp = MpvProcess()
bikespeed = 0
speedometer = True
trip_total =  False
tallytotal = 0
distance = 0
save = False
vidspeedadj = .03 * vidspeedcal
lasttally = 0
speedospeed = 0
spdspd = 0
endtime = perf_counter()

try:
   with open("/home/pi/.config/mpv/picycle-odo", "rt") as talin:
      tallytotal = int(talin.read())
except FileNotFoundError:
   pass

pi = pigpio.pi()
if not pi.connected:
   exit()
pi.set_mode(18, pigpio.INPUT)
pi.set_pull_up_down(18, pigpio.PUD_UP)

lasttick = pi.get_current_tick()

def spd(gpio, level, tick):
    global lasttick
    global bikespeed
    bikespeed = 500000/pigpio.tickDiff(lasttick, tick)
    lasttick = tick

cbo = pi.callback(18, pigpio.FALLING_EDGE)
cbs = pi.callback(18, pigpio.FALLING_EDGE,spd)

if speed_label == " km/h":
    roller_speed = 176.838/roller_diameter
    dist_label = " km"
    atenth = 6366.203/roller_diameter #636620.3/100 for fl-int convert
else:
    roller_speed = (176.838/roller_diameter) * 1.609344
    dist_label = " m"
    atenth = (6366.203/roller_diameter) * 1.609344

devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()]
eventlist = []
for device in devices:
    eventlist.append(device.fn)
devices = map(InputDevice, eventlist)
devices = {dev.fd: dev for dev in devices}

while save == False:
    if tag[-17:] == ("Videos_on_SD_card"):
        vidFolder = defaultVidFolder
    code, tag = d.fselect(vidFolder,20,70,)
    print("\033[H\033[J")
    if tag == "":     # cancel button ends program
        break
    mp.commandv("loadfile", tag)
    vidFolder = tag
    mp.commandv("set", "audio", "no")
    mp.commandv("set", "cache", "no")
    mp.commandv("set", "framedrop", "no")
    mp.set_property("fullscreen", "yes")

    while mp.get_property_native("idle-active") == 0 and save == False:
        r, w, x = select(devices, [], [], 0)
        for fd in r:
            if r:
                for event in devices[fd].read():
                    if (event.type == ecodes.EV_KEY and
                            categorize(event).keystate == 1):
                        if categorize(event).scancode == 105:    #left: back small
                            mp.commandv("keypress", "LEFT")
                        elif categorize(event).scancode == 106:  #right: forward small
                            mp.commandv("keypress", "RIGHT")
                        elif categorize(event).scancode == 103:  #up: forward large
                            mp.commandv("keypress", "UP")
                        elif categorize(event).scancode == 108:  #down: back large
                            mp.commandv("keypress", "DOWN")
                        elif categorize(event).scancode == 32:   #d: video progress
                            mp.commandv("osd-bar", "show-progress")
                        elif categorize(event).scancode == 19:   #r: reset trip odometer
                            tallytotal = tallytotal + cbo.tally() 
                            cbo.reset_tally()
                        elif categorize(event).scancode == 22: #u: reset odometer total
                            tallytotal = 0
                            cbo.reset_tally()
                        elif categorize(event).scancode == 31:   #s: speedo on/off
                            speedometer = not speedometer
                        elif categorize(event).scancode == 20:   #t: trip/total
                            trip_total = not trip_total
                        elif categorize(event).scancode == 17:  #w: quit, save position
                            mp.set_property("pause", "yes")
                            save = True
                        elif categorize(event).scancode == 16:   #q: stop video
                            mp.commandv("stop")

        if perf_counter() < endtime:
            spdspd = spdspd + (cbo.tally() - lasttally)
            lasttally = cbo.tally()
        else:
            speedospeed = spdspd
            endtime = perf_counter() + 1
            spdspd = 0

        if speedospeed < 0:
            speedospeed = 0
        if speedometer == True:
            speedo = round(speedospeed / roller_speed)
            speedoform = ("{:0>2d}".format(speedo))
            if trip_total == False:
               distint = int(cbo.tally() / atenth)
            else:
               distint = int((cbo.tally() + tallytotal) / atenth)
            distance = distint * .01
            distanceform = ("{:.2f}".format(distance));
            if trip_total == True:
                speedostr = (speedoform + speed_label + "  " + distanceform + dist_label + ".")
            else:   
                speedostr = (speedoform + speed_label + "  " + distanceform + dist_label)
            mp.commandv("show-text", speedostr)

        mp.commandv("set", "speed", round(bikespeed * vidspeedadj, 2))
        if pigpio.tickDiff(lasttick, pi.get_current_tick()) > 400000:
           bikespeed = 0
           speedospeed = 0
           spdspd = 0
        if (bikespeed < 1):
            mp.set_property("pause", "yes")
        else:
            mp.set_property("pause", "no")
 
    mp.set_property("fullscreen", "no")

if save == True:
    mp.commandv("quit-watch-later")
else:
    mp.commandv("quit")

with open("/home/pi/.config/mpv/picycle-odo", "wt") as talout:
    talout.write(str(cbo.tally() + tallytotal))
   
cbo.cancel()
cbs.cancel()
print("\033[H\033[J")

right arrow = small jump forward
left arrow = small jump backward
up arrow = large jump forward
down arrow = large jump backward
s = speedometer display on/off
r = reset trip odometer
t = toggle trip/total
u = reset odometer total
d = brief display of video progress
q = stop video, return to file selector
w = save current video position, exit program

MrBulp
Posts: 11
Joined: Wed Dec 07, 2016 2:45 am

Re: PiCycling – bike trainer controlled video

Mon Mar 06, 2017 6:06 am

Hi 49325,

thank you for the update.. actually i cant install MPV Player even im clean install from fresh boot of Raspbian..

im stuck at to configure WAF.. disable something but then still couldnt install MPV..

anyway would you share your own MPV Player that you build from your Pi ?

best regards

MrBulp

49152
Posts: 10
Joined: Mon Mar 14, 2016 8:16 pm

Re: PiCycling – bike trainer controlled video

Mon Mar 06, 2017 7:18 am

Hi MrBulp,
Before posting the newer piCycle version, I tried a new install and (like you) found that the mpv build now fails. When attempts with older Raspbian/firmware still didn't work, I uploaded a compiled mpv file. A link and installation steps have been added to the instructions in my earlier Feb.16th post.
Hope this helps.

MrBulp
Posts: 11
Joined: Wed Dec 07, 2016 2:45 am

Re: PiCycling – bike trainer controlled video

Tue Mar 07, 2017 3:11 am

Hi 49152,

Thank you very much for the guide & source files for this PiCycling Bike Trainer..

thank you.

MrBulp
Posts: 11
Joined: Wed Dec 07, 2016 2:45 am

Re: PiCycling – bike trainer controlled video

Thu Mar 09, 2017 1:29 am

49152 wrote:Hi MrBulp,
Before posting the newer piCycle version, I tried a new install and (like you) found that the mpv build now fails. When attempts with older Raspbian/firmware still didn't work, I uploaded a compiled mpv file. A link and installation steps have been added to the instructions in my earlier Feb.16th post.
Hope this helps.
Hi 49152, may i have your email ? i need some customwork regarding your PiCycling script. :)

49152
Posts: 10
Joined: Mon Mar 14, 2016 8:16 pm

Re: PiCycling – bike trainer controlled video

Thu Mar 09, 2017 9:51 pm

Hi MrBulp,
I've sent you a private message.

davidt37
Posts: 3
Joined: Sun Feb 24, 2019 7:37 pm

Re: PiCycling – bike trainer controlled video

Sun Feb 24, 2019 8:39 pm

For what it's worth, here is a version that uses the VLC viewer and the TkInter for the GUI.
No compiling necessary but make sure you install the VLC player. Also load the python VLC bindings via: sudo pip3 install python-vlc

- uses the VLC player to play video at 0-2x speed, dependent on your pedaling rate. (uses a sliding 2 second averaging window)
- GUI allows you to tune/adjust your wheel revolutions per sec to the VLC player playback speed. No need to mess with the python source.
- GUI allows you to chose a *.mp4 video
- GUI displays wheel revolutions per second.
- GUI is persistent. Changes are stored when you hit the "stop" button and recalled when you start up again.
- Uses the same pins/HW connections are the original post. Should be plug and play
- No sound. Sorry

-Run the program via cmd line: python3 vtrain.py


It's my first stab at python (i.e. global variables galore), so take this for what it's worth. Seems to work most of the time. Works ok on a pi zero. Better on a Pi 3 :D

See mock-up below





file: vtrain.py

Code: Select all

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Assumptions:
Python3 Virtual Trainer 1.1 02/24/2019
By David Tamashiro

Hardware connections:
Pin 6 GND
Pin 1 3.3V+
Pin 12 (BCM18 or GPIO18)  Sensor in.  Pulled up to 3.3V+

Also assumes the following commands have been run:
sudo pip3 install python-vlc


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""


import vlc
from tkinter import *
from tkinter import filedialog
from tkinter.ttk import Progressbar
from tkinter import ttk

import RPi.GPIO as GPIO

#Global variables and "Constants"
constMaxPlayerRate= 2.0   ##Max player view rate 2x normal speed
constDefWheelRevPerSec = 1.0 ## Default ## of revolutions/sec
constDefTkWin = "580x40"
constWheelCounterSampInterval = 1000  ## in milli-seconds


whlScale = 0.0  ## scaling factor x weighted average

mrlFile = "sv.mp4"   ## Default File to run

## Current relative rate of player
curRate = 1.0
manualMode = False

## Wheel counter state and averager
whlCount = 0  ## current wheel count
whlResPtr = 0
whlRes = [0,0,0,0]
playerRunning = False
totalSamplingWindow = len(whlRes) * constWheelCounterSampInterval/1000.0


## Full size screen, no xLib for VLC 
Instance = vlc.Instance('-f --no-xlib')
player = Instance.media_player_new()


############################  #Wheel interupt handler
def wheelEventHandler (pin):
   global whlCount 
   whlCount += 1
   #print("Handling wheel event", pin, " count=",whlCount)

## Set BCM18, HW pin 12 as input
GPIO.setmode(GPIO.BCM)
GPIO.setup(18,GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(18,GPIO.FALLING,callback=wheelEventHandler,bouncetime=50)


window = Tk()
window.title("Virtual Bike Tour 1.1")
winsize = constDefTkWin + "+50+50"
window.geometry(winsize)
style=ttk.Style()

geo=window.geometry()
print(geo)


style.theme_use('default')
style.configure("red.Horizontal.TProgressbar",background='red')
style.configure("green.Horizontal.TProgressbar",background='green')

lblMeasRevPS = Label(window, text = "0.0")

def startClicked():
    global whlScale, playerRunning
    if (not playerRunning):
        playerRunning = True
        whlInput = float (txtAdjRevPerSec.get())
        if (whlInput <= 0):
            whlInput = 10
        player.set_mrl(mrlFile)
        setPlayerPos(0.0)
        player.play()
       

def stopClicked():
    file = open("vtraindef.py","w")
    line = "mrlFile = '" + mrlFile + "'\n"
    file.write(line)
    line= "setWheelAdj(" + txtAdjRevPerSec.get() + ")" + "\n"
    file.write(line)
    
    geolist= window.geometry().split("+")

    line = "window.geometry('" + constDefTkWin + "+" + geolist[1] + "+" + geolist[2] + "')\n"
    file.write(line)
    line= "print('reading in defaults')\n"
    file.write(line)
    file.close()
    GPIO.cleanup()
    quit()

def fileClicked():
    global mrlFile
    file = filedialog.askopenfilename()
    if (len(file) > 1):
        mrlFile= file
        print("Loading file:", mrlFile)


def mscrollCmd(obj1, obj2='', obj3=''):
    print("in mscroll", obj1)
    if (obj1 =="scroll"):
        pos = player.get_position()
        if (obj2 =='-1'):
            pos -= 0.05
        elif (obj2 == '1'):
            pos += 0.05
        setPlayerPos(pos)

    elif (obj1 == "moveto"):
        pos = float(obj2)
        setPlayerPos(pos)


def scrollCmd(obj1, obj2='', obj3=''):
    global manualMode
    if (obj1 =="scroll"):
        if (obj2 =='-1'):
           setPlayerRate(curRate - 0.1)
        elif (obj2 == '1'):
           setPlayerRate(curRate + 0.1)

    elif (obj1 == "moveto"):
        x = float(obj2)
        setPlayerRate(x* constMaxPlayerRate)
        manualMode = True

 
def updateWhlScale(obj=''):
    global whlScale, manualMode
    revPerSec = float(txtAdjRevPerSec.get())
    if  (revPerSec < 0.5):
        revPerSec = 0.5

    whlScale = 1.0/(revPerSec * (totalSamplingWindow))
    print("whlScale changed to ",whlScale)
    manualMode = False


txtAdjRevPerSec = Entry(window,width=5)
txtAdjRevPerSec.bind('<Return>',updateWhlScale)

lblRevPerSec = Label(window, width=5, text="Rev/S ")
lblAdjRevPerSec = Label(window, text="1xRev ")
lbRPS = Label(window,text = '   Player Speed   ')
lbVidPos = Label(window,text = '  Position  ')
xbar = Scrollbar(window,orient="horizontal",command = scrollCmd)
mbar = Scrollbar(window,orient="horizontal",command = mscrollCmd)


startBtn = Button(window, text="Start",command=startClicked)
stopBtn = Button(window, text="Stop",command=stopClicked)
fileBtn = Button(window, text="Select Tour",command=fileClicked)

col=0
lblAdjRevPerSec.grid(column=col,row=0)
col += 1
txtAdjRevPerSec.grid(column=col,row=0)
col += 1
lblMeasRevPS.grid(column=col,row=0)
col += 1
lblRevPerSec.grid(column=col,row=0)
col += 1
fileBtn.grid(column=col,row=0)
col += 1
mbar.grid(column=col,row=0,sticky="we")
lbVidPos.grid(column=col,row=1)
col += 1
xbar.grid(column=col,row=0,sticky="we")
lbRPS.grid(column=col,row=1)
col +=1
startBtn.grid(column=col,row=0)
col +=1
stopBtn.grid(column=col,row=0)

playerPaused = False

def setPlayerRate(newRate):
    global curRate, playerPaused


    ## Only change the rate if change is more than 10%.  Hopefully reduces jitter
    if (abs(curRate - newRate) <= 0.05):
        return

    curRate = newRate

    if (curRate < 0.0):
        curRate = 0.0
    elif (curRate > constMaxPlayerRate):
        curRate = constMaxPlayerRate
    ratePerc = curRate/constMaxPlayerRate
    xbar.set(ratePerc,ratePerc)
    if (curRate <= 0.01):
       player.set_pause(True)
       playerPaused = True
       print("Player paused")
    elif (playerPaused):
        player.set_pause(False)
        playerPaused = False
        print("Player un-paused")
    player.set_rate(curRate)
    print("Player rate set to ",curRate)

def setPlayerPos(pos):
        if (pos <= 0.0):
            pos = 0.0
        elif (pos > 1.0):
            pos = 1.0
        mbar.set(pos,pos)
        player.set_position(pos)
        print("pos=",pos)



def setWheelAdj(RevPerSec):
    txtAdjRevPerSec.delete(0,END)
    txtAdjRevPerSec.insert(0,str(RevPerSec))
    updateWhlScale()


## Set GUI defaults
setWheelAdj(constDefWheelRevPerSec)
setPlayerRate(1.0)

try:
     exec(open("vtraindef.py").read())
except:
     print("No state file found. Goiong with defaults")


## update status checker and gui updater
def statUpdate():
    global whlCount,whlRes,whlResPtr, mrlFile

    ## Save current wheel count
    whlRes[whlResPtr] = whlCount
    whlCount = 0

    whlResPtr += 1
    if (whlResPtr >= len(whlRes)):
        whlResPtr = 0

    ## Calculate moving average
    sum = 0
    for i in whlRes:
        sum = sum + i
    avg = float(sum) / totalSamplingWindow
    scaleAvg = float(sum) * whlScale
    line = "{:4.1f}".format(avg)
    lblMeasRevPS.configure(text = line)

    if (scaleAvg >= 0.1 or (not manualMode)):
        setPlayerRate(scaleAvg)
        ##print("            avg=",line +  "scaleAvg=",scaleAvg)


    ## Check to make sure the movie is still running.
    if (playerRunning):
        state = str(player.get_state())
        ##print("PlayerRunning=",playerRunning)
        ##print ("state=",state,".",type(state))
        if ([attachment=0]rasp screen.jpg[/attachment][attachment=0]rasp screen.jpg[/attachment]state == "State.Ended"):
            ##print("mrl=",mrlFile)
            player.set_mrl(mrlFile)
            setPlayerPos(0.0)
            player.play()  ## player must have finished. Loop to start
            print("re-starting")


    ##go to sleep for a while
    window.after(500,statUpdate)

    #x = player.get_rate()
    ##print (x)
    #print ("wheel count, avg",whlCount, avg)

statUpdate() 
window.attributes("-topmost", True)  ## set controller to be above all windows
window.mainloop()
GPIO.cleanup()


Attachments
rasp screen.jpg
mock up
rasp screen.jpg (201.34 KiB) Viewed 9659 times

davidt37
Posts: 3
Joined: Sun Feb 24, 2019 7:37 pm

Re: PiCycling – bike trainer controlled video

Tue Feb 26, 2019 1:00 am

Just noticed a file corruption in the code. The line that contains:

Code: Select all

 if ([attachment=0]rasp screen.jpg[/attachment][attachment=0]rasp screen.jpg[/attachment]state == "State.Ended"):
should be:

Code: Select all

if (state == "State.Ended"):
Here is the full code, with the corrections applied:

Code: Select all


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
Assumptions:
Python3 Virtual Trainer 1.1.1
By David Tamashiro

Hardware connections:
Pin 6 GND
Pin 1 3.3V+
Pin 12 (BCM18 or GPIO18)  Sensor in.  Pulled up to 3.3V+

Also assumes the following commands have been run:
sudo pip3 install python-vlc


"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""


import vlc
from tkinter import *
from tkinter import filedialog
from tkinter.ttk import Progressbar
from tkinter import ttk

import RPi.GPIO as GPIO

#Global variables and "Constants"
constMaxPlayerRate= 2.0   ##Max player view rate 2x normal speed
constDefWheelRevPerSec = 1.0 ## Default ## of revolutions/sec
constDefTkWin = "580x40"
constWheelCounterSampInterval = 1000  ## in milli-seconds


whlScale = 0.0  ## scaling factor x weighted average

mrlFile = "sv.mp4"   ## Default File to run

## Current relative rate of player
curRate = 1.0
manualMode = False

## Wheel counter state and averager
whlCount = 0  ## current wheel count
whlResPtr = 0
whlRes = [0,0,0,0]
playerRunning = False
totalSamplingWindow = len(whlRes) * constWheelCounterSampInterval/1000.0


## Full size screen, no xLib for VLC 
Instance = vlc.Instance('-f --no-xlib --aout=adummy --no-audio')
player = Instance.media_player_new()


############################  #Wheel interupt handler
def wheelEventHandler (pin):
   global whlCount 
   whlCount += 1
   #print("Handling wheel event", pin, " count=",whlCount)

## Set BCM18, HW pin 12 as input
GPIO.setmode(GPIO.BCM)
GPIO.setup(18,GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(18,GPIO.FALLING,callback=wheelEventHandler,bouncetime=50)


window = Tk()
window.title("Virtual Bike Tour 1.1")
winsize = constDefTkWin + "+50+50"
window.geometry(winsize)
style=ttk.Style()

geo=window.geometry()
print(geo)


style.theme_use('default')
style.configure("red.Horizontal.TProgressbar",background='red')
style.configure("green.Horizontal.TProgressbar",background='green')

lblMeasRevPS = Label(window, text = "0.0")

def startClicked():
    global whlScale, playerRunning
    if (not playerRunning):
        playerRunning = True
        whlInput = float (txtAdjRevPerSec.get())
        if (whlInput <= 0):
            whlInput = 10
        player.set_mrl(mrlFile)
        setPlayerPos(0.0)
        player.play()
       

def stopClicked():
    file = open("vtraindef.py","w")
    line = "mrlFile = '" + mrlFile + "'\n"
    file.write(line)
    line= "setWheelAdj(" + txtAdjRevPerSec.get() + ")" + "\n"
    file.write(line)
    
    geolist= window.geometry().split("+")

    line = "window.geometry('" + constDefTkWin + "+" + geolist[1] + "+" + geolist[2] + "')\n"
    file.write(line)
    line= "print('reading in defaults')\n"
    file.write(line)
    file.close()
    GPIO.cleanup()
    quit()

def fileClicked():
    global mrlFile
    file = filedialog.askopenfilename()
    if (len(file) > 1):
        mrlFile= file
        print("Loading file:", mrlFile)


def mscrollCmd(obj1, obj2='', obj3=''):
    print("in mscroll", obj1)
    if (obj1 =="scroll"):
        pos = player.get_position()
        if (obj2 =='-1'):
            pos -= 0.05
        elif (obj2 == '1'):
            pos += 0.05
        setPlayerPos(pos)

    elif (obj1 == "moveto"):
        pos = float(obj2)
        setPlayerPos(pos)


def scrollCmd(obj1, obj2='', obj3=''):
    global manualMode
    if (obj1 =="scroll"):
        if (obj2 =='-1'):
           setPlayerRate(curRate - 0.1)
        elif (obj2 == '1'):
           setPlayerRate(curRate + 0.1)

    elif (obj1 == "moveto"):
        x = float(obj2)
        setPlayerRate(x* constMaxPlayerRate)
        manualMode = True

 
def updateWhlScale(obj=''):
    global whlScale, manualMode
    revPerSec = float(txtAdjRevPerSec.get())
    if  (revPerSec < 0.5):
        revPerSec = 0.5

    whlScale = 1.0/(revPerSec * (totalSamplingWindow))
    print("whlScale changed to ",whlScale)
    manualMode = False


txtAdjRevPerSec = Entry(window,width=5)
txtAdjRevPerSec.bind('<Return>',updateWhlScale)

lblRevPerSec = Label(window, width=5, text="Rev/S ")
lblAdjRevPerSec = Label(window, text="1xRev ")
lbRPS = Label(window,text = '   Player Speed   ')
lbVidPos = Label(window,text = '  Position  ')
xbar = Scrollbar(window,orient="horizontal",command = scrollCmd)
mbar = Scrollbar(window,orient="horizontal",command = mscrollCmd)


startBtn = Button(window, text="Start",command=startClicked)
stopBtn = Button(window, text="Stop",command=stopClicked)
fileBtn = Button(window, text="Select Tour",command=fileClicked)

col=0
lblAdjRevPerSec.grid(column=col,row=0)
col += 1
txtAdjRevPerSec.grid(column=col,row=0)
col += 1
lblMeasRevPS.grid(column=col,row=0)
col += 1
lblRevPerSec.grid(column=col,row=0)
col += 1
fileBtn.grid(column=col,row=0)
col += 1
mbar.grid(column=col,row=0,sticky="we")
lbVidPos.grid(column=col,row=1)
col += 1
xbar.grid(column=col,row=0,sticky="we")
lbRPS.grid(column=col,row=1)
col +=1
startBtn.grid(column=col,row=0)
col +=1
stopBtn.grid(column=col,row=0)

playerPaused = False

def setPlayerRate(newRate):
    global curRate, playerPaused


    ## Only change the rate if change is more than 10%.  Hopefully reduces jitter
    if (abs(curRate - newRate) <= 0.05):
        return

    curRate = newRate

    if (curRate < 0.0):
        curRate = 0.0
    elif (curRate > constMaxPlayerRate):
        curRate = constMaxPlayerRate
    ratePerc = curRate/constMaxPlayerRate
    xbar.set(ratePerc,ratePerc)
    if (curRate <= 0.01):
       player.set_pause(True)
       playerPaused = True
       print("Player paused")
    elif (playerPaused):
        player.set_pause(False)
        playerPaused = False
        print("Player un-paused")
    player.set_rate(curRate)
    print("Player rate set to ",curRate)

def setPlayerPos(pos):
        if (pos <= 0.0):
            pos = 0.0
        elif (pos > 1.0):
            pos = 1.0
        mbar.set(pos,pos)
        player.set_position(pos)
        print("pos=",pos)



def setWheelAdj(RevPerSec):
    txtAdjRevPerSec.delete(0,END)
    txtAdjRevPerSec.insert(0,str(RevPerSec))
    updateWhlScale()


## Set GUI defaults
setWheelAdj(constDefWheelRevPerSec)
setPlayerRate(1.0)

try:
     exec(open("vtraindef.py").read())
except:
     print("No state file found. Goiong with defaults")


## update status checker and gui updater
def statUpdate():
    global whlCount,whlRes,whlResPtr, mrlFile

    ## Save current wheel count
    whlRes[whlResPtr] = whlCount
    whlCount = 0

    whlResPtr += 1
    if (whlResPtr >= len(whlRes)):
        whlResPtr = 0

    ## Calculate moving average
    sum = 0
    for i in whlRes:
        sum = sum + i
    avg = float(sum) / totalSamplingWindow
    scaleAvg = float(sum) * whlScale
    line = "{:4.1f}".format(avg)
    lblMeasRevPS.configure(text = line)

    if (scaleAvg >= 0.1 or (not manualMode)):
        setPlayerRate(scaleAvg)
        ##print("            avg=",line +  "scaleAvg=",scaleAvg)


    ## Check to make sure the movie is still running.
    if (playerRunning):
        state = str(player.get_state())
        ##print("PlayerRunning=",playerRunning)
        ##print ("state=",state,".",type(state))
        if (state == "State.Ended"):
            ##print("mrl=",mrlFile)
            player.set_mrl(mrlFile)
            setPlayerPos(0.0)
            player.play()  ## player must have finished. Loop to start
            print("re-starting")


    ##go to sleep for a while
    window.after(500,statUpdate)

    #x = player.get_rate()
    ##print (x)
    #print ("wheel count, avg",whlCount, avg)

statUpdate() 
window.attributes("-topmost", True)  ## set controller to be above all windows
window.mainloop()
GPIO.cleanup()



Simo_fine
Posts: 1
Joined: Tue Sep 24, 2019 6:18 am

Re: PiCycling – bike trainer controlled video

Tue Sep 24, 2019 6:33 am

Hi,
I'm currently working on similar project at school which we plan to donate to an organization that work with people with intellectual disabilities. These individuals are adults who behaves as kids due to their disabilities. The project is to create same device as yours but only play a cartoon video to motivate these individuals to get on the bike. As of now I already purchased all required materials "Hall effect sensor, raspberry Pi 3, magnets...etc" the only problem I have is the code which will play the video "no speed monitoring is required". The code need to play the video once the user start pedaling, and pause for 30 seconds after 10 seconds from the last movement of the pedal. If the user resumes the video resumes playing. Would you please help me to create this code, your help will be greatly appreciated! Also if you could instruct me on the format if the video and its characteristic will be plus!
Thank you in advance!

Return to “Other projects”