Constant stepper rotation, smooth acceleration with AccelStepper library

I am having trouble with the seemingly simple task of getting a stepper motor to rotate constantly with no jumps or jitters.

What I would like to happen: The motor sits at rest until a button is pressed. Once the button is pressed, the motor spins up to speed with a very smooth acceleration profile (rates and speed easily adjustable in code). The motor spins indefinitely until I press the button once again, where it follows a smooth deceleration profile down to rest.

I have commented out the LCD functionality because it appears to introduce serious code hangups that cause non-smooth rotation. (question for another day?)

I tried to program the ability to change the speed on the fly with two additional buttons (speed increment and speed decrement). I will play with these once the overarching problem of just spinning the motor has been solved.

The code is pretty non-functional, I have been losing track of what works together in this library. All of the constant speed examples I have been able to find do not implement accelerations.

// LIBRARIES
#include <Bounce.h> // debouncing
#include <serLCD.h> // serial LCD from SparkFun
#include <SoftwareSerial.h> // Software Serial (for LCD)
#include <AccelStepper.h> // accelleration based stepper library
#include <Wire.h> // ????????

// Variables
int MotorRun = 0; // TRUE or FALSE for if the mnotor is moving or not
int MotorSpeed = 0; // integer RPM (rotations per minute)

int SetMotorSpeed = 1600; // The motor speed that is desired (SETPOINT)

int RateInc = 5; // Amount (in RPMs) that the motor speed is incremented each time a decrement button is pressed
int StartUp = 1; // TRUE or FALSE if it is starting up
int MaxAccel = 1000; // Used by AccelStepper for accelleration

// INPUTS
#define PowerButt 2 // On/Off button
#define IncButt 3 // increment button
#define DecButt 4 // decrement button

Bounce bouncerON = Bounce( PowerButt,5 ); // debouncing
Bounce bouncerINC = Bounce( IncButt,1 );
Bounce bouncerDEC = Bounce( DecButt,1 );

// OUTPUTS
#define LitePin 5 // pin for light-up button LEDs
#define DisplayPin 6 // pin for Rx on lcd display

// AccelStepper
#define DIR_PIN 7 // direction pin on Big EasyDriver from SparkFun
#define STEP_PIN 9 // Step pin on Big EasyDriver
AccelStepper stepperB(1, STEP_PIN, DIR_PIN);

// Lcd Screen
serLCD lcd(DisplayPin);


void setup() {
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(PowerButt, INPUT);
  pinMode(IncButt, INPUT);
  pinMode(DecButt, INPUT);
  
  Serial.begin(38400);
  
  lcd.clear(); lcd.print("WELCOME"); delay(1000);

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
  
  bouncerON.update ( );
  int valuePow = bouncerON.read(); // read the power button
  
  if (valuePow == LOW) { // button is pressed (low because of PULLLUP resistor)
    if (MotorRun == 0) { // motor is off
      
      MotorRun = 1; // turn on the motor
      
      stepperB.setMaxSpeed(1600);
      stepperB.setAcceleration(100);
    }
    
    else {
      MotorRun = 0; // turn off the motor
    }
  }
    
  if (MotorRun == 1) { // are we supposed to be spinning???????????????????????????????????????????????????????????????????????????????????
  
    digitalWrite(LitePin, HIGH); // Leds on buttons are on
    //lcd.clearLine(2); lcd.print("RPM = "); lcd.print(SetMotorSpeed);  
    
    stepperB.run();
  }
  // stop rotating smoothly
  else { // if motor has been turned off, slow it down gradually ???????????????????????????????????????????????????????????????????????????????
  
    digitalWrite(LitePin, LOW); // Leds on buttons are off
    //lcd.print("Motor OFF"); lcd.selectLine(2); lcd.print("set RPM "); lcd.print(SetMotorSpeed); lcd.selectLine(1);
    
    stepperB.setMaxSpeed(0); // stop the motor
    stepperB.setAcceleration(MaxAccel);
    stepperB.run();
  
    }
  
  bouncerINC.update ( );
  int valueInc = bouncerINC.read(); // read the power button
  bouncerDEC.update ( );
  int valueDec = bouncerDEC.read(); // read the power button
  
  if (valueInc == LOW || valueDec == LOW) { // if either of the increment buttons have been pressed
    if (valueInc == LOW) {
      SetMotorSpeed = SetMotorSpeed + RateInc;
    }
    else {
      SetMotorSpeed = SetMotorSpeed - RateInc;
    }
  }
  
  Serial.println(MotorRun);
  
  
} // end of LOOP

