Immediately toggle loops when switch toggled

Hi, I'm new to Arduino and just playing around to get a feel for it all.

I made a sample sketch to change the blink pattern of an LED when the selector switch is toggled. I have it working in tinkercad, but right now the sketch will only switch between loops after the current running loop has completed.

How can I make it switch modes as soon as the switch is toggled?

Here's my current code:

// Sketch for testing switch between loops for LED patterns

int buzzerPin = 9;
int pinSwitch = 2;

void setup()
{
  pinMode(9, OUTPUT);
  pinMode(pinSwitch, INPUT_PULLUP);
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{
	if (digitalRead(pinSwitch) == HIGH) { 
      Serial.println("Mode 1");
      loop1();
      
   }
  else {
    Serial.println("Mode 2");
    loop2();
   }
}   

// primer loop
void loop1() {

  digitalWrite(9,HIGH);
  delay(500);
  digitalWrite(9,LOW);
  delay(500);
  digitalWrite(9,HIGH);
  delay(500);
  digitalWrite(9,LOW);
  delay(500);
  digitalWrite(9,HIGH);
  delay(500);

}

//effector loop
void loop2() {

  digitalWrite(9,HIGH);
  delay(1000);
  digitalWrite(9,LOW);
  delay(500);
  digitalWrite(9,HIGH);
  delay(1000);
  digitalWrite(9,LOW);
  delay(500);
  digitalWrite(9,HIGH);
  delay(1000);

}

Hello quantumhorizons

Avoid the ussage of the delay() function. This function blocks the execution of the sketch.
Take a view into the IDE example BlinkWithOutDelay example to get rid of it.

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

Your loop1() and loop2() are functions, and you are asking how to abort and exit the functions early.

The c++ language statement for that is return():

You could write your functions to check the button frequently with:

if (digitalRead(pinSwitch) == HIGH) return;

or

if (digitalRead(pinSwitch) == LOW) return; 

Or you could write your code to be non-blocking so the attention of the processor doesn't get trapped in your delays().

Hi @quantumhorizons Welcome to the forum..

here's a non-blocking equivalent..

// Sketch for testing switch between loops for LED patterns

int buzzerPin = 9;
int pinSwitch = 2;

unsigned long lastMilli = 0;
int intervalDelay = 500;
byte mode = 0;

unsigned long lastCheck = 0;
int intervalDebounce = 50;

void setup()
{
  pinMode(9, OUTPUT);
  pinMode(pinSwitch, INPUT_PULLUP);
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{

  if (millis() - lastCheck >= intervalDebounce) {
    lastCheck = millis();
    if (digitalRead(pinSwitch) == HIGH) {
      if (mode != 1) {
        Serial.println("Mode 1");
        mode = 1;
      }
      intervalDelay = 500;
    }
    else {
      if (mode != 2) {
        Serial.println("Mode 2");
        mode = 2;
      }
    }
  }

  switch (mode) {
    case 1: loop1(); break;
    case 2: loop2(); break;
  }

}

// primer loop
void loop1() {

  if (millis() - lastMilli >= intervalDelay) {
    lastMilli = millis();
    digitalWrite(9, !digitalRead(9));
  }
}

//effector loop
void loop2() {
  if (millis() - lastMilli >= intervalDelay) {
    lastMilli = millis();
    if (!digitalRead(9)) intervalDelay = 1000; else intervalDelay = 500;
    digitalWrite(9, !digitalRead(9));
  }
}

Simmed here..

have fun.. ~q

Thanks! This works exactly as I wanted, but now I want to go a little more advanced...

Let's say for mode 1 I want to increase the on time of the LED by 100 ms (M1OnIncrement) for each cycle up to a maximum of 1000 ms (M1MaxOn), and for mode 2 I want to increase the on time of the LED by 1000 ms (M2OnIncrement) for each cycle up to a max of 5000 ms (M2MaxOn), using a default off cycle time of 500 ms for both modes.

It would be even more versatile if I could have a custom default off cycle time for each mode (M1DefaultOff & M2DefauiltOff), but I don't want to ask for too much if that gets really complex.

This was my attempt which seems to work for the most part, but not nearly as neat as what you cooked up and no debounce detection.

// These variables store the flash pattern
// and the current state of the LED

int ledPin =  9;      // the number of the LED pin
int pinSwitch = 2;
int ledState = LOW;             // ledState used to set the LED
unsigned long previousMillis = 0;        // will store last time LED was updated
long Mode1OnTime = 500;           // milliseconds of on-time
int Mode1OnIncrement = 100;       // increment on time in milliseconds
int M1OnTimeMax = 1000 - Mode1OnIncrement; // Mode 1 max on time before cycling back to start
long Mode2OnTime = 1000;          // milliseconds of on-time
long Mode2OnIncrement = 1000;      // increment on time in milliseconds
int M2OnTimeMax = 5000 - Mode2OnIncrement; // Mode 2 max on time before cycling back to start
long DefaultOffTime = 500;          // milliseconds of off-time
long M1OnTimeCurr = Mode1OnTime;;   // initialize variable for Mode 1 current OnTime incrementing
long M2OnTimeCurr = Mode2OnTime;;   // initialize variable for Mode 2 current OnTime incrementing

void setup()
{
  pinMode(ledPin, OUTPUT);
  pinMode(pinSwitch, INPUT_PULLUP);
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{
 
  if (digitalRead(pinSwitch) == HIGH) { 
      // Serial.println("Mode 1");
      mode1();
      
   }
  else {
    // Serial.println("Mode 2");
    mode2();
   }  
}

void mode1() {

  // check to see if it's time to change the state of the LED
  unsigned long currentMillis = millis();

  if((ledState == HIGH) && (currentMillis - previousMillis >= M1OnTimeCurr))
  {
    ledState = LOW;  // Turn it off
    previousMillis = currentMillis;  // Remember the time
    digitalWrite(ledPin, ledState);  // Update the actual LED
    // increment on time up to Max
    if((M1OnTimeCurr <= M1OnTimeMax)) {
      M1OnTimeCurr = M1OnTimeCurr + Mode1OnIncrement;
    }
    else
      M1OnTimeCurr = Mode1OnTime; // after Max cycle set back to preset
  }

  else if ((ledState == LOW) && (currentMillis - previousMillis >= DefaultOffTime))
  {
    ledState = HIGH;  // turn it on
    previousMillis = currentMillis;   // Remember the time
    digitalWrite(ledPin, ledState);	  // Update the actual LED
  }
}

void mode2() {

  // check to see if it's time to change the state of the LED
  unsigned long currentMillis = millis();

  if((ledState == HIGH) && (currentMillis - previousMillis >= M2OnTimeCurr))
  {
    ledState = LOW;  // Turn it off
    previousMillis = currentMillis;  // Remember the time
    digitalWrite(ledPin, ledState);  // Update the actual LED
    // increment on time up to Max
    if((M2OnTimeCurr <= M2OnTimeMax)) { 
      M2OnTimeCurr = M2OnTimeCurr + Mode2OnIncrement;
    }
    else
      M2OnTimeCurr = Mode2OnTime; // after Max cycle set back to preset
  }

  else if ((ledState == LOW) && (currentMillis - previousMillis >= DefaultOffTime))
  {
    ledState = HIGH;  // turn it on
    previousMillis = currentMillis;   // Remember the time
    digitalWrite(ledPin, ledState);   // Update the actual LED
  }
}

Ok I think I figured out how to make it work with incremental on cycles, based on the code you shared. Here's what I came up with, let me know if I botched anything.

// Sketch for testing switch between loops for LED patterns

unsigned long lastMilli = 0;
int intervalDelay = 500;
byte mode = 0;

unsigned long lastCheck = 0;
int intervalDebounce = 50;

int ledPin =  9;      // the number of the LED pin
int pinSwitch = 2;
int ledState = LOW;             // ledState used to set the LED
long Mode1OnTime = 500;           // milliseconds of on-time
int Mode1OnIncrement = 100;       // increment on time in milliseconds
int M1OnTimeMax = 1000 - Mode1OnIncrement; // Mode 1 max on time before cycling back to start
long Mode2OnTime = 1000;          // milliseconds of on-time
long Mode2OnIncrement = 1000;      // increment on time in milliseconds
int M2OnTimeMax = 5000 - Mode2OnIncrement; // Mode 2 max on time before cycling back to start
long DefaultOffTime = 500;          // milliseconds of off-time
long M1OnTimeCurr = Mode1OnTime;   // initialize variable for Mode 1 current OnTime incrementing
long M2OnTimeCurr = Mode2OnTime;   // initialize variable for Mode 2 current OnTime incrementing

void setup()
{
  pinMode(ledPin, OUTPUT);
  pinMode(pinSwitch, INPUT_PULLUP);
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{

  if (millis() - lastCheck >= intervalDebounce) {
    lastCheck = millis();
    if (digitalRead(pinSwitch) == HIGH) {
      if (mode != 1) {
        Serial.println("Mode 1");
        mode = 1;
      }
      intervalDelay = 500;
    }
    else {
      if (mode != 2) {
        Serial.println("Mode 2");
        mode = 2;
      }
    }
  }

  switch (mode) {
    case 1: mode1(); break;
    case 2: mode2(); break;
  }

}

// mode 1 pattern
void mode1() {

  // check to see if it's time to change the state of the LED
  unsigned long currentMillis = millis();

  if((ledState == HIGH) && (millis() - lastMilli >= M1OnTimeCurr))
  {
    ledState = LOW;  // Turn it off
    lastMilli = currentMillis;  // Remember the time
    digitalWrite(ledPin, ledState);  // Update the actual LED
    // increment on time up to Max
    if((M1OnTimeCurr <= M1OnTimeMax)) {
      M1OnTimeCurr = M1OnTimeCurr + Mode1OnIncrement;
    }
    else
      M1OnTimeCurr = Mode1OnTime; // after Max cycle set back to preset
  }

  else if ((ledState == LOW) && (currentMillis - lastMilli >= DefaultOffTime))
  {
    ledState = HIGH;  // turn it on
    lastMilli = currentMillis;   // Remember the time
    digitalWrite(ledPin, ledState);	  // Update the actual LED
  }
}

// mode 2 pattern
void mode2() {

  // check to see if it's time to change the state of the LED
  unsigned long currentMillis = millis();

  if((ledState == HIGH) && (currentMillis - lastMilli >= M2OnTimeCurr))
  {
    ledState = LOW;  // Turn it off
    lastMilli = currentMillis;  // Remember the time
    digitalWrite(ledPin, ledState);  // Update the actual LED
    // increment on time up to Max
    if((M2OnTimeCurr <= M2OnTimeMax)) { 
      M2OnTimeCurr = M2OnTimeCurr + Mode2OnIncrement;
    }
    else
      M2OnTimeCurr = Mode2OnTime; // after Max cycle set back to preset
  }

  else if ((ledState == LOW) && (currentMillis - lastMilli >= DefaultOffTime))
  {
    ledState = HIGH;  // turn it on
    lastMilli = currentMillis;   // Remember the time
    digitalWrite(ledPin, ledState);   // Update the actual LED
  }
}

Simulation here..

your code has a lotta redundant statements

look this over

  • just read millis() once
  • toggle mode based on button press, not button state
  • just made mode2 blink LED quickly
// Sketch for testing switch between loops for LED patterns

unsigned long lastMilli = 0;
int intervalDelay = 500;
byte mode = 0;
unsigned long lastCheck = 0;
unsigned long intervalDebounce = 50;

int ledPin =  9;      // the number of the LED pin
int pinSwitch = 2;

int ledState = LOW;             // ledState used to set the LED

unsigned long Mode1OnTime        = 500;           // milliseconds of on-time
unsigned long Mode1OnIncrement   = 100;       // increment on time in milliseconds
unsigned long M1OnTimeMax        = 1000 - Mode1OnIncrement; // Mode 1 max on time before cycling back to start

unsigned long Mode2OnTime        = 100;          // milliseconds of on-time

unsigned long M1OnTimeCurr = Mode1OnTime;   // initialize variable for Mode 1 current OnTime incrementing
unsigned long M2OnTimeCurr = Mode2OnTime;   // initialize variable for Mode 2 current OnTime incrementing

unsigned long currentMillis;
byte butLst;

// -----------------------------------------------------------------------------
void loop ()
{
    currentMillis = millis ();

    byte but = digitalRead(pinSwitch);
    if (butLst != but)  {
        butLst = but;
        delay (20);     // debunce
        if (LOW == but)
            mode = !mode;
    }

    if (mode)
        mode1 ();
    else
        mode2 ();
}

// -----------------------------------------------------------------------------
void mode1 ()
{
    if (currentMillis - lastMilli >= M1OnTimeCurr)  {
        lastMilli = currentMillis;

        if (ledState == HIGH) {
            ledState = LOW;  // Turn it off

            // increment on time up to Max
            if ((M1OnTimeCurr <= M1OnTimeMax))
                M1OnTimeCurr = M1OnTimeCurr + Mode1OnIncrement;
            else
                M1OnTimeCurr = Mode1OnTime;
        }
        else 
            ledState = HIGH;  // turn it on

        digitalWrite (ledPin, ledState);  // Update the actual LED

        Serial.print ("mode1: ");
        Serial.println (M1OnTimeCurr);
    }
}

// -----------------------------------------------------------------------------
void mode2 ()
{
    if (currentMillis - lastMilli >= M2OnTimeCurr) {
        lastMilli = currentMillis;

        digitalWrite (ledPin, ! digitalRead (ledPin));
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    pinMode (ledPin, OUTPUT);
    pinMode (pinSwitch, INPUT_PULLUP);
    butLst = digitalRead (pinSwitch);

    Serial.begin (9600);
}

works as you described and you didn't use delay once..
Great Job!! :partying_face:

Looks like you got the concept..

happy coding.. ~q

I had a feeling there was a bit of unnecessary redundancy in that code. That's much cleaner, thank you!

You said to toggle mode based on button press rather than state. That's perfect for a momentary push button, but what would you change if using a toggle switch instead?

I want to attach a blue LED to SW pin 1 and tie to 5V rail with a resistor to indicate Mode 1 is active with the SW in the off position and a green LED on SW pin 3 to indicate Mode 2 active. I'm guessing the changes would be very minimal.

For the off duration in both mode 1 and 2 I want to use the intervalDelay var. How would I implement this in the code you provided?

Finally, this code remembers the incremental state each mode was last in, so when the button is pressed it continues from the last known interval. What would need to be changed so that the loop starts over from ModeXOnTime ms?

Here's what I have so far using momentary push button to switch modes: SwitchLed Patterns v3

// Sketch for testing switch between loops for LED patterns v3 (remembers mode state)

unsigned long lastMilli = 0;
int intervalDelay = 500;
byte mode = 1;
unsigned long lastCheck = 0;

int ledPin =  9;      // the number of the LED pin
int pinSwitch = 2;    // pin to button to GND (use internal pullup)

int ledState = LOW;             // ledState used to set the LED

unsigned long M1InitOnTime        = 500;           // milliseconds of on-time
unsigned long M1OnIncrement   = 100;       // increment on time in milliseconds
unsigned long M1OnTimeMax        = 1000 - M1OnIncrement; // Mode 1 max on time before cycling back to start

unsigned long M2InitOnTime         = 1000;      // milliseconds of on-time
unsigned long M2OnIncrement    = 1000;      // increment on time in milliseconds
unsigned long M2OnTimeMax         = 5000 - M2OnIncrement; // Mode 2 max on time before cycling back to start

unsigned long M1OnTimeCurr = M1InitOnTime;   // initialize variable for Mode 1 current On Time incrementing
unsigned long M2OnTimeCurr = M2InitOnTime;   // initialize variable for Mode 2 current On Time incrementing

unsigned long currentMillis;
byte butLst;   // last button state

// -----------------------------------------------------------------------------
void loop ()
{
    currentMillis = millis ();

    byte but = digitalRead(pinSwitch);
    if (butLst != but)  {
        butLst = but;
        delay (20);     // debounce
        if (LOW == but)
            mode = !mode;
    }

    if (mode)
        mode1 ();
    else
        mode2 ();
}

// -----------------------------------------------------------------------------
void mode1 ()
{
    if (currentMillis - lastMilli >= M1OnTimeCurr)  {
        lastMilli = currentMillis; // Remember the time

        if (ledState == HIGH) {
            ledState = LOW;  // Turn it off

            // increment on time up to Max
            if ((M1OnTimeCurr <= M1OnTimeMax))
                M1OnTimeCurr = M1OnTimeCurr + M1OnIncrement;
            else
                M1OnTimeCurr = M1InitOnTime;
        }
        else 
            ledState = HIGH;  // turn it on

        digitalWrite (ledPin, ledState);  // Update the actual LED

        Serial.print ("mode1: ");
        Serial.println (M1OnTimeCurr);
    }
}

// -----------------------------------------------------------------------------
void mode2 ()
{
    if (currentMillis - lastMilli >= M2OnTimeCurr)  {
        lastMilli = currentMillis; // Remember the time

        if (ledState == HIGH) {
            ledState = LOW;  // Turn it off

            // increment on time up to Max
            if ((M2OnTimeCurr <= M2OnTimeMax))
                M2OnTimeCurr = M2OnTimeCurr + M2OnIncrement;
            else
                M2OnTimeCurr = M2InitOnTime;
        }
        else 
            ledState = HIGH;  // turn it on

        digitalWrite (ledPin, ledState);  // Update the actual LED

        Serial.print ("mode2: ");
        Serial.println (M2OnTimeCurr);
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    pinMode (ledPin, OUTPUT);
    pinMode (pinSwitch, INPUT_PULLUP);
    butLst = digitalRead (pinSwitch);

    Serial.begin (9600);
}
    if (HIGH == digitalRead(pinSwitch))
        mode1 ();
    else
        mode2 ();

wouldn't intervalDelay be the same as your M1OnTimeCurr?

the only difference between you modes are the min/max times and the increment. couldn't they be variables set by the switch setting and there only be on timer loop?

where are you going with this? what are you trying to learn?

wouldn't intervalDelay be the same as your M1OnTimeCurr ?

Rather than intervalDelay, a more descriptive var would be LEDoffTime.

the only difference between you modes are the min/max times and the increment. couldn't they be variables set by the switch setting and there only be on timer loop?

Yes, I'm sure it could be reduced to that. I'm not skilled enough yet to know how, but the code is very understandable. I used to play with assembly lang and C when I was younger, just got the urge to get back into coding after digging out an old Ardunio.

This is just a little learning project idea I came up with. It's for a game I'm working on where the modes are used to signal team mates as to what activity you're working on.

I really appreciate all the help. What a great community you guys have here!

just to clear up some confusion -- Capitalize Constants

LedOffTime suggests a constant different in value from LedOnTime that some variable ledTime might be set to.

in your code you turn the LED on and off the same amount of time


look this over, see if it helps

// Sketch for testing switch between loops for LED patterns v3 (remembers mode state)

#if 0
const byte PinLed    = 13;
const byte PinSwitch = A1;
#else
const byte PinLed    = 9;
const byte PinSwitch = 2;
#endif

int ledState;

unsigned long M1InitOnTime  = 500;
unsigned long M1OnIncrement = 100;
unsigned long M1OnTimeMax   = 1000 - M1OnIncrement;

unsigned long M2InitOnTime  = 1000;
unsigned long M2OnIncrement = 1000;
unsigned long M2OnTimeMax   = 5000 - M2OnIncrement;

const unsigned long MsecLedOff = 100;

unsigned long msecPeriod;
unsigned long msecLst;
unsigned long msec;

unsigned long msecPeriodMin;
unsigned long msecPeriodMax;
unsigned long msecPeriodInc;

enum { On = HIGH, Off = LOW };

// -----------------------------------------------------------------------------
void loop ()
{
    msec = millis ();
    if (msec - msecLst >= msecPeriod)  {
        msecLst = msec;
        if (ledState == On) {
            ledState   = Off;
            msecPeriod = MsecLedOff;
        }
        else {
            ledState = On;
            msecPeriod += msecPeriodInc;
            if (msecPeriodMax <= msecPeriod)
                msecPeriod = msecPeriodMin;
        }

        digitalWrite (PinLed, ledState);
    }

    if (LOW == digitalRead(PinSwitch))  {
        msecPeriodMin = M1InitOnTime;
        msecPeriodMax = M1OnTimeMax;
        msecPeriodInc = M1OnTimeMax;
    }
    else {
        msecPeriodMin = M2InitOnTime;
        msecPeriodMax = M2OnTimeMax;
        msecPeriodInc = M2OnTimeMax;
    }
}

// -----------------------------------------------------------------------------
void setup ()
{
    pinMode (PinLed, OUTPUT);
    pinMode (PinSwitch, INPUT_PULLUP);

    Serial.begin (9600);
}

Yes in that last code that I tried to adapt from what you shared, but in my v2 it's working as I described. Just a lot of unnecessary code as you've shown.

I cleaned up the code a bit and added some serial output: v2 simulation

The code you provided doesn't seem to work as expected. It starts in mode 2 instead of mode 1, with the SW in the off or left position and it doesn't increment the on time. See here: Switch LED Patterns v4