Arduino stepper motor Speed fluctuations

Hi Every one,

This is my first post so apologies if I leave anything out, ill try make it as detailed as possible.

I have made a equatorial platform that tracks the night sky for my telescope and am using an arduino powered stepper motor to try and get the precise and smooth speed I need.

I have been having some slight issues with the motor seeming to speed up and slow down and just want to confirm if i’ve made any errors in my set up. Although I would like to point out the speeding up and slowing down may be quite minor but exaggerated due to high magnification of the telescope. overall the speed is correct and can keep an object in the field of view for 1.5 hours but the fluctuations are happening back and forth within the center of the field of view. (i hope this makes sense)

Currently I’m using a single 12v 2A power source that supplies 12v through a decoupling capacitor - 100UF/25V to the stepper driver set set at 1.4A (have tried both DRV8825 and S109 driver) 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 which i recently switched to from a nano for the extra accuracy of crystal oscillator.

The Arduino sketch runs the motor at 32 micro steps to reduce vibration at high magnification (testing shows no difference between 16 or 32 micro steps) and loops taking a single step at 1 step every 6032 micro seconds.

Could the single power source be causing an issue? or does anything stand out?

looking forward to your response.

Greg

Hi Greg,

Hmm, that’s going to be a tricky one to chase down. Depending on the size of the movement, it could be mechanical, electrical, magnetic, or software. It may just be non-linearity in the response of the stepper due to micro-stepping.

Are you able to supply photos of how you’ve got everything set up, and some wiring schematics?

If you have a way to accurately measure the position of your telescope it would be good to log that data and see if there’s any recognisable pattern that matches up with something.

Hi thanks for your reply.

I thought it be a tough ask. was just hoping something may have stood out in the electronics.

Performing another test next clear night where I will continuously take 8 second photos through the telescope for 1.5 hours to see if any patterns emerge.

Photos and schematic to come in next post. I’ve tried my best with the schematic, apologies if its a bit messy.

Hi Gregory,

No worries. Will do some digging as well in the meantime and see if I can find anything.

1 Like

Thank you Oliver. Please see attached

1 Like

Oh, And here is the sketch incase that helps.

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 = HIGH;      // 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 = false;   

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, 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 the motor to move in a particular direction
  // Makes 200 pulses for making one full cycle rotation
  for(int x = 0; x < 18000 ; x++) {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(100); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(100); }
    
         itAlreadyHappened = true;}


  
 
    // These four lines result in 1 step:
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(3016);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(3016);

      


} else {
 
  // Set the spinning direction clockwise:
  digitalWrite(dirPin, LOW);

  
  
    // These four lines result in 1 step:
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(8);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(8);
  
 
  
   itAlreadyHappened = false;
  
}
 
  }
1 Like

Hey Gregory,

That’s the first thing that comes to mind when I look over this. I’d suggest increasing the source voltage to your converter so you can be assured that the output power is sufficient, or including a second power supply so that each motor can use each individually. Otherwise, there doesn’t appear to be any obvious issues with the script, or your circuit I can see. Have a great day!

1 Like

Thanks Bryce,

Regarding the power supply. There is only one motor receiving the 12v. the converter to 5v is for the Arduino Micro. Will test with two separate power supplies and see how I go though.

Hi Gregory,

I think what Bryce is getting at is that it may be worth either getting a dedicated psu for your motor or alternatively bumping up the power delivered to your existing circuit.

Hi Gregory,

Here’s a slightly counterintuitive thought - if you decrease your micro-stepping resolution, does that reduce the variation?

It could be that what’s happening is there is insufficient torque form a single micro step to actually move anything, then the torque builds up over several micro-steps until there’s enough torque to move the telescope and it jumps forward - and this cycle is repeating. If so, and you’re photographing on a time lapse you may actually get a better result with larger steps.

Here are a few interesting articles I found:

https://www.geckodrive.com/support/step-motor-basics/accuracy-and-resolution.html

