
Saturday morning traffic light hack @HullRaspJam with @Raspberry_Pi and @gpiobox. Safe to cross, now ??#rjam pic.twitter.com/vbPv7pQYzJ
— Claire Garside (@cgarside) 12 November 2016

If you are interested in how we got all of the hardware working you can read our blog here.
Introduction
Contents
This project builds on the Traffic Light project by adding code and hardware to simulate a UK pedestrian crossing. Pedestrian Crossing have changed recently from ‘Pelican Crossings’ to Puffin Crossings’. This project describes the newer Puffin Crossing.
Connect to the gPiO Box
The traffic lights project uses gPiO outputs 1,2 & 3 for the red, amber and green lights. We extend this to use all six gPiO outputs with red man, green man and wait light on outputs 4,5 & 6. We also use input A to sense when the button has been pressed. Refer to the traffic lights project if you are unsure how to make the physical connections.
Create the Code
Use the infographic below to understand the sequence of a puffin crossing.
.The table below shows how the inputs & outputs are connected to the various lights.
Notice that in the infographic there is no flashing amber. The newer Puffin crossings don’t have the flashing amber phase of the older Pelican Crossings but instead have a sensor that detects when pedestrians are no longer in the road. The flashing amber phase confused some pedestrians who were not sure what do do if the light started flashing.

- You can code this project using a variety of platforms and languages.
- We’ve shown two versions of text based languages and a Scratch script for the Raspberry Pi.
- We’ve also shown the code using the PXT and Mu editors on the micro:bit.
- All of these are available in the Code Library.
Raspberry Pi version
Python
Scratch
This is the Scratch 2 version of the project.
- Please note that this requires a Raspberry Pi version 2 or 3.
- In addition the code needs a Raspberry Pi with a Raspbian image dated 21/6/2017 or later. There is more information on this here.

