2012-07-02

Raspberry Pi GPIO and Motion

This post shows how I've set up Motion with the Raspberry Pi GPIOs to indicate motion detection and video production on LEDs and a push button snapshot control. As part of this project I have created a Python service to allow easy scripted control of the GPIOs. This allows you to set up commands you want to run on GPIs and simple command line access to set GPOs high, low or flash. If you're new to these blog posts take a look at: Battery powered, Wireless, Motion detecting Raspberry Pi and Motion Google Drive Uploader and Emailer.

The electronics

I used the Electronic Starter Kit for Raspberry Pi plus a few extra jumper wires to give me 3 LEDs to control and two push button inputs:

The circuit uses the "low to glow" principle - set the GPO low to light the LED. I won't repeat a load of GPIO information available elsewhere, here's a very good article to read: Getting Started with Raspberry Pi GPIO and Python. I'm using 3.3v for the positive rail, pins 11, 12 and 13 for the red, yellow and green LEDs and 7 and 22 for the two buttons.

Flashing an LED from Motion

Motion has a number of configuration options which allow you to run shell commands when specific events occur. For example on_motion_detected, defined as Command to be executed when a motion frame is detected. The Motion web interface allows you to change all of these on the fly without restarting the service - great for testing. The on_motion_detected event does not have a sibling event like "on_motion_not_detected" so you can't easily switch an LED on on the first event and off on the second. Therefore you have to write a little bit of code to light the LED, wait for a short period and then switch it off. This is a common thing to want to do - a momentary indicator.

Another common thing to want to do is to flash an LED continuously, this gives you 3 indications for each LED: On, Off and Flash. But flashing can only be achieved by repeatedly setting the GPO high and low. I wanted to flash the red LED while Motion is in the "event window" this is defined by two events:
  • on_event_startCommand to be executed when an event starts. An event starts at first motion detected after a period of no motion defined by gap.
  • on_event_end: Command to be executed when an event ends after a period of no motion. The period of no motion is defined by option gap.

Taking a Motion snapshot

There are a few controls available for Motion through the action section of its web interface. The snapshot action captures a jpeg and creates a symlink to it called lastsnap.jpg. I then show this snapshot on my web site  (http://jerbly.uk.to/picam) next to the live feed. You can trigger a snapshot from the command line using curl like this:
curl http://localhost:8080/0/action/snapshot
All I needed was a way to run commands when a button is pressed. I needed to capture the GPI state going from False normally to True briefly (while the button is down) and then back to False again when it's released.
So, instead of writing more and more little scripts to handle controlling the GPIOs I wrote a service to do it for me. This allows me to echo simple instructions like "red flash" and "red high" to the service and it takes care of continuously flashing the GPO or setting it high etc.

My GPIO service

The code is available on GitHub here: Jerbly Raspberry Pi code. Download gpioservice.py and the example config file gpioservice.cfg to somewhere on your Raspberry Pi. Then, before you run it up, change the config file. This is very important as the config file determines which pins should be set as inputs and outputs and it also sets the initial mode of the pin (e.g. high). So you want to get this right before you start it up and blow something up!

The service gives you the follow features:
  • Name to pin mapping: so you can send "red flash" or "motor low" commands
  • Common modes: 
    • High: set the pin high
    • Low: set the pin low
    • Flash: flip the pin between high and low every 0.25 second
    • LowShot: set the pin low for 0.25s then leave it high
    • HighShot: set the pin high for 0.25s then leave it low
  • Commands are sent through a named pipe (fifo) which has write permissions for all, this means you don't have to be root to control the GPOs!
  • Run shell commands for button presses on GPIs
The code in gpioservice.py uses two secondary threads alongside the main thread. The main thread is a blocking reader of the named pipe, it just parses the input on the pipe and sets the mode on the GPO objects. The GPO thread sleeps for 0.25s each loop, inverts the current flash state and then runs through each GPO object calling its action method. The action method determines, based on the mode, whether to set the pin high or low. It's this that means you can have flash, lowshot and highshot. The GPI thread sleeps for 0.1s each loop and then calls the action method on all the configured GPI objects. The action method reads the input and uses a state variable to trigger the command to run when the state first changes from False to True and not to continuously call the command if the button is held down. (Other modes may be added to this in the future so you can have "button held" operations).

Setting it all up

My Motion and GPIOs Raspberry Pi is set up to do this:
  • Flash the red LED while in the "event window" (see above)
  • Light the yellow LED when Motion starts making a video
  • Light the green LED when it's finished making the movie and my Motion Google Drive Uploader and Emailer is running.
  • When the upload has finished switch off the yellow and green LEDs
  • When a Motion frame is detected briefly flash the green LED (lowshot)
  • When the first button is pressed take a snapshot
  • When the second button is pressed just lowshot the green LED for testing
Here's the gpioservice.cfg file:
[gpos]
fifo = /tmp/gpopipe

[gpo_pins]
# name = pin, initial_mode {high/low/flash}
red = 11, high
yellow = 12, high
green = 13, high

[gpi_pins]
# name = pin, command
motion_snapshot = 22, curl http://localhost:8080/0/action/snapshot
green_flash = 7, echo "green lowshot" >> /tmp/gpopipe

[options]
debug = False

Note that the fifo is configured at the top. The service takes care of creating this and setting the permissions. Then in the gpi_pins section you can see how to briefly flash the green LED when the button is pressed on pin 7.

Here's the Motion events section from my Motion config file:
# Command to be executed when an event starts. (default: none)
# An event starts at first motion detected after a period of no motion defined by gap
on_event_start echo "red flash" >> /tmp/gpopipe

# Command to be executed when an event ends after a period of no motion
# (default: none). The period of no motion is defined by option gap.
on_event_end echo "red high" >> /tmp/gpopipe

# Command to be executed when a picture (.ppm|.jpg) is saved (default: none)
# To give the filename as an argument to a command append it with %f
; on_picture_save value

# Command to be executed when a motion frame is detected (default: none)
on_motion_detected echo "green lowshot" >> /tmp/gpopipe

# Command to be executed when motion in a predefined area is detected
# Check option 'area_detect'. (default: none)
; on_area_detected value

# Command to be executed when a movie file (.mpg|.avi) is created. (default: none)
# To give the filename as an argument to a command append it with %f
on_movie_start echo "yellow low" >> /tmp/gpopipe

# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none)
# To give the filename as an argument to a command append it with %f
on_movie_end /etc/motion/movie_end %f

# Command to be executed when a camera can't be opened or if it is lost
# NOTE: There is situations when motion doesn't detect a lost camera!
# It depends on the driver, some drivers don't detect a lost camera at all
# Some hang the motion thread. Some even hang the PC! (default: none)
; on_camera_lost value

Note that the on_movie_end setting calls a script that I have placed in /etc/motion to handle controlling the LEDs and uploading to Google Drive:
#!/bin/sh
echo "green low" >> /tmp/gpopipe
/root/py/jerbly/src/uploader.py /etc/motion/uploader.cfg $1
echo "green high" >> /tmp/gpopipe
echo "yellow high" >> /tmp/gpopipe

In Action

This first video shows the green lowshot by pressing the button connected to pin 7:

video

This second video shows the sequence when I trigger a motion event on the camera and start a recording. Note just after I remove the card from the camera there are two motion frame flashes on the green LED too.
video

Post a Comment