learndesign.tech Search

Traffic light state machine on Arduino

· Fox Bright

Six LEDs, two buttons, four states, no delay(). A two-direction traffic light where a timer or a button press moves the system on, and the code is one switch statement.

An Arduino Uno wired to a breadboard with six LEDs in two rows of red, yellow and green, and two pushbuttons.

Two directions of traffic, north–south and east–west. Each gets a red, a yellow and a green LED. Every ten seconds the green side goes yellow, then red, and the other side goes green. Two buttons stand in for a car sensor or a pedestrian button: press one and its direction gets green early. The whole thing is a finite state machine with four states.

The hardware

  • Six LEDs through 1 kΩ resistors from pins 13, 12, 11 (north–south green, yellow, red) and 10, 9, 8 (east–west green, yellow, red). All cathodes to the ground rail.
  • Two pushbuttons on pins 7 and 6, using the internal pull-up, so a press reads LOW.

Lay it out so you can read it. Straighten resistor legs and put them in the same rows every time. Bend the long (positive) LED leg into a hook so polarity is visible at a glance. When something does not light, you want to rule out polarity in a second, not a minute.

A hand bending the longer leg of a yellow LED beside a breadboard
Bending the positive LED leg into a hook makes polarity obvious on the board.

The four states

State North–south East–west Leaves on
0 green red 10 s, or the east–west button
1 yellow red 1 s
2 red green 10 s, or the north–south button
3 red yellow 1 s

Yellow states only exit on the timer. A button press cannot skip a yellow; that is the point of yellow.

The code

The important decision is no delay(). A delay would block the loop, and a button pressed during the wait would be missed. Instead the loop runs continuously and compares millis() with the time of the last state change.

Arduino IDE showing pin assignments for six LEDs and two buttons and timing variables
Pin definitions and timing constants at the top of the sketch. Times are unsigned long because millis() is.

At the top: pin numbers, greenTime = 10000 and yellowTime = 1000 as unsigned long, lastChange, state, and two boolean flags for the buttons.

setup() opens the serial port, sets the LED pins as outputs, the button pins as INPUT_PULLUP, writes the starting lights, and records lastChange = millis().

loop():

  1. currentTime = millis().
  2. Read each button. If it reads LOW, set its flag and print a line to serial so you can see it happen.
  3. switch (state). Each case prints its name, then checks currentTime - lastChange >= greenTime (or yellowTime) or, in the green states, the relevant button flag. If so: write the new LED pattern, set the next state, set lastChange = currentTime, clear the button flag.
Arduino code showing a switch statement with case 0 for north-south green and east-west red
One case of the switch: check the elapsed time or the button, set the lights, move to the next state.

Case 3 goes back to state 0. In the video I had typed 4 there by mistake; there is no state 4, and the lights would have frozen. Serial prints are how you catch that kind of thing.