Toggle Push-Button ON / OFF

Hello,
I want to use a push-button to turn the stepper motor ON and OFF.
The code works, but when pressing the button again to turn it off nothing happens.
Could someone please take a look at the code and help me out?

const int stepPin = 5; 
const int dirPin = 2; 
const int ButtonPin = 7; 

void setup() 
{
  pinMode(ButtonPin, INPUT_PULLUP);
  
  pinMode(dirPin,OUTPUT); 
  pinMode(stepPin,OUTPUT);   
  
  digitalWrite(dirPin,LOW);

}
void loop() 
{ 
  int buttonState = digitalRead(ButtonPin);

   while(buttonState == HIGH)))
   { 
       MyFunction(); 
   }
        
}
    

Welcome to the forum

Take a look at the StateChangeDetection example in the IDE. You need to detect when the button becomes pressed rather than when it is pressed

   while(buttonState == HIGH)))
   { 
       MyFunction(); 
   }
  • First off, ask the question where is buttonState being updated ?

  • Second, suggest you avoid using while( ) until you learn that using it can block your other code from executing.

  • As @UKHeliBob says . . .

1 Like

Hi @johnmed46

welcome to the forum.

Some time ago I have written a demo-code that does exactly what you are asking for.
This code uses the MobaTools-Library for creating the stepper-signals.

You can install the MobaTools-library with the library-manager inside the Arduino-IDE

/* explanation of the most important details:

  realising a functionality where using a momentary push-button
  acts as a toogle-switch
  push       => activated  push again => DE-activated
  push again => activated  push again => DE-activated
  etc. etc. ...
  This needs quite some code. This code is well organised in MULTIPLE functions
  where each function is a senseful SUB-program

*/

#define ProjectName "momentary push-button toggle on/off stepper-motor"
#include <MobaTools.h>

const int FULLROT1 = 200;
const byte stepPin   = 5; 
const byte dirPin    = 2; 

const byte ToggleButtonPin = 7; 

MoToStepper myStepper(FULLROT1,STEPDIR);           // HALFSTEP is default

// define IO-states for inputs with pull-up-resistors
// pull-up-resistors invert the logig
#define unPressed HIGH
#define pressed   LOW

bool forward = false;

unsigned long myCounter = 0;


void setup() {
  Serial.begin(115200); // adjust baudrate in the serial monitor to match the number
  Serial.println( F("Setup-Start") );
  printFileNameDateTime();

  pinMode (LED_BUILTIN, OUTPUT);  // used for indicating logging active or not
  digitalWrite(LED_BUILTIN, LOW);
  // wire button between IO-pin and GND
  // Pull-up-resistor inverts the logic
  // unpressed: IO-pin detects HIGH
  // pressed:   IO-Pin detects LOW
  pinMode(ToggleButtonPin, INPUT_PULLUP);
  myStepper.attach(stepPin,dirPin);
  myStepper.setSpeed( 300 );              // 30 rev/min (if stepsPerRev is set correctly)
  myStepper.setRampLen( FULLROT1 / 2); // Ramp length is 1/2 revolution

}


void loop () {
  forward = GetToggleSwitchState(); // must be executed all the time
  
  if (forward) { 
    digitalWrite(LED_BUILTIN, HIGH);    
    myStepper.rotate(1);
  }
  else {
    digitalWrite(LED_BUILTIN, LOW);    
    myStepper.stop();    
  }
}


bool GetToggleSwitchState() {
  // "static" makes variables persistant over function calls
  static bool toggleState     = false;
  static bool lastToggleState = false;

  static byte buttonStateOld = unPressed;
  static unsigned long buttonScanStarted  =  0;
  unsigned long buttonDebounceTime = 50;
  unsigned long buttonDebounceTimer;

  byte buttonStateNew;

  if ( TimePeriodIsOver(buttonDebounceTimer, buttonDebounceTime) ) {
    // if more time than buttonDebounceTime has passed by
    // this means let pass by some time until
    // bouncing of the button is over
    buttonStateNew = digitalRead(ToggleButtonPin);

    if (buttonStateNew != buttonStateOld) {
      // if button-state has changed
      buttonStateOld = buttonStateNew;
      if (buttonStateNew == unPressed) {
        // if button is released
        toggleState = !toggleState; // toggle state-variable
      } // the attention-mark is the NOT operator
    }   // which simply inverts the boolean state
  }     // !true  = false   NOT true  is false
  //       !false = true    NOT false is true
  return toggleState;
}


