Arduino Controlled Traffic Lights for Kids

Arduino, Cardboard, DIY
  Share   Tweet   This!   Email

Arduino Traffic Light Controller

Our kids love playing cars and trains, so we decided to make them a cool set of traffic lights out of cardboard and Arduino.

We think these traffic lights are pretty awesome because of a few reasons:

We didn't use the 'delay' function as a timer.
We included a dial to change the green light time.
Compact Design running on 6AA batteries.
Made from Recycled Materials
Very Low Cost to Make

In this post, we go into depth about how we made the traffic light controller, and explain our unique code and why we don't use the delay function. You can also download our Arduino Sketch so you can make your own traffic lights at home. This project is part of our list of Arduino projects for kids, where you can find other awesome projects to build.

If you are new to Arduino, you might want to read about getting started with Arduino, and a simple explanation of how Arduino works. Also, mega fans, you might want to take a look at Our awesome list of Arduino t-shirts & tops.


Yes, the cars are on the correct side, we live in Australia ;)

Equipment Used In this Arduino Project

To make this Arduino traffic light controller we used just a few electronic components and recycled cardboard and junk from around the house. Here is a list of the things we used.

Cardboard Traffic Light Equipment

Electronic Components

We went for equipment that didn't cost too much. After all, these are just kids traffic lights. We bought our parts from Aus Electronics Direct in Australia. Not a sponsor or anything, but they do have pretty good prices. Here is a list of the electronics we used.

Arduino Nano: We bought a knock off version for under $20 AUD. We did notice a couple of small timing issues when coding it, and this might be because it was a copy and not the original. Didn't have an original around here to test with, so we can't confirm.

Potentiometer: These are really cheap, and we had one laying around from a beginners kit we bought a long time ago. We used it to control how long the lights stay green for, and we gave it the nickname 'Speed Dial'.

Power Switch: A really simply inline switch. Again, a really cheap part to buy.

12 LED's: 4 red, 4 orange, and 4 green LED's. We bought a box or 200 for just a few dollars. We went for the clear LED type because they were brighter than the coloured ones but it doesn't really matter.

6 Resistors: We are NOT experts in electronics, but from all of our research and calculations, we went with 100 ohm resistors. Because the LED's were in pairs in series, we needed one for every 2 LED's, so six in total. Here is a LED series resistor calculator if you need.

Battery Holder: We are using a 6 x AA battery holder to produce 9v in total. From our research, don't use a 9v battery. Also, the weight of the AA's were great for the base.

Recycled Materials

We always have some storage tubs with junk in it for projects, and we just grabbed a few things to make these lights.

Cardboard: We just used a box we got from our last delivery.

Cardboard Tube: The tube is used vertically and houses the Arduino Nano. This needs to be quite strong, so don't use a toilet roll. We used one from the inside of a roll of garbage bags. Very sturdy.

Cat 5 Cable: We put this under recycled stuff because we have a tub of junk wire, and cat 5 was perfect for this as it is 8 core, and we needed 7 cores to get power to the LED's. Cat 5 is not great for carrying a lot of voltage, but we only needed to light 2 LED's on each core, so it was perfect.

Wire Coat Hanger: There is a length of wire that runs with the CAT 5 cable to hold the LED's hanging in place and stop it falling to the ground. We used a coat hanger as it was in the junk box, but you can use any sturdy wire.

Heat Shrink/Tape: Mentioned above, the CAT 5 and the wire run together so you need to bound them together. We used heat shrink because it's neat, and we have a ton of it. You can just tape them together if you like.

Tools Needed for This Project

No fancy tools needed to make these traffic lights. Here is what we used.

Hot Glue Gun: A must for Arduino projects. If you don't have one, invest.

Soldering Iron: Needed to solder all of the connections.

Heat Gun: Only used this for the heat shrink around the CAT 5 and the wire. Not needed if you use tape.

Pliers & Cutters: Pretty standard stuff. Needed to cut and strip all the wiring.

Stanley Knife: Used for the cardboard and to make holes.

How It All Works

