interrupting a for loop with button press

hi, im doing my first Adafruit Gemma project (light up dog collar!) and am hitting a barrier.

I've used the buttoncycler example to give me a head start on utilizing a button to switch modes, and I've adjusted the code in the for loops to give me repeating color patterns. However I'm not sure how to change modes with the button as button presses do not interrupt the for loop, meaning i am stuck on the first color pattern of repeating sequential red lights.

Any help would be greatly appreciated. I'm using the Adafruit Tactile switch

// This is a demonstration on how to use an input device to trigger changes on your neo pixels.
// You should wire a momentary push button to connect from ground to a digital IO pin.  When you
// press the button it will change to a new pixel animation.  Note that you need to press the
// button once to start the first animation!

#include <Adafruit_NeoPixel.h>

#define BUTTON_PIN   2    // 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    1    // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 4

// 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(20);
    // Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW) {
      showType++;
      if (showType > 3)
        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: colorWipe(strip.Color(255, 0, 0), 250);  // Red
            break;
    case 2: colorWipe(strip.Color(0, 255, 0), 250);  // Green
            break;
    case 3: colorWipe(strip.Color(0, 0, 255), 250);  // Blue
            break;
  }
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
  int previousPixel = i - 1;
    strip.setPixelColor(previousPixel, strip.Color(0, 0, 0));
    strip.show();
    delay(wait);
    if (i == 3){
      strip.setPixelColor(3, strip.Color(0, 0, 0));
      i = -1;
    }
  }
}

You have two choices. The first is to scrap all that code and start over. The loop() function loops. Let it handle all the looping. On any given pass through loop(), it may, or may not, be time to do something (such as change pixel number, etc.) or it may not.

You can, then, read the switch on every pass through loop(), resulting in nearly instantaneous recognition of switch presses.

Or, you can continue with that poor structure, and add poll the switch on every pass through every for loop, and break out of the for loop when the switch is pressed.

Your loop variable is an unsigned int. You set it to "-1" which sets it to 65535. No wonder it takes a long time.

It is usually a bad sign if you feel the need to modify the loop variable inside the loop.

Looks like you were trying to solve the problem that the 'previous pixel' is strange at the start of the string. Try something like this, where a single 'if' statement takes care of the single strange case.

  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);  // Turn on pixel 'i'
    int previousPixel = i-1;
    if (i == 0)
        previousPixel = strip.numPixels() - 1;  // Last Pixel
    strip.setPixelColor(previousPixel, strip.Color(0, 0, 0));  // Turn off previous pixel
    strip.show();
    delay(wait);
    }

PaulS:
You have two choices. The first is to scrap all that code and start over. The loop() function loops. Let it handle all the looping. On any given pass through loop(), it may, or may not, be time to do something (such as change pixel number, etc.) or it may not.

You can, then, read the switch on every pass through loop(), resulting in nearly instantaneous recognition of switch presses.

Or, you can continue with that poor structure, and add poll the switch on every pass through every for loop, and break out of the for loop when the switch is pressed.

would it be possible to give me an example of how this might be structured if i used the loop() function? not sure how i would manage the switches between the different modes?

Open up your examples folder and look for BlinkWithoutDelay. I think it is in Digital.

That example is very simple. But can you see how it remembers if the light is currently on or off and when the time is up, it makes a decision to change it?

lewisp:
would it be possible to give me an example of how this might be structured if i used the loop() function? not sure how i would manage the switches between the different modes?

Have a look at how its done in Several Things at a Time and in Planning and Implementing a Program

...R

MorganS:
Open up your examples folder and look for BlinkWithoutDelay. I think it is in Digital.

That example is very simple. But can you see how it remembers if the light is currently on or off and when the time is up, it makes a decision to change it?

So i checked out the example you mentioned and took a little time to fiddle with it. I seem to have got the result I want although it may not have been the most efficient way! it's only for my pup so i'm happy that i managed to work something out!

// This is a demonstration on how to use an input device to trigger changes on your neo pixels.
// You should wire a momentary push button to connect from ground to a digital IO pin.  When you
// press the button it will change to a new pixel animation.  Note that you need to press the
// button once to start the first animation!

#include <Adafruit_NeoPixel.h>

#define BUTTON_PIN   2    // 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    1    // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 4

// 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;
const long interval = 250;           // interval at which to blink (milliseconds)
unsigned long previousMillis = 0;        // will store last time LED was updated
int i = 0;
int redcolorVar = 0;
int bluecolorVar = 0;
int greencolorVar = 0;
int previousPixel = 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(20);
    // Check if button is still low after debounce.
    newState = digitalRead(BUTTON_PIN);
    if (newState == LOW) {
      showType++;
      if (showType > 3)
        showType=0;
    }
  }

  // Set the last button state to the old state.
  oldState = newState;
  if(showType == 1){
    redcolorVar = 255;
    greencolorVar = 0;
    bluecolorVar = 0;
    if(i >3 ){
      i = 0;
    }
  }
  if(showType == 2){
    redcolorVar = 0;
    greencolorVar = 255;
    bluecolorVar = 0;
    if(i >3 ){
      i = 0;
    }
  }
  if(showType == 3){
    redcolorVar = 0;
    greencolorVar = 0;
    bluecolorVar = 255;
    if(i >3 ){
      i = 0;
    }
  }

     // check to see if it's time to blink the LED; that is, if the difference
  // between the current time and last time you blinked the LED is bigger than
  // the interval at which you want to blink the LED.
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;
    previousPixel = i-1;
    if(i == 0){
        strip.setPixelColor(3, strip.Color(0, 0, 0));
        strip.show();
    }
    strip.setPixelColor(i, strip.Color(redcolorVar, greencolorVar, bluecolorVar));
    strip.setPixelColor(previousPixel, strip.Color(0, 0, 0));
    strip.show();
    i++;
    
  }
}

from this code I've been able to implement a chasing light sequence that i can change the color of at any time by pressing (or half pressing in this buttons case) my button. thanks for the pointers