/*
Name: HW03_drag_race_xmas_tree___prof_solution___blizard.ino
Created: 9/20/2022 6:19:00 PM
Author: nbliz
*/
/* HW03 Drag Race Xmas Tree using SevSeg library - PROF SOLUTION
Date: 9/20/2022
Author n.blizard
Purpose - Drag Race Timer
FOLLOW PROFS OUTLINE EXACTLY (NO EMBELLISHMENTS!!!)
Objective: Build a drag racing Christmas Tree just like they use at the
NHRA U.S. National drag races in Indianapolis.
You will use 3x yellow LEDs,
1x red LED, 1x Green LED,
4 digit 7-segment display,
a push button, and the sevseg library.
Watch this video to see how it works (starting at 3:33):
https://www.youtube.com/watch?v=DPCq6Ukr61I&ab_channel=CJPonyParts
We are not going to do the pre-stage lights.
Also on ours, you can’t go before the green or you cheated and get a red light.
There are three keys to this homework we learned in the lecture:
Doing something while you wait - Used instead of delays.
Sometimes you will just be refreshing the display.
Sometimes you will also be checking if the user cheated by pushing the button before the green light.
You’ll probably want to set a flag to check later if the user cheated.
Flags - in the timing and display part you may want to check a “cheated” flag
and light the red LED and abort the run before you start timing.
Sevseg library - if you give it a number greater than 9999, it will display “----”.
If you use the function sevseg.setChars("") it will clear the display.
Lastly, after you set the numbers or characters,
you must refresh the display to have that displayed.
In setup() start with everything off and a blank 7-segment display.
There are generally three parts in the loop():
Initialize -
Turn on the green LED to indicate to the user to “start”.
Wait for the user to press the button while refreshing the display.
Then debounce the button while still refreshing the display.
Turn off the red LED in case it’s lit from a previous run.
Clear the seven segment display.
You may need to initialize some variables you use here.
Sequence the Christmas Tree -
Turn off the green LED.
Blink the three yellow LEDs like in the video,
waiting a half second before moving to the next LED.
Remember to refresh the display and check if the user cheated during this time.
Turn on the green LED.
Timing and Display -
Record the start time.
While you wait for the user to press the pushbutton,
calculate the elapsed time in milliseconds and
display it while continuously refreshing the display.
Then check the “cheated” flag to see if the user cheated.
If he did, set the sevseg display to “----”, turn on the red LED, and clear the flag.
Then debounce the button while still refreshing the display.
Test Cases
- no buttons pushed - waits for start
- first button push but not 2nd or third -
- second button push too early
CHEATEd, abort test
- 2nd button push after green light lights, start timer and start run
- third button push - end of run
- if pushed too late, TOO SLOW, abort run
- reset for another run
*/
#define RED_LIGHT A1 // red light is on pin A1 (index 0 in array)
#define YELLOW_LIGHT1 A2 // yellow1 is on pin A2
#define YELLOW_LIGHT2 A3 // yellow2 is on pin A3
#define YELLOW_LIGHT3 A4 // yellow3 is on pin A4
#define GREEN_LIGHT A5 // green light is on A5 (index 4 in array)
#define PUSHBUTTON A0 // pushbutton is on A0 INPUT_PULLUP
#define STAGING_LIGHT_ON_TIME 500 // on time for each yellow light
// include seven seg library
#include "SevSeg.h"
SevSeg sevseg; //Instantiate a seven segment controller object
// globals
const bool PRESSED = LOW; // low if pressed
//const byte r3ygLED[] = { A1, A2, A3, A4, A5 }; // R Y1 Y2 Y3 G
bool cheatedFlag = false; // set to true if cheated
unsigned long elapsedTime = 0; // display at end of run (make global)
const byte DEBOUNCE_TIME = 100; // msec to prevent multiple button pushes detected
const int TIME_MAX = 10000; // max display ("----")
void setup() {
//sev segment specific variables - declaring const saves memory
const byte numDigits = 4;
const byte digitPins[] = { 2, 3, 4, 5 };
const byte segmentPins[] = { 6, 7, 8, 9, 10, 11, 12, 13 };
const bool resistorsOnSegments = true; // 'true' means resistors are on segment pins
const byte hardwareConfig = COMMON_CATHODE; // See README.md for options
const bool updateWithDelays = false; // Default 'false' is Recommended
const bool leadingZeros = false; // Use 'true' if you'd like to keep the leading zeros
const bool disableDecPoint = false; // Use 'true' if your decimal point doesn't exist or isn't connected
// initialize 7 segment display
sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments,
updateWithDelays, leadingZeros, disableDecPoint);
sevseg.setBrightness(255);
// initialize serial print
Serial.begin(9600);
//set input pinMode for button, pullup to set high with input resistor
pinMode(PUSHBUTTON, INPUT_PULLUP);
//digitalWrite(PUSHBUTTON,HIGH);
// set pinmodes for leds
pinMode(GREEN_LIGHT, OUTPUT);
pinMode(YELLOW_LIGHT1, OUTPUT);
pinMode(YELLOW_LIGHT2, OUTPUT);
pinMode(YELLOW_LIGHT3, OUTPUT);
pinMode(RED_LIGHT, OUTPUT);
digitalWrite(RED_LIGHT, HIGH); //set high initially
for (int j = 2; j <= 13; j++) {
pinMode(j, OUTPUT);
}
}
void loop() {
InitializationSequence();
SequenceXmasTree();
DoTiming();
displayFinalTime();
}
//--------------------------------------------------------------------------------
// Initialize function
// Turn on the green LED to indicate to the user to “start”.
// Wait for the user to press the button while refreshing the display.
// Then debounce the button while still refreshing the display.
// Turn off the red LED in case it’s lit from a previous run.
// Clear the seven segment display.
// You may need to initialize some variables you use here.
void InitializationSequence() {
Serial.println("------ ready for run --------------");
// Turn on the green LED to indicate to the user to “start”.
digitalWrite(GREEN_LIGHT, HIGH);
// Wait for the user to press the button while refreshing the display.
while (digitalRead(PUSHBUTTON) != PRESSED) {
sevseg.refreshDisplay();
}
// Then debounce the button while still refreshing the display.
debounceWhileRefreshing(DEBOUNCE_TIME);
// Turn off the red LED in case it’s lit from a previous run.
digitalWrite(RED_LIGHT, LOW);
// Clear the seven segment display.
sevseg.setChars("");
sevseg.refreshDisplay();
// Initialize some variables you use here.
cheatedFlag = false;
} // end InitializationSequence
//--------------------------------------------------------------------------------
//debounce function (delay in while loop without actually calling delay function -which pauses arduino)
void debounceWhileRefreshing(int delayTime) {
unsigned long futureTime = millis() + delayTime; // this sets delay time to be in future
while (millis() < futureTime) {
sevseg.refreshDisplay();
}
} // end debounceWhileRefreshing
//--------------------------------------------------------------------------------
// Sequence the Christmas Tree function
// Turn off the green LED.
// Blink the three yellow LEDs like in the video,
// waiting a half second before moving to the next LED.
// Remember to refresh the display and check if the user cheated during this time.
// Turn on the green LED.
void SequenceXmasTree() {
// Turn off the green LED.
digitalWrite(GREEN_LIGHT, LOW);
// make sure the cheated flag is false before starting sequence
cheatedFlag = false; // force false for test FIXME???????????????????????????????
// Blink the three yellow LEDs in sequence like video
BlinkLEDAndCheckButton(YELLOW_LIGHT1);
if (cheatedFlag) {
Serial.println("..caught cheating. RED LIGHT");
}
BlinkLEDAndCheckButton(YELLOW_LIGHT2);
if (cheatedFlag) {
Serial.println("..caught cheating. RED LIGHT");
}
BlinkLEDAndCheckButton(YELLOW_LIGHT3);
if (cheatedFlag) {
Serial.println("..caught cheating. RED LIGHT");
}
// Turn on the green LED.
digitalWrite(GREEN_LIGHT, HIGH);
} // end SequenceXmasTree
// Turns on and off the selected Light
// checks for button press while lighting led for required delay
// wait half second before moving to the next LED.
//--------------------------------------------------------------------------------
void BlinkLEDAndCheckButton(byte led) {
digitalWrite(led, HIGH); // lights the led
WaitAndCheckButton(STAGING_LIGHT_ON_TIME); // wait and check for cheating
digitalWrite(led, LOW); // turns the led off
} // end BlinkLED...
//--------------------------------------------------------------------------------
// refresh the display and check if the user cheated during this time.
void WaitAndCheckButton(int delayTime) {
unsigned long futureTime = millis() + delayTime; // on time for led
cheatedFlag = false;
while (millis() < futureTime) {
if (digitalRead(PUSHBUTTON) == PRESSED) {
cheatedFlag = true; // he cheated, while in yellow light sequence
// debounce while refreshing
debounceWhileRefreshing(DEBOUNCE_TIME); // FIXME IS THIS NECESSARY ???????????
}
sevseg.refreshDisplay();
}
} // end WaitandCheck...
//--------------------------------------------------------------------------------
// Timing and Display -
// Record the start time.
// While you wait for the user to press the pushbutton,
// calculate the elapsed time in milliseconds and
// display it while continuously refreshing the display.
// Then check the “cheated” flag to see if the user cheated.
// If he did, set the sevseg display to “----”, turn on the red LED, and clear the flag.
// Then debounce the button while still refreshing the display.
void DoTiming() {
// Record the start time.
unsigned long startTime = millis();
// While you wait for the user to press the pushbutton,
while (digitalRead(PUSHBUTTON) != PRESSED) {
// calculate the elapsed time in milliseconds
elapsedTime = millis() - startTime; // FIXME I made this a global for a later routine to display run time
// display it while continuously refreshing the display.
sevseg.setNumber(elapsedTime, 0); // setnumber in display, no decimal point or leading zeros
sevseg.refreshDisplay();
// check the “cheated” flag to see if the user cheated.
if (elapsedTime > TIME_MAX) {
cheatedFlag = true; // penalize anyway
}
if (cheatedFlag == true) {
// If he did or too slow, set the sevseg display to “----”,
sevseg.setNumber(TIME_MAX, 0);
sevseg.refreshDisplay();
// turn on the red LED, and clear the flag.
digitalWrite(RED_LIGHT, HIGH);
cheatedFlag = false;
debounceWhileRefreshing(DEBOUNCE_TIME); // FIXME IS THIS NECESSARY ???????????
} // end cheated
} // end while
} // end DoTiming
//--------------------------------------------------------------------------------
// this routine displays final elapsed timing if not cheated
// display until button pressed again
void displayFinalTime() {
while (digitalRead(PUSHBUTTON) != PRESSED) {
// display the final elapsed time in milliseconds
// while continuously refreshing the display.
sevseg.setNumber(elapsedTime, 0);
sevseg.refreshDisplay();
}
// reset for another run
Serial.print("Elapsed time = ");
Serial.println(elapsedTime);
Serial.println("resetting for another run");
delay(2000);
}