Stepper motor - Arduino alternatives/accuracy improvement

Hi All,

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.

  1. Change the stepper motor. Maybe something with a larger gear ratio to allow for less micro steps.
  2. Change the board/ how the stepper is run
  3. 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;
    
  }
}

Thanks in advance,
Greg

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.

1 Like

Thanks Jeff, i will have to look into how to do the above. I do have a second arduino on hand which is a start.

After logging the delay if we did find an issue what would the next steps be?

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.

I Use these in my camera platform:

Implement the button monitoring using interrupts. The difference would be tiny, but might be enough.

1 Like

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

1 Like

You could also add an encoder to the stepper motor and determine the actual position and adjust of necessary.

1 Like

Thanks Andrew,

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.

2 Likes

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?

1 Like

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.

1 Like

Thanks Jeff you’ve been very helpful.

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.

Wish me Luck.

Cheer,

Greg

1 Like

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.

2 Likes

Hi Gregory,

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.

-James

1 Like

No worries, it’s all in the gearing.

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.

1 Like

Hi Jeff,

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;  
   
 }
2 Likes

This should work (only very primitive testing).

// 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
  }
}
1 Like

Thats awesome, Thansk Jeff.

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.

17108
284
180
7608
11588
336
216
484
12588
6112
512
192
312
5440
12592
1684
372
10560
8220
604
184
396
3176
15700
592
188
220
8508
10340
500
224
204
336
13576
5848
276
6484
12588
620
476
11536
7340
456
192
384
4192
14508
788
180
224
9512
9312
808
376
2092
16708
576
192
200
192
7308
11492
560
192
392
12580
6964
476
5132
12592
1824
10808
9156
400
3036
12596
3908
8712
10864
348
1372
18224
396
6584
12588
332
244
220
11840
7404
612
168
360
4024
15520
324
9384
9512
696
2412
12568
4220
496
268
452
7164
12412
260
12548
6448
412
252
5464
12576
1076
664
180
344
10356
6580
2680
396
2936
16452
260
288
8208
11140
256
260
928
18000
364
248
212
372
5996
12552
632
236
196
260
11348
7940
272
4372
15596
400
9220
10388
2228
12576
4444
240
288
188
352
7084
11664
880
312
12368
6260
240
380
312
444
4920
11952
632
1956
480
10192
8508
324
500
388
2872
12592
3112
272
560
192
396
8068
10652
532
196
204
236
756
18060
696
168
340
5936
12584
328
568
192
384
11148
7580
604
172
324
3904
15508
256
9444
10292
264
352
1704
12572
4896
212
196
276
7020
11808
408
208
252
236
12300
6432
624
292
204
236
4772
12588
1788
220
184
408
10020
9440
444
2724
16020
496
348
344
7980
11596
480
504
18224
416
284
444
5828
12580
328
372
248
212
11464
8352
236
256
3736
15788
504
8928
9756
788
368
1700
17168
448
272
244
232
6804
12068
508
224
208
12212
6544
712
172
384
4756
12584
1360
496
216
200
228
10108
9560
404
2644
12580
4152
228
8236
11812
784
12596
5716
516
208
324
5824
12588
660
576
11396
7808
244
284
236
428
3572
15084
720
192
9216
9896
756
428
1536
15356
1912
552
176
436
18572
256
444
248
384
12048
7576
404
4588
12588
1524
236
484
216
300
9856
9508
244
372
2488
16804
280
244
7856
11772
312
192
320
18592
320
260
196
5820
12584
324
312
508
212
11268
8004
512
184
192
312
3368
12600
2904
360
188
9156
9992
496
244
192
1684
18212
276
6684
12116
476
264
400
11976
6796
768
176
248
4580
12576
1468
364
296
256
200
192
9824
8668
304
212
340
184
544
2328
16336
584
188
436
7632
11164
560
188
404
272
12600
5928
736
364
5564
12588
704
664
172
272
10816
7972
832
360
3420
16156
9064
10944
284
1356
12596
5780
19116
300
200
280
12152
6940
248
268
284
4832
12580
1792
516
1 Like

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

12580
4196
224
192
196
204
7564
9756
1740
220
188
200
180
308
12600
6352
220
184
216
196
5396
12584

1 Like

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.

12612
12616
12612
12612
12620
12608
12612
12620
12612
12612
12616
12612
12612
12620
12608
12608
12616
12616
12608
12624
12608
12608
12620
12612
12616

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).

1 Like

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.