incubate!(bang) has officially started back up - check incubatebang.com for details

isotope|eleven


About_jd

J.D. Warren

developer

JD Warren is a Ruby on Rails developer and extensive hardware hacker. Prior to joining isotope|eleven, he was a builder focusing on remodeling apartments and rental houses. In his spare time, JD builds robots, electronic circuits, and is a technical writer with work in several online and print publications and a recent book titled "Arduino Robotics".

Blog Posts

Nathan Stott from Whiteboard IT giving a talk on Node.js:

Ho-Sheng Hsiao giving the second talk:

Chris Winslet from MongoHQ, giving a talk on Twilio and Mongo. There are 3 videos in this talk:

Today I am going to walk through our recent continuous integration Traffic light notifier project that we just finished at the office. This project stemmed from my company's desire to immediately know if a developer has broken a software project, and what better way to do that than to have a huge red light flashing in your face. We connected an old salvaged traffic light fixture to our Jenkins CI-server that monitors the testing status of all of our current software projects. If all our tests are passing, the light stays green, if any test fails the light turns red to provide a visual notification of a problem. While Jenkins is running a test suite on any project, the yellow light will flash to let us know of the activity.

So how does one connect a 48" tall traffic light to a continuous integration server? With a Ruby script, an Arduino, and a few relays of course.

Traffic Light

The Ruby code will create a serial connection with the Arduino to send data, then create a web connection with the CI server to request the build status data via our CI server's built in API. A quick look through the returned data will give us a chance to see if there are any problems – if so, we'll send a signal to the Arduino to change the light status, otherwise it stays green. The Ruby script requires 3 gem dependencies to run: faraday, json, and serialport – all available from rubygems.org (eg. gem install faraday).

# Isotope11 continous integration server Traffic-light
# Ruby script to monitor json output from Jenkins CI-server, and output the status of projects to a Traffic-light. 
# If all builds are passing, the light is green. 
# If a job is currently building, the yellow light flashes. 
# If any job is failing, the red light is turned on and green turned off.
require "serialport"
require "json"
require "faraday"
require "net/http"

# create a new Serial port for the Arduino Uno which uses port /dev/ttyACM0. Older Arduinos should use /dev/ttyUSB0
sp = SerialPort.new("/dev/ttyACM0", 9600)

# wait for connection
sleep(1)

# create a new Faraday connection with the Jenkins server to read the status of each job
conn = Faraday.new('http://your_Jenkins_server_address.com')
puts 'go to loop'

loop do
  begin
    # grab the json from the jenkins api
    response = conn.get('/api/json')
    # parse the response into a list of jobs that are being monitored
    jobs = JSON.parse(response.body)["jobs"]

    # search each job to see if it contains either "anime" (building) or "red" (failing)
    should_blink = jobs.detect{|j| j["color"] =~ /anime/ }
    should_red   = jobs.detect{|j| j["color"] =~ /red/ }
  rescue
    # if no response, assume server is down – turn on Red and Yellow lights solid
    server_down = true
  end

  # check results of job colors
  if should_blink
    # something is building... flash yellow light!
    puts "Something is building... flash yellow light!"
    sp.write("1")
  else
    # nothing is building... turn yellow light Off.
    #sp.write("2")
  end

  if should_red
    # something is red... turn On red light!
    puts "Something is broken... turn On red light!"
    sp.write("3")
  else
    # nothing is red... turn On green light.
    sp.write("4")
  end

  if server_down
    sp.write("5")
  end

  # wait 5 seconds
  sleep(5)
end

# close serial data line
sp.close

The Arduino board is fitted inside of the traffic light housing, and mounts to a perforated prototyping board from Radio Shack using some male-pin headers. Above the Arduino, are two small PC mount relays capable of switching up to 1 amp at 120vac – perfect for some low wattage light bulbs. The relay coils are controlled using a 5v signal, and only consume about 90mA at that voltage level, so we can use the Arduino's onboard 5v regulator to power the relay coils. Unfortunately, we cannot simply drive the relays directly from an Arduino pin because it can only supply around 40mA per pin and the inductive switching properties present in a relay might cause damage to the Arduino. Instead, we can use 2 small N-type signal transistors (either bjt or mosfet) to interface between each relay and the Arduino output pin. Building the relay board might require some hands-on tinkering, but is a rewarding task when complete (circuit schematic file included).

Arduino mounted inside Traffic Light traffic_light schematic

The Arduino code is simple, basically listening on the serial port for 1 of about 5 signals. If the Arduino detects a recognized serial byte, it will carry out a function to control the Traffic lights - there is no extra fluff, just what is needed. If you are having trouble locating an old Traffic light, or would like to build a smaller desktop version of the notifier, you can do so with only an Arduino and a few LEDs (red, yellow, and green) - you don't even have to solder anything!

