Want to use a button to jump out of a loop at anytime.

UPDATED 8/16 See latest post
I know this is a simple thing to do but for some reason I can't find the right combo to get this to work.

I have code that just spins a motor left for a set time, then right for another set time. I want to be able to pause this motion at anypoint in time with a button. I have only been able to use a while loop to get the code to stop only after the loops are done.

I know I can do this with a function but those are still confusing to me and there has to be an easier way. Below is my code.

SEE NEW CODE BELOW

Any help will be awesome. I could also just set a power button since the Pro Mini boots really fast but I feel like thats cheating. Btw, this is a watch winder.

Hint: you can't process anything while a big whonking delay is running.

delay(50000); //Spins for 5 mins <<< is this metric?

delay(5601000UL); //Spins for 5 mins :wink:

Have you heard of the "Blink Without Delay" technique?

.

State machine?

Have a look at the code in several things at a time. It uses millis() to manage timing without blocking.

Your code needs to do something like this

check the switch
check the value of millis()
is the motor OFF
    is it time to switch the motor ON
        turn the motor ON
 is the motor ON
   is the switch pressed OR is it time to turn the motor OFF
      turn the motor OFF
repeat

...R

Ok thanks guys. I will give this a look and try out some new things. If they dont work ill be back.

I knew from the get go that using a delay wouldnt be the best idea and that my timer isnt exactly 5 mins. However, to save me the stress of figuring out which watches need to be rotated which way and having them run a overly complicated countdown function I thought delays would be fine. I just didn't know if I could break out of them quickly.

Robin, thank for the link. Ill play with his parts of the code to see what I can come up with.

Ok everyone. I have come back to this code after wanting to refine it. I started using a hardware interrupt and the delays from before but I knew that wasnt a proper way to code (esp since you shouldnt use delays in interrupts) so I dove into it again.

I am now using functions and for statements to rotate the motor which works great. I then tried to add my pause button and started having issues. First I have a push button connected to pin 2 with a wire going to Vin and a pull-down resistor.

In my code I want to be able to call the pause function in each aspect of the code but it is only working during the top of LOOP and no where else. Sometimes it also gets stuck in a pause then rotate then pause loop (happened once) or will do the pause function twice.

Can someone help me get the ability to be able to call my pause function during any point in time without using a hardware interrupt?

const int pin1 = 9; //set motor input 1
const int pin2 = 10; //set motor input 2
const int pausePin = 2; //Set to pin 2
int buttonState = 0; // variable for reading the pushbutton status
int i; //Not used right now
int n; //Used for rotation variable

void setup()
{

pinMode(pin1, OUTPUT); // initialize the motor pin as an output
pinMode(pin2, OUTPUT); // initialize the motor pin as an output
pinMode(pausePin, INPUT); // initialize the pushbutton pin as an input
Serial.begin(9600);
}

void loop()
{
Serial.println("Loop Top"); //Used for debugging
buttonState = digitalRead(pausePin); //Read if pausePin is HIGH or LOW
Serial.println(n); //Used for debugging
if (buttonState == HIGH){ //If pausePin is HIGH goto pausing
pausing();
}
rotateMotorCW(); //Clockwise function
digitalWrite(pin1, LOW); // sets the motor off
delay(1000); //Stops for short time before changing direction
rotateMotorCCW(); //Counter Clockwise funtion
digitalWrite(pin2, LOW); // sets the motor off
delay(1000); //Stops for short time before changing direction
}

void rotateMotorCW(){
buttonState = digitalRead(pausePin); //Read if pausePin is HIGH or LOW
if (buttonState == HIGH){ //If pausePin is HIGH goto pausing
pausing();
}
for(int n = 0; n < 10; n++){ //Uses "n" for rotation amounts
digitalWrite(pin1, HIGH); // sets the motor on
digitalWrite(pin2, LOW); // sets the motor on rotating CW
delay(1000); //delay before looping back through
Serial.println(n);
}
}

void rotateMotorCCW(){
buttonState = digitalRead(pausePin); //Read if pausePin is HIGH or LOW
if (buttonState == HIGH){ //If pausePin is HIGH goto pausing
pausing();
}
Serial.println("CCW"); //Used for debugging
for(int n=10; n >= 0; n--){ //Uses "n" for rotation amounts in opposite direction
Serial.println(n); //Used for debugging
digitalWrite(pin1, LOW); // sets the motor on
digitalWrite(pin2, HIGH); // sets the motor on rotating CCW
delay (1000);
}
}

