AccelStepper stop and continue problem

Hello all,

I have a problem with my code. It works fine exept when I press stop and continue button. It stops almost immediately and continues at full speed. I mean it does not decelerate at stop or accelerate at continue.

Does anyone have idea how I can get it to decelerate and accelerate when I pause and resume winding ?

void RunLayers(long rounds,int speed, float wiresteps, float wire, int layers, boolean stop)
{ // rounds = rounds, Speed = motor speed, wire = wire diameter in mm, layers = how many layers, stop = stop between layers
long pos[2], val1, val2, oldval, stepcount = 0;
int layer = 1;
  //Serial.print("Speed 2 "); Serial.print((float)speed*((float)rounds[1]/(float)rounds[0]));
  pos[0] = rounds*stepsperrev; //pos[1] = rounds*wire;
  pos[1] = (int)((float)rounds*(float)wiresteps);
  stepper1.setMaxSpeed(speed);
  stepper2.setMaxSpeed((float)0.87*speed*((float)pos[1]/(float)pos[0]));
  Serial.print("Layer winding. Wireguide goes : "); Serial.print(((float)rounds*(float)wire)); 
  Serial.print(" mm back and forth "); Serial.print(layers); Serial.println(" times");
  for (int layer=1; layer <= layers; layer++){
  if ( (layer & 0x01) != 0) { /* layer is odd */
  Serial.print("Forward winding, layer "); Serial.println(layer);/* layer is odd */
  while ((stepper1.currentPosition() != pos[0]) or 
         (stepper2.currentPosition() != pos[1])) {
   stepper1.moveTo(pos[0]);
   stepper2.moveTo(pos[1]);
   if (digitalRead(butred)==HIGH) {
      Serial.println("Winding pause");
      // Does not accelerate / decelerate on pause / continue
      stepper1.stop(); stepper2.stop();
      delay(600);
      while (digitalRead(butred)==LOW) { }
      Serial.println("Winding continue");
      delay(600);
    } else {
      stepper1.run(); stepper2.run();
    }
    stepcount++;
    val1 = (float)((pos[0]-stepper1.currentPosition())/stepsperrev);
    val2 = (float)(round((pos[0]-stepper1.currentPosition())/stepsperrev));
    if ((val1 == val2) and (oldval != val2)){
    Serial.print("rounds left: "); Serial.println(val2);
    oldval = val2;
    }
  }
  stepper1.setCurrentPosition(0); stepper2.setCurrentPosition(0);
  if ((stop == true) and (layer < layers)){
    Serial.println("Press blue button to continue");
    while (digitalRead(butblu)==LOW) { } // button = joystick button
  }
  } else {
  Serial.print("Backward winding, layer "); Serial.println(layer); /* layer is even */
  stepper1.moveTo(pos[0]);
  stepper2.moveTo(pos[1]*-1);
  while ((stepper1.currentPosition() != pos[0]) or 
         (stepper2.currentPosition() != pos[1]*-1)) {
    stepper1.run(); stepper2.run();
    stepcount++;
    val1 = (float)((pos[0]-stepper1.currentPosition())/stepsperrev);
    val2 = (float)(round((pos[0]-stepper1.currentPosition())/stepsperrev));
    if ((val1 == val2) and (oldval != val2)){
    Serial.print("rounds left: "); Serial.println(val2);
    oldval = val2;
    }
  }
  stepper1.setCurrentPosition(0); stepper2.setCurrentPosition(0);
  if ((stop == true) and (layer < layers)){
    Serial.println("Press blue button to continue");
    while (digitalRead(butblu)==LOW) { } // button = joystick button
  }
  }
  }
   Serial.print(layers); Serial.print(" layers, "); Serial.print(layers*rounds); Serial.println(" rounds ready");
}

Your code is almost impossible to read. For example there is a FOR on line 13 - but where does it end? Please use the AutoFormat tool to indent your code consistently.

And you seem to have WHILEs all over the place. They are not conducive to a responsive program.

Finally, post the complete program. Add the .ino file as an attachment if it is too long.

...R

Robin,

Thank you for your answer. Looks like I'm not very good or professional programmer.
I used WHILEs because they were first thing that popped in my mind for waiting button press.

Here is the whole code as attachment. It's too long to post as code.

winder-180815.ino (12.9 KB)