1 Like

Hey Oliver,

Performed a test last night comparing 16 micro steps and 32 microsteps.

32 microsteps = 55% good frames
16 microsteps = 65% good frames.

Seems like you may be onto something oliver. Will try 8 micro steps tonight. However at such slow speed this is really pushing it regarding vibrations for my set up.

3 Likes

First, try a simple change: don’t make the delay in the loop equal for the high and low delays. Set the pin high, delay for the time that the motor requires, then set it low and delay for the remainder to make up the 6032.

Using a delay in the processing loop won’t give you accurate timing, although it’s not clear that is the problem here. The more accurate solution is to use interrupts with a timer library, such as Timer Interrupts | Multi-tasking the Arduino - Part 2 | Adafruit Learning System. Your code doesn’t change much - once you set up the interrupt function it’s just a matter of shifting the stepper code into that function and then making sure that the interrupt is enabled or disabled appropriately.

2 Likes

Tested 8 Micro steps last night with quite poor results. Having said that, the motor had significant vibrations which i understand can cause the motor to miss steps.

Thanks for the info Jeff, reading through it now and can hopefully figure it out and update my sketch! WIll report back shortly.

Morning all,

i’'ve been trying to get the sketch up and running with a timer interrupt but cant seem to get it working/get my head around it. I initially tried to set up the timer interrupt then put my existing code into it but guessing its not that simple because i’ve been getting weird results and the inability to utilize the push buttons for reverse (guessing because its in the setup section rather than loop)?

Would any one be willing to assist with converting my current sketch above to a timer interrupt?

looking forward to your responses.

Greg

Which library are you using? I like this one because it is really easy to use for a timer interval, but you need to be careful that the loop executes more frequently than the interrupt interval. With an interval of 6032 that should not be a problem, but note that serial print at 9600 takes a very long time. This is a cut and paste from your code and the author’s example. It is completely untested so I have no idea whether or not it works, but it shows the structure of an interval timer. If you can explain what the other parts of the code in the loop are doing then I could create a test setup. I would guess that you need to wrap the code inside the if statements with timer stop and timer start instructions, so the timer cannot fire while those if conditions apply.

/*
  || MillisTimer interval timer library 
  || @author         Brett Hagman <bhagman@wiring.org.co>
  || @url            http://wiring.org.co/
  || @license Please see the accompanying LICENSE.txt file for this project.
  || @download https://github.com/bhagman/MillisTimer
*/

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 = HIGH;         // 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 longs 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 = false; 

#include "MillisTimer.h"

// Create a timer that fires every 6032 milliseconds.
MillisTimer timer1 = MillisTimer(6032);

// This is the function that is called when the timer expires.
void myTimerFunction(MillisTimer &mt)
{
  // These four lines result in 1 step:
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(8);
  digitalWrite(stepPin, LOW);
}

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, HIGH);

  timer1.setInterval(6032);
  timer1.expiredHandler(myTimerFunction);
  timer1.setRepeats(0);
  timer1.start();
}

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 the motor to move in a particular direction
      // Makes 200 pulses for making one full cycle rotation
      for (int x = 0; x < 18000 ; x++) {
        digitalWrite(stepPin, HIGH);
        delayMicroseconds(100);
        digitalWrite(stepPin, LOW);
        delayMicroseconds(100);
      }
      itAlreadyHappened = true;
    }
  } else {
    // Set the spinning direction clockwise:
    digitalWrite(dirPin, LOW);
    timer1.run();    // trigger the timer function IF it has expired
    itAlreadyHappened = false;
  }
}

/edit/ Looking at it again I can see that itAlreadyHappened should not be set every time through the loop, but only if the timer actually expired.

1 Like

Thanks Jeff,

I was trying to use one called Timerone, but i had no idea what i was doing.

Unfortunately the sketch didn’t work. I will try my best to explain what everything does in my Sketch. This sketch was the first sketch i’ve ever written and took me quite a while and i cant remember the logic behind everything.

