Blog | About | Hire Me

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\" &" &
This website is generated by Emacs.