Toggle Stepper Direction Using Push Button

Folks,

I'm trying to change the direction of a stepper motor using a momentary push button. That is, with each press the motor reverses direction. I've been at it for a couple days and have not worked it out so I'm turning to the expertise of the forum. After writing and rewriting the code about 12 times, it's time to seek some help.

I did find a couple other posts where users had a similar project but a couple differences are preventing me from figuring it out. One, I'm using the AccelStepper library (Stepper.h was used in the other posts). And I'm also using the ezButton library whereas the other users were not. I'm still quite new to coding but I'm trying to do my best to move my knowledge base forward.

Currently, with the provided code nothing is happening. No movement on the button press. In the past I at least had the motor spinning one direction.

#include <AccelStepper.h>     // Include AccelStepper
#include <ezButton.h>         // Include ezButton
 
#define MP1   8               // IN1 on the ULN2003     //motor Pins
#define MP2   10              // IN2 on the ULN2003
#define MP3   9               // IN3 on the ULN2003
#define MP4   11              // IN4 on the ULN2003

#define MotorInterfaceType 8    // half step

//Define sequence (IN1-IN3-IN2-IN4)
AccelStepper stepper1 = AccelStepper(MotorInterfaceType, MP1, MP3, MP2, MP4);   


ezButton button(12);                    // set pin 12 as ezButton


int motorDirection = 1;                 // direction - 1 is positive direction

int directionButtonState;
int directionLastButtonState;
int stepsPerRevolution;

int motorSpeed = 200;                   // set motor speed
int acceleration = 200;                 // set acceleration
int maxSpeed = 1000;                    // max motor speed

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

  button.setDebounceTime(100);                // debounce button

  stepper1.setMaxSpeed(maxSpeed);             // max motor speed
  stepper1.setAcceleration(acceleration);     // max acceleration 
  stepper1.setSpeed(motorSpeed);              // set motor speed

}


void loop() 
{
  
  button.loop();                                // call loop for exButton

  if (directionButtonState != directionLastButtonState) 
  {
        if (directionButtonState == HIGH) 
        {
          stepper1.moveTo(stepsPerRevolution);
          stepper1.run();
        } 
  
        if (directionButtonState == LOW) 
        {
          stepper1.moveTo(-stepsPerRevolution);
          stepper1.run();
        }
 
  }
 
 directionLastButtonState = directionButtonState;

}

Any help/input would be appreciated.

Best,

Tony

Hello

You never use button to verify if it was pressed, and this condition if (directionButtonState != directionLastButtonState) will never be true

Well, I have some movement but no directional change upon push button. I feel like I'm making this harder than it has to be but I'm struggling to see the code necessary to make it work.

I think I need to step away from in for a few minutes.

#include <AccelStepper.h>     // Include AccelStepper
#include <ezButton.h>         // Include ezButton
 
#define MP1   8               // IN1 on the ULN2003     //motor Pins
#define MP2   10              // IN2 on the ULN2003
#define MP3   9               // IN3 on the ULN2003
#define MP4   11              // IN4 on the ULN2003

#define MotorInterfaceType 8    // half step

//Define sequence (IN1-IN3-IN2-IN4)
AccelStepper stepper1 = AccelStepper(MotorInterfaceType, MP1, MP3, MP2, MP4);   


ezButton button(12);                    // set pin 12 as ezButton


int motorDirection = 1;                 // direction - 1 is positive direction

int motorDestination = 4096;

int motorSpeed = 200;                   // set motor speed
int acceleration = 200;                 // set acceleration
int maxSpeed = 1000;                    // max motor speed

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

  button.setDebounceTime(100);                // debounce button

  stepper1.setMaxSpeed(maxSpeed);             // max motor speed
  stepper1.setAcceleration(acceleration);     // max acceleration 
  stepper1.setSpeed(motorSpeed);              // set motor speed


}


void loop() 
{
  
      button.loop();                                // call loop for exButton

      if (button.isPressed()) 
      {
            motorDirection = !motorDirection;
      }


      if (stepper1.currentPosition() != motorDestination)
      {
      stepper1.moveTo(motorDestination);
      stepper1.run();
      }  

        
}

