Stepper -start run once on button press

Hi there, Wondering if anyone could let me know where I'm going wrong with my code, I have cobbled together to the best of my knowledge a semi-functioning program but one part seems to be giving me trouble. Ok, so I simply want a 28BYJ-48 stepper to rotate one way then the other once a button is pressed then stop until the button is pressed again repeating the same function. So far I have got the stepper to perform its function as desired when the button is pressed but instead of stopping it performs the function repeatedly until I reset. Any help or suggestions would be much appreciated. I'm sure I'm doing something not quite right. Here is my code:

#include <ezButton.h>Preformatted text
#include <Stepper.h>

#define LOOP_STATE_STOPPED 0
#define LOOP_STATE_STARTED 1

#define STEPS 2038 // (28BYJ-48)

Stepper stepper(STEPS, 8, 10, 9, 11);

ezButton button(7); // pin 7;
int loopState = LOOP_STATE_STOPPED;

void setup() {

button.setDebounceTime(50); // set debounce time to 50 milliseconds
}

void loop() {
button.loop();

if (button.isPressed()) {
if (loopState == LOOP_STATE_STOPPED)
loopState = LOOP_STATE_STARTED;
else // if(loopState == LOOP_STATE_STARTED)
loopState = LOOP_STATE_STOPPED;
}

if (loopState == LOOP_STATE_STARTED) {
stepper.setSpeed(8); // 8 rpm
stepper.step(203); // 203 steps
delay(1000); // wait for one second
stepper.setSpeed(6); // 6 rpm
stepper.step(-406); // do -406 steps
}
}

You should post code by using code-tags

click on the pencil-icon below your posting to actiavte edit mode

There is an automatic function for posting code in the Arduino-IDE
just three steps

  1. press Ctrl-T for autoformatting your code
  2. do a rightclick with the mouse and choose "copy for forum"
  3. paste clipboard into write-window of a posting

write down a detailed description what do you see if you let the code run.

best regards Stefan

1 Like

should you set loopState to LOOP_STATE_STOPPED after the stepper motor operations complete?

1 Like

Look at your loop code closely. It is doing exactly what you have instructed. When you press the button then loopState is set to LOOP_STATE_STARTED. The next if statement checks to see if loopState is equal to LOOP_STATE_STARTED and, if so, rotates the stepper. Next time loop() is executed if the button has not been pressed again then loopState still equals LOOP_STATE_STARTED and therefore the stepper rotates again. One solution is to set loopState to LOOP_STATE_STOPPED after rotating as shown below.

void loop() {
  button.loop();

  if (button.isPressed()) {
    if (loopState == LOOP_STATE_STOPPED)
      loopState = LOOP_STATE_STARTED;
    else // if(loopState == LOOP_STATE_STARTED)
      loopState = LOOP_STATE_STOPPED;
  }

  if (loopState == LOOP_STATE_STARTED) {
    stepper.setSpeed(8); // 8 rpm
    stepper.step(203); // 203 steps
    delay(1000); // wait for one second
    stepper.setSpeed(6); // 6 rpm
    stepper.step(-406); // do -406 steps
    loopState = LOOP_STATE_STOPPED;
  }
}

Otherwise you could do it really simply:

void loop() {
  button.loop();

  if (button.isPressed()) {
    stepper.setSpeed(8); // 8 rpm
    stepper.step(203); // 203 steps
    delay(1000); // wait for one second
    stepper.setSpeed(6); // 6 rpm
    stepper.step(-406); // do -406 steps
  }
}
1 Like

I Will do this in the future.
Thanks, Adam

Thank you, I was a little confused With the else statement as I thought it stopped the loop but your explanation made so much sense I now see the error.

I will upload the code tomorrow, just in time to get my automated 3d printed dog treat dispenser working!
Such a great community I wish I had started tinkering with Arduino sooner.
``

Just uploaded the code to the Arduino and it now stops the code after running. However, it does something very weird it will run the code twice before stopping. I played around with it a bit removing the second stepper movement and the delay and it works but adding the second movement seems to cause it to run twice.
I cannot for the life of me figure out why this would be, both of your examples seem to be spot on? I'm gonna run some troubleshooting to see if I cannot pinpoint the cause. Thanks again for your help

Something is not right as I have run a different code example and it always runs void loop twice without fail even with stop. I thought it was working with one movement but it rotates twice the amount. I am going to reinstall everything and reassign the com port.

Post your latest code and maybe we can see what the issue is.

Here is the latest code, I added a function to turn off stepper hold to reduce power usage and heat. Every which way the stepper turns twice, I have tried with and without delay but it still runs twice.
Many thanks in advance.

