#include <LiquidCrystal.h>
#include <EEPROM.h>
// --- Hardware Definitions ---
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// --- Constants ---
const int NUM_TIMERS = 4;
const int MASTER_CHANNEL = 0; // Channel 1 is the master (index 0)
const unsigned long UPDATE_INTERVAL = 200; // Interval
const unsigned long ONE_HOUR_SECONDS = 3600;
const unsigned long LONG_PRESS_TIME = 3000; // 3 seconds
const unsigned long ALARM_INCREMENT = 60; // 1 minute in seconds (TESTING)
const unsigned long INACTIVITY_RESET_TIME = 8000; // 8 seconds
// --- Button Pins ---
const int buttonPins[NUM_TIMERS] = {6, 7, 8, 9}; // Assign digital pins for timer buttons
const int ALARM_BUTTON = 10; // Setup alarm mode button pin
// --- EEPROM Addresses ---
const int EEPROM_START_ADDRESS = 0;
const int ALARM_TIME_SIZE = sizeof(unsigned long);
// --- Data Structures ---
struct TimerData {
unsigned long startTime;
unsigned long elapsedTime; // Time elapsed since start (in seconds)
bool isRunning;
bool longFormat; // Flag to switch between MM:SS and HH:MM
unsigned long alarmTime; // Alarm time in seconds
unsigned long lastAlarmUpdateTime; // Time when alarm was last updated
bool alarmTriggered; // Flag to prevent continuous triggering
};
TimerData timers[NUM_TIMERS];
bool blinkColon = false;
unsigned long lastSecond = 0; // Store the last second we updated blinkColon
bool alarmSetupMode = false; // Flag to indicate alarm setup mode
bool setupButtonHeld = false;
unsigned long lastActivityTime = 0; // Time of last button press
// --- Button Debouncing Variables ---
unsigned long lastDebounceTime[NUM_TIMERS + 1] = {0, 0, 0, 0, 0}; // Last time buttons were debounced
const int debounceDelay = 50; // Debounce interval in milliseconds
unsigned long lastAlarmButtonDebounceTime = 0;
// --- Long Press Variables ---
unsigned long buttonPressTime[NUM_TIMERS] = {0, 0, 0, 0};
bool buttonHeld[NUM_TIMERS] = {false, false, false, false};
// --- Last Displayed Values ---
unsigned long lastDisplayedTime[NUM_TIMERS]; // Store last displayed elapsed time
unsigned long lastDisplayedAlarmTime[NUM_TIMERS]; // Store last displayed alarm time
// --- Function Declarations ---
void updateTimers();
void displayTimers();
void printTime(int timerIndex, unsigned long totalSeconds);
void checkButtons();
void displayAlarmSetup(); // New function to display alarm setup interface
unsigned long readAlarmTimeFromEEPROM(int timerIndex);
void writeAlarmTimeToEEPROM(int timerIndex, unsigned long alarmTime);
void printAlarmTime(int timerIndex);
void incrementAlarmTime(int timerIndex);
void resetAlarmTime(int timerIndex);
void copyMasterAlarmToOthers();
void resetAllAlarmTimes();
void checkAlarms();
void setup() {
// --- Initialize LCD ---
lcd.begin(16, 2);
// Initialize Serial Monitor
Serial.begin(9600);
Serial.println("Program Started"); // Print a message to make serial monitor visible
// --- Initialize Timers ---
unsigned long currentTime = millis();
for (int i = 0; i < NUM_TIMERS; i++) {
timers[i].startTime = currentTime;
timers[i].elapsedTime = 0;
timers[i].isRunning = true; // Timers start running immediately
timers[i].longFormat = false;
timers[i].alarmTime = readAlarmTimeFromEEPROM(i); // Load alarm time from EEPROM
timers[i].lastAlarmUpdateTime = 0; // Initialize last alarm update time
timers[i].alarmTriggered = false; // Initialize alarm flag
// If EEPROM is empty, initialize alarmTime to a default value (e.g., 0)
if (timers[i].alarmTime == 0xFFFFFFFF) { // Check if EEPROM is unwritten
timers[i].alarmTime = 0;
writeAlarmTimeToEEPROM(i, timers[i].alarmTime); // Write the default value to EEPROM
}
// --- Set button pins as input and activate pull-up resistor ---
pinMode(buttonPins[i], INPUT_PULLUP);
buttonPressTime[i] = 0;
buttonHeld[i] = false;
lastDisplayedTime[i] = 0xFFFFFFFF; // Initialize with a value that will force an update
lastDisplayedAlarmTime[i] = 0xFFFFFFFF; // Initialize similarly for alarm times
}
pinMode(ALARM_BUTTON, INPUT_PULLUP);
lastSecond = millis() / 1000; // Initialize lastSecond
}
void loop() {
checkButtons();
updateTimers();
checkAlarms(); // Check for alarms
if (!alarmSetupMode) {
displayTimers();
} else {
displayAlarmSetup();
}
delay(UPDATE_INTERVAL);
}
void updateTimers() {
unsigned long currentTime = millis();
unsigned long currentSecond = currentTime / 1000;
if (currentSecond != lastSecond) {
blinkColon = !blinkColon;
lastSecond = currentSecond;
for (int i = 0; i < NUM_TIMERS; i++) {
if (timers[i].isRunning) {
timers[i].elapsedTime = (currentTime - timers[i].startTime) / 1000;
if (timers[i].elapsedTime >= ONE_HOUR_SECONDS && !timers[i].longFormat) {
timers[i].longFormat = true;
}
if (timers[i].elapsedTime >= (99 * ONE_HOUR_SECONDS + (59 * 60))) { // 99 hours * 3600 seconds + 59 minutes * 60 seconds
timers[i].isRunning = false;
timers[i].elapsedTime = (99 * ONE_HOUR_SECONDS + (59 * 60));
}
}
}
}
}
void displayTimers() {
for (int i = 0; i < NUM_TIMERS; i++) {
int row = i / 2;
int col = (i % 2) * 8;
lcd.setCursor(col, row);
lcd.print(i + 1);
lcd.print("-");
printTime(i, timers[i].elapsedTime);
}
}
void printTime(int timerIndex, unsigned long totalSeconds) {
int hours = totalSeconds / ONE_HOUR_SECONDS;
int minutes = (totalSeconds % ONE_HOUR_SECONDS) / 60;
int seconds = totalSeconds % 60;
lcd.setCursor((timerIndex % 2) * 8 + 2, timerIndex / 2); //Update the cursor position
if (timers[timerIndex].longFormat) {
if (hours < 10) lcd.print("0");
lcd.print(hours);
lcd.print(blinkColon ? ":" : " ");
if (minutes < 10) lcd.print("0");
lcd.print(minutes);
} else {
if (minutes < 10) lcd.print("0");
lcd.print(minutes);
lcd.print(blinkColon ? ":" : " ");
if (seconds < 10) lcd.print("0");
lcd.print(seconds);
}
}
void checkButtons() {
// --- Check ALARM_BUTTON ---
int alarmButtonState = digitalRead(ALARM_BUTTON);
unsigned long currentTime = millis();
// Debounce the ALARM_BUTTON
if (alarmButtonState == LOW) {
if (millis() - lastAlarmButtonDebounceTime > debounceDelay) {
if (!setupButtonHeld) {
alarmSetupMode =
alarmSetupMode = true; // Enter alarm setup mode
lastAlarmButtonDebounceTime = millis();
setupButtonHeld = true;
lastActivityTime = currentTime; // Initialize last activity time
}
}
} else {
if (millis() - lastAlarmButtonDebounceTime > debounceDelay) {
if (setupButtonHeld) {
alarmSetupMode = false; // Exit alarm setup mode
lastAlarmButtonDebounceTime = millis();
setupButtonHeld = false;
// Save alarm times to EEPROM
for (int i = 0; i < NUM_TIMERS; i++) {
writeAlarmTimeToEEPROM(i, timers[i].alarmTime);
}
}
}
}
// Check for inactivity reset
if (alarmSetupMode && (currentTime - lastActivityTime > INACTIVITY_RESET_TIME)) {
resetAllAlarmTimes();
lastActivityTime = currentTime + 100000; // Prevent immediate re-trigger
}
for (int i = 0; i < NUM_TIMERS; i++) {
int buttonState = digitalRead(buttonPins[i]);
if (alarmSetupMode && buttonState == LOW) {
if (millis() - lastDebounceTime[i] > debounceDelay) {
lastDebounceTime[i] = millis();
if (!buttonHeld[i]) {
buttonHeld[i] = true;
buttonPressTime[i] = millis();
incrementAlarmTime(i); // Increment on initial press
if (i == MASTER_CHANNEL) {
copyMasterAlarmToOthers();
}
lastActivityTime = currentTime; // Update last activity time
timers[i].alarmTriggered = false; // reset the alarm here.
}
}
if ((millis() - buttonPressTime[i]) >= LONG_PRESS_TIME) {
resetAlarmTime(i);
if (i == MASTER_CHANNEL) {
copyMasterAlarmToOthers();
}
buttonPressTime[i] = millis() + 100000;
lastActivityTime = currentTime;
timers[i].alarmTriggered = false; // reset the alarm here.
}
} else {
if (buttonState == LOW) {
if (millis() - lastDebounceTime[i] > debounceDelay) {
lastDebounceTime[i] = millis();
if (!buttonHeld[i]) {
buttonHeld[i] = true;
buttonPressTime[i] = millis();
}
}
if ((millis() - buttonPressTime[i]) >= LONG_PRESS_TIME) { // Long press detected
timers[i].elapsedTime = 0;
timers[i].startTime = millis();
displayTimers();
timers[i].alarmTriggered = false;
}
} else {
if (buttonHeld[i]) {
buttonHeld[i] = false;
}
}
}
}
}
void displayAlarmSetup() {
lcd.setCursor(0, 0);
lcd.print("A>");
printAlarmTime(0);
lcd.print(" A>");
printAlarmTime(1);
lcd.setCursor(0, 1);
lcd.print("A>");
printAlarmTime(2);
lcd.print(" A>");
printAlarmTime(3);
}
void printAlarmTime(int timerIndex) {
unsigned long alarmTime = timers[timerIndex].alarmTime;
int alarmHours = (alarmTime / 3600) % 100;
int alarmMinutes = (alarmTime % 3600) / 60;
lcd.setCursor((timerIndex % 2) * 8 + 2, timerIndex / 2); //Update the cursor position
if (alarmHours < 10) {
lcd.print("0");
}
lcd.print(alarmHours);
lcd.print(":");
if (alarmMinutes < 10) {
lcd.print("0");
}
lcd.print(alarmMinutes);
}
unsigned long readAlarmTimeFromEEPROM(int timerIndex) {
int address = EEPROM_START_ADDRESS + (timerIndex * ALARM_TIME_SIZE);
unsigned long alarmTime;
EEPROM.get(address, alarmTime);
return alarmTime;
}
void writeAlarmTimeToEEPROM(int timerIndex, unsigned long alarmTime) {
int address = EEPROM_START_ADDRESS + (timerIndex * ALARM_TIME_SIZE);
EEPROM.put(address, alarmTime);
}
void incrementAlarmTime(int timerIndex) {
timers[timerIndex].alarmTime += ALARM_INCREMENT;
if (timers[timerIndex].alarmTime >= 360000) { // 100 hours
timers[timerIndex].alarmTime = 0; // Reset to 0
}
timers[timerIndex].lastAlarmUpdateTime = millis(); // Update last alarm update time
timers[timerIndex].alarmTriggered = false; // Reset alarm flag
displayAlarmSetup(); // Update display immediately
}
void resetAlarmTime(int timerIndex) {
timers[timerIndex].alarmTime = 0;
timers[timerIndex].lastAlarmUpdateTime = millis(); // Update last alarm update time
timers[timerIndex].alarmTriggered = false; // Reset alarm flag
displayAlarmSetup();
}
void copyMasterAlarmToOthers() {
unsigned long masterAlarmTime = timers[MASTER_CHANNEL].alarmTime;
for (int i = 0; i < NUM_TIMERS; i++) {
if (i != MASTER_CHANNEL) {
timers[i].alarmTime = masterAlarmTime;
}
}
displayAlarmSetup(); // Update display immediately
}
void resetAllAlarmTimes() {
for (int i = 0; i < NUM_TIMERS; i++) {
timers[i].alarmTime = 0;
}
displayAlarmSetup();
}
void checkAlarms() {
unsigned long currentTime = millis();
for (int i = 0; i < NUM_TIMERS; i++) {
unsigned long elapsedTime = (currentTime - timers[i].startTime) / 1000;
if (timers[i].isRunning && timers[i].alarmTime > 0 && elapsedTime >= timers[i].alarmTime && !timers[i].alarmTriggered) {
Serial.print("Alarm Triggered for Timer: ");
Serial.println(i + 1);
timers[i].alarmTriggered = true; // Set alarm flag
}
}
}