// defines pins #define stepPin 3 #define dirPin 4 #define potPin 0 #define buttonPin 7 #define motorEnablePin 10 #include "RTClib.h" #include #include #include unsigned int oldVal; // Define variables for timing unsigned long lastUpdate = 0; const int updateInterval = 100; // Update display every 100ms const unsigned long rtcSyncInterval = 600000; // 172 seconds in milliseconds unsigned long lastRtcSync = 0; unsigned long lastDebugTime = 0; const int debugInterval = 500; // Only check RTC/Print every 500ms //motor values uint32_t totalSecondsinYear = 31536000; //uint32_t fullRotationMotorSteps = 194400; uint32_t fullRotationMotorSteps = 183348; bool isCalibrated = false; bool homingComplete = false; bool realTime = false; int count=0; long targetPos; //inititate EEPROM with preferences library Preferences preferences; // Instantiation and pins configurations // Pin 2 - > DIO // Pin 1 - > CLK TM1637 tm(1, 2); TM1637 tm2(5, 6); int potValue = 0; RTC_DS3231 rtc; AccelStepper stepper1(1, stepPin, dirPin); //define state machine enum {calibrationMode, manualMode, realTimeMode}; unsigned char machineState; void setup() { pinMode(buttonPin, INPUT_PULLDOWN); pinMode(motorEnablePin,OUTPUT); Serial.begin(115200); //preferences.begin("persistent-mem", false); //oldVal = preferences.getUInt("oldVal", 0); tm.init(); tm2.init(); tm.setBrightnessPercent(5); tm2.setBrightnessPercent(5); rtc.begin(); // rtc.adjust(DateTime(2026, 4, 1, 12, 0, 0)); // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); stepper1.setMaxSpeed(4000); stepper1.setAcceleration(500); stepper1.setSpeed(4000); //enable motor digitalWrite(motorEnablePin ,LOW); } void loop() { switch (machineState){ case calibrationMode: calibrateOrrery(); break; case realTimeMode: if (digitalRead(buttonPin) == HIGH){ tm.display("real"); tm2.display("time"); realTime=true; updateMotor(); Serial.println("Real time switch"); } if (realTime && (millis() - lastRtcSync >= rtcSyncInterval)) { lastRtcSync = millis(); updateMotor(); // Calculates new targetPos based on RTC Serial.println("Periodic RTC Sync Triggered"); } manualControl(isCalibrated); break; case manualMode: break; default: Serial.println("Default Mode"); // first check if theres state in memorty if(oldVal == 0){ machineState = calibrationMode; }else{ stepper1.setCurrentPosition(oldVal); isCalibrated = true; realTime=true; updateMotor(); machineState = realTimeMode; } } } void calibrateOrrery(){ if (digitalRead(buttonPin) == HIGH && !homingComplete) { tm.display("clbr"); tm2.display("set"); stepper1.setCurrentPosition(1500); updateMotor(); homingComplete = true; } else if(!homingComplete) { tm.display("clbr"); tm2.display("reqd"); manualControl(isCalibrated); } else{ if (stepper1.distanceToGo() == 0){ isCalibrated = true; machineState = realTimeMode; tm.display("done"); realTime = true; Serial.println("DONE"); } } } long currentMotorPos(){ DateTime now = rtc.now(); DateTime newYear(2026, 1, 1, 0, 0, 0); uint32_t nowUnixTime = now.unixtime(); uint32_t newYearUnixTime = newYear.unixtime(); uint32_t secondsSinceNewYear = nowUnixTime - newYearUnixTime; uint32_t motorPosRT = (uint64_t) secondsSinceNewYear * fullRotationMotorSteps / totalSecondsinYear; // preferences.putUInt("oldVal", motorPosRT); return motorPosRT; } void manualControl(bool calibrated){ potValue = analogRead(potPin); potValue = map(potValue, 0, 4095, 3000, -3000); if(potValue > -900 && potValue<300){ }else{ stepper1.setSpeed(potValue); stepper1.runSpeed(); realTime=false; } if (calibrated && (millis() - lastUpdate >= updateInterval)) { lastUpdate = millis(); if (realTime) { displayTime(rtc.now()); } else { displayTime(simulatedTime(stepper1.currentPosition())); } } } void displayTime(DateTime timeObject) { char timeStr[10]; // Buffer to hold the formatted string int h = timeObject.hour(); int m = timeObject.minute(); // Determine if we show the dot (colon) bool showDot = (millis() / 1000) % 2; bool screenUpdate = (millis() / 500) % 2; if(realTime){ if (showDot) { snprintf(timeStr, sizeof(timeStr), "%02d.%02d", h, m); } else { snprintf(timeStr, sizeof(timeStr), "%02d%02d", h, m); } if (screenUpdate) { tm.display(String(timeStr)); tm2.display(formatDate(timeObject.month(), timeObject.day())); } } else { tm.display((int)timeObject.year()); tm2.display(formatDate(timeObject.month(), timeObject.day())); //tm.display(timeStr); } } DateTime simulatedTime(long motorPosition) { uint32_t secondsSince2026 = (uint32_t)((uint64_t)totalSecondsinYear * motorPosition / fullRotationMotorSteps); return DateTime(DateTime(2026, 1, 1, 0, 0, 0).unixtime() + secondsSince2026); } String formatDate(int month, int day) { String months[] = {"Ja", "Fe", "mr", "Al", "ma", "Jn","Jl", "Au", "Se", "Oc", "No", "De"}; String dayStr = (day < 10) ? "0" + String(day) : String(day); String monthStr = months[month - 1]; return monthStr + dayStr; } int motorStep2seconds(int motorStep){ // return motorStep*1460.0/9.0; return motorStep*172; } void updateMotor(){ targetPos = currentMotorPos(); stepper1.moveTo(targetPos); Serial.print("Target updated to: "); Serial.println(targetPos); Serial.print("Current Pos: "); Serial.print(stepper1.currentPosition()); Serial.print(" Distance to go: "); Serial.println(stepper1.distanceToGo()); Serial.println("RUNING!"); stepper1.runToPosition(); Serial.print("Current Pos: "); Serial.print(stepper1.currentPosition()); Serial.print(" Distance to go: "); Serial.println(stepper1.distanceToGo()); Serial.println("DONE!!"); } void updateMotorManual(){ manual stepper1.moveTo(97200); Serial.print("Target updated to: "); Serial.println(targetPos); Serial.print("Current Pos: "); Serial.print(stepper1.currentPosition()); Serial.print(" Distance to go: "); Serial.println(stepper1.distanceToGo()); Serial.println("RUNING!"); stepper1.runToPosition(); Serial.print("Current Pos: "); Serial.print(stepper1.currentPosition()); Serial.print(" Distance to go: "); Serial.println(stepper1.distanceToGo()); Serial.println("DONE!!"); }