AccelStepper library, stop() function

I'm making an overhead stirrer for a hobby laboratory. I have chosen to use a stepper motor for propulsion over a DC one, to avoid sparks from commutators that can be detrimental when working with volatile fluids. Boom, fire, bad.

I'm however new to stepper motor control and have been trying out a lot of different controls and drivers to find the most suitable solution. I ended up with:

  1. Arduino UNO board
  2. AccelStepper library
  3. Classic A4899 driver (tried TMC2209, but it was too slow even though it was BEAUTIFULLY quiet ...).
  4. NEMA 17 1,5A stepper which I power with a 20 V 1,5A switched power supply decoupled with a suitable capacitor.

It all works great in my first basic trials with potentiometer control, except I can't get the acceleration/deceleration to work. When stirring viscous mixtures one really want the acc&decelleration to be really slow, especially at high speeds.
I use 1/8 hardwired micro stepping to smoothen motion a bit. Along with the capacitor and a careful setting of peak current the motion is now satisfactory free from vibrations. I've tried different speed settings, and with the suitable micro steps a maximum speed of 3000 steps/s produces a sufficient top stirring speed. Any speed above this creates jagged results. I can't however get it to decelerate at any speed.

See my trial code below. I run the stepper at a speed set by a potentiometer, and then simulate a panic stop by a pushbutton through the stop()- function in the library. From the available documentation (available here and here, also see the stop()-function text cited below) it is my understanding that the motor is supposed to decelerate with the previously set acceleration value (in steps/s/s) to a full stop when calling this function. This is not what happens. It comes to an instant halt and then makes slow, jagged steps for a while before stopping completely.

What am I doing wrong? I know I latch the code into a while(1)-loop after calling the function, but this is just for the trial to avoid any new commands after the calling of stop(), and whichever way I do this ends in the above result.

I guess I just don't understand the function properly, but at this point I really need help if someone can spare their time and experience!

#include <AccelStepper.h>
int potVal;
int potPin = A0;
int stopBut = 7;

AccelStepper stepper(AccelStepper::DRIVER,3,4); 

void setup()
{  stepper.setMaxSpeed(3000);
   stepper.setAcceleration(1000.0);
   pinMode(potPin,INPUT);
   pinMode(stopBut,INPUT);
}

void loop()
{  potVal = analogRead(potPin);
   int spdVal = map(potVal, 0,1023,0,3000);
   stepper.setSpeed(spdVal);
   stepper.runSpeed();
   
   if(digitalRead(stopBut)){
      stepper.stop();
      while(1);
      }
}

Notes on stop() function from documentation:

> Sets a new target position that causes the stepper to stop as quickly as possible, using the current speed and acceleration parameters.
*> *
> References move().

It looks okey. Acceleration == 1000 looks like much.
Trying to get speed using an UNO, 4000 is the max of speed and acceleration 4000 as well.
One question about the stop button: How is it wired? The suggestion is to use INPUT_PULLUP, not only INPUT, and the button wired between the digital input and GND. This is not the answer to the slow down problem.

Other, experienced helpers, often suggest the MoobaTools (spelling?) library as it is considered to be easier to get hold of.
Does the stop function return directly or does it hang the execution until no rotation? Holding the button down until the stepper stops would tell.

I am one of those that beleive that the MobaTools stepper library ois easier to learn and use than AccelStepper. The MobaTools library is available for installation via the IDE library manager

How is the button switch wored? Please post a schematic.

Great, I will look into that library. Provided it has decent documentation I'm sure I will swap. I really can't understand why all of these libraries lack a simple "these are the functions, here's what they do and here's the arguments for them"- manual? It's amazing.

I can absolutely post a schematic, even though it's nothing spectacular. The button is wired with an external 2k pulldown resistor to ground and a capacitor for debounce (tried delay() debounce but I was unsure if that was what caused my problems). Then just +5V from the Arduino to pin 7 when pushed.

But why is the wiring of the push button important? Please explain your thinking?

Oh, I tried all kinds of values on both acc and max speed. These are just the latest in a long series of trial and errors.
And as for the stop() function returning- I would think so, yes. If I uncomment the while(1) - part there's just a minor tick in the spin, so the function doesn't latch the code. If I hold the key down nothing really happens.