Here is the plan. We wanted to build a battery operated traffic light controller for our kids to use when playing cars or trains.

The lights are very simple and is designed for a simple 4 way intersection. The lights on the front and back (north and south) are the same, and left and right (east and west) are the same. So, when the light is green, it is green for north and south, and red for east and west. Then, the lights go orange then red, and then green for east and west.

There was one variable for us, and that was how long the lights stayed green for. When playing cars it is pretty short, but for trains it was longer. We wanted to be able to control this on the unit easily, and not have to reprogram it every time we wanted it changed. So we implemented the Speed Dial.

When you initially program the Arduino, you can set a few variables, and there are two variables for the speed dial. Min time and max time. When the dial is all the way to the left, the green lights will stay on for the time you set as 'Min', and when the dial is all the way to the right, the lights will stay green for the time you set as 'Max'. If the dial is in between, then it is just an increment from min to max.

Layout of Our Arduino Traffic Lights

Here is the Breadboard Layout for this project. It really is quite simple. The only thing we didn't include was the on/off switch (Sorry). It is just an inline switch on the positive side of the battery. This layout was created by us using Fritzing. It's free and awesome so it's worth a shout out.

Breadboard Layout Fritzing for Adjustable Traffic Lights

Because the lights are the same for north/south and east/west, we ran the LED's in series, hence why you see pairs of LED's in the layout. They will be placed opposite each other in the final build. The negative of one LED goes into the positive of the next, and then through the resistor to ground.

Building the Arduino Traffic Lights

To be honest, this was a 'make it up as you go' design. I had the rough concept in my head, and just went from there.

We started out by setting all the electronics up on the breadboard, uploading the code, and testing it all worked. From there, we just built one section at a time.

Tip: The batteries make for a great heavy weighted base. It allowed us to place a long arm and hang the lights over the intersection just like they do in real life.

Here are some of our pics from the build. We will explain them as we go.

Wiring the Arduino Nano

The first thing we did was build a base for the battery pack and attach the upright tube. Then, time to wire the Arduino Nano Board. The Cat 5 cable is the cable that takes the power to the LED's and grounds them. Is has 8 cores and we only used 7.

On top of the Cat 5 for the LED's, we had to connect the battery pack with inline switch and the speed dial (Potentiometer). Other than the LED's, the wiring is complete.

Wiring the Traffic Lights

Hiding the Arduino Nano

We chose the tube as the upright because we wanted to house the Nano inside it. Here you can see we have tucked it up inside. Time over, we would have moved up the potentiometer a little to give us more room.

Traffic Light Construction for Arduino

LED Enclosure

Once the base was built, we made the enclosure for the LED's. Notice the width on them, that's because the long sides get glued onto the short sides. We used a pencil to make the holes. Push from the outside first, then from the inside to finish. Practise on scrap cardboard, you want them to look good.

Cutting LED Holes for Arduino Traffic Lights

Heat Shrink the Cat 5 & Wire

We added some coat hanger wire to the CAT 5 to give it strength and so we could shape it. We used hot glue to glue it inside the tube against the far side, and also put a bend in it so it ran down the tube a little. Here you can see the arm is complete.

Wire Bracket for Lights Installed

Wiring the LED's

Before we connected the CAT 5 to the LED's, we wired them together in series. The opposite sides are linked in series, just like the breadboard layout. We then wired all of the remaining negatives to their resistors and linked them ready to connect to the ground wire in the CAT 5. This can be a little fiddly, but take your time.

LED Wiring for Arduino Traffic Lights

Connect the Cat 5 to LED's

The last thing to do is glue the enclosure together and put a top and bottom on it. Because we took our time, and wired it neatly, the cables all folded in nicely. We glued the top cardboard in against the heat shrink so it wouldn't slide when finished.

Cardboard Arduino Traffic Lights Almost Complete

Coding the Arduino Traffic Lights

This is our code, and it quite unique. One thing you will notice is that we didn't use 'delay' for our timing. Read more about why further down. We have attached the Arduino Sketch to the bottom of this page, feel free to download it, and work it in to your projects.