Thanks for posting the program. The indentation is much better, but now I can see that you have multiple statements on some lines. That also makes things hard to read and offers no benefit to the compiler.

When I look at your problem description in your Original Post

exept when I press stop and continue button. It stops almost immediately and continues at full speed.

I can't see whereabouts in the code the "stop and continue" button is pressed.

My first thought is that the program needs a complete overhaul. Break it up into a greater number of functions and ensure that each function just does one task. In particular I would have a function to read the buttons and save their states for use elsewhere in the program. And doing things that way will almost certainly require all the WHILEs to go. The loop() function is designed to do the repetition - allow it to do so. The FOR loops should probably also go. Both WHILE and FOR can usually be replaced by IF.

I only use FOR (and occasionally WHILE) for a loop that will be completed in a few microseconds - for example iterating over an array.

Have a look at Planning and Implementing a Program

I also think you are not using the AccelStepper library properly - you don't need to use WHILE. The stepper.run() function will stop moving the motor when it reaches the destination. Ideally your only calls to stepper.run() should be in loop() and loop() should repeat much more frequently than the step rate.

Finally (for now) it would probably be a good idea to write a short program to explore the ability to stop the motor without the confusion of all the rest of the code.

...R

Robin,

Thank you for your efforts and input.

I made very simple example that I can demonstrate this problem of mine.
It pauses and continues motors by pressing the red button. It has the same problem that it does not decelerate at pause or accelerate at continue. Motors stops immediately when I press red button and starts running at full speed after I press red button second time to continue.

I'd also be thankful if you suggest how to get rid of WHILE in this code.

#include <AccelStepper.h>

// Define some steppers and the pins the will use
AccelStepper stepper1(AccelStepper::FULL4WIRE, 28, 26, 24, 22);
AccelStepper stepper2(AccelStepper::FULL4WIRE, 29, 27, 25, 23);

const int butred = 47;     // the number of the pushbutton red
const int butgrn = 49;     // the number of the pushbutton green
const int butblu = 51;     // the number of the pushbutton blue
const int butylw = 53;     // the number of the pushbutton yellow

void setup()
{
  Serial.begin(9600);
  stepper1.setMaxSpeed(600.0);
  stepper1.setAcceleration(100.0);
  stepper1.moveTo(1500);

  stepper2.setMaxSpeed(600.0);
  stepper2.setAcceleration(100.0);
  stepper2.moveTo(1000);

}

void loop()
{
  if (digitalRead(butred) == HIGH) {
    Serial.println("Winding pause");
    // Does not accelerate / decelerate on pause / continue
    stepper1.stop();
    stepper2.stop();
    stepper1.disableOutputs();
    stepper2.disableOutputs();
    delay(600);
    while (digitalRead(butred) == LOW) { }
    stepper1.moveTo(1500);
    stepper2.moveTo(1000);
    Serial.println("Winding continue");
    delay(600);
  }
  // Change direction at the limits
  if (stepper1.distanceToGo() == 0)
    stepper1.moveTo(-stepper1.currentPosition());
  if (stepper2.distanceToGo() == 0)
    stepper2.moveTo(-stepper2.currentPosition());
  stepper1.run();
  stepper2.run();
}

Thanks for the shortened program. As you will see from the following I have restructured it very considerably. Even though it is a short program that is how I would lay out my own program. And I am conscious that you want to evolve it into a much bigger program.

I have checked that the program compiles but I know it is not complete because it does not take account of your delay()s. I did not know what their purpose is.

Also I have made no attempt to deal with your WHILE because I don't know what it is intended to do.

However I hope that the way I have structured the program may make it easer to describe what else is required.

#include <AccelStepper.h>

// Define some steppers and the pins the will use
AccelStepper stepper1(AccelStepper::FULL4WIRE, 28, 26, 24, 22);
AccelStepper stepper2(AccelStepper::FULL4WIRE, 29, 27, 25, 23);

const byte butred = 47;     // the number of the pushbutton red
const byte butgrn = 49;     // the number of the pushbutton green
const byte butblu = 51;     // the number of the pushbutton blue
const byte butylw = 53;     // the number of the pushbutton yellow

byte redButtonState;
boolean motorMayRun = true;
boolean motorRunning = true;

//==========