Best,

Tony

Another stab at it but no success.

#include <AccelStepper.h>     // Include AccelStepper
#include <ezButton.h>         // Include ezButton
 
#define MP1   8               // IN1 on the ULN2003     //motor Pins
#define MP2   10              // IN2 on the ULN2003
#define MP3   9               // IN3 on the ULN2003
#define MP4   11              // IN4 on the ULN2003

#define MotorInterfaceType 8    // half step

//Define sequence (IN1-IN3-IN2-IN4)
AccelStepper stepper1 = AccelStepper(MotorInterfaceType, MP1, MP3, MP2, MP4);   


ezButton button(12);                    // set pin 12 as ezButton


int motorDirection = 1;                 // direction - 1 is positive direction

//int motorDestination = 4096;

int motorSpeed = 600;                   // set motor speed
int acceleration = 200;                 // set acceleration
int maxSpeed = 1000;                    // max motor speed

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

  button.setDebounceTime(100);                // debounce button

  stepper1.setMaxSpeed(maxSpeed);             // max motor speed
  stepper1.setAcceleration(acceleration);     // max acceleration 
  stepper1.setSpeed(motorSpeed);              // set motor speed


}


void loop() 
{
  
      button.loop();                                // call loop for ezButton

      if (button.isPressed())                       // check to see if button is pressed
      {
            motorDirection = !motorDirection;       // if pressed chnage motorDirection
      }

      stepper1.setSpeed(motorSpeed);              // set motor speed
      stepper1.moveTo(stepper1.currentPosition() + motorDirection);     // set motor to move to stepper current position + motorDirection
      stepper1.run();                                                   // run motor
        

       
}

Any help would be appreciated.

Best,

Tony

To run the motor in the opposite direction, change the sign of setSpeed(), for example:

    motorSpeed = motorSpeed * -1;

(you were using motorDirection = !motorDirection;

xfpd,

The code is working fine now. I never would have guessed motorSpeed. Thank you for your help.

Best,

Tony

The code:

#include <AccelStepper.h>     // Include AccelStepper
#include <ezButton.h>         // Include ezButton
 
#define MP1   8               // IN1 on the ULN2003     //motor Pins
#define MP2   10              // IN2 on the ULN2003
#define MP3   9               // IN3 on the ULN2003
#define MP4   11              // IN4 on the ULN2003

#define MotorInterfaceType 8    // half step

//Define sequence (IN1-IN3-IN2-IN4)
AccelStepper stepper1 = AccelStepper(MotorInterfaceType, MP1, MP3, MP2, MP4);   

ezButton button(12);                    // set pin 12 as ezButton

int motorDirection = 1;                 // direction - 1 is positive direction
int motorSpeed = 600;                   // set motor speed
int acceleration = 200;                 // set acceleration
int maxSpeed = 1000;                    // max motor speed

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

  button.setDebounceTime(100);                // debounce button

  stepper1.setMaxSpeed(maxSpeed);             // max motor speed
  stepper1.setAcceleration(acceleration);     // max acceleration 
  stepper1.setSpeed(motorSpeed);              // set motor speed

}


void loop() 
{
  
      button.loop();                                // call loop for ezButton

      if (button.isPressed())                       // check to see if button is pressed
      {
            motorSpeed = motorSpeed * -1;             // to run motor in the opposite direction
      }

      stepper1.setSpeed(motorSpeed);              // set motor speed
      stepper1.moveTo(stepper1.currentPosition() + motorDirection);     // set motor to move to stepper current position + motorDirection
      stepper1.run();                                                   // run motor  

}

Best,

T

But this is not the way AccelStepper should be used. It's more of a coincidence that it works.

setSpeed() and run() should not be used together, because run() uses setSpeed() internally.
run() is used to move the stepper to a destination ( e.g. set by moveTo()). To change the direction of movement, you must change the target position to the opposite side.

If you don't want the motor to stop at a target position but to run endlessly, you can use runSpeed() together with setSpeed. Than you don't need to set a target position ( no moveTo() ) and you can change the direction by changing the sign in setSpeed. But you will not have acceleratio/decleration in that case.

