Costs:
Arduino Nano ea $5.33
Pushbutton ea. $0.50
Red, Yellow, Green LEDs (6) $1.00
OLED Display $3.00
220 ohm Resistors $1.00
Half-Breadboard ea $1.26
AMS-1117 5V Regulator ea $0.50
9V Battery and Clip ea $3.00
Barrel-Jack Adaptor ea $0.79
$16.38
Students can build a real intersection with cardboard and other makerspace building materials. Especially encourage them to keep building in more complexity, and take it as far as they possibly can.
https://projecthub.arduino.cc/krishna_agarwal/traffic-light-using-arduino-a-beginner-project-35f8c6
Phase 1: Traffic lights turn green, yellow, then red in a repeating cycle. Make this sequence into a function.
Phase 2: Add Pedestrian Crossing Switch
Students can add a push-button to act as a pedestrian crossing switch, forcing the light sequence to change when pressed. This introduces the use of digital inputs,conditional logic, and hardware interrupts.
Create a new function for the Walk sequence.
To ensure the system catches the button press immediately—even if it happens while the Arduino is stuck in a long delay() waiting for a light to change—we use a hardware interrupt. The interrupt calls an interrupt service routine(ISR), which sets a flag variable.This allows the Arduino to run its normal cycle, instantly flag a button press whenever it occurs, and process that request when it reaches the Red light phase.
Phase 3: Add a walk/don't walk sign using an OLED or dot matrix display.
Button: Connect one leg to Pin 2 and the other leg to GND. (The code uses the internal pull-up resistor, so no external resistor is needed).
Car Lights: Red to Pin 4, Yellow to Pin 5, Green to Pin 6 (with 220Ω or 330Ω resistors).
Pedestrian Lights: Red (Don't Walk) to Pin 7, Green (Walk) to Pin 8 (with resistors).
The Interrupt (attachInterrupt): Standard delay() functions pause the Arduino entirely. If someone pressed the button during a 5-second green light, the Arduino normally wouldn't notice. The interrupt bypasses this by constantly watching Pin 2 in the background.
volatile bool pedestrianWaiting = false;
pinMode(buttonPin, INPUT_PULLUP); // Attach the interrupt to Pin 2.
attachInterrupt(digitalPinToInterrupt(buttonPin), buttonPressISR, FALLING);
// 'FALLING' triggers the function when the signal goes from HIGH to LOW (button pressed)
// --- Interrupt Service Routine (ISR) This function runs instantly the moment the button is pressed
void buttonPressISR() {
pedestrianWaiting = true; //sets flag variable
}
volatile bool pedestrianWaiting: When the button is pressed, the interrupt function (buttonPressISR) instantly sets this variable to true. The volatile keyword is critical—it tells the Arduino's memory that this variable can change outside the normal flow of the loop().
The Cycle Execution: When the main loop() hits the Red Light phase, it checks the pedestrianWaiting variable. If it's true, it executes the 5-second pedestrian crossing sequence and resets the variable. If false, it just waits through a standard red light cycle.
The Interrupt (attachInterrupt): Standard delay() functions pause the Arduino entirely. If someone pressed the button during a 5-second green light, the Arduino normally wouldn't notice. The interrupt bypasses this by constantly watching Pin 2 in the background.
volatile bool pedestrianWaiting: When the button is pressed, the interrupt function (buttonPressISR) instantly sets this variable to true. The volatile keyword is critical—it tells the Arduino's memory that this variable can change outside the normal flow of the loop().
The Cycle Execution: When the main loop() hits the Red Light phase, it checks the pedestrianWaiting variable. If it's true, it executes the 5-second pedestrian crossing sequence and resets the variable. If false, it just waits through a standard red light cycle.
// --- Pin Definitions ---
/*Note: Pin 2 is used because it supports external interrupts
on the Arduino Uno and Arduino Nano. The Nano also supports interrupts on Pin 3*/
const int buttonPin = 2;
const int carRed = 4;
const int carYellow = 5;
const int carGreen = 6;
const int pedRed = 7;
const int pedGreen = 8;
// --- System Variables ---
// 'volatile' tells the compiler this variable can change at any time (via the interrupt)
volatile bool pedestrianWaiting = false;
void setup() {
// Set light pins as outputs
pinMode(carRed, OUTPUT);
pinMode(carYellow, OUTPUT);
pinMode(carGreen, OUTPUT);
pinMode(pedRed, OUTPUT);
pinMode(pedGreen, OUTPUT);
// Set button pin as input with internal pull-up resistor
// The pin will read HIGH normally, and LOW when pressed
pinMode(buttonPin, INPUT_PULLUP);
// Attach the interrupt to Pin 2.
// 'FALLING' triggers the function when the signal goes from HIGH to LOW (button pressed)
attachInterrupt(digitalPinToInterrupt(buttonPin), buttonPressISR, FALLING);
// Initialize starting state: Cars go, Pedestrians stop
digitalWrite(carGreen, HIGH);
digitalWrite(pedRed, HIGH);
}
void loop() {
// 1. CAR GREEN PHASE
digitalWrite(carRed, LOW);
digitalWrite(carGreen, HIGH);
delay(5000); // Cars have a green light for 5 seconds
// 2. CAR YELLOW PHASE
digitalWrite(carGreen, LOW);
digitalWrite(carYellow, HIGH);
delay(2000); // Yellow light for 2 seconds
// 3. CAR RED PHASE
digitalWrite(carYellow, LOW);
digitalWrite(carRed, HIGH);
delay(1000); // 1-second safety pause before pedestrians or standard red
// 4. PEDESTRIAN LOGIC
if (pedestrianWaiting) {
// Turn on the Walk sign
digitalWrite(pedRed, LOW);
digitalWrite(pedGreen, HIGH);
delay(5000); // Walk sign stays on for 5 seconds
// Turn off Walk sign, turn on Don't Walk
digitalWrite(pedGreen, LOW);
digitalWrite(pedRed, HIGH);
delay(1000); // 1-second safety pause before cars get green again
// Reset the flag so the walk sign doesn't trigger again until pressed
pedestrianWaiting = false;
} else {
// If no button was pressed, just wait a normal red light duration
delay(3000);
}
}
// --- Interrupt Service Routine (ISR) ---
// This function runs instantly the moment the button is pressed
void buttonPressISR() {
pedestrianWaiting = true;
}