/**
* Authour: Henderson Hood
* Date: August 1, 2023
* Traffic Light Controller using FSM's
* Pedestrian must press button to get a Green crossing light, otherwise
* a Red pediatrian light is on.
*/
#include <Arduino.h>
bool isButtonPressed = false;
unsigned long lastButtonCheckTime = 0; //last time button checked
const unsigned long buttonInterval = 50; // 0.2 second
unsigned long previousDelayMillis = 0; // 4 second
unsigned long delayInterval = 0; // 0 second
// The varios states of the traffic lights
enum State {STATE_RED_ON, STATE_RED_YELLOW_ON, STATE_GREEN_ON,
STATE_YELLOW_ON, STATE_BUTTON_ACTIVATED, STATE_BUTTON_NOT_ACTIVATED};
State currentState = STATE_BUTTON_NOT_ACTIVATED;
constexpr long int timeBetweenState = 2000; //time between states
//enum buttonState { STATE_BUTTON_ACTIVATED, STATE_BUTTON_NOT_ACTIVATED};
constexpr long int timeBetweenButtonChecks = 500; //time between button checks
//Each led/light is represented by a bit positon in an 8-bit interger.
//The value of the bit determines if led/light is on or off.
constexpr uint8_t T_RED_ON = 1; //Traffic red led - 00000001
constexpr uint8_t T_YELLOW_ON = 2; //Traffic yellow - 00000010
constexpr uint8_t T_GREEN_ON = 4; //Traffic red - 00000100
constexpr uint8_t P_RED_ON = 8; //Pedestrian red - 00001000
constexpr uint8_t P_GREEN_ON = 16; //Pedestrian green - 00010000
//state state functions: to turn on/off leds/lights
void turn_on_leds(uint8_t);
bool isTimerElapsed(unsigned long timerDuration);
void nonBlockingDelay(unsigned long);
//Assigned Traffic lights to GPIO pins
constexpr uint8_t TRAFFIC_LIGHT_RED = 13;
constexpr uint8_t TRAFFIC_LIGHT_YELLOW = 12;
constexpr uint8_t TRAFFIC_LIGHT_GREEN = 11;
constexpr uint8_t PEDESTRIAN_LIGHT_GREEN = 4;
constexpr uint8_t PEDESTRIAN_LIGHT_RED = 5;
constexpr int PEDESTRIAN_BUTTON = 2;
constexpr uint16_t LEDS[] = {TRAFFIC_LIGHT_RED, TRAFFIC_LIGHT_YELLOW,
TRAFFIC_LIGHT_GREEN, PEDESTRIAN_LIGHT_RED, PEDESTRIAN_LIGHT_GREEN};
void setup() {
//setup GPIO OUTPUT pins for traffic/lighs
for (uint8_t i = 0; i<5; i++){
pinMode(LEDS[i], OUTPUT);
}
pinMode(PEDESTRIAN_BUTTON, INPUT_PULLUP);
Serial.begin(115200);
}
void loop() {
switch (currentState){
case STATE_BUTTON_NOT_ACTIVATED:
if (isTimerElapsed(timeBetweenState)) {
digitalWrite(PEDESTRIAN_LIGHT_RED, HIGH);
digitalWrite(PEDESTRIAN_LIGHT_GREEN, LOW);
currentState = STATE_RED_ON;
}
break;
case STATE_RED_ON:
if (isTimerElapsed(timeBetweenState)) {
turn_on_leds(T_RED_ON | P_RED_ON);
//currentState = STATE_YELLOW_ON;
if (isButtonPressed) {
nonBlockingDelay(5000); //used to stop traffic for 5 seconds
currentState = STATE_BUTTON_ACTIVATED;
} else {
currentState = STATE_RED_YELLOW_ON;
}
}
break;
case STATE_RED_YELLOW_ON:
if (isTimerElapsed(timeBetweenState)) {
turn_on_leds(T_RED_ON | T_YELLOW_ON | P_RED_ON);
currentState = STATE_GREEN_ON;
}
break;
case STATE_GREEN_ON:
if (isTimerElapsed(timeBetweenState)) {
turn_on_leds(T_GREEN_ON | P_RED_ON);
currentState = STATE_YELLOW_ON;
}
break;
case STATE_YELLOW_ON:
if (isTimerElapsed(timeBetweenState)) {
turn_on_leds(T_YELLOW_ON | P_RED_ON);
currentState = STATE_RED_ON;
}
break;
case STATE_BUTTON_ACTIVATED:
digitalWrite(PEDESTRIAN_LIGHT_GREEN, HIGH);
digitalWrite(PEDESTRIAN_LIGHT_RED, LOW);
digitalWrite(TRAFFIC_LIGHT_RED, HIGH);
if (isNonBlockingDelayComplete()){
isButtonPressed = false;
delayInterval = 0;
currentState = STATE_BUTTON_NOT_ACTIVATED;
}
break;
}
//Check every 50ms if predestrian press button to cross
if (millis() - lastButtonCheckTime >= buttonInterval ) {
lastButtonCheckTime = millis();
if (!digitalRead(PEDESTRIAN_BUTTON)) { //pedestrian wants to cross
isButtonPressed = true;
}
}
}
//given a mask that represent an led position and value
//in the mask, this function turn on/off that related GPIO pin
void turn_on_leds(uint8_t which_leds) {
//iterate thru each bit in <which_leds> integer
for ( uint8_t i = 0; i < 3; i++) {
//if bit is set to 1, turn on associated GPIO pin (high)
//if bit is set to 0, turn off assiciated GPIO pint (low)
digitalWrite(LEDS[i], (1 << i) & which_leds);
}
}
bool isTimerElapsed(unsigned long timerDuration) {
static bool timerRunning = false;
static unsigned long timerStartTime = 0;
unsigned long currentTime = millis();
if (!timerRunning) {
// Timer is not running, start the timer
timerStartTime = currentTime;
timerRunning = true;
}
if (currentTime - timerStartTime >= timerDuration) {
// Timer has elapsed, reset the timer and return true
timerRunning = false;
return true;
} else {
// Timer is still running, return false
return false;
}
}
//used to stoop traffic flow for 5 seconds when pedestrian has green light
void nonBlockingDelay(unsigned long duration) {
previousDelayMillis = millis();
delayInterval = duration;
}
bool isNonBlockingDelayComplete() {
return (millis() - previousDelayMillis >= delayInterval);
}