A Raspberry Pi Traffic Light with Capistrano Integration

I’ve been wanting to start a project using a Raspberry Pi for a while, I just needed to come up with a practical idea. A few months ago, I was in the process of automating my company’s deployment process using Capistrano — this seemed as good a time as any. I decided on picking up a real traffic light and using the Pi’s GPIO pins to control the individual lights through relays, using the Pi to trigger light sequences programmatically. I eventually hooked it into our Capistrano recipe, using light sequences to show the progress of our staging/production deployments.

I picked up a traffic light on Craigslist for around $50 and a Raspberry Pi B, as well as an 8 Channel 5V Relay Shield Module. Though the Pi’s GPIO outputs are only 3.3v, the relays are active on low inputs. This means as long as we give the relays an output marginally above 0v, they’ll be deactivated. Here is the Pi wired up to the relay board:

20140730_095310

Installing the wiringPi library and issuing the commands to send low signals to the relays made them click as expected. The next step was to wire the relay outputs to the traffic light. Here, I wrote a quick script to sequence through all three lights:

Time to get this hooked up to Capistrano so it can show the status of our deployment process. The first step was to install Apache/PHP and write a very simple script to illuminate the individual lights. I decided to use PHP to directly interact with the CLI wiringPi library. This definitely isn’t the most elegant solution, but remains simpler than some of the other API interactions I’d come across. The below script allows web requests to trigger specific lights to illuminate when requested with their corresponding GET parameter:

function clear_all()
{
    $pins = array(3,4,5);
    
    foreach($pins as $pin)
    {
        $cmd = "gpio write $pin 1";
        shell_exec($cmd);
    }
}

if(!empty($_GET['color']))
{
    clear_all();

    switch($_GET['color'])
    {       
        case 'yellow-blink-start':
            shell_exec("/etc/init.d/yellow-blinking start > /dev/null 2>&1 &");
            break;
            
        case 'yellow-blink-stop':
            shell_exec("/etc/init.d/yellow-blinking stop > /dev/null 2>&1 &");
            break;
            
        case 'red':
        
            $pin = 3;
            $inner_cmd .= "gpio write $pin 0 && sleep 5m && gpio write $pin 1";
            
            $cmd = "($inner_cmd) > /dev/null 2>&1 &";
            shell_exec($cmd);
            break;
            
        case 'green':
        
            $pin = 5;
            $inner_cmd .= "gpio write $pin 0 && sleep 1m && gpio write $pin 1";
            
            $cmd = "($inner_cmd) > /dev/null 2>&1 &";
            shell_exec($cmd);
            break;            
    }
}

Pretty straightforward. Basically, the script constructs a shell command and sends it to the background. This way, the web request isn’t waiting for the command to complete and we still timeout the lights after certain intervals. For instance, after green is show, it shuts off automatically after one minute. This is built directly into the shell command when it’s issued.

With the yellow light however, I needed to come up with a different solution. The plan  for the yellow light was for it to blink continuously on and off during the deployment process, but since that process can vary in length, we need to be able to tell it to start blinking and stop blinking, without requiring the web request to be held for that entire duration. To handle the yellow light blinking process, I created a simple daemon:

#!/bin/bash

do_start()
{
    touch /tmp/testfile
    
    while true;
    do
        if [ ! -f /tmp/testfile ]; then
             break
        fi
        
        gpio write 4 0
        sleep 1
        gpio write 4 1
        sleep 1
    done
}

do_stop()
{
    echo "stopping!"
    rm -rf /tmp/testfile
}


case "$1" in
   start)
     do_start &
     ;;
   stop)
     do_stop
     ;;
esac

Essentially, this service begins by creating a tmp file, followed by looping through a blinking sequence (one second delay between on/off). The loop lasts until the tmp file no longer exists. Stopping the service of course removes this temp file, and the loop terminates.

Great! At this point, I’m able to manually toggle the lights by visiting the corresponding URLs on my Pi. All that’s left is to create a Capistrano recipe. I don’t write a lot of Ruby, so I’m sure this can be done more efficiently.

require 'open-uri'

set :trafficlight_host, "http://10.0.0.214"

namespace :deploy do

  task :show_red do
    trafficlight_host = fetch(:trafficlight_host)
    begin
      open("#{trafficlight_host}/cap.php?color=red")  
    rescue OpenURI::HTTPError => ex
      puts "traffic light 404 Error"
    end
  end
  
  task :start_blinking do  
    trafficlight_host = fetch(:trafficlight_host)  
    begin
    open("#{trafficlight_host}/cap.php?color=yellow-blink-start")
    rescue OpenURI::HTTPError => ex
      puts "traffic light 404 Error"
    end    
  end
  
  task :stop_blinking do
    trafficlight_host = fetch(:trafficlight_host)  
    begin
    open("#{trafficlight_host}/cap.php?color=yellow-blink-stop")
    rescue OpenURI::HTTPError => ex
      puts "traffic light 404 Error"
    end    
  end  
  
  task :show_green do
    trafficlight_host = fetch(:trafficlight_host)
    begin
    open("#{trafficlight_host}/cap.php?color=green")
    rescue OpenURI::HTTPError => ex
      puts "traffic light 404 Error"
    end    
  end

  before :starting, :show_red
  before :started, :start_blinking
  after :finished, :stop_blinking
  after :finished, :show_green
  after :failed, :stop_blinking
  
end

The deployment went off as expected! As you can see, I initially show a red signal while picking branches and beginning the preparation to deploy. It then begins the blinking process until finishing with the “finished” hook. It’s now hung in the corner of my office and keeps me informed of deployments to our different server environments throughout the day!

20140910_095520

Share this: Facebooktwitterlinkedin