// update min hand every 15 sec, correct for step calc rounding errors every hour.
#include <Button2.h>
#include <Encoder.h>
#include <RTClib.h>;
RTC_DS3231 rtc;
// SDA to A4, CLK to A5, VCC to 5V
// Motor and clock parameters
const long STEPS_PER_REV = 4096; //64 STEPS PER MOTOR REV * 64:1 GEAR RATIO
/*
4096 *1/60 hr to min * 1/60 min to sec = 1.3777 steps/sec
15 sec * 1.377777 = 17.06666 steps. Use 17 steps every 15 sec.
.066666*4*60 = 16 step undershoot per hour.
*/
//#define STEPS_PER_REV 4096 // steps of a single rotation of motor
//#define RATIO 15 // minutes per a rotation
#define BUTTON_PIN 12
Button2 button;
// wait for a single step of stepper
int delaytime = 2;
// ports used to control the stepper motor
// if your motor rotate to the opposite direction,
// 7 to IN1, 6 to IN2, 5 to IN3, 4 to IN5
// change the order as {4, 5, 6, 7};
int port[4] = { 7, 6, 5, 4 };
// sequence of stepper motor control
int seq[8][4] = {
{ LOW, HIGH, HIGH, LOW },
{ LOW, LOW, HIGH, LOW },
{ LOW, LOW, HIGH, HIGH },
{ LOW, LOW, LOW, HIGH },
{ HIGH, LOW, LOW, HIGH },
{ HIGH, LOW, LOW, LOW },
{ HIGH, HIGH, LOW, LOW },
{ LOW, HIGH, LOW, LOW }
};
void rotate(int step) {
static int phase = 0;
int i, j;
int delta = (step > 0) ? 1 : 7;
step = (step > 0) ? step : -step;
for (j = 0; j < step; j++) {
phase = (phase + delta) % 8;
for (i = 0; i < 4; i++) {
digitalWrite(port[i], seq[phase][i]);
}
delay(delaytime);
}
// power cut
for (i = 0; i < 4; i++) {
digitalWrite(port[i], LOW);
}
}
#define ENCODER_CLK 2
#define ENCODER_DT 3
Encoder myEnc(ENCODER_CLK, ENCODER_DT);
void pressed(Button2& btn) {
while (digitalRead(12) == LOW) {
delaytime = 2;
rotate(-68);
}
delaytime = 10;
}
int prevMin = 0;
int thisMin = 0;
int thisSec = 0;
int prevSec = 0;
int thisHr = 0;
int prevHr = 0;
DateTime now;
void setup() {
Serial.begin(9600);
pinMode(port[0], OUTPUT);
pinMode(port[1], OUTPUT);
pinMode(port[2], OUTPUT);
pinMode(port[3], OUTPUT);
pinMode(14, OUTPUT);
digitalWrite(14, LOW);
pinMode(16, INPUT_PULLUP);
if (!rtc.begin()) {
Serial.println(F("Couldn't find RTC"));
while (1)
;
}
button.begin(BUTTON_PIN);
button.setPressedHandler(pressed);
// following line sets the RTC to the date & time this sketch was compiled
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); //uncomment to set time, then recomment and reupload
//rtc.adjust(DateTime(2023, 8, 16, 0, 15, 0)); // YYY, MM, DD, HH, MM, SS
now = rtc.now();
Serial.print(now.hour());
Serial.println(" hr");
Serial.print(now.minute());
Serial.println(" min");
// Doesn't work - won't rotate the full number of times to set the intial time from midnight, stops at just over 2 rotations
// unsigned long initStepper = (now.hour() * 60 + now.minute()) * STEPS_PER_REV / 60;
// Serial.print(initStepper);
// Serial.println(" initial stepper pos");
// rotate(initStepper);
prevSec = now.second();
thisSec = prevSec;
prevMin = now.minute();
thisMin = prevMin;
prevHr = now.hour();
thisHr = prevHr;
int delaytime = 10;
}
int interval = 1000;
unsigned long lastmilli = 0;
long steps = 0;
int incr = 0;
long oldPosition = -999; // set encoder initial position
void loop() {
int elapseSec;
unsigned long thismilli = millis();
button.loop();
if (thismilli - lastmilli > interval) {
lastmilli = thismilli;
now = rtc.now();
thisSec = now.second();
thisMin = now.minute();
thisHr = now.hour();
//Serial.print(thisMin); Serial.println(" thisMin");
}
// update min hand every 15 sec
if (thisSec != prevSec) {
elapseSec++;
if (elapseSec % 15 == 0) {
rotate(17); //assume direct 1:1 gearing between stepper output shaft and min hand
if(elapseSec >= 15) elapseSec = 0;
Serial.print("elapsetime: "); Serial.println(elapseSec);
}
}
// if (thisMin != prevMin) {
// prevMin = thisMin;
// Serial.print(thisMin);
// Serial.println(" mins");
// // if stepper to min hand is 1:1 then each min = 68.26666... steps (4096/60).
// // therefore move 68 steps each time, then on the 15th time, move an extra 4 steps (15*.2666666)
// if (thisMin % 15 == 0) {
// incr = 72;
// } else {
// incr = 68;
// }
// Serial.print(incr);
// Serial.println(" incr");
// rotate(incr);
// steps += incr;
// Serial.print(steps);
// Serial.println(" steps");
// Serial.println();
// // if stepper to min hand has a 4:1 ratio (4 stepper motor rotor rotations per 1 min increment) then
// // each 1 min tick would require 68.266666 * 4 steps = 273.0666666... steps. So a correction of 4 steps would
// // be needed at each full rotation (1 hr | .06666 * 60 = 4) instead of each 15 min.
// }
//read Encoder
long newPosition = myEnc.read() / 4;
if (newPosition != oldPosition) {
if (newPosition > oldPosition) {
//clockwise
delaytime = 2;
rotate(-68);
delaytime = 10;
} else {
//anti-clockwise
delaytime = 2;
rotate(68);
delaytime = 10;
}
oldPosition = newPosition;
}
}