How to continuously check whether a button is pressed within a for loop

Hi,

I'm having some trouble with where to check whether a pushbutton is pressed (or a switch is turned off), which is supposed to interrupt a loop. What I have now is a if statement in the first lines of my for loop, which checks whether the button was pressed, and breaks the loop if this is the case. However, there are other for loops within this for loop, so I am afraid that the input information might get lost if the button is pressed during one of these sub-for loops. I basically want to trigger the event "button pushed" whenever the pushbutton is pressed, but only break the loop when all sub-loops are done. This is the structure I'm talking about:

for (timestep; timestep < secondsinday / deltat ; timestep++) {  //start looping over all timesteps until end of day
    timestep_timer = millis();
    if(digitalRead(startPin)==LOW) {   //if start switch is set to OFF
        break;                         //break the loop
    }
    //do some stuff
    if (millis() - timestep_timer > deltat * 1000) {
        //do some stuff
        for (int i = 0; i < N; i++) {
            //do some stuff
        }
        //do some more stuff
    }
}

So, any clues how I could add some kind of "memory" checking the state of the button during the whole loop?

Thank you!

The first thing I would suggest is to do away with the for loops and use the loop() function to do what it does best, ie it loops, You can check for input each time through loop(), which occurs every few microseconds if written properly, and take appropriate action. If you need to "remember" that an input has changed state then set a boolean variable to true when it happens and check the value when you need to know whether the input changed state at any time.

UKHeliBob:
If you need to "remember" that an input has changed state then set a boolean variable to true when it happens and check the value when you need to know whether the input changed state at any time.

Make it a Global Variable.

Hi and thank you,

check the value when you need to know whether the input changed state

This is exactly the challenge: I basically need to check it all the time. But i don't want to write the code checking the state of the button between every line of code (which would be ridiculous). Indeed using a boolean to remember the state would be the easiest, but again this implies that the button is pressed when the program reads the input, after which that change of state can be memorized in the boolean.

I didn't quite get the idea of using loop(). I indeed want to loop, but only a finite amount of times (secondsinday / deltat times). For this reason I used a for loop. What would be the benefit of switching to loop()? Is it faster?

If written properly, your loop() function will be able to read the switch state much faster than a human would be able to press and release the switch. Thus, it won’t “miss” a switch activation.

Get rid of all of the for loops and write the program as a Finite State Machine. The program will be in one of many states at a time and each time through loop() you take the appropriate action for the current state. For instance, instead of your inner for loop initialise a variable when you enter the state and increment it each time through loop() for as many times as needed and execute the associated code but make sure that none of it blocks the free running of the program. When all of the iterations the inner state have been carried out change to the next state and carry on.

Doing things this way means that you can read the inputs frequently as loop() will run fast and often. A convenient way to manage the state is to use a variable to hold the current state number and switch/case within loop() to control which code is executed.

UKHeliBob:
A convenient way to manage the state is to use a variable to hold the current state number and switch/case within loop() to control which code is executed.

Such a variable is (not coincidentally) called a State Variable :slight_smile:

Also check out the debounce example:
File --> Examples --> 02.Digital --> Debounce

Basically when you enter loop() you check things like what "time" is it, what inputs are set, buffers full, check your notes about what you have going on etc. Do what needs doing, note down what you have going on and LEAVE.

Almost instantly you find yourself back, do it again.

If you think like this, you can add almost as many things as you like to run in your program. As far as anyone can tell, they all run concurrently.

You, on the other hand are going in and just being the guest that wouldn't leave. Nobody likes that, don't be that person. Look at the clock, look at the inputs, look at your notes. Switch things that need it NOW, note down what's going on and go.

-jim lee

Thanks a lot.

I've done some research on Finite State Machines and it indeed seems a much cleaner way to program this. I drew a simplified diagram to make it clearer to myself. However, I have a couple of doubts.

UKHeliBob:
Get rid of all of the for loops and write the program as a Finite State Machine.

Why isn't it ok to add a for loop within a state? Like, in my case, I have 4 parallel circuits opened or closed with 4 relays. Within my state, say, "MEASURE", I want to measure stuff for each of these circuits, one after the other. It seems more intuitive to me to do this with a for loop from 1 to 4.

UKHeliBob:
execute the associated code but make sure that none of it blocks the free running of the program.

Here is where I'm not convinced yet I guess, and the reason for which i'm still considering the above for loop for example. Let's take the same example as above. Why would it not be blocking the free running of the program to write the for loop as a loop() function? I any case all 4 measurements have to be done before the next part of the program is read, right? Or is the difference that, in the case of a loop() function, the WHOLE loop is run again after every measurement instead of doing all 4 measurements and only then moving on? This whole loop would skip everything that is not within the state I am in, so it would directly go to the next measurement as if it were in a for loop. This is how I could understand the benefit of using loop(), because it would read relevant information such as buttonstates before it takes the measurement of the next circuit.

However, it can't accelerate the actions themselves, so it can't take less time than necessary to perform all actions: to me, this means that loop() can't loop that fast at all, since it needs to finish all actions first. Right? So the initial problem is still present. No?

Why isn't it ok to add a for loop within a state?

OK, I was exaggerating a bit for effect. It is OK to use a for loop, perhaps to read a number of inputs, when in a state. The important thing is not to block the free running of the loop() function so that you can read inputs, such as an abort switch, as frequently as possible.

Using a state machine with no for loops in any state, particularly a for loop reading several inputs can speed up response to general inputs. Instead of reading say 4 sensor inputs in a single state imagine that there are 4 sub states each reading 1 sensor input. Each time through loop() you read 1 sensor when in the sensor reading state and in loop() you read the abort switch. This avoids the need to have code to read the abort switch in several places or even to have function calls to the switch reading code in several places.

