#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <RTClib.h>
// Pin definitions
const int menuButtonPin = 2;
const int upButtonPin = 3;
const int downButtonPin = 4;
const int backButtonPin = 5;
const int setButtonPin = 6;
const int relayPin = 7;
// Create LCD and RTC objects
LiquidCrystal_I2C lcd(0x27, 20, 4);
RTC_DS3231 rtc;
struct AlarmEvent {
uint8_t hour;
uint8_t minute;
};
AlarmEvent alarms[10]; // Array to store 10 alarm events
int currentMenu = 0; // 0: Time, 1: Alarm Settings
int selectedAlarm = 0; // Index of selected alarm event
bool isSettingTime = true;
bool settingHour = true; // Toggle between hour and minute settings
// Variables to hold the last displayed information
String lastDisplay = "";
int lastAlarm = -1;
// Buzzer control variables
bool buzzerOn = false;
unsigned long buzzerStartTime = 0;
const unsigned long buzzerDuration = 5000; // Buzzer duration in milliseconds
// Add a new variable to store the alarm duration
unsigned long alarmDuration = 10000; // 10 seconds
unsigned long alarmStartTime = 0;
// Settings menu variables
int menuOption = 0; // Tracks the current selected menu option
int lastMenuOption = -1; // Tracks the last selected menu option to prevent unnecessary redraws
bool inMenu = false; // Tracks if we're in the menu screen
bool inSettings = false; // Tracks if we're in the settings screen
// Variables to track the last button states
bool lastUpButtonState = LOW;
bool lastDownButtonState = LOW;
bool lastMenuButtonState = LOW;
bool lastBackButtonState = LOW;
bool lastSetButtonState = LOW;
byte customChar[] = {
B10000,
B11000,
B11100,
B11110,
B11110,
B11100,
B11000,
B10000
};
void setup() {
pinMode(menuButtonPin, INPUT_PULLUP);
pinMode(upButtonPin, INPUT_PULLUP);
pinMode(downButtonPin, INPUT_PULLUP);
pinMode(backButtonPin, INPUT_PULLUP);
pinMode(setButtonPin, INPUT_PULLUP);
pinMode(relayPin, OUTPUT);
digitalWrite(relayPin, LOW);
lcd.init();
lcd.backlight();
if (!rtc.begin()) {
lcd.clear();
lcd.print("RTC not found!");
while (1);
}
// Load alarms from EEPROM
for (int i = 0; i < 10; i++) {
alarms[i].hour = EEPROM.read(i * 2);
alarms[i].minute = EEPROM.read(i * 2 + 1);
}
interface(); // Display the initial interface
}
void loop() {
bool menuButton = digitalRead(menuButtonPin);
bool upButton = digitalRead(upButtonPin);
bool downButton = digitalRead(downButtonPin);
bool backButton = digitalRead(backButtonPin);
bool setButton = digitalRead(setButtonPin);
// Handle the main menu button
if (menuButton == LOW && lastMenuButtonState == HIGH && !inMenu) {
inMenu = true;
displayMenu();
}
lastMenuButtonState = menuButton;
if (inMenu && upButton == LOW && lastUpButtonState == HIGH) {
menuOption = (menuOption > 0) ? menuOption - 1 : 3; // Move up in menu
displayMenuOptions();
}
lastUpButtonState = upButton;
if (inMenu && downButton == LOW && lastDownButtonState == HIGH) {
menuOption = (menuOption < 3) ? menuOption + 1 : 0; // Move down in menu
displayMenuOptions();
}
lastDownButtonState = downButton;
if (inMenu && setButton == LOW && lastSetButtonState == HIGH) {
inSettings = true;
executeOption(menuOption);
}
lastSetButtonState = setButton;
if (inMenu && backButton == LOW && lastBackButtonState == HIGH) {
inMenu = false;
inSettings = false;
interface(); // Go back to the main interface
}
lastBackButtonState = backButton;
// Display time and date
if (!inMenu) {
DateTime now = rtc.now();
// Title on the 1st line
lcd.setCursor(0, 0);
lcd.print(" PISD BELL SYSTEM");
// Date on the 2nd line
lcd.setCursor(0, 1);
lcd.print("Date: ");
lcd.print(formatDate(now));
// Time on the 3rd line
lcd.setCursor(0, 2);
lcd.print("Time: ");
lcd.print(format12Hour(now.hour()) + ":" + formatTime(now.minute()) + (now.hour() < 12 ? " AM" : " PM"));
// Note on the 4th line
lcd.setCursor(0, 3);
lcd.print("Press M for Settings");
}
}
void interface() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" PISD BELL SYSTEM");
lcd.setCursor(0, 1);
lcd.print("Date: ");
lcd.setCursor(0, 2);
lcd.print("Time: ");
lcd.setCursor(0, 3);
lcd.print("Press M for Settings");
}
void displayMenu() {
lcd.clear();
lastMenuOption = -1; // Force a redraw of the menu
displayMenuOptions();
}
void displayMenuOptions() {
if (menuOption != lastMenuOption) {
lcd.clear();
if (menuOption == 0) {
pointerOne();
} else if (menuOption == 1) {
pointerTwo();
} else if (menuOption == 2) {
pointerThree();
} else if (menuOption == 3) {
pointerFour();
}
lcd.setCursor(1, 0);
lcd.print("Set Date");
lcd.setCursor(1, 1);
lcd.print("Set Time");
lcd.setCursor(1, 2);
lcd.print("Adjust Bell");
lcd.setCursor(1, 3);
lcd.print("Reset Defaults");
lastMenuOption = menuOption; // Update lastMenuOption to current
}
}
void executeOption(int option) {
lcd.clear();
switch (option) {
case 0:
setDate();
break;
case 1:
setTime();
break;
case 2:
adjustBellTime();
break;
case 3:
resetDefaults();
break;
}
inSettings = false;
displayMenu();
}
void pointerOne() {
lcd.createChar(0, customChar);
lcd.setCursor(0, 0);
lcd.write(0);
}
void pointerTwo() {
lcd.createChar(0, customChar);
lcd.setCursor(0, 1);
lcd.write(0);
}
void pointerThree() {
lcd.createChar(0, customChar);
lcd.setCursor(0, 2);
lcd.write(0);
}
void pointerFour() {
lcd.createChar(0, customChar);
lcd.setCursor(0, 3);
lcd.write(0);
}
// Helper function to format time with leading zeros
String formatTime(int value) {
return (value < 10) ? "0" + String(value) : String(value);
}
// Helper function to convert 24-hour format to 12-hour format
String format12Hour(int hour) {
int hr = hour % 12;
return hr == 0 ? "12" : String(hr);
}
// Helper function to format date
String formatDate(DateTime now) {
return String(now.day()) + "/" + String(now.month()) + "/" + String(now.year());
}
void setDate() {
int day, month, year;
bool confirmed = false;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Date:");
// Input the day
day = getUserInput("Day:", rtc.now().day()); // Use current day as default
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Date:");
lcd.setCursor(0, 1);
lcd.print("Day: ");
lcd.print(day);
// Confirm day
confirmed = false;
while (!confirmed) {
lcd.setCursor(0, 2);
lcd.print("Press OK to Save");
if (digitalRead(setButtonPin) == LOW) {
confirmed = true;
delay(200); // Debounce delay
}
}
// Input the month
month = getUserInput("Month:", rtc.now().month()); // Use current month as default
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Date:");
lcd.setCursor(0, 1);
lcd.print("Month: ");
lcd.print(month);
// Confirm month
confirmed = false;
while (!confirmed) {
lcd.setCursor(0, 2);
lcd.print("Press OK to Save");
if (digitalRead(setButtonPin) == LOW) {
confirmed = true;
delay(200); // Debounce delay
}
}
// Input the year
year = getUserInput("Year:", rtc.now().year()); // Use current year as default
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Date:");
lcd.setCursor(0, 1);
lcd.print("Year: ");
lcd.print(year);
// Confirm year
confirmed = false;
while (!confirmed) {
lcd.setCursor(0, 2);
lcd.print("Press OK to Save");
if (digitalRead(setButtonPin) == LOW) {
confirmed = true;
delay(200); // Debounce delay
}
}
// Save the date
rtc.adjust(DateTime(year, month, day, rtc.now().hour(), rtc.now().minute(), 0));
lcd.clear();
lcd.print("Date set.");
delay(1000); // Pause to let the user read the message
}
void setTime() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Time:");
// Input hour and minute
int hour = getUserInput("Hour:", rtc.now().hour()); // Use current hour as default
int minute = getUserInput("Minute:", rtc.now().minute()); // Use current minute as default
rtc.adjust(DateTime(rtc.now().year(), rtc.now().month(), rtc.now().day(), hour, minute, 0));
lcd.clear();
lcd.print("Time set.");
delay(1000); // Pause to let the user read the message
}
void adjustBellTime() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Bell Time:");
// Input bell time in seconds
unsigned long bellTime = getUserInput("Seconds:", 10); // Provide a default duration
alarmDuration = bellTime * 1000; // Convert to milliseconds
lcd.clear();
lcd.print("Bell time set.");
delay(1000); // Pause to let the user read the message
}
void resetDefaults() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Resetting...");
// Reset alarms in EEPROM
for (int i = 0; i < 10; i++) {
EEPROM.write(i * 2, 0); // Default alarm hour 0
EEPROM.write(i * 2 + 1, 0); // Default alarm minute 0
}
// Reset bell duration
alarmDuration = 10000; // 10 seconds
lcd.clear();
lcd.print("Defaults reset.");
delay(1000); // Pause to let the user read the message
}
int getUserInput(String prompt, int defaultValue) {
int value = defaultValue;
bool inputAdjusted = false;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(prompt);
while (!inputAdjusted) {
lcd.setCursor(0, 1);
lcd.print("Value: ");
lcd.print(value);
if (digitalRead(upButtonPin) == LOW) {
value++;
delay(200); // Debounce delay
} else if (digitalRead(downButtonPin) == LOW) {
value--;
delay(200); // Debounce delay
}
// Ensure value stays within valid range for day, month, or year
if (prompt.startsWith("Day:")) {
if (value > 31) value = 1; // Wrap around for day
if (value < 1) value = 31; // Wrap around for day
} else if (prompt.startsWith("Month:")) {
if (value > 12) value = 1; // Wrap around for month
if (value < 1) value = 12; // Wrap around for month
} else if (prompt.startsWith("Year:")) {
if (value < 2000) value = 2000; // Minimum year
if (value > 2100) value = 2100; // Maximum year
}
if (digitalRead(setButtonPin) == LOW) {
inputAdjusted = true;
delay(200); // Debounce delay
}
}
return value;
}