```cpp
#include <Button.h>
#include <Stepper.h>

#define LOOP_STATE_STOPPED 0
#define LOOP_STATE_STARTED 1

#define STEPS 2038  // (28BYJ-48)

Stepper stepper(STEPS, 8, 10, 9, 11);

Button button(7);  //  pin 7;
int loopState = LOOP_STATE_STOPPED;

void setup() {

  button.setDebounceTime(50);  // set debounce time to 50 milliseconds
}

void loop() {
  button.loop();

  if (button.isPressed()) {
    if (loopState == LOOP_STATE_STOPPED)
      loopState = LOOP_STATE_STARTED;
    else  // if(loopState == LOOP_STATE_STARTED)
      loopState = LOOP_STATE_STOPPED;
  }

  if (loopState == LOOP_STATE_STARTED) {
    delay(1000);
    stepper.setSpeed(4);             // 8 rpm
    stepper.step(1019);              // 1019 steps half (for testing)
    loopState = LOOP_STATE_STOPPED;  //stop ste

    digitalWrite(8, LOW);  //turn off stepper hold to reduce power usage.
    digitalWrite(9, LOW);
    digitalWrite(10, LOW);
    digitalWrite(11, LOW);
  }
}

which button-library are you using?

This is another case where these totally generalised non-specific library-names is causing a problem

best regards Stefan

1 Like

I'm now using ezButton, as you suggested it may be the issue. However, I have narrowed the issue down to the debounce as when this is removed the code runs once and this is exactly as I require but I do eventually want to you the debounce function. Could you recommend a better button library or one that works with debounce? Cheers, Adam

I'm at the same point.
No setDebounceTime(50);
and function isPressed() evaluates to true only one time like it should.

I haven't used buttons much where debounce would have been necsessary.
So I can't recommend a button-library
best regards Stefan

1 Like

How about using the MobaTools?

They offer support for Buttons and for Stepper-motors in one library?
best regards Stefan

1 Like

I will check this out, I was under the impression that buttons caused issues if not denounced but this seems to be the opposite here.
Best, Adam

So here is a code-version that uses the MobaTools-library

for functionalities like yours a state-machine is very good.

a state-machine reduces the number if -if-conditions substantially
Once you have understood how state-machines work you will never go back
to not use them.

In Your case the state-machine has 3 states:
here are the self-explaining names:

// constants used in the state-machine
const byte waitForButtonStart     = 0;
const byte StartStepper           = 1;
const byte waitForStepperToFinish = 2;

I added serial-output that shows what is going on in the code
I use two macros for that (which are at the top of the code)
At first you can ignore the macros "dbg" and "dgbi"

The more interesting thing is the state-machine itself
the state-machine starts with state waitForButtonStart
and then proceeds as conditions become TRUE
Maybe you have to adapt the speed.

As the MobaTools create the step-pulses "in the backround" other code can execute even while the stepper is running.

I have writen quite a lot of comments to explain
Have fun analysing how it works

// start of macros dbg and dbgi
#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a
// Serial.print is executed
// end of macros dbg and dbgi

#include <MobaTools.h>

#define STEPS_per_rotation 2038  // (28BYJ-48)

MoToStepper stepper(STEPS_per_rotation, FULLSTEP);          // HALFSTEP ist default

const byte myButton_pin = 7;
const byte myBtnPinArray[] = {myButton_pin};
const byte NrOfButtons = sizeof(myBtnPinArray);


MoToButtons myButtons( myBtnPinArray, NrOfButtons, 50, 500 );

// constants used in the state-machine
const byte waitForButtonStart     = 0;
const byte StartStepper           = 1;
const byte waitForStepperToFinish = 2;

byte myActualState = waitForButtonStart;


void myStateMachine() {
  myButtons.processButtons();
  
  switch (myActualState) {

    case waitForButtonStart:
      if (myButtons.pressed(0)) { // just a single button index-numbers start at 0
        Serial.println("Buttonpressed");
        myActualState = StartStepper;
      }
      break;


    case StartStepper:
      dbg("right before delay(1000);", 0);
      delay(1000);
      dbg("start half turn stepper.write(180)", 0);
      stepper.write(180); // function write uses angle in degree 180 degree = 1/2 rotation
      myActualState = waitForStepperToFinish;
      break;


    case waitForStepperToFinish:
      dbgi("stepper running", stepper.stepsToDo(), 250);
      if ( stepper.stepsToDo() == 0 ) {
        // set new position as new zero-point. Otherwise MobaTools knows
        // that stepper already is at 180 degrees and would not rotate
        stepper.setZero();
        myActualState = waitForButtonStart;
        dbg("stepper finished", stepper.stepsToDo());
        Serial.println( F("Press button to start stepper run\n\n") );        
      }
  }
}


void setup() {
  Serial.begin(115200);
  Serial.println( F("Setup-Start") );
  stepper.attach(8, 10, 9, 11); // four parameters for 4-pin-stepper-drivers
  stepper.setSpeed(200);  // must be set as real rpm * 10   rpm = 4   parameter = 4 * 10 = 40
  stepper.setZero();
  Serial.println( F("Press button to start stepper run") );
}

void loop() {
  myStateMachine();
}

best regards Stefan

1 Like

Wow, so good to have this amazing support and encouragement! This looks really interesting and certainly something that has made me excited to figure out how this works and with such a detailed description.
I was actually limiting my project with the understanding that Arduino was linear and couldn't run multiple things at a time. This has made me rethink my project and can push it to the next level!
I have been playing around with adding a servo and led but the code looks messy and overly complex, works but is a bit of a bodge....Now I have this to play with !

Stefan, you're a legend!

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