// ==============================================
// CMPE30274 - Final Activity 3
// PROJECT: ESP32 Interrupts, Timers & Debounce
// TITLE: Pedestrian Traffic Light System
// Group #: 2
// Section: BSCPE LADDERIZED BATCH 2
// MEMBERS: Ashley Ambas
// Exequiel John Gicana
// Jovan Laguitao
// Aliyah Macy Lara
// Mark Daryl Sablon
//
// Description:
// This program simulates a pedestrian traffic light system using
// ESP32 hardware interrupt, IRAM_ATTR ISR, volatile flag,
// and millis()-based software debouncing.
// ==============================================
// ===================== PIN DEFINITIONS =====================
#define GREEN_LED 18 // Vehicle green light
#define YELLOW_LED 19 // Vehicle yellow warning light
#define RED_LED 21 // Vehicle red light
#define PED_LED 22 // Pedestrian crossing light
#define BUZZER_PIN 23 // Active buzzer
#define BUTTON_PIN 27 // Push button interrupt pin
// ===================== TIMING CONSTANTS =====================
const unsigned long DEBOUNCE_MS = 200;
const unsigned long YELLOW_TIME = 2000; // 2 seconds
const unsigned long CROSSING_TIME = 5000; // 5 seconds
const unsigned long BUZZER_INTERVAL = 300; // buzzer beep interval
const unsigned long PRINT_INTERVAL = 2000; // serial print every 2 seconds
// ===================== INTERRUPT VARIABLES =====================
volatile bool buttonInterruptFlag = false;
// Debounce handled in loop()
unsigned long lastValidButtonPress = 0;
// ===================== STATE MACHINE =====================
enum TrafficState {
NORMAL,
YELLOW_WARNING,
PEDESTRIAN_CROSSING,
RESET_STATE
};
TrafficState currentState = NORMAL;
// ===================== TIMER VARIABLES =====================
unsigned long stateStartTime = 0;
unsigned long lastBuzzerToggle = 0;
unsigned long lastPrintTime = 0;
bool buzzerState = false;
// ===================== ISR FUNCTION =====================
// ISR must be short and safe.
// Do NOT use Serial.print() or delay() inside ISR.
void IRAM_ATTR onButtonPress() {
buttonInterruptFlag = true;
}
// ===================== STATE NAME HELPER =====================
String getStateName() {
switch (currentState) {
case NORMAL:
return "NORMAL";
case YELLOW_WARNING:
return "YELLOW WARNING";
case PEDESTRIAN_CROSSING:
return "PEDESTRIAN CROSSING";
case RESET_STATE:
return "RESET";
default:
return "UNKNOWN";
}
}
// ===================== OUTPUT CONTROL =====================
void turnOffAllOutputs() {
digitalWrite(GREEN_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(RED_LED, LOW);
digitalWrite(PED_LED, LOW);
digitalWrite(BUZZER_PIN, LOW);
}
// ===================== STATE FUNCTIONS =====================
void setNormalState() {
turnOffAllOutputs();
digitalWrite(GREEN_LED, HIGH);
currentState = NORMAL;
stateStartTime = millis();
Serial.println();
Serial.println("STATE 1: NORMAL OPERATION");
Serial.println("Vehicle GREEN LED ON");
Serial.println("Vehicle YELLOW LED OFF");
Serial.println("Vehicle RED LED OFF");
Serial.println("Pedestrian LED OFF");
Serial.println("Buzzer OFF");
Serial.println("Waiting for pedestrian button press...");
Serial.println("----------------------------------------");
}
void setYellowWarningState() {
turnOffAllOutputs();
digitalWrite(YELLOW_LED, HIGH);
currentState = YELLOW_WARNING;
stateStartTime = millis();
Serial.println();
Serial.println("STATE 2: YELLOW WARNING");
Serial.println("Button interrupt detected.");
Serial.println("Vehicle GREEN LED OFF");
Serial.println("Vehicle YELLOW LED ON for 2 seconds.");
Serial.println("----------------------------------------");
}
void setPedestrianCrossingState() {
turnOffAllOutputs();
digitalWrite(RED_LED, HIGH);
digitalWrite(PED_LED, HIGH);
currentState = PEDESTRIAN_CROSSING;
stateStartTime = millis();
lastBuzzerToggle = millis();
buzzerState = false;
digitalWrite(BUZZER_PIN, LOW);
Serial.println();
Serial.println("STATE 3: PEDESTRIAN CROSSING");
Serial.println("Vehicle RED LED ON");
Serial.println("Pedestrian LED ON");
Serial.println("Buzzer beeping for 5 seconds.");
Serial.println("----------------------------------------");
}
void setResetState() {
turnOffAllOutputs();
currentState = RESET_STATE;
stateStartTime = millis();
Serial.println();
Serial.println("STATE 4: RESET");
Serial.println("Pedestrian LED OFF");
Serial.println("Buzzer OFF");
Serial.println("Vehicle RED LED OFF");
Serial.println("Returning to normal operation.");
Serial.println("----------------------------------------");
}
// ===================== SETUP =====================
void setup() {
pinMode(GREEN_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
pinMode(PED_LED, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
// This uses external pull-down resistor in diagram.json.
// Button gives HIGH signal when pressed.
pinMode(BUTTON_PIN, INPUT);
Serial.begin(9600);
delay(1000);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), onButtonPress, RISING);
Serial.println("========================================");
Serial.println("CMPE30274 Final Activity 3");
Serial.println("Group 2 - Pedestrian Traffic Light");
Serial.println("Interrupt + Timer + Debounce System");
Serial.println("Serial Monitor Started at 9600 baud");
Serial.println("========================================");
setNormalState();
}
// ===================== MAIN LOOP =====================
void loop() {
unsigned long currentTime = millis();
// SERIAL MONITOR STATUS PRINT
if (currentTime - lastPrintTime >= PRINT_INTERVAL) {
lastPrintTime = currentTime;
Serial.println("------ STATUS ------");
Serial.print("Current State: ");
Serial.println(getStateName());
Serial.print("Button Reading: ");
Serial.println(digitalRead(BUTTON_PIN));
Serial.print("Green LED: ");
Serial.println(digitalRead(GREEN_LED) ? "ON" : "OFF");
Serial.print("Yellow LED: ");
Serial.println(digitalRead(YELLOW_LED) ? "ON" : "OFF");
Serial.print("Red LED: ");
Serial.println(digitalRead(RED_LED) ? "ON" : "OFF");
Serial.print("Pedestrian LED: ");
Serial.println(digitalRead(PED_LED) ? "ON" : "OFF");
Serial.print("Buzzer: ");
Serial.println(digitalRead(BUZZER_PIN) ? "ON" : "OFF");
Serial.println("--------------------");
Serial.println();
}
// HANDLE INTERRUPT FLAG OUTSIDE ISR
if (buttonInterruptFlag == true) {
buttonInterruptFlag = false;
// millis-based software debounce
if (currentTime - lastValidButtonPress > DEBOUNCE_MS) {
lastValidButtonPress = currentTime;
if (currentState == NORMAL) {
setYellowWarningState();
} else {
Serial.println();
Serial.println("Button press ignored.");
Serial.println("Crossing sequence is already running.");
Serial.println("----------------------------------------");
}
}
}
// STATE MACHINE LOGIC
switch (currentState) {
case NORMAL:
// Waiting for button press
break;
case YELLOW_WARNING:
if (currentTime - stateStartTime >= YELLOW_TIME) {
setPedestrianCrossingState();
}
break;
case PEDESTRIAN_CROSSING:
// Buzzer beep using millis(), no delay()
if (currentTime - lastBuzzerToggle >= BUZZER_INTERVAL) {
lastBuzzerToggle = currentTime;
buzzerState = !buzzerState;
digitalWrite(BUZZER_PIN, buzzerState);
}
if (currentTime - stateStartTime >= CROSSING_TIME) {
setResetState();
}
break;
case RESET_STATE:
setNormalState();
break;
}
}