#include "SevSeg.h" // Use the sevseg library for the 7-segment display
#define r_pin 11 // Define an RGB LED (Red pin)
#define g_pin 12 // Define an RGB LED (Green pin)
#define b_pin 13 // Define an RGB LED (Blue pin)
#define buzzer 10 // Define buzzer pin
#define freq 500 // Define frequency for the buzzer
#define doorPin 2 // Define the input door pin
#define snoozeButtonPin 3 // Define snooze button pin
#define modePotentiometer A0 // Define the analog pin for the mode selector
#define timePotentiometer A1 // Define the analog pin for the time selector
unsigned long lastOpenTime = 0; // The last time the door changed state from close to open
unsigned long currentMillis; // The current time variable
unsigned long counter; // The counter for the 7-segment count-down
const int timer = 1000; // One second variable, used for calculating the timer
const int maxPotentiometerValue = 1023; // The maximum value of the potentiometer
int oneThird = maxPotentiometerValue/3; // The one-third value of the potentiometer used for comparison
int twoThirds = (maxPotentiometerValue/3)*2; // The two-thirds value of the potentiometer used for comparison
int lastInputState; // The previous reading from the input door sensor
int currentInputState = 1; // The current reading from the input door sensor
int modeState = 0; // The mode state variable: 0 -> No Alarm Mode, 1 -> Immediate Mode, 2 -> Timed Mode
int timeState = 10; // The time state variable for the timed mode
int seconds = 0; // The number displayed by the 7-segment as a count-down timer
volatile bool inputFlag = false; // The input flag variable, triggered by the input door (external interrupt)
volatile bool snoozeFlag = false; // The snooze flag variable, triggered by the snooze button (external interrupt)
SevSeg sevseg; //Instantiate a seven segment controller object
void setup() {
Serial.begin(9600);
DDRE &= B11101111; // Set D2 as Door Sensor INPUT -> (PE4)
DDRE &= B11011111; // Set D3 as Snooze Button INPUT -> (PE5)
PORTE |= B00110000; // Enable the pullup for PORT E pin 4 and 5
DDRH |= B00010000; // Set D7 as OUTPUT (LED) -> (PH4)
DDRB |= B11100000; // Set D11, D12, and D13 as OUTPUT (RGB) -> (PB5, PB6, and PB7)
DDRB |= B00010000; // Set D10 as OUTPUT (Buzzer) -> (PB4)
attachInterrupt(digitalPinToInterrupt(doorPin), input_ISR_Change, CHANGE);
attachInterrupt(digitalPinToInterrupt(snoozeButtonPin), snooze_ISR_Change, CHANGE);
// Initialize the 7-segment display
byte numDigits = 2;
byte digitPins[] = {22, 24};
byte segmentPins[] = {40, 42, 44, 46, 48, 50, 52};
bool resistorsOnSegments = true;
byte hardwareConfig = COMMON_CATHODE;
bool updateWithDelays = false;
bool leadingZeros = true;
bool disableDecPoint = true;
sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments,
updateWithDelays, leadingZeros, disableDecPoint);
sevseg.setBrightness(100);
}
void loop() {
// If the inputflag is TRUE call the doorOpen() function. If not, call the doorClose() function.
if (inputFlag) {
doorOpen();
} else {
doorClose();
currentInputState = 1; // If the door is close, set door close state (reset input state)
}
sevseg.refreshDisplay(); // Refresh the 7-segment display, which has to run repeatedly
}
// Interrupt Service Routine for the door pin (D2)
void input_ISR_Change() {
int inputState = (PINE >> 4 & B00010000 >> 4);
if (inputState) {
inputFlag = false;
} else {
inputFlag = true;
}
}
// Interrupt Service Routine for the snooze button pin (D3)
void snooze_ISR_Change() {
int snoozeState = (PINE >> 5 & B00100000 >> 5);
if (snoozeState) {
snoozeFlag = false;
} else {
snoozeFlag = true;
}
}
// Function to turn RGB LED on (RED)
void rgb_Red_On() {
analogWrite(r_pin, 255);
analogWrite(g_pin, 0);
analogWrite(b_pin, 0);
}
// Function to turn RGB LED on (GREEN)
void rgb_Green_On() {
analogWrite(r_pin, 0);
analogWrite(g_pin, 255);
analogWrite(b_pin, 0);
}
// This is the doorOpen() function
void doorOpen() {
// The input are wired to the ground with the internal pull up enabled.
// The input will be HIGH when not pressed, and LOW when pressed. (Input normally HIGH)
lastInputState = currentInputState; // Set lastInputState to current input state (save state for next loop)
currentInputState = (PINE >> 4 & B00010000 >> 4); // Set the input from Port E pin 4 to currentInputState
// If the door change state from close to open, then save the time mark to lastOpenTime
if ((currentInputState != 1) && (lastInputState == 1)) {
lastOpenTime = millis();
// If the mode state is 2, then set the counter to current time, and set seconds variable to current time state
if (modeState == 2) {
counter = millis();
seconds = getTimeState();
}
}
PORTH = (PORTH & B00000000) | B00010000; // Turn on the red LED on D7 (Port H pin 4)
rgb_Red_On(); // Call the function to turn on the red RGB LED
buzzerOn(); // Call the buzzerOn() function
sevseg.refreshDisplay(); // Refresh the 7-segment display
}
// This is the doorClose() function
void doorClose() {
PORTH &= B11101111; // Turn off the red LED
rgb_Green_On(); // Turn on green RGB LED
noTone(buzzer); // Turn the buzzer off
modeState = getModeState(); // Get the mode state
// If the modeState is 2, then set the timeState variable to the current time state according to the potentiometer
if (modeState == 2) {
timeState = getTimeState();
}
// If the modeState is in No alarm mode, display "no" on the 7-segment display
// If the modeState is in Immediate mode, display "ia"
// If the modeState is in Timed mode, display the timeState based on the current potentiometer position
switch (modeState) {
case (0) :
sevseg.setChars("no");
break;
case (1) :
sevseg.setChars("ia");
break;
case (2) :
seconds = getTimeState();
sevseg.setNumber(seconds);
break;
}
sevseg.refreshDisplay(); // Refresh the 7-segment display
}
// This function is for getting the current mode state from the mode selector potentiometer position
int getModeState() {
// Set the current value of the potentiometer to the modeSelectorValue variable
int modeSelectorValue = analogRead(modePotentiometer);
// If the value is less than one-third of the max potentiometer value, then return mode 0
// If the value is between one-third and two-thirds, then return mode 1
// If the value is greater than two-thirds, then return mode 2
if (modeSelectorValue <= oneThird) {
return 0;
} else if ((modeSelectorValue > oneThird) && (modeSelectorValue <= twoThirds)) {
return 1;
} else if (modeSelectorValue > twoThirds) {
return 2;
}
}
// This function is for getting the current time state from the time selector potentiometer position
int getTimeState() {
// Set the current value of the potentiometer to the timeSelectorValue variable
int timeSelectorValue = analogRead(timePotentiometer);
// If the value is less than one-third of the max potentiometer value, then return 10 seconds
// If the value is between one-third and two-thirds, then return 15 seconds
// If the value is greater than two-thirds, then return 20 seconds
if (timeSelectorValue <= oneThird) {
return 10;
} else if ((timeSelectorValue > oneThird) && (timeSelectorValue <= twoThirds)) {
return 15;
} else if (timeSelectorValue > twoThirds) {
return 20;
}
}
// This is the snooze() function
void snooze() {
// Get current time state, set the counter, and refresh the 7-segment display
timeState = getTimeState();
counter = millis();
sevseg.setNumber(timeState);
// Then, based on the current time state, set the time mark, seconds counter, and refresh display.
// The snooze button adds 10 seconds when pressed, no matter what time state you're in.
// So we must subtract different time based on the current time state to compensate the difference.
switch (timeState) {
case (10) :
lastOpenTime = millis();
seconds = getTimeState();
sevseg.setNumber(seconds);
break;
case (15) :
lastOpenTime = millis() - (5*timer);
seconds = getTimeState() - 5;
sevseg.setNumber(seconds);
break;
case (20) :
lastOpenTime = millis() - (10*timer);
seconds = getTimeState() - 10;
sevseg.setNumber(seconds);
break;
}
}
// This is the buzzerOn() function, where the magic happens.
// It does different things based on the mode state and the state of the snooze button.
void buzzerOn() {
// If the modeState is 0, turn off the buzzer
// If the modeState is 1, if the snooze button is pressed, then turn off the buzzer; if not, then turn on the buzzer
// If the modeState is 2, check if the time has passed the set time or not. If it is and the snooze button is pressed,
// then call the snooze() function, if the snooze button is not pressed, then display "al"
// on the 7-segment and turn on the buzzer.
// If the time has not passed the set time, turn off the buzzer, and if the snooze button is pressed,
// call the snooze() function; otherwise, display the countdown on the 7-segment display.
switch (modeState) {
case (0) :
noTone(buzzer);
break;
case (1) :
if (snoozeFlag) {
noTone(buzzer);
} else {
tone(buzzer, freq);
}
break;
case (2) :
currentMillis = millis();
if ((currentMillis - lastOpenTime) >= timer*timeState) {
if (snoozeFlag) {
snooze();
} else {
sevseg.setChars("al");
tone(buzzer, freq);
}
} else {
if (snoozeFlag) {
snooze();
} else {
if (currentMillis - counter >= 1000) {
counter += 1000;
seconds--;
sevseg.setNumber(seconds);
}
}
noTone(buzzer);
}
break;
}
}