I want my stepper motors to tell me when they're done moving

Hello everyone,
I have this beefy chunk of code, and I’ve tried putting this line
if (stepper1.distanceToGo() == 0) {Serial.println(“Done Moving”)}
a bunch of different places and it either does nothing, or spams “Done Moving” over and over again
How would I get my program to tell me “Done Moving” only once for each motor when they are done moving?

//=========== Libraries

#include <AccelStepper.h>
#include <MultiStepper.h>

//=========== Defining motor names and pins

AccelStepper stepper1(1, 8, 9); // stepper1, Pulse Pin 8, Direction Pin 9
AccelStepper stepper2(1, 4, 5); // stepper2, Pulse Pin 4, Direction Pin 5

//=========== Variables for running the motors

bool newData = false;
bool runallowed = false;
char receivedCommand;
long CoordinateX = 0;
long CoordinateY = 0;

//=========== Variables for endstop switches and homing

const byte switchPin1 = 2;
const byte switchPin2 = 7;
int switchState1 = 0;
int lastSwitchState1 = 0;
int switchState2 = 0;
int lastSwitchState2 = 0;

//=========== Variables for parsing data

const byte numChars = 32; //number of characters set to 32, constant which means read only basically
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use when parsing


char messageFromPC[numChars] = {0}; // variables to hold the parsed data
int bottleNum = 0; //defining first input variable for location

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

void setup()
{
  pinMode(switchPin1, INPUT_PULLUP); //internal pullup resistor
  pinMode(switchPin2, INPUT_PULLUP); //internal pullup resistor

  Serial.begin(9600); //baud rate for serial monitor
  Serial.println("Motor Position Testing"); //print this message on open
  Serial.println("Positions available: 1-9"); // ^
  Serial.println("Syntax <01>");

  stepper1.setMaxSpeed(1600); //Max speed steps/second
  stepper1.setAcceleration(800); //Acceleration steps/second/second
  stepper2.setMaxSpeed(1600); //Max speed steps/second
  stepper2.setAcceleration(800); //Acceleration steps/second/second
  stepper1.disableOutputs(); //Keeps outputs disabled when not in use
  stepper2.disableOutputs(); //Keeps outputs disabled when not in use
}

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

void loop()
{

  //=========== Checking for input from endstop switch 1
  {
    static unsigned long timer1 = 0; //milliseconds starting at 0
    unsigned long interval1 = 20; //20ms interval
    if (millis() - timer1 >= interval1) //if ms-timer >= 20ms
    {
      timer1 = millis(); //set timer and ms equal
      switchState1 = digitalRead(switchPin1); //setting a variable to switchPin reading
      if (switchState1 != lastSwitchState1) //comparing current switch state and previous switch state
      {
        if (switchState1 == LOW) //if the switch state has changed to low meaning the switch has been closed
        {
          stopMotor1(); //stop and home the motor
        }
        else
        {
          //do nothing
        }
        lastSwitchState1 = switchState1;  // save the current state as the last state, for next time through the loop
      }
      lastSwitchState1 = switchState1;  // save the current state as the last state, for next time through the loop
    }
  }

  //============ Checking for input from endstop switch 2

  {
    static unsigned long timer2 = 0; //milliseconds starting at 0
    unsigned long interval2 = 20; //20ms interval
    if (millis() - timer2 >= interval2) //if ms-timer >= 20ms
    {
      timer2 = millis(); //set timer and ms equal
      switchState2 = digitalRead(switchPin2); //setting a variable to switchPin reading
      if (switchState2 != lastSwitchState2) //comparing current switch state and previous switch state
      {
        if (switchState2 == LOW) //if the switch state has changed to low meaning the switch has been closed
        {
          stopMotor2(); //stop and home the motor
        }
        else
        {
          //do nothing
        }
        lastSwitchState2 = switchState2;  // save the current state as the last state, for next time through the loop
      }
      lastSwitchState2 = switchState2;  // save the current state as the last state, for next time through the loop
    }
  }

  //============= Parsing incoming data and moving the motors

  recieveData(); //void command defined later
  if (newData == true) {
    strcpy(tempChars, receivedChars); // this temporary copy is necessary to protect the original data because strtok() used in parseData() replaces the commas with \0
    parseData(); //void command defined later
    printData(); //prints parsed bottle location
    motorLocations(); //tells the motor where to go and returns message
    newData = false; //setting new data bool back to false at end of loop
  }
  runMotor(); //enables or disables motor outputs
}

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

