Adding Loop To a Case Statment???

Hello, I'm very new to this and have been doing some research.
I was wondering if there was a way to do add a loop to my case statement so that when the button hasn't been press the code continues to loop until the button has been pressed again.

I was thinking about rewriting the code so that the button runs on a counter (not 100% sure how to do that an example would be nice) so that when it is press x amount of times it runs through each case/program till it comes back to the beginning.

Everything works fine with the code, except that the cases only run once which I'm trying to eliminate. Thanks!

here is the code:

#include <Adafruit_NeoPixel.h>

#define BUTTON_PIN   1    // Digital IO pin connected to the button.  This will be
                          // driven with a pull-up resistor so the switch should
                          // pull the pin to ground momentarily.  On a high -> low
                          // transition the button press logic will execute.

#define PIXEL_PIN    2    // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 58

// Parameter 1 = number of pixels in strip,  neopixel stick has 8
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_RGB     Pixels are wired for RGB bitstream
//   NEO_GRB     Pixels are wired for GRB bitstream, correct for neopixel stick
//   NEO_KHZ400  400 KHz bitstream (e.g. FLORA pixels)
//   NEO_KHZ800  800 KHz bitstream (e.g. High Density LED strip), correct for neopixel stick
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

bool oldState = HIGH;
int showType = 0;

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}

void loop() {
  // Get current button state.
  bool newState = digitalRead(BUTTON_PIN);

  // Check if state changed from high to low (button press).
  if (newState == LOW && oldState == HIGH) {
    // Short delay to debounce button.
    delay(5);
    // Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW) {
      showType++;
      if (showType > 6)
        showType=0;
      startShow(showType);
    }
  }

  // Set the last button state to the old state.
  oldState = newState;

}

void startShow(int i) {
  switch(i){
    case 0: colorWipe(strip.Color(0, 0, 0), 50);    // Black/off
            break;
    case 1: CylonBounce(0xff, 0xff, 0xff, 4, 10, 50);  // White
            break;
    case 2: colorWipe(strip.Color(0, 255, 255), 50);  // Cyan
            break;
    case 3: theaterChaseRainbow(50);
            break;
    case 4: colorWipe(strip.Color(255,0, 0), 50);  // Red
            break;
    case 5: theaterChase(strip.Color(  0,   0, 127), 50); // Blue
            break;
    case 6: rainbowCycle(20);
            break;
  }
}

//Color Wipe Mode
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

// Rainbow Cycle Mode
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256*10; j++) { // 10 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 512; j++) {     // cycle all 256 colors in the wheel twice
    for (int q=0; q < 3; q++) {
      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (int i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}


void CylonBounce(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay){

  for(int i = 0; i < PIXEL_COUNT-EyeSize-2; i++) {
    setAll(0,0,0);
    setPixel(i, red/10, green/10, blue/10);
    for(int j = 1; j <= EyeSize; j++) {
      setPixel(i+j, red, green, blue); 
    }
    setPixel(i+EyeSize+1, red/10, green/10, blue/10);
    showStrip();
    delay(SpeedDelay);
  }

  delay(ReturnDelay);

  for(int i = PIXEL_COUNT-EyeSize-2; i > 0; i--) {
    setAll(0,0,0);
    setPixel(i, red/10, green/10, blue/10);
    for(int j = 1; j <= EyeSize; j++) {
      setPixel(i+j, red, green, blue); 
    }
    setPixel(i+EyeSize+1, red/10, green/10, blue/10);
    showStrip();
    delay(SpeedDelay);
  }
  
  delay(ReturnDelay);
}

//extra stuff
void showStrip() {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.show();
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   FastLED.show();
 #endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H 
   // FastLED
   leds[Pixel].r = red;
   leds[Pixel].g = green;
   leds[Pixel].b = blue;
 #endif
}
void setAll(byte red, byte green, byte blue) {
  for(int i = 0; i < PIXEL_COUNT; i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

You want to change the loop() where the startShow() function is called, if you want to ensure that full sequence are played on a button press. Something like this (untested) :

void loop() {
  // Get current button state.
  bool newState = digitalRead(BUTTON_PIN);

  // Check if state changed from high to low (button press).
  if (newState == LOW && oldState == HIGH) {
    // Short delay to debounce button.
    delay(5);
    // Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW) {
     // showType++;
     // if (showType > 6)
     //   showType=0;
     // startShow(showType);
     for ( showType = 0 ; showType <= 6 ; showType++ ) { 
        startShow(showType);
     }    
    }
  }

  // Set the last button state to the old state.
  oldState = newState;

}

I was wondering if there was a way to do add a loop to my case statement so that when the button hasn't been press the code continues to loop until the button has been pressed again.

Why put a loop inside your case statement when the loop() function is there to do what you want ? In fact the current code seems to do what you describe. The showType will only change when the button becomes pressed. Is that not what you want ?

UKHeliBob:
Why put a loop inside your case statement when the loop() function is there to do what you want ? In fact the current code seems to do what you describe. The showType will only change when the button becomes pressed. Is that not what you want ?

Agreed, however the thing I'm trying to change is, for example when the rainbowcyle comes to an end I want it to wait for a second and if no command is given replay cycle. Essentially until the button is press, I want that particular case to continuously run until told otherwise.

6v6gt:
You want to change the loop() where the startShow() function is called, if you want to ensure that full sequence are played on a button press. Something like this (untested) :

void loop() {

// Get current button state.
  bool newState = digitalRead(BUTTON_PIN);

// Check if state changed from high to low (button press).
  if (newState == LOW && oldState == HIGH) {
    // Short delay to debounce button.
    delay(5);
    // Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW) {
    // showType++;
    // if (showType > 6)
    //  showType=0;
    // startShow(showType);
    for ( showType = 0 ; showType <= 6 ; showType++ ) {
        startShow(showType);
    }   
    }
  }

// Set the last button state to the old state.
  oldState = newState;

}

Close, however I think I tried this method before in a similar fashion. I just tried testing it and the code completely ignores the button press. Should I incorporate a while statement to pause the function on a particular case an not to move forward until the button is press while keeping that case running in a loop?

Essentially until the button is press, I want that particular case to continuously run until told otherwise.

I think you just have to move startShow(showType) out of the test for the button and just put it in loop.

void loop() {
  // Get current button state.
  bool newState = digitalRead(BUTTON_PIN);

  // Check if state changed from high to low (button press).
  if (newState == LOW && oldState == HIGH) {
    // Short delay to debounce button.
    delay(5);
    // Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW) {
      showType++;
      if (showType > 6)
        showType=0;
     // startShow(showType);
    }
  }

  // Set the last button state to the old state.
  oldState = newState;
  
  startShow(showType);
}

cattledog:
I think you just have to move startShow(showType) out of the test for the button and just put it in loop.

void loop() {

// Get current button state.
  bool newState = digitalRead(BUTTON_PIN);

// Check if state changed from high to low (button press).
  if (newState == LOW && oldState == HIGH) {
    // Short delay to debounce button.
    delay(5);
    // Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW) {
      showType++;
      if (showType > 6)
        showType=0;
    // startShow(showType);
    }
  }

// Set the last button state to the old state.
  oldState = newState;
 
  startShow(showType);
}

