Why we built it
Coordinate two traffic directions so conflicting green lights never occur, while keeping the beginner circuit and program easy to inspect and extend.
Represent the junction as a small state machine with explicit north-south, transition, and east-west phases. Every change passes through amber and an all-red safety interval.
From sketch to prototype
A delay-only sketch can light LEDs, but it hides the rules that make an intersection safe. Naming each phase turns the project into a clear automation lesson.
After the sequence works, a pedestrian request or vehicle sensor can be added without rewriting the entire program. Inputs request the next safe state instead of directly switching lamps.
“The best version wasn’t the one with the most features—it was the one people understood fastest.”
Hardware & tools
Software
Make it step by step

Build two identical signals
Wire red, amber, and green LEDs for each road through resistors. Test every output individually.

Define safe traffic states
Write one function per phase and verify that both directions are never green together. Include an all-red interval.

Add non-blocking timing
Use millis instead of long delays so a pedestrian button or sensor can be read while the current phase is active.
The core logic
if (millis() - stateStarted >= durations[state]) {
state = static_cast<State>((state + 1) % STATE_COUNT);
stateStarted = millis();
applyLights(state);
}
if (digitalRead(PED_BUTTON) == LOW) pedestrianQueued = true;What we learned
- State machines make automation rules visible and testable.
- An all-red interval creates a safety buffer between directions.
- Non-blocking timing makes future buttons and sensors easier to add.
Watch preview
Watch preview
Watch preview