I think that I get the idea.

So, if I wanted to rewrite the example that I presented before, I should make N states for every iteration of the inner for loop? In my case this is possible since N = 4. But with a larger loop this seems a lot of work.

I'll try to give an example of how I would rewrite the original code I posted. This code isn't complete, it's just to get an idea of the structure, I only described a limited amount of states.

Writing this, I was thinking that it was actually probably useless to add the states CIRCUIT1 to CIRCUIT4 while I could add the respective code of these states within the switch circuit statement, right?

Thanks a lot for any feedback on if this would be a more efficient way to program my sketch. I don't want to rewrite the whole sketch again (which is of course MUCH larger than what I posted) without being sure that I'm doing it the correct way.

void loop()
switch state{
//various other cases
case MEASURE:

if (digitalRead(startPin)==LOW) {
        state = INTERRUPTED;
    }

    else if (timestep < secondsinday / deltat) {

        switch circuit{
    case circuit=1:
        timestep++;
        state = CIRCUIT1;
        break;
    case circuit=2:
        timestep++;
        state = CIRCUIT2;
        break;
    case circuit=3:
        timestep++;
        state = CIRCUIT3;
        break;
    case circuit=4:
        timestep++;
        state = CIRCUIT4;
        break;
    case circuit=5:
        //datalog all measured data and print on LCD
        circuit=1;
        state = DONE;
        break;
    }
}

else {
    state = DONE;
}
break;

case CIRCUIT1:
   //measure data for 1st circuit
   circuit=2;
   state=MEASURE;
   break;

case CIRCUIT2:
   //measure data for 2nd circuit
   circuit=3;
   state=MEASURE;
   break;

case CIRCUIT3:
   //measure data for 3rd circuit
   circuit=4;
   state=MEASURE;
   break;

case CIRCUIT4:
   //measure data for 4th circuit
   circuit=5;
   state=MEASURE;
   break;

}

In my case this is possible since N = 4. But with a larger loop this seems a lot of work.

If you increment a variable each time round loop() when in the state that replaces the inner for loop then you can use the value of that variable in exactly the same way you would use the for loop variable.

I can't make much sense of your example program segment and I assume that you know that you have the syntax of the switch/case wrong.

Here is some pseudo code to consider

set state to waitForSwitch

start of loop()
  read switch
    if switch has become pressed 
      set action to true
    end if
    
  switch state
    case waitForSwitch
      if action = true
        state = readSensors
        sensorNumber = 0
      end if
      break
      
    case readSensors
      read sensor[sensorNumber]
      increment sensorNumber
      if sensorNumber equals 4
        state = waitForSwitch
      end if
      break
      
  end of switch
end of loop()

There are only 2 states but 4 sensors would be read when a switch becomes closed before going back to wait for the switch to become closed again. Obviously it is not exactly what you are doing but it might give you some ideas

This is exactly the challenge: I basically need to check it all the time. But i don't want to write the code checking the state of the button between every line of code (which would be ridiculous).

Just curious ... what's the rush? (what's the application?)

digitalRead time is about 5µs (0.005ms)

Your reaction time to press a button is ____ms?

Average loop time is ____ms? (elapsed time per iteration) <--- this is good to know and easy to test

For near instantaneous response to button status, an interrupt could be used, but for most applications this isn't required.

@UKHeliBob: Thanks, that example is way simpler than mine. I was basically making a state for every sensor. Now the question I'm tumbling on is when one should stop making new states and including the code in existing states. For almost any action a state could be created, but these actions could also simply be included within the existing states. Let me give an example illustrating my doubt: after all measurements are done, the state READSENSORS should be exited. I could create a state DONE in which for example a success message is printed on the LCD screen, before it goes back to the state SLEEP (or waitForSwitch). OR, I could simply add the success message within the corresponding IF within the READSENSORS state, and directly set the state to SLEEP. The measurements can also be interrupted if a stop button is pressed. In that case, the state INTERRUPTED would be activated, which would also, after an error message, set the state to SLEEP. Again, this error message could be written in the IF function of the READSENSORS state, which would directly set the state to SLEEP. Would one of both methods be preferred?

edit: I think I should suggest how I see it: If a state has no condition, then most probably it is not useful to make it a state. For example, the DONE state has no IF condition, it just writes performs and action and always sends back to SLEEP. So the DONE state is useless. Does this make sense?

@dlloyd: The problem I had was that my for loops basically lasted 10h, because I need to perform the measurements during long periods of time. Every minute, the inner sub-for loop would be entered. So the line digitalRead would be read only rarely, instead of looping over it continuously. So even if digitalRead is super fast, it can't read a change of state if measurements are being taken without re-reading that line. The application is just the interruption of a sensing and datalogging process.

I had a look at some tutorials for implementing FSM, and don't understand why the initial state is set in the loop() function in this tutorial. Shouldn't the line static int state = 1; // initial state is 1, the "idle" state. be written in the setup() and not in the loop()? Thanks!!

How many states do you need is an open question. More states gives you more control and switch/case makes them easy to implement. Using an enum to give the states names, combined with switch/case make code easier to read, maintain and modify.

static int state = 1;

Sets the state variable to an initial state of 1 when the code is encountered for the first time and is subsequently ignored due to the use of static. The declaration of the state variable is done in loop() to restrict the scope of the variable to that function, which is good programming practice.

If the variable were declared as above in setup() then the code would not compile because the variable would be out of scope in loop(). You could, of course, use a global variable for state.

Thanks a lot, helped a bunch. I now replaced my whole sketch with a FSM and it works much better! Also much, much easier to debug. I decided to use global variables for the states, counters, etc. Thanks again!