void recieveData() //command for recieving data and identifying start and end markers
{
  static boolean recvInProgress = false; //static is visible to only one function, defining this static bool as false by default
  static byte owo = 0; //static as above, but for number of characters
  char startMarker = '<'; //defining start marker
  char endMarker = '>'; //defining end marker
  char uwu; //defining variable uwu as a character value

  while (Serial.available() > 0 && newData == false) { //while serial monitor is unavailable, i.e getting inputs between markers
    uwu = Serial.read(); //set input to variable uwu

    if (recvInProgress == true) { //if we are currently recieving data
      if (uwu != endMarker) { //if the character is not an end marker
        receivedChars[owo] = uwu; //Set number of characters to static byte
        owo++; //add one character to static byte
        if (owo >= numChars) { //if static byte is the same or greater than contant byte
          owo = numChars - 1; //static byte becomes constant byte
        }
      }
      else {
        receivedChars[owo] = '\0'; // terminate the string
        recvInProgress = false; //set recieve in progress to false
        owo = 0; //set static byte back to 0
        newData = true; //set new data to true
      }
    }

    else if (uwu == startMarker) { //set character value to start marker
      recvInProgress = true; //set recieve in progress to true
    }
  }
}

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

void parseData() {      // split the data into its parts

  char * strtokIndx; // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ","); // get the first part
  bottleNum = atoi(strtokIndx);     // convert it to an integer

}

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

void runMotor() //enables or disable motor outputs
{
  if (runallowed == true)
  {
    stepper1.enableOutputs(); //enable output pins if run is allowed
    stepper1.run();
    stepper2.enableOutputs(); //enable output pins if run is allowed
    stepper2.run();
  }
  else
  {
    stepper1.disableOutputs(); //disable output pins if run is not allowed
    stepper2.disableOutputs(); //disable output pins if run is not allowed
    return;
  }
}

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

void motorLocations() //check for recieved commands on the serial
{
  stepper1.setMaxSpeed(1600); //Max speed steps/second
  stepper1.setAcceleration(800); //Acceleration steps/second/second
  stepper2.setMaxSpeed(1600); //Max speed steps/second
  stepper2.setAcceleration(800); //Acceleration steps/second/second

  switch (bottleNum) //we check what is the command
  {

    case 0: //Position Home for the motors

      Serial.println("Homing..."); //print text before moving
      CoordinateX = 0;
      CoordinateY = 0;
      homeSequence(); //run the command for moving to a position
      break;

    case 1: //Position 1 for the motors

      Serial.println("Moving to Position 1"); //print text before moving
      CoordinateX = 1000;
      CoordinateY = 1000;
      MovePosition(); //run the command for moving to a position
      break;

    //deleted 8 more cases to save characters for this post

    default:

      break;

  }
}

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

void MovePosition()
{
  runallowed = true; //enable outputs
  stepper1.moveTo(CoordinateX); //move large motor to coordinates defined in the serial cases
  stepper2.moveTo(CoordinateY); //move small motor to coordinates defined in the serial cases
}

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

//deleted homeSequence() for this post

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

//deleted stopMotor1() and stopMotor2() for this post 

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

void printData() {
  Serial.print("Recieved Bottle Number: ");
  Serial.println(bottleNum);
}

Thanks in Advance!

How would I get my program to tell me “Done Moving” only once for each motor when they are done moving?

Make printing the “done moving” message dependant on boolean and only print it if the boolean is true and distanceToGo() is zero. Once the message has been printed set the boolean to false to prevent it being printed again