brian15co,
I don't know the <AccelStepper.h> library, so take my comments with a pinch of salt.

  1. Don't you choke the Loop() by putting a Serial.println there? .. It would be good to have Serial.print in ie the button-events instead.

  2. I'll 'guess' that you set
    stepperB.setMaxSpeed(0); // stop the motor
    too early (if you want decelleration to kick in .. & shouldn't 'acelleration' be a negative value ?). Anyway, such a command ought to stop the stepper promptly.

  3. If you employ your own routine of setting speed by adding your own speed-increments, then the library is superflous .. You should choose which of the two routine to use.

Hey Carsten,

addressing point 3), I would like to be able to change the speed while the motor is running, although I want the speed transitions to be smooth (employ acceleration). This is why I feel the library will help me get there, If I can ever figure it out!

Hello Everyone, I solved the issue with a little bit of trickery. I tell the motor to spin up smoothly to a location and once the accelleration has been completed I switch loops so that it is spinning to a location very far away (psuedo-indefinite spinning). When I press the button to stop it, it starts to decellerate smoothly to a stop. If this description was as confusing to you as it was to me, check the code.

// 
// -*- mode: C++ -*-
//
// Running the motor smoothly with accelstepper library
// 6-11-2012
// fixed button issue after 5!

#include <AccelStepper.h>
#include <Bounce.h> // debouncing

// INPUTS
#define PowerButt 2 // On/Off button
#define IncButt 3 // increment button
#define DecButt 4 // decrement button

Bounce bouncerON = Bounce( PowerButt, 50 ); // debouncing
Bounce bouncerINC = Bounce( IncButt, 1 );
Bounce bouncerDEC = Bounce( DecButt, 1 );

// OUTPUTS
#define LitePin 5 // pin for light-up button LEDs
#define DisplayPin 6 // pin for Rx on lcd display

#define DIR_PIN 7 // direction pin on Big EasyDriver from SparkFun
#define STEP_PIN 9 // Step pin on Big EasyDriver
AccelStepper stepperB(1, STEP_PIN, DIR_PIN);


// Variables
int MotorRun = 0; // TRUE or FALSE for if the mnotor is moving or not
int MotorSpeed = 0; // integer RPM (rotations per minute)

int SetMotorSpeed = 3200; // The motor speed that is desired (SETPOINT)

int RateInc = 5; // Amount (in RPMs) that the motor speed is incremented each time a decrement button is pressed
int StartUp = 1; // TRUE or FALSE if it is starting up
int MaxAccel = 1000; // Used by AccelStepper for accelleration

int count = 0; // counter for debuggggg
int buttonTime = millis();