That did the trick, but came with another problem. The click of the button doesn't switch to the next case unless timed perfectly, which is an issues because I can't completely tell all the time when the program is done running.

Should I attempt to redo the code where that even when the program is running and the button is held down for a certain amount of time end program and go to the next program? (Where would I find an example to learn how to write a code like that?)

That did the trick, but came with another problem. The click of the button doesn't switch to the next case unless timed perfectly, which is an issues because I can't completely tell all the time when the program is done running.

Should I attempt to redo the code where that even when the program is running and the button is held down for a certain amount of time end program and go to the next program? (Where would I find an example to learn how to write a code like that?)

I would expect that your display routines are blocking and how to re-write them to be non blocking is not clear to me. I'm not familiar with the neo pixel displays.

You can certainly allow for a gap between displays,without using delay(), where the button can be read, but if you don't know when the display is done running and waiting for the next input, then you are still not going to be able to press the button when it can be read. How long do the display routines last?

This may be a case for using an interrupt with a button press. The interrupt servic routine will increment showType and the new value will be picked up next time in loop. The issues will be debouncing, and if the execution of the interrupt service routine disrupts the display.

What Arduino are you using? Can you move the button pin to digital 2 or 3?

cattledog:
I would expect that your display routines are blocking and how to re-write them to be non blocking is not clear to me. I'm not familiar with the neo pixel displays.

You can certainly allow for a gap between displays,without using delay(), where the button can be read, but if you don't know when the display is done running and waiting for the next input, then you are still not going to be able to press the button when it can be read. How long do the display routines last?

This may be a case for using an interrupt with a button press. The interrupt servic routine will increment showType and the new value will be picked up next time in loop. The issues will be debouncing, and if the execution of the interrupt service routine disrupts the display.

What Arduino are you using? Can you move the button pin to digital 2 or 3?

It depends on the routine, some cycles for 10 rounds about 10 to 15 seconds and others take a bit longer. I'm using a GEMMA and as for the interrupt servic not particularly familiar with that function, is there an example or explanation on how to use that code on the site or do you have an example?. Currently I have the button on pin 1 the lights on pin 2 and nothing on pin 0.

Here is some code with a pin change interrupt on pin1. It uses Nico Hood's pin change interrupt library which you will have to download and add. The library is available with the library manager in the ide or you can get it from github.

It should work for the ATTiny85(GEMMA).

There is a 15 ms debounce on the button press. It compiles for the GEMMA, and appears to fit the memory, so give it a try.

#include <PinChangeInterrupt.h>
#include <PinChangeInterruptBoards.h>
#include <PinChangeInterruptPins.h>
#include <PinChangeInterruptSettings.h>

#include <Adafruit_NeoPixel.h>

#define BUTTON_PIN   1    // Digital IO pin connected to the button.  This will be
// driven with a pull-up resistor so the switch should
// pull the pin to ground momentarily.  On a high -> low
// transition the button press logic will execute.

#define PIXEL_PIN    2    // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 58

