Live Simulation
Components Required
Everything you need is beginner-friendly and inexpensive. A standard traffic light uses a red, amber and green LED per direction — six LEDs in total for a two-way junction.
Pin Assignments
We use six digital output pins — one per LED. Pins 2–7 are ideal as they are well clear of the serial (0, 1) and SPI (10–13) pins.
| Direction | Colour | Arduino Pin | Resistor |
|---|---|---|---|
| North / South | Red | Pin 2 | 220 Ω |
| North / South | Amber | Pin 3 | 220 Ω |
| North / South | Green | Pin 4 | 220 Ω |
| East / West | Red | Pin 5 | 220 Ω |
| East / West | Amber | Pin 6 | 220 Ω |
| East / West | Green | Pin 7 | 220 Ω |
GND rail on breadboard → Arduino GND. Each LED cathode (short leg) → GND rail.
Circuit Diagram
Traffic Light Sequence
A standard UK-style four-phase cycle ensures both road directions can never be green simultaneously.
| Phase | N/S Signal | E/W Signal | Duration | Purpose |
|---|---|---|---|---|
| 1 | GREEN | RED | 5 s | North–South traffic flows |
| 2 | AMBER | RED | 2 s | N/S prepares to stop |
| 3 | RED | GREEN | 5 s | East–West traffic flows |
| 4 | RED | AMBER | 2 s | E/W prepares to stop |
RED+AMBER phase of 1–2 s before each Green if you want to replicate full UK sequencing.Arduino Code
Upload this sketch to your Uno. The logic is deliberately readable — each phase is a clearly labelled block of digitalWrite calls followed by a delay.
// ═══════════════════════════════════════════════════ // Two-Way Crossroads Traffic Light Controller // Arduino Uno — 6 LEDs (2 × Red, Amber, Green) // No Worries IT — Arduino Tutorial Series // ═══════════════════════════════════════════════════ // ── Pin definitions ────────────────────────────── const int NS_RED = 2; // North–South red const int NS_AMBER = 3; // North–South amber const int NS_GREEN = 4; // North–South green const int EW_RED = 5; // East–West red const int EW_AMBER = 6; // East–West amber const int EW_GREEN = 7; // East–West green // ── Timing (milliseconds) ───────────────────────── const unsigned long GREEN_TIME = 5000; // 5 seconds green const unsigned long AMBER_TIME = 2000; // 2 seconds amber // ───────────────────────────────────────────────── void setup() { // Set all six pins as outputs pinMode(NS_RED, OUTPUT); pinMode(NS_AMBER, OUTPUT); pinMode(NS_GREEN, OUTPUT); pinMode(EW_RED, OUTPUT); pinMode(EW_AMBER, OUTPUT); pinMode(EW_GREEN, OUTPUT); // Safe starting state: both directions RED allRed(); delay(1000); } // ───────────────────────────────────────────────── void loop() { // ── PHASE 1: N/S GREEN, E/W RED ───────────────── setLights(HIGH, LOW, LOW, // NS: red=ON, amb=OFF, grn=OFF LOW, LOW, HIGH); // EW: red=OFF, amb=OFF, grn=ON // Wait — swap: N/S gets green setLights(LOW, LOW, HIGH, HIGH, LOW, LOW); delay(GREEN_TIME); // ── PHASE 2: N/S AMBER, E/W RED ───────────────── setLights(LOW, HIGH, LOW, HIGH, LOW, LOW); delay(AMBER_TIME); // ── PHASE 3: N/S RED, E/W GREEN ───────────────── setLights(HIGH, LOW, LOW, LOW, LOW, HIGH); delay(GREEN_TIME); // ── PHASE 4: N/S RED, E/W AMBER ───────────────── setLights(HIGH, LOW, LOW, LOW, HIGH, LOW); delay(AMBER_TIME); // Loop back to Phase 1 } // ───────────────────────────────────────────────── // Helper: set all six LEDs in one call // Parameters: NS_red, NS_amb, NS_grn, EW_red, EW_amb, EW_grn void setLights(int nsr, int nsa, int nsg, int ewr, int ewa, int ewg) { digitalWrite(NS_RED, nsr); digitalWrite(NS_AMBER, nsa); digitalWrite(NS_GREEN, nsg); digitalWrite(EW_RED, ewr); digitalWrite(EW_AMBER, ewa); digitalWrite(EW_GREEN, ewg); } // Helper: put both directions to red (safe state) void allRed() { setLights(HIGH, LOW, LOW, HIGH, LOW, LOW); }
Code Walk-Through
1 — Pin Constants
Using named constants (NS_RED, EW_GREEN, etc.) instead of bare numbers makes the code self-documenting. If you rewire to different pins, you only change one line per LED.
2 — setup()
Every pin is declared as OUTPUT with pinMode(). Without this the pin floats in a high-impedance input state and the LED won't light reliably.
allRed() is called immediately. This guarantees a known safe state when the board powers on or resets, before the main loop begins.
3 — setLights() helper
Rather than writing six digitalWrite calls in every phase, setLights() wraps them. This keeps each phase block to a single readable line and prevents accidentally leaving a light in the wrong state.
4 — loop()
The four phases run sequentially inside loop(), which Arduino repeats forever. delay() pauses execution for the given number of milliseconds. This is a simple blocking approach — ideal for a standalone controller with no other tasks.
delay() with millis()-based non-blocking timing to let the Arduino do other things (e.g. read a button for a pedestrian crossing) while the lights are running. Look up the "Blink Without Delay" pattern in the Arduino documentation.Ideas to Extend the Project
Pedestrian crossing button — wire a push-button to an interrupt pin. When pressed, the current green phase finishes, then amber runs, then a "walk" LED illuminates while all traffic lights show red.
Traffic density adaptation — use IR or ultrasonic sensors to detect waiting vehicles and dynamically extend or shorten the green phase on the busier road.
Four-junction model — coordinate multiple Uno boards or use an Arduino Mega's extra pins to control a full 4-way junction with pedestrian phases on all crossings.
Serial monitor display — add Serial.begin(9600) and Serial.println() calls to each phase so you can watch the current state in the Arduino IDE's serial monitor — great for debugging.