/*
28BYJ-48 stepper motor with ULN2003
IN1 7
IN2 6
IN3 5
IN4 4
32 full step with 64:1 gear ratio = 2048
https://lastminuteengineers.com/28byj48-stepper-motor-arduino-tutorial/
*/
// update min hand every 15 sec, correct for step calc rounding errors every hour.
#include <Button2.h>
#include <Encoder.h>
#include <TaskScheduler.h>
// Callback methods prototypes
void t1Callback(); // increment stepper 17 steps each 15 sec (68 steps / min or 4080 steps / hr)
void t2Callback(); // increment stepper 16 steps each hour to get to 4096 total steps each hour
//Tasks
Task t1(15000, TASK_FOREVER, &t1Callback); //17 steps each 15 sec (68 steps / min or 4080 steps / hr)
Task t2(1800000, TASK_FOREVER, &t2Callback); //16 steps per hour correction (8 steps / 30 min), 68x60 = 4080 and should be 4096
Scheduler runner;
// 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
// Motor and clock parameters
const long STEPS_PER_REV = 4096; //64 HALF 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 RATIO 15 // minutes per a rotation
#define BUTTON_PIN 12
Button2 button;
// wait for a single step of stepper
int delaytime = 5; //smaller = faster
// 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 }; <- works with UNL2003 IN1, IN2, IN3, IN4 and 28BYJ-48
//int port[4] = { 4, 5, 6, 7 }; <- works with UNL2003 IN1, IN2, IN3, IN4 and 28BYJ-48
int port[4] = { 4, 6, 5, 7 }; //<- works w/ direct drive
// either sequence seems to work fine but only half of the commanded steps occur in simulation. real hw is fine.
// sequence of stepper motor control for 1/2 steps
// 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 }
// };
// sequence of stepper motor control - alternate from https://www.utmel.com/components/28byj-48-5v-stepper-motor-28byj-48-datasheet-pinout-wiring?id=1294
int seq[8][4] = {
{ LOW, HIGH, HIGH, HIGH },
{ LOW, LOW, HIGH, HIGH },
{ HIGH, LOW, HIGH, HIGH },
{ HIGH, LOW, LOW, HIGH },
{ HIGH, HIGH, LOW, HIGH },
{ HIGH, HIGH, LOW, LOW },
{ HIGH, HIGH, HIGH, LOW },
{ LOW, HIGH, HIGH, 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);
}
// de-entergize coils
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;
// }
void click(Button2 &btn) {
//delaytime = 2;
for (int i = 0; i <= 15; i++) {
rotate(-256);
}
//delaytime = 10;
}
void doubleClick(Button2 &btn) {
//delaytime = 2;
for (int i = 0; i <= 15; i++) {
rotate(+256);
}
//delaytime = 10;
}
long oldPosition = 0; // set encoder initial position
char buf[128]; //buffer for sprintf
void setup() {
Serial.begin(9600);
/* start task to take care of 47 step adjust each 4 hours */
runner.init();
Serial.println("Initialized scheduler");
runner.addTask(t1);
Serial.println("added t1");
runner.addTask(t2);
Serial.println("added t2");
t1.enable();
Serial.println("Enabled t1");
t2.enable();
Serial.println("Enabled t2");
/* ************************************** */
pinMode(port[0], OUTPUT);
pinMode(port[1], OUTPUT);
pinMode(port[2], OUTPUT);
pinMode(port[3], OUTPUT);
button.begin(BUTTON_PIN);
//button.setPressedHandler(pressed);
button.setClickHandler(click);
button.setDoubleClickHandler(doubleClick);
//int delaytime = 10; // stepper speed
}
void loop() {
button.loop();
runner.execute();
//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;
}
}
// increment stepper 17 steps each 15 sec
void t1Callback() {
if (t1.isFirstIteration()) {
Serial.println("1st");
} else {
Serial.println("17 step");
// Serial.println(millis());
rotate(17);
}
}
// increment stepper 8 steps each 30 min
void t2Callback() {
if (t2.isFirstIteration()) {
Serial.println("1st");
} else {
Serial.println("8 step correction");
// Serial.println(millis());
rotate(8);
}
}