/*
Interview project for Lexington Medical
Apurva Reddy
Sdisplay32 Arduino Core (sdisplay32duino)
Specification:
1. With no use of the crosswalk button the traffic lights should cycle between
red/yellow/green per the following sequence:
13 seconds: Mass Ave Green, Beacon St Red
2 seconds: Mass Ave Yellow, Beacon St Red
13 seconds: Mass Ave Red, Beacon St Green
2 seconds: Mass Ave Red, Beacon St Yellow
2. If ever both traffic lights are red, pedestrian light can be green,
otherwise pedestrian light should be red.
3. If the crosswalk button is pressed then whichever traffic light is
currently green (or yellow) should transition if needed to the yellow
state and then after finishing the 2 second yellow state, both traffic
lights should be red for 20 seconds before resuming green on whichever
traffic light holds the longest elapsed time since last being green.
4. Once the crosswalk button has been pressed, further presses should be
queued until one full cycle has finished (meaning 20 seconds for the
pedestrian cycle and 60 seconds for one full traffic cycle).
For reference guides see https://docs.wokwi.com/
*/
extern "C" {
#include "traffic_ctrl.h"
}
//display
#include <SevenSegmentTM1637.h>
// Pin map per diagram.json
constexpr uint8_t PIN_MASS_AVE_RED = A0;
constexpr uint8_t PIN_MASS_AVE_YELLOW = A1;
constexpr uint8_t PIN_MASS_AVE_GREEN = A2;
constexpr uint8_t PIN_BEACON_ST_RED = A5;
constexpr uint8_t PIN_BEACON_ST_YELLOW = A6;
constexpr uint8_t PIN_BEACON_ST_GREEN = A7;
constexpr uint8_t PIN_PED_RED = A3;
constexpr uint8_t PIN_PED_GREEN = A4;
constexpr uint8_t PIN_BUTTON = PB0; // active-low, use pull-up
constexpr uint8_t PIN_CLK = PB10;
constexpr uint8_t PIN_DIO = PB1;
constexpr uint8_t PIN_BUZZER = PC15;
//initialize structs
static TrafficController controller;
static TrafficOutputs outputs;
//initialize display
static SevenSegmentTM1637 display(PIN_CLK, PIN_DIO);
//initialize text that goes onto display
static char buf[5];
//initialize variable that will later help us debounce the button
static bool buttonStableReleased = true;
static bool buttonPreviousReleased = true;
static uint32_t buttonChangeStartTime = 0;
static uint32_t buttonDebounceTime = 30;
//to reduce CPU usage, only update LEDs, Display, and Buzzer when needed
//initialize these intervals to 0 ms
static uint32_t next_tick_1ms = 0;
static uint32_t next_tick_10ms = 0;
static uint32_t next_tick_200ms = 0;
void setup() {
pinMode(PIN_MASS_AVE_RED, OUTPUT);
pinMode(PIN_MASS_AVE_YELLOW, OUTPUT);
pinMode(PIN_MASS_AVE_GREEN, OUTPUT);
pinMode(PIN_BEACON_ST_RED, OUTPUT);
pinMode(PIN_BEACON_ST_YELLOW, OUTPUT);
pinMode(PIN_BEACON_ST_GREEN, OUTPUT);
pinMode(PIN_PED_RED, OUTPUT);
pinMode(PIN_PED_GREEN, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
noTone(PIN_BUZZER);
//crosswalk button is an input
//HIGH when not pressed and LOW when pressed
pinMode(PIN_BUTTON, INPUT_PULLUP); // active-low
Serial.begin(115200);
display.begin();
display.setBacklight(100);
display.setColonOn(false);
Serial.println("BOOT: dual-road TL controller");
Serial.println("Code Begins!");
uint32_t current_time = millis();
traffic_ctrl_initialize(&controller, (uint32_t)millis());
next_tick_1ms = current_time;
next_tick_10ms = current_time;
next_tick_200ms = current_time;
}
void loop() {
uint32_t current_time = millis();
detect_crosswalk_button_push(&controller, current_time);
traffic_ctrl_run_all(&controller, current_time);
apply_buzzer_outputs(&controller.outputs);
// while ((int32_t)(current_time - next_tick_1ms) >= 0) {
// apply_buzzer_outputs(&controller.outputs);
// next_tick_1ms += 1;
// }
while ((int32_t)(current_time - next_tick_10ms) >= 0) {
apply_led_outputs(&controller.outputs);
next_tick_10ms += 10;
}
while ((int32_t)(current_time - next_tick_200ms) >= 0){
output_to_seven_segment_display(&controller);
next_tick_200ms +=200;
}
}
void output_to_seven_segment_display(TrafficController *controller){
if(controller->crosswalk_countdown_timer_value==0){
display.clear();
}
else{
snprintf(buf, sizeof(buf), "%4d", controller->crosswalk_countdown_timer_value);
buf[4] = '\0';
display.print(buf);
}
}
void detect_crosswalk_button_push(TrafficController *controller, uint32_t current_time) {
//button is HIGH when not pressed, LOW when pressed
bool buttonReleasedRaw = (digitalRead(PIN_BUTTON) == HIGH);
// if raw reading differs from stable reading, start /continue debounce timing
if (buttonReleasedRaw != buttonStableReleased) {
if (buttonChangeStartTime == 0) {
//start timing the change
buttonChangeStartTime = current_time;
}
// if the raw value stayed changed long enough, accept it as the new stable value
if ((uint32_t)(current_time - buttonChangeStartTime) >= buttonDebounceTime) {
buttonPreviousReleased = buttonStableReleased;
buttonStableReleased = buttonReleasedRaw;
buttonChangeStartTime = 0;
// most crosswalk buttons immediately process the push when you press it
// not similar to other buttons that might wait for you to press and then release
// this checks if the button was previously not pressed, and now is pressed
if (buttonPreviousReleased == true && buttonStableReleased == false) {
controller->crosswalk_pending = true;
}
}
} else {
// no change and reset debounce timing
buttonChangeStartTime = 0;
}
}
void apply_buzzer_outputs(const TrafficOutputs *outputs) {
if (outputs->buzzer_stop) {
noTone(PIN_BUZZER);
}
if (outputs->buzzer_start) {
tone(PIN_BUZZER, outputs->buzzer_frequency_hz);
}
}
void apply_led_outputs(const TrafficOutputs *outputs) {
digitalWrite(PIN_MASS_AVE_RED, outputs->mass_ave_red);
digitalWrite(PIN_MASS_AVE_YELLOW, outputs->mass_ave_yellow);
digitalWrite(PIN_MASS_AVE_GREEN, outputs->mass_ave_green);
digitalWrite(PIN_BEACON_ST_RED, outputs->beacon_st_red);
digitalWrite(PIN_BEACON_ST_YELLOW, outputs->beacon_st_yellow);
digitalWrite(PIN_BEACON_ST_GREEN, outputs->beacon_st_green);
digitalWrite(PIN_PED_RED, outputs->ped_red);
digitalWrite(PIN_PED_GREEN, outputs->ped_green);
}