void printFileNameDateTime() {
  Serial.print( F("File   : ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("Date   : ") );
  Serial.println( F(__DATE__) );
  Serial.print( F("Project: ") );
  Serial.println( F(ProjectName) );
}

// helper-function for easy to use non-blocking timing
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod ) {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

best regards Stefan

Thanks for the hints. @ Stefan: I only edited the code, and it works, but i hold the push button a long time to turn it of. Maybe, something is not quite right with the code…

#include <MobaTools.h>

//const int FULLROT1 = 200;

const byte stepPin   = 5; 
const byte dirPin    = 2; 
const byte ToggleButtonPin = 7; 

//MoToStepper myStepper(FULLROT1,STEPDIR);           

#define unPressed HIGH
#define pressed   LOW

bool forward = false;

unsigned long myCounter = 0;

void setup() {
  
  pinMode (LED_BUILTIN, OUTPUT);  
  digitalWrite(LED_BUILTIN, LOW);
  pinMode(ToggleButtonPin, INPUT_PULLUP);

  pinMode(dirPin,OUTPUT); 
  pinMode(stepPin,OUTPUT);   
  digitalWrite(dirPin,LOW);
  
}

void MyFunction()
{ 
  for(int x = 0; x < 1000; x++) 
  {           
    digitalWrite(dirPin,HIGH);

    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(800); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(800); 
  }
   delay(800);
                 
   for(int x = 0; x < 1000; x++) 
   { 
    digitalWrite(dirPin,LOW);

    digitalWrite(stepPin,HIGH);
    delayMicroseconds(800);
    digitalWrite(stepPin,LOW);
    delayMicroseconds(800);
   }
   delay(800);

}

void loop () 
{
  forward = GetToggleSwitchState(); 
  
  if (forward) 
  { 
    digitalWrite(LED_BUILTIN, HIGH);    
    //myStepper.rotate(1);
  
    MyFunction(); 
 
  }
  else 
  {
    digitalWrite(LED_BUILTIN, LOW);    
    //myStepper.stop(); 
    return(MyFunction); 
  }
 
}


bool GetToggleSwitchState() 
{
  // "static" makes variables persistant over function calls
  static bool toggleState     = false;
  static bool lastToggleState = false;

  static byte buttonStateOld = unPressed;
  static unsigned long buttonScanStarted  =  0;
  unsigned long buttonDebounceTime = 50;
  unsigned long buttonDebounceTimer;

  byte buttonStateNew;

  if ( TimePeriodIsOver(buttonDebounceTimer, buttonDebounceTime) ) {
    // if more time than buttonDebounceTime has passed by
    // this means let pass by some time until
    // bouncing of the button is over
    buttonStateNew = digitalRead(ToggleButtonPin);

    if (buttonStateNew != buttonStateOld) {
      // if button-state has changed
      buttonStateOld = buttonStateNew;
      if (buttonStateNew == unPressed) {
        // if button is released
        toggleState = !toggleState; // toggle state-variable
      } // the attention-mark is the NOT operator
    }   // which simply inverts the boolean state
  }     // !true  = false   NOT true  is false
  //       !false = true    NOT false is true
  return toggleState;
}
// helper-function for easy to use non-blocking timing
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod ) {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

Your initial posting said only this

Now your modification has two for-loops.
making the stepper-motor running clockwise and then counter-clockwise

You will have to describe in much more details what you want the code to do

For-loops are designed to always run from
start-value to end-value
and this needs time
And this is the reason why you have to press the button so long.

What is really strange is that you coded this line

What do you expect from this line of code??

This code-version prints to the serial monitor to make visible what your code is doing

#include <MobaTools.h>

//const int FULLROT1 = 200;

const byte stepPin   = 5;
const byte dirPin    = 2;
const byte ToggleButtonPin = 7;

//MoToStepper myStepper(FULLROT1,STEPDIR);

#define unPressed HIGH
#define pressed   LOW

bool forward = false;

unsigned long myCounter = 0;

void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  pinMode (LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  pinMode(ToggleButtonPin, INPUT_PULLUP);

  pinMode(dirPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  digitalWrite(dirPin, LOW);
  Serial.println("exiting Setup");

}

void MyFunction()
{
  Serial.println("entering first for-loop");
  for (int x = 0; x < 1000; x++)
  {
    digitalWrite(dirPin, HIGH);

    digitalWrite(stepPin, HIGH);
    delayMicroseconds(800);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(800);
  }
  Serial.println("first for-loop finished");
  delay(800);

  Serial.println("entering second for-loop ");
  for (int x = 0; x < 1000; x++)
  {
    digitalWrite(dirPin, LOW);

    digitalWrite(stepPin, HIGH);
    delayMicroseconds(800);
    digitalWrite(stepPin, LOW);
    delayMicroseconds(800);
  }
  Serial.println("second for-loop finished");
  delay(800);
  Serial.println("delay(800) finished");
  Serial.println("exiting MyFunction()");

}

void loop ()
{
  forward = GetToggleSwitchState();

  if (forward)
  {
    digitalWrite(LED_BUILTIN, HIGH);
    //myStepper.rotate(1);

    MyFunction();

  }
  else
  {
    digitalWrite(LED_BUILTIN, LOW);
    //myStepper.stop();
    return (MyFunction);
  }

}


bool GetToggleSwitchState()
{
  // "static" makes variables persistant over function calls
  static bool toggleState     = false;
  static bool lastToggleState = false;

  static byte buttonStateOld = unPressed;
  static unsigned long buttonScanStarted  =  0;
  unsigned long buttonDebounceTime = 50;
  unsigned long buttonDebounceTimer;

  byte buttonStateNew;

  if ( TimePeriodIsOver(buttonDebounceTimer, buttonDebounceTime) ) {
    // if more time than buttonDebounceTime has passed by
    // this means let pass by some time until
    // bouncing of the button is over
    buttonStateNew = digitalRead(ToggleButtonPin);

    if (buttonStateNew != buttonStateOld) {
      // if button-state has changed
      buttonStateOld = buttonStateNew;
      if (buttonStateNew == unPressed) {
        // if button is released
        toggleState = !toggleState; // toggle state-variable
      } // the attention-mark is the NOT operator
    }   // which simply inverts the boolean state
  }     // !true  = false   NOT true  is false
  //       !false = true    NOT false is true
  return toggleState;
}
// helper-function for easy to use non-blocking timing
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod ) {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}
1 Like

It is probably a mistake. It should properly be entitled : return; I just wanted to get out of the loop.

unprecise description!

which loop?

you have three loops

If the push-button is pressed, the function "void MyFunction ()" should be terminated.

as long as the for loops are running nothing else than for-loop running will happen.
The for-loops run until x counted up to 1000

first for-loop 0,1,2,3-----1000
second for-loop 0,1,2,3-----1000

Interesting. But how can I exit the function (MyFunction()) ? Is there another command besides return?

still an unprecise description

your code has a two for-loops
one rotating the stepper-motor clockwise
a second rotating the stepper-motor counter-clockwise

you will have to decribe with much more precision what you want to do

do you want the stepper-motor just to immidiately stop if the button is pressed again?
do you want the stepper-motor to rotate back to the position the axle was before starting rotating clockwise?
do you want the motor to rotate back 1000 steps if the button is pressed again?
do you want the motor to interrupt the 1000 steps clockwise
and if button is pressed again continue with rotating until the motor has rotated 1000 steps clockwise and then rotate back 1000 steps
and this behaviour shall be the same if the motor has already started rotating (back) counterclockwise?
if you press the button while motor is running back counter-clockwise) stop the motor
and resume rotating with the next button-press?

All possible to write code for it.

Yes there is but this way is the most ugliest way of coding. You will have to find it yourself.
I won't tell it to you.

You should really use the mobatools library because the mobatools make it very easy to write code for all the variants that I have described above.

With exiting the for-loop you would have a much harder time to write the code for it.

I want the stepper-motor just to immediately stop if the button is pressed again. And after this if the button is pressed again the stepper-motor continue with rotating until the motor has rotated 1000 steps clockwise and then rotate back 1000 steps.

Ok this clarifies what the functionality shall be.

This is completely different from a simple

Here is a WOKWI-Simulation that is able to start / stop on rotating clockwise counter-clockwise
Such a functionality is easier to write with a technique called state-machine.

I think now you understand why a very precise description of the wanted functionality is nescessary

best regards Stefan

1 Like

3 posts were split to a new topic: Program not waiting for me to start it with the push button

i just posted this on another thread for controlling 2 DC motors

int enA = 9;
int in1 = 8;
int in2 = 7;

int enB = 3;
int in3 = 5;
int in4 = 4;

int pushbutton = 2;
byte butState;

bool carIsRunning = 0;

void loop ()
{
    if (carIsRunning) {
        digitalWrite (enA, HIGH);
        digitalWrite (enB, HIGH);
    }
    else {
        digitalWrite (enA, LOW);
        digitalWrite (enB, LOW);
    }

    int buttonVal = digitalRead (pushbutton);
    if (butState != buttonVal) {
        butState  = buttonVal;
        delay (20);                         // debounce

        if (LOW == butState)
            carIsRunning = ! carIsRunning;  // toggle
    }
}

void setup () {
    pinMode (enA, OUTPUT);
    pinMode (enB, OUTPUT);
    pinMode (in1, OUTPUT);
    pinMode (in2, OUTPUT);
    pinMode (in3, OUTPUT);
    pinMode (in4, OUTPUT);

    pinMode (pushbutton, INPUT);
    butState = digitalRead (pushbutton);
    
    digitalWrite (in1, HIGH);
    digitalWrite (in2, LOW);
    digitalWrite (in3, HIGH);
    digitalWrite (in4, LOW);
}

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