Here is the code:

// green light minimum time, potentiometer set to minimum
int minGreenDelay = 5000;// green light max time, potentiometer set to maximum
int maxGreenDelay = 10000;
// orange light wait time
int orangeDelay = 3000;
// time that both directions are red, before green starts
int redDelay = 1000;

// name pins for lights
int r1Pin = 2;
int o1Pin = 3;
int g1Pin = 4;
int r2Pin = 6;
int o2Pin = 7;
int g2Pin = 8;
// name the pin for potentiometer/speed input
int speedPin = A0;

/*
no need to change anything below here
*/

// set initial times for lights
unsigned long greenMillis = 0;
unsigned long orangeMillis = 0;
unsigned long redMillis = 0;
// set current direction
int currentDirection = 1;
// set the starting status - we set it green
bool greenStatus = true;
bool orangeStatus = false;
bool redStatus = false;

// start the setup
void setup() {
// initialise pins for leds as outputs
pinMode(r1Pin, OUTPUT);
pinMode(o1Pin, OUTPUT);
pinMode(g1Pin, OUTPUT);
pinMode(r2Pin, OUTPUT);
pinMode(o2Pin, OUTPUT);
pinMode(g2Pin, OUTPUT);
// turn on green light direction 1
digitalWrite(g1Pin, HIGH);
// turn on red light direction 2
digitalWrite(r2Pin, HIGH);
}

// start the loop
void loop() {
// read the potentiometer for speed
int sensorValue = analogRead(speedPin);
// delay 1 millisecond for reading/saving time
delay(1);
// calculate the delay using mapping
int greenDelay = map(sensorValue, 0, 1023, minGreenDelay, maxGreenDelay);
// store current time
unsigned long currentMillis = millis();

// if green time is reached and green is the current mode
if (currentMillis - greenMillis >= greenDelay && greenStatus == true) {
// set orange millis start time to current time
orangeMillis = millis();
// change to orange using function
goOrange(currentDirection);
// delay 4 milliseconds because of millis bug
delay(4);
// un set the green mode
greenStatus = false;
// set the current mode to orange
orangeStatus = true;
}

// if orange time is reached and orange is the current mode
if (currentMillis - orangeMillis >= orangeDelay && orangeStatus == true) {
// set red millis start time to current time
redMillis = millis();
// change to orange using function
goRed(currentDirection);
// un set the orange mode
orangeStatus = false;
// set the current mode to red
redStatus = true;
}

// if red is on (both ways) then check the time and change to green other direction
if (currentMillis - redMillis >= redDelay && redStatus == true) {
// set green millis start time to current time
greenMillis = millis();
// change to green using function
goGreen(currentDirection);
redStatus = false;
// unset the red mode
greenStatus = true;
// change direction to the new direction
currentDirection = (currentDirection == 2) ? 1: 2;
}
}

// function to change to orange just needs current direction
void goOrange(int currentDirection){
if(currentDirection == 1)
{
digitalWrite(g1Pin, LOW);
digitalWrite(o1Pin, HIGH);
}
else
{
digitalWrite(g2Pin, LOW);
digitalWrite(o2Pin, HIGH);
}
}

// function to change to red just needs current direction
void goRed(int currentDirection){
if(currentDirection == 1)
{
digitalWrite(o1Pin, LOW);
digitalWrite(r1Pin, HIGH);
}
else
{
digitalWrite(o2Pin, LOW);
digitalWrite(r2Pin, HIGH);
}
}

// function to change to green just needs current direction
void goGreen(int currentDirection){
if(currentDirection == 1)
{
digitalWrite(r2Pin, LOW);
digitalWrite(g2Pin, HIGH);
}
else
{
digitalWrite(r1Pin, LOW);
digitalWrite(g1Pin, HIGH);
}
}

How the Code Works

Let's run through the code, and break it down a little.

User Defined Delay Times

At the very top is where the user can define the time that the traffic light lasts at each of the stages.

Orange and red are easy: The orange delay is how long the orange light lasts. The red delay is how long both directions are red before one goes green.

