/*
Arduino RTC Clock
3/1/26
Encoder library:
https://github.com/mathertel/RotaryEncoder/tree/master
TO DO: Add code to set time / date via rotary encoder
*/
#include <LiquidCrystal.h>
#include <RotaryEncoder.h>
#include <RTClib.h>
typedef enum { STATE_INIT,
STATE_DISPLAY,
STATE_DATE_FMT,
STATE_TIME_FMT,
STATE_SET_YEAR,
STATE_SET_MONTH,
STATE_SET_DATE,
STATE_SET_HOUR,
STATE_SET_MINUTE,
STATE_SET_SECOND
} states;
// set initial state
states state = STATE_INIT;
states oldState = state;
states nextState = STATE_DISPLAY;
const int RS = 12, EN = 11, D4 = 10, D5 = 9, D6 = 8, D7 = 7;
const int CK_PIN = 3;
const int DT_PIN = 2;
const int SW_PIN = 4;
const char daysOfTheWeek[7][4] = {
"Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat"
};
const unsigned long ONE_SEC = 1000;
bool isMDY = true;
bool is12Hour = true;
int oldSwState = HIGH; // INPUT_PULLUP
int pos = 0;
int dir = 0;
unsigned long prevTime = 0;
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);
RotaryEncoder *encoder = nullptr;
RTC_DS1307 rtc;
void checkPosition() {
encoder->tick(); // just call tick() to check the state.
}
void checkSwitch() {
int currSwState = digitalRead(SW_PIN); // read the button
if (oldSwState != currSwState) { // if it changed
oldSwState = currSwState; // save the state
if (currSwState == LOW) { // if LOW was just pressed
Serial.println("The button was just pressed");
lcd.clear();
state = nextState;
} else { // if HIGH was just released
//Serial.println("The button was just released");
}
delay(20); // debounce the button
}
}
void setDateFormat() {
//Serial.println("Date format");
lcd.setCursor(2, 0);
lcd.print("Date format?");
if (dir != 0) {
isMDY = !isMDY;
}
lcd.setCursor(3, 1);
lcd.print(isMDY ? "MM/DD/YYYY" : "DD/MM/YYYY");
}
void setTimeFormat() {
//Serial.println("Time format");
lcd.setCursor(2, 0);
lcd.print("Time format?");
if (dir != 0) {
is12Hour = !is12Hour;
}
lcd.setCursor(6, 1);
lcd.print(is12Hour ? "12 H" : "24 H");
}
void setYear() {
DateTime now = rtc.now();
//Serial.println("Year");
lcd.setCursor(4, 0);
lcd.print("Set year");
lcd.setCursor(6, 1);
lcd.print(now.year());
}
void setMonth() {
Serial.println("Month");
}
void setDate() {
Serial.println("Date");
}
void setHour() {
Serial.println("Hour");
}
void setMinute() {
Serial.println("Minute");
}
void setSecond() {
Serial.println("Second");
}
void setState() {
//bool isDone = false;
if (state != oldState) {
oldState = state;
Serial.print("State: ");
Serial.println(state);
}
switch (state) {
case STATE_INIT:
showSplash();
state = nextState;
break;
case STATE_DISPLAY:
showClock();
nextState = STATE_DATE_FMT;
break;
case STATE_DATE_FMT:
setDateFormat();
nextState = STATE_TIME_FMT;
break;
case STATE_TIME_FMT:
setTimeFormat();
nextState = STATE_SET_YEAR;
break;
case STATE_SET_YEAR:
setYear();
nextState = STATE_SET_MONTH;
break;
case STATE_SET_MONTH:
setMonth();
nextState = STATE_SET_DATE;
break;
case STATE_SET_DATE:
setDate();
nextState = STATE_SET_HOUR;
break;
case STATE_SET_HOUR:
setHour();
nextState = STATE_SET_MINUTE;
break;
case STATE_SET_MINUTE:
setMinute();
nextState = STATE_SET_SECOND;
break;
case STATE_SET_SECOND:
setSecond();
nextState = STATE_DISPLAY;
break;
default:
break;
}
}
void showClock() {
char dateBuffer[16];
char timeBuffer[16];
if (millis() - prevTime >= ONE_SEC) {
prevTime = millis();
DateTime now = rtc.now();
if (is12Hour) {
snprintf(timeBuffer, 16, "%2d:%02d:%02d %s",
now.twelveHour(),
now.minute(),
now.second(),
now.isPM() ? "PM" : "AM"
);
lcd.setCursor(2, 0);
} else {
snprintf(timeBuffer, 16, "%2d:%02d:%02d",
now.hour(),
now.minute(),
now.second()
);
lcd.setCursor(4, 0);
}
lcd.print(timeBuffer);
//Serial.print(timeBuffer);
//Serial.print("\t");
if (isMDY) {
snprintf(dateBuffer, 16, "%s %2d/%2d/%4d",
daysOfTheWeek[now.dayOfTheWeek()],
now.month(),
now.day(),
now.year()
);
} else {
snprintf(dateBuffer, 16, "%s %2d/%2d/%4d",
daysOfTheWeek[now.dayOfTheWeek()],
now.day(),
now.month(),
now.year()
);
}
lcd.setCursor(1, 1);
lcd.print(dateBuffer);
//Serial.println(dateBuffer);
}
}
void showSplash() {
lcd.setCursor(1, 0);
lcd.print("Arduino Clock");
lcd.setCursor(5, 1);
lcd.print("V1.00");
delay(2000);
lcd.clear();
}
void setup() {
Serial.begin(115200);
lcd.begin(16, 2);
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
while (true);
}
// setup the rotary encoder functionality
// use FOUR3 mode when CK_PIN, DT_PIN signals are always HIGH in latch position.
encoder = new RotaryEncoder(CK_PIN, DT_PIN, RotaryEncoder::LatchMode::FOUR3);
attachInterrupt(digitalPinToInterrupt(CK_PIN), checkPosition, CHANGE);
attachInterrupt(digitalPinToInterrupt(DT_PIN), checkPosition, CHANGE);
pinMode(SW_PIN, INPUT_PULLUP);
//showSplash();
}
void loop() {
//static int pos = 0;
//static int dir = 0;
//encoder->tick(); // just call tick() to check the state.
int newPos = encoder->getPosition();
int newDir = (int)encoder->getDirection();
if (pos != newPos) {
Serial.print("pos:");
Serial.print(newPos);
Serial.print(" dir:");
Serial.println(newDir);
pos = newPos;
dir = newDir;
} else {
dir = 0;
}
checkSwitch();
setState();
}