/* Silent clock driver using CNC Shield V4 driver board Build a minimum hardware stepper motor clock driver using CNC Shield V4, TMC2208 stepper driver, and DS3231 RTC CNC Shield V4 is modified to fix jumper connections Jumpers are removed from lower right TMC2208 port All 3 pins hard wired to VIO (5V) TMC2208 motor supply voltage hard wired to 5V One motor header is hacked to connect DS3231 RTC Pin 1 connected to Vss (RTC NC and Vss pins shorted together) Pin 2 connected to SCL Pin 3 connected to SDA Pin 4 connected to VIO (5V) Use a DS3231 precision RTC as a reference time beat Accuracy 2PPM = about 1 minute per year drift Plug TMC2208 into lower right port ENN = D8 (PB0) Low = motors enabled DIR = D4 (PD4) STEP = D7 (PD7) Jumpers in the 8 pin header set speed and direction CoolEN changes motor speed Resume, Hold, and Abort set speed to one of 8 speeds Parameters calculated any time jumpers are changed Note: This sketch uses RTC.h by Manjunath CV to access the real time clock Type to install libraries Search for "RTC" and install RTC.h Download this sketch from https://www.stevesclocks.com/sp9 Cut and paste the code into the Arduino IDE (Or change the file extension from .txt to .ino) Revision History: 05-Jan-23 1.00 Original release 06-Jan-23 1.01 Switched from RTC.h to DS3231.h 11-Jan-23 1.02 Updated debug monitor 25-Jan-23 1.03 Added 4.667 speed ratio for SP9 26-Jan-23 1.04 Added status LED monitor (on for "+") 10-Feb-23 1.05 Swtched back to RTC.h (more stable) */ #define REVISION 1.05 #include #include static DS3231 RTC; // RTC register definition // define pins used #define MOTOR_ENN_PIN 8 #define MOTOR_DIR_PIN 4 #define MOTOR_STEP_PIN 7 #define STATUS_LED_PIN 13 #define MOTOR_JUMPER_PIN A3 #define MOTOR_SPEED_PIN_2 A2 #define MOTOR_SPEED_PIN_1 A1 #define MOTOR_SPEED_PIN_0 A0 // make most variables global (accessible during setup() and loop() int motor_delay; // delay value sent to motor int min_motor_delay; // short delay value int max_motor_delay; // long delay value int motor_steps; // number of steps per second int extra_step_time; // how often an extra step is needed int extra_steps; // how many extra steps get added int extra_step_count = 0; // counter for when to add extra steps long seconds_counted = 0; // total count of seconds elapsed long seconds_moved = 0; // total count of seconds motor has moved int char_counter = 0; // character display count (for CR/LF every minute) int motor_speed_pins; // current reading of 3 motor speed pins int last_motor_speed = -1; // previous reading of 3 motor speed pins int rtc_seconds; // actual seconds value returned by RTC int last_seconds; // previous seconds from RTC to compare against void setup() { // put your setup code here, to run once: pinMode(STATUS_LED_PIN, OUTPUT); // status LED pinMode(MOTOR_ENN_PIN, OUTPUT); // motor enable# pinMode(MOTOR_DIR_PIN, OUTPUT); // motor direction pinMode(MOTOR_STEP_PIN, OUTPUT); // motor step pinMode(MOTOR_JUMPER_PIN, INPUT_PULLUP); // input to set motor direction pinMode(MOTOR_SPEED_PIN_2, INPUT_PULLUP); // input #2 to set motor speed pinMode(MOTOR_SPEED_PIN_1, INPUT_PULLUP); // input #1 to set motor speed pinMode(MOTOR_SPEED_PIN_0, INPUT_PULLUP); // input #0 to set motor speed digitalWrite(STATUS_LED_PIN, LOW); // turn off status LED // debug message at program start Serial.begin(9600); Serial.println(""); Serial.println(""); Serial.print("CNC Shield V4 clock movement - Rev "); Serial.println(REVISION); // wait for seconds to change at beginning of algorithm // (algorithm will hang if RTC is not present) RTC.begin(); rtc_seconds = RTC.getSeconds(); // initialize rtc_seconds setting last_seconds = rtc_seconds; while (rtc_seconds == last_seconds) { rtc_seconds = RTC.getSeconds(); } } void loop() { // put your main code here, to run repeatedly: // local variables int step_count; // counter for one second of movement // reset motor direction at every loop // (to allow changing direction anytime) digitalWrite(MOTOR_DIR_PIN, digitalRead(MOTOR_JUMPER_PIN)); // read motor speed pins and determine if they have changed motor_speed_pins = 4 * !digitalRead(MOTOR_SPEED_PIN_2) + 2 * !digitalRead(MOTOR_SPEED_PIN_1) + 1 * !digitalRead(MOTOR_SPEED_PIN_0); if (motor_speed_pins != last_motor_speed) { // determine new speed based on speed pins switch (motor_speed_pins) { case 0: // 2.4:1 (24:10, 36:15, etc.) motor_steps = 128; // 3200 * 24 / 10 / 60 = 128 extra_step_time = 0; break; case 1: // 2.667:1 (32:12, etc.) motor_steps = 142; // 3200 * 32 / 12 / 60 = 142.222 extra_step_time = 9; extra_steps = 2; // 2 extra steps every 9 seconds break; case 2: // 3:1 (30:10, 45:15, etc.) motor_steps = 160; // 3200 * 30 / 10 / 60 = 160 extra_step_time = 0; break; case 3: // 3.6:1 (36:10, 54:15, etc.) motor_steps = 192; // 3200 * 36 / 10 / 60 = 192 extra_step_time = 0; break; case 4: // 4.667:1 (56:12, 84:18, etc.) motor_steps = 248; // 3200 * 84 / 18 / 60 = 248.888 extra_step_time = 9; extra_steps = 8; // 8 extra steps every 9 seconds break; case 5: // 5.4:1 (54:10, etc.) motor_steps = 288; // 3200 * 54 / 10 / 60 = 288 extra_step_time = 0; break; case 6: // 10:1 (60:6, 80:8, etc.) motor_steps = 533; // 3200 * 60 / 6 / 60 = 533.333 extra_step_time = 3; extra_steps = 1; // 1 extra step every 3 seconds break; default: // 60:1 (fast debug mode) motor_steps = 3200; // 3200 * 60 / 60 = 3200 extra_step_time = 0; break; } // end switch() // calculate min/max steps based on parameters set during switch // NOTE: Need to add an offset (large motor steps need shorter min delays) min_motor_delay = (int)((long) 970000 / motor_steps / 2 - 40); max_motor_delay = (int)((long)1010000 / motor_steps / 2); Serial.println(""); Serial.print("speed "); Serial.println(motor_speed_pins); Serial.print("steps "); Serial.print(motor_steps); if (extra_step_time) { // only print this value if extra_step time is set Serial.print(" (plus "); Serial.print(extra_steps); Serial.print("/"); Serial.print(extra_step_time); Serial.print(")"); } Serial.println(); Serial.print("min_delay "); Serial.println(min_motor_delay); Serial.print("max_delay "); Serial.println(max_motor_delay); Serial.println(""); // start with minimum motor delay (will probably miss first time) motor_delay = min_motor_delay; seconds_counted = seconds_moved = 0; char_counter = 0; last_motor_speed = motor_speed_pins; // save latest speed setting } // end if(motor_speed_pins != last_motor_speed){} // move motor for approximately one second for (step_count = 0; step_count < motor_steps; step_count++) { // pulse one complete cycle (up and down) digitalWrite(MOTOR_STEP_PIN, HIGH); delayMicroseconds(motor_delay); digitalWrite(MOTOR_STEP_PIN, LOW); delayMicroseconds(motor_delay); // check every few cycles to see if RTC has advanced // also, check just before the end of the loop if (((step_count % 50) == 0) || (step_count == (motor_steps - 1))) { // check if seconds have advanced rtc_seconds = RTC.getSeconds(); if (rtc_seconds != last_seconds) { seconds_counted++; // RTC has changed, increment seconds_counted last_seconds = rtc_seconds; // reset last_seconds to current time } } } seconds_moved++; // motor has moved motor_steps, increment seconds_moved // check if seconds counted is the same as seconds moved if (seconds_counted >= seconds_moved) { // delay is just right (or slightly long) if seconds match // decrease motor_delay to speed up slightly motor_delay = min_motor_delay; Serial.print("-"); digitalWrite(STATUS_LED_PIN, LOW); // turn off status LED } else { // delay is too short if seconds do not match // increase motor delay to max motor_delay = max_motor_delay; Serial.print("+"); digitalWrite(STATUS_LED_PIN, HIGH); // turn on status LED } // check if extra delay needs to be added if (extra_step_time) { if (++extra_step_count >= extra_step_time) { for (step_count = 0; step_count < extra_steps; step_count++) { // pulse one complete cycle (up and down) digitalWrite(MOTOR_STEP_PIN, HIGH); delayMicroseconds(motor_delay); digitalWrite(MOTOR_STEP_PIN, LOW); delayMicroseconds(motor_delay); } extra_step_count = 0; // reset extra step counter } } // add a CR/LF once per minute if (++char_counter >= 60) { // extra debug info every minute Serial.print(" ("); if ((seconds_moved / 3600) >= 24) { // print days Serial.print(seconds_moved / 3600 / 24); Serial.print("d"); } if (seconds_moved >= 3600) { // print hours Serial.print((seconds_moved / 3600) % 24); Serial.print("h"); } // print minutes Serial.print((seconds_moved / 60) % 60); Serial.println("m)"); char_counter = 0; } } // end loop()