The first sequence
Red for five seconds, green for five, yellow for two, and round again. Written with delay() it is three steps and a function that sets all three pins, and it works. It also spends almost every moment of its life waiting.
Which sequence
Traffic lights do not work the same everywhere. This book models the plain three-step cycle many countries use: red, then green, then yellow as a warning that red is coming, then red again. The UK, among others, adds a step with red and yellow together before green. The next article shows how to add it.
The times are short so you can watch a whole cycle: five seconds of red, five of green and two of yellow. Real signals are much longer and set for each junction.
One cycle, line by line
The light on the left is whatever the last show() set. The highlighted line
is where the sketch is. Setting the pins takes a few microseconds. Each
delay() takes seconds. So for nearly the whole cycle, the sketch is inside a
delay().
show() sets all three
show(HIGH, LOW, LOW) sets the red pin HIGH and the other two LOW in one call.
Every step goes through it, and every step names all three lights, including
the two that are off.
That is the whole defence against two lights at once. One pin, one
light showed that the
board lights whatever pins are HIGH. A sketch that only ever wrote
digitalWrite(GREEN_PIN, HIGH) to go green would leave the red on from the
step before, and the board would show both.
What you should see
Red for five seconds, green for five, yellow for two, and round again. The serial monitor at 115200 prints each name as it changes:
red
green
yellow
redWhat delay() costs
The sketch works, and for a light that does nothing else it is fine. But try to add anything. A button that should switch the light to night mode can only be read between delays, so a press during the five-second red is not seen until the red ends, if it is still held then.
That is the same problem as blinking without stopping, made worse by longer waits. The XL LED fixed it by checking the clock. A traffic light needs one more thing as well: it has to remember which light it is on.
The code
No library. show() sets all three pins in one call, so no step can leave a light on by accident. loop() is three steps: set the lights, then wait.
/*
Traffic Light - first sequence, with delay() TK03 / /p/tk03
Wiring. Count from the square pad on the TinkerBlock board, LEDs up,
header at the bottom:
GND -> GND
NC -> nothing (both NC pins are unconnected on the board)
RED -> D9 on an Uno, GPIO 25 on an ESP32, GPIO 4 on an ESP32-S3,
GP13 on a Raspberry Pi Pico
YELLOW -> D10 on an Uno, GPIO 26 on an ESP32, GPIO 5 on an ESP32-S3,
GP14 on a Pico
GREEN -> D11 on an Uno, GPIO 27 on an ESP32, GPIO 6 on an ESP32-S3,
GP15 on a Pico
Arduino IDE
Tools > Board your board, e.g. ESP32S3 Dev Module
Tools > Port the one that appears when you plug in
Tools > USB CDC On Boot Enabled (ESP32-S3 only)
No library needed.
*/
// GPIO numbers. Uno: 9, 10, 11. ESP32: 25, 26, 27. ESP32-S3: 4, 5, 6.
// Pico: 13, 14, 15.
const int RED_PIN = 4;
const int YELLOW_PIN = 5;
const int GREEN_PIN = 6;
// Set all three at once, so no step can leave a light on by mistake.
void show(bool red, bool yellow, bool green) {
digitalWrite(RED_PIN, red ? HIGH : LOW);
digitalWrite(YELLOW_PIN, yellow ? HIGH : LOW);
digitalWrite(GREEN_PIN, green ? HIGH : LOW);
}
void setup() {
Serial.begin(115200);
pinMode(RED_PIN, OUTPUT);
pinMode(YELLOW_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
}
void loop() {
show(HIGH, LOW, LOW); // red: stop
Serial.println("red");
delay(5000);
show(LOW, LOW, HIGH); // green: go
Serial.println("green");
delay(5000);
show(LOW, HIGH, LOW); // yellow: about to turn red
Serial.println("yellow");
delay(2000);
}Every delay() stops the whole sketch. That is fine for a light on its own, and it is exactly what goes wrong the moment the sketch should also notice a button or the serial port. The next article rewrites it so it never waits.
The same sequence in MicroPython. show() sets all three pins in one call, and time.sleep_ms() is delay() by another name.
"""
Traffic Light - first sequence, MicroPython TK03 / /p/tk03
Wiring. Count from the square pad on the TinkerBlock board, LEDs up,
header at the bottom:
GND -> GND
NC -> nothing (both NC pins are unconnected on the board)
RED -> GPIO 25 on an ESP32, GPIO 4 on an ESP32-S3, GP13 on a Pico
YELLOW -> GPIO 26 on an ESP32, GPIO 5 on an ESP32-S3, GP14 on a Pico
GREEN -> GPIO 27 on an ESP32, GPIO 6 on an ESP32-S3, GP15 on a Pico
Thonny
Run > Configure interpreter MicroPython (ESP32) or
MicroPython (Raspberry Pi Pico)
Save it to the board as main.py to run it on every power-up.
Nothing to install: machine and time are built in.
"""
from machine import Pin
import time
# GPIO numbers. ESP32: 25, 26, 27. ESP32-S3: 4, 5, 6. Pico: 13, 14, 15.
RED_PIN = 4
YELLOW_PIN = 5
GREEN_PIN = 6
red = Pin(RED_PIN, Pin.OUT, value=0)
yellow = Pin(YELLOW_PIN, Pin.OUT, value=0)
green = Pin(GREEN_PIN, Pin.OUT, value=0)
def show(r, y, g):
"""Set all three at once, so no step leaves a light on by mistake."""
red.value(r)
yellow.value(y)
green.value(g)
while True:
show(1, 0, 0) # red: stop
print("red")
time.sleep_ms(5000)
show(0, 0, 1) # green: go
print("green")
time.sleep_ms(5000)
show(0, 1, 0) # yellow: about to turn red
print("yellow")
time.sleep_ms(2000)sleep_ms stops the whole program just as delay() does, so this version has the same limit: while it sleeps it notices nothing. The next article's version checks the clock instead.
When it does not work
A step turned one light on without turning the others off. The show() function in this sketch sets all three pins every time, so as long as every step goes through it, only the lights you name can be lit.
Many countries do, the UK among them. Add a step after red: show(HIGH, HIGH, LOW) and a delay of about two seconds. The next article turns the sequence into a table, where that is one extra row.
Change the numbers in the delay() calls; they are milliseconds. Real signals are timed by traffic engineers for each junction, often much longer than these. The short times here are so a whole cycle fits in the time it takes to watch it.
Move the green step to the top of loop(). The order of the steps is the order of the lights, and loop() starts from its first line every time round. Starting on red is the usual choice because it is the safe state.
The same light as a table and a clock, and a loop() that never waits.
A sketch that remembers →Edit this page — content/books/traffic-light/the-first-sequence.mdx
Questions about this product
See what other owners have asked, and read their solutions.
Traffic Light
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.