Using button to break for() in switch case

I am working on lighting up WS2812 strips in different patterns. The idea is that they show a certain pattern based on a button press. The button press increments currentPressMode, which controls a switch case. Each function in the switch case controls sets of for loops that light up a set of LEDs and move that "bar" over a certain number of LEDs, like a Larson Scanner.

I'm running into the issue of my delays are interfering with the button press, which makes sense to me. If I could just get some direction on how to replace those delays with millis() or if there is another way to break my for loops I would appreciate it. I have tried millis() from other examples in forums but can't find a setup that preserves the current functionality of the code. The delay(speedDelay) statement controls the speed of the LED sweep.

#include "FastLED.h"

#define LED_PIN 7     //define forward top controller pin
#define NUM_LEDS 144  //Total LEDs on each strip for quadrant
#define BAR_LENGTH 4  //number of LEDs for "bars"
#define DIVISION 36   //Index for front top LEDs

//change values for LED colors
#define RED_VAL 0x1F    //Must be >0 to function
#define GREEN_VAL 0x1F  //Must be >0 to function
#define BLUE_VAL 0xFF   //Set to Blue

//delay values, only for in phase and 180 degrees
#define SWEEP_DELAY 100  //defines how long LEDs wait before sweeping again

CRGB ledsFront[NUM_LEDS];

//button constants
const int modeButton = 2;

//Button logic variables
int lastBtnMode = HIGH;
int btnMode;
int currentPressMode = 0;
int buttonMode;

int SPEED_DELAY = 50;  //initialized at 50 clock ticks

//Button debounce variables
unsigned long lastDebounceTimeMode = 0;
unsigned long debounceDelay = 50;

void setup() {
  Serial.begin(9600);  //For debugging
  //define led strips
  FastLED.addLeds<WS2812, LED_PIN, GRB>(ledsFront, NUM_LEDS).setCorrection(TypicalLEDStrip);
  //define button input
  pinMode(modeButton, INPUT);
}

void loop() {
  //initialize button debounce
  buttonMode = digitalRead(modeButton);
  if (buttonMode != lastBtnMode) lastDebounceTimeMode = millis();

  //switch case for mode button.
  switch (currentPressMode) {
    //180 degrees out of phase
    case 1:
      setAll(0, 0, 0);
      Serial.print("Displaying 180 out of phase.\n");
      Pulse180(RED_VAL, GREEN_VAL, BLUE_VAL, BAR_LENGTH, SPEED_DELAY, SWEEP_DELAY);
      break;
    //90 degrees out of phase
    case 2:
      setAll(0, 0, 0);
      Pulse90(RED_VAL, GREEN_VAL, BLUE_VAL, BAR_LENGTH, SPEED_DELAY, SWEEP_DELAY);
      Serial.print("Displaying 90 out of phase.\n");
      break;
    //In phase
    case 3:
      setAll(0, 0, 0);
      PulseInPhase(RED_VAL, GREEN_VAL, BLUE_VAL, BAR_LENGTH, SPEED_DELAY, SWEEP_DELAY);
      Serial.print("Displaying in phase.\n");
      break;
    //Clear LEDs
    default:
      setAll(0, 0, 0);
      //start LEDs with button press from "off"
      if ((millis() - lastDebounceTimeMode) > debounceDelay) {
        if (buttonMode != btnMode) {
          btnMode = buttonMode;
          if (btnMode == LOW) {
            currentPressMode = 1;
            Serial.print(currentPressMode);
            Serial.print("\n");
          }
        }
      }
      lastBtnMode = buttonMode;
      break;
  }
}

