diff --git a/CAD/PCB_Adapter.stl b/CAD/PCB_Adapter.stl new file mode 100644 index 0000000..e5716a5 Binary files /dev/null and b/CAD/PCB_Adapter.stl differ diff --git a/CAD/motor_Adapter.stl b/CAD/motor_Adapter.stl new file mode 100644 index 0000000..cecebcb Binary files /dev/null and b/CAD/motor_Adapter.stl differ diff --git a/Firmware/orreryDriver.ino b/Firmware/orreryDriver.ino new file mode 100644 index 0000000..ee71113 --- /dev/null +++ b/Firmware/orreryDriver.ino @@ -0,0 +1,283 @@ +// 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!!"); + + } \ No newline at end of file diff --git a/README.md b/README.md index ec47821..a83908b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ # OrreryDriver +![Human made](https://img.shields.io/badge/human-coded-green?logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1wZXJzb24tc3RhbmRpbmctaWNvbiBsdWNpZGUtcGVyc29uLXN0YW5kaW5nIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjUiIHI9IjEiLz48cGF0aCBkPSJtOSAyMCAzLTYgMyA2Ii8+PHBhdGggZD0ibTYgOCA2IDIgNi0yIi8+PHBhdGggZD0iTTEyIDEwdjQiLz48L3N2Zz4=) +[![License: CERN-OHL-S](https://img.shields.io/badge/Hardware%20License-CERN--OHL--S%20v2-blueviolet)](https://ohwr.org/cern_ohl_s_v2.pdf) +[![License: GNU General Public License v3.0](https://img.shields.io/badge/Firmware%20License-GNU%20GPL--v3.0-yellow)](https://www.gnu.org/licenses/gpl-3.0.en.html#license-text) +[![License: CC BY-SA 4.0](https://img.shields.io/badge/Documentation%20License-CC_BY--SA_4.0-lightgrey)](https://creativecommons.org/licenses/by-sa/4.0/) + +This repository contains all the files and documentation for the LEGO Orrery Driver Mod! +- [Visit the DIY guide](https://gorkem.cc/projects/LegoOrreryMod) for details. + + +File structure: ++ `Firmware` contains the code and necessary drivers. ++ `CAD` contains print ready .stl files. ++ `PCB` contains gerber files. + +Components list: +- ESP32-C3 Super mini +- DS3231 mini RTC Module +- 10K Panel Mount Pot with a center detent +- TMC 2208 Silent Stepper Driver Module +- 2.54 PCB mount DC Jack +- JST-EH 1x04 P2.50mm Horizontal Connector +- JST-EH 1x06 P2.50mm Horizontal Connector +- B3F-3152 Button +- Male header pins