It actually seems a bit backwards as it executes form the bottom (the else statement first).

The system as photographed above consists of the motor and two push button switches which both link to the same Pin.

So when the Arduino is given power the motor turns in reverse DIRpin LOW 1 step every 16 micro seconds. suggesting arduino starts up with the digital read button = HIGH.

 } else {

 // Set the spinning direction clockwise:
digitalWrite(dirPin, LOW);

  
   // These four lines result in 1 step:
   digitalWrite(stepPin, HIGH);
   delayMicroseconds(8);
     digitalWrite(stepPin, LOW);
    delayMicroseconds(8);
 
 itAlreadyHappened = false;

it then continues at this speed and direction until it hits the push button on the opposite end. Triggering the digital read button to low.

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

Now the motor will run for 18000 steps in the forward direction DirPin HIGH one step every 200 microseconds. This only happens once which is the reason for the code of itAlreadyHappened= true or false. this happens so after hitting the push button the platform is moved far enough away from it so it doesn’t trigger it again.

if (itAlreadyHappened == false)
  {
digitalWrite(dirPin,HIGH); // Enables the motor to move in a particular direction
  // Makes 200 pulses for making one full cycle rotation
  for(int x = 0; x < 18000 ; x++) {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(100); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(100); }
    
   itAlreadyHappened = true;}

Once the above is executed the motor will now run continuously in the forward direction at 1 step every 6032 micro seconds. This is the primary tracking. This continues until the push button is hit again re triggering the above else statement rewind.

// These four lines result in 1 step:
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(3016);
  digitalWrite(stepPin, LOW);
delayMicroseconds(3016);

I hope this makes sense and is helpful.

This code works, sort of. I couldn’t replicate the two switches, but I can switch between ‘running’ and ‘resetting’ with one switch, and get a pulse every 6032 ms. The timer you use should work just the same. Whether or not you use an interval timer, things to consider are:

  • The delay for the HIGH pulse in the step. This should be set according to the requirements of the motor controller. Use the minimum that the controller requires - I used 50, but it seemed to work OK down to about 25.
  • Make sure your controller is holding the motor between steps. If the controller is releasing the motor then it might move.

I couldn’t confirm the accuracy of each step in microstepping mode. If you get consistent results in some modes and not in others, then perhaps the motor isn’t suited to those other modes.

I had to use 12 instead of 13 for the button - don’t know why.

int button = 12;        // 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 = HIGH;        // 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 longs 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 = 500;   // the debounce time, increase if the output flickers
unsigned long endTime;

boolean itAlreadyHappened = false; 

#include "MillisTimer.h"

// Create a timer that fires every 6032 milliseconds.
MillisTimer timer1 = MillisTimer(6032);

// This is the function that is called when the timer expires.
void myTimerFunction(MillisTimer &mt)
{
  // These four lines result in 1 step:
  Serial.println("Step");
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(50);
  digitalWrite(stepPin, LOW);
}

void setup()
{
  Serial.begin(9600);
  
  pinMode(button, INPUT_PULLUP);
  pinMode(Buzzer, OUTPUT);
  
  // Declare motor controller 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, HIGH);

  timer1.setInterval(6032);
  timer1.expiredHandler(myTimerFunction);
  timer1.setRepeats(0);
  Serial.println("Setup complete");
  
}

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) {
    Serial.println("Sw Low-->High processing");
    if (state == HIGH)
      state = LOW;
    else
      state = HIGH;
    time = millis();
    Serial.println("State = " + String(state));
  }

  if (state == LOW) {
    if (itAlreadyHappened == false)
    {
      Serial.println("timer stop");
      timer1.stop();
      Serial.println("Resetting");
      digitalWrite(dirPin, HIGH); // Enables the motor to move in a particular direction
      // Makes 200 pulses for making one full cycle rotation
      for (int x = 0; x < 18000 ; x++) {
        digitalWrite(stepPin, HIGH);
        delayMicroseconds(50);
        digitalWrite(stepPin, LOW);
        delayMicroseconds(50);
      }
      itAlreadyHappened = true;
     Serial.println("it Already Happened");
     
     Serial.println("timer start");
     timer1.start();
    }
  } else {
    // Set the spinning direction clockwise:
    digitalWrite(dirPin, LOW);
    timer1.run();    // trigger the timer function IF it has expired
    itAlreadyHappened = false;
    delay(10);
  }
}

