#include <Stepper.h>
#include <DS1307RTC.h> // https://github.com/PaulStoffregen/DS1307RTC // SDA to A4, CLK to A5, VCC to 5V
#include <Timezone.h> // https://github.com/JChristensen/Timezone
#include <Button2.h>
#include <Encoder.h>
#include <EEPROM.h>
// US Pacific Time - assuming RTC is set to PST
TimeChangeRule myDST = { "PDT", Second, Sun, Mar, 2, +60 }; //Daylight time = RTC + 60 minutes
TimeChangeRule mySTD = { "PST", First, Sun, Nov, 2, 0 }; //Standard time = RTC + 0 minutes
Timezone myTZ(myDST, mySTD);
TimeChangeRule *tcr; //pointer to the time change rule, use to get TZ abbrev
// Define stepper motor connections
const long stepsPerRevolution = 4096; // Adjust based on your motor
const int motorPin1 = 7;
const int motorPin2 = 6;
const int motorPin3 = 4;
const int motorPin4 = 5;
// minute hand stepper
Stepper stepperMinute(stepsPerRevolution, motorPin1, motorPin2, motorPin3, motorPin4);
#define BUTTON_PIN 12
Button2 button;
#define ENCODER_CLK 2
#define ENCODER_DT 3
Encoder myEnc(ENCODER_CLK, ENCODER_DT);
char buf[128]; //buffer for sprintf
time_t t;
time_t tz;
unsigned long prevminuteSteps = 0;
const char *monthName[12] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
void click(Button2 &btn) {
for (int i = 0; i <= 15; i++) {
stepperMinute.step(-256);
delay(50);
}
}
void doubleClick(Button2 &btn) {
for (int i = 0; i <= 15; i++) {
stepperMinute.step(256);
delay(50);
}
}
// To set the time of the RTC to the local time when compiled, set setRTC to true. Once the RTC time is set, set it to false and reflash.
#define setRTC false
tmElements_t tm;
bool getTime(const char *str);
bool getDate(const char *str);
void setup() {
Serial.begin(115200);
#if setRTC
bool parse = false;
bool config = false;
// get the date and time the compiler was run
if (getDate(__DATE__) && getTime(__TIME__)) {
parse = true;
// and configure the RTC with this info
if (RTC.write(tm)) {
config = true;
}
}
while (!Serial)
; // wait for Arduino Serial Monitor
delay(200);
if (parse && config) {
Serial.print("DS1307 configured Time=");
Serial.print(__TIME__);
Serial.print(", Date=");
Serial.println(__DATE__);
} else if (parse) {
Serial.println("DS1307 Communication Error :-{");
Serial.println("Please check your circuitry");
} else {
Serial.print("Could not parse info from the compiler, Time=\"");
Serial.print(__TIME__);
Serial.print("\", Date=\"");
Serial.print(__DATE__);
Serial.println("\"");
}
#endif
button.begin(BUTTON_PIN);
button.setClickHandler(click);
button.setDoubleClickHandler(doubleClick);
setSyncProvider(RTC.get); // the function to get the time from the RTC
if (timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
// intialize steps
t = now();
tz = myTZ.toLocal(t, &tcr); //set to local time zone
int seconds = second(tz);
int minutes = minute(tz);
int hours = hour(tz);
unsigned long minuteSteps = hours * stepsPerRevolution + (minutes * stepsPerRevolution + seconds * stepsPerRevolution / 60) / 60;
prevminuteSteps = minuteSteps; //remove intial time offest
}
int interval = 10000;
long prevTime = 0;
long cumuSteps = 0;
long oldPosition = -999; // set encoder initial position
bool prevDST;
int hour12 = 0;
void loop() {
button.loop();
long currentTime = millis();
if (currentTime - prevTime > interval) {
prevTime = currentTime;
t = now();
tz = myTZ.toLocal(t, &tcr);
hour12 = (hour(tz) + 11) % 12 + 1; // take care of noon and midnight-- converts 24h time to 12h time
int seconds = second(tz);
int minutes = minute(tz);
int hours = hour(tz);
bool isDST = myTZ.locIsDST(tz); // if true, then in daylight savings is active.
// verify RTC is set to standard time
sprintf(buf, "%02d %s %02d:%02d %d", day(tz), monthName[month(tz) - 1], hour(tz), minute(tz), isDST);
Serial.println(buf);
// increment every minute
//unsigned long minuteSteps = hours * stepsPerRevolution + minutes * stepsPerRevolution / 60;
// increment every second
unsigned long minuteSteps = hours * stepsPerRevolution + (minutes * stepsPerRevolution + seconds * stepsPerRevolution / 60) / 60;
long steps = minuteSteps - prevminuteSteps;
prevminuteSteps = minuteSteps;
// Move stepper motors to correct positions
stepperMinute.step(steps);
//long cumuSteps = cumuSteps + steps;
//cumuSteps >= stepsPerRevolution ? cumuSteps = 0 : cumuSteps = cumuSteps + steps;
//Serial.println ((double(cumuSteps) / double(stepsPerRevolution)) * 12 );
//check if dst has changed
//track isDST and if this changes then move the hour hand +1 or -1 hour depending on if moving into or out of DST.
prevDST = EEPROM.read(0); //if EEPROM has never been set, this will be 255
//bool isDST = myTZ.locIsDST(tz); // if true, then in daylight savings is active.
if (prevDST >= 2) {
EEPROM.update(0, isDST); //if invalid data, update with current isDST value
prevDST = EEPROM.read(0);
}
//compare prevDST to currentDST
//if prevDST = 0 (not DST) and isDST = 1 (is DST) then time has moved from standard time to daylight savings time. Diff = -1
//if prevDST = 0 (not DST) and isDST = 0 (not DST) then no change. Diff = 0
//if prevDST = 1 (is DST) and isDST = 0 (not DST) then time has moved from daylight savings to standard time. Diff = 1
//if prevDST = 1 (is DST) and isDST = 1 (is DST) then no change. Diff = 0
int diff = prevDST - isDST;
if (diff == -1) { // moved from ST to DST, so "Spring Back"
// roll back an hour | stepper -4096 steps
EEPROM.update(0, isDST); //store new DST value
for (int i = 0; i <= 15; i++) {
stepperMinute.step(-256);
}
}
if (diff == 1) { // moved from DST to ST, so "Fall Forward"
// add an hour | stepper motor +4096 steps
EEPROM.update(0, isDST); //store new DST value
for (int i = 0; i <= 15; i++) {
stepperMinute.step(256);
}
}
}
//read Encoder
long newPosition = myEnc.read() / 4;
if (newPosition != oldPosition) {
if (newPosition > oldPosition) {
//clockwise
stepperMinute.step(-68);
} else {
//anti-clockwise
stepperMinute.step(68);
}
oldPosition = newPosition;
}
}
bool getTime(const char *str) {
int Hour, Min, Sec;
if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false;
tm.Hour = Hour;
tm.Minute = Min;
tm.Second = Sec;
return true;
}
bool getDate(const char *str) {
char Month[12];
int Day, Year;
uint8_t monthIndex;
if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false;
for (monthIndex = 0; monthIndex < 12; monthIndex++) {
if (strcmp(Month, monthName[monthIndex]) == 0) break;
}
if (monthIndex >= 12) return false;
tm.Day = Day;
tm.Month = monthIndex + 1;
tm.Year = CalendarYrToTm(Year);
return true;
}