void pausing (){
digitalWrite(pin1, LOW); // sets the motor off
digitalWrite(pin2, LOW); // sets the motor off
Serial.println("Pausing"); //Used for debugging
delay(10000); //Stops for delayed time
}
]

Im having a lot of fun learning more about coding and better ways to do so but it is still upsetting when something gives you so much trouble.

I would LOVE to be able to code the pause button to pause when pressed and unpause only when pressed again but ill get what I can take.

Please use the code button </> to post your code properly (as in Reply #4) as then I can easily select it and copy it to my text editor to study it.

...R

I read the program as

if button pushed and not paused rotate cw for 10 seconds (11 seconds as relays are active during pause)
stop for 1 second (does that work?)
rotate ccw for 10 seconds then repeat
if paused stop motors after they Finnish turning and delay 10 seconds.
then carry on pausing 10 seconds until button is released and the pause is finished (0 to 10 seconds)

you need to understand code flow and to read the code the same way the arduino does.

code is read top to bottom left to right.
when it sees a delay it says to the arduino stop here and wait
when it sees a "for" it says stop here and stay here until the "for" is complete

it doesn't care what you did with a button during the time it in a delay or a for.
The arduino is busy doing nothing waiting for the delay, for to Finnish so it will see the button has changed when it gets to a code line that tells it to check it.

The arduino outputs are set. They need one instruction to turn them on then they will stay on. If you turn something on then you should also plan to turn it off.

treahuggs:
Can someone help me get the ability to be able to call my pause function during any point in time without using a hardware interrupt?

I would LOVE to be able to code the pause button to pause when pressed and unpause only when pressed again but ill get what I can take.

People are trying.

Look at loop(). It goes around and around executing whatever is inside over and over.

So if there is code for one motor inside that runs sometimes and not others... and code for the other motor and code for the button that may need to be debounced.....
........then if none of those holds up the rest.......... button pressed detection by the button-only code could make a stop signal for the motor-codes.

Each section can be small and made and tested in a small control sketch. It's MUCH easier.
Edit: and I see you pretty much have that. Now you need millis().

What you may be missing is the use of time in code. Arduino has functions that return how many milliseconds/microseconds-rounded-to-4 have elapsed since startup as type unsigned long.
You can save the time at the start of where you have a delay and then every time through loop() see if the current value of millis() minus the start time is >= the delay time and only run the next code when that is true.

The state machine makes that easy when placed inside a timer. I did this last year for a greenhouse automation in Budapest. His original code was laced with delays. I coded around them and it runs fine.

Here's what I did with the 1-wire sensors read and report.
Every time it runs, it performs a time check if the wait is > 0.
If the wait is not up, it returns to let the next block of code run where delay() blocks everything.
And when the code inside does run, it runs one step that ends setting the wait time before the next.
All the waits used to be delays. The switch-case states are the code that was between the delay()s.
I learned about 1-wire fixing 1-wire code. And SIM900 GSM too. Also note it makes a trace log.

inline void readSensorsToSerial( )
{
  // this takes care of waits for ALL cases
  if ( sensorsTempWait ) // if == 0 then skip to switch-case
  {
    if ( millis() - sensorsTempStart < sensorsTempWait )
    {
      return; // if it's not time to run a case, return
    }
    else
    {
      sensorsTempWait = 0UL;
    }
  }

  switch ( readSensorsState )
  {

  case request : // REQUEST READ TEMPERATURE
    stampLog( sensorsTask, request );
    sensorsTimerStart = sensorsTempStart = millis();
    logFlashMsg( FsensorMsgs[ 0 ], 0 );
    sensors.requestTemperatures(); // how long does this take???
    logFlashMsg( FsensorMsgs[ 1 ], 0 );
    logUL( millis(), 1 );
    logFlashMsg( FsensorMsgs[ 2 ], 0 );
    sensorsTempWait = 100UL;
    readSensor = 0;
    readSensorsState = readTmp;
    break;

  case readTmp : //  READ TEMPERATURE
    stampLog( sensorsTask, readTmp );
    temperature[ readSensor ] = ( sensors.getTempC( Thermometer[ readSensor ] ));
    logInt( temperature[ readSensor ], 0 );
    logFlashMsg( FsensorMsgs[ 3 ], 0 );
    if ( readSensor < thermometers - 1 )
    {
      logMsg( ", ", 0 );
      readSensor++;
    }
    else
    {
      logMsg( "\n\n", 0 );
      //    updateSDFAT( );

      readSensor = 0;

      //  read sht sensor

      sht10temp = sht1x.readTemperatureC();
      sht10humid = sht1x.readHumidity();

      //  read co2 sensor

      co2 = czr.CO2();
      co2temp = czr.Celsius();
      co2humid = czr.Humidity();

      // avarage inside temperature

      Tin = ((temperature [ 0 ] + temperature [ 1 ] + temperature [ 2 ] + sht10temp + co2temp ) / 5);

      lcd.clear();
      readSensorsState = showLCD;
      sensorsTempStart = millis();
      sensorsTempWait = 100UL;
    }
    break;

  case showLCD :
    if ( readSensor < thermometers )
    {
      lcd.setCursor( 0, readSensor );
      lcd.print( F( "T" ));
      lcd.print( readSensor );
      lcd.print( F( ":   C" ));
      lcd.setCursor( 3, readSensor );
      lcd.print( temperature[ readSensor ] );
      readSensor++;
    }
    else
    {
      lcd.setCursor( 0, 3 );
      lcd.print( F( "CO2:" ));
      lcd.setCursor( 5, 3 );
      lcd.print( co2 );

      lcd.setCursor( 10, 0 );
      lcd.print( F( "Tout:   C" ));
      lcd.setCursor( 15, 0 );
      lcd.print( sht10temp );

      lcd.setCursor( 10, 1 );
      lcd.print( F( "Tin:   C" ));
      lcd.setCursor( 14, 1 );
      lcd.print( Tin );

      lcd.setCursor( 10, 2 );
      lcd.print( F( "Hout:   %" ));
      lcd.setCursor( 15, 2 );
      lcd.print( sht10humid );

      lcd.setCursor( 10, 3 );
      lcd.print( F( "Hin:   %" ));
      lcd.setCursor( 14, 3 );
      lcd.print( co2humid );

      readSensor = 0;
      sensorsTempStart = sensorsTimerStart;
      sensorsTempWait = sensorsTimerWait;
      readSensorsState = request;
    }
    break;

  }
}

// end sensor functions

Copy paste is hard.

const int pin1 = 9;  //set motor input 1
const int pin2 = 10; //set motor input 2
const int pausePin = 2;  //Set to pin 2
int buttonState = 0;     // variable for reading the pushbutton status
int i; //Not used right now
int n; //Used for rotation variable

void setup()
{
//  attachInterrupt(0, interruptFunction, HIGH);  //Not used
  pinMode(pin1, OUTPUT);  // initialize the motor pin as an output
  pinMode(pin2, OUTPUT);  // initialize the motor pin as an output
  pinMode(pausePin, INPUT); // initialize the pushbutton pin as an input
 Serial.begin(9600);
  }
 
void loop() 
{
      Serial.println("Loop Top");          //Used for debugging
      buttonState = digitalRead(pausePin); //Read if pausePin is HIGH or LOW
      Serial.println(n);                   //Used for debugging
  if (buttonState == HIGH){          //If pausePin is HIGH goto pausing
      pausing();
                          }    
    rotateMotorCW();        //Clockwise function
          digitalWrite(pin1, LOW);    // sets the motor off
          delay(1000);                //Stops for short time before changing direction
    rotateMotorCCW();       //Counter Clockwise funtion
          digitalWrite(pin2, LOW);    // sets the motor off
          delay(1000);                //Stops for short time before changing direction
}
  
 void rotateMotorCW(){ 
    buttonState = digitalRead(pausePin);  //Read if pausePin is HIGH or LOW  
      if (buttonState == HIGH){          //If pausePin is HIGH goto pausing
      pausing();
    }  
      for(int n = 0; n < 10; n++){   //Uses "n" for rotation amounts
      digitalWrite(pin1, HIGH);      // sets the motor on
      digitalWrite(pin2, LOW);       // sets the motor on rotating CW
      delay(1000);                   //delay before looping back through
      Serial.println(n);
    }
   }
 
 
  void rotateMotorCCW(){
      buttonState = digitalRead(pausePin);  //Read if pausePin is HIGH or LOW  
        if (buttonState == HIGH){          //If pausePin is HIGH goto pausing
      pausing();
    }      
    Serial.println("CCW"); //Used for debugging
    for(int n=10; n >= 0; n--){   //Uses "n" for rotation amounts in opposite direction
      Serial.println(n); //Used for debugging
      digitalWrite(pin1, LOW);   // sets the motor on
      digitalWrite(pin2, HIGH);   // sets the motor on rotating CCW
      delay (1000);
   }
}
  
 void pausing (){
  digitalWrite(pin1, LOW);    // sets the motor off
  digitalWrite(pin2, LOW);    // sets the motor off
  Serial.println("Pausing"); //Used for debugging
  delay(10000);         //Stops
  }

gpop1:
I read the program as

if button pushed and not paused rotate cw for 10 seconds (11 seconds as relays are active during pause)
stop for 1 second (does that work?) Yes it works, why wouldnt it? its done to ease stress on the motor, thats it
rotate ccw for 10 seconds then repeat
if paused stop motors after they Finnish turning and delay 10 seconds.
then carry on pausing 10 seconds until button is released and the pause is finished (0 to 10 seconds)

you need to understand code flow and to read the code the same way the arduino does.Thats why I have my debugging print lines in and why im asking for help. I tried to use VisualMicro with no success.

code is read top to bottom left to right.
when it sees a delay it says to the arduino stop here and wait
when it sees a "for" it says stop here and stay here until the "for" is completeThat is why I put my check for pause button everywhere, by this logic i should be able to put my if statement inside my for statement and then it would function like that, which does not work either

it doesn't care what you did with a button during the time it in a delay or a for.
The arduino is busy doing nothing waiting for the delay, for to Finnish so it will see the button has changed when it gets to a code line that tells it to check it. I understand this and that is why there are miniuim delays and all of them can be changed for final release.

The arduino outputs are set. They need one instruction to turn them on then they will stay on. If you turn something on then you should also plan to turn it off.

GoForSmoke, The button has a pulldown resistor on the button to avoid bouncing. There is only one motor and all I do is switch the high and low pins to spin in both directions

There are three parts, rotating CW, rotating CCW, and pausing. By themselves they all work (tested), both rotatings work together (tested), but getting the pause to work whenever its pushed is where I am having issues.

I was previously using a delay for both rotates of 5 mins and then using a hardware interrupt. I know thats not the right way so I have been working on doing it a better way. When the coding is done I can remove all delays so lets not get hung up on seeing those. I used variables in place of my old delays so I can increase those and prolong the rotations. I just assumed I should be able to jump out of a for statement without much hassle.

EDIT: I forgot my digitalRead in my for loops when I tried and put my if statements inside it. So now it works and comes back to the for loop prefectly. Below is what I was missing, this is about 90% of how I want the program to work. I will look at the other methods I want to try and will of course ask any questions when I get stuck. Thank you all.

  void rotateMotorCCW(){
    Serial.println("CCW"); //Used for debugging
    for(int n=10; n >= 0; n--){   //Uses "n" for rotation amounts in opposite direction
              buttonState = digitalRead(pausePin);
              if (buttonState == HIGH){          //If pausePin is HIGH goto pausing
      pausing();
              }
      Serial.println(n); //Used for debugging
      digitalWrite(pin1, LOW);   // sets the motor on
      digitalWrite(pin2, HIGH);   // sets the motor on rotating CCW
      delay (1000);
   }
}

The delays are why your code is slow to respond and if you put your button on a scope it will show you bounce. But hey, with slow buttons your code will never even see the bounce.

you try to help someone and they get all upset.

I asked a simple question

if button pushed and not paused rotate cw for 10 seconds (11 seconds as relays are active during pause)
stop for 1 second (does that work?)

the answer

Yes it works, why wouldnt it? its done to ease stress on the motor, thats it.

what he fails to mention is that the original code he deleted didn't have the digitalWrite(pin1, LOW); so the motor once started would run until the pause button that kinda worked was pressed.

Read your PM

Have a look at these pieces of your code

void loop()
{
	Serial.println("Loop Top");          //Used for debugging
	buttonState = digitalRead(pausePin); //Read if pausePin is HIGH or LOW
	Serial.println(n);                   //Used for debugging
	if (buttonState == HIGH){          //If pausePin is HIGH goto pausing
		pausing();
	}
	rotateMotorCW();        //Clockwise function

and

void rotateMotorCW(){
	buttonState = digitalRead(pausePin);  //Read if pausePin is HIGH or LOW 
	if (buttonState == HIGH){          //If pausePin is HIGH goto pausing
		pausing();
	}

Do you see that some lines are repeated in loop() and in rotateMotorCW()

You should not write code like that. It makes it almost impossible to maintain and when you type the same stuff twice you have two chances to make typos.

You should have a function to read and save the button state(s) and those saved values should be used throughout loop().

Have a look at how the code is organized in planning and implementing a program

...R

You are right, the if statement for the button does not need to be in the loop. I placed it there after a few attempts at debugging I can remove it now and the code still functions the same. Its all a learning (some is re-learning) exp and that is why I came back to the code after I decided I didnt like using the cheater method and wanted to do it better. I will study the link you posted Robin and see what other ways I can clean up the code. Thank you.

gpop1:
you try to help someone and they get all upset.

I asked a simple question

if button pushed and not paused rotate cw for 10 seconds (11 seconds as relays are active during pause)
stop for 1 second (does that work?)

the answer

Yes it works, why wouldnt it? its done to ease stress on the motor, thats it.

what he fails to mention is that the original code he deleted didn't have the digitalWrite(pin1, LOW); so the motor once started would run until the pause button that kinda worked was pressed.

No upset here, it was just a silly question. If the motor pins are turned off and there is a delay afterwards then the motor will stay off during that delay. I have not failed to mention anything and the only code I have altered is the code first posted at the start of the thread which is very old and is completely changed. I did not want anyone to get confused if they were reading this thread for the first time. But since you think ive changed what ive posted below is my first code I shared with everyone which is very very wrong. (which also digitalWrites LOW for pause so im not sure if you mis-read or where you see that error)

void loop() 
{
  if (digitalRead(pausePin) == LOW){ //read pause pin to see if it is high
    interruptFunction();
  }
  else
  {  
    rotateMotor();
  }
}
  
 void rotateMotor(){
  digitalWrite(pin1, HIGH);   // sets the motor on
  digitalWrite(pin2, LOW);    // sets the motor on rotating
  delay(5*60*500UL);         //Spins for 5 mins
  digitalWrite(pin1, LOW);    // sets the motor off
  delay(500);         //Stops
  digitalWrite(pin1, LOW);   // sets the motor on
  digitalWrite(pin2, HIGH);   // sets the motor on rotating
  delay(5*60*500UL);         //Spins for 5 mins         
  digitalWrite(pin2, LOW);    // sets the motor off
  delay(500);         //Stops
}

  
 void interruptFunction(){
  digitalWrite(pin1, LOW);    // sets the motor off
  digitalWrite(pin2, LOW);    // sets the motor off
  delay(500);         //Stops
  }

GoForSmoke:
The delays are why your code is slow to respond and if you put your button on a scope it will show you bounce. But hey, with slow buttons your code will never even see the bounce.

Thank you. Since I got the pause button working I can actually now lower and take out some delays and use variables instead (this i know how to do) like the for loops variables being increased and even creating a slow down function instead of delays. All easier things to tackle at this time. I am lost on what you mean by putting the button on a scope however.

You can attach the button to an oscilloscope and see the bounces. There are articles on the web showing this and covering the subject in detail and offering different solutions.

You might see them when your code gets those cement boots off.

This should always see your button within 1ms of contact but it may be fast enough to see bounces so I stuck in a delay(10) where the user is stopping the works anyway. If you want to do more with the code then you may need to debounce that button in a non-wastey way.

untested.

char motorState = 0;
unsigned long waitStart = 0;
unsigned long waitWait = 0;


void loop() 
{
  if (digitalRead(pausePin) == LOW) // stop when the INPUT_PULLUP button is pressed
  { 
    motorState = 100;
    delay( 10 );
  }
  else if ( motorState > 3 ) // start back up when it is let go
  {
    motorState = 0;
  }

  rotateMotor();
}
  
 void rotateMotor()  // non-blocking version
 {
  if ( millis() - waitStart < waitWait )
  {
    return;
  }

  switch ( motorState ) // note that motorState > 3 shuts the motor off
  {
   case 0 :
   digitalWrite(pin1, HIGH);   // sets the motor on
   digitalWrite(pin2, LOW);    // sets the motor on rotating
//   delay(5*60*500UL);         //Spins for 5 mins
   motorState = 1;
   waitWait = 5UL * 60UL * 500UL;
   waitStart = millis();
   break;

   case 1 :
   digitalWrite(pin1, LOW);    // sets the motor off
//   delay(500);         //Stops
   motorState = 2;
   waitWait = 500UL;
   waitStart = millis();
   break;

   case 2 :
   digitalWrite(pin1, LOW);   // sets the motor on
   digitalWrite(pin2, HIGH);   // sets the motor on rotating
//   delay(5*60*500UL);         //Spins for 5 mins  
   motorState = 3;
   waitWait = 5UL * 60UL * 500UL;
   waitStart = millis();
   break;
       
   case 3 :
   digitalWrite(pin2, LOW);    // sets the motor off
//   delay(500);         //Stops
   motorState = 0;
   waitWait = 500UL;
   waitStart = millis();
   break;

   default :
   digitalWrite(pin1, LOW);    // sets the motor off
   digitalWrite(pin2, LOW);    // sets the motor off
   waitWait = 0;

}