Here is an example based on your Sketch in post #3:

#include <AccelStepper.h>     // Include AccelStepper
#include <ezButton.h>         // Include ezButton
 
#define MP1   8               // IN1 on the ULN2003     //motor Pins
#define MP2   10              // IN2 on the ULN2003
#define MP3   9               // IN3 on the ULN2003
#define MP4   11              // IN4 on the ULN2003

#define MotorInterfaceType 8    // half step

//Define sequence (IN1-IN3-IN2-IN4)
AccelStepper stepper1 = AccelStepper(MotorInterfaceType, MP1, MP3, MP2, MP4);   


ezButton button(12);                    // set pin 12 as ezButton


int motorDirection = 1;                 // direction - 1 is positive direction

int motorDestination = 4096;

int motorSpeed = 200;                   // set motor speed
int acceleration = 200;                 // set acceleration
int maxSpeed = 1000;                    // max motor speed

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

  button.setDebounceTime(100);                // debounce button

  stepper1.setMaxSpeed(maxSpeed);             // max motor speed
  stepper1.setAcceleration(acceleration);     // max acceleration 
  stepper1.setSpeed(motorSpeed);              // set motor speed


}


void loop() 
{
  
      button.loop();                                // call loop for exButton

      if (button.isPressed()) 
      {
            //motorDirection = !motorDirection;
            motorDestination = -motorDestination;
            stepper1.moveTo(motorDestination);
      }


      if (stepper1.currentPosition() != motorDestination)
      {
      //stepper1.moveTo(motorDestination);
      stepper1.run();
      }  
        
}

And this is based on your last sketch, without acceleration/deceleration:

#include <AccelStepper.h>     // Include AccelStepper
#include <ezButton.h>         // Include ezButton
 
#define MP1   8               // IN1 on the ULN2003     //motor Pins
#define MP2   10              // IN2 on the ULN2003
#define MP3   9               // IN3 on the ULN2003
#define MP4   11              // IN4 on the ULN2003

#define MotorInterfaceType 8    // half step

//Define sequence (IN1-IN3-IN2-IN4)
AccelStepper stepper1 = AccelStepper(MotorInterfaceType, MP1, MP3, MP2, MP4);   

ezButton button(12);                    // set pin 12 as ezButton

int motorDirection = 1;                 // direction - 1 is positive direction
int motorSpeed = 600;                   // set motor speed
int acceleration = 200;                 // set acceleration
int maxSpeed = 1000;                    // max motor speed

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

  button.setDebounceTime(100);                // debounce button

  stepper1.setMaxSpeed(maxSpeed);             // max motor speed
  stepper1.setAcceleration(acceleration);     // max acceleration 
  stepper1.setSpeed(motorSpeed);              // set motor speed

}


void loop() 
{
  
      button.loop();                                // call loop for ezButton

      if (button.isPressed())                       // check to see if button is pressed
      {
            motorSpeed = motorSpeed * -1;             // to run motor in the opposite direction
            stepper1.setSpeed(motorSpeed);              // set motor speed
      }

      stepper1.runSpeed();                                                   // run motor  

}

MicroBahner,

Thank you for your advice. I've uploaded the code and it's working great. While I have you, I have one more (hopefully small) question. I make up little projects and work toward completing them to lean to program the Arduino and C++.

My next little project is a pushbutton and a stepper motor that spins faster when the button is held in, and slower upon release.

Again, I think I'm close but I can't seem to solve the equation, so to speak.

I've posted the code below. Would you take a peek at it and lend me your thoughts?

Best

Tony

#include <AccelStepper.h>     // Include AccelStepper
#include <ezButton.h>         // Include ezButton
 
#define MP1   8               // IN1 on the ULN2003     //motor Pins
#define MP2   10              // IN2 on the ULN2003
#define MP3   9               // IN3 on the ULN2003
#define MP4   11              // IN4 on the ULN2003

#define MotorInterfaceType 8    // half step

//Define sequence (IN1-IN3-IN2-IN4)
AccelStepper stepper1 = AccelStepper(MotorInterfaceType, MP1, MP3, MP2, MP4);   

