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
Key Concepts
- All-Red Phase: The 2-second period where both directions are red provides a safety buffer for clearing the intersection
- Alternating Priority: Each direction gets its turn in strict sequence
- 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
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
BeginnerQuick start guide, installation instructions, and project overview. Start here!
📄 traffic_lights_tutorial.md
BeginnerComplete text tutorial with detailed explanations of logic, safety, and state diagrams.
🐍 traffic_simulator.py
BeginnerNo-hardware simulator using emoji lights. Perfect for testing logic before building the circuit!
🐍 two_way_traffic_simple.py
BeginnerSimple sequential state machine approach for two-way intersection. Best for learning!
🐍 two_way_traffic_generator.py
IntermediateElegant generator-based approach using Python generators and gpiozero's source parameter.
🐍 three_way_traffic_paired.py
IntermediateT-junction controller with paired operation (main road A+B vs side road C).
🐍 three_way_traffic_sequential.py
IntermediateThree-way intersection where each direction takes turns: A → B → C → repeat.
🐍 traffic_oo_controller.py
AdvancedProfessional object-oriented design with classes, enums, logging, and scalable architecture.
Suggested Learning Path
- Read the README.md and this HTML tutorial
- Run traffic_simulator.py to see the logic in action (no hardware needed!)
- Build your circuit following the GPIO pin allocation
- Start with two_way_traffic_simple.py
- Try the generator approach with two_way_traffic_generator.py
- Extend to three-way with three_way_traffic_paired.py
- Explore sequential logic with three_way_traffic_sequential.py
- 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:
- ✓ No two conflicting directions can be green simultaneously
- ✓ All-red safety buffers exist between direction changes
- ✓ Proper red+amber sequences in place
- ✓ 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