Continuous integration build traffic lights with TeamCity

Summary: Put simply we had a requirement to make a broken build of our software incredibly obvious to everyone, the purpose was to get everyone’s buy in (developers, testers, and management alike) to build quality, after all everyone knows what a red traffic light means!

In my current contract we have recently switched from an incredibly monolithic Team Foundation Server custom build to using the frankly awesome TeamCity.

Due to the historical nature of this company the true continuous integration ethic had never really been instilled, with lack of responsibility for broken builds an unfortunate by product. The switch to TeamCity was mainly a technical decision, with more in house experience of CI using TeamCity and to be honest it seemed like a far less daunting (and more enjoyable) task than getting it right with TFS. Our new TeamCity build has given us the following benefits:

  • Reduced build times by over 60%.
  • Focused us on only ever building once, every developer integration with a successful build is a potential release candidate, i.e. everything is built in Release mode.
  • Improved build notification options, the TeamCity WebUI, taskbar notifier and RSS feeds.

The final point is what I will focus on in this post, put simply we had a requirement to make a broken build off our software incredibly obvious to everyone, the purpose was to get everyone’s buy in (developers, testers, and management alike) to build quality, after all everyone knows what a red traffic light means!

So a home project ensued, which started with me winning the following Ebayauction.

Post3-EbayTrafficLightsWin

These lights are pretty simple, being 24Volt DC and coming supplied with two 24volt/40W bulbs (unfortunately only one of which worked, I bought replacements online from BLT Direct, opening up the traffic lights by removing the lenses I was pleasantly surprised to find the wiring all in excellent condition.

Post3-TrafficLightsInsides

Next step was to put together some sort of parts list to get these lamps lighting up our TeamCity build status.

  1. Traffic lights – Got.
  2. Some spare bulbs BLT Direct – Got.
  3. A power supply capable of running the bulbs continuously, so around 2Amps rating, i.e. 40W/24volt = 1.6Amps.
  4. An Arduino Uno prototyping microprocessor board.
  5. An Arduino ethernet shieldto allow the Arduino board to chat to TeamCity over the network.
  6. A suitable relay, Single Poll Double Throw (SPDT), capable of switching 2Amps at 24 volts with a 5volt control signal (from the Arduino board).
  7. A plastic case to house the Arduino board and ethernet shield off board of the traffic lights.
  8. A power supply capable of running the Arduino board, nominal 5volts output.
  9. Minimum 2 Amp rated wire, for internal lamp wiring.
  10. A handful of insulated crimp butt connectors.
  11. Some low rated in equipment wire various colours (for the power, ground and control wires from the Arduino to the relay).
  12. Insulating tape (to keep things tidy).
  13. Heatshrink tubing (to keep the power, ground and control wires from the Arduino to the relay nice and tidy).
  14. Double sided sticky pads.

For 2. I found a 19volt laptop AC-DC power supply that would do nicely (it had a 3.42A rating so could handle powering a traffic lamp continuously, i.e. 40W/19volt = 2.1Amps).

For 6. I used a multi voltage AC-DC power supply, that would output 6Volts at up to 300mA, perfect for the Arduino which can handle a voltage input of around 3-10Volts.

Post3-TrafficLightsPowerSupplies

The rest of the parts I bought from the links in the parts list, mostly from maplin.co.uk proto-pic.co.uk and

The build Part #1, checking the lights

I began the build by simply wiring up each traffic light bulb directly to a low voltage power supply to check the integrity of the supplied wiring which passed with flying colours.

Post3-TrafficLightsSimpleWiring

The build Part #2, switching the relay

The next step was to prove how to control the relay with the Arduino, I started by simply wiring it up as follows. See here for an explanation of Normally Open (NO) and Normally Closed (NC) states.

Post3-ArduinoAndRelayWiringDiagram

I adapted an example that did nothing more than simply switch digital pin 8 of the Arduino on and off as below.

/* Blink without Delay

Turns on and off a light emitting diode(LED) connected to a digital
pin, without using the delay() function. This means that other code
can run at the same time without being interrupted by the LED code.

The circuit:
* LED attached from pin 13 to ground.
* Note: on most Arduinos, there is already an LED on the board
that’s attached to pin 13, so no hardware is needed for this example.

created 2005
by David A. Mellis
modified 8 Feb 2010
by Paul Stoffregen

This example code is in the public domain.

http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

*/

// constants won’t change. Used here to
// set pin numbers:
const int ledPin = 8; // the number of the LED pin

// Variables will change:
int ledState = LOW; // ledState used to set the LED
long previousMillis = 0; // will store last time LED was updated

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000; // interval at which to blink (milliseconds)

void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}

