#include <LiquidCrystal.h>
enum Modes {
Normal,
SetClock,
SetAlarm
};
Modes currentMode = Normal;
struct ClockTime {
int hours, minutes, seconds;
};
struct ClockTime currentTime;
struct CursorPos {
int x, y;
};
CursorPos cursorPos;
enum DisplayLines {
DisplayFirstLine,
DisplaySecondLine
};
enum ClockPos {
TenHoursXPos,
OneHourXPos,
SeparatorXPos,
TenMinutesXPos,
OneMinuteXPos
};
struct Alarm {
struct ClockTime time;
bool status;
};
Alarm alarm;
struct DPad {
bool up, right, down, left;
};
const int rs = 12, en = 11, d4 = 10, d5 = 9, d6 = 8, d7 = 7;
LiquidCrystal lcdDisplay(rs, en, d4, d5, d6, d7);
const int GreenSetButtonPin = 2;
const int GrayUpButtonPin = 3;
const int GrayRightButtonPin = 6;
const int GrayDownButtonPin = 5;
const int GrayLeftButtonPin = 4;
const int BuzzerPin = A0;
const long updateInterval = 1000; // o secunda
unsigned long previousMillis = 0;
String displayMode(Modes mode) {
switch (mode) {
case Normal:
return "Normal";
case SetClock:
return "SeClock";
case SetAlarm:
return "SeAlarm";
default:
return "";
}
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(BuzzerPin, OUTPUT);
pinMode(GreenSetButtonPin, INPUT_PULLUP);
pinMode(GrayUpButtonPin, INPUT_PULLUP);
pinMode(GrayRightButtonPin, INPUT_PULLUP);
pinMode(GrayDownButtonPin, INPUT_PULLUP);
pinMode(GrayLeftButtonPin, INPUT_PULLUP);
lcdDisplay.begin(16, 2);
lcdDisplay.print("00:00:00 " + displayMode(currentMode));
cursorPos.x = 4;
cursorPos.y = 0;
}
void nextMode(Modes * mode) {
switch (*mode) {
case Normal:
*mode = SetClock;
return;
case SetClock:
*mode = SetAlarm;
return;
case SetAlarm:
*mode = Normal;
return;
default:
*mode = Normal;
}
}
String intToDigits(int value) {
if (value < 10) {
return String("0" + String(value));
}
return String(value);
}
void incrementTime(struct ClockTime * time) {
time->seconds++;
if (time->seconds > 59) {
time->seconds = 0;
time->minutes++;
}
if (time->minutes > 59) {
time->minutes = 0;
time->hours++;
}
if (time->hours > 23) {
time->hours = 0;
}
}
String displayBool(bool status) {
if (status) {
return "On";
}
return "Off";
}
String displayAlarmTime(Alarm alarm) {
return String(
intToDigits(alarm.time.hours) + ":" + intToDigits(alarm.time.minutes)
);
}
void displayNormalTime(struct ClockTime time, Modes currentMode) {
lcdDisplay.clear();
lcdDisplay.noCursor();
lcdDisplay.print(
String(intToDigits(time.hours) + ":" + intToDigits(time.minutes) + ":" + intToDigits(time.seconds) + " " + displayMode(currentMode))
);
lcdDisplay.setCursor(0,1);
lcdDisplay.print(
displayAlarmTime(alarm)
);
lcdDisplay.setCursor(9, 1);
lcdDisplay.print(
displayBool(alarm.status)
);
}
void moveCursorLeft(struct CursorPos * cursorPos) {
if (cursorPos->y == DisplaySecondLine && cursorPos->x == 9) {
cursorPos->y = DisplayFirstLine;
cursorPos->x = OneMinuteXPos;
}
if (cursorPos->y > DisplayFirstLine) {
cursorPos->y = DisplayFirstLine;
}
cursorPos->x--;
if (cursorPos->x == SeparatorXPos) {
cursorPos->x--;
}
bool outOfBounds = cursorPos->x < TenHoursXPos || cursorPos->x > OneMinuteXPos;
if (outOfBounds && currentMode == SetAlarm) {
cursorPos->x = 9;
cursorPos->y = DisplaySecondLine;
} else if (outOfBounds) {
cursorPos->x = OneMinuteXPos;
}
}
void moveCursorRight(struct CursorPos * cursorPos) {
if (cursorPos->y == DisplaySecondLine && cursorPos->x == 9) {
cursorPos->y = DisplayFirstLine;
cursorPos->x = TenHoursXPos;
}
if (cursorPos->y > DisplayFirstLine) {
cursorPos->y = DisplayFirstLine;
}
cursorPos->x++;
if (cursorPos->x == SeparatorXPos) {
cursorPos->x++;
}
bool outOfBounds = cursorPos->x < TenHoursXPos || cursorPos->x > OneMinuteXPos;
if (outOfBounds && currentMode == SetAlarm) {
cursorPos->x = 9;
cursorPos->y = DisplaySecondLine;
} else if (outOfBounds) {
cursorPos->x = TenHoursXPos;
}
}
int getValueByCursorPos(struct CursorPos cursorPos) {
switch (cursorPos.x) {
case TenHoursXPos:
case TenMinutesXPos:
return 10;
case OneHourXPos:
case OneMinuteXPos:
return 1;
default:
return 0;
}
}
void incrementValueUnderCursor(struct ClockTime * time, struct CursorPos cursorPos) {
switch (cursorPos.x) {
case TenHoursXPos:
case OneHourXPos:
time->hours += getValueByCursorPos(cursorPos);
break;
case TenMinutesXPos:
case OneMinuteXPos:
time->minutes += getValueByCursorPos(cursorPos);
break;
default:
break;
}
if (time->minutes > 59) {
time->minutes -= 60;
time->hours++;
}
if (time->hours > 23) {
time->hours -= 24;
}
}
void decrementValueUnderCursor(struct ClockTime * time, struct CursorPos cursorPos) {
switch (cursorPos.x) {
case TenHoursXPos:
case OneHourXPos:
time->hours -= getValueByCursorPos(cursorPos);
break;
case TenMinutesXPos:
case OneMinuteXPos:
time->minutes -= getValueByCursorPos(cursorPos);
break;
default:
break;
}
if (time->minutes < 0) {
time->minutes += 60;
time->hours--;
}
if (time->hours < 0) {
time->hours += 24;
}
}
void displaySetClockScreen(struct ClockTime time, struct CursorPos cursorPos) {
lcdDisplay.clear();
lcdDisplay.print(
intToDigits(time.hours) + ":" + intToDigits(time.minutes)
);
lcdDisplay.setCursor(9, 0);
lcdDisplay.print("SeClock");
lcdDisplay.setCursor(cursorPos.x, cursorPos.y);
lcdDisplay.cursor();
}
bool isAlarmOn(struct Alarm alarm) {
return alarm.status;
}
bool isAlarmDue(struct ClockTime time, struct Alarm alarm) {
return
time.hours == alarm.time.hours
&&
time.minutes == alarm.time.minutes;
}
void handleNormalMode(bool hasBeenUpdated) {
if (! hasBeenUpdated) return;
displayNormalTime(currentTime, currentMode);
}
bool selectActionBasedOnButtonPressed(struct ClockTime * time, struct DPad buttons) {
bool buttonPressed = false;
if (buttons.left) {
moveCursorLeft(&cursorPos);
buttonPressed = true;
}
if (buttons.right) {
moveCursorRight(&cursorPos);
buttonPressed = true;
}
if (buttons.up && currentMode == SetAlarm && cursorPos.x == 9 && cursorPos.y == 1) {
alarm.status = true;
buttonPressed = true;
}
else if (buttons.up) {
incrementValueUnderCursor(time, cursorPos);
buttonPressed = true;
}
if (buttons.down && currentMode == SetAlarm && cursorPos.x == 9 && cursorPos.y == 1) {
alarm.status = false;
buttonPressed = true;
}
else if (buttons.down) {
decrementValueUnderCursor(time, cursorPos);
buttonPressed = true;
}
return buttonPressed;
}
void handleSetClockMode(bool hasBeenUpdated, struct DPad buttons) {
bool buttonPressed = selectActionBasedOnButtonPressed(¤tTime, buttons);
if (hasBeenUpdated || buttonPressed) {
displaySetClockScreen(currentTime, cursorPos);
}
}
void displaySetAlarmScreen(struct Alarm alarm, struct CursorPos cursorPos) {
lcdDisplay.clear();
lcdDisplay.print(
intToDigits(alarm.time.hours) + ":" + intToDigits(alarm.time.minutes)
);
lcdDisplay.setCursor(9, 0);
lcdDisplay.print("SeAlarm");
lcdDisplay.setCursor(9, 1);
lcdDisplay.print(displayBool(alarm.status));
lcdDisplay.setCursor(cursorPos.x, cursorPos.y);
lcdDisplay.cursor();
}
void handleSetAlarmMode(bool hasBeenUpdated, struct DPad buttons) {
bool buttonPressed = selectActionBasedOnButtonPressed(&alarm.time, buttons);
if (hasBeenUpdated || buttonPressed) {
displaySetAlarmScreen(alarm, cursorPos);
}
}
void loop() {
bool clockHasBeenUpdated = false;
bool ringTheAlarm = false;
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= updateInterval) {
previousMillis = currentMillis;
incrementTime(¤tTime);
clockHasBeenUpdated = true;
}
if (clockHasBeenUpdated) {
ringTheAlarm = isAlarmOn(alarm) && isAlarmDue(currentTime, alarm);
}
if (ringTheAlarm) {
tone(BuzzerPin, 262, 3000);
digitalWrite(LED_BUILTIN, HIGH);
} else {
digitalWrite(LED_BUILTIN, LOW);
}
int GreenSetButtonState = digitalRead(GreenSetButtonPin);
if (GreenSetButtonState == LOW) {
nextMode(¤tMode);
displayNormalTime(currentTime, currentMode);
}
struct DPad buttons;
buttons.up = digitalRead(GrayUpButtonPin) == LOW;
buttons.right = digitalRead(GrayRightButtonPin) == LOW;
buttons.down = digitalRead(GrayDownButtonPin) == LOW;
buttons.left = digitalRead(GrayLeftButtonPin) == LOW;
switch (currentMode) {
case Normal:
handleNormalMode(clockHasBeenUpdated);
break;
case SetClock:
handleSetClockMode(clockHasBeenUpdated, buttons);
break;
case SetAlarm:
handleSetAlarmMode(clockHasBeenUpdated, buttons);
break;
}
}