void setup() {
  pinMode(DIR_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(PowerButt, INPUT);
  pinMode(IncButt, INPUT);
  pinMode(DecButt, INPUT);
  
  Serial.begin(115200);
  Serial.flush(); 

}

void loop() { // ********************************************************************************
  bouncerON.update ( );
  int valuePow = bouncerON.read(); // read the power button
  
  if (valuePow == LOW) { // button is pressed (low because of PULLLUP resistor)
    if (MotorRun == 0 && bouncerON.fallingEdge() == true) { // motor is off
      
      MotorRun = 1; // turn on the motor
      
      stepperB.setMaxSpeed(SetMotorSpeed);
      stepperB.setSpeed(SetMotorSpeed);
      stepperB.setAcceleration(100);
      stepperB.moveTo(1000000);
      while(stepperB.currentPosition() != 50000){
         stepperB.run(); }
      stepperB.setCurrentPosition(0);
      stepperB.moveTo(1000000);
      stepperB.setSpeed(SetMotorSpeed);
     
    }
    
    else if(MotorRun == 1 && bouncerON.fallingEdge() == true) { // motor is off 
      MotorRun = 0; // turn off the motor
      stepperB.setCurrentPosition(0);
      stepperB.setAcceleration(100);
      stepperB.moveTo(60000);
      stepperB.setSpeed(SetMotorSpeed);
    }
  }

stepperB.run();

count++;

//Serial.print(count); Serial.print(" "); Serial.print(MotorRun); Serial.print(" "); Serial.println(stepperB.distanceToGo());

} // END

hello Brian

first of all i want to thank you for your code that help me a lot with my project.

please i need your help.

im using Accelstepper to control 2 stepper motors with 1 arduino UNo and 2 driver DRV8825.

i can accelerate both motor smoothly to a maximum speed and let both run in constant speed for a while. (copied and edited from your code)

after that id like both motor to stop with smooth deceleration. unfortunately only one motor decelerate and the other motor stopped immediatly without deceleration.

i think the stop() function will block the next code before it finish its work...

do you have any solution?

sorry to bother you and i hope u can help me.

thanks

here is my code:-

#include <AccelStepper.h>

// Define a stepper and the pins it will use

int SetMotorSpeed = 2000;
AccelStepper stepperA(1, 9, 8 );
AccelStepper stepperB(1, 3, 4 );

void setup()
{  
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
}

void loop()
{
 
  stepperB.setCurrentPosition(0);
  stepperA.setCurrentPosition(0);
      
  stepperB.setMaxSpeed(SetMotorSpeed);
  stepperA.setMaxSpeed(SetMotorSpeed);

      stepperB.setAcceleration(100);
      stepperA.setAcceleration(100);
      stepperB.moveTo(1000000);
      stepperA.moveTo(1000000);
     
      while((stepperB.currentPosition()) != 50000){
      stepperB.run();
      stepperA.run();}
      
     stepperB.stop();
     stepperA.stop();
     stepperB.runToPosition(); 
     stepperA.runToPosition();
     
}

@fiera11235, you are asking a question on a 2-year old Thread.

And you did not read the How to use the forum which explains how to put your code in code tags to make it easy for people to read.

I haven't read any of the old stuff in this Thread.

This piece of code

while((stepperB.currentPosition()) != 50000){
      stepperB.run();
      stepperA.run();}

suggests to me that you don't understand how AccelStepper works.

In general you tell AccelStepper where you want the motor to go and then every time you call stepper.run() it makes a step subject to 2 conditions - ( A ) it is time to make a step and ( B ) it has not yet reached its destination. These conditions are internal to the Library and you don't see them.

That means that the code in loop() should be designed to be quick and every iteration of loop() should call stepper.run() Using WHILE just gets in the way of everything.

Doing it the correct way also means that every iteration of loop can test whether a button is pressed and can alter the destination or speed as required.

...R

@fiera11235, you are asking a question on a 2-year old Thread.

2 years old thread. So what?? Its relevant to my Project.

And you did not read the How to use the forum which explains how to put your code in code tags to make it easy for people to read.

Oh sorry about that my dear..

Yada Yada Yada.... Bla bla bla.... So where is your solution?? Only general information???

U have not read the old thread? So how do you know what I'm talking about?

Using WHILE just gets in the way of everything.

I asked the same question at accelstepper-group and the author of accelstepper himself gave me suggestion ie solution using WHILE.

this piece of code suggests to me that you don't understand how AccelStepper works.

If I understand I wouldn't ask.

U don't have to answer if don't like the question.

Btw I didn't ask u, I asked Brian... So no thank you for your yada yada yada...

fiera11235:
Btw I didn't ask u, I asked Brian... So no thank you for your yada yada yada...

I find your attitude very offensive in response to a genuine attempt to help you.

I will report the matter to the moderator.

...R

Yep... it's an old thread, but as of this morning, I knew nothing about AccelStepper, either, and spent a chunk of the day reading whatever I could. Turns out I learned a fair bit by experimenting, but I couldn't find the answers to what I was trying to do, so thought I'd share in case it's useful.

Basically, I needed 2-axis movement with smooth accel and decel. I'm making a gantry that carries video gear, and jerky movement doesn't cut it.

Here's what I came up with... works absolutely like a champ, and there's no harm at all in your approach to using the while() loop.
Hope it helps.

/*

Control of a 2-axis gantry using 4 buttons

- all steppers controlled by Big Easy Drivers
- (2) steppers on the X-axis... X2 is mounted in reverse so we run it in reverse
- AccelStepper library makes for smooth accel / decel
- (4) user navigation buttons : up/down, left/right. Code is blocking... we don't care about diagonal moves, and don't have t
   deal with UP / DOWN fighting each other this way.
- limit switches on both axes
- running on a MEGA 2560

*/

#include <AccelStepper.h>

const int MX1_Step = 10;                      // Big Easy Driver pins
const int MX1_Dir = 11;
const int MX2_Step = 7;
const int MX2_Dir = 9;
const int MX_Enable = 22;
const int MY1_Step = 12;
const int MY1_Dir = 13;
const int MY_Enable = 23;

const int navUp = 47;                          // Navigation buttons
const int navDown = 51;
const int navLeft = 53;
const int navRight = 49;

AccelStepper X1(1, MX1_Step, MX1_Dir);          
AccelStepper X2(1, MX2_Step, MX2_Dir);
AccelStepper Y1(1, MY1_Step, MY1_Dir);

const int maxSpeed = 800;
const int accel = 600;
const int xLimit = 6500;                      // Right end of X axis
const int yLimit = 1800;                      // Back/far end of Y axis

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

  pinMode(navUp, INPUT_PULLUP);  
  pinMode(navDown, INPUT_PULLUP);    
  pinMode(navLeft, INPUT_PULLUP);    
  pinMode(navRight, INPUT_PULLUP);
  pinMode(MX_Enable, OUTPUT);
  pinMode(MY_Enable, OUTPUT);
  
  digitalWrite(MX_Enable, HIGH);                  // Active LOW.... disable motors to start
  digitalWrite(MY_Enable, HIGH);                  // Active LOW.... disable motors to start
  
  X1.setMaxSpeed(maxSpeed);
  X2.setMaxSpeed(maxSpeed);
  Y1.setMaxSpeed(maxSpeed);

  X1.setAcceleration(accel);
  X2.setAcceleration(accel);
  Y1.setAcceleration(accel);
  
  X2.setPinsInverted(true, false, false);            // Dir, Step, Enable : X2 mounted reverse, so opposite rotation

  X1.setCurrentPosition(0);
  X2.setCurrentPosition(0);
  Y1.setCurrentPosition(0);

  Serial.println("Setup complete");
}
/************************************************************************************************************/
void loop(){
  if (!digitalRead(navUp)){            // once we see the first button press....
    Y1.moveTo(yLimit);                 // set the Target
    if (Y1.distanceToGo()!=0){            // and if we need to
      moveUp();                        // then move the motor
    }
  }  
  if (!digitalRead(navDown)){
    Y1.moveTo(0);
    if (Y1.distanceToGo()!=0){
      moveDown();
    }
  }
  if (!digitalRead(navRight)){
    X1.moveTo(xLimit);
    X2.moveTo(xLimit);
    moveRight();
  }
  if (!digitalRead(navLeft)){
    X1.moveTo(0);
    X2.moveTo(0);
    moveLeft();
  }
}  