The green has a min and max: The green time can be adjusted using the Potentiometer on the light pole. The Potentiometer is basically a dial. Turned to the left is the 'Min' time, and turned to the right is 'Max' time. In between is just a sliding scale. EG: Let's say you set the min variable to 1000 (1 Sec), and the max to 10000 (10 secs). If the dial is all the way to the left, the green light will last 1 second. When you turn the dial all the way to the right, the green light will last 10 seconds. If you turn the dial just half way, the green light will last 5 seconds.

// green light minimum time, potentiometer set to minimum
int minGreenDelay = 5000;// green light max time, potentiometer set to maximum
int maxGreenDelay = 10000;
// orange light wait time
int orangeDelay = 3000;
// time that both directions are red, before green starts
int redDelay = 1000;

Setting Variable Names

Pretty straight forward. We just gave a name to the pins we wanted to use. The LED's are on digital pins, and the Potentiometer is on an analogue pin.

// name pins for lights
int r1Pin = 2;
int o1Pin = 3;
int g1Pin = 4;
int r2Pin = 6;
int o2Pin = 7;
int g2Pin = 8;
// name the pin for potentiometer/speed input
int speedPin = A0;

Set Starting Variables

When the program runs, it is constantly changing the status from green to orange to red, and changing the direction from 1 to 2 and 2 to 1. We need to set these manually for the first run, so we initialise them. We set the millis() for each timer, and we set the current status which is green because just above the set the direction 1 light to green.

// set initial times for lights
unsigned long greenMillis = 0;
unsigned long orangeMillis = 0;
unsigned long redMillis = 0;
// set current direction
int currentDirection = 1;
// set the starting status - we set it green
bool greenStatus = true;
bool orangeStatus = false;
bool redStatus = false;

Setup Code

In setup, all we need to do is initialise the LED's as outputs and set the current direction to green and the other to red.

// start the setup
void setup() {
// initialise pins for leds as outputs
pinMode(r1Pin, OUTPUT);
pinMode(o1Pin, OUTPUT);
pinMode(g1Pin, OUTPUT);
pinMode(r2Pin, OUTPUT);
pinMode(o2Pin, OUTPUT);
pinMode(g2Pin, OUTPUT);
// turn on green light direction 1
digitalWrite(g1Pin, HIGH);
// turn on red light direction 2
digitalWrite(r2Pin, HIGH);
}

Top of the Loop Section

At the beginning of the loop section, we just need to read the speed from the speed dial and then convert that reading to a time for the green light delay. This looks a bit complex, but is made easy by using the map() function. On top of setting the green delay time, we just need to set the current time.

// read the potentiometer for speed
int sensorValue = analogRead(speedPin);
// delay 1 millisecond for reading/saving time
delay(1);
// calculate the delay using mapping
int greenDelay = map(sensorValue, 0, 1023, minGreenDelay, maxGreenDelay);
// store current time
unsigned long currentMillis = millis();

Check Time and Status

There are three sections, one for each colour/status. Lets say the current status is green. Here is the equation

If the (current time) minus (time light last changed to green) is greater than or equal to the (time set for green delay) then lets change to orange.

We do this for each of the colours. We then change the status, and set the start time for the next colour. To make the code a bit shorter, we have a few functions that we call to. EG, goOrange(). These are explained next, and are where we turn the LED pins on and off.

// if green time is reached and green is the current mode
if (currentMillis - greenMillis >= greenDelay && greenStatus == true) {
// set orange millis start time to current time
orangeMillis = millis();
// change to orange using function
goOrange(currentDirection);
// delay 4 milliseconds because of millis bug
delay(4);
// un set the green mode
greenStatus = false;
// set the current mode to orange
orangeStatus = true;
}

// if orange time is reached and orange is the current mode
if (currentMillis - orangeMillis >= orangeDelay && orangeStatus == true) {
// set red millis start time to current time
redMillis = millis();
// change to orange using function
goRed(currentDirection);
// un set the orange mode
orangeStatus = false;
// set the current mode to red
redStatus = true;
}

