How to get out of a for loop once a switch is pressed?

My Aim:
When no switch is pressed, I want my LED to keep turning on and off 6 times, stop for a couple of seconds, and repeat. If the switch is pressed, I want the LED to stop turning on. Once the switch is released, I want it to turn on and off as before.
My Problem:
My problem is that the system only reviews the switch's state once the 'for loop' has finished, i.e. the LED has completed turning on/ off 6 times. What do I have to do if I want the sequence (aka stream) to stop exactly when I press the switch, when when it is still in the for loop?
The code I wrote so far:

int LED_RED_GPIO=15; //led pin
int SW1_GPIO=22; //switch pin

void setup() 
{
Serial.begin(9600); //Sets the data rate in bits per second (baud) for serial data transmission
pinMode(LED_RED_GPIO, OUTPUT); //Configures pin 15 to behave as an output
pinMode(SW1_GPIO, INPUT); //Configures pin 22 to behave as an output
}

//The stream occurs unless switch is pressed
void loop() 
{
  int switchstate = digitalRead(SW1_GPIO); //local variable, holding state of switch

        if (switchstate==HIGH)
        {
          digitalWrite(LED_RED_GPIO, LOW);
          Serial.println("switch pressed, stream inactive");
        }
        
    else{sw
          Serial.println("switch not pressed, stream active");
          
          for (int i = 0; i <=5; i++) //repeats loop 6 times
          {
            Serial.println(i);
            digitalWrite(LED_RED_GPIO, HIGH); //LED is on for 1 second
            delay(1000);
            digitalWrite(LED_RED_GPIO, LOW); //LED is off for 1 second
            delay(1000);
           }
           delay(2000);
         }
}

Appreciate any help. Thanks.

Think about including the reading of the switch as part of the "for" condition.

...and how happy you'll be with up to two seconds of latency.

1 Like

That is, indeed, the problem, so what is the solution ?

Part of the solution is not to use a for loop in the first place because the well named loop() function will do the looping for you. However, if you use delay()s for the timing that will not help

You need to implement non blocking using millis() for the timing.

See Using millis() for timing. A beginners guide, Several things at the same time and the BlinkWithoutDelay example in the IDE

2 Likes

what is the syntax for that? I tried:

for (int switchstate==HIGH; int i = 0; i <=5; i++) //repeats loop 6 times

The condition in a for loop is the expression between the semicolons.

Personally, I'd lose the for loop.

2 Likes

You MUST read the switch as part of the "for" syntax.

I tried putting an 'if' statement in the for loop that said: if the switch is pressed then break out of the 'for' loop. but I assume that is not what you mean? :sweat_smile:

The condition in an if statement is the one between the parentheses.

1 Like

If I get rid of the for loop then I don't know how to have the LED continuously turn on/off 6 times with 2 seconds between each set.

To do "exactly" you need to convert your for+delay loop into a non-blocking https://www.arduino.cc/en/Tutorial/BuiltInExamples/BlinkWithoutDelay form. This would be best. The core part of the Blink without delay for the 6 flashes would need to count the flashes and change the interval depending on the number of flashes:

    int flash = 0;
    ...
    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH; 
      flash++;
      interval = 1000;
    } else {
      ledState = LOW;
      if (flash == 6) {
         flash = 0;
         interval = 2000;
      } else {
         interval = 1000;
      }
    }

...then add some code into loop() to reset things when your inhibiting switch is pressed.

   if (digitalRead(SW1_GPIO) == HIGH){ // reset led state
       ledState = LOW;
       flash = 0;
       interval = 1000;
       previousMillis=millis()-interval;
   }

If "exactly" means you can wait until your loop iterates again, you can put it in the for loop's iteration condition like Paul said:

    for (int i = 0; (i <=5 && digitalRead(SW1_GPIO) ==LOW) ; i++)

If you want to do something hacky in the middle you can add a conditional break at points in your for loop:

     if (digitalRead(SW1_GPIO)==HIGH) break;
1 Like

Something like this

unsigned long periodStartTime;
unsigned long currentTime;
const unsigned long period = 1000;
byte xNow = 0;
byte xMax = 5;

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

void loop()
{
  currentTime = millis();
  if (currentTime - periodStartTime >= period)
  {
    Serial.println(xNow); 
    xNow++;
    if (xNow == xMax)
    {
      xNow = 0;
    }
    periodStartTime = currentTime;
  }
  //read the input here
}
2 Likes

consider

#define MyHW
#ifdef MyHW
int LED_RED_GPIO = LED_BUILTIN;
int SW1_GPIO     = A1;
#else
int LED_RED_GPIO=15; //led pin
int SW1_GPIO=22; //switch pin
#endif