/************************************************************************************************************/
void moveUp() {
  digitalWrite(MY_Enable, LOW);            // Enable the Big Easy Driver motor outputs
  while (!digitalRead(navUp)&&Y1.distanceToGo()!=0){             // Keep looping here as long as the button's held down
    Y1.run();
  }                                        // ... or the button was let go
  Y1.stop();                               // tell AccelStepper to decel to a smooth stop ASAP, if it's not at the Target
  while (Y1.distanceToGo()!=0){             // loop until you get to the new decel target
    Y1.run();
  }
  digitalWrite(MY_Enable, HIGH);            // disable the BED to save motor current (keep motors cool.... no load)
}
 
void moveDown() {
  digitalWrite(MY_Enable, LOW);
  while (!digitalRead(navDown)&&Y1.distanceToGo()!=0){           
    Y1.run();
  }
  Y1.stop();    
  while (Y1.distanceToGo()!=0){           
    Y1.run();
  }  
  digitalWrite(MY_Enable, HIGH);            
}
 
void moveLeft() {
  digitalWrite(MX_Enable, LOW);            
  while (!digitalRead(navLeft)&&X1.distanceToGo()!=0){           
    X1.run();                              // Remember we have 2 motors to run on the X axis
    X2.run();
  }  
  X1.stop();                               
  X2.stop();
  while (X1.distanceToGo()!=0){             
    X1.run();
    X2.run();
  }
 digitalWrite(MX_Enable, HIGH);            
 }
 
 void moveRight() {
  digitalWrite(MX_Enable, LOW);            
  while (!digitalRead(navRight)&&X1.distanceToGo()!=0){          
    X1.run();
    X2.run();
  }  
  X1.stop();                               
  X2.stop();
  while (X1.distanceToGo()!=0){             
    X1.run();
    X2.run();
  }
 digitalWrite(MX_Enable, HIGH);            
 }

