//Copyright (C) 22.09.2023, Kirill ZHivotkov
#include <IRremote.h>
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"
#include <Wire.h>
//Declare display class variable
LiquidCrystal_I2C lcd(39, 16, 2); //Address = 39
//Declare display class variable
//Declare real-time clock class variable
RTC_DS1307 rtc;
//Declare real-time clock class variable
//Alarm clock set icon
byte alarm_icon[8] = { B00000, B10001, B01110, B10001, B10001, B01110, B11111, B00000 };
//Alarm clock set icon
//Alarm clock melody icon
byte melody_icon[8] = { B00000, B00100, B00110, B00101, B00100, B11100, B11100, B00000 };
//Alarm clock melody icon
//Implement class "Cursor"
class Cursor {
private:
//Private class members
byte cursorPos;
byte lcdRow;
byte lcdColumn;
bool cursorIsInTimeSetMode;
bool cursorIsInAlarmSetMode;
float cursorBlinkingTime;
//Private class members
public:
//Setters
void setCursorPos(byte cursorPos) { //Set cursor position on the LCD display
this->cursorPos = cursorPos;
}
void setCursorBlinkingTime(float cursorBlinkingTime) { //Set cursor blinking time
this->cursorBlinkingTime = cursorBlinkingTime;
}
void setCursorTimeSetMode(bool cursorIsInTimeSetMode) { //Set time set status (cursor is blinking or not)
this->cursorIsInTimeSetMode = cursorIsInTimeSetMode;
}
void setCursorAlarmSetMode(bool cursorIsInAlarmSetMode) { //Set time set status (cursor is blinking or not)
this->cursorIsInAlarmSetMode = cursorIsInAlarmSetMode;
}
//Setters
//Getters
byte getCursorPos() { //Get cursor position on the LCD display
return this->cursorPos;
}
float getCursorBlinkingTime() { //Get time of the blinking for the cursor
return this->cursorBlinkingTime;
}
bool getIsSetModeValue() { //Get time set status (cursor is blinking or not)
return this->cursorIsInTimeSetMode;
}
bool getIsSetAlarmModeValue() { //Get alarm set status (cursor is blinking or not)
return this->cursorIsInAlarmSetMode;
}
//Getters
//Prototypes for class methods
void setLCDCursorPos(byte, byte);
void cursorStopBlink();
void cursorStartBlink();
void switchTimeSetEditMode(unsigned long);
void switchAlarmSetEditMode(unsigned long);
void showAlarmModeMessage(String);
void setCursorMaxBlinkingTime(unsigned long);
//Prototypes for class methods
//Default constructor
Cursor() {
this->setCursorPos(0);
this->lcdRow = 0;
this->lcdColumn = 0;
this->setCursorTimeSetMode(0);
this->setCursorAlarmSetMode(0);
this->setCursorBlinkingTime(0.0);
}
//Default constructor
//Destructor
~Cursor() {}
//Destructor
};
//Implement class "Cursor"
//Set LCD display cursor position
void Cursor::setLCDCursorPos(byte lcdColumn, byte lcdRow) {
if ((lcdColumn >= 0 && lcdColumn <= 15) && (lcdRow == 0 || lcdRow == 1)) {
this->lcdColumn = lcdColumn;
this->lcdRow = lcdRow;
lcd.setCursor(this->lcdColumn, this->lcdRow);
}
}
//Set LCD display cursor position
//Switch cursor blinking off
void Cursor::cursorStopBlink() {
lcd.noBlink();
}
//Switch cursor blinking off
//Switch cursor blinking on
void Cursor::cursorStartBlink() {
lcd.blink();
}
//Switch cursor blinking on
//Describe time setting mode (for cursor)
void Cursor::switchTimeSetEditMode(unsigned long key) {
if (key == 1336999680 && this->getIsSetModeValue() == 0) {
lcd.setCursor(this->getCursorPos(), 0);
this->cursorStartBlink();
this->setCursorTimeSetMode(1);
} else if (key == 1470693120 && this->getIsSetModeValue() == 1) {
lcd.setCursor(0, 0);
this->cursorStopBlink();
this->setCursorTimeSetMode(0);
}
}
//Describe time setting mode (for cursor)
//Output an error message (time or date were set incorrectly)
void Cursor::showAlarmModeMessage(String msg) {
this->cursorStopBlink();
this->setLCDCursorPos(0, 1);
lcd.print(msg);
this->setLCDCursorPos(this->getCursorPos(), 0);
this->cursorStartBlink();
}
//Output an error message (time or date were set incorrectly)
//Describe alarm setting mode (for cursor)
void Cursor::switchAlarmSetEditMode(unsigned long key) {
if (this->getIsSetModeValue() == 1) {
if (key == 1738080000 && this->getIsSetAlarmModeValue() == 0) {
this->setCursorAlarmSetMode(1);
} else if (key == 4244832000 && this->getIsSetAlarmModeValue() == 1) {
this->showAlarmModeMessage(" ");
this->showAlarmModeMessage("Alarm is set");
}
}
}
//Describe alarm setting mode (for cursor)
//Set cursor maximum blinking
void Cursor::setCursorMaxBlinkingTime(unsigned long limit) {
if (((millis() - this->getCursorBlinkingTime()) >= limit) && (this->getIsSetModeValue() == 1)) { //Max blinking = 2 minutes
this->cursorStopBlink();
this->setCursorTimeSetMode(0);
this->setLCDCursorPos(this->getCursorPos(), 0);
}
}
//Set cursor maximum blinking
//Create an instance of the class described above
Cursor* cur = new Cursor();
//Create an instance of the class described above
//Implement class "Display"
class LCD {
private:
//Private class members
byte digit;
//Time (clock)
byte h;
byte hh;
byte m;
byte mm;
//Time
//Time (alarm)
byte h_a;
byte hh_a;
byte m_a;
byte mm_a;
//Time
//Date (clock)
byte d;
byte dd;
byte mth;
byte mmth;
byte y;
byte yy;
byte yyy;
byte yyyy;
//Date (clock)
//Date (alarm)
byte d_a;
byte dd_a;
byte mth_a;
byte mmth_a;
byte y_a;
byte yy_a;
byte yyy_a;
byte yyyy_a;
//Date (alarm)
//Weekday
byte weekDay;
//Weekday
bool displayIsOff;
bool isInAlarmMode;
bool alarmRingIsStopped;
//Private class members
public:
//Setters
void setIsInAlarmMode(bool isInAlarmMode) { //Set alarm status
this->isInAlarmMode = isInAlarmMode;
}
void setDisplayIsOff(bool displayIsOff) { //Set LCD status (if display is on or off)
this->displayIsOff = displayIsOff;
}
void setAlarmRingIsStopped(bool alarmRingIsStopped) { //Set alarm ringing status
this->alarmRingIsStopped = alarmRingIsStopped;
}
//Setters
//Getters
bool getIsInAlarmMode() { //Get alarm status
return this->isInAlarmMode;
}
bool getDisplayIsOff() { //Get LCD status (if display is on or off)
return this->displayIsOff;
}
bool getAlarmRingIsStopped() { //Get alarm ringing status
return this->alarmRingIsStopped;
}
//Getters
//Prototypes for class methods
bool checkHours(byte);
bool checkMinutes(byte);
bool checkDays(byte);
bool checkMonths(byte);
bool checkYears(int);
bool checkLeapYear(int);
byte getMinuteAlarm();
byte getHourAlarm();
byte getDayAlarm();
byte getMonthAlarm();
int getYearAlarm();
byte getMinute();
byte getHour();
byte getDay();
byte getWeekDay();
byte getMonth();
int getYear();
bool checkDisplayData();
bool checkAlarmValidity();
void showAlarmMessage(String);
void copyClockValuesToAlarm(unsigned long);
void showAlarmIcon(byte*);
void setClockTimeMode(unsigned long);
void setClockDigit(int);
void setAdjustedClockTime();
void setDisplayInfo(int);
void setDisplayInfo(String);
void displayProcessedDate();
void displayProcessedWeekDay();
void displayProcessedTime();
void switchDisplayOn();
void switchDisplayOff();
void switchBackLightOn();
void switchBackLightOff();
void clearDisplay();
void switchDisplayOnOff(unsigned long);
void printAllDataOnDisplay();
//Prototypes for class methods
//Default constructor
LCD() {
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
this->digit = 0;
//Time (clock)
this->h = 0;
this->hh = 0;
this->m = 0;
this->mm = 0;
//Time
//Time (alarm)
this->h_a = 0;
this->hh_a = 0;
this->m_a = 0;
this->mm_a = 0;
//Time
//Date (clock)
this->d = 0;
this->dd = 1;
this->mth = 0;
this->mmth = 1;
this->y = 2;
this->yy = 0;
this->yyy = 0;
this->yyyy = 0;
//Date (clock)
//Date (alarm)
this->d_a = 0;
this->dd_a = 1;
this->mth_a = 0;
this->mmth_a = 1;
this->y_a = 2;
this->yy_a = 0;
this->yyy_a = 3;
this->yyyy_a = 0;
//Date (alarm)
//Weekday
this->weekDay = -1;
//Weekday
this->setDisplayIsOff(0);
this->setIsInAlarmMode(0);
this->setAlarmRingIsStopped(1);
//pinMode(9, OUTPUT); //if LCD backlight jumper is removed (for Arduino Uno)
//analogWrite(9, 10);
}
//Default constructor
//Destructor
~LCD() {}
//Destructor
};
//Implement class "Display"
//Check hours range
bool LCD::checkHours(byte h) {
if (h >= 0 && h <= 23) {
return true;
}
return false;
}
//Check hours range
//Check minutes range
bool LCD::checkMinutes(byte m) {
if (m >= 0 && m <= 59) {
return true;
}
return false;
}
//Check minutes range
//Check days range
bool LCD::checkDays(byte d) {
int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (checkLeapYear(this->getYear()) == true && days[1] == 28) {
days[1]++;
}
if (d >= 1 && d <= days[this->getMonth() - 1]) {
return true;
}
return false;
}
//Check days range
//Check months range
bool LCD::checkMonths(byte m) {
if (m >= 1 && m <= 12) {
return true;
}
return false;
}
//Check months range
//Check years range
bool LCD::checkYears(int y) {
if (y >= 2000 && y <= 2030) {
return true;
}
return false;
}
//Check years range
//Check a leap year
bool LCD::checkLeapYear(int y) {
if (y % 400 == 0) {
return true;
} else if (y % 100 == 0) {
return false;
} else if (y % 4 == 0) {
return true;
} else {
return false;
}
}
//Check a leap year
//Get a minute (clock)
byte LCD::getMinute() {
return this->m * 10 + this->mm;
}
//Get a minute (clock)
//Get a minute (alarm)
byte LCD::getMinuteAlarm() {
return this->m_a * 10 + this->mm_a;
}
//Get a minute (alarm)
//Get an hour (clock)
byte LCD::getHour() {
return this->h * 10 + this->hh;
}
//Get an hour (clock)
//Get an hour (alarm)
byte LCD::getHourAlarm() {
return this->h_a * 10 + this->hh_a;
}
//Get an hour (alarm)
//Get a day (clock)
byte LCD::getDay() {
return this->d * 10 + this->dd;
}
//Get a day (clock)
//Get a day (alarm)
byte LCD::getDayAlarm() {
return this->d_a * 10 + this->dd_a;
}
//Get a day (alarm)
//Get a month (clock)
byte LCD::getMonth() {
return this->mth * 10 + this->mmth;
}
//Get a month (clock)
//Get a month (alarm)
byte LCD::getMonthAlarm() {
return this->mth_a * 10 + this->mmth_a;
}
//Get a month (alarm)
//Get a year (clock)
int LCD::getYear() {
return this->y * 1000 + this->yy * 100 + this->yyy * 10 + this->yyyy;
}
//Get a year (clock)
//Get a year (alarm)
int LCD::getYearAlarm() {
return this->y_a * 1000 + this->yy_a * 100 + this->yyy_a * 10 + this->yyyy_a;
}
//Get a year (alarm)
//Get a weekday
byte LCD::getWeekDay() {
return this->weekDay;
}
//Get a weekday
//Check input data format
bool LCD::checkDisplayData() {
if (this->checkHours(this->getHour()) == true &&
this->checkMinutes(this->getMinute()) == true &&
this->checkDays(this->getDay()) == true &&
this->checkMonths(this->getMonth()) == true &&
this->checkYears(this->getYear()) == true) {
return true;
}
return false;
}
//Check input data format
//Check alarm validity
bool LCD::checkAlarmValidity() {
if ((this->getHour() != this->getHourAlarm()) ||
(this->getMinute() != this->getMinuteAlarm()) ||
(this->getDay() != this->getDayAlarm()) ||
(this->getMonth() != this->getMonthAlarm()) ||
(this->getYear() != this->getYearAlarm())) {
return true;
}
return false;
}
//Check alarm validity
//Output an alarm error
void LCD::showAlarmMessage(String msg) {
cur->cursorStopBlink();
cur->setLCDCursorPos(0, 1);
this->setDisplayInfo(msg);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
cur->cursorStartBlink();
}
//Output an alarm error
//Copy clock values to alarm set time to enter its mode
void LCD::copyClockValuesToAlarm(unsigned long key) {
if (key == 1738080000 && cur->getIsSetAlarmModeValue() == 1) {
//Time (alarm)
this->h_a = this->getHour() / 10;
this->hh_a = this->getHour() % 10;
this->m_a = this->getMinute() / 10;
this->mm_a = this->getMinute() % 10;
//Time (alarm)
//Date (alarm)
this->d_a = this->getDay() / 10;
this->dd_a = this->getDay() % 10;
this->mth_a = this->getMonth() / 10;
this->mmth_a = this->getMonth() % 10;
this->y_a = this->getYear() / 1000;
this->yy_a = this->getYear() / 100 % 10;
this->yyy_a = this->getYear() / 10 % 10;
this->yyyy_a = this->getYear() % 10;
//Date (alarm)
//Alarm mode enter message
cur->showAlarmModeMessage("Alarm mode on");
//Alarm mode enter message
}
}
//Copy clock values to alarm set time to enter its mode
//Show an alarm icon
void LCD::showAlarmIcon(byte* icon) {
lcd.createChar(0, icon);
lcd.setCursor(15, 1);
lcd.print(char(0));
}
//Show an alarm icon
//Set time with remote control
void LCD::setClockTimeMode(unsigned long key) {
if (cur->getIsSetModeValue() == 1) {
if (key == 534839040) { //Move cursor to the left
if (cur->getCursorPos() > 0) {
if (cur->getCursorPos() == 3) { //Time
cur->setCursorPos(1);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
} else if (cur->getCursorPos() == 6) { //Date
cur->setCursorPos(4);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
} else if (cur->getCursorPos() == 9) { //Date
cur->setCursorPos(7);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
} else if (cur->getCursorPos() == 12) { //Date
cur->setCursorPos(10);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
} else {
cur->setCursorPos(cur->getCursorPos() - 1);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
}
}
} else if (key == 1871773440) { //Move cursor to the right
if (cur->getCursorPos() < 16) {
if (cur->getCursorPos() == 1) { //Time
cur->setCursorPos(3);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
} else if (cur->getCursorPos() == 4) { //Date
cur->setCursorPos(6);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
} else if (cur->getCursorPos() == 7) { //Date
cur->setCursorPos(9);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
} else if (cur->getCursorPos() == 10) { //Date
cur->setCursorPos(12);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
} else {
cur->setCursorPos(cur->getCursorPos() + 1);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
}
}
} else {
if (cur->getCursorPos() >= 0 && cur->getCursorPos() <= 15) {
if (key == 2540240640) { //Key 0
this->setClockDigit(0);
} else if (key == 3476094720) { //Key 1
this->setClockDigit(1);
} else if (key == 3877175040) { //Key 2
this->setClockDigit(2);
} else if (key == 2239430400) { //Key 3
this->setClockDigit(3);
} else if (key == 4010868480) { //Key 4
this->setClockDigit(4);
} else if (key == 3342401280) { //Key 5
this->setClockDigit(5);
} else if (key == 2774204160) { //Key 6
this->setClockDigit(6);
} else if (key == 3175284480) { //Key 7
this->setClockDigit(7);
} else if (key == 3041591040) { //Key 8
this->setClockDigit(8);
} else if (key == 2907897600) { //Key 9
this->setClockDigit(9);
}
}
}
}
}
//Set time with remote control
//Input and output LCD dispay digits from 1 to 9
void LCD::setClockDigit(int digit) {
this->digit = digit;
if (this->digit >= 0 && this->digit <= 9) {
if (cur->getCursorPos() == 0) { //Hours
if (cur->getIsSetAlarmModeValue() == 0) {
this->h = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->h);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->h_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->h_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 1) { //Hours
if (cur->getIsSetAlarmModeValue() == 0) {
this->hh = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->hh);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->hh_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->hh_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 3) { //Minutes
if (cur->getIsSetAlarmModeValue() == 0) {
this->m = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->m);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->m_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->m_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 4) { //Minutes
if (cur->getIsSetAlarmModeValue() == 0) {
this->mm = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->mm);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->mm_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->mm_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 6) { //Days
if (cur->getIsSetAlarmModeValue() == 0) {
this->d = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->d);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->d_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->d_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 7) { //Days
if (cur->getIsSetAlarmModeValue() == 0) {
this->dd = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->dd);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->dd_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->dd_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 9) { //Months
if (cur->getIsSetAlarmModeValue() == 0) {
this->mth = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->mth);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->mth_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->mth_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 10) { //Months
if (cur->getIsSetAlarmModeValue() == 0) {
this->mmth = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->mmth);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->mmth_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->mmth_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 12) { //Years
if (cur->getIsSetAlarmModeValue() == 0) {
this->y = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->y);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->y_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->y_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 13) { //Years
if (cur->getIsSetAlarmModeValue() == 0) {
this->yy = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->yy);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->yy_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->yy_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 14) { //Years
if (cur->getIsSetAlarmModeValue() == 0) {
this->yyy = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->yyy);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->yyy_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->yyy_a);
cur->cursorStartBlink();
}
} else if (cur->getCursorPos() == 15) { //Years
if (cur->getIsSetAlarmModeValue() == 0) {
this->yyyy = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->yyyy);
cur->cursorStartBlink();
} else if (cur->getIsSetAlarmModeValue() == 1) {
this->yyyy_a = this->digit;
cur->cursorStopBlink();
this->setDisplayInfo(this->yyyy_a);
cur->cursorStartBlink();
}
}
if (this->checkDisplayData() == true) { //Move through the first display row to the right after a digit was confirmed to be set
if (cur->getCursorPos() != 1 &&
cur->getCursorPos() != 4 &&
cur->getCursorPos() != 7 &&
cur->getCursorPos() != 10) {
if (cur->getCursorPos() >= 0 && cur->getCursorPos() < 15) {
cur->setCursorPos(cur->getCursorPos() + 1);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
}
} else {
cur->setCursorPos(cur->getCursorPos() + 1);
cur->setCursorPos(cur->getCursorPos() + 1);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
}
}
}
}
//Input and output LCD dispay digits from 1 to 9
//Update a clock time
void LCD::setAdjustedClockTime() {
if (this->getDisplayIsOff() == 0) { //If LCD display is on
if (this->checkAlarmValidity() == true || this->getAlarmRingIsStopped() == 1) {
DateTime now = rtc.now();
if (now.hour() < 10) {
this->h = 0;
this->hh = now.hour();
Serial.print("h >>> ");
Serial.print(this->h);
Serial.print(" hh >>> ");
Serial.print(this->hh);
} else {
this->h = now.hour() / 10;
this->hh = now.hour() % 10;
Serial.print("h >>> ");
Serial.print(this->h);
Serial.print(" hh >>> ");
Serial.print(this->hh);
}
if (now.minute() < 10) {
this->m = 0;
this->mm = now.minute();
Serial.print(" m >>> ");
Serial.print(this->m);
Serial.print(" mm >>> ");
Serial.print(this->mm);
} else {
this->m = now.minute() / 10;
this->mm = now.minute() % 10;
Serial.print(" m >>> ");
Serial.print(this->m);
Serial.print(" mm >>> ");
Serial.print(this->mm);
}
Serial.print(" ");
Serial.print(now.second());
Serial.println();
if (now.day() >= 0 && now.day() < 10) {
this->d = 0;
this->dd = now.day();
} else {
if (now.day() >= 1 && now.day() <= 31) {
this->d = now.day() / 10;
this->dd = now.day() % 10;
}
}
if (now.month() >= 0 && now.month() < 10) {
this->mth = 0;
this->mmth = now.month();
} else {
if (now.month() >= 10 && now.month() <= 12) {
this->mth = now.month() / 10;
this->mmth = now.month() % 10;
}
}
if (now.year() >= 2000 && now.year() <= 2030) {
this->y = now.year() / 1000;
this->yy = now.year() / 100 % 10;
this->yyy = now.year() / 10 % 10;
this->yyyy = now.year() % 10;
}
//Clear display if a weekday has changed
if (this->weekDay != -1 && this->weekDay != now.dayOfTheWeek()) {
this->clearDisplay();
}
//Clear display if a weekday has changed
this->weekDay = now.dayOfTheWeek();
this->displayProcessedTime();
this->displayProcessedDate();
this->displayProcessedWeekDay();
Serial.print("Alarm h => ");
Serial.println(this->getHourAlarm());
Serial.print("Alarm m => ");
Serial.println(this->getMinuteAlarm());
Serial.print("Alarm d => ");
Serial.println(this->getDayAlarm());
Serial.print("Alarm m => ");
Serial.println(this->getMonthAlarm());
Serial.print("Alarm y => ");
Serial.println(this->getYearAlarm());
delay(1000);
} else { //A clock alarm has rung
this->displayProcessedTime();
this->displayProcessedDate();
this->displayProcessedWeekDay();
}
}
}
//Update a clock time
//Output info to the display (integer data type)
void LCD::setDisplayInfo(int data) {
lcd.print((String)data);
}
//Output info to the display (integer data type)
//Output info to the display (string data type)
void LCD::setDisplayInfo(String data) {
lcd.print(data);
}
//Output info to the display (string data type)
//Output calculated date
void LCD::displayProcessedDate() {
cur->setLCDCursorPos(6, 0);
//Day
if (this->d * 10 + this->dd < 10) {
this->setDisplayInfo("0" + (String)(this->d * 10 + this->dd));
} else {
this->setDisplayInfo((String)(this->d * 10 + this->dd));
}
//Day
this->setDisplayInfo("-");
//Month
if (this->mth * 10 + this->mmth < 10) {
this->setDisplayInfo("0" + (String)(this->mth * 10 + this->mmth));
} else {
this->setDisplayInfo((String)(this->mth * 10 + this->mmth));
}
//Month
this->setDisplayInfo("-");
//Year
int tmp_year = this->y * 1000 + this->yy * 100 + this->yyy * 10 + this->yyyy;
if (tmp_year >= 2000 && tmp_year <= 2030) {
this->setDisplayInfo((String)(tmp_year));
} else {
this->setDisplayInfo("2000");
}
//Year
}
//Output calculated date
//Output calculated weekday
void LCD::displayProcessedWeekDay() {
String week[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
if (this->getIsInAlarmMode() == 0) {
cur->setLCDCursorPos(0, 1);
this->setDisplayInfo(week[this->weekDay]);
} else if (this->getIsInAlarmMode() == 1 && (cur->getIsSetAlarmModeValue() == 1)) {
cur->setLCDCursorPos(0, 1);
this->setDisplayInfo(week[this->weekDay]);
this->showAlarmIcon(alarm_icon);
}
}
//Output calculated weekday
//Output calculated time
void LCD::displayProcessedTime() {
cur->setLCDCursorPos(0, 0);
//Hour
if (this->h * 10 + this->hh < 10) {
this->setDisplayInfo("0" + (String)(this->h * 10 + this->hh));
} else {
this->setDisplayInfo((String)(this->h * 10 + this->hh));
}
//Hour
this->setDisplayInfo(":");
//Minute
if (this->m * 10 + this->mm < 10) {
this->setDisplayInfo("0" + (String)(this->m * 10 + this->mm));
} else {
this->setDisplayInfo((String)(this->m * 10 + this->mm));
}
//Minute
}
//Output calculated time
//Switch LCD display on
void LCD::switchDisplayOn() {
lcd.display();
}
//Switch LCD display on
//Switch LCD backlight on
void LCD::switchBackLightOn() {
lcd.backlight();
}
//Switch LCD backlight on
//Switch LCD display off
void LCD::switchDisplayOff() {
lcd.noDisplay();
}
//Switch LCD display off
//Switch LCD backlight off
void LCD::switchBackLightOff() {
lcd.noBacklight();
}
//Switch LCD backlight off
//Clear the whole display
void LCD::clearDisplay() {
cur->cursorStopBlink();
cur->setCursorTimeSetMode(0);
lcd.clear();
}
//Clear the whole display
//Compound function to switch LCD display on/off
void LCD::switchDisplayOnOff(unsigned long a) {
if (this->getDisplayIsOff() == 0) {
this->switchBackLightOff();
this->setDisplayIsOff(1);
} else if (this->getDisplayIsOff() == 1) {
this->switchBackLightOn();
this->setDisplayIsOff(0);
}
}
//Compound function to switch LCD display on/off
//Print All data on display
void LCD::printAllDataOnDisplay() {
//Output time that was set with RTC
if (this->checkDisplayData() == true) { //If clock data is correct
if (cur->getIsSetModeValue() == 0) { //If clock is not in edit mode
if (this->checkAlarmValidity() == false && this->getIsInAlarmMode() == 1) {
this->showAlarmIcon(melody_icon);
this->setAlarmRingIsStopped(0);
Serial.println("Ringing..."); //tone
} else {
this->setAdjustedClockTime();
}
}
}
//Output time that was set with RTC
}
//Print All data on display
//Create an instance of the class described above
LCD* disp;
//Create an instance of the class described above
//Setup
void setup() {
disp = new LCD();
disp->clearDisplay();
Wire.begin();
rtc.begin();
rtc.adjust(DateTime(2000, 1, 1, 0, 0, 0));
pinMode(A0, INPUT);
IrReceiver.begin(A0, ENABLE_LED_FEEDBACK);
Serial.begin(9600);
}
//Setup
//Implement class "Sound"
class Sound {
private:
//Private class members
String msg;
//Private class members
public:
//Prototypes for class methods
void setErrorMessage(String);
void showErrorMessage();
void playErrorSound();
//Prototypes for class methods
//Default constructor
Sound() {
this->msg = "";
}
//Default constructor
//Destructor
~Sound() {}
//Destructor
};
//Implement class "Sound"
//Set an error message restriction
void Sound::setErrorMessage(String msg) {
if (msg.length() == 16) {
this->msg = msg;
}
}
//Set an error message restriction
//Output an error message (time or date were set incorrectly)
void Sound::showErrorMessage() {
cur->cursorStopBlink();
cur->setLCDCursorPos(0, 1);
disp->setDisplayInfo(this->msg);
cur->setLCDCursorPos(cur->getCursorPos(), 0);
cur->cursorStartBlink();
}
//Output an error message (time or date were set incorrectly)
//Make an error sound if the clock input data were incorrect
void Sound::playErrorSound() {
if (disp->checkDisplayData() == false) {
this->setErrorMessage("Incorrect format");
this->showErrorMessage();
} else {
this->setErrorMessage(" ");
this->showErrorMessage();
}
}
//Make an error sound if the clock input data were incorrect
//Create an instance of the class described above
Sound* snd = new Sound();
//Create an instance of the class described above
//Implement class "Remote Control"
class RemoteControl {
private:
//Private class members
unsigned long key;
//Private class members
public:
//Setters
void setRemoteControlKey(unsigned long key) { //Set remote control key
this->key = key;
}
//Setters
//Getters
unsigned long getRemoteControlKey() { //Get remote control key
return this->key;
}
//Getters
//Prototypes for class methods
void inputDigitalClockCommand();
//Prototypes for class methods
//Default contructor
RemoteControl() {
this->setRemoteControlKey(0);
}
//Default contructor
//Destructor
~RemoteControl() {}
//Destructor
};
//Implement class "Remote Control"
//Input a clock command
void RemoteControl::inputDigitalClockCommand() {
if (IrReceiver.decode()) {
this->setRemoteControlKey(IrReceiver.decodedIRData.decodedRawData); //Receive a value from the remote control
IrReceiver.resume();
disp->setClockTimeMode(this->getRemoteControlKey()); //Set a clock time and date
cur->switchTimeSetEditMode(this->getRemoteControlKey()); //Switch between edit mode and running time (cursor)
cur->switchAlarmSetEditMode(this->getRemoteControlKey()); //Set alarm time and date
if (this->getRemoteControlKey() == 1336999680) { //EDIT_CLOCK_ON > Set edit mode on
//Set current time for the blinking cursor
cur->setCursorBlinkingTime(millis());
//Set current time for the blinking cursor
//Exit from alarm set mode
disp->setIsInAlarmMode(0);
disp->setAlarmRingIsStopped(1);
cur->setCursorAlarmSetMode(0);
//Exit from alarm set mode
}
if (this->getRemoteControlKey() == 1470693120) { //EDIT_CLOCK_OFF > Set new RTC values if some new time settings' values were confirmed (a user entered new time and date)
disp->clearDisplay();
rtc.adjust((DateTime(disp->getYear(), disp->getMonth(), disp->getDay(), disp->getHour(), disp->getMinute(), 0)));
}
if (this->getRemoteControlKey() == 1738080000) { //EDIT_ALARM_ON > Copy clock values to alarm
if (cur->getIsSetModeValue() == 1) {
disp->copyClockValuesToAlarm(this->getRemoteControlKey());
cur->setCursorAlarmSetMode(1);
}
}
if (this->getRemoteControlKey() == 4244832000) { //EDIT_ALARM_OFF > Set alarm RTC values
if ((cur->getIsSetModeValue() == 1) && (cur->getIsSetAlarmModeValue() == 1)) {
if (disp->checkAlarmValidity() == true) {
disp->setIsInAlarmMode(1);
} else {
disp->showAlarmMessage(" ");
disp->showAlarmMessage("Alarm error");
}
}
}
if (this->getRemoteControlKey() == 1570963200) { //Switch LCD on/off
disp->switchDisplayOnOff(this->getRemoteControlKey());
}
if (this->getRemoteControlKey() == 2540240640 || //0 Emit a sound if some incorrect time settings were applied (wrong time or date formats)
this->getRemoteControlKey() == 3476094720 || //1
this->getRemoteControlKey() == 3877175040 || //2
this->getRemoteControlKey() == 2239430400 || //3
this->getRemoteControlKey() == 4010868480 || //4
this->getRemoteControlKey() == 3342401280 || //5
this->getRemoteControlKey() == 2774204160 || //6
this->getRemoteControlKey() == 3175284480 || //7
this->getRemoteControlKey() == 3041591040 || //8
this->getRemoteControlKey() == 2907897600) { //9
if (cur->getIsSetModeValue() == 1) {
snd->playErrorSound();
}
if (cur->getIsSetAlarmModeValue() == 1) {
cur->showAlarmModeMessage(" ");
cur->showAlarmModeMessage("Alarm mode on");
}
}
}
}
//Input a clock command
//Create an instance of the class described above
RemoteControl* rc = new RemoteControl();
//Create an instance of the class described above
//Program loop (RTC ticking clock)
void loop() {
cur->setCursorMaxBlinkingTime(120000); //Set a maximum time for the cursor to blink (in milliseconds)
rc->inputDigitalClockCommand(); //Input a digital clock command
disp->printAllDataOnDisplay(); //Display all data on the screen (running clock, set alarm, input time/alarm error)
}
//Program loop (RTC ticking clock)