//Top Level function
//Front LEDs start in center, rear LEDs start on outside
void Pulse180(byte redVal, byte greenVal, byte blueVal, int barLen, int speedDelay, int sweepDelay) {
  //Sweep front from center to outside, back from outside to center
  int i = ((DIVISION - barLen) / 2);
  for (i = ((DIVISION - barLen - 2) / 2); i >= 1; i--) {
    //Check for button press
    if ((millis() - lastDebounceTimeMode) > debounceDelay) {
      if (buttonMode != btnMode) {
        btnMode = buttonMode;
        if (btnMode == LOW) {
          currentPressMode = 2;
          Serial.print(currentPressMode);
          Serial.print("\n");
          break;
          return;
        }
      }
    }
    lastBtnMode = buttonMode;

    setPixel(i, redVal, greenVal, blueVal);             //Front
    setPixel(DIVISION - i, redVal, greenVal, blueVal);  //Front
    for (int j = 1; j <= barLen; j++) {
      if ((millis() - lastDebounceTimeMode) > debounceDelay) {
        if (buttonMode != btnMode) {
          btnMode = buttonMode;
          if (btnMode == LOW) {
            currentPressMode = 2;
            Serial.print(currentPressMode);
            Serial.print("\n");
            break;
            return;
          }
        }
      }
      lastBtnMode = buttonMode;

      setPixel(i + j, redVal, greenVal, blueVal);
      setPixel(DIVISION - i - j, redVal, greenVal, blueVal);
    }
    setPixel(i + barLen, 0, 0, 0);
    setPixel(DIVISION - i - barLen, 0, 0, 0);

    FastLED.show();
    delay(speedDelay);
  }
  setAll(0, 0, 0);

  //Sweep front from outside to center, back from center to outside
  i = ((DIVISION - barLen) / 2);
  for (i = ((DIVISION - barLen - 2) / 2); i >= 0; i--) {
    //Check for button press
    if ((millis() - lastDebounceTimeMode) > debounceDelay) {
      if (buttonMode != btnMode) {
        btnMode = buttonMode;
        if (btnMode == LOW) {
          currentPressMode = 2;
          Serial.print(currentPressMode);
          Serial.print("\n");
          break;
          return;
        }
      }
    }
    lastBtnMode = buttonMode;

    setPixel((((DIVISION - barLen) / 2) - i), redVal, greenVal, blueVal);             //Front
    setPixel(DIVISION - (((DIVISION - barLen) / 2) - i), redVal, greenVal, blueVal);  //Front
    for (int j = 1; j <= barLen; j++) {
      if ((millis() - lastDebounceTimeMode) > debounceDelay) {
        if (buttonMode != btnMode) {
          btnMode = buttonMode;
          if (btnMode == LOW) {
            currentPressMode = 2;
            Serial.print(currentPressMode);
            Serial.print("\n");
            break;
            return;
          }
        }
      }
      lastBtnMode = buttonMode;

      setPixel((((DIVISION - barLen) / 2) - i) + j, redVal, greenVal, blueVal);
      setPixel(DIVISION - (((DIVISION - barLen) / 2) - i) - j, redVal, greenVal, blueVal);
    }
    setPixel((((DIVISION - barLen) / 2) - i) - barLen + (barLen - 1), 0, 0, 0);
    setPixel(DIVISION - (((DIVISION - barLen) / 2) - i) - barLen + barLen, 0, 0, 0);

    FastLED.show();
    delay(speedDelay);
  }
  setAll(0, 0, 0);
}