Feel free to shoot questions. I could post a YouTube video of the system if that helps.
Cheers -
Todd

thank you tlharris....

your code seem good....

  X1.stop();                               
  X2.stop();
  while (X1.distanceToGo()!=0){             
    X1.run();
    X2.run();

btw I already got the solution of my problem. thanks to Mr. Mike McCauley, the author of Accelstepper.

so here is the the solution:-

#include <AccelStepper.h>

int SetMotorSpeed = 2000;
AccelStepper stepperA(1, 11, 8);
AccelStepper stepperB(1, 3, 4);

void setup()
{  
  pinMode(8, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
}

void loop()
{
 stepperB.setCurrentPosition(0);
 stepperA.setCurrentPosition(0);
      
 stepperB.setMaxSpeed(SetMotorSpeed);
 stepperA.setMaxSpeed(SetMotorSpeed);

 stepperB.setAcceleration(100);
 stepperA.setAcceleration(100);
  
 stepperB.moveTo(1000000);
 stepperA.moveTo(1000000);
     
  while((stepperB.currentPosition()) != 50000){
   stepperB.run();
   stepperA.run();}

      
    stepperB.stop(); 
    stepperA.stop(); 

while (stepperA.run() && stepperB.run()) ;
   
}

thank you very much. While() indeed :stuck_out_tongue:

Brilliant... thanks for sharing that back....
I hadn't realized the return value of stepper.run().... that makes for an even more efficient loop check.

while (stepper.run());

Perfect! Thanks!

@tlharris

while (stepper.run());

yeah baby!

glad that it helps you...

cheers
-fiera

yes, i know this is an "ancient" thread.
my question goes to when using a "while()" function, isn't the loop stationary at that while statement until the statement passes? therefore, when you tell a stepper to go to a place, say 3000 steps away, the program will halt there (and the motor will turn) until it reaches it 3000 steps?
a lot of programs don't want to work that way - they want to be reading other sensors, or play sounds, or receive other commands from a controller.

am i wrong in this, or what?

Russ from Coral Springs, Fl. usa

rholt:
yes, i know this is an "ancient" thread.
my question goes to when using a "while()" function, isn't the loop stationary at that while statement until the statement passes? therefore, when you tell a stepper to go to a place, say 3000 steps away, the program will halt there (and the motor will turn) until it reaches it 3000 steps?
a lot of programs don't want to work that way - they want to be reading other sensors, or play sounds, or receive other commands from a controller.

am i wrong in this, or what?

Russ from Coral Springs, Fl. usa

If you are looking for advice for your own project I suggest you start your own Thread and post the code you are having trouble with.

If you are offering advice for this Thread then I reckon the party is long over and the beer is all drunk. Indeed the partygoers have probably even had time to sober up.

...R