// Parameter 1 = number of pixels in strip,  neopixel stick has 8
// Parameter 2 = pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_RGB     Pixels are wired for RGB bitstream
//   NEO_GRB     Pixels are wired for GRB bitstream, correct for neopixel stick
//   NEO_KHZ400  400 KHz bitstream (e.g. FLORA pixels)
//   NEO_KHZ800  800 KHz bitstream (e.g. High Density LED strip), correct for neopixel stick
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_GRB + NEO_KHZ800);

volatile byte showType = 0;//volatile for change in interrupt

//Interrupt Service Routine for button press
void increment_showType()
{
  static unsigned long lastInterruptTime = 0;
  unsigned long interruptTime = millis();
  if (interruptTime - lastInterruptTime > 20)
  {
    showType++;
    if (showType > 6)
      showType = 0;
  }
  lastInterruptTime = interruptTime;
}

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  attachPCINT(1, increment_showType, FALLING);
}

void loop() {
  startShow(showType);
}

void startShow(int i) {
  switch (i) {
    case 0: colorWipe(strip.Color(0, 0, 0), 50);    // Black/off
      break;
    case 1: CylonBounce(0xff, 0xff, 0xff, 4, 10, 50);  // White
      break;
    case 2: colorWipe(strip.Color(0, 255, 255), 50);  // Cyan
      break;
    case 3: theaterChaseRainbow(50);
      break;
    case 4: colorWipe(strip.Color(255, 0, 0), 50); // Red
      break;
    case 5: theaterChase(strip.Color(  0,   0, 127), 50); // Blue
      break;
    case 6: rainbowCycle(20);
      break;
  }
}

//Color Wipe Mode
void colorWipe(uint32_t c, uint8_t wait) {
  for (uint16_t i = 0; i < strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

// Rainbow Cycle Mode
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for (j = 0; j < 256 * 10; j++) { // 10 cycles of all colors on wheel
    for (i = 0; i < strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j = 0; j < 10; j++) { //do 10 cycles of chasing
    for (int q = 0; q < 3; q++) {
      for (int i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, c);  //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (int i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, 0);      //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j = 0; j < 512; j++) {   // cycle all 256 colors in the wheel twice
    for (int q = 0; q < 3; q++) {
      for (int i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, Wheel( (i + j) % 255)); //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (int i = 0; i < strip.numPixels(); i = i + 3) {
        strip.setPixelColor(i + q, 0);      //turn every third pixel off
      }
    }
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if (WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}


void CylonBounce(byte red, byte green, byte blue, int EyeSize, int SpeedDelay, int ReturnDelay) {

  for (int i = 0; i < PIXEL_COUNT - EyeSize - 2; i++) {
    setAll(0, 0, 0);
    setPixel(i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(i + j, red, green, blue);
    }
    setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
    showStrip();
    delay(SpeedDelay);
  }

  delay(ReturnDelay);

  for (int i = PIXEL_COUNT - EyeSize - 2; i > 0; i--) {
    setAll(0, 0, 0);
    setPixel(i, red / 10, green / 10, blue / 10);
    for (int j = 1; j <= EyeSize; j++) {
      setPixel(i + j, red, green, blue);
    }
    setPixel(i + EyeSize + 1, red / 10, green / 10, blue / 10);
    showStrip();
    delay(SpeedDelay);
  }

  delay(ReturnDelay);
}

//extra stuff
void showStrip() {
#ifdef ADAFRUIT_NEOPIXEL_H
  // NeoPixel
  strip.show();
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
  // FastLED
  FastLED.show();
#endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
#ifdef ADAFRUIT_NEOPIXEL_H
  // NeoPixel
  strip.setPixelColor(Pixel, strip.Color(red, green, blue));
#endif
#ifndef ADAFRUIT_NEOPIXEL_H
  // FastLED
  leds[Pixel].r = red;
  leds[Pixel].g = green;
  leds[Pixel].b = blue;
#endif
}
void setAll(byte red, byte green, byte blue) {
  for (int i = 0; i < PIXEL_COUNT; i++ ) {
    setPixel(i, red, green, blue);
  }
  showStrip();
}

@cattledog That seemed to do the trick, the only few problems that I'm noticing is that it skips some sequences and gets stuck on case one after going through all the cases once. I'm starting to think if it is a battery/charging issue why the sequences are being skipped and the communication from the button to the lights aren't sometimes being seen. I've ordered a new lithium battery charge from adafruit to output more power, hopefully that does the trick as well as helps with the code problem.

That seemed to do the trick, the only few problems that I'm noticing is that it skips some sequences and gets stuck on case one after going through all the cases once.

Skipping some sequences may be due to multiple increments caused by contact bounce in the button switch. You can try increase the debounce delay beyond 20 ms.
if (interruptTime - lastInterruptTime > 20)

I don't understand the getting stuck on case 1. Are you saying that you go through 0-1-2-3-4-5-6-0-1 and then it does not increment any more?

Why do you think that there is a battery issue which explains the skipped sequences and the non responsiveness of the increment to the button?

When you were using the button without the interrupt, but had to time the press to when a sequence had finished, did you see these same problems?

In your original code where the sequence only ran once, did you see these same problems?