🚦 Multi-Way Traffic Light Control Tutorial

From Basic Signals to Complex Intersections

Introduction

This tutorial extends the basic single traffic light example to handle two-way and three-way intersections. We'll explore the logic, safety considerations, and implementation strategies for realistic traffic control systems using Raspberry Pi and Python.

What You'll Learn

  • Traffic light sequencing and timing
  • Safety-critical system design
  • State machine programming
  • Multi-direction coordination
  • Python GPIO control techniques

Safety First: The Golden Rule

⚠️ CRITICAL SAFETY RULE

Never have conflicting green lights!

This is the fundamental safety constraint in traffic light systems. Before we write any code, we must ensure that vehicles on collision courses never both have green lights.

UK Traffic Light Sequence

Phase 1: Green

Go (10s)

Phase 2: Amber

Prepare to stop (3s)

Phase 3: Red

Stop (varies)

Phase 4: Red+Amber

Prepare to go (2s)

UK vs US Sequences

The Red + Amber phase is unique to UK/European systems and warns drivers to prepare for movement. US traffic lights go directly from Red to Green.

Two-Way Traffic Lights

Scenario: Simple Crossroads

Imagine a simple crossroads where:

  • Direction A: North-South traffic
  • Direction B: East-West traffic

The Logic

At any given time:

  • If A is green, B must be red
  • If B is green, A must be red
  • There's a transition period where both are red (for safety)

State Diagram

State 1: A=Green, B=Red (10s)
State 2: A=Amber, B=Red (3s)
State 3: A=Red, B=Red (2s) ← Safety buffer
State 4: A=Red, B=Red+Amber (2s)
State 5: A=Red, B=Green (10s)
State 6: A=Red, B=Amber (3s)
State 7: A=Red, B=Red (2s) ← Safety buffer
State 8: A=Red+Amber, B=Red (2s)
→ Back to State 1

Key Concepts

  1. All-Red Phase: The 2-second period where both directions are red provides a safety buffer for clearing the intersection
  2. Alternating Priority: Each direction gets its turn in strict sequence
  3. Predictable Timing: Consistent timing helps drivers anticipate changes

Code Example: Simple Approach

from gpiozero import TrafficLights
from time import sleep

lights_a = TrafficLights(2, 3, 4)    # North-South
lights_b = TrafficLights(17, 27, 22) # East-West

while True:
    # Direction A gets priority
    lights_a.red.on()
    lights_a.amber.on()
    lights_b.red.on()
    sleep(2)
    
    lights_a.green.on()
    lights_a.red.off()
    lights_a.amber.off()
    sleep(10)
    
    lights_a.amber.on()
    lights_a.green.off()
    sleep(3)
    
    lights_a.red.on()
    lights_a.amber.off()
    sleep(2)  # Safety buffer
    
    # Direction B gets priority
    lights_b.red.on()
    lights_b.amber.on()
    sleep(2)
    
    lights_b.green.on()
    lights_b.red.off()
    lights_b.amber.off()
    sleep(10)
    
    lights_b.amber.on()
    lights_b.green.off()
    sleep(3)
    
    lights_b.red.on()
    lights_b.amber.off()
    sleep(2)  # Safety buffer

Three-Way Traffic Lights

Scenario: T-Junction

Consider a T-junction with:

  • Direction A: Main road (straight through)
  • Direction B: Main road (opposite direction)
  • Direction C: Side road (joining the main road)

Strategy 1: Paired Operation