Ill have to admit, I have no idea how to implement that, although it does sound like a good idea. I am relatively new to arduino stuff.

I am guessing that you did not write the code that you posted. There is nothing wrong with that but it does make changing it more difficult for you

For a start, where in the sketch are the commands to start the motors moving ?

I technically did write the code, but I used a lot of example sketches and help from this forum to get it where it is right now.

runMotor() is where the run command sits
motorLocations() set the coordinates X and Y for moveTo
MovePosition() is where the moveTo commands are at

hopefully that answers your question

#include <AccelStepper.h>
#include <MultiStepper.h>

AccelStepper stepper1(1, 8, 9); // stepper1, Pulse Pin 8, Direction Pin 9
AccelStepper stepper2(1, 4, 5); // stepper2, Pulse Pin 4, Direction Pin 5

bool newData = false;
bool runallowed = false;
char receivedCommand;
long CoordinateX = 0;
long CoordinateY = 0;

void setup()
{
  Serial.begin(9600); //baud rate for serial monitor
  Serial.println("Motor Position Testing"); //print this message on open
  Serial.println("Positions available: 1-9"); // ^

  stepper1.setMaxSpeed(1600); //Max speed steps/second
  stepper1.setAcceleration(800); //Acceleration steps/second/second
  stepper2.setMaxSpeed(1600); //Max speed steps/second
  stepper2.setAcceleration(800); //Acceleration steps/second/second

  stepper1.disableOutputs(); //Keeps outputs disabled when not in use
  stepper2.disableOutputs(); //Keeps outputs disabled when not in use
}

void loop()
{
  checkSerial(); //check for recieved commands on the serial monitor
  runMotor(); //enables or disables motor outputs
}

void runMotor() //enables or disable motor outputs
{
  if (runallowed == true)
  {
    stepper1.enableOutputs(); //enable output pins if run is allowed
    stepper1.run();
    stepper2.enableOutputs(); //enable output pins if run is allowed
    stepper2.run();
  }
  else
  {
    stepper1.disableOutputs(); //disable output pins if run is not allowed
    stepper2.disableOutputs(); //disable output pins if run is not allowed
    return;
  }
}


void checkSerial() //check for recieved commands on the serial
{
  if (Serial.available() > 0) //if something comes from the computer
  {
    receivedCommand = Serial.read(); // pass the value to the receivedCommand variable
    newData = true; //indicate that there is a new data by setting this bool to true

    if (newData == true) //we only enter this long switch-case statement if there is a new command from the computer
    {
      switch (receivedCommand) //we check what is the command
      {

        case '1': //Position 1 for the motors

          Serial.println("Moving to Position 1"); //print text before moving
          CoordinateX = 1000;
          CoordinateY = 1000;
          MovePosition(); //run the command for moving to a position
          break;

        case '2': //Position 2 for the motors

          Serial.println("Moving to Position 2"); //print text before moving
          CoordinateX = 2000;
          CoordinateY = 1000;
          MovePosition(); //run the command for moving to a position
          break;

        case '3': //Position 3 for the motors

          Serial.println("Moving to Position 3"); //print text before moving
          CoordinateX = 3000;
          CoordinateY = 1000;
          MovePosition(); //run the command for moving to a position
          break;

        case '4': //Position 4 for the motors

          Serial.println("Moving to Position 4"); //print text before moving
          CoordinateX = 1000;
          CoordinateY = 2000;
          MovePosition(); //run the command for moving to a position
          break;

        case '5': //Position 5 for the motors

          Serial.println("Moving to Position 5"); //print text before moving
          CoordinateX = 2000;
          CoordinateY = 2000;
          MovePosition(); //run the command for moving to a position
          break;

        case '6': //Position 6 for the motors

          Serial.println("Moving to Position 6"); //print text before moving
          CoordinateX = 3000;
          CoordinateY = 2000;
          MovePosition(); //run the command for moving to a position
          break;

        case '7': //Position 7 for the motors

          Serial.println("Moving to Position 7"); //print text before moving
          CoordinateX = 1000;
          CoordinateY = 3000;
          MovePosition(); //run the command for moving to a position
          break;

        case '8': //Position 8 for the motors

          Serial.println("Moving to Position 8"); //print text before moving
          CoordinateX = 2000;
          CoordinateY = 3000;
          MovePosition(); //run the command for moving to a position
          break;

        case '9': //Position 9 for the motors

          Serial.println("Moving to Position 9"); //print text before moving
          CoordinateX = 3000;
          CoordinateY = 3000;
          MovePosition(); //run the command for moving to a position
          break;

        case '0': //Position Home for the motors

          Serial.println("Moving to Home"); //print text before moving
          CoordinateX = 0;
          CoordinateY = 0;
          MovePosition(); //run the command for moving to a position
          break;

        default:

          break;

      }
    }

    newData = false;

  }
}

void MovePosition()
{
  runallowed = true; //enable outputs
  stepper1.moveTo(CoordinateX); //move large motor to coordinates defined in the serial cases
  stepper2.moveTo(CoordinateY); //move small motor to coordinates defined in the serial cases
}

Here is another version of the code without end stop switches, homing or parsing. I think this may be easier to work with, while maintaining the scope of the problem

Hello Everyone,

I have some code for running stepper motors, and there is a bool called runallowed that controls whether or not the motor outputs are active. This is defined in runMotor().
In MovePosition(), we set runallowed true, then moveTo the coordinates defined in checkSerial().
After this the outputs stay enabled, we waste power and the motor gets toasty.
I tried adding a 4th line to set runallowed back to false like so, and the motor never runs.

void MovePosition()
{
  runallowed = true; //enable outputs
  stepper1.moveTo(CoordinateX); //move large motor to coordinates defined in the serial cases
  stepper2.moveTo(CoordinateY); //move small motor to coordinates defined in the serial cases
  runallowed = false;
}

Okay maybe it happens too quickly, and turns off the outputs before the motor has a chance to move, lets add a delay.

void MovePosition()
{
  runallowed = true; //enable outputs
  stepper1.moveTo(CoordinateX); //move large motor to coordinates defined in the serial cases
  stepper2.moveTo(CoordinateY); //move small motor to coordinates defined in the serial cases
  delay(5000);
  runallowed = false;
}

Nope, same thing happens and the motors never run.
I printed runallowed to the serial monitor and it never changes from false in either of the cases above.
Okay maybe if we try moving it to the case section of checkSerial() where we define the variables

void checkSerial() //check for recieved commands on the serial
{
  if (Serial.available() > 0) //if something comes from the computer
  {
    receivedCommand = Serial.read(); // pass the value to the receivedCommand variable
    newData = true; //indicate that there is a new data by setting this bool to true
    if (newData == true) //we only enter this long switch-case statement if there is a new command from the computer
    {
      switch (receivedCommand) //we check what is the command
      {
        case '1': //Position 1 for the motors
          Serial.println("Moving to Position 1"); //print text before moving
          CoordinateX = 1000;
          CoordinateY = 1000;
          MovePosition(); //run the command for moving to a position
          delay(5000);
          runallowed = false;
          break;

No luck, same result.
So it appears I completely do not understand how code works. How would I go about switching runallowed back to false after the motors move? Why is my current method not working? Thanks in advance for your help!

#include <AccelStepper.h>
#include <MultiStepper.h>
AccelStepper stepper1(1, 8, 9); // stepper1, Pulse Pin 8, Direction Pin 9
AccelStepper stepper2(1, 4, 5); // stepper2, Pulse Pin 4, Direction Pin 5
bool newData = false;
bool runallowed = false;
char receivedCommand;
long CoordinateX = 0;
long CoordinateY = 0;
void setup()
{
  Serial.begin(9600); //baud rate for serial monitor
  Serial.println("Motor Position Testing"); //print this message on open
  Serial.println("Positions available: 1-9"); // ^
  stepper1.setMaxSpeed(1600); //Max speed steps/second
  stepper1.setAcceleration(800); //Acceleration steps/second/second
  stepper2.setMaxSpeed(1600); //Max speed steps/second
  stepper2.setAcceleration(800); //Acceleration steps/second/second
  stepper1.disableOutputs(); //Keeps outputs disabled when not in use
  stepper2.disableOutputs(); //Keeps outputs disabled when not in use
}
void loop()
{
  checkSerial(); //check for recieved commands on the serial monitor
  runMotor(); //enables or disables motor outputs
}
void runMotor() //enables or disable motor outputs
{
  if (runallowed == true)
  {
    stepper1.enableOutputs(); //enable output pins if run is allowed
    stepper1.run();
    stepper2.enableOutputs(); //enable output pins if run is allowed
    stepper2.run();
  }
  else
  {
    stepper1.disableOutputs(); //disable output pins if run is not allowed
    stepper2.disableOutputs(); //disable output pins if run is not allowed
    return;
  }
}
void checkSerial() //check for recieved commands on the serial
{
  if (Serial.available() > 0) //if something comes from the computer
  {
    receivedCommand = Serial.read(); // pass the value to the receivedCommand variable
    newData = true; //indicate that there is a new data by setting this bool to true
    if (newData == true) //we only enter this long switch-case statement if there is a new command from the computer
    {
      switch (receivedCommand) //we check what is the command
      {
        case '1': //Position 1 for the motors
          Serial.println("Moving to Position 1"); //print text before moving
          CoordinateX = 1000;
          CoordinateY = 1000;
          MovePosition(); //run the command for moving to a position
          delay(5000);
          runallowed = false;
          break;
        case '2': //Position 2 for the motors
          Serial.println("Moving to Position 2"); //print text before moving
          CoordinateX = 2000;
          CoordinateY = 1000;
          MovePosition(); //run the command for moving to a position
          break;
        case '3': //Position 3 for the motors
          Serial.println("Moving to Position 3"); //print text before moving
          CoordinateX = 3000;
          CoordinateY = 1000;
          MovePosition(); //run the command for moving to a position
          break;
        case '4': //Position 4 for the motors
          Serial.println("Moving to Position 4"); //print text before moving
          CoordinateX = 1000;
          CoordinateY = 2000;
          MovePosition(); //run the command for moving to a position
          break;
        case '5': //Position 5 for the motors
          Serial.println("Moving to Position 5"); //print text before moving
          CoordinateX = 2000;
          CoordinateY = 2000;
          MovePosition(); //run the command for moving to a position
          break;
        case '6': //Position 6 for the motors
          Serial.println("Moving to Position 6"); //print text before moving
          CoordinateX = 3000;
          CoordinateY = 2000;
          MovePosition(); //run the command for moving to a position
          break;
        case '7': //Position 7 for the motors
          Serial.println("Moving to Position 7"); //print text before moving
          CoordinateX = 1000;
          CoordinateY = 3000;
          MovePosition(); //run the command for moving to a position
          break;
        case '8': //Position 8 for the motors
          Serial.println("Moving to Position 8"); //print text before moving
          CoordinateX = 2000;
          CoordinateY = 3000;
          MovePosition(); //run the command for moving to a position
          break;
        case '9': //Position 9 for the motors
          Serial.println("Moving to Position 9"); //print text before moving
          CoordinateX = 3000;
          CoordinateY = 3000;
          MovePosition(); //run the command for moving to a position
          break;
        case '0': //Position Home for the motors
          Serial.println("Moving to Home"); //print text before moving
          CoordinateX = 0;
          CoordinateY = 0;
          MovePosition(); //run the command for moving to a position
          break;
        default:
          break;
      }
    }
    newData = false;
  }
}
void MovePosition()
{
  runallowed = true; //enable outputs
  stepper1.moveTo(CoordinateX); //move large motor to coordinates defined in the serial cases
  stepper2.moveTo(CoordinateY); //move small motor to coordinates defined in the serial cases
}

Excessive and possibly inaccurate commenting is for myself, I know you guys know better.

void MovePosition()
{
  runallowed = true; //enable outputs
  stepper1.moveTo(CoordinateX); //move large motor to coordinates defined in the serial cases
  stepper2.moveTo(CoordinateY); //move small motor to coordinates defined in the serial cases
}

after you change the value of runallowed, you have to call runMotor() straight away, actually it should be more like this

void runMotor(bool runmotor) //enables or disable motor outputs
{
  if (runmotor == true)
  {
    stepper1.enableOutputs(); //enable output pins if run is allowed
    stepper1.run();
    stepper2.enableOutputs(); //enable output pins if run is allowed
    stepper2.run();
  }
  else
  {
    stepper1.disableOutputs(); //disable output pins if run is not allowed
    stepper2.disableOutputs(); //disable output pins if run is not allowed
    return;
  }
}

Sorry, I don't quite understand what you're trying to say, and that code creates a syntax error. Do you mind elaborating?

I would agree, It would help if you included a schematic, not a frizzy thing including all power and ground connections. Your problem could be a simple wiring mistook or something major. Links to the parts would also help.

Sorry, I don't quite understand what you're trying to say, and that code creates a syntax error. Do you mind elaborating?

I probably shouldn't have posted the code. The problem is that when movePosition() is called, and runallowed gets set to true, runMotors() hasn't been called yet, so physically they haven't been turned on yet. That is why regardless of your delay, the steppers don't move. So you have to call runMotors() to physically turn them on. Then you tell them where to go, then you wait i guess, does the moving of the steppers happen in the background, or is it done by using timer interrupts ?Anyway, just remember, after you set the boolean (true or false), you have to call runMotors() before the new setting will take effect.

void MovePosition()
{
  runallowed = true; //enable outputs
  stepper1.moveTo(CoordinateX); //move large motor to coordinates defined in the serial cases
  stepper2.moveTo(CoordinateY); //move small motor to coordinates defined in the serial cases
}

The runallowed boolean is true when the motors are running, so if you used that as the boolean I suggested in reply #1 you could use it to control whether the "done moving" message is printed in conjunction with the distanceToGo() value. Put the test in loop() so that it occurs frequently

Incidentally, your switch/case testing the user input would not be necessary if you used the input as the index to arrays of coordinate values. Subtracting '0' from receivedCommand will give you a value to be used as the array index. This would make the code considerably shorter

Topics on the same project merged to avoid duplication of effort in responding to both

I have not thoroughly read through your comments yet, but it does seem like one comment here is from a different topic so heads up gilshutz

Okay guys so I figured out the thing with disabling the outputs when the motor isnt running, your explanation on the order in which my code is running was extremely helpful, and the solution I believe was pretty simple.

void loop()
{
  checkSerial(); //check for recieved commands on the serial monitor
  runMotor(); //enables or disables motor outputs
  if (stepper1.distanceToGo() != 0)
  {
    motorRunning = true;
  }
  else
  {
    motorRunning = false;
    runallowed = false;
  }
}

I am still stuck on printing "Done Moving" when it is done though. You had mentioned using the bool (I chose motorRunning) to control when it prints, but now it just spams it because it always sits on false when the motor is not running. You said something about setting it back to true after printing, but if I am using it in conjunction with disabling the outputs, that would cause the outputs to be default enabled. Its very possible I'm misunderstanding something. Is there a way to track a state change of the motorRunning bool so it only prints "Done Moving" when it goes from true to false? Thats the solution in my mind at the moment.

clutchcurl:
Is there a way to track a state change of the motorRunning bool so it only prints "Done Moving" when it goes from true to false? Thats the solution in my mind at the moment.

Yes If you look in the Examples folder you will find a sketch called "state change detection" that shows how it can be done.

Thanks everyone, this issue is resolved!

Congratulations

Please post your code for future reference and for suggestions on possible enhancements

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.