void setup() {
    Serial.begin(9600);
    stepper1.setMaxSpeed(600.0);
    stepper1.setAcceleration(100.0);
    stepper1.moveTo(1500);

    stepper2.setMaxSpeed(600.0);
    stepper2.setAcceleration(100.0);
    stepper2.moveTo(1000);
}

//==========

void loop() {
    readButtons();
    respondToButtons();
    controlMotor();

    stepper1.run();
    stepper2.run();
}

//=========

void readButtons() {
    redButtonState = digitalRead(butred);
}

//=========

void respondToButtons() {
    if (redButtonState == HIGH) {
        motorMayRun = false;
    }
    else {
        motorMayRun = true;
    }
}

//==========

void controlMotor() {
    if (motorMayRun == false and motorRunning == true) {
        stopMotor();
    }
    if (motorMayRun == true and motorRunning == false) {
        startMotor();
    }
    updateDirection();
}

//==========

void stopMotor() {

    Serial.println("Winding pause");
    // Does not accelerate / decelerate on pause / continue
    stepper1.stop();
    stepper2.stop();
    stepper1.disableOutputs();
    stepper2.disableOutputs();
    motorRunning = false;

}

//==========

void startMotor() {

    stepper1.moveTo(1500);
    stepper2.moveTo(1000);
    motorRunning = true;
    Serial.println("Winding continue");

}

//==========

void updateDirection() {
        // Change direction at the limits
    if (stepper1.distanceToGo() == 0) {
        stepper1.moveTo(-stepper1.currentPosition());
    }
    if (stepper2.distanceToGo() == 0) {
        stepper2.moveTo(-stepper2.currentPosition());
    }
}

...R

Thank you for this reconstructed version. It really looks totally different and professional comparing to my messed up code :slight_smile:

I have to leave for the weekend. I'll test it as soon as I get back to test my coil winder.

I'll tell you how it works when I have tested it.

Thanks again.

DataSpark:
Robin,

Thank you for your answer. Looks like I'm not very good or professional programmer.
I used WHILEs because they were first thing that popped in my mind for waiting button press.

Here is the whole code as attachment. It's too long to post as code.

You should not be waiting, you should be checking.

Robin,

I tested your code. It works fine after just a little modifications.

I got it working properly and it responses to button presses like it should.

here is my updated code :

#include <AccelStepper.h>

// Define some steppers and the pins the will use
AccelStepper stepper1(AccelStepper::FULL4WIRE, 28, 26, 24, 22);
AccelStepper stepper2(AccelStepper::FULL4WIRE, 29, 27, 25, 23);

const byte butred = 47;     // the number of the pushbutton red
const byte butgrn = 49;     // the number of the pushbutton green
const byte butblu = 51;     // the number of the pushbutton blue
const byte butylw = 53;     // the number of the pushbutton yellow

byte redButtonState = LOW;
byte blueButtonState = LOW;
boolean motorMayRun = false;
boolean motorRunning = false;

//==========

void setup() {
    Serial.begin(9600);
    stepper1.setMaxSpeed(600.0);
    stepper1.setAcceleration(100.0);
    stepper1.moveTo(1500);

    stepper2.setMaxSpeed(600.0);
    stepper2.setAcceleration(100.0);
    stepper2.moveTo(1000);
}

//==========

void loop() {
    readButtons();
    respondToButtons();
    controlMotor();
    if (motorMayRun == true) {
      stepper1.run();
      stepper2.run();
    }

}

//=========

void readButtons() {
    redButtonState = digitalRead(butred);
    blueButtonState = digitalRead(butblu);
    
}

//=========

void respondToButtons() {
    if (blueButtonState == HIGH) {
        Serial.println("Winding start");
        motorMayRun = true;
        delay(600);
        startMotor();
    }
    if (redButtonState == HIGH) {    // Does not decelerate on pause
        if (motorMayRun == true) {
            motorMayRun = false;
            Serial.println("Winding pause");
            delay(600); // Allows user to release their finger from the button
            stopMotor();
        } else
        if (motorMayRun == false) {     // Does not accelerate on continue
            Serial.println("Winding continue");
            motorMayRun = true;
            delay(600); // Allows user to release their finger from the button
            startMotor();
        }
    }
    
}

//==========

void controlMotor() {
    if (motorMayRun == false and motorRunning == true) {
        stopMotor();
    }
    if (motorMayRun == true and motorRunning == false) {
        startMotor();
    }
    updateDirection();
}