A and B operate together (they're on the same road) vs C operating independently. This creates a two-way system: (A+B) vs C

Phase 1: A=Green, B=Green, C=Red (12s)
Phase 2: A=Amber, B=Amber, C=Red (3s)
Phase 3: A=Red, B=Red, C=Red (2s) ← Safety
Phase 4: A=Red, B=Red, C=Red+Amber (2s)
Phase 5: A=Red, B=Red, C=Green (8s)
Phase 6: A=Red, B=Red, C=Amber (3s)
Phase 7: A=Red, B=Red, C=Red (2s) ← Safety
Phase 8: A=Red+Amber, B=Red+Amber, C=Red (2s)
→ Back to Phase 1

Strategy 2: Sequential Operation

Each direction gets its own green phase. Order: A → B → C → repeat. More complex but potentially more efficient for specific junction layouts.

Choosing a Strategy

Paired Operation: Best when two directions don't conflict (same road, opposite directions)

Sequential Operation: Best when all directions potentially conflict with each other

Circuit Setup

GPIO Pin Allocation

Direction Red Amber Green
Direction A (North-South / Main North) GPIO 2 GPIO 3 GPIO 4
Direction B (East-West / Main South) GPIO 17 GPIO 27 GPIO 22
Direction C (Side Road) Three-way only GPIO 10 GPIO 9 GPIO 11

Component Requirements

Two-Way System

  • 6 LEDs (2 red, 2 amber/yellow, 2 green)
  • 6× 330Ω resistors
  • Breadboard
  • Jumper wires
  • Raspberry Pi (any model with GPIO)

Three-Way System

  • 9 LEDs (3 red, 3 amber/yellow, 3 green)
  • 9× 330Ω resistors
  • Breadboard
  • Jumper wires
  • Raspberry Pi (any model with GPIO)

Current Draw Calculation

Each LED draws approximately 20mA through a 330Ω resistor:

  • Two-way system: 6 LEDs max = 120mA (safe)
  • Three-way system: 9 LEDs max = 180mA (safe)

The Raspberry Pi can safely source up to 50mA per GPIO pin and approximately 500mA total, so these configurations are well within limits.

Programming Approaches

1. Sequential State Machine

Define each state explicitly and transition between them.

Pros: Very explicit and readable, easy to modify timing, simple to add sensor-based control

Cons: More code for complex systems, timing locked into state definitions

2. Generator Functions

Use Python generators to yield light states, leveraging gpiozero's source parameter.

Pros: Elegant and Pythonic, non-blocking operation, clean separation of logic

Cons: Harder to integrate dynamic control, less intuitive for beginners

def direction_a_sequence():
    while True:
        yield (1, 1, 0)  # red+amber
        sleep(2)
        yield (0, 0, 1)  # green
        sleep(10)
        yield (0, 1, 0)  # amber
        sleep(3)
        yield (1, 0, 0)  # red
        sleep(15)

lights_a.source = direction_a_sequence()

3. Object-Oriented Design

Create a traffic light controller class with methods for each phase.

Pros: Object-oriented and scalable, easy to extend, can include logging/error handling

Cons: More complex setup, overkill for simple demonstrations

Project Files

📄 README.md

Beginner

Quick start guide, installation instructions, and project overview. Start here!

📥 Download README.md

📄 traffic_lights_tutorial.md

Beginner

Complete text tutorial with detailed explanations of logic, safety, and state diagrams.

📥 Download traffic_lights_tutorial.md

🐍 traffic_simulator.py

Beginner

No-hardware simulator using emoji lights. Perfect for testing logic before building the circuit!

📥 Download traffic_simulator.py

🐍 two_way_traffic_simple.py

Beginner

Simple sequential state machine approach for two-way intersection. Best for learning!

📥 Download two_way_traffic_simple.py

🐍 two_way_traffic_generator.py

Intermediate

Elegant generator-based approach using Python generators and gpiozero's source parameter.

📥 Download two_way_traffic_generator.py

🐍 three_way_traffic_paired.py

Intermediate

T-junction controller with paired operation (main road A+B vs side road C).

📥 Download three_way_traffic_paired.py

🐍 three_way_traffic_sequential.py

Intermediate

Three-way intersection where each direction takes turns: A → B → C → repeat.

📥 Download three_way_traffic_sequential.py

🐍 traffic_oo_controller.py

Advanced

Professional object-oriented design with classes, enums, logging, and scalable architecture.

📥 Download traffic_oo_controller.py

Suggested Learning Path

  1. Read the README.md and this HTML tutorial
  2. Run traffic_simulator.py to see the logic in action (no hardware needed!)
  3. Build your circuit following the GPIO pin allocation
  4. Start with two_way_traffic_simple.py
  5. Try the generator approach with two_way_traffic_generator.py
  6. Extend to three-way with three_way_traffic_paired.py
  7. Explore sequential logic with three_way_traffic_sequential.py
  8. Study the professional approach in traffic_oo_controller.py

Challenges & Extensions

Once you've mastered the basics, try these extensions:

🚶 Pedestrian Crossing

Add a button-activated pedestrian phase that interrupts the normal sequence. When pressed, bring both directions to red, show a green walking signal, then resume normal operation.

📡 Sensor Integration

Use ultrasonic or PIR sensors to detect waiting vehicles. Adjust green time dynamically based on traffic flow - busy directions get more time!

🚨 Emergency Override

Add an emergency button that immediately brings all lights to red and keeps them there until released. Perfect for emergency vehicle access.

🕐 Time-of-Day Scheduling

Different timing profiles for different times: longer green times during rush hour, faster cycling at night, or flashing amber mode in the early morning.

🌐 Web Interface

Create a web dashboard using Flask or FastAPI to monitor and control the lights remotely. Show real-time status and allow manual overrides.

📊 Data Logging

Log all state changes with timestamps to CSV or database. Analyze traffic patterns and optimize timing based on historical data.

Testing & Debugging

Safety Checklist

Before running your code, verify:

  1. ✓ No two conflicting directions can be green simultaneously
  2. ✓ All-red safety buffers exist between direction changes
  3. ✓ Proper red+amber sequences in place
  4. ✓ Timing values are reasonable and tested

Common Issues

LEDs Not Lighting

  • Check LED polarity (flat side = cathode/negative)
  • Verify GPIO pin numbers (BCM mode)
  • Test individual LEDs with simple on/off code
  • Check resistor connections

Incorrect Sequence

  • Add print statements to track state changes
  • Run the simulator first to verify logic
  • Check timing constants
  • Verify all sleep() calls are in correct order

Lights Flickering

  • Ensure proper power supply to Raspberry Pi
  • Check for loose connections
  • Verify GPIO pins aren't being shared