Stepper Motor Objects and millis()

So I've been trying to write my own object oriented approach to controlling a stepper motor with an Arduino Nano (I know there are premade libraries, but I prefer writing my own code).

In the attached Stepper_Motor_Class header file, I defined a method that calls a singlestep() function every X number of seconds, and it does this for Y number of steps. This code works absolutely fine when X is 1000 milliseconds (ie it calls the singlestep() function every 1000 milliseconds for however many steps (Y) I want).

However, the instant I reduce that time to 5 milliseconds (or 50 or 100 etc) the step counter breaks down. Instead of going say 1500 steps, it jumps out of the while loop after only doing usually 28.

I am at my wits end with figuring out why it is doing this. Can anyone help me please?

Object_Oriented_Stepper.ino (348 Bytes)

Stepper_Motor_Class.h (2.97 KB)

Sorry dont know how to upload code :o

#include "Stepper_Motor_Class.h" //class for stepper motor

const char MOTOR_PIN1 = 6;
const char MOTOR_PIN2 = 5;

Stepper_Motor stepper (MOTOR_PIN1, MOTOR_PIN2);

//Initialize objects with begin method
void setup() {
Serial.begin(9600);
stepper.begin();
stepper.stepsconstantspeed(50,1000);//put this in setup to debug
}

void loop() {
}

Header file:
#include "Arduino.h"

class Stepper_Motor
{
public:
Stepper_Motor (int pin, int pin2);
Stepper_Motor (unsigned long RPM, double steps);
void begin();
void singlestep();
void stepsconstantspeed (unsigned long _RPM, double _steps);

private:
int _pin_status;
int _pin2_status;
int _done;
double _steps;
int _timer;
unsigned long _RPM;
int _counter;
int _pin;
int _pin2;
unsigned long _startpoint;
};

//Constructor
Stepper_Motor::Stepper_Motor (int pin, int pin2)
{
_pin = pin;
_pin2 = pin2;
}

Stepper_Motor::Stepper_Motor (unsigned long RPM, double steps)
{
_RPM = RPM;
_steps = steps;
}

//**********************************************************************************
//**********************************************************************************

void Stepper_Motor::begin()//this method initializes pins and variables for stepper function
{
pinMode(_pin, OUTPUT);//set pin to output
pinMode(_pin2, OUTPUT);
digitalWrite(_pin, LOW);
digitalWrite(_pin2, LOW);
_pin_status = 0;
_pin2_status = 0;
_counter = 0;
_done = 0;

}

void Stepper_Motor::singlestep()//this method moves stepper forward according to sequence
{
if (_pin_status == 0 && _pin2_status == 0 && _done == 0)
{
digitalWrite(_pin, LOW);
digitalWrite(_pin2, HIGH);
_pin_status = 0;
_pin2_status = 1;
_done = 1;
}

if (_pin_status == 0 && _pin2_status == 1 && _done == 0)
{
digitalWrite(_pin, HIGH);
digitalWrite(_pin2, HIGH);
_pin_status = 1;
_pin2_status = 1;
_done = 1;
}

if (_pin_status == 1 && _pin2_status == 1 && _done == 0)
{
digitalWrite(_pin, HIGH);
digitalWrite(_pin2, LOW);
_pin_status = 1;
_pin2_status = 0;
_done = 1;
}

if (_pin_status == 1 && _pin2_status == 0 && _done == 0)
{
digitalWrite(_pin, LOW);
digitalWrite(_pin2, LOW);
_pin_status = 0;
_pin2_status = 0;
_done = 1;
}

_done = 0;
}

void Stepper_Motor::stepsconstantspeed (unsigned long _RPM, double _steps)//this method moves motor x steps at constant speed
{
_startpoint = millis();
Serial.print (_startpoint);

while (millis() <= _startpoint + _RPM)//constant speed loop
{
if (millis() == _startpoint + _RPM)//will execute step every _RPM milliseconds
{
if (_counter <= _steps)//exits while loop when counter = steps
{
singlestep();
_startpoint = millis();
Serial.print (_startpoint);
_counter ++;
Serial.print ("Step");
Serial.print (_counter);
}
}
}

_counter = 0;

Serial.print ("Im done");
Serial.println();
}

Sorry dont know how to upload code

Suggested reading for you : read this before posting a programming question

Bill_Strathern:
In the attached Stepper_Motor_Class header file, I defined a method that calls a singlestep() function every X number of seconds, and it does this for Y number of steps. This code works absolutely fine when X is 1000 milliseconds (ie it calls the singlestep() function every 1000 milliseconds for however many steps (Y) I want).

what exactly are you trying to do? are you trying to move more steps than for which you have the time?

what exactly are you trying to do? are you trying to move more steps than for which you have the time?

Basically, I am trying to get the stepper motor to rotate at a constant speed. What the function is supposed to do is make the motor perform 1 step every 5 or 1000 or 25 milliseconds etc. This the function does.

However, I cannot have the stepper motor rotate at a constant speed forever in a loop. Instead, I want it to rotate at a constant speed for only 12000 or 2500 or 5600 steps etc. That's why I built a counter into the loop that is supposed to make the program exit the loop the instant it equals the number of steps the motor is supposed to rotate.

