Creating an intelligent traffic light system for a four-way intersection involves managing vehicle flow efficiently while prioritizing pedestrian safety. This project leverages an Arduino microcontroller, four Infrared (IR) sensors for vehicle detection on each road, and pedestrian interaction mechanisms (push buttons and dedicated signals). The goal is to develop a system that dynamically adapts to traffic conditions and pedestrian needs, moving beyond simple timed cycles.
The core of this system is an Arduino board (an Arduino Mega is recommended due to the number of I/O pins required for a full 4-way system with individual pedestrian controls). Each of the four roads is equipped with standard Red, Yellow, and Green LEDs for vehicles, an IR sensor to detect approaching vehicles, a push button for pedestrians to request crossing, and separate Red and Green LEDs for pedestrian signals.
Conceptual diagram of a sensor-based traffic management system.
Properly wiring the components to the Arduino is crucial. The following table outlines a suggested pin configuration. Note that IR sensors typically have VCC, GND, and OUT pins. The OUT pin connects to the Arduino. Push buttons are connected to an input pin and GND, often using an internal or external pull-up resistor.
| Component Group | Road | Vehicle Red LED | Vehicle Yellow LED | Vehicle Green LED | Pedestrian Red LED | Pedestrian Green LED | IR Sensor (Analog/Digital Pin) | Pedestrian Button (Digital Pin) |
|---|---|---|---|---|---|---|---|---|
| North | 1 | Pin 2 | Pin 3 | Pin 4 | Pin 22 | Pin 23 | Pin A0 | Pin 30 |
| East | 2 | Pin 5 | Pin 6 | Pin 7 | Pin 24 | Pin 25 | Pin A1 | Pin 31 |
| South | 3 | Pin 8 | Pin 9 | Pin 10 | Pin 26 | Pin 27 | Pin A2 | Pin 32 |
| West | 4 | Pin 11 | Pin 12 | Pin 13 | Pin 28 | Pin 29 | Pin A3 | Pin 33 |
Note: Ensure LEDs are connected with current-limiting resistors (e.g., 220Ω). Button pins are configured with `INPUT_PULLUP`, so connect the other terminal of the button to GND. IR sensor output pins are connected to the specified Arduino pins; their VCC and GND must also be connected. Pin numbers are examples and can be adjusted in the code.
The system operates on a cycle, granting green lights to different roads. IR sensors check for vehicles to potentially extend green times. Pedestrian buttons, when pressed, signal a request to cross. The system will then, at an appropriate point in the traffic cycle (e.g., after the current green phase completes with a yellow light), turn all vehicle lights red and activate the pedestrian "WALK" signal for the requesting road.
Example of pedestrian walk and don't walk signals.
IR sensors are placed to detect vehicles waiting at each approach. A detection (typically a LOW signal from the sensor) can be used to inform the Arduino. The system might extend the green light duration for a road if vehicles are continuously detected or prioritize a road with waiting vehicles.
This mindmap illustrates the key components of the 4-way smart traffic light system and their relationships, providing a high-level overview of its architecture.
The following Arduino code provides a comprehensive structure for the 4-way traffic light controller with IR sensors and pedestrian features. It uses non-blocking delays (`millis()`) for responsive operation. Pin assignments match the table provided earlier. The IR sensors are assumed to pull their output LOW when an object is detected.
// Define a structure to hold pin numbers for each road's lights and sensors
struct TrafficLightPins {
int red; // Vehicle Red LED pin
int yellow; // Vehicle Yellow LED pin
int green; // Vehicle Green LED pin
int pedRed; // Pedestrian Red LED pin
int pedGreen; // Pedestrian Green LED pin
int irSensor; // IR Sensor pin
int pedButton;// Pedestrian Button pin
};
// Array of structures, one for each of the 4 roads (North, East, South, West)
TrafficLightPins roads[4] = {
// North (Road 0)
{2, 3, 4, 22, 23, A0, 30},
// East (Road 1)
{5, 6, 7, 24, 25, A1, 31},
// South (Road 2)
{8, 9, 10, 26, 27, A2, 32},
// West (Road 3)
{11, 12, 13, 28, 29, A3, 33}
};
// Timing constants (in milliseconds)
const unsigned long minGreenTime = 10000; // Minimum green time for vehicles (10s)
const unsigned long yellowTime = 3000; // Yellow light time (3s)
const unsigned long pedGreenTime = 7000; // Pedestrian green crossing time (7s)
const unsigned long allRedClearanceTime = 2000; // Brief all-red after yellow/before ped (2s)
// State tracking variables
unsigned long lastChangeTime = 0; // Stores time of the last major state change
int currentRoad = 0; // Index of the road currently having (or about to have) green light for vehicles
bool pedestrianWaiting[4] = {false, false, false, false}; // Flags if a pedestrian is waiting at road i
bool pedestrianCrossingActive = false; // True if a pedestrian crossing sequence is active
int roadForPedestrian = -1; // Index of the road where pedestrian crossing is happening
// Enum for main system states to manage traffic flow
enum SystemState { VEHICLE_GREEN, VEHICLE_YELLOW, ALL_RED_TRANSITION, PEDESTRIAN_CROSSING_START, PEDESTRIAN_WALK, PEDESTRIAN_DONT_WALK_CLEARANCE };
SystemState currentSystemState = VEHICLE_GREEN;
void setup() {
Serial.begin(9600); // Initialize serial communication for debugging
// Initialize all pins for LEDs as OUTPUTs and sensors/buttons as INPUT_PULLUPs
for (int i = 0; i < 4; i++) {
pinMode(roads[i].red, OUTPUT);
pinMode(roads[i].yellow, OUTPUT);
pinMode(roads[i].green, OUTPUT);
pinMode(roads[i].pedRed, OUTPUT);
pinMode(roads[i].pedGreen, OUTPUT);
pinMode(roads[i].irSensor, INPUT_PULLUP); // IR sensor: LOW means vehicle detected
pinMode(roads[i].pedButton, INPUT_PULLUP); // Pedestrian button: LOW means pressed
// Initial light states: All vehicle red, all pedestrian red
digitalWrite(roads[i].red, HIGH);
digitalWrite(roads[i].yellow, LOW);
digitalWrite(roads[i].green, LOW);
digitalWrite(roads[i].pedRed, HIGH);
digitalWrite(roads[i].pedGreen, LOW);
}
// Start with currentRoad (e.g., North) Green
digitalWrite(roads[currentRoad].red, LOW);
digitalWrite(roads[currentRoad].green, HIGH);
lastChangeTime = millis();
currentSystemState = VEHICLE_GREEN;
Serial.println("System Initialized. Road " + String(currentRoad) + " GREEN.");
}
// Function to turn all vehicle lights RED and pedestrian lights to DON'T WALK (RED)
void setAllVehiclesRedAllPedRed() {
for (int i = 0; i < 4; i++) {
digitalWrite(roads[i].red, HIGH);
digitalWrite(roads[i].yellow, LOW);
digitalWrite(roads[i].green, LOW);
digitalWrite(roads[i].pedRed, HIGH);
digitalWrite(roads[i].pedGreen, LOW);
}
}
// Function to set lights for a road going green
void setVehicleGreen(int roadIndex) {
setAllVehiclesRedAllPedRed(); // First, ensure all others are red
digitalWrite(roads[roadIndex].red, LOW);
digitalWrite(roads[roadIndex].green, HIGH);
digitalWrite(roads[roadIndex].pedRed, HIGH); // Pedestrians on this road see DON'T WALK
digitalWrite(roads[roadIndex].pedGreen, LOW);
Serial.println("Road " + String(roadIndex) + " VEHICLE GREEN.");
}
// Function to set lights for a road going yellow
void setVehicleYellow(int roadIndex) {
digitalWrite(roads[roadIndex].green, LOW);
digitalWrite(roads[roadIndex].yellow, HIGH);
Serial.println("Road " + String(roadIndex) + " VEHICLE YELLOW.");
}
// Function to start pedestrian crossing on a specific road
void startPedestrianWalk(int pedRoadIndex) {
setAllVehiclesRedAllPedRed(); // All vehicle lights red
// Pedestrian lights: WALK (Green ON) for the specific road, DON'T WALK for others
for (int i = 0; i < 4; i++) {
if (i == pedRoadIndex) {
digitalWrite(roads[i].pedRed, LOW);
digitalWrite(roads[i].pedGreen, HIGH);
} else {
digitalWrite(roads[i].pedRed, HIGH);
digitalWrite(roads[i].pedGreen, LOW);
}
}
Serial.println("Road " + String(pedRoadIndex) + " PEDESTRIAN WALK.");
}
// Read IR sensor: returns true if vehicle present (sensor output is LOW)
bool isVehicleDetected(int roadIndex) {
return digitalRead(roads[roadIndex].irSensor) == LOW;
}
// Check pedestrian buttons and update waiting flags
void checkPedestrianButtons() {
for (int i = 0; i < 4; i++) {
if (digitalRead(roads[i].pedButton) == LOW && !pedestrianWaiting[i]) {
pedestrianWaiting[i] = true; // Pedestrian requested crossing
Serial.println("Pedestrian button pressed for road " + String(i));
}
}
}
// Select the next road for green light or a road for pedestrian crossing
// Priority: 1. Pedestrians waiting, 2. Vehicles detected on other roads, 3. Normal cycle.
int determineNextAction(int currentVehicleRoad, int& outPedestrianRoadIndex) {
outPedestrianRoadIndex = -1; // Reset
// Check for pedestrian requests first (any road)
for (int i = 0; i < 4; i++) {
if (pedestrianWaiting[i]) {
outPedestrianRoadIndex = i; // Found a pedestrian waiting
pedestrianWaiting[i] = false; // Clear the request flag
return PEDESTRIAN_CROSSING_START; // Signal to start pedestrian sequence
}
}
// If no pedestrians, check for vehicles on other roads (simple cycle for now)
// A more advanced logic could check IR sensors of (currentVehicleRoad + 1), (currentVehicleRoad + 2) etc.
int nextRoad = (currentVehicleRoad + 1) % 4;
// For simplicity, we'll just cycle. IR sensor use is primarily for green time extension here.
return VEHICLE_GREEN; // Signal to go to next vehicle green
}
void loop() {
unsigned long currentTime = millis();
checkPedestrianButtons(); // Continuously check pedestrian buttons
switch (currentSystemState) {
case VEHICLE_GREEN:
// Green light is active for 'currentRoad'
// IR sensor could extend green time here (not implemented in this simplified state timing)
if (currentTime - lastChangeTime >= minGreenTime) {
setVehicleYellow(currentRoad);
currentSystemState = VEHICLE_YELLOW;
lastChangeTime = currentTime;
}
break;
case VEHICLE_YELLOW:
if (currentTime - lastChangeTime >= yellowTime) {
setAllVehiclesRedAllPedRed(); // All vehicles red
currentSystemState = ALL_RED_TRANSITION;
lastChangeTime = currentTime;
Serial.println("All Red Transition after Yellow for Road " + String(currentRoad));
}
break;
case ALL_RED_TRANSITION:
if (currentTime - lastChangeTime >= allRedClearanceTime) {
// Decide next action: pedestrian or next vehicle
int nextActionState = determineNextAction(currentRoad, roadForPedestrian);
if (nextActionState == PEDESTRIAN_CROSSING_START && roadForPedestrian != -1) {
pedestrianCrossingActive = true;
currentSystemState = PEDESTRIAN_WALK;
startPedestrianWalk(roadForPedestrian);
} else {
// No pedestrian, or error, proceed to next vehicle
pedestrianCrossingActive = false;
currentRoad = (currentRoad + 1) % 4; // Cycle to next road
setVehicleGreen(currentRoad);
currentSystemState = VEHICLE_GREEN;
}
lastChangeTime = currentTime;
}
break;
case PEDESTRIAN_WALK:
if (currentTime - lastChangeTime >= pedGreenTime) {
// Transition to "Don't Walk" (solid red for pedestrians)
digitalWrite(roads[roadForPedestrian].pedGreen, LOW);
digitalWrite(roads[roadForPedestrian].pedRed, HIGH);
currentSystemState = PEDESTRIAN_DONT_WALK_CLEARANCE;
lastChangeTime = currentTime;
Serial.println("Road " + String(roadForPedestrian) + " PEDESTRIAN DON'T WALK (Clearance).");
}
break;
case PEDESTRIAN_DONT_WALK_CLEARANCE:
// This state is for pedestrian clearance time, typically a flashing "Don't Walk" or just a fixed duration.
// Here, it's a fixed duration with solid "Don't Walk" (Red).
if (currentTime - lastChangeTime >= allRedClearanceTime) { // Using allRedClearanceTime as ped clearance
pedestrianCrossingActive = false;
roadForPedestrian = -1; // Reset
// After pedestrian crossing, decide next vehicle road.
// Could be the road that just had pedestrians, or the next in sequence.
// For simplicity, we cycle from the last 'currentRoad'.
currentRoad = (currentRoad + 1) % 4;
setVehicleGreen(currentRoad);
currentSystemState = VEHICLE_GREEN;
lastChangeTime = currentTime;
Serial.println("Pedestrian cycle complete. Resuming vehicle traffic.");
}
break;
}
}
TrafficLightPins roads[4] organizes all pin numbers for each road, making the code cleaner and easier to manage.currentSystemState, currentRoad, pedestrianWaiting[], pedestrianCrossingActive, and roadForPedestrian track the system's status.setup(): Initializes serial communication for debugging and configures all pins. It sets initial light states (typically all red for vehicles, then one road green).setAllVehiclesRedAllPedRed(): Turns all vehicle lights red and pedestrian lights to "Don't Walk".setVehicleGreen(roadIndex) / setVehicleYellow(roadIndex): Control lights for a specific road.startPedestrianWalk(pedRoadIndex): Manages lights for pedestrian crossing.isVehicleDetected(roadIndex): Reads the IR sensor for a given road. (Note: The current main loop's timing for green is fixed; IR detection is primarily for future extension to dynamic green times).checkPedestrianButtons(): Scans all pedestrian buttons.determineNextAction(): Decides whether the next phase is for a pedestrian or the next vehicle road.loop(): This is the heart of the program. It's a state machine driven by currentSystemState and uses millis() for non-blocking time management. It transitions through states like VEHICLE_GREEN, VEHICLE_YELLOW, ALL_RED_TRANSITION (to safely switch or allow pedestrians), and various pedestrian crossing states.isVehicleDetected() function is provided. In this version of the code, vehicle detection is mainly for future enhancements like extending green times. The primary logic focuses on timed cycles and pedestrian interrupts. To fully utilize IR sensors for dynamic timing, the VEHICLE_GREEN state would need to check isVehicleDetected(currentRoad) and potentially extend minGreenTime or adjust lastChangeTime.A smart traffic system can be evaluated on several attributes. The radar chart below offers a conceptual comparison between a basic timed system, one with IR sensors, and a comprehensive system including advanced pedestrian priority and adaptive controls.
This chart visualizes how adding features like IR sensors and sophisticated pedestrian handling can improve overall system performance, though potentially increasing complexity and cost.
For a practical demonstration of a 4-way traffic light system built with Arduino, the following video provides valuable insights into its construction and operation. While it may not cover all specific features of the code provided here (like individual pedestrian lights per road), it serves as an excellent visual aid for the general concept.
Demonstration of a 4-Way Traffic Light Control System using Arduino.