Skip to content

Instantly share code, notes, and snippets.

@oleeander
Last active August 29, 2015 14:21
Show Gist options
  • Save oleeander/e36651dfe57a8207fd11 to your computer and use it in GitHub Desktop.
Save oleeander/e36651dfe57a8207fd11 to your computer and use it in GitHub Desktop.
Video player `mpv' controlled by an exercise bike with integrated hall effect sensor.
#!/usr/bin/env python3
# Copyright (C) 2015 Oleander Reis <oleander@oleander.cc>
#
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
# claim that you wrote the original software. If you use this software
# in a product, an acknowledgment in the product documentation would be
# appreciated but is not required.
# 2. Altered source versions must be plainly marked as such, and must not be
# misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
import RPi.GPIO as GPIO
import time
import socket
import json
hall_sensor_pin = 18 # connect hall-sensor to physical pin 18 (IO-5 on banana pi)
mpv_socket = '/tmp/mpv.socket'
GPIO.setmode(GPIO.BOARD)
GPIO.setup(hall_sensor_pin, GPIO.IN, GPIO.PUD_DOWN)
class Mpv():
def __init__(self, mpv_socket):
self.mpv = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.mpv.connect(mpv_socket)
def _recv_reply(self):
buf = b''
while b'\n' not in buf:
tempbuf = self.mpv.recv(1024)
if not tempbuf: return None
buf += tempbuf
return buf
def set_property(self, key, value):
cmd = {
'command': ['set_property', key, value]
}
self.mpv.send((json.dumps(cmd) + '\n').encode()) # see `man --pager='less -p ^JSON\ IPC' mpv'
reply = self._recv_reply()
return reply
def play(self):
return self.set_property('pause', False)
def pause(self):
return self.set_property('pause', True)
def close(self):
self.mpv.close()
class Lights():
def __init__(self):
# 0 = 11, 1 = 12, 2 = 13, 3 = 15, 4 = 16
self._pins = [11, 12, 13, 15, 16]
self._current_light_position = 11
for pin in self._pins:
GPIO.setup(pin, GPIO.OUT, GPIO.PUD_DOWN)
def _determine_light_position(self, cadence_milliseconds):
if cadence_milliseconds > 3000:
return 11
elif cadence_milliseconds > 1200:
return 12
elif cadence_milliseconds <= 1200 and cadence_milliseconds > 800:
return 13
elif cadence_milliseconds <= 800 and cadence_milliseconds > 600:
return 15
elif cadence_milliseconds <= 600:
return 16
def set_light_position(self, cadence_milliseconds):
light_position = self._determine_light_position(cadence_milliseconds)
if self._current_light_position != light_position:
for pin in self._pins:
if pin != light_position:
GPIO.output(pin, False)
else:
GPIO.output(pin, True)
self._current_light_position = light_position
class Cadence():
def __init__(self):
self.last_milliseconds = 0
self.delta_milliseconds = 10000
def _milliseconds_delta(self):
current_milliseconds = int(round(time.time() * 1000))
delta_milliseconds = current_milliseconds - self.last_milliseconds
return current_milliseconds, delta_milliseconds
def revolution(self, channel):
delta = self._milliseconds_delta()
self.last_milliseconds = delta[0]
self.delta_milliseconds = delta[1]
def get_current_milliseconds_delta(self):
return self._milliseconds_delta()[1]
def set_values(cadence, lights):
if cadence.get_current_milliseconds_delta() > 1200: # pause when cadence goes just under 1 Hz
mpv.set_property('contrast', -100) # brightness seems to be broken in libvdpau-sunxi
mpv.set_property('saturation', -100)
mpv.pause()
lights.set_light_position(cadence.get_current_milliseconds_delta())
else:
mpv.play()
mpv.set_property('contrast', int(20 - cadence.delta_milliseconds / 20)) # FIXME: works but generates illegal values sometimes; mpv is robust
mpv.set_property('saturation', int(20 - cadence.delta_milliseconds / 15)) # FIXME: set these values smoothly
#print(int(20 - delta_milliseconds / 20), int(20 - delta_milliseconds / 15))
lights.set_light_position(cadence.delta_milliseconds)
while not 'mpv' in globals(): # reconnect when mpv hasn't started yet (FIXME: let systemd do this)
try:
mpv = Mpv(mpv_socket)
except (FileNotFoundError, ConnectionRefusedError):
time.sleep(1)
pass
lights = Lights()
cadence = Cadence()
GPIO.add_event_detect(hall_sensor_pin, GPIO.RISING, callback=cadence.revolution)
try:
while True:
set_values(cadence, lights)
time.sleep(0.1)
except KeyboardInterrupt:
mpv.close()
GPIO.cleanup()
pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment