Running two stepper motors at once

Hello, I'm looking for some help controlling two Nema 14 Bipolar stepper motors using two Pololu A4988 stepper drivers. I aim to move the stepper motors at the same time back and forth, in opposite directions.

The current code I have runs each motor on a button press, but they are running one after the other. When I try to put the code for the two stepper motors together, the motors jerk little distances at a time. Is this due to blocking? Do I need to be using millis()? If so, some coding suggestions would be appreciated.

Im using digitalWrite(enPin1, HIGH); to disable power to the stepper motor so that it won't use power/overheat when sitting idle for long periods.

Ive tried to absorb Simple Stepper Program and Stepper Motor Basics, as well as similar posts here on the forum, but I still cant figure it out.

Grateful for any help :slight_smile:

#include <AccelStepper.h>

#define dirPinLeft 7
//#define stepPinLeft 8
#define stepPin 8   // shared step pin so both motors move at the same time
#define dirPinRight 3
//#define stepPinRight 4
#define motorInterfaceType 1

const int enPin1 = 9;     // enable pin 1
const int enPin2 = 10;    // enable pin 2
const int buttonPin = 5;  // button pin
int buttonState = 0;  // variable for reading the pushbutton status

AccelStepper stepperLeft = AccelStepper(motorInterfaceType, stepPin, dirPinLeft);
AccelStepper stepperRight = AccelStepper(motorInterfaceType, stepPin, dirPinRight);

void setup() {
  stepperLeft.setMaxSpeed(200);
  stepperLeft.setAcceleration(150);
  stepperRight.setMaxSpeed(200);
  stepperRight.setAcceleration(150);
  pinMode(buttonPin, INPUT);
  pinMode(enPin1, INPUT);
  pinMode(enPin2, INPUT);
  digitalWrite(buttonPin, HIGH);  // activate internal pull up
  digitalWrite(enPin1, HIGH);     // disable motor control
  digitalWrite(enPin2, HIGH);     // disable motor control
}

void loop() {
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);
  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == LOW) {
    leftMove();
    rightMove();
  } else {
    digitalWrite(enPin1, HIGH);  // disable motor control
    digitalWrite(enPin2, HIGH);  // disable motor control
  }
}

void leftMove() {
  digitalWrite(enPin1, LOW);  // enable motor control
  for (int c = 0; c < 1; c++) {
    stepperLeft.moveTo(-35);
    while (stepperLeft.currentPosition() != -35)  // Full speed up to -35
      stepperLeft.run();
    stepperLeft.stop();  // Stop as fast as possible: sets new target
    stepperLeft.runToPosition();
    // Now stopped after quickstop

    stepperLeft.moveTo(35);                     // go backwards
    while (stepperLeft.currentPosition() != 0)  /// Full speed back to 0
      stepperLeft.run();
    stepperLeft.stop();           /// Stop as fast as possible: sets new target
    stepperLeft.runToPosition();  /// stopped
  }
  digitalWrite(enPin1, HIGH);  /// disable motor control
}

void rightMove() {
  for (int c = 0; c < 1; c++) {
    digitalWrite(enPin2, LOW);  /// enable motor control
    stepperRight.moveTo(35);
    while (stepperRight.currentPosition() != 35)  /// Full speed up tp -35
      stepperRight.run();
    stepperRight.stop();  /// Stop as fast as possible: sets new target
    stepperRight.runToPosition();
    // Now stopped after quickstop
    stepperRight.moveTo(-35);                     // go backwards
    while (stepperRight.currentPosition() != -35)  // Full speed back to 0
      stepperRight.run();
    stepperRight.stop();           // Stop as fast as possible: sets new target
    stepperRight.runToPosition();  // stopped
  }
  digitalWrite(enPin2, HIGH);  // disable motor control
}

Your LeftMove() and RightMove() functions are "blocking" and will prevent each other from working simultaneously.

See:

1 Like

something to play with..

UnoServo

2 motors non blocking..

have fun.. ~q

2 Likes

It is due to blocking. You don't need millis() because Accelstepper does the non-blocking timing within itself.

Did you see the multiple stepper example:

https://www.airspayce.com/mikem/arduino/AccelStepper/MultipleSteppers_8pde-example.html


// MultiStepper.pde
// -*- mode: C++ -*-
//
// Shows how to multiple simultaneous steppers
// Runs one stepper forwards and backwards, accelerating and decelerating
// at the limits. Runs other steppers at the same time
//
// Copyright (C) 2009 Mike McCauley
// $Id: MultiStepper.pde,v 1.1 2011/01/05 01:51:01 mikem Exp mikem $
 
#include <AccelStepper.h>
 
// Define some steppers and the pins the will use
AccelStepper stepper1; // Defaults to AccelStepper::FULL4WIRE (4 pins) on 2, 3, 4, 5
AccelStepper stepper2(AccelStepper::FULL4WIRE, 6, 7, 8, 9);
AccelStepper stepper3(AccelStepper::FULL2WIRE, 10, 11);
 
void setup()
{  
    stepper1.setMaxSpeed(200.0);
    stepper1.setAcceleration(100.0);
    stepper1.moveTo(24);
    
    stepper2.setMaxSpeed(300.0);
    stepper2.setAcceleration(100.0);
    stepper2.moveTo(1000000);
    
    stepper3.setMaxSpeed(300.0);
    stepper3.setAcceleration(100.0);
    stepper3.moveTo(1000000); 
}
 
void loop()
{
    // Change direction at the limits
    if (stepper1.distanceToGo() == 0)
        stepper1.moveTo(-stepper1.currentPosition());
    stepper1.run();
    stepper2.run();
    stepper3.run();
}// MultiStepper.pde
// -*- mode: C++ -*-
//
// Shows how to multiple simultaneous steppers
// Runs one stepper forwards and backwards, accelerating and decelerating
// at the limits. Runs other steppers at the same time
//
// Copyright (C) 2009 Mike McCauley
// $Id: MultiStepper.pde,v 1.1 2011/01/05 01:51:01 mikem Exp mikem $
 
#include <AccelStepper.h>
 
// Define some steppers and the pins the will use
AccelStepper stepper1; // Defaults to AccelStepper::FULL4WIRE (4 pins) on 2, 3, 4, 5
AccelStepper stepper2(AccelStepper::FULL4WIRE, 6, 7, 8, 9);
AccelStepper stepper3(AccelStepper::FULL2WIRE, 10, 11);
 
void setup()
{  
    stepper1.setMaxSpeed(200.0);
    stepper1.setAcceleration(100.0);
    stepper1.moveTo(24);
    
    stepper2.setMaxSpeed(300.0);
    stepper2.setAcceleration(100.0);
    stepper2.moveTo(1000000);
    
    stepper3.setMaxSpeed(300.0);
    stepper3.setAcceleration(100.0);
    stepper3.moveTo(1000000); 
}
 
void loop()
{
    // Change direction at the limits
    if (stepper1.distanceToGo() == 0)
        stepper1.moveTo(-stepper1.currentPosition());
    stepper1.run();
    stepper2.run();
    stepper3.run();
}

Since you need a button-press, consider adapting the built-in state change detection example to control the targets of the steppers:

https://docs.arduino.cc/built-in-examples/digital/StateChangeDetection

Here's a simulation of moving two steppers at once with AccelStepper:

1 Like

Switches are more often wired to ground and an input set to INPUT_PULLUP so that an external resistor is not required. My state change for active low inputs tutorial shows switch wiring and example code.

2 Likes

Thanks everyone. I got it working by following the MultiStepper example but also by enabling and disabling the steppers at certain points. I will work on incorporating the buttons as per advice.

This is the working code:

#include <AccelStepper.h>

#define dirPinLeft 7
#define stepPinLeft 8
#define dirPinRight 3
#define stepPinRight 4
#define motorInterfaceType 1

AccelStepper stepperLeft = AccelStepper(motorInterfaceType, stepPinLeft, dirPinLeft);
AccelStepper stepperRight = AccelStepper(motorInterfaceType, stepPinRight, dirPinRight);

const int enPin1 = 9;   // enable pin 1
const int enPin2 = 10;  // enable pin 2
//const int buttonPin = 5;  // button pin
//int buttonState = 0;  // variable for reading the pushbutton status

void setup() {
  stepperLeft.setMaxSpeed(1000);
  stepperLeft.setAcceleration(300);
  stepperLeft.moveTo(30);

  stepperRight.setMaxSpeed(1000);
  stepperRight.setAcceleration(300);
  stepperRight.moveTo(-30);

  //pinMode(buttonPin, INPUT);
  pinMode(enPin1, INPUT);
  pinMode(enPin2, INPUT);
  //digitalWrite(buttonPin, HIGH);  // activate internal pull up
  digitalWrite(enPin1, HIGH);  // disable motor control
  digitalWrite(enPin2, HIGH);  // disable motor control
}

void loop() {
  slowMove();
}

void slowMove() {
  digitalWrite(enPin1, LOW);  // enable motor control
  digitalWrite(enPin2, LOW);  // enable motor control

  if (stepperLeft.distanceToGo() == 0)
    stepperLeft.moveTo(-stepperLeft.currentPosition());
  stepperLeft.run();

  if (stepperRight.distanceToGo() == 0)
    stepperRight.moveTo(-stepperRight.currentPosition());
  stepperRight.run();

  digitalWrite(enPin1, HIGH);  // disable motor control
  digitalWrite(enPin2, HIGH);  // disable motor control
}

Does anyone know how I limit the number of times the motor moves back and forth? I suspect distanceToGo() might not be appropriate, or maybe run() is not right. Can't quite get my head around it.

How many times? If it is, for example, 5 times after every button press, I'd use the state-detection example, set a static or global variable to 5, and then decrement it each time a cycle completes.

Consider how this new 'processEnablerStateVariableCount' variable works in this modified State Change Detection example:

/*
  State change detection (edge detection)
 Often, you don't need to know the state of a digital input all the time,
 but you just need to know when the input changes from one state to another.
 For example, you want to know when a button goes from OFF to ON.  This is called
 state change detection, or edge detection.
 This example shows how to detect when a button or button changes from off to on
 and on to off.
 The circuit:
 * pushbutton attached to pin 2 from +5V
 * 10K resistor attached to pin 2 from ground
 * LED attached from pin 13 to ground (or use the built-in LED on
   most Arduino boards)
 created  27 Sep 2005
 modified 30 Aug 2011
 by Tom Igoe
This example code is in the public domain.
 http://www.arduino.cc/en/Tutorial/ButtonStateChange
 */

// this constant won't change:
const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

int processEnablerStateVariableCount = 0; // state variable for 5-count process enabling

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button
      // wend from off to on:
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes:  ");
      Serial.println(buttonPushCounter);
      if(processEnablerStateVariableCount == 0 ){
          processEnablerStateVariableCount = 5 ; // initialize count
          Serial.println("Meta-cycle count initiated");
      }
    } else {
      // if the current state is LOW then the button
      // wend from on to off:
      Serial.println("off");
      if (buttonPushCounter % 4 == 0){
            if(processEnablerStateVariableCount > 0) {
          --processEnablerStateVariableCount; 
          Serial.print("Meta-cycles left: ");
          Serial.println(processEnablerStateVariableCount); 

      }

      }
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;


  // turns on the LED every four button pushes by
  // checking the modulo of the button push counter.
  // the modulo function gives you the remainder of
  // the division of two numbers:
  if (buttonPushCounter % 4 == 0) {
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }

}

By setting an maintaining a "state variable", you can then use it in a conditional like if(processEnablerStateVariableCount > 0){...} as a virtual button or switch for controlling other things.

In your use-case, you could set a similar state variable with a button press, and decrement it with each oscillation, and make movement dependent on if(processEnablerStateVariableCount > 0)

1 Like

Any amount of times really. I'd like to use this nonblocking function (a la MultipleSteppers) but a for loop doesn't work (it works perfectly without the for loop, but it moves indefinitely):

void test01() {
  for (int c = 0; c < 2; c++) {
  digitalWrite(enPin1, LOW);  /// enable motor control
  digitalWrite(enPin2, LOW);  /// enable motor control

  if (stepperLeft.distanceToGo() == 0)
    stepperLeft.moveTo(-stepperLeft.currentPosition());
  stepperLeft.run();

  if (stepperRight.distanceToGo() == 0)
    stepperRight.moveTo(-stepperRight.currentPosition());
  stepperRight.run();

  digitalWrite(enPin1, HIGH);  /// disable motor control
  digitalWrite(enPin2, HIGH);  /// disable motor control
  }
}

Instead, I thought I'd try a simple function like this, but it jams up and doesn't move, with or without the for loop:

void test01() {
  stepperLeft.setMaxSpeed(2000);
  stepperLeft.setAcceleration(300);
  stepperRight.setMaxSpeed(2000);
  stepperRight.setAcceleration(300);
  stepperLeft.moveTo(35);    /// Set the target position:
  stepperRight.moveTo(-35);  /// Set the target position:

  // for (int c = 0; c < 2; c++) {
  digitalWrite(enPin1, LOW);  /// enable motor control
  digitalWrite(enPin2, LOW);  /// enable motor control

  stepperLeft.run();   /// Run to target position with set speed and acceleration/deceleration:
  stepperRight.run();  /// Run to target position with set speed and acceleration/deceleration:

  stepperLeft.moveTo(0);   /// Move back to zero:
  stepperRight.moveTo(0);  /// Move back to zero:
  stepperLeft.run();
  stepperRight.run();

  digitalWrite(enPin1, HIGH);  /// disable motor control
  digitalWrite(enPin2, HIGH);  /// disable motor control
  // }
}

Does anyone know if there's another method in the AccelStepper library to do this? Thanks.

Test a state variable in the condition:

int processEnablerStateVariableCount = 2;  // global state variable for remembering how many times to go

void test01() {
  for (int c = 0; c < 2; c++) {
  digitalWrite(enPin1, LOW);  /// enable motor control
  digitalWrite(enPin2, LOW);  /// enable motor control

  if (stepperLeft.distanceToGo() == 0 && processEnablerStateVariableCount > 0){
    stepperLeft.moveTo(-stepperLeft.currentPosition());
    --processEnablerStateVariableCount; // reduce the count by one
  }
  stepperLeft.run();

  if (stepperRight.distanceToGo() == 0 && processEnablerStateVariableCount > 0)
    stepperRight.moveTo(-stepperRight.currentPosition());
  stepperRight.run();

  digitalWrite(enPin1, HIGH);  /// disable motor control
  digitalWrite(enPin2, HIGH);  /// disable motor control
  }
}

1 Like

Thank you so much, @DaveX. I didn't fully grok your first suggestion of using processEnablerStateVariableCount, but seeing it in situ has dropped the penny. I now have it working as advertised, so thanks again for your help.

1 Like

You're welcome. Happy Coding!

Do please post your working code. Mine was just a sloppy hack.

Working code*:

#include <AccelStepper.h>

#define dirPinLeft 7          /// direction left
#define stepPinLeft 8         /// step left
#define dirPinRight 3         /// direction right
#define stepPinRight 4        /// step right
#define motorInterfaceType 1  /// bipolar motor

AccelStepper stepperLeft = AccelStepper(motorInterfaceType, stepPinLeft, dirPinLeft);
AccelStepper stepperRight = AccelStepper(motorInterfaceType, stepPinRight, dirPinRight);

const int enPin1 = 9;                      /// enable pin 1
const int enPin2 = 10;                     /// enable pin 2
const int buttonPin = 5;                   /// button pin
int buttonState = 0;                       /// variable for reading the pushbutton status
int processEnablerStateVariableCount = 5;  // global state variable for remembering how many times to go

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);  // Button between Pin and Ground
  pinMode(enPin1, INPUT);
  pinMode(enPin2, INPUT);
  digitalWrite(buttonPin, HIGH);  /// activate arduino internal pull up
  digitalWrite(enPin1, HIGH);     /// disable motor control
  digitalWrite(enPin2, HIGH);     /// disable motor control

  stepperLeft.setMaxSpeed(100);  /// slow is 100, fast is 900000
  stepperLeft.setAcceleration(50);
  stepperLeft.moveTo(15);
  stepperRight.setMaxSpeed(100);
  stepperRight.setAcceleration(50);
  stepperRight.moveTo(-15);
}

void loop() {
  slowMove1();
}