enum { Off = HIGH, On = LOW };

enum { ST_IDLE, ST_TGL, ST_PAUSE };
int  state = ST_TGL;

const int CntTgl   = 12;
const int CntPause = CntTgl + 4;
int  cnt = 0;


const unsigned long Cycle = 200;
unsigned long msecLst;

void setup()
{
    Serial.begin(9600);
    pinMode(LED_RED_GPIO, OUTPUT);
    pinMode(SW1_GPIO,     INPUT_PULLUP);
    digitalWrite(LED_RED_GPIO, Off);
}

//The stream occurs unless switch is pressed
void loop()
{
    unsigned long msec = millis ();

    if (LOW == digitalRead(SW1_GPIO))  {
        cnt   =  0;
        state = ST_IDLE;
    }

    switch (state)  {
    case ST_IDLE:
        digitalWrite(LED_RED_GPIO, Off);
        state = ST_TGL;
        break;

    case ST_TGL:
        if ( (msec - msecLst) > Cycle)  {
            msecLst = msec;
            digitalWrite(LED_RED_GPIO, ! digitalRead (LED_RED_GPIO));
            if (CntTgl < cnt++)
                state = ST_PAUSE;
        }
        break;

    case ST_PAUSE:
        if ( (msec - msecLst) > Cycle)  {
            msecLst = msec;
            if (CntPause < ++cnt) {
                cnt   = 0;
                state = ST_TGL;
            }
        }
        break;
    };
}
2 Likes

Hello banana2000
Try this sketch. I´ve used event driven timers each for flashing and for interupting. You have to change the pin address and make some fine tuning to your project. Emergency flash lights?

/* BLOCK COMMENT
When no switch is pressed, I want my LED to keep turning on and off 6 times, stop for a couple of seconds, 
and repeat. If the switch is pressed, I want the LED to stop turning on. Once the switch is released,
I want it to turn on and off as before.
  https://forum.arduino.cc/t/how-to-get-out-of-a-for-loop-once-a-switch-is-pressed/953756
*/
#define ProjectName "How to get out of a for loop once a switch is pressed?"
// HARDWARE AND TIMER SETTINGS
// YOU MAY NEED TO CHANGE THESE CONSTANTS TO YOUR HARDWARE AND NEEDS
constexpr byte ButtonPin {A0};      // portPin o---|button|---GND
constexpr byte LedPin {9};          // portPin o---|220|---|LED|---GND
#define OutPutTest
constexpr  unsigned long OutPutTestTime {1000};
// VARIABLE DECLARATION AND DEFINITION
unsigned long currentTime;
struct TIMER {              // has the following members
  unsigned long duration;   // memory for interval time
  unsigned long stamp;      // memory for actual time
  bool control_;            // control for start/stop
};
TIMER flash {50, 0, true};
TIMER startStopp {1000, 0, true};
// -------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  Serial.println(F("."));
  Serial.print(F("File   : ")), Serial.println(__FILE__);
  Serial.print(F("Date   : ")), Serial.println(__DATE__);
  Serial.print(F("Project: ")), Serial.println(ProjectName);
  pinMode (LED_BUILTIN, OUTPUT);  // used as heartbeat indicator
  pinMode(ButtonPin, INPUT_PULLUP);
  pinMode(LedPin, OUTPUT);
#ifdef OutPutTest
  // check outputs
  digitalWrite(LedPin, HIGH), delay(OutPutTestTime);
  digitalWrite(LedPin, LOW), delay(OutPutTestTime);
#endif
}
void loop () {
  currentTime = millis();
  static int blinkCount = 6 * 2;
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  if (currentTime - flash.stamp >= flash.duration && blinkCount ) {
    flash.stamp = currentTime;
    blinkCount--;
    if (digitalRead(ButtonPin)) digitalWrite(LedPin, !digitalRead(LedPin));
  }
  if (currentTime - startStopp.stamp >= startStopp.duration && !blinkCount ) {
    startStopp.stamp = currentTime;
    blinkCount = 6 * 2;
  }
}

Have a nice day and enjoy coding in C++.

1 Like

Thank you so much for the code. It has been very useful. I am just confused about this section:

Can you please explain it in words. for example, I am confused about the variable 'msecLst', what is it for?

To remember what time it was last time you did something significant.

ah I see. and why should msec - msecLst be smaller than the 'cycle' in order for the led to toggle?

It doesn't - it needs to be greater.

sorry I meant to say greater

msec is the time now.
msecLst is the time when you last did something.
So, the difference between those two values is the elapsed time.