// if red is on (both ways) then check the time and change to green other direction
if (currentMillis - redMillis >= redDelay && redStatus == true) {
// set green millis start time to current time
greenMillis = millis();
// change to green using function
goGreen(currentDirection);
redStatus = false;
// unset the red mode
greenStatus = true;
// change direction to the new direction
currentDirection = (currentDirection == 2) ? 1: 2;
}

Functions to Change Light Colours

To save space, and to make it easier to understand, each time we need to change the lights we call a function to switch the LED's. All we need to pass this function is the current direction. If you take a look, you will see how simple these functions are.

// function to change to orange just needs current direction
void goOrange(int currentDirection){
if(currentDirection == 1)
{
digitalWrite(g1Pin, LOW);
digitalWrite(o1Pin, HIGH);
}
else
{
digitalWrite(g2Pin, LOW);
digitalWrite(o2Pin, HIGH);
}
}
// function to change to red just needs current direction
void goRed(int currentDirection){
if(currentDirection == 1)
{
digitalWrite(o1Pin, LOW);
digitalWrite(r1Pin, HIGH);
}
else
{
digitalWrite(o2Pin, LOW);
digitalWrite(r2Pin, HIGH);
}
}
// function to change to green just needs current direction
void goGreen(int currentDirection){
if(currentDirection == 1)
{
digitalWrite(r2Pin, LOW);
digitalWrite(g2Pin, HIGH);
}
else
{
digitalWrite(r1Pin, LOW);
digitalWrite(g1Pin, HIGH);
}
}

Why We Didn't Use the Delay Function

If you look at all the traffic light controllers using Arduino, you will notice one thing, they almost all use the 'Delay' function as the timer for the lights. The reason is, because it is easy, and reliable. However, there is a big downside. The delay function actually stops the program running (Blocking) for a set amount of time. Here is a great article on why you shouldn't use delay.

We didn't want to block our program, so we used millis() for the timing. So now, even while the traffic lights are on green for 10 seconds, the program loop is still looping, and can still do other tasks. EG, if you turn the speed dial while the lights are green, it is actively be listening and adjusting. Also, if we wanted to add anything else to the program, maybe a street light with a switch, we can.

Now, if you look through the code, you will notice we used the delay function twice, and the reason is because of tiny programming bugs. We only stop the program for a couple of thousandths of a second just to avoid bugs. EG. When we didn't use it, the millis() didn't even have time to save it's data and the calculations would not work. These are technically blocking the program, but it doesn't have any impact because it is just so quick.

Links and Resources

Here are some of the resources we used when making this traffic light project.

Arduino Create

Arduino Create is the online coding platform where you can write your code and save your projects. Basically your home page for your Arduino projects.

Arduino Create

This Project on Arduino Project Hub

Arduino Project Hub is where the Arduino community posts their projects. Here is the link to this project.

Arduino Traffic Light Controller

Arduino Getting Started

A great documentation resource for anyone getting started with Arduino.

Arduino Getting Started Guide

Aus Electronics

For Australian Consumers, this is a great electronics store online. We buy a lot of products from this store. Please note, this is NOT a paid endorsement. We did not receive anything from them. We just really like their price, products and service.

Aus Electronics Arduino Page

Documents & Downloads

Arduino 4 Way Traffic Lights Sketch

  Share   Tweet   This!   Email
Thomas Kelly
Thanks. Just what I needed. You should make a video next time.
Reply
STEM Mayhem
We did actually record these lights being, but it was a disaster. It just wan't planned well enough. We learnt from it though, and next time things might be different.

We have been asked for a video of the completed project, so we might update this post soon if we get a chance to record one.

Thanks for the comment, and the suggestion.
Andre
This is so in depth, thanks for taking the time to write it. We made arduino traffic lights recently but we used delay. I think we are going to challenge ourselves to write it without using the delay feature, and use this as a guide. Thanks.
Reply
STEM Mayhem
Thanks Andre. Using millis() is a good way to time small projects like this. Have fun rewriting, and I hope this helps you.

Read Next