//==========

void stopMotor() {
    stepper1.stop();
    stepper2.stop();
    stepper1.disableOutputs();
    stepper2.disableOutputs();
    motorRunning = false;

}

//==========

void startMotor() {
    stepper1.moveTo(1500);
    stepper2.moveTo(1000);
    motorRunning = true;

}

//==========

void updateDirection() {
        // Change direction at the limits
    if (stepper1.distanceToGo() == 0) {
        stepper1.moveTo(-stepper1.currentPosition());
    }
    if (stepper2.distanceToGo() == 0) {
        stepper2.moveTo(-stepper2.currentPosition());
    }
}

But it still has the same problem I had when I started this thread : It does not delerate / accelerate when I press red pause / resume button.

How can I get it to do it ?

Blue button starts winding and red button pauses / resumes it. Delays are there only to ensure that user has a little time to release their finger from pause / resume button because red button handles both. They prevent multiple pause / resume from happening.

DataSpark:
How can I get it to do it ?

First thing is to understand the problem. When you set motorMayRun to false after the pause button is pressed the code immediately stops calling stepper.run() so, naturally, there is an instant stop.

You need to call stepper.stop() first and then check that the motor actually has stopped before you set motorMayRun to false.

...R

Never stop calling stepper.run(), that's always the wrong thing to do with AccelStepper library.

Looks like I did it wrong again. I'll modify it and check how it goes.

Thank you for your advices again :slight_smile:

DataSpark:
Looks like I did it wrong again.

Don't worry - getting to grips with the AccelStepper concepts can take a little time.

...R

I managed to get it accelerating after I press pause button(continue) second time :smiley: But it stops immediately after I press it first time(pause)

It is not actually so bad. At least it will accelerate after pause.

I also was not able to figure out how to get rid of "if (motorMayRun == true)" in loop. If I comment it out, then motor starts running as soon I switch power of Arduino on

#include <AccelStepper.h>

// Define some steppers and the pins the will use
AccelStepper stepper1(AccelStepper::FULL4WIRE, 28, 26, 24, 22);
AccelStepper stepper2(AccelStepper::FULL4WIRE, 29, 27, 25, 23);

const byte butred = 47;     // the number of the pushbutton red
const byte butgrn = 49;     // the number of the pushbutton green
const byte butblu = 51;     // the number of the pushbutton blue
const byte butylw = 53;     // the number of the pushbutton yellow

byte redButtonState = LOW;
byte blueButtonState = LOW;
boolean motorMayRun = false;
boolean motorRunning = false;
long position1 = 3500;
long position2 = 1500;
long pos1 = 0;
long pos2 = 0;

//==========

void setup() {
  Serial.begin(9600);
  stepper1.setMaxSpeed(600.0);
  stepper1.setAcceleration(100.0);
  stepper1.moveTo(1500);

  stepper2.setMaxSpeed(600.0);
  stepper2.setAcceleration(100.0);
  stepper2.moveTo(1000);
}

//==========

void loop() {
  readButtons();
  respondToButtons();
  controlMotor();
  if (motorMayRun == true) {
    stepper1.run();
    stepper2.run();
  }

}

//=========

void readButtons() {
  redButtonState = digitalRead(butred);
  blueButtonState = digitalRead(butblu);

}

//=========

void respondToButtons() {
  if (blueButtonState == HIGH) {
    Serial.println("Winding start");
    motorMayRun = true;
    stepper1.setCurrentPosition(0);
    stepper2.setCurrentPosition(0);
    stepper1.moveTo(position1);
    stepper2.moveTo(position2);
    delay(600);
    startMotor();
  }
  if (redButtonState == HIGH) {    // Does not decelerate on pause
    if (motorMayRun == true) {
      stopMotor();
      pos1 = position1 - stepper1.currentPosition();
      pos2 = position2 - stepper2.currentPosition();
      if (motorRunning == false) {
        motorMayRun = false;
      }
      Serial.println("Winding pause");
      delay(600); // Allows user to release their finger from the button
    } else if (motorMayRun == false) {    // Does not accelerate on continue
      Serial.println("Winding continue");
      motorMayRun = true;
      delay(600); // Allows user to release their finger from the button
      stepper1.setCurrentPosition(0);
      stepper2.setCurrentPosition(0);
      stepper1.moveTo(pos1);
      stepper2.moveTo(pos2);
    }
  }

}