Hey Jeff,

Unfortunately this isn’t working either. i’ve taken what you have done and simplified it just to turn the motor and do nothing else and still cant get that working either. Not sure whats holding it back. hmm

#include “MillisTimer.h”

#define stepPin 7

#define dirPin 8

// Create a timer that fires every 1000 milliseconds.

// Create a timer that fires every 1000 milliseconds.
MillisTimer timer1 = MillisTimer(1000);

// This is the function that is called when the timer expires.
void myTimerFunction(MillisTimer &mt)
{

digitalWrite(stepPin, HIGH);

delayMicroseconds(100);

digitalWrite(stepPin, LOW);

}

void setup()
{
Serial.begin(9600);

timer1.setInterval(1000);
timer1.expiredHandler(myTimerFunction);
timer1.setRepeats(0);
timer1.start();
}

void loop()
{
timer1.run();

delay(10);
}

1 Like

Add

pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);

into setup, just to be sure. That code works just fine here, at 1x and 16x microstepping (BT6600, 4017-831 AMP motor at 200 S/R). So unfortunately I have no explanation for the what might be different in your case. You may have to abandon the interval timer and concentrate on the original code. Was there any result after adjusting the High/Low proportion in that original code to make it a pulse instead of a square wave?

thanks so much Jeff, Success! Got It all working after making some changes to the code. i also overlooked that the millisecond timer was still using my microsecond so 6000+ milliseconds was was step every 6 seconds.

Code below incase there are any obvious errors.

Only question is under the mytimerfunction do I have to factor in the delay into the timer for the perstep calculation?
I thought since the timer controlled when the step is fired it wouldn’t matter, but after doing some exaggerated testing it seems to have some effect, although doesn’t correlate with the exact time of the delay.

e.g timer set to 0.01 milliseconds with a delay of 50 microseconds runs quite fast. the same with a 2 second delay will run significantly slower but not at a rate of 2 seconds per step.

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 = HIGH;        // 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 longs 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 = 1000;   // the debounce time, increase if the output flickers
unsigned long endTime;

boolean itAlreadyHappened = false; 

#include "MillisTimer.h"

// Create a timer that fires every 12.413 milliseconds.
MillisTimer timer1 = MillisTimer(12.413);

// This is the function that is called when the timer expires.
void myTimerFunction(MillisTimer &mt)
{
  // These four lines result in 1 step:
  
  digitalWrite(stepPin, HIGH);
  delayMicroseconds(20);
  digitalWrite(stepPin, LOW);
}

void setup()
{
 
  
  pinMode(button, INPUT);
  pinMode(Buzzer, OUTPUT);
  
  // Declare motor controller 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);

  timer1.setInterval(12.413);
  timer1.expiredHandler(myTimerFunction);
  timer1.setRepeats(0);
  timer1.start();

  
}

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 the motor to move in a particular direction
  // Makes 200 pulses for making one full cycle rotation
  for(int x = 0; x < 18000 ; x++) {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(200); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(200); }

    timer1.start();
    
         itAlreadyHappened = true;}


  
 
  timer1.run();

      


} else {
 
  // Set the spinning direction clockwise:
  digitalWrite(dirPin, LOW);

  timer1.stop();

  
  
    // These four lines result in 1 step:
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(16);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(16);
  
 
  
   itAlreadyHappened = false;
  
}
 
  }