I have a question about running a stepper motor at extremely accurate and slow speeds. I am currently running the motor with an Arduino set up and wondering if there are any other/better ways to run it.
Use case:
I have made a diy equatorial platform that allows for my telescope to track the night sky. Due to the high magnification (and long exposures when imaging) the platform relies on the stepper motor running at slow and accurate speeds in order to minimise distortions, elongated stars etc when imaging.
Issue:
With my current set up I’ve got everything dialled in quite nicely but over an hour of tracking there will be several events where the motor will slow down and then speed back up to the correct speed. Please note we are talking very minute changes in speed but due to the large magnification it is exaggerated.
Current set up:
Arduino Micro with sketch created by me and improved previously by this forum and by some others in the astronomy field to eliminate periodic error. Please see sketch below.
Power: 12v 2A power source that supplies 12v through a decoupling capacitor - 100UF/25V to the stepper driver set set at 1.4A and also splits before the capacitor and goes to a buck converter that reduces voltage to 5v to power the arduino Micro via the 5v pin.
have tried both DRV8825 and S109 driver. S109 currently installed.
Nema 17 Stepper Motor Bipolar L=48mm w/ Gear Ratio 27:1 Planetary Gearbox
Stepper motor running at 16 micro steps at 1 step per
Anyway as mentioned, I have had help improving the sketch and still having these issues which in my eyes leaves me with three options moving forward to cover most variables.
Change the stepper motor. Maybe something with a larger gear ratio to allow for less micro steps.
Change the board/ how the stepper is run
Accept that I’m running the stepper at the limit for my use and the issue cant be resolved.
int button = 13; // the number of the input pin
int Buzzer = 11; // the number of the output pin
int ms1 = 2;
int ms2 = 3;
int ms3 = 4;
int state = LOW; // the current state of the output pin
int reading; // the current reading from the input pin
int previous = LOW; // the previous reading from the input pin
#define dirPin 8
#define stepPin 7
#define stepsPerRevolution 5400
// the follow variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long time = 0; // the last time the output pin was toggled
long debounce = 2000; // the debounce time, increase if the output flickers
unsigned long endTime;
boolean itAlreadyHappened = true;
unsigned long delay_Micros = 12620;
unsigned long next_Micros = 0;
void setup()
{
Serial.begin(9600);
pinMode(button, INPUT);
pinMode(Buzzer, OUTPUT);
// Declare pins as output:
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
pinMode(ms1, OUTPUT);
pinMode(ms2, OUTPUT);
pinMode(ms3, OUTPUT);
digitalWrite(ms1, HIGH);
digitalWrite(ms2, HIGH);
digitalWrite(ms3, LOW);
// Next (first) step should occur delay_Micros from now
next_Micros = micros() + delay_Micros;
// Set the spinning direction clockwise (tracking direction)
digitalWrite(dirPin, HIGH);
}
void loop()
{
reading = digitalRead(button);
// If the input just went from LOW and HIGH and we've waited long enough
// to ignore any noise on the circuit, toggle the output pin and remember
// the time
if (reading == HIGH && previous == LOW && millis() - time > debounce) {
if (state == HIGH)
state = LOW;
else
state = HIGH;
time = millis();
}
if (state==LOW) {
if (itAlreadyHappened == false)
{
digitalWrite(dirPin, HIGH); // Enables motor to speed up in normal tracking direction to clear push button eliminating multiple reads.
for(int x = 0; x < 18000; x++) {
digitalWrite(stepPin, HIGH);
delayMicroseconds(200);
digitalWrite(stepPin, LOW);
delayMicroseconds(200);
}
// Reset our target time for the next "tracking" step
next_Micros = micros() + delay_Micros;
itAlreadyHappened = true;}
// Handle normal tracking steps
// These lines result in 1 step:
if (micros() >= next_Micros) {
next_Micros += delay_Micros;
digitalWrite(stepPin, HIGH);
delayMicroseconds(10);
digitalWrite(stepPin, LOW);
}
}
else {
// Set the spinning direction in reverse to reset platform and return to start position.
// this actualy runs first when arduino powered on.
digitalWrite(dirPin, LOW);
// These four lines result in 1 step:
digitalWrite(stepPin, HIGH);
delayMicroseconds(16);
digitalWrite(stepPin, LOW);
delayMicroseconds(16);
itAlreadyHappened = false;
}
}
A slight change in speed suggests it is not simply missing a step, so the problem is likely to be in the timing in the loop. The motor steps when micros() is equal to or greater than delay-Micros, so it would be good to know how large this ‘greater than’ can get. Calculating this difference would barely affect the timing, but logging it through the serial port would. You could write it to memory but you would quickly run out of space, and a SD card or similar would also seriously affect the timing. One possibility is to use a second Arduino to monitor the step signal and log the timing. The logging MCU would connect to the Step output and note the elapsed time between steps. This could then be logged to the serial port in plenty of time for the next step. To maximise the timing accuracy the logging MCU should use an interrupt to monitor the signal. This form of logging has no impact on the timing of the controller MCU.
Not quite your question, but you can use small DC encoder motors instead of the stepper.
This allows you to use WAY less power and accurately determine the position, rather than just telling the steps to go xx steps and hoping that it goes that many.
If you don’t find an issue, that will also be useful information, because it may indicate that the problem is mechanical. This also depends on exactly what form the slowing and speeding up takes, and whether there is a pattern to it. For instance, an imperfection in the moving mechanism may impose additional resistance in the movement at certain combinations of motor and mount positions. For some of the microsteps the torque available is significantly reduced - if this corresponded to a point of increased resistance in the mount then the microstep would be skipped. The motor would ‘catch up’ at or before the next full step, so it would be back in sync - the ‘speeding up’ that you have noticed. See here for some discussion of the problem of reduced torque for single microstepping. Microstepping myths | Machine Design
going to look into this, although i think i would need a bigger motor or maybe two, one on each side of the platform, as my scope is quite large and heavy 10" 1.25 meter dobsonian, total weight 30kg.
This is my first arduino/electronics project so apologies for all the questions.
Is the step output the step output from the arduino (that goes into the step input of the stepper driver) or the output to the actual motor (1 of the 4 wires going to stepper motor).
Regarding the serial monitor would it just be a matter of tapping into the correct step output, connecting it to a pin in the second arduino and then writing a sketch that checks if that pin is high or low and measures the difference in time between when its high?
It’s the Step output from the controlling Arduino (pin 7 in your code). You don’t want any direct connection between the motor and the Arduino. Your code would be as you describe - loop continually checking the pin state and note the difference between times (micros()) at which it goes high (or, between the times when it goes low - the result will be the same). If you want to upgrade to bigger or additional motors then PM me and something can be arranged.
The mention of a larger or additional motors was in reference to Andrews suggestion of the N20 DC motor however, depending on the results from this test a different motor or addition could help so I will be sure to keep you posted.
If the problem is lack of torque in microstepping, a DC motor won’t help, as it suffers even more than the stepper with low torque at low speed (and I assume you don’t want to be doing a complete repositioning for each image). My suggestion is a larger (more powerful) stepper.
If you determine that the timing isn’t up to scratch, you may want to use two timers/counters directly to toggle the step pin and count the steps moved (if it needs to stop eventually).
Let us know how your timing experiments go, and look into interrupts as Jeff mentioned, then I’ll help where I can regarding timer code
When I’m not sure about the timing of my code, I usually use a cheap logic analyser to get a second opinion.
Yeah those are the ones that I am using, but there are plenty more, bigger, encoder motors available that will work.
As above, it depends on the gearing, no-one is going to direct drive anything this big or heavy.
Telescopes are usually worm driven with a huge worm wheel gear and a geared down motor.
From my experience with highly geared down small motors, I think that you could easily move a 30kg telescope with that little N20, but I am not sure that it would be fast enough to keep up with 15degrees an hour, but it probably would.
Unfortunately i’ve spent 4 hours on this and cant even seem to make the most simple sketch work! Any help with the sketch would be greatly appreciated.
Here are acouple i tried to get working. I’m not very good with writing sketch’s so i have tried to edit some i found on the net.
Attempt 1
int stepPin = 2;
void setup() {
Serial.begin(9600);
pinMode(testpin, OUTPUT);
digitalWrite(testpin, HIGH);
}
void loop() {
Serial.println("test");
// put your main code here, to run repeatedly:
while(stepPin); //wait for LOW on digital pin 1
unsigned long start = micros();
while ( (stepPin) == 0); //wait for HIGH
while (stepPin); //wait for LOW
unsigned long pulse_time = micros()-start;
Serial.println(pulse_time);
}
Attempt 2
int stepPin = 2;
int testpin = 3;
unsigned long starttime;
unsigned long steptime;
unsigned long counting;
void setup() {
pinMode(testpin, OUTPUT);
pinMode(stepPin, INPUT);
digitalWrite(testpin, HIGH);
Serial.begin(9600);
}
void loop() {
Serial.println("test");
if (stepPin == HIGH && counting==false){
starttime=micros();
counting=true;
}
if (stepPin == LOW && counting==true){
counting=false;
steptime=micros()- starttime;
}
Serial.println(steptime);
steptime=0;
}
Attempt 3
int stepPin = 2;
int testpin = 3;
unsigned long lastTime = 0;
int lastVal = HIGH;
int val = LOW;
void setup() {
Serial.begin(9600);
pinMode(testpin, OUTPUT);
pinMode(stepPin, INPUT);
digitalWrite(testpin, HIGH);
Serial.begin(9600);
}
void loop() {
Serial.println("test");
// read the current input value
val = digitalRead(stepPin);
// if the value has changed since last time and is HIGH
if(val != lastVal && val){
// get current time
unsigned long currTime = micros();
// calculate difference to last time
unsigned long timeDiff = currTime - lastTime;
// get a new lastTime for next high
lastTime = currTime;
Serial.println(timeDiff);
}
// update lastVal so we know the input changed
lastVal = val;
}
// Log the delay between successive low to high transitions
int testPin = 2;
void setup() {
Serial.begin(115200);
pinMode(testPin, INPUT_PULLUP);
Serial.println("Test:");
}
long timeStart = micros(); // Start time of previous transition.
long timeInterval = 0; // Duration.
boolean previousState = HIGH; // Pin state at last transition.
void loop() {
if (previousState == LOW && digitalRead(testPin) == HIGH) {
// Low to High transition has occurred
previousState = HIGH; // State is now High
timeInterval = micros() - timeStart; // Calculate interval
timeStart = micros(); // Reset timer
Serial.println(timeInterval); // Log it
}
else if (previousState == HIGH && digitalRead(testPin) == LOW) {
// High to Low transition has occurred.
previousState = LOW; // State is now Low
}
}
First results in and definitely not what i was expecting. i dont really see any patter in the numbers; was expecting to see 12620 repeated with some variations. Heres a very small sample space.
further analysis shows there is a patter (sort of). Take the bellow sample space of numbers where there is clear steps at the corret or close to correct step time e.g 12600,12580,12584. between those figure the sum of smaller numbers add up to close to the 12600. 12576, 12592 and 12564.
Is this normal behaviour?
I’ve also looked for the largest numbers and there are over 300 significantly over 12600. with the largest being 19420. will do a proper test tonight and see if i can find any other correlations
I can’t repeat those results. I get exactly what you would expect. That is, 12620 less varying small amounts representing the delay in checking the timer.
Your data looks like a glitch in the step pin causing it to fire in between steps. Check all your connections.
Or, is it possible that there is a poor connection on the button input and the button processing code is getting executed? I would comment out that first part of the loop and try again. (I didn’t install a button, and I got odd results until I disabled that code).
Ok, i’ve done some testing and have found the issue, although i have no idea why it is an issue.
My platform is powered by a 12v 3 amp power supply thats split so 12v goes directly to the stepper driver and 12v to a buck converter which converts to 5v and the the 5v powers the arduino micro via the 5v pin. Checked with multimeter and can confirm its 5/5.1 v going into the arduino.
With this set up im having issues with the step timing as per above.
if i was to leave everything as is but also plug in the usb cable to teh arduino and connected to my computer the issue is immediately fixed and i can see correct step timing. Any idea why this might be?
I also removed the buck converter and just powered arduino with USB and it worked fine too. However, when i unplugged the arduino the system continue to work, although back to incorrect step timing. Should this happen? Seems that some how it is getting power from the driver?
anyway, i will go ahead and test the steps with the computer usb power and report back. regarding timing.