Indeed :wink:. The main problem with Accelstepper is creating the step pulses, because this must be done in loop(). There are two main functions that create pulses ( apart from blocking functions ): runSpeed() and run(). runSpeed() doesn't implement acceleration/deceleraton - it is mainly to turn the motor at a constant speed. So run() is the function you must use if you want acc/dec.
The stop function:

But that requires that step pulses are generated after calling the stop() function.

After stop no pulses are generated anymore - the motor stops immediately.

Main advantage of the MobaTools library is, that step pulses are generated by timer interrupts. You don't need to care for creating them in loop(). If needed they are even created during delay() or a while(1);

Here you can find the documentation of MobaTools. It will also be copied to your library folder if you install it via library manager.

1 Like

THANK YOU! This clarification was just was I was looking for, and what I had suspected. So I did try to implement run() after the call for stop(), but it didn't work anyway. Instead of continue to bang my head against this library I will immediately (as soon as I have time, that is) start exploring the MobaTools library.

My issue, I think, is that the library is designed primarily to use the function of the stepper motor as it is intended: to run the motor to a certain position, and that all the perks of the library is designed around this function. I, however, try to use the stepper motor as one would a DC motor: running it at constant/variable speeds in different directions, and then I want the acceleration/deceleration functions too. The AccelSteppers different calls for position, speed and actual implementation are extremely confusing. Let's see if MobaTools fits my needs better.

How did you do that? run() will at max create one step pulse if one is due. So only calling run() once will not help. With your test sketch above you must call the run() function in your while loop:

      stepper.stop();
      while(1) { run(); }

Yes, but that's also true with MobaTools. If that will really be a problem depends on how long you will turn your stepper without resetting. The furthest position is 2.147.483.647. With 3000 steps/sec that means about 198 hours of moving.
With MobaTools you can use the rotate() methods, which automatically set this maximum target.

I did it like this (just as in your suggestion):


#include <AccelStepper.h>
int potVal;
int potPin = A0;
int stopBut = 7
AccelStepper stepper(AccelStepper::DRIVER,3,4); 

void setup()
{  stepper.setMaxSpeed(3000);
   stepper.setAcceleration(1000.0);
   pinMode(potPin,INPUT);
   pinMode(stopBut,INPUT);
}

void loop()
{  potVal = analogRead(potPin);
   int spdVal = map(potVal, 0,1023,0,3000);
   stepper.setSpeed(spdVal);
   //Serial.println(stepper.speed());
   stepper.runSpeed();
   
   if(digitalRead(stopBut)){
      stepper.stop();
      while(1){
        stepper.run();
        }
      }
}

The result is: upon button press, the motor stops immediately, then accelerate up to a certain speed, and then decelerate to a full stop. Very interesting.

I think the problem is the mix of runSpeed()/setSpeed() and run() and that you don't set any target position. runSpeed/setSpeed is intended for turning the motor at a constant speed without acceleration/deceleration. run() is a higher level functionality which implements acceleration/deceleration and uses runSpeed and setSpeed internally. Therefore I don't think it's a good idea to mix them. If you want acceleration/deceleration stick to setMaxSpeed() and run() and don't use setSpeed() and runSpeed() at all.

1 Like

You can try this for testing:

#include <AccelStepper.h>
int potVal;
int potPin = A0;
int stopBut = 7
AccelStepper stepper(AccelStepper::DRIVER, 3, 4);

void setup()
{ stepper.setMaxSpeed(3000);
  stepper.setAcceleration(1000.0);
  pinMode(potPin, INPUT);
  pinMode(stopBut, INPUT);
  stepper.moveTo( 0x07ffffff );
}

void loop()
{ potVal = analogRead(potPin);
  int spdVal = map(potVal, 0, 1023, 0, 3000);
  stepper.setMaxSpeed(spdVal);
  //Serial.println(stepper.speed());
  stepper.run();

  if (digitalRead(stopBut)) {
    stepper.stop();
    while (1) {
      stepper.run();
    }
  }
}
1 Like

Yes, I thought so also, so I tried changing the runSpeed() in the pot-adjusted part of the loop to run(). This produces the same instant stop on button press, then followed by slow, jagged steps that eventually goes to a halt that I described in the first post.