//Top level function
//Front LEDs start in middle and move outside, back LEDs start outside and move inward
void Pulse90(byte redVal, byte greenVal, byte blueVal, int barLen, int speedDelay, int sweepDelay) {
  int i;
  //Sweep front from middle to outside, back from outside to middle
  for (i = ((DIVISION - barLen - 2) / 4); i >= 1; i--) {
    //Check for button press
    if ((millis() - lastDebounceTimeMode) > debounceDelay) {
      if (buttonMode != btnMode) {
        btnMode = buttonMode;
        if (btnMode == LOW) {
          currentPressMode = 3;
          Serial.print(currentPressMode);
          Serial.print("\n");
          return;
        }
      }
    }
    lastBtnMode = buttonMode;

    setPixel(i, redVal, greenVal, blueVal);             //Front
    setPixel(DIVISION - i, redVal, greenVal, blueVal);  //Front
    for (int j = 1; j <= barLen; j++) {
      if ((millis() - lastDebounceTimeMode) > debounceDelay) {
        if (buttonMode != btnMode) {
          btnMode = buttonMode;
          if (btnMode == LOW) {
            currentPressMode = 3;
            Serial.print(currentPressMode);
            Serial.print("\n");
            return;
          }
        }
      }
      lastBtnMode = buttonMode;
      setPixel(i + j, redVal, greenVal, blueVal);
      setPixel(DIVISION - i - j, redVal, greenVal, blueVal);
    }
    setPixel(i + barLen, 0, 0, 0);
    setPixel(DIVISION - i - barLen, 0, 0, 0);

    FastLED.show();
    delay(speedDelay);
  }
  //delay(sweepDelay);
  setAll(0, 0, 0);

  //Sweep front from outside to middle, back from middle to center
  for (i = ((DIVISION - barLen - 2) / 2); i >= ((DIVISION - barLen - 2) / 4); i--) {
    //Check for button press
    if ((millis() - lastDebounceTimeMode) > debounceDelay) {
      if (buttonMode != btnMode) {
        btnMode = buttonMode;
        if (btnMode == LOW) {
          currentPressMode = 3;
          Serial.print(currentPressMode);
          Serial.print("\n");
          return;
        }
      }
    }
    lastBtnMode = buttonMode;

    setPixel((((DIVISION - barLen) / 2) - i), redVal, greenVal, blueVal);            //Front
    setPixel(DIVISION - (((DIVISION - barLen) / 2) - i), redVal, greenVal, blueVal);             //Front

    for (int j = 1; j <= barLen; j++) {
      if ((millis() - lastDebounceTimeMode) > debounceDelay) {
        if (buttonMode != btnMode) {
          btnMode = buttonMode;
          if (btnMode == LOW) {
            currentPressMode = 3;
            Serial.print(currentPressMode);
            Serial.print("\n");
            return;
          }
        }
      }lastBtnMode = buttonMode;
      setPixel((((DIVISION - barLen) / 2) - i) + j, redVal, greenVal, blueVal);
      setPixel(DIVISION - (((DIVISION - barLen) / 2) - i) - j, redVal, greenVal, blueVal);
    }
    setPixel((((DIVISION - barLen) / 2) - i) - barLen + (barLen - 1), 0, 0, 0);
    setPixel(DIVISION - (((DIVISION - barLen) / 2) - i) - barLen + barLen, 0, 0, 0);

    FastLED.show();
    delay(speedDelay);
  }
  //delay(sweepDelay);
  setAll(0, 0, 0);

  //Sweep front from middle to center, back from center to middle
  for (i = ((DIVISION - barLen - 2) / 2); i >= ((DIVISION - barLen - 2) / 4); i--) {
    //Check for button press
    if ((millis() - lastDebounceTimeMode) > debounceDelay) {
      if (buttonMode != btnMode) {
        btnMode = buttonMode;
        if (btnMode == LOW) {
          currentPressMode = 3;
          Serial.print(currentPressMode);
          Serial.print("\n");
          return;
        }
      }
    }lastBtnMode = buttonMode;

    setPixel(((DIVISION - barLen) / 4) + i, redVal, greenVal, blueVal);             //Front
    setPixel(DIVISION - ((DIVISION - barLen) / 4) - i, redVal, greenVal, blueVal);             //Front
    for (int j = 1; j <= barLen; j++) {
      if ((millis() - lastDebounceTimeMode) > debounceDelay) {
        if (buttonMode != btnMode) {
          btnMode = buttonMode;
          if (btnMode == LOW) {
            currentPressMode = 3;
            Serial.print(currentPressMode);
            Serial.print("\n");
            return;
          }
        }
      }lastBtnMode = buttonMode;
      setPixel(((DIVISION - barLen) / 4) + i + j, redVal, greenVal, blueVal);
      setPixel(DIVISION - ((DIVISION - barLen) / 4) - i - j, redVal, greenVal, blueVal);
    }
    setPixel(((DIVISION - barLen) / 4) + i + barLen, 0, 0, 0);
    setPixel(DIVISION - ((DIVISION - barLen) / 4) - i - barLen, 0, 0, 0);

    FastLED.show();
    delay(speedDelay);
  }
  //delay(sweepDelay);
  setAll(0, 0, 0);

  //Sweep front from center to middle, back from middle to outside
  for (i = ((DIVISION - barLen - 2) / 4); i >= 1; i--) {
    //Check for button press
    if ((millis() - lastDebounceTimeMode) > debounceDelay) {
      if (buttonMode != btnMode) {
        btnMode = buttonMode;
        if (btnMode == LOW) {
          currentPressMode = 3;
          Serial.print(currentPressMode);
          Serial.print("\n");
          return;
        }
      }
    }lastBtnMode = buttonMode;

    setPixel(((DIVISION - barLen - 2) / 4) + i, redVal, greenVal, blueVal);             //Front
    setPixel(DIVISION - ((DIVISION - barLen - 2) / 4) - i, redVal, greenVal, blueVal);             //Front
    for (int j = 1; j <= barLen; j++) {
      if ((millis() - lastDebounceTimeMode) > debounceDelay) {
        if (buttonMode != btnMode) {
          btnMode = buttonMode;
          if (btnMode == LOW) {
            currentPressMode = 3;
            Serial.print(currentPressMode);
            Serial.print("\n");
            return;
          }
        }
      }lastBtnMode = buttonMode;
      setPixel(((DIVISION - barLen - 2) / 4) + i + j, redVal, greenVal, blueVal);
      setPixel(DIVISION - ((DIVISION - barLen - 2) / 4) - i - j, redVal, greenVal, blueVal);
    }
    setPixel(((DIVISION - barLen - 2) / 4) + i + barLen, 0, 0, 0);
    setPixel(DIVISION - ((DIVISION - barLen - 2) / 4) - i - barLen, 0, 0, 0);

    FastLED.show();
    delay(speedDelay);
  }
  //delay(sweepDelay);
  setAll(0, 0, 0);
}