void slowMove1() {  /// motors are NOT disabled at end of function
  digitalWrite(enPin1, LOW);  /// enable motor control
  digitalWrite(enPin2, LOW);  /// enable motor control

  if (stepperLeft.distanceToGo() == 0 && processEnablerStateVariableCount > 0) {
    stepperLeft.moveTo(-stepperLeft.currentPosition());
    --processEnablerStateVariableCount;  /// reduce the count by one
  }
  stepperLeft.run();

  if (stepperRight.distanceToGo() == 0 && processEnablerStateVariableCount > 0) {
    stepperRight.moveTo(-stepperRight.currentPosition());
  }
  stepperRight.run();

    digitalWrite(enPin1, HIGH);  /// disable motor control
    digitalWrite(enPin2, HIGH);  /// disable motor control
}

*Unfortunately for my application, this code leaves the stepper motors enabled (i.e., locked in place and therefore getting hot). It's odd because the function below disables them. Thoughts?

void slowMove2() {  /// motors are disabled at end of function
  for (int c = 0; c < 2; c++) {
    digitalWrite(enPin1, LOW);  /// enable motor control
    digitalWrite(enPin2, LOW);  /// enable motor control

    if (stepperLeft.distanceToGo() == 0)
      stepperLeft.moveTo(-stepperLeft.currentPosition());
    stepperLeft.run();

    if (stepperRight.distanceToGo() == 0)
      stepperRight.moveTo(-stepperRight.currentPosition());
    stepperRight.run();

    digitalWrite(enPin1, HIGH);  /// disable motor control
    digitalWrite(enPin2, HIGH);  /// disable motor control
  }
}
1 Like

My first thought would be to not try to make the functions clean up after themselves, but to add a watchdog to periodically disable them when not in use, using something like this untested code:

bool motorsEnabled = false;

...

void loop(void){
  ...
  disableMotorsAsNeeded();
}  
...

void disableMotorsAsNeeded(void){
   uint32_t interval = 1000; 
   static uint32_t lastCheck = -interval;
   if(motorsEnabled && millis() - lastCheck >= interval){
     lastCheck = millis();
     if(stepperLeft.distanceToGo() == 0) && stepperLeft.distanceToGo() == 0 ) {
       digitalWrite(enPin1, HIGH);  /// disable motor control
       digitalWrite(enPin2, HIGH);  /// disable motor control
       motorsEnabled = false;
     }
   }
}

but you then need to enable them when you need to move them:

void slowMove2() {  for (int c = 0; c < 2; c++) {
    if( ! motorsEnabled ){
    digitalWrite(enPin1, LOW);  /// enable motor control
    digitalWrite(enPin2, LOW);  /// enable motor control
    motorsEnabled = true;
}
    if (stepperLeft.distanceToGo() == 0)
      stepperLeft.moveTo(-stepperLeft.currentPosition());
    stepperLeft.run();

    if (stepperRight.distanceToGo() == 0)
      stepperRight.moveTo(-stepperRight.currentPosition());
    stepperRight.run();
 }
}

but my second thought would be that since slowMove1() is the only thing going on in your loop and you aren't doing other moves, it is fine to clean up after itself. It is enabling and disabling the steppers at the beginning and end of each call, and this routing is most of each loop, so they are only disabled for the time it takes for loop() to loop. You could move the processEnablerStateVariableCount
condition around the body of the function:

void slowMove1() {
  if (processEnablerStateVariableCount > 0){

    ...   

  }
}

In this case, it only enables the steppers when needed.

Re: post 12:

  • there's continuous activity on enable pins (the motors never get 100% disabled)
  • the motors change direction and stop at slightly different values

steppermotors are allowed to warm up to 50 to 70 °C without any problem
But you have to avoid getting hotter. If the become hotter than 70°C the magnets are damaged and loose strength.

Without the coils beeing powered permanently you are in danger to loose steps and then your code does no longer know what the exact position of your steppermotor is.

You haven't described anything about your project. So no advice possible.

Except you switch off the coil-current and rotate the stepper-motors by hand they act as electricity generators. Depending on the rpm the amount of voltage created in this way may damage your stepper-motor-driver.

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