void loop()
{  potVal = analogRead(potPin);
   int spdVal = map(potVal, 0,1023,0,1000);
   stepper.setSpeed(spdVal);
   //Serial.println(stepper.speed());
   stepper.run();
   
   if(digitalRead(stopBut)){
      stepper.stop();
      while(1){
        stepper.run();
        }
      }
}

BUT! The code you posted above WORKS! I did an attempt with the same idea, but I didn't include the line
stepper.moveTo(0x07ffffff);
in the setup, and then it produced no movement at all.

What is the purpose of that line? Maybe that will help me understand where my thinking goes wrong.

However, using your code, the stepper never comes to a full stop when the pot is turned to zero. It always makes slow, jagged steps.

In the end Accelstepper is a positioning tool. So you must set a target position to get the motor moving. This line sets a target position that's most possible far away. Be aware that the stop() method changes the target position very near to the actual position. If you want to get it moving again later, you must set the target position again too.

Most likely this is because setMaxSpeed() must be greater than zero. So you must handle stopping the motor ( when the pot is set to zero ) separately.

1 Like

I also found the library ContinuousStepper, which is an adaptation of the AccalStepper library to better handle continuous motions- but it uses the same basic infrastructure which causes problems with delay() calls. Also, it uses the function stepper.loop(), which implements accelerations in every call- which creates delayed reactions to changes in potentiometer-based speedchanges.
So I will now venture into the world of MobaTools and see if that is better suited for my application.

One thing though: I will use interrupts in my final code. How will that work with the timer use of the MobaTools library you think?

Yes, I found that too and was about to recommend it for you that it may fit better to what you want to do. But I don't have any experience with that library.

Well that's with all accelerations - if you move your pot faster than the set acceleration the speed change will be delayed. I think that's normal behaviour.

As far as I understand, you can configure it to use timer interrupts.

Usually if your IRQ's are short ( as they should be ) and you don't use the same timer as MobaTools, there should be no problems.

Here is your test sketch based on MobaTools. I added a millis() functionalityt to not read the pot with every loop() cycle ( the AccelStepper version also works better if you do so ).

#include <MobaTools.h>
int potVal;
uint32_t lastReadings;
const byte potPin = A0;
const byte stopBut =7
const byte stepPin = 3
const byte dirPin = 4

MoToStepper stepper(200 * 8, STEPDIR);

void setup()
{ Serial.begin(115200);
  stepper.attach( stepPin, dirPin );
  stepper.setSpeedSteps(25000, 1600);
  pinMode(potPin, INPUT);
  pinMode(stopBut, INPUT);
  stepper.rotate(1);
}
void loop()
{
  if ( millis() - lastReadings > 200 ) {
    potVal = analogRead(potPin);
    int spdVal = map(potVal, 0, 1023, 0, 25000);
    Serial.println(spdVal);
    if ( spdVal == 0 ) {
      stepper.rotate(0);
    } else {
      stepper.setSpeedSteps(spdVal);
      stepper.rotate(1);
    }
    lastReadings = millis();
  }

  if (digitalRead(stopBut)) {
    stepper.setSpeedSteps(0);
    while (1);
  }
}

I had to change the pin numbers for my test equipment. I hope to have it restored correctly.

With your test sketch I also found a bug in MobaTools when setting the speed to zero. So there is a workaround in the pot part.
I will look after that.

1 Like

Many new users do not know of the necessity of using a pullup or pulldown resistor.

The more accepted way to wire a button switch is to wire to ground an to an input set to pinMode INPUT_PULLUP.

The input reads high when the switch is not pressed and low when pressed.

For what purpose? Interrupts are difficult to use but considered as a miracle pill by ignorant members. Why is the unknown a solution?

Well, I usually think that the truly ignorant person is the one who discards possibly valuable solutions because he or she doesn’t bother to try and understand them.
But then again, we’re all different. You do yours.

Oh, there are several acceptable ways to do most things. But I need to ask you, in what way is the wiring of the button relevant to the issue at hand?
Or did you just want to make a little lecture about some irrelevant topic to hide the fact that you couldn’t answer the actual question? Cause people do that on this forum. A lot. And they always - always- start by asking for more and irrelevant information, usually something about hardware when the question is about code. And they are never the ones that comes up with the solution in the end. I do wish they would stop, because it’s not an ACCEPTABLE way of handling oneself in forums.

But thank you and all others for the tip on the MobaTools library. I’ve just tried it out and it’s absolutely great!