ezButton button(12);                    // set pin 12 as ezButton

int motorDirection = 1;                 // direction - 1 is positive direction
int motorSpeed = 200;                   // set motor speed
int fastMotorSpeed = 800;
int acceleration = 400;                 // set acceleration
int maxSpeed = 1000;                    // max motor speed

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

  button.setDebounceTime(100);                // debounce button

  stepper1.setMaxSpeed(maxSpeed);             // max motor speed
  stepper1.setAcceleration(acceleration);     // max acceleration 
  stepper1.setSpeed(motorSpeed);              // set motor speed

}


void loop() 
{
  
      button.loop();                                        // call loop for ezButton

    while (button.isPressed())                              // check to see if button is being held
    {
            stepper1.setSpeed(fastMotorSpeed);              // set motor speed
            stepper1.moveTo(stepper1.currentPosition() + motorDirection);     // set motor to move to stepper current position + motorDirection
            stepper1.run();                                 // run motor
    }


    stepper1.setSpeed(motorSpeed);                                    // set motor speed
    stepper1.moveTo(stepper1.currentPosition() + motorDirection);     // set motor to move to stepper current position + motorDirection
    stepper1.run();                                                   // run motor  
     
                                                               
}

Thanks, again.

T

@MicroBahner @tperry724 - Sorry for the "hold my beer" answer. I recognize MicroBahner as an authority (and author) in motors, and happy to have learned more today. I am not trying to excuse my answer, but here is how I came to my wrong answer... if negating motorDirection does not work, I saw motorDirection possibly having three states (forward, stop, reverse) ... so, knowing "speed in the negative direction" is the vector "reverse" ... and with a short look at the AccelStepper, and seeing motorSpeed as "close enough" the fact that it worked I saw as "a term in physics" ... heh. But really, thank you for removing my duct tape and not spilling my beer. Party on, Garth.

What do you observe this sketch doing?

xfpd,

Great sense of humor by the way.

My sketch turns the motor slowly. A button press and hold speeds up the motor but upon release, the motor does not slow down again. It continues to spin faster.

Best,

T

Tinkering... it seems "isPressed()" does not have a "longPress". [meaning, it detects a long press as one press, not a long press as multiple presses] I tested two lines in your code

Inside button.isPressed():

      fastMotorSpeed++; if (fastMotorSpeed > 2000) fastMotorSpeed = 2000;

Outside button.isPressed():

      fastMotorSpeed--; if (fastMotorSpeed < 800) fastMotorSpeed = 800;

My aim was "if the button is being pressed, increase speed toward maximum" else "decrease speed toward the minimum." But, all I got was an increase on buttonPress, not isHeld...

I think there are several problems.
First problem: As already mentioned setSpeed() and run() don't fit together. You must decide if you want to have acceleration/deceleration and moving to a target, or if you want to spin endless, but without acceleration and deceleration.

Second problem is the usage of the ezButton library. Your while loop will never end, because you never call button.loop() when entering the while loop. So the result of button.isPressed() will always be the same and never change. And button.isPressed() doen NOT return the true while the button is pressed, but only when it becomes pressed (state change detection ).
This is a version without acceleration and deceleration and endless moving:

#include <AccelStepper.h>     // Include AccelStepper
#include <ezButton.h>         // Include ezButton
 
#define MP1   8               // IN1 on the ULN2003     //motor Pins
#define MP2   10              // IN2 on the ULN2003
#define MP3   9               // IN3 on the ULN2003
#define MP4   11              // IN4 on the ULN2003

#define MotorInterfaceType 8    // half step

//Define sequence (IN1-IN3-IN2-IN4)
AccelStepper stepper1 = AccelStepper(MotorInterfaceType, MP1, MP3, MP2, MP4);   

ezButton button(12);                    // set pin 12 as ezButton

int motorDirection = 1;                 // direction - 1 is positive direction
int motorSpeed = 200;                   // set motor speed
int fastMotorSpeed = 800;
int acceleration = 400;                 // set acceleration
int maxSpeed = 1000;                    // max motor speed

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

  button.setDebounceTime(100);                // debounce button

  stepper1.setMaxSpeed(maxSpeed);             // max motor speed
  stepper1.setAcceleration(acceleration);     // max acceleration 
  stepper1.setSpeed(motorSpeed);              // set motor speed

}


