Neopixel Mode change with a button

Hey guys, I've been struggling with this for a few days now and I've reached a wall..
Basically I have some neopixels and I want to set them in different modes controlled by a button, using the internal pullup, the button will ground an input when pressed and then will increment the mode by 1, until it reaches 6 where it wil loop back to mode 1 again and so on.. but I'm struggling, I've finally got the arduino to see the button and react but it will not loop back to 1 and just gets stuck at mode 6 and seems to require a 2 second push to make it change.. the more I play with it, the more it laughs at me.. any input would be greatly appreciated, and heres my code :slight_smile: :

#include <Adafruit_NeoPixel.h>

#define PIN 6

Adafruit_NeoPixel strip = Adafruit_NeoPixel(25, PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  pinMode(4, INPUT);
  digitalWrite(4, HIGH); // connect internal pull-up
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
}

int counter = 1;


void loop() {
  if(digitalRead(4) == LOW && counter < 6)
  {
    counter++;
    if(counter == 6){
      counter = 1;
    }

  }

  // Some example procedures showing how to display to the pixels:
  colorWipe(strip.Color(255, 0, 0), 50); // Red
  colorWipe(strip.Color(0, 255, 0), 50); // Green
  colorWipe(strip.Color(0, 0, 255), 50); // Blue
  // Send a theater pixel chase in...
  theaterChase(strip.Color(127, 127, 127), 50); // White
  theaterChase(strip.Color(200,   0,   0), 50); // Red
  theaterChase(strip.Color(200,   0,   200), 50);
  theaterChase(strip.Color(  0,   0, 200), 50); // Blue

  rainbow(20);
  rainbowCycle(20);
  theaterChaseRainbow(30);

}


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

void rainbow(uint8_t wait) {
  if (counter == 2) {
    uint16_t i, j;

    for(j=0; j<256; j++) {
      for(i=0; i<strip.numPixels(); i++) {
        strip.setPixelColor(i, Wheel((i+j) & 255));
      }
      strip.show();
      delay(wait);
    }
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  if (counter == 3) {
    uint16_t i, j;

    for(j=0; j<256*5; j++) { // 5 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) {
  if (counter == 4) {
    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) {
  if (counter == 5) {
    for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
      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);
        delay(wait);
        delay(wait);
        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) {
  if(WheelPos < 85) {
    return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } 
  else if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } 
  else {
    WheelPos -= 170;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}
#define PIN 6

What's this for?

controlled by a button, using the internal pullup

It's been my experience that buttons are great at holding shirts closed, but don't work for shit as input devices. Switches don't work worth a damn at holding shirts closed, but they do make great input devices.

  if(digitalRead(4) == LOW && counter < 6)

Will counter ever NOT be less than 6? If not, why bother testing?

Where do you actually use counter to define what the strip should do?

define pin 6

is for the neopixel strip, it define's pin 6 as the data output pin.. (so the instructions say)

ok ok ok.. it's an spst switch.. my bad..

and each loop has a statement such as:

void rainbow(uint8_t wait) {
if (counter == 2) {
uint16_t i, j;

which selects that loop when the counter reaches a certain number..

sorry I'm quite new to C programming so I might well be making horrendous mistakes!

and each loop has a statement such as:

void rainbow(uint8_t wait) {
if (counter == 2) {
uint16_t i, j;

Seems backwards to me.

if(counter == 2)
   rainbow(20);

seems more straightforward.

I suspect that you want to increment counter when the switch BECOMES pressed, though, rather than when the switch IS pressed. Look at the state change detection example.

The problem is that your display functions take a long time to run (92 seconds for theaterChaseRainbow), during which the momentary pushbutton is ignored. If you want a responsive UI you will need to redesign your program.

I have changed the program slightly. (I discovered that I was getting the program to check for the button press in the function rather than whilst the function is called! ]:slight_smile: )

It now looks like this:

void loop() {
if(digitalRead(4) == LOW )
{
counter++;
if(counter == 7){
counter = 1;
}

}

if (counter == 1){
// Some example procedures showing how to display to the pixels:
colorWipe(strip.Color(255, 0, 0), 50); // Red
colorWipe(strip.Color(0, 255, 0), 50); // Green
colorWipe(strip.Color(0, 0, 255), 50); // Blue
}
if (counter == 2){
// Send a theater pixel chase in...
theaterChase(strip.Color(127, 127, 127), 50); // White
theaterChase(strip.Color(200, 0, 0), 50); // Red
theaterChase(strip.Color(200, 0, 200), 50);
theaterChase(strip.Color( 0, 0, 200), 50); // Blue
}
if (counter == 3){
rainbow(20);
}
if (counter == 4){
rainbowCycle(20);
}
if (counter == 5){
theaterChaseRainbow(1);
}
if (counter ==6) {
Wheel (20);
}
}

however I have the problem that you stated in that the button press is not recognised until after the loop is finished. Is there a command that would mean that the button press forces the loops to exit?

There are a few options to consider:

  • remove the delays and have the display functions do a portion of the work each time they are called
  • use an interrupt on your pushbutton that sets a flag, and check for the flag periodically in each display function
  • read your pushbutton periodically in each display function

The first option is probably best from a design viewpoint, but difficult to code. The second option would be a good compromise between design and coding effort. The last option is least desirable from a design perspective, but likely the easiest to code.

Here you go boss.... the code still has a delay but it'll pick up your button change within 50ms...

int lightCycle = 0;

void loop(){
theaterChaseRainbow(50);
}    


void theaterChaseRainbow(uint8_t wait) {
  //for (int j=0; j < 5; j++) {     // cycle all 256 colors in the wheel
    lightCycle++;
    for (int q=0; q < 3; q++) {
      for (int i=0; i < strip.numPixels(); i=i+3) {
          strip.setPixelColor(i+q, Wheel( (i+lightCycle) % 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
        }
    }
  //}
  
  if(lightCycle == 256){
    lightCycle = 0;}
}