(NeoPixels) - Multiple Iterations of colorWipe in different colors using millis?

Hi everyone!

I’m super new to coding, so bear with me if this is a ridiculously easy issue. I’m using an Adafruit FLORA & a 12 pixel NeoPixel ring. I’m trying to write a code that will light up one pixel every few seconds over ten minutes, and then will flash to alert that 10 minutes has elapsed. The code is written so that it goes through 5 loops around the ring, with each loop a different color, to show the user how close they are to the 10 minute alert.

This is the code we originally had, which is pretty much just jacked from the buttonTest example and modified so it runs a couple colorWipes one after the other:

// 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   9    // 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    6    // Digital IO pin connected to the NeoPixels.

#define PIXEL_COUNT 12

// 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'
  strip.setBrightness(10);
  
}

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 > 4)
        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(0, 0, 255), 1000); //Dark Blue
            colorWipe(strip.Color(66, 206, 244), 1000);  // Light Blue
            colorWipe(strip.Color(249, 249, 17), 1000);  //Yellow
            colorWipe(strip.Color(255, 110, 0), 1000);  // Orange
            colorWipe(strip.Color(255, 0, 0), 1000);  // Red
            theaterChase(strip.Color(127, 127, 127), 50); // White
            break;
  }
}


void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(2 * 5000);
  }
}

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
      }
    }
  }
}

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);
}

The code above works, but I realized that because it uses delay(), it’s not responsive to any input. I’m trying to convert the whole pattern over the millis() so that we can interrupt the pattern partway through.

The code below is what we found to recreate the colorWipe function using millis(). It’s straight from this blog post.

#include <Adafruit_NeoPixel.h>

#define NUM_PIXELS 60
unsigned long interval=50;  // the time we need to wait
unsigned long previousMillis=0;

uint32_t currentColor;// current Color in case we need it
uint16_t currentPixel = 0;// what pixel are we operating on



Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, 6, NEO_GRB + NEO_KHZ800);

void setup() {
  currentColor = strip.Color(255,0,0);
  currentPixel = 0;
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}

// Basic idea. You could reorg and pass pixel index and color as a function - or you could go through a gradient of colors.
void loop(){
if ((unsigned long)(millis() - previousMillis) >= interval) {
  previousMillis = millis();
  colorWipe();
 }
}


void colorWipe(){
  strip.setPixelColor(currentPixel,currentColor);
  strip.show();
  currentPixel++;
  if(currentPixel == NUM_PIXELS){
    currentPixel = 0;
}

I’ve tried a bunch of different ways to modify it so that we can run one colorWipe all the way around and then go around again with a different color, but it pretty much freaks out every time. I’ve gotten it to run around in red and then turn all of the pixels green at once, run the green and red patterns at the same time like they’re chasing each other, and run the red pattern and then just turn one single pixel green but not the rest of them. Cannot for the life of me get it to run the red pattern and just stay red while the green pattern goes.

Does anyone have any ideas of how to get this to work? I’m driving myself crazy trying to fiddle with this thing and I feel like I’m not making much headway with this issue.

The simplest thing to do is change the original code. Replace:

    delay(2 * 5000);

with

unsigned long start = millis();
while(millis() - start <= 2 * 5000)
{
   // read the switch
   // if pressed, set a flag
}

(Of course, you need to put real code in in place of the comments.)

Then, modify the for loop:

  for(uint16_t i=0; i<strip.numPixels() && !buttonPressed; i++) {

Declare buttonPressed as a global boolean, initialized to false.

This way, colorWipe() will end as soon as you press the switch.

You can do something similar in theatreWipe() terminating all the loops when buttonPressed is true.

In loop(), then, only call colorWipe() and theaterWipe() if buttonPressed is false.

After the call to startShow(), reset buttonPressed to false.