//Top level function
//All LEDs start in center
void PulseInPhase(byte redVal, byte greenVal, byte blueVal, int barLen, int speedDelay, int sweepDelay) {
  int i = ((DIVISION - barLen) / 2);
  for (i = ((DIVISION - barLen - 2) / 2); i >= 1; i--) {
    //Check for button press
    if ((millis() - lastDebounceTimeMode) > debounceDelay) {
      if (buttonMode != btnMode) {
        btnMode = buttonMode;
        if (btnMode == LOW) {
          currentPressMode = 0;
          Serial.print(currentPressMode);
          Serial.print("\n");
          return;
        }
      }
    }lastBtnMode = buttonMode;

    setPixel(i, redVal, greenVal, blueVal);              //Front
    setPixel(DIVISION - i, redVal, greenVal, blueVal);              //Front
    for (int j = 1; j <= barLen; j++) {
      if ((millis() - lastDebounceTimeMode) > debounceDelay) {
        if (buttonMode != btnMode) {
          btnMode = buttonMode;
          if (btnMode == LOW) {
            currentPressMode = 0;
            Serial.print(currentPressMode);
            Serial.print("\n");
            return;
          }
        }
      }lastBtnMode = buttonMode;
      setPixel(i + j, redVal, greenVal, blueVal);
      setPixel(DIVISION - i - j, redVal, greenVal, blueVal);
    }
    setPixel(i + barLen, 0, 0, 0);
    setPixel(DIVISION - i - barLen, 0, 0, 0);

    FastLED.show();
    delay(speedDelay);
  }
  setAll(0, 0, 0);

  i = ((DIVISION - barLen) / 2);
  for (i = ((DIVISION - barLen - 2) / 2); i >= 0; i--) {
    //Check for button press
    if ((millis() - lastDebounceTimeMode) > debounceDelay) {
      if (buttonMode != btnMode) {
        btnMode = buttonMode;
        if (btnMode == LOW) {
          currentPressMode = 0;
          Serial.print(currentPressMode);
          Serial.print("\n");
          return;
        }
      }
    }lastBtnMode = buttonMode;

    setPixel((((DIVISION - barLen) / 2) - i), redVal, greenVal, blueVal);              //Front
    setPixel(DIVISION - (((DIVISION - barLen) / 2) - i), redVal, greenVal, blueVal);              //Front
    for (int j = 1; j <= barLen; j++) {
      if ((millis() - lastDebounceTimeMode) > debounceDelay) {
        if (buttonMode != btnMode) {
          btnMode = buttonMode;
          if (btnMode == LOW) {
            currentPressMode = 0;
            Serial.print(currentPressMode);
            Serial.print("\n");
            return;
          }
        }
      }lastBtnMode = buttonMode;
      setPixel((((DIVISION - barLen) / 2) - i) + j, redVal, greenVal, blueVal);
      setPixel(DIVISION - (((DIVISION - barLen) / 2) - i) - j, redVal, greenVal, blueVal);
    }
    setPixel((((DIVISION - barLen) / 2) - i) + barLen - barLen, 0, 0, 0);
    setPixel(DIVISION - (((DIVISION - barLen) / 2) - i) - barLen + barLen, 0, 0, 0);

    FastLED.show();
    delay(speedDelay);
  }
  setAll(0, 0, 0);
}