//==========

void controlMotor() {
  if (motorMayRun == false and motorRunning == true) {
    stopMotor();
  }
  if (motorMayRun == true and motorRunning == false) {
    startMotor();
  }
  //updateDirection();
}

//==========

void stopMotor() {
  stepper1.stop();
  stepper2.stop();
  stepper1.disableOutputs();
  stepper2.disableOutputs();
  motorRunning = false;

}

//==========

void startMotor() {
  //stepper1.moveTo(position1);
  //stepper2.moveTo(position2);
  motorRunning = true;

}

//==========

void updateDirection() {
  // Change direction at the limits
  if (stepper1.distanceToGo() == 0) {
    stepper1.moveTo(-stepper1.currentPosition());
  }
  if (stepper2.distanceToGo() == 0) {
    stepper2.moveTo(-stepper2.currentPosition());
  }
}

You are still stopping the calls to stepper.run() prematurely.

Try this version. I think I have marked all the changes. And, generally there is less code. It compiles but I have not tested it.

#include <AccelStepper.h>

// Define some steppers and the pins the will use
AccelStepper stepper1(AccelStepper::FULL4WIRE, 28, 26, 24, 22);
AccelStepper stepper2(AccelStepper::FULL4WIRE, 29, 27, 25, 23);

const byte butred = 47;     // the number of the pushbutton red
const byte butgrn = 49;     // the number of the pushbutton green
const byte butblu = 51;     // the number of the pushbutton blue
const byte butylw = 53;     // the number of the pushbutton yellow

byte redButtonState = LOW;
byte blueButtonState = LOW;
        //  boolean motorMayRun = false;  // not used
boolean motorRunning = false;
long position1 = 3500;
long position2 = 1500;
long pos1 = 0;
long pos2 = 0;

//==========

void setup() {
    Serial.begin(9600);
    stepper1.setMaxSpeed(600.0);
    stepper1.setAcceleration(100.0);
    stepper1.moveTo(1500);

    stepper2.setMaxSpeed(600.0);
    stepper2.setAcceleration(100.0);
    stepper2.moveTo(1000);
}

//==========

void loop() {
    readButtons();
    respondToButtons();
            // changed
            // changed
    stepper1.run();
    stepper2.run();


}

//=========

void readButtons() {
    redButtonState = digitalRead(butred);
    blueButtonState = digitalRead(butblu);

}

//=========

void respondToButtons() {
    if (blueButtonState == HIGH) {
        Serial.println("Winding start");
        motorRunning = true;
        stepper1.setCurrentPosition(0);
        stepper2.setCurrentPosition(0);
        stepper1.moveTo(position1);
        stepper2.moveTo(position2);
        delay(600);
            // changed
    }
    if (redButtonState == HIGH) {
        if (motorRunning == true) {
            stopMotor();
            pos1 = position1 - stepper1.currentPosition();
            pos2 = position2 - stepper2.currentPosition();
                // changed
            motorRunning = false;

            Serial.println("Winding pause");
            delay(600); // Allows user to release their finger from the button

        } else if (motorRunning == false) {
            Serial.println("Winding continue");
            motorRunning = true;
            delay(600); // Allows user to release their finger from the button
            stepper1.setCurrentPosition(0);
            stepper2.setCurrentPosition(0);
            stepper1.moveTo(pos1);
            stepper2.moveTo(pos2);
        }
    }

}

//==========

void controlMotor() {
    // not used
}

//==========

void stopMotor() {
    stepper1.stop();
    stepper2.stop();
    //~ stepper1.disableOutputs();
    //~ stepper2.disableOutputs();
    motorRunning = false;

}

//==========

void startMotor() {
    // notused

}

//==========

void updateDirection() {
    // Change direction at the limits
    if (stepper1.distanceToGo() == 0) {
        stepper1.moveTo(-stepper1.currentPosition());
    }
    if (stepper2.distanceToGo() == 0) {
        stepper2.moveTo(-stepper2.currentPosition());
    }
}

Study carefully the impact of the changes so you understand their purpose - even if it still does not work perfectly

...R

Thank you for this. I really need to study more to make it work like you have told.

When you use AccelStepper library you should be calling stepper.run() in loop() all the time, there is no
sense in doing anything else, the library is designed with this as a fundamental assumption.