Raspberry Pi TV (Part 2)
by Philippe Olivier
Created 2021-07-30
You can read the first part of this project here: Raspberry Pi TV (Part 1).
1. Circuit
When facing the sensor (with the rounded part in front of you), the legs of the sensor are as such:
- Left leg is OUT: Connect to GPIO 17 (pin 11).
- Center leg is GROUND: Connect to ground (e.g., pin 9).
- Right leg is 3V3: Connect to 3v3 power (e.g., pin 1).
2. IR
We start by getting the IR receiver to register the signals sent by the remote.
2.1. IR software
First, install LIRC, ir-keytable, and evdev:
$ sudo apt-get install lirc ir-keytable $ pip3 install evdev
Uncomment the line "dtoverlay=gpio-ir,gpio_pin=17" in /boot/config.txt
and reboot. Then, make sure that everything works:
$ sudo ir-keytable -c -p all -t
Pressing keys on a remote should display some kind of feedback in the terminal.
2.2. Simple IR script
Let's do a simple test to figure out how to code the rest of this project. First, we use this small script to find out the codes associated with buttons "1" and "2" of our remote:
import evdev import pathlib path = next(pathlib.Path('/dev/input/by-path/').glob('platform-ir-receiver*')) irr = evdev.InputDevice(path) print('Press keys on the remote.') for event in irr.read_loop(): if event.type == 4: print(event.value)
In my case:
$ python3 simple_test.py Press keys on the remote. 460548 460549 ^C
So, button "1" is 460548 and button "2" is 460549. Now, let's slightly modify the previous script:
import evdev import pathlib path = next(pathlib.Path('/dev/input/by-path/').glob('platform-ir-receiver*')) irr = evdev.InputDevice(path) print('Let\'s test buttons 1 and 2.') for event in irr.read_loop(): if event.type == 4: if event.value == 460548: print('You pressed button 1!') elif event.value == 460549: print('You pressed button 2!')
Which results in:
$ python3 simple_test2.py Let's test buttons 1 and 2. Press Ctrl-C to quit. You pressed button 1! You pressed button 1! You pressed button 2! You pressed button 1! You pressed button 2! You pressed button 2! ^C
We should now be able to make a Python script to send commands to VLC with the TV remote.
3. VLC
VLC should already be installed. We need to install the VLC Python bindings:
$ pip3 install python-vlc
We can test that this works with this simple script:
import vlc media = vlc.MediaPlayer("legal-media-file.mkv") media.play() input() # To prevent the script from exiting before the file finishes playing.
4. Integrated IR/VLC script
We start by mapping the remote keys that interest us with their codes. In my case:
Key | Code |
---|---|
Play | 460615 |
Pause | 460618 |
Stop | 460614 |
Fast forward | 460616 |
Fast backward | 460613 |
Skip intro | 460617 |
This script will take as its argument the path of the video file to be played. The video will be launched in full screen mode and then immediately paused, so that the viewer can start playing it when they want. Fast forward/backward will move the video forward/backward by 10 seconds per key press. Pressing stop twice will exit the script. It is possible to customize a button to skip the introduction of videos. All of this can be achieved with the script play.py
:
import evdev import pathlib import sys import time import vlc file_path = sys.argv[1] media = vlc.MediaPlayer(file_path) media.toggle_fullscreen() media.play() time.sleep(0.5) # This ensures that the video has time to go full screen before pausing. media.pause() # The video starts paused. device_path = next(pathlib.Path('/dev/input/by-path/').glob('platform-ir-receiver*')) irr = evdev.InputDevice(device_path) # Signal codes. PLAY = 460615 PAUSE = 460618 STOP = 460614 FAST_FORWARD = 460616 FAST_BACKWARD = 460613 SKIP_INTRO = 460652 # When each code was last registered. signal_times = {PLAY: 0, PAUSE: 0, STOP: 0, FAST_FORWARD: 0, FAST_BACKWARD: 0, SKIP_INTRO: 0} def duplicate_signal(signal: int) -> bool: """Prevents the script from registering too many of the same signals. When a button is pressed on the remote, it sends the same signal multiple times in a short interval. However, we want the IR receiver to register the signal only once. This function tells us if the signal received is a duplicate. Returns: True if the signal is a duplicate, False otherwise. """ now = time.time() since_last_signal = now - signal_times[signal] signal_times[signal] = now if since_last_signal < 0.15: return True return False # The video will stop if the STOP button is pressed twice. stop_counter = 0 # Main loop. for event in irr.read_loop(): if event.type == 4: # If the signal is not one we want, or a duplicate, skip it. if event.value not in signal_times or \ duplicate_signal(event.value): continue # If the button is not STOP, reset the STOP counter. if event.value != STOP: stop_counter = 0 # Buttons. if event.value == PLAY: media.play() elif event.value == PAUSE: media.pause() elif event.value == STOP: stop_counter += 1 if stop_counter > 1: break elif event.value == FAST_FORWARD: media.pause() media.set_time(min(media.get_length(), media.get_time()+10000)) time.sleep(0.2) media.play() elif event.value == FAST_BACKWARD: media.pause() media.set_time(max(0, media.get_time()-10000)) time.sleep(0.2) media.play() elif event.value == SKIP_INTRO: media.pause() media.set_time(min(media.get_length(), media.get_time()+40000)) time.sleep(0.2) media.play()
We wrap this Python script in a bash script (play.sh
), to allow starting VLC on the TV from the terminal, and enabling all protocols for ir-keytable. This script also takes the file path as input, and deletes the file when it is done playing.
#!/bin/bash export DISPLAY=:0 sudo ir-keytable -p all python3 play.py "$1" export DISPLAY=:1 rm "$1"
The previous two scripts are used in the following way, from our local machine:
$ ssh -i ~/.ssh/rpi-tv pi@rpi-tv.local "./play.sh \"legal-media-file.mkv\" &" &