This counter doesn't always seem to work though. Sometimes it successfully makes the motor turn the number of steps its supposed to. Other times it exits the loop after only completing 28 (or some other arb number) steps.

I am pretty sure the logic is fine. Could this be a variable type or scope error?

Bill_Strathern:
Basically, I am trying to get the stepper motor to rotate at a constant speed. What the function is supposed to do is make the motor perform 1 step every 5 or 1000 or 25 milliseconds etc. This the function does.

and you are happy with a blocking function or are you looking to eventually make this function non-blocking?

and you are happy with a blocking function or are you looking to eventually make this function non-blocking?

For the purposes of the code, I think its fine just to have a blocking function. But not very familiar with blocking/nonblocking.

How would it affect the function of the code/address the problem?

Bill_Strathern:
How would it affect the function of the code/address the problem?

well, if you want to do anything else whilst your motor is turning, you will not want blocking code preventing the execution of other parts of your sketch.

and you are happy with a blocking function or are you looking to eventually make this function non-blocking?

I see...how would you recommend making it non-blocking?
I'm not quite sure how this will fix the problem with the function not counting properly though?

Bill_Strathern:
I see...how would you recommend making it non-blocking?
I'm not quite sure how this will fix the problem with the function not counting properly though?

If you want a constant speed, then why do you need a counter at all? You merely have to be concerned about the last time you moved your motor.

  while (millis() <= _startpoint + _RPM)//constant speed loop

This is a common mistake, trying to set a future time and waiting for millis() to get there. Maybe you should look at the millis() tutorial that is the first post in this forum.

This is a common mistake, trying to set a future time and waiting for millis() to get there.

This part of the method seems to work fine though. It calls the singlestep() function at the exact intervals it is supposed to. The problem is that the loop usually ends early. Surely that is some problem with the imbedded counter function rather than the "timer"?

Please comply with reply #2 before we go on.

Bill_Strathern:
This part of the method seems to work fine though. It calls the singlestep() function at the exact intervals it is supposed to. The problem is that the loop usually ends early. Surely that is some problem with the imbedded counter function rather than the "timer"?

take a look at this non-blocking method to move the motor (compiled but not tested):

enum MotorDirection{
  STOP,
  FORWARD,
  REVERSE,
};

class StepperMotor{
  public:
    StepperMotor(int pin0, int pin1){
      stepperPin0 = pin0; stepperPin1 = pin1;
      stepIndex = 0;
    }

    void begin(){
      pinMode(stepperPin0, OUTPUT);
      pinMode(stepperPin1, OUTPUT);
      step();
    }

    void setSpeed(uint16_t updateInterval){
      interval = updateInterval;
    }

    void setDirection(MotorDirection dir){
      state = dir;
    }

    void update(void){
      if (state != STOP)
      {
        if (millis() - lastMillis > interval)
        {
          lastMillis = millis();
          if (state == FORWARD)
            step();
          else
            step(false);
        }
      }
    }
    
  private:
    
    MotorDirection state = STOP;
    
    void step(bool forward = true){
      if (forward)
      {
        stepIndex++;
        stepIndex %= 4;
      }
      else
      {
        stepIndex--;
        if(stepIndex < 0)
          stepIndex = 3;
      }
      digitalWrite(stepperPin0, pulseSequence[stepIndex] & 0b01);
      digitalWrite(stepperPin1, pulseSequence[stepIndex] & 0b10);
      //Serial.println(pulseSequence[stepIndex], BIN);
    }
    const byte pulseSequence[4] = {
      0b00,
      0b01,
      0b11,
      0b10
    };
    int8_t stepIndex;
    bool reverse;
    uint8_t stepperPin0;
    uint8_t stepperPin1;
    uint32_t lastMillis;
    uint16_t interval = 10;
};

StepperMotor stepper(2,3);

void setup() 
{
  Serial.begin(9600);
  stepper.begin();
  stepper.setSpeed(50);
}

void loop() 
{
  stepper.update();

  static uint32_t startMillis = 0;
  static int motion = 0;
  if (millis() - startMillis > 5000)
  {
    startMillis = millis();
    motion ++;
    motion %= 4;
    if(motion == 0)
    {
      Serial.println(F("STOP"));
      stepper.setDirection(STOP);
    }
    else if (motion == 1)
    {
      Serial.println(F("FORWARD, faster"));
      stepper.setSpeed(50);
      stepper.setDirection(FORWARD);
    }
    else if(motion == 2)
    {
      Serial.println(F("STOP"));
      stepper.setDirection(STOP);
    }
    else if (motion == 3)
    {
      Serial.println(F("REVERSE, slower"));
      stepper.setSpeed(100);
      stepper.setDirection(REVERSE);
    }
  }
}

Have a look at the second example in this Simple Stepper Code to see an approach to non-blocking movement.

Compared to my few lines of code your .h file looks very complicated. :slight_smile:

Why is your variable _steps a floating point value? I have not yet seen a stepper motor that can do a fraction of a step.

Also, I don't see why you need to bother with RPM. Just use a calculator to work out the equivalent number of steps per second, or, better still, microseconds (or millisecs) between steps.

...R