// =========================================
// DEFINES
// =========================================
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))
#define ISPRESSED LOW
bool isDark = false; // changed from macro to real variable
// =========================================
// ENUMS
// =========================================
// =========================================
// ENUMS
// =========================================
enum CarState {
CAR_BLINK,
CAR_RED,
CAR_GREEN,
CAR_ORANGE
};
enum CrossingState {
CROSS_RED,
CROSS_GREEN,
CROSS_BLINKING
};
// =========================================
// STRUCTS
// =========================================
struct Button {
const uint8_t pin;
unsigned long lastDeBounceTime;
uint8_t lastButtonState;
uint8_t currDebouncedState;
uint8_t prevDebouncedState;
};
struct Timer {
// ======================
unsigned long startTime = 0;
bool reset = true;
void start() {
startTime = millis();
reset = false;
}
bool expired(unsigned long interval) {
unsigned long now = millis();
if (reset) start();
if (now - startTime >= interval) {
reset = true;
return true;
}
return false;
}
void restart() {
start();
}
};
Timer carTimer;
Timer pedTimer;
Timer flashTimer;
// =========================================
// STATE TABLE
// =========================================
struct StateInfo {
const char* label;
// ======================
unsigned long duration;
uint8_t next;
};
// =========================================
// STATE TABLES
// =========================================
// Car states
const StateInfo carStateTable[] = {
{ "Blink", 6000, CAR_RED },
{ "Red", 5000, CAR_GREEN },
{ "Green", 5000, CAR_ORANGE },
{ "Orange", 3000, CAR_RED }
};
// Pedestrian states
const StateInfo pedStateTable[] = {
{ "Red", 0, CROSS_GREEN },
{ "Green", 3000, CROSS_BLINKING },
{ "Blink", 6000, CROSS_RED }
};
// Retrieve a state entry by enum
const StateInfo* getCarStateInfo(CarState state) {
for (uint8_t i = 0; i < NUMELEMENTS(carStateTable); i++)
if (carStateTable[i].label == state)
return &carStateTable[i];
return nullptr; // important fix
}
// Retrieve a state entry by enum
const StateInfo* getPedistrianStateInfo(CrossingState state) {
for (uint8_t i = 0; i < NUMELEMENTS(pedStateTable); i++)
if (pedStateTable[i].name == state)
return &pedStateTable[i];
return nullptr; // important fix
}
// =========================================
// GLOBALS
// =========================================
CarState currentCar = CAR_BLINK;
CrossingState currentPed = CROSS_RED;
bool pedRequest = false;
unsigned long lastPedCycle = 0; // track last pedestrian cycle
const unsigned long pedCooldown = 120000;
Button buttonList[] = {
{ 3, 0, !ISPRESSED, !ISPRESSED, !ISPRESSED }
};
uint32_t debounceDelay = 20;
uint8_t pinsTL[] = { 8, 7, 6 }; // Car: Red, Orange, Green
uint8_t pinsPL[] = { 12, 11 }; // Ped: Red, Green
int interval_flash = 500;
// =========================================
// UTILITIES
// =========================================
void digitalWrite2(uint8_t pin1, uint8_t pin2, uint8_t value) {
digitalWrite(pin1, value);
digitalWrite(pin2, value);
}
void allLightsOff() {
for (uint8_t i = 0; i < NUMELEMENTS(pinsTL); i++)
digitalWrite(pinsTL[i], LOW);
for (uint8_t i = 0; i < NUMELEMENTS(pinsPL); i++)
digitalWrite(pinsPL[i], LOW);
}
void resetState(bool lights = true) {
carTimer.reset = true;
pedTimer.reset = true;
flashTimer.reset = true;
if (lights) allLightsOff();
}
// =========================================
// BUTTON HANDLER
// =========================================
void readButton() {
for (uint8_t i = 0; i < NUMELEMENTS(buttonList); i++) {
uint8_t buttonState = digitalRead(buttonList[i].pin);
if (buttonState != buttonList[i].lastButtonState)
buttonList[i].lastDeBounceTime = millis();
if (millis() - buttonList[i].lastDeBounceTime > debounceDelay) {
buttonList[i].prevDebouncedState = buttonList[i].currDebouncedState;
buttonList[i].currDebouncedState = buttonState;
}
buttonList[i].lastButtonState = buttonState;
}
}
// =========================================
// FLASHER
// =========================================
void flashLED(uint8_t pin, Timer& timer, unsigned long interval) {
if (timer.expired(interval)) {
int status = digitalRead(pin);
digitalWrite(pin, !status);
}
}
/*
// =========================================
// STATE HANDLING
// =========================================
void enterState(StateNames state) {
const StateInfo* info = getStateInfo(state);
if (!info) {
Serial.println(F("ERROR: Invalid state"));
return;
}
Serial.print(F("Entering state: "));
Serial.println(info->label);
if (state == stX) {
resetState(false);
} else {
resetState();
}
}
StateNames nextByTable(StateNames current) {
const StateInfo* info = getStateInfo(current);
if (!info) return current;
if (timers.loop.expired(info->duration)) {
enterState(info->next);
return info->next; // fixed
}
return current;
}
// =========================================
// SETUP
// =========================================
void setup() {
Serial.begin(9600);
// Initialize pins
for (uint8_t i = 0; i < NUMELEMENTS(pinsTL); i++)
pinMode(pinsTL[i], OUTPUT);
for (uint8_t i = 0; i < NUMELEMENTS(pinsPL); i++)
pinMode(pinsPL[i], OUTPUT);
for (uint8_t i = 0; i < NUMELEMENTS(buttonList); i++)
pinMode(buttonList[i].pin, INPUT_PULLUP);
allLightsOff();
}
// =========================================
// MAIN LOOP
// =========================================
void loop() {
static StateNames currentState = st_Blink;
readButton(); // keep button debounced
switch (currentState) {
case st_Blink:
if (!isDark) {
if (timers.loop.expired(getStateInfo(currentState)->duration)) {
currentState = st_Red_Cars_Red_Pedestrians;
enterState(currentState);
break;
}
}
// Flash orange (pinTL[1])
flashLED(pinsTL[1], timers.flashCar, interval_flash);
break;
case st_Red_Cars_Red_Pedestrians:
digitalWrite2(pinsTL[0], pinsPL[0], HIGH); // red/red
currentState = nextByTable(currentState);
break;
case st_Green_Cars_Red_Pedestrians:
digitalWrite2(pinsTL[2], pinsPL[0], HIGH);
if (buttonList[0].currDebouncedState == ISPRESSED)
{
lastTimePressed = millis();
Serial.print(F("button pressed @ "));
Serial.print(lastTimePressed);
Serial.print(F("\tcurrentState changed from "));
Serial.print(stateTable[currentState].label);
currentState = nextByTable(currentState);
Serial.print(F(" to "));
Serial.println(stateTable[currentState].label);
}
break;
case stX:
if (millis() - lastTimePressed > getStateInfo(currentState)->duration) {
currentState = nextByTable(currentState);
} else {
if (timers.loop.expired(getStateInfo(currentState)->duration)) {
currentState = nextByTable(currentState);
}
}
break;
case st_Orange_Cars_Red_Pedestrians:
digitalWrite2(pinsTL[1], pinsPL[0], HIGH);
currentState = nextByTable(currentState);
break;
case st_Red_Cars_Red_Pedestrians_After_Cars_Loop:
digitalWrite2(pinsTL[0], pinsPL[0], HIGH);
currentState = nextByTable(currentState);
break;
case st_Red_Cars_Green_Pedestrians:
digitalWrite(pinsTL[0], HIGH); // cars red
digitalWrite(pinsPL[1], HIGH); // ped green
currentState = nextByTable(currentState);
break;
case st_Red_Cars_Green_Blinking_Pedestrians:
digitalWrite(pinsTL[0], HIGH);
flashLED(pinsPL[1], timers.flashPed, interval_flash);
currentState = nextByTable(currentState);
break;
case st_Red_Cars_Red_Pedestrians_after_Pedestrians_Loop:
digitalWrite2(pinsTL[0], pinsPL[0], HIGH);
currentState = nextByTable(currentState);
break;
}
}
*/