#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Define the LED pins based on your exact circuit wiring
const int VEHICLE_GREEN = 8;
const int VEHICLE_YELLOW = 9;
const int VEHICLE_RED = 10;
const int PED_RED = 6;
const int PED_GREEN = 5;
const int BUTTON_PIN = 2;
// Define Ultrasonic Sensor Pins
const int TRIG_PIN = 4;
const int ECHO_PIN = 3;
// Define LDR Pin (Using A0 as a Digital Pin for the DO connection)
const int LDR_PIN = A0;
// Define Piezo/Buzzer LED indicator Pin
const int PIEZO_PIN = 7;
/* --- Clean C Syntax State Definition --- */
typedef enum {
STATE_VEHICLE_GREEN,
STATE_VEHICLE_YELLOW,
STATE_VEHICLE_RED,
STATE_ALL_RED_BUFFER // 2s safety buffer where everything is red
} TrafficState;
TrafficState currentState = STATE_VEHICLE_GREEN;
unsigned long stateStartTime = 0;
unsigned long stateDuration = 15000; // Baseline 15s for Vehicle Green
// Volatile flag updated by hardware interrupt
volatile int buttonPressed = 0;
// Memory flags for system states
int activeCrossing = 0;
int sensorReadExecuted = 0;
int isBusyTraffic = 0;
// Variables to track changes in seconds to eliminate LCD flickering
int lastRemainingSeconds = -1;
// Night-time Blinking Engine Trackers (Non-blocking 200ms tracking)
unsigned long lastBlinkTime = 0;
int blinkState = LOW;
/* --- Initialize the I2C LCD (Address 0x27) --- */
LiquidCrystal_I2C lcd(0x27, 16, 2);
/* --- Standard C Function Prototypes --- */
void setLights(int vG, int vY, int vR, int pR, int pG);
void handleButtonPress(void);
long readUltrasonicDistance(void);
void updateLCDDisplay(unsigned long current, unsigned long start, unsigned long duration);
void setup() {
pinMode(VEHICLE_GREEN, OUTPUT);
pinMode(VEHICLE_YELLOW, OUTPUT);
pinMode(VEHICLE_RED, OUTPUT);
pinMode(PED_RED, OUTPUT);
pinMode(PED_GREEN, OUTPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(LDR_PIN, INPUT); // Configured to read the DO signal
pinMode(BUTTON_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), handleButtonPress, RISING);
// Initialize Piezo hardware state safely
noTone(PIEZO_PIN);
// Initialize LCD hardware
lcd.init();
lcd.backlight();
lcd.clear();
stateStartTime = millis();
}
void loop() {
unsigned long currentTime = millis();
long distance_cm = 0;
int isNight = 0;
// 1. GLOBAL LDR NIGHT SENSING CHECK (Digital Module Version)
int ldrState = digitalRead(LDR_PIN);
if (ldrState == HIGH) { // Most blue modules send a HIGH signal when it gets dark
isNight = 1;
}
// 2. FAST NON-BLOCKING BLINK GENERATOR ENGINE (200ms for night yellow caution)
if (currentTime - lastBlinkTime >= 200) {
lastBlinkTime = currentTime;
if (blinkState == LOW) {
blinkState = HIGH;
} else {
blinkState = LOW;
}
}
// 3. RESTRICTED BUTTON CHECK (Only registers during Green or Yellow phases)
if (buttonPressed == 1) {
buttonPressed = 0;
if (currentState == STATE_VEHICLE_GREEN || currentState == STATE_VEHICLE_YELLOW) {
activeCrossing = 1;
}
}
// 4. DYNAMIC LCD REFRESH ENGINE
updateLCDDisplay(currentTime, stateStartTime, stateDuration);
// 5. RUN THE TRAFFIC LIGHT STATE MACHINE
switch (currentState) {
case STATE_VEHICLE_GREEN:
noTone(PIEZO_PIN); // Absolute guard: Silence during vehicle green flow
if (isNight == 1) {
setLights(HIGH, blinkState, LOW, HIGH, LOW);
} else {
setLights(HIGH, LOW, LOW, HIGH, LOW);
}
// Monitor ultrasonic sensor at the 13th second window
if ((currentTime - stateStartTime >= 13000) && (sensorReadExecuted == 0)) {
sensorReadExecuted = 1;
distance_cm = readUltrasonicDistance();
if (distance_cm >= 5 && distance_cm <= 250 && activeCrossing == 0) {
stateDuration = stateDuration + 5000; // Extend Green phase to 20 seconds
isBusyTraffic = 1;
}
}
if (currentTime - stateStartTime >= stateDuration) {
currentState = STATE_VEHICLE_YELLOW;
stateStartTime = currentTime;
stateDuration = 3000;
lastRemainingSeconds = -1;
}
break;
case STATE_VEHICLE_YELLOW:
noTone(PIEZO_PIN); // Absolute guard: Silence during yellow clear phase
setLights(LOW, HIGH, LOW, HIGH, LOW);
if (currentTime - stateStartTime >= stateDuration) {
if (activeCrossing == 1) {
currentState = STATE_ALL_RED_BUFFER;
stateDuration = 2000; // Load 2s safety buffer
} else {
currentState = STATE_VEHICLE_RED;
// Apply 3x proportional Red light delay based on previous Green state length
if (isBusyTraffic == 1) {
stateDuration = 60000; // 3 * 20s = 60s
} else {
stateDuration = 45000; // 3 * 15s = 45s
}
}
stateStartTime = currentTime;
lastRemainingSeconds = -1;
}
break;
case STATE_ALL_RED_BUFFER:
noTone(PIEZO_PIN); // Absolute guard: Silence during 2s full clearance lock
if (isNight == 1) {
setLights(LOW, blinkState, HIGH, HIGH, LOW);
} else {
setLights(LOW, LOW, HIGH, HIGH, LOW);
}
if (currentTime - stateStartTime >= stateDuration) {
currentState = STATE_VEHICLE_RED;
stateStartTime = currentTime;
// Ensure 3x Red light timer balance matches even when crosswalk paths trigger
if (isBusyTraffic == 1) {
stateDuration = 60000;
} else {
stateDuration = 45000;
}
}
break;
case STATE_VEHICLE_RED:
if (activeCrossing == 1) {
tone(PIEZO_PIN, 1000);
if (isNight == 1) {
setLights(LOW, blinkState, HIGH, LOW, HIGH);
} else {
setLights(LOW, LOW, HIGH, LOW, HIGH);
}
} else {
noTone(PIEZO_PIN);
if (isNight == 1) {
setLights(LOW, blinkState, HIGH, HIGH, LOW);
} else {
setLights(LOW, LOW, HIGH, HIGH, LOW);
}
}
if (currentTime - stateStartTime >= stateDuration) {
noTone(PIEZO_PIN);
activeCrossing = 0;
sensorReadExecuted = 0;
isBusyTraffic = 0;
currentState = STATE_VEHICLE_GREEN;
stateStartTime = currentTime;
stateDuration = 15000;
lastRemainingSeconds = -1;
}
break;
}
}
/* --- Standard C Function Declarations --- */
void setLights(int vG, int vY, int vR, int pR, int pG) {
digitalWrite(VEHICLE_GREEN, vG);
digitalWrite(VEHICLE_YELLOW, vY);
digitalWrite(VEHICLE_RED, vR);
digitalWrite(PED_RED, pR);
digitalWrite(PED_GREEN, pG);
}
void handleButtonPress(void) {
buttonPressed = 1;
}
long readUltrasonicDistance(void) {
long duration = 0;
long distance = 0;
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
duration = pulseIn(ECHO_PIN, HIGH, 30000);
if (duration == 0) {
return -1;
}
distance = (duration * 0.0343) / 2;
return distance;
}
// Managed Interface Engine
void updateLCDDisplay(unsigned long current, unsigned long start, unsigned long duration) {
long timeRemainingLeft = (long)(duration - (current - start));
if (timeRemainingLeft < 0) {
timeRemainingLeft = 0;
}
int secondsLeft = (int)(timeRemainingLeft / 1000) + 1;
if (secondsLeft > (int)(duration / 1000)) {
secondsLeft = (int)(duration / 1000);
}
if (secondsLeft != lastRemainingSeconds) {
lastRemainingSeconds = secondsLeft;
lcd.clear();
switch (currentState) {
case STATE_VEHICLE_GREEN:
lcd.setCursor(0, 0);
if (isBusyTraffic == 1) {
lcd.print("busy traffic");
} else {
lcd.print("normal flow");
}
lcd.setCursor(0, 1);
lcd.print("Green: ");
lcd.print(secondsLeft);
lcd.print("s");
break;
case STATE_VEHICLE_YELLOW:
lcd.setCursor(0, 0);
lcd.print("clear lanes");
lcd.setCursor(0, 1);
lcd.print("Yellow: ");
lcd.print(secondsLeft);
lcd.print("s");
break;
case STATE_ALL_RED_BUFFER:
lcd.setCursor(0, 0);
lcd.print("PREPARING");
lcd.setCursor(0, 1);
lcd.print("HOLD POS: ");
lcd.setCursor(11, 1);
lcd.print(secondsLeft);
lcd.print("s");
break;
case STATE_VEHICLE_RED:
lcd.setCursor(0, 0);
if (activeCrossing == 1) {
lcd.print("WALK SIGNAL ON");
lcd.setCursor(0, 1);
lcd.print("CROSS TIME: ");
} else {
lcd.print("cross traffic");
lcd.setCursor(0, 1);
lcd.print("Red: ");
}
lcd.print(secondsLeft);
lcd.print("s");
break;
}
}
}