#include <RTClib.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
RTC_DS1307 rtc; // Creates an RTC object
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int buttonTouchPin = 3;
const int buttonOkPin = 4; // Button to enter "OK"
const int buttonPlusPin = 5;
const int buttonMinusPin = 6;
const int buttonBackPin = 7;
const int ledPin = 9;
bool menu_state = false;
bool clock_state = true;
bool change_alarm_state = false;
bool alarm_menu_state = false;
bool alarm_state = true;
bool change_time_state = false;
bool clock_menu_state = false;
String main_options[2] = {"time", "alarm"};
int main_options_max = 1;
String alarm_options[2] = {"change", "toggle"};
int alarm_options_max = 1;
String clock_options[2] = {"display", "change"};
int clock_options_max = 1;
int hover_index = 0;
DateTime alarmTime = DateTime(2023, 1, 1, 0, 0, 0);
unsigned long previousMillis = 0;
bool displayTime = true;
int touchPresses = 0;
int maxPresses = 10;
bool alarm_light = false;
bool alarm_on = false;
const unsigned long backlightTimeout = 30000; // 30 seconds timeout
unsigned long lastInputTime = 0;
bool backlightOn = true;
void setup() {
rtc.begin();
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
pinMode(buttonOkPin, INPUT_PULLUP);
pinMode(buttonPlusPin, INPUT_PULLUP);
pinMode(buttonMinusPin, INPUT_PULLUP);
pinMode(buttonBackPin, INPUT_PULLUP);
pinMode(buttonTouchPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
// Attempt to read the alarm time from EEPROM
if (EEPROM.read(0) != 255) {
// If the EEPROM value is not 255 (uninitialized), read it
EEPROM.get(0, alarmTime);
}
}
void loop() {
unsigned long currentMillis = millis();
// Check if it's time to turn off the backlight
if (currentMillis - lastInputTime >= backlightTimeout && backlightOn) {
lcd.noBacklight(); // Turn off the backlight
backlightOn = false;
}
if (currentMillis - lastInputTime < backlightTimeout && !backlightOn) {
lcd.backlight(); // Turn on the backlight
backlightOn = true;
}
if (clock_state) {
clock();
if (digitalRead(buttonOkPin) == LOW) {
selectMainMenu();
}
}
if (clock_menu_state) {
clock_menu();
}
if (menu_state) {
main_menu();
}
if (alarm_menu_state) {
alarm_menu();
}
if (change_alarm_state) {
change_alarm();
}
if (alarm_state) {
alarm();
}
if (digitalRead(buttonOkPin) == LOW || digitalRead(buttonPlusPin) == LOW || digitalRead(buttonMinusPin) == LOW || digitalRead(buttonBackPin) == LOW) {
lastInputTime = currentMillis; // Update the last input time
}
if (digitalRead(buttonBackPin) == LOW) {
if (change_alarm_state) {
selectAlarm();
}
if (change_time_state) {
displayTime = true;
change_time_state = false;
}
}
if (digitalRead(buttonTouchPin) == LOW) {
int brightness = 0;
if (alarm_on) {
alarm_light = false;
}
else {
if (touchPresses >= maxPresses) {
touchPresses = 0;
}
else {
touchPresses += 1;
}
int range = 255;
brightness = (range * touchPresses) / maxPresses;
}
analogWrite(ledPin, brightness);
delay(100);
}
// Adds a delay to avoid multiple rapid button presses
delay(100);
}
void displayMenu(String menu_options[], int options_max, String menu_identifier) {
// Displays menu
lcd.setCursor(0, 0);
String option_1 = menu_options[hover_index];
option_1.toUpperCase();
lcd.print(option_1);
lcd.setCursor(0, 1);
if (hover_index + 1 > options_max) {
lcd.print(menu_options[0]);
}
else {
lcd.print(menu_options[hover_index + 1]);
}
if (digitalRead(buttonMinusPin) == LOW) {
hover_index += 1;
if (hover_index > options_max) {
hover_index = 0;
}
lcd.clear();
}
else if (digitalRead(buttonPlusPin) == LOW) {
hover_index -= 1;
if (hover_index < 0) {
hover_index = options_max;
}
lcd.clear();
}
if (digitalRead(buttonOkPin) == LOW) {
// Perform the action associated with the selected option
selectOption(hover_index, options_max, menu_identifier);
lcd.clear();
hover_index = 0;
}
if (digitalRead(buttonBackPin) == LOW) {
if (menu_state) {
selectTime();
}
if (alarm_menu_state || clock_menu_state) {
selectMainMenu();
}
lcd.clear();
}
}
// Define function pointers for menu options
void (*menu_optionActions[])(void) = {selectTime, selectAlarm};
void selectTime() {
// Perform actions for "time" option
menu_state = false;
clock_menu_state = true;
delay(100);
}
void selectAlarm() {
// Perform actions for "alarm" option
menu_state = false;
change_alarm_state = false;
alarm_menu_state = true;
delay(100);
}
void selectMainMenu() {
hover_index = 0;
lcd.clear();
menu_state = true;
clock_state = false;
clock_menu_state = false;
alarm_menu_state = false;
delay(100);
}
void (*alarm_optionActions[])(void) = {change, toggle};
void toggle() {
// Toggle the alarm
alarm_toggle();
}
void change() {
// Change alarm time
alarm_menu_state = false;
change_alarm_state = true;
}
void (*clock_optionActions[])(void) = {display, change_time};
void display() {
clock_menu_state = false;
clock_state = true;
}
void change_time() {
clock_menu_state = false;
change_time_state = true;
clock_state = true;
}
void selectOption(int selectedOption, int option_max, String menu_type) {
if (selectedOption >= 0 && selectedOption <= option_max) {
// Call the corresponding function based on the selected option
if (menu_type == "main") {
menu_optionActions[selectedOption]();
}
if (menu_type == "alarm")
alarm_optionActions[selectedOption]();
if (menu_type == "clock") {
clock_optionActions[selectedOption]();
}
}
}
void clock_menu() {
change_time_state = false;
displayMenu(clock_options, clock_options_max, "clock");
}
void clock() {
unsigned long currentMillis = millis(); // Get the current time
int interval = 500;
if (change_time_state){
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis; // Save the last time the display was updated
displayTime = !displayTime; // Toggle the display flag
}
}
// Reads the current time from the RTC module
DateTime now = rtc.now();
if (displayTime) {
// Checks if the hour button is pressed
if (change_time_state) {
if (digitalRead(buttonPlusPin) == LOW) {
// Increment hours
now = now + TimeSpan(0, 1, 0, 0); // Increments by one hour
}
// Checks if the minute button is pressed
if (digitalRead(buttonMinusPin) == LOW) {
// Increment minutes
now = now + TimeSpan(0, 0, 1, 0); // Increments by one minute
}
}
// Sets the updated time back to the RTC module
rtc.adjust(now);
// Display the time
lcd.setCursor(0, 0);
if (now.hour() < 10) {
lcd.print('0'); // Adds leading zero for single-digit hours
}
lcd.print(now.hour(), DEC);
lcd.print(':');
if (now.minute() < 10) {
lcd.print('0'); // Adds leading zero for single-digit minutes
}
lcd.print(now.minute(), DEC);
}
else {
lcd.clear();
}
}
void alarm_menu() {
// Displays menu
displayMenu(alarm_options, alarm_options_max, "alarm");
}
void change_alarm() {
// Displays alarm settings
if (digitalRead(buttonPlusPin) == LOW) {
// Increment hours
alarmTime = alarmTime + TimeSpan(0, 1, 0, 0); // Increments by one hour
}
// Checks if the minute button is pressed
if (digitalRead(buttonMinusPin) == LOW) {
// Increment minutes
alarmTime = alarmTime + TimeSpan(0, 0, 1, 0); // Increments by one minute
}
// Update and store the alarm time in EEPROM
EEPROM.put(0, alarmTime);
// Displays the time
lcd.setCursor(0, 0);
if (alarmTime.hour() < 10) {
lcd.print('0'); // Adds leading zero for single-digit minutes
}
lcd.print(alarmTime.hour(), DEC);
lcd.print(':');
if (alarmTime.minute() < 10) {
lcd.print('0'); // Adds leading zero for single-digit minutes
}
lcd.print(alarmTime.minute(), DEC);
}
void alarm_toggle() {
lcd.clear();
lcd.setCursor(0, 0);
if (alarm_state) {
lcd.print("Alarm off");
alarm_state = false;
}
else {
lcd.print("Alarm on");
alarm_state = true;
}
delay(400);
lcd.clear();
}
void alarm() {
// Activates alarm
DateTime time = rtc.now();
int alarm_hour = alarm_value_check(alarmTime.hour());
int time_hour = alarm_value_check(time.hour());
int time_delta = (alarm_hour * 60 + alarmTime.minute()) - (time_hour * 60 + time.minute());
int range = 255; // 0 - 255 for LED birghtness
int alarm_length = 30; // 30 minute diff until light should turn on
int brightness = (range * (alarm_length - time_delta)) / alarm_length;
if (time_delta > 0 && time_delta <= alarm_length) {
if (alarm_light) {
analogWrite(ledPin, brightness);
alarm_on = true;
}
}
else {
alarm_light = true;
alarm_on = false;
}
}
int alarm_value_check(int to_check) {
if (to_check == 0) {
to_check = 24;
}
return to_check;
}
void main_menu() {
// Displays menu
displayMenu(main_options, main_options_max, "main");
}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
lcd1:GND
lcd1:VCC
lcd1:SDA
lcd1:SCL
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r
btn2:1.l
btn2:2.l
btn2:1.r
btn2:2.r
rtc1:GND
rtc1:5V
rtc1:SDA
rtc1:SCL
rtc1:SQW
btn3:1.l
btn3:2.l
btn3:1.r
btn3:2.r
led1:A
led1:C
btn6:1.l
btn6:2.l
btn6:1.r
btn6:2.r
btn4:1.l
btn4:2.l
btn4:1.r
btn4:2.r