Poor man's traffic light

// Isotope11 CI-server traffic light
// Arduino Uno with 2 relays (SPDT) attached to pins 4 and 7
// isotope11.com 2-9-12

// declare variables and output pins:
int inByte; // create a variable to hold the serial input byte
long lastTx = 0; // create a “long” variable type to hold the millisecond timer value
int yellow_light = 4; // create an output variable attached to pin 4
int red_green_light = 7; // create an output variable attached to pin 7

void setup() {
  Serial.begin(9600);   // start Arduino serial monitor at 9600bps
  pinMode(yellow_light, OUTPUT); // set up pin 4 as an output
  pinMode(red_green_light, OUTPUT); // set up pin 7 as an output
}

void loop() {
  // check serial buffer
  if (Serial.available() > 0){
    inByte = Serial.read();    // read serial byte
    Serial.println(inByte);     // print serial byte
    lastTx = millis(); // set the lastTx time-stamp variable equal to the current system timer value

    // the serial bits “49” - “53” are detected when the numeric buttons “1” – “5” are pressed on the keyboard.
    switch(inByte){
    case 49:  // if serial value received is "49" (number 1), blink yellow light
      digitalWrite(yellow_light, HIGH);
      delay(1000);
      digitalWrite(yellow_light, LOW);
      break;
    case 50:  // if serial value is "50" (number 2), turn yellow light off
      digitalWrite(yellow_light, LOW);
      break;
    case 51:  // if serial value is "51" (number 3), turn red light on (green off)
      digitalWrite(red_green_light, HIGH);
      break;
    case 52:  // if serial value is "52" (number 4), turn green light on (red off)
      digitalWrite(red_green_light, LOW);
      break;
    case 53:  // if serial value is "53" (number 5), turn green and yellow lights on solid (api error)
      digitalWrite(red_green_light, LOW);
      digitalWrite(yellow_light, HIGH);  
    }    
  }
  else {
    if ((millis() - lastTx) > 10000) {
       // it has been more than 10 seconds (10000 milliseconds) since any serial information has been received
       // assume there is a break in the PC connection, and turn red and yellow lights on solid.
      digitalWrite(red_green_light, HIGH);
      digitalWrite(yellow_light, HIGH);
    }
  }
}

Repo

The github repository is here.

Parts list:

  1. an old Traffic light
  2. Arduino Uno, Radio Shack part # 276-128 - $34.99
  3. PC prototyping board, Radio Shack part #276-168 - $3.19
  4. (2) PC pin relays, Radio Shack part #275-240 - $4.69 ea
  5. (2) NPN transistors or mosfets, Radio Shack part #276-2016 - $1.19 ea
  6. (2) 10kohm resistors, Radio Shack part #271-1335 - $1.19 pk
  7. (2) 100kohm resistors, Radio Shack part #271-1347 - $1.19 pk
  8. (20) male-pin breakaway headers, Sparkfun part#PRT-00116 - $1.50
  9. (4) 8mm bolts, 1” long (with nuts) - $1.00

Tools needed:

  1. wire/wire snips
  2. solder/soldering iron
  3. drill/drill bit

A form that allows you to create 2 models at once can be quite useful. For example, let's say you have a User and Image model with a has_many/belongs_to relationship. In your new User form, it is ideal to attach an Image to the User and upload them with a single form submission. To do this, we need to allow the User model to accept attributes for the Image that will be created with it... enter accepts_nested_attributes_for. With a few easy steps, you can create a complex form in no time.

First, edit the User model with a has_many association and the accepts_nested_attributes_for tag for the model you would like to include (Image in this case):

app/models/user.rb


class User < ActiveRecord::Base
  has_many :images
  accepts_nested_attributes_for :images
end

Make sure your Image model has a belongs_to association:

app/models/image.rb


class Image < ActiveRecord::Base
  belongs_to :user
end

Now, add the fields for Image in the new User form:

app/views/user/new.html.erb


<% form_for @user do |f| %>
  <%= f.label "Name" %>
  <%= f.text_field :name %>
  <% f.fields_for :images do |i| %>
    <%= i.label "Image" %>
    <%= i.file_field :attachment %>
  <% end %>
<% end %>

As you can see from the form above, the fields beginning with "f." will be passed to User, and the fields_for :images, denoted by "i." will be passed to Image.

Lastly, you will want to create an empty Image model in the controller for the form to use, that is associated with the User:

app/controllers/user/users_controller.rb


class UsersController < ApplicationController
  def new
    @user = User.new
    @image = @user.images.build
  end 
end

Obviously, your Users controller would need a standard Create method (not shown), but other than the above code, you need not add anything to the controller - rails takes care of the rest. And that's it! you should now be uploading mutliple models using the same form.