void loop() 
{
      button.loop();                                        // call loop for ezButton

    if (button.isPressed())                              // check to see if button is pressed
    {
      Serial.println("Button isPressed");
      stepper1.setSpeed(fastMotorSpeed);
    }

    if (button.isReleased())                              // check to see if button is released
    {
      Serial.println("Button isReleased");
      stepper1.setSpeed(motorSpeed);
    }


    stepper1.runSpeed();                                                   // run motor  
     
                                                               
}

Yes, .isPressed() detects the moment when the button becomes pressed and is true only during one loop() cycle ( until the next call of button.loop() ). .isReleased() is the same when the button is release. I inserted a Serial.print() where you can see that. Only one line is printed when you press the Button and another line when you release it.

Maybe some logic can be added: <<while "press" "wasPressed" without "release", increase (something)>>

MicroBahner,

Code works.

Every time I have some small success with coding and I'm feeling confident, I'm quickly humbled by an obstacle with my next learning exercise.

Thanks to you and xfpd for carving out time from your day for me.

Best,

Tony

xfpd,

Thank you for helping me. I know time is precious and it means a lot that folks like you spare some for me.

Best,

Tony

@tperry724 :
Hi Tony,
if you want to accelerate/decelerate between the two speeds things will become more difficult. With acceleration you must use the run() method, and the speed it accelerates to is set by setMaxSpeed(). If you increase MaxSpeed, it accelerates to the new speed. But if you decrease MaxSpeed it doesn't slow down to the new speed, but changes it immediately.

You could switch to my MobaTools library. This library is able to ramp up and down when changing the speed.

#include <MobaTools.h>     // Include MobaTools
#include <ezButton.h>         // Include ezButton
 
#define MP1   8               // IN1 on the ULN2003     //motor Pins
#define MP2   10              // IN2 on the ULN2003
#define MP3   9               // IN3 on the ULN2003
#define MP4   11              // IN4 on the ULN2003

#define MotorInterfaceType 8    // half step

//Define sequence (IN1-IN3-IN2-IN4)
MoToStepper stepper1 ( 2048, HALFSTEP );

ezButton button(12);                    // set pin 12 as ezButton

int motorDirection = 1;                 // direction - 1 is positive direction
int motorSpeed = 200*10;                   // speed in steps/10sec
int fastMotorSpeed = 800*10;
int acceleration = 400;                 // set acceleration
int maxSpeed = 1000;                    // max motor speed

void setup() 
{
  Serial.begin(115200);

  button.setDebounceTime(100);                // debounce button
  stepper1.attach(MP1, MP2, MP3, MP4);        // depending on the driver used, MP2/MP3 my need to be swapped
  stepper1.setSpeedSteps(motorSpeed);             // motor speed
  stepper1.setRampLen(200);     // acceleration 
  stepper1.rotate(1);
}

void loop() 
{
      button.loop();                                        // call loop for ezButton

    if (button.isPressed())                              // check to see if button is being held
    {
      Serial.println("Button isPressed");
      stepper1.setSpeedSteps(fastMotorSpeed);
    }

    if (button.isReleased())                              // check to see if button is being held
    {
      Serial.println("Button isReleased");
      stepper1.setSpeedSteps(motorSpeed);
    }
                                                               
}

If you are interested, you can give it a try :wink: .
Regards,
FP

MicroBahner,

When I saw your library I was encouraged. I like making little projects and your library may help. Currently, I'm making a dinosaur for my grandson. It will move its legs (servos), roar, have LEDs for eyes, and a series of LEDs down its spine.

My next project was going to be a small trainyard for another of my grandsons (we've got four of these things running around). I just need to dream up what it will look like. While I'm on the subject, what would be a nice, solid, smooth running train for a young boy. Although he will be 4 1/2 when I hand it over to him, I think he has the personality to enjoy the railroad without destroying it the first week.

You library seems to complement what I find myself interested in making. Thank you for sharing.

Best,

Tony