C
This C code is rather different in that the code was written by a professional highway signals engineer and is designed to simulate an actual real world crossing, rather than the simplified Python & Scratch versions we have shown here. The code is relatively complex but may give some idea how C could be used. Many thanks to Chris Kennett at Green Signals Consulting Limited
[sourcecode language=”csharp”]
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
/* 2 Phase UK Puffin Crossing Sequence
*
* Developed by Chris Kennett based on previous UK traffic signal emulations.
* This is designed as a simple traffic signal controller. It is designed
* for use with real LED signal heads from a scrapped traffic signal junction.
*
* Aspects being used are Imtech (PEEK) ‘Elite’ ELV CLS-LED and AGD ELV Toucan nearside display unit.
*
* Electronics are controlled by the RPi via opto-coupler and separate TRIAC,
* driving ELV traffic signal LED aspects. Working voltage is between 27.5v – 48v.
* Actual voltage chosen for this project is 30v (nominal) to avoid burning my eyes out.
*
* Opto-couplers are held high by a line from the 3.3v rail and sink to the output when switched
* low. 150R resistor is in line with the output and a separate 110R resistor is added on the 3.3v rail
* to limit pin current to between 6 and 12mA, depending on the number of aspects lit.
*
* Do not try to drive more than 3 aspects, as the current available is not sufficient and the
* opto-couplers may not be triggered.
*
* Resistor on TRIAC gate is approx 47R. LED aspects are not inductive, so no snubber
* circuit is needed. This circuit does not dim. If dimming is needed, use a different tapping
* or a separate transformer and switch between them.
*
* No audible bleeper of tactile rotatting cone is fitted. Tactile cones are likely to draw a high current
* and are potentially an inductive or disruptive load.
*
* Push button demands must be registered on detectors (switches) through pin 24
* to trigger interrupts. Extensions (such as on-crossing or kerbside detection) is polled and so can
* be wired to any available digital IO. Inputs are pulled high and normally open-circuit.
*
* Pins have been chosen to allow Arduino to easily be swapped in/out for a Raspberry PI model B+ or later
* (40 pin IO). If this is done, beware differences in pin-out, operating voltages and lower pin-current
* thresholds.
*
* For details, contact Chris Kennett at chris.kennett[at]greensignals.co.uk
*/
int phaseDriveA[3] = {7, 8, 25};
int phaseDriveB[3] = {18, 15, 14};
//inputs associated to phases
int phaseDetA = 4;
int phaseDetB = 24;
//phase times
int minA = 7000;
int minB = 6000;
int maxA = 30000;
int extA = 1000;
//interStage times exclude closing and starting ambers
int interStage1_2 = 3;
int interStage2_1 = 6;
//starting and closing amber times
const int closeAmber = 3000;
const int startAmber = 2000;
//set up volatile variables for interrupts
volatile int demA = 1;
volatile int demB = 0;
//current stage
int stage = 0;
//demands
//String dA = String(“_”);
//String dB = String(“_”);
//extensions
//String eA = String(“_”);
//Phase changes
void startingAmberA() {
digitalWrite(phaseDriveA[0], LOW);
digitalWrite(phaseDriveA[1], LOW);
digitalWrite(phaseDriveA[2], HIGH);
}
void greenA() {
digitalWrite(phaseDriveA[0], HIGH);
digitalWrite(phaseDriveA[1], HIGH);
digitalWrite(phaseDriveA[2], LOW);
}
void greenB() {
digitalWrite(phaseDriveB[0], HIGH);
digitalWrite(phaseDriveB[2], LOW);
demB = 0;
}
void redA() {
digitalWrite(phaseDriveA[0], LOW);
digitalWrite(phaseDriveA[1], HIGH);
digitalWrite(phaseDriveA[2], HIGH);
}
void redB() {
digitalWrite(phaseDriveB[0], LOW);
digitalWrite(phaseDriveB[2], HIGH);
}
void closingAmberA() {
digitalWrite(phaseDriveA[0], HIGH);
digitalWrite(phaseDriveA[1], LOW);
digitalWrite(phaseDriveA[2], HIGH);
}
void waitB() {
digitalWrite(phaseDriveB[1], LOW);
}
void waitClearB() {
digitalWrite(phaseDriveB[1], HIGH);
}
//startup sequence
void startUp() {
redB();
waitB();
demB = 1;
delay(closeAmber);
delay((interStage2_1)*1000);
greenA();
stage = 1;
delay(minA);
}
//Stage Moves
void stage1_2() {
closingAmberA();
delay(closeAmber);
redA();
delay((interStage1_2)*1000);
greenB();
stage = 2;
//printf(“Stage “);
//printf(stage);
//printf(“\n”);
waitClearB();
demB = 0;
delay(minB);
}
void stage2_1() {
redB();
if(digitalRead(phaseDetB) == LOW){
demB = 1;
waitB();
}
delay((interStage2_1)*1000);
startingAmberA();
delay(startAmber);
greenA();
stage = 1;
//printf(“Stage “);
//printf(stage);
//printf(“\n”);
delay(minA);
}
//stage extensions
void extendSt1() {
int timA = 0;
while ((timA < maxA) && (digitalRead(phaseDetA) == LOW)){
delay(extA);
//serialOutput((timA/1000));
timA += extA;
//eA = “E”;
}
//eA = “_”;
if ((timA < maxA) && (digitalRead(phaseDetA) == HIGH)){
}
}
//detection interrupts (ISRs)
void det1(){
demB = 1;
waitB();
}
/*
*Serial Port responses
* int serialOutput(int tim) {
* if (demA) {dA = “D”;}
* else {dA = “_”;}
* if (demB) {dB = “D”;}
* else {dB = “_”;}
* printf(“Stage: “);
* printf(stage);
* printf(“\n\tDemand\tExtension\tTime\tMax”);
* printf(“\nPhase A\t\t”);
* printf(dA);
* printf(“\t”);
* printf(eA);
* printf(“\t\t”);
* printf(tim);
* printf(“\t”);
* printf(maxA/1000);
* printf(“\nPhase B\t\t”);
* printf(dB);
* printf(“\n”);
*}
*/
int main(void) {
wiringPiSetupGpio();
//initialise appropriate pins for phase drive outputs
pinMode(phaseDriveA[0], OUTPUT);
pinMode(phaseDriveA[1], OUTPUT);
pinMode(phaseDriveA[2], OUTPUT);
pinMode(phaseDriveB[0], OUTPUT);
pinMode(phaseDriveB[1], OUTPUT);
pinMode(phaseDriveB[2], OUTPUT);
digitalWrite(phaseDriveA[0], HIGH);
digitalWrite(phaseDriveA[1], HIGH);
digitalWrite(phaseDriveA[2], HIGH);
digitalWrite(phaseDriveB[0], HIGH);
digitalWrite(phaseDriveB[1], HIGH);
digitalWrite(phaseDriveB[2], HIGH);
//initialise appropriate pins for detector inputs
pinMode(4, INPUT);
pinMode(24, INPUT);
pullUpDnControl(4, PUD_UP);
pullUpDnControl(24, PUD_UP);
delay(3000);
wiringPiISR (24, INT_EDGE_BOTH, &det1); //pin 7 (phaseDetB)
startUp();
while(1) {
//serialOutput(0);
if(stage == 1){
if(demB == 1){
//printf(“extending Stage 1\n”);
extendSt1();
//printf(“moving to stage 2\n”);
stage1_2();
}
}else {
stage2_1();
}
}
}
[/sourcecode]
micro:bit version
Python - Mu Editor
[sourcecode language=”python”]
from microbit import *
while True:
pin0.write_digital(1) # Green Light On
pin16.write_digital(1) # Red Man On
if button_a.is_pressed():
pin15.write_digital(1) # Wait Light on
sleep(2000)
pin0.write_digital(0) # Wait Light off
pin12.write_digital(1) # Amber Light on
sleep(2000)
pin12.write_digital(0) # Amber Light off
pin8.write_digital(1) # Red Light on
sleep(2000)
pin15.write_digital(0) # Wait Light off
pin16.write_digital(0) # Red Man Off
pin13.write_digital(1) # Green Man on # Pedestrians can cross
sleep(5000)
pin16.write_digital(1) # Red Man On
pin13.write_digital(0) # Green Man off
sleep(2000)
pin12.write_digital(1) # Amber Light on
sleep(2000)
pin12.write_digital(0) # Amber Light off
pin8.write_digital(0) # Red Light off
[/sourcecode]
PXT Editor
External Links
Puffin Good Practice Guide” (PDF). Department for Transport. 2006.