/*
MIT License
Copyright (c) 2023
Author: R McCleery [email protected]
Date: May 3rd 2023
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <Wire.h> //I2C communication library
#include <LiquidCrystal_I2C.h> //LCD display library
#define display_columns 16
#define display_rows 2
#define display_add 0x27
/****** Comment out for NO Serial Print **********************************/
#define PRINT
/************ LED and Button PIN ****************************************/
#define LED LED_BUILTIN
#define BUTTON 2
//Non-blocking timers for button de-bounce, LED Blink and our clock
#define DUTY_CYCLE 50
#define ONE_SEC 20 // 20 x 50mS = 1000mS or 1 second
#define SET_RATE 10 // 5 x 50mS = 250mS or two blinks a second in set mode
#define DAYS 7
LiquidCrystal_I2C lcd(display_add, display_columns, display_rows);
#define START 0
#define CURRENT 1
#define ENTER 2
#define SUCCESS 3
//Variables
uint32_t previousDuty = 0;
uint8_t blinkCount = 0, clockCnt = 0, heartBeat = ONE_SEC, labelCheck, d=0, h=0, m=0, s=0;
bool buttonState, validTime, validDay, buttonPress = false, a=0,
lastButtonState = 1, ledState, clock = 1, setTime = 0;
String dayName[] = {"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"};
String label[] = {" Starting RTC ", " Current Time ", " Enter New Time ", "New Time Success"};
String use = "";
struct STRUCT1{ //Time values are held in this "rtc." structure
uint8_t day = 0;
uint8_t hour = 11;
uint8_t min = 59;
uint8_t sec = 55;
bool mer = 1;
}rtc;
//To test that you entered the time or alarm correctly
bool inRange(int val, int minimum, int maximum){
return ((minimum <= val) && (val <= maximum));}
void myClock(uint8_t d, uint8_t h,uint8_t m, uint8_t s, bool MM){
labelCheck++;
String wday = dayName[d];
String time_str = (wday+" "+(h<10?"0":"")+String(h)+":"+(m<10?"0":"")+String(m)+":"+(s<10?"0":"")+String(s)+String(MM ?" PM":" AM")+" ");
if(labelCheck > 3){
labelCheck = 0;
if(use != label[CURRENT]){
use = label[CURRENT];
lcd.setCursor(0,0);
lcd.print(use);
}
}
lcd.setCursor(0, 1); // Second Row
lcd.print(time_str); // "Day h:mm:ss MM"
#ifdef PRINT
Serial.println(time_str);
#endif
digitalWrite(LED, LOW);
return;
}
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
pinMode(BUTTON, INPUT_PULLUP);
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
use = label[START];
lcd.print(use);
labelCheck = 0;
}
void loop() {
//Watching for a button press
buttonState = digitalRead(BUTTON);
if(buttonState != lastButtonState){
buttonPress = true;
previousDuty = millis();
}
if(!clock){
if(Serial.available() > 0) {
d = Serial.parseInt();
h = Serial.parseInt();
m = Serial.parseInt();
s = Serial.parseInt();
a = Serial.parseInt();
#ifdef PRINT
char buf_t[28];
sprintf(buf_t,"I have understood %u %u:%u:%u %u\n\n", d, h, m, s, a);
Serial.println(buf_t);
#endif
validDay = (inRange(d, 0, 6) && inRange(a, 0, 1));
validTime = (inRange(h, 1, 12) && inRange(m, 0, 59) && inRange(s, 0, 59));
}
if(validDay && validTime){
heartBeat = ONE_SEC;
lcd.setCursor(0, 0);
use = label[SUCCESS];
lcd.print(use);
rtc.day = d;
rtc.hour = h;
rtc.min = m;
rtc.sec = s;
rtc.mer = a;
clock = true;
labelCheck = 0;
Serial.flush();
}
}
//Non-Blocking button de-bounce and led blink timer function
if(millis() - previousDuty > DUTY_CYCLE){
blinkCount++; clockCnt++;
if(buttonPress){
if(buttonState == LOW){
heartBeat = SET_RATE;
blinkCount = 0;
clock = false;
use = label[ENTER];
lcd.clear();
lcd.setCursor(0,0);
lcd.print(use);
#ifdef PRINT
Serial.println("\n\n******\t Clock Adjust Mode **********");
Serial.println("******\t!! MUST use correct format to continue !! **********");
Serial.println("\n******\t Weekday (d) Sun = 0, Sat = 6 **********");
Serial.println("******\t No Leading ZERO's 0:0:0 **********");
Serial.println("******\t (M) 0 = AM or 1 = PM **********");
Serial.println("******\t Type in: d hh:mm:ss M **********\n");
#endif
}
buttonPress = false;
}
if(clockCnt >= ONE_SEC){
if(clock){
//Once your clock has been and the main loop starts
//this clock loop runs
digitalWrite(LED, HIGH);
clockCnt = 0;
rtc.sec++;
if(rtc.sec > 59){rtc.sec = 0; rtc.min++;}
if(rtc.min > 59){rtc.min = 0; rtc.hour++;}
if(rtc.hour == 13 && rtc.min == 0 && rtc.sec == 0){ //For a 12hr Display
rtc.hour = 1;
}else if(rtc.hour == 12 && rtc.min == 0 && rtc.sec == 0){ //change AM ? PM
if(rtc.mer == 1){
rtc.mer = 0; //Change to AM
rtc.day++; //Day rolls over
} else {
rtc.mer = 1; //Change to PM
}
}
rtc.day %= 7; //Sun = 0 -> Sat = 6. Rolls back to 0 at Midnight Saturday
myClock(rtc.day, rtc.hour, rtc.min, rtc.sec, rtc.mer);
}
}
if(heartBeat == SET_RATE){
if(blinkCount >= heartBeat){
blinkCount = 0;
digitalWrite(LED, (ledState = !ledState));
}
}
previousDuty = millis();
} // END millis() statement
lastButtonState = buttonState; // Update Last button State
}