//set color of individual LEDs
void setPixel(int Pixel, byte red, byte green, byte blue) {
  ledsFront[Pixel].r = red;
  ledsFront[Pixel].g = green;
  ledsFront[Pixel].b = blue;
}

//set all LEDs to specified color (used to clear LEDs)
void setAll(byte red, byte green, byte blue) {
  for (int i = 0; i < NUM_LEDS; i++) {
    setPixel(i, red, green, blue);
  }
  FastLED.show();
}

I'm attaching a dumbed down version of my code as a file since it's pretty massive. I apologize for the obnoxious amount of checking for a button press, I just posted what currently works.

For Loop With Button Forum.ino (14.7 KB)

Post your code inline here.... not an attachment.

The basic concept is that you have to get rid of your for() loops and just do a single step/pattern each time through loop() so your buttons stay responsive. When you call your pattern function, it needs to remember how many milliseconds have passed since the last update and then either update the pattern, or do nothing.

Yes, yes you are.

See

please, and study that code. That's the best way out if the box you in.

And the thread, and the links in that post I linked.

a7

1 Like

This is one situation where attaching a switch to an interrupt pin might be your solution.

A switch press could be caught by the ISR, you would then react accordingly.


In the Arduino IDE, use Ctrl T or CMD T to format your code then copy the complete sketch.

Use the </> icon from the ‘reply menu’ to attach the copied sketch.

You need to majorly restructure your code. You should use state machines. Your top-level state machine should track the pattern currently being executed (you sort of already have that with your currentPressMode switch statement) and then a state machine for each pattern to track the sweeps and timing.

A state machine is not as complex as it sounds. It is just a way to "remember" where you are at. Essentially you will remove all loops from the loop() function and let loop() do the looping. Since there are no delays you can check the button every time you enter loop() and interrupt the currently executing pattern if you wish.

That would solve the problem of detecting the button press but any delays may cause a corresponding delay in reacting to it. I know you already know that just pointing out for the OP.

The other day I already saw one that got lost in loop() . :nerd_face:

1 Like

Thank you. At first glance this looks to be exactly what I need, it just never showed up in my search. I'll play around with it and see if it is what I need.

No need to apologize for so much button checking, even if there are better ways than checking all over the place that to accomplish the larger goals of the program.

But you should apologize for essentially duplicating the code that does that checking I lost count how many times. The only difference I spotted was what currentPressMode is set to in the event of a press, this calls for using a function. If all your use of functions so far has been copying other code, it is time to learn how to create your own. Functions.

So if you do continue to need to check the button all over the place, see if this makes sense - wherever you had those duplicated line of code, do something like

  if (buttonGotPressed())
    currentPressMode = 3;  // or whatever

And then put all that code for button press checking into one function what returns true or false, button did or didn't get pressed:
bool buttonGotPressed()
{
  buttonMode = digitalRead(modeButton);
  if (buttonMode != lastBtnMode) lastDebounceTimeMode = millis();

    //Check for button press
    if ((millis() - lastDebounceTimeMode) > debounceDelay) {
      if (buttonMode != btnMode) {
        btnMode = buttonMode;
        if (buttonMode == LOW) {
          return true;
        }
      }
    }
    lastBtnMode = buttonMode;
    return false;
}

When you find yourself cutting, pasting and tweaking a buncha code, it is a clue you could benefit from writing it once, generalizing it if necessary, as a function.

In this case the only difference is the currentPressMode setting, so just use the fact that the button got pressed to set it.

I tried to leave your logic and where you called and put all the variables involved unchanged. It could be simplified a bit, and many global variables could be "hidden" by making them internal to the function and declaring them as static. Another time. :- |

Here is the function in a little test program on the simulator:

button press function simulated

HTH

Uh oh - I've just noticed some odd lines

    break;
    return;    // never reaches this!

and I must confess I have an incomplete idea how you using the button press "fact" in you logic. Nevertheless, I remain convinced that all those checks can be done with the aid of the function, whilst leaving your flow intact.

TBC, the function still needs to see the button when it is down, and reports true once, when it does. No magic like button got pressed sometime between the last call and the current call. So sluggish response will still be sluggish, same as it may be now.

But it is time for other things.

a7

Thank you so much for the help. I am making note of this stuff in my new code to optimize it.

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