void loop()
{
// here is where you’d put code that needs to be running all the time.

// check to see if it’s time to blink the LED; that is, if the
// difference between the current time and last time you blinked
// the LED is bigger than the interval at which you want to
// blink the LED.
unsigned long currentMillis = millis();

if(currentMillis – previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;

// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;

// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}

With the script loaded on to the Arduino board the on board LED on the relay flashed as the NO (Normally Open) state was made… not particularly exciting but proved that side of the circuit.

[youtube=http://www.youtube.com/watch?v=n54ThO9-3lY&w=448&h=252]
Arduino blinking relay LED from NO (Normally Open) to NC (Normally Closed)

The build Part #3, switching the lights with the relay

The next step was to get the relay wired up as per the diagram and schematic below, the relay can also be clearly seen in the top right of the traffic light housing in the picture below.

Post3-TrafficLightsWiringWithRelayPost3-TrafficLightsWiringDiagram

You can see from the schematic that I chose to wire the Normally Closed (NC) terminal of the relay to the red light of the traffic lights. The thought behind this is that in an errored or unknown state I always wanted the traffic light to display red and to switch to green only with a known good build state.  So with the above all in place I could prove (using the same Arduino sketch as in step 2) that we could switch between NC (red light on) and NO (green light on).
Post3-TrafficLightsNOGreen

The build Step #4, talking to TeamCity

I went with a very simple approach for querying TeamCity for a build status, I have described in more detail when working out the best approach in the TeamCity developer forum but basically it involved developing a simple TeamCity plugin that returns just an HTTP header response, reflecting the status of a selected TeamCity build project, the plugin does the following:

  1. Exposes a non authenticated simpleBuildStatus page “http://<myTeamCityServer>:<port>/simpleBuildstatus.html?projectName=<myTeamCityProjectName>&guest=1” that returns an HTTP Response Header (no HTML page content in the response).
  2. The simpleBuildStatus page returns an ‘HTTP/1.1 200 OK’ in the HTTP Response Header if all build configurations under the selected build project are currently successful.
  3. The simplebuildStatus page returns an ‘HTTP/1.1 409 CONFLICT’ in the HTTP Response Header if any build configurations under the selected build project are currently unsuccessful

For reference I found the following resources useful when developing my first TeamCity plugin

http://www.jetbrains.com/idea/download/index.html; I used IntellijIDEA 10.5 (Community Edition)as my Java IDE of choice.
http://confluence.jetbrains.net/display/TCD6/Bundled+Development+Package; The sample TeamCity development package that demonstrates how to develop various plugins

http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u25-download-346242.html; Needed as a prerequisite to building the above sample TeamCity development package.

http://confluence.jetbrains.net/display/TCD6/Plugins+Packaging; This helped me understand the options for packaging the TeamCity Plugin, i.e. as a JAR that you drop into the TeamCity data directory/plugins folder.

http://confluence.jetbrains.net/display/TCD6/Installing+Additional+Plugins; And some more general basic information about installing TeamCity plugins.

The following two screen shots show the HTTP 200 OK and HTTP 409 CONFLICT status codes that the SimpleBuildStatus plugin returns for successful and failed builds respectively.

Post3-TCSimpleBuildStatusPluginHTTP200
Post3-TCSimpleBuildStatusPluginHTTP409

If a build name isn’t provided in the url the following HTTP 400 error is displayed

Post3-TCSimpleBuildStatusPluginError

To get the Arduino Uno chatting to TeamCity I bought an Arduino Ethernet shield at the same time as the board.  A “shield” in the Arduino world is simply an expansion board that sits on top of the Arduino board, providing additional functionality, in this case ethernet capability. http://www.shieldlist.org is a great resource for working out shield and board compatibility for more complex projects.

It was now time to author an Arduino sketch that would pole the TeamCity SimpleBuildStatus plugin for a project build status, checking the returned HTTP status code and switching the relay to Normally Open (NO) if it’s a 200 and to Normally Closed (NC) if it’s a 409.  Remember I have already wired the traffic lights up to the relay in step 3 above.

The resulting first cut at the sketch was as follows:

Traffic lights client

This sketch connects to a software build, continuous integration (CI) server
(e.g. TeamCity) using an Arduino Wiznet Ethernet shield and switches a SPDT relay
depending on the state (HTTP response code) returned from a buildStatus page.

HTTP/200 -> Build(s) successful, Green light on
HTTP/400 or any other value -> Build(s) broken, Red light on

Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
* VEZ Single Pole Double Through (SPDT) Relay attached to pins +5v, gnd, 8

created 23 April 2011
by Lloyd Holman

https://github.com/lholman/TeamCitySimpleBuildStatus#readme

This code is in the public domain.

Based on WebClient and BlinkWithoutDelay examples (by David A. Mellis)
*/

#include <SPI.h>
#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xD90, 0xDA2, 0xDDA, 0xD00, 0xD3D, 0xDB8 };
byte subnet[] = { 255, 255, 255, 0 };
byte ip[] = { 192,168,4,30 };
byte gateway[] = { 192, 168, 4, 1 };
byte server[] = { 10,100,101,8 };

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
Client client(server, 80);

int pinState = LOW; // used to set the digital pin driving the relay, HIGH or LOW
const int relayPin = 8; // the digital pin driving the relay

int GetServerStatus();

void setup() {
// set the digital pin as output:
pinMode(relayPin, OUTPUT);

// start the Ethernet connection:
Ethernet.begin(mac, ip, gateway);
// start the serial library:
Serial.begin(9600);
// give the Ethernet shield a second to initialize:
delay(1000);
}

void loop()
{
int buildStatus = GetServerStatus();
Serial.print(“buildStatus: “);
Serial.println(buildStatus);

if (buildStatus == 200 && pinState == LOW)
pinState = HIGH;
else if (buildStatus != 200 && pinState == HIGH)
pinState = LOW;

digitalWrite(relayPin, pinState);

delay(30000);
}

static int GetServerStatus()
{
int httpStatus = 0; //indicate a connection error with 0
int parseStatus = 0;

Serial.println(“connecting…”);
if (client.connect()) {
Serial.println(“connected”);
// Make the HTTP Get request:
client.println(“GET /simpleBuildStatus.html?projectName=DCP_R17&guest=1 HTTP/1.0″);
client.println();
}
else {
// if you didn’t get a connection to the server:
Serial.println(“connection failed”);
}

// wait for the response from the CI server.
bool gotStatus = false;
while (1){
while (client.available()) {
char c = client.read();
Serial.print(c);

switch(parseStatus) {
case 0:
if (c == ‘ ‘) parseStatus++; break; // skip “HTTP/1.1 ”
case 1:
if (c >= ’0′ && c <= ’9′) {
httpStatus *= 10;
httpStatus += c – ’0′;
} else {
parseStatus++;
}
}

}
if (!client.connected()) {
break;
}
}

client.flush();
client.stop();
return httpStatus;
}

I have hosted the code for the TeamCity SimpleBuildStatusPlugin (discussed earlier), along with the corresponding Arduino sketch (above) to control the Arduino digital pin and Ethernet shield in a public github repo here: https://github.com/lholman/TeamCitySimpleBuildStatus#readme

The build Step #5, packaging it all up

I wanted to make a neat job so I modified the plastic case from the parts list, with a carful bit of measuring, drilling and cutting with a stanley knife I ended up with a rather neat little case made for the Arduino.

Post3-ArduinoAntistaticFoam

Post3-ArduinoInACustomBox

I then simply stuck the packaged Arduino box to the side of the traffic lights with some double sided sticky pads.  It was always my aim from the start to have the Arduino circuitry mounted outside of the lights, mainly to avoid the heat from the 40watt bulbs frying it.

Post3-TrafficLightsWithArduinoPackage1

Post3-TrafficLightsWithArduinoPackageCloseUp

Post3-TrafficLightsWithArduinoPackageLightsOn

The end product is a very noticeable build traffic light on the wall in our office:

Post3-TrafficLightsInTheOffice

Keep an eye on the Github repo as I plan to update with some of the following ideas:

  1. Multi project support for TeamCity.
  2. An audible piezo buzzer when status changes.
  3. Other CI server support, by simple plugins.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>