Unsuccessful neopixel animation

I am trying to write a function that fades through the colors on the rgb spectrum, while actively waiting for a button press to exit the function. For some reason the pixels get the initial color but doesn't fade. However it still gets a button press.

Here is the function. I can verify that Wheel() and checkButton() work, but if you want I could post the rest of my code.

//fade through colors
int changeColors(uint8_t c) {     //the changeColors function is called when there is a long button press
  int b=0;                        //initialize the button state value to zero
  while (b == 0 || b == 4){       //this loop runs as long as the button state is 0 or 4
    strip.fill(Wheel(c));         //fill the strip with 1 color. Wheel() takes an 8 bit value and returns a 32 bit rgb color
    strip.show();                 //push the command to the strip
    for(uint8_t i=0; i<50; i++) { //loop 50 times = 50ms
      delay(1);                   //delay 1ms at a time = 50ms
      b=checkButton();            //check for button state and load it in b
      if (b != 0 && b != 4) break;//breaks out of loop if the button state is not 0 and not 4
    }
    c++;                          //increment the color value each loop
  }                               //if we break out of the for loop it also shouldnt repeat the while loop
  EEPROM.write(COLOR_ADDR, c);    //save the color value for next time
  return b;                       //return the button state to activate the next mode
}

I can also verify that it's not a SRAM issue by changing the code to light up only 1 pixel.
I'm wondering if anyone can spot anything simple/stupid that I am overlooking.
Thanks for any help I can get.

why wouldn't something like this work?

int changeColors(uint8_t c) {
  int buttonState = 0;
  while ((buttonState == 0) || (buttonState == 4)) {
    strip.fill(Wheel(c));
    strip.show();
    c++;
    buttonState = checkButton();
  }
  EEPROM.write(COLOR_ADDR, c - 1);
  return buttonState;
}

an enum for the button states would make it easier to read (instead of 0 and 4 magic numbers)

I am more fan of using boolean functions (bool changeColors), but int should work well too.
Anyway, the program should work fine with J-M-L 's approach.
I dont know either why 0 and 4 ethereal numbers to check, but it would be great if you could post the rest of the code.

Cheers!

here OP can return multiple values from the button state apparently (maybe there are actually multiple buttons), so bool won't cut it.

I'm more in favour of using a formal type

enum t_buttonState : byte {state0, state1, state2, state3, state4, state5}; // use meaningful names of course

t_buttonState checkButton() {
  ...
}

t_buttonState changeColors(uint8_t c) {
  t_buttonState buttonState = checkButton();
  while ((buttonState == state0) || (buttonState == state4)) {
    strip.fill(Wheel(c));
    strip.show();
    c++;
    buttonState = checkButton();
  }
  EEPROM.write(COLOR_ADDR, c - 1);
  return buttonState;
}

I need a 50 ms delay between every time I increment the color value.

Here is the rest of the code. I didn't comment it all, but I hope you can get what I'm trying to do.

/* The lights are connected to the
 * Trinket's pin 1 through a resistor.  A pushbutton is connected from
 * pin 0 to ground.  You can use the pushbutton to cycle through the modes:
 *
 * Uses the Trinket's EEPROM to store the mode, so it is retained
 * when the power is off.
 */

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif
#include <EEPROM.h>

#define SWITCH 0
#define PIN 1
#define COLOR_ADDR 1
#define MODE_ADDR 0
#define N_MODES 4

// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(5, PIN, NEO_RGB + NEO_KHZ800);

// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel.  Avoid connecting
// on a live circuit...if you must, connect GND first.

boolean copMode;

void setup() {
  // This is for Trinket 5V 16MHz
  #if defined (__AVR_ATtiny85__)
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
  #endif
  // End of trinket special code


  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

  // initialize the SWITCH pin as an input.
  pinMode(SWITCH, INPUT);
  // ...with a pullup
  digitalWrite(SWITCH, HIGH);
  copMode = digitalRead(SWITCH);
}

// Button timing variables
int debounce = 20;                //| ms debounce period to prevent flickering when pressing or releasing the button
int DCgap = 250;                  //| max ms between clicks for a double click event
int holdTime = 1000;              //| ms hold period: how long to wait for press+hold event
// Button variables
boolean buttonVal = HIGH;         //| value read from button
boolean buttonLast = HIGH;        //| buffered value of the button's previous state
boolean DCwaiting = false;        //| whether we're waiting for a double click (down)
boolean DConUp = false;           //| whether to register a double click on next release, or whether to wait and click
boolean singleOK = true;          //| whether it's OK to do a single click
long downTime = -1;               //| time the button was pressed down
long upTime = -1;                 //| time the button was released
boolean ignoreUp = false;         //| whether to ignore the button release because the click+hold was triggered
boolean waitForUp = false;        //| when held, whether to wait for the up event
boolean holdEventPast = false;    //| whether or not the hold event happened already

int checkButton() {    
   int event = 0;
   buttonVal = digitalRead(SWITCH);
   // Button pressed down
   if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce)
   {
       downTime = millis();
       ignoreUp = false;
       waitForUp = false;
       singleOK = true;
       holdEventPast = false;
       if ((millis()-upTime) < DCgap && DConUp == false && DCwaiting == true)  DConUp = true;
       else  DConUp = false;
       DCwaiting = false;
   }
   // Button released
   else if (buttonVal == HIGH && buttonLast == LOW && (millis() - downTime) > debounce)
   {        
       if (not ignoreUp)
       {
           upTime = millis();
           if (DConUp == false) DCwaiting = true;
           else
           {
               event = 2;
               DConUp = false;
               DCwaiting = false;
               singleOK = false;
           }
       }
   }
   // Test for normal click event: DCgap expired
   if ( buttonVal == HIGH && (millis()-upTime) >= DCgap && DCwaiting == true && DConUp == false && singleOK == true && event != 2)
   {
       event = 1;
       DCwaiting = false;
   }
   // Test for hold
   if (buttonVal == LOW && (millis() - downTime) >= holdTime) {
       // Trigger "normal" hold
       if (not holdEventPast)
       {
           event = 4;
           waitForUp = true;
           ignoreUp = true;
           DConUp = false;
           DCwaiting = false;
           holdEventPast = true;
       }
   }
   buttonLast = buttonVal;
   return event;
}


// Fill the dots with a color
int singleColor(uint8_t c) {
  int b=0;
  strip.fill(Wheel(c));
  strip.show();
  while (b == 0 || b == 1){
    b=checkButton();
  }
  return b;
}


//wipe a color across the dots
int wipe(void) {
  int c=0;
  int b=0;
  while (b == 0 || b == 2){
    for(uint16_t i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(c));
      strip.show();
      for(uint8_t j=0; j<100; j++) {//wait 100 ms but keep checking button
        delay(1);
        b=checkButton();
        if (b != 0 && b!= 2) break;
      }
      if (b != 0 && b!= 2) break;
    }
  c = (c+120) % 255;//choose next color☺
  }
  return b;
}


//blink like a police car
int copBlink() {
  int b=0;
  uint16_t n;
  uint32_t c;
  while (b == 0 || b == 3) {
    for(uint16_t i=0; i<strip.numPixels(); i++) {
      if ((i+n) % 4 == 0){
        c = strip.Color(0, 255, 0);
      }
      else{
        c = strip.Color(255, 0, 0);
      }
      strip.setPixelColor(i, c);
    }
    strip.show();
    n+=5;
    for(uint8_t j=0; j<150; j++) {//wait 150 ms but keep checking button
      delay(1);
      b=checkButton();
      if (b != 0 && b != 3) break;
    }
    strip.clear();
    strip.show();
    for(uint8_t k=0; k<75; k++) {//wait 75 ms but keep checking button
      delay(1);
      b=checkButton();
      if (b != 0 && b != 3) break;
    }
  }
  return b;
}


//fade through colors
int changeColors(uint8_t c) {     //the changeColors function is called when there is a long button press
  int b=0;                        //initialize the button state value to zero
  while (b == 0 || b == 4){       //this loop runs as long as the button state is 0 or 4
    strip.fill(Wheel(c));         //fill the strip with 1 color. Wheel() takes an 8 bit value and returns a 32 bit rgb color
    strip.show();                 //push the command to the strip
    for(uint8_t i=0; i<50; i++) { //loop 50 times = 50ms
      delay(1);                   //delay 1ms at a time = 50ms
      b=checkButton();            //check for button state and load it in b
      if (b != 0 && b != 4) break;//breaks out of loop if the button state is not 0 and not 4
    }
    c++;                          //increment the color value each loop
  }                               //if we break out of the for loop it also shouldnt repeat the while loop
  EEPROM.write(COLOR_ADDR, c);    //save the color value for next time
  return b;                       //return the button state to activate the next mode
}


// 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 loop() {
  uint8_t mode = EEPROM.read(MODE_ADDR) % N_MODES;
  uint8_t color = EEPROM.read(COLOR_ADDR);
  if (mode == 0){//for first start
    mode=1;
  }
  if (mode > N_MODES){//keep from hanging above 
    mode=1;
  }
  if (copMode == false){
    mode = 3;
    copMode = true;
  }
  switch (mode) {
    case 1:
      mode = singleColor(color);
      break;
    case 2:
      mode = wipe();
      break;
    case 3:
      mode = copBlink();
      break;
    case 4:
      mode = changeColors(color);
      break;
  }
  EEPROM.write(MODE_ADDR, mode);
}

I would rewrite the animation functions so that they only do "one step" of whatever animation is on-going and manage them as small independent state machine and handle the button in the loop

(you have a lot of EEPROM.write(), you should probably use update() instead of write() )

using the OneButton library would probably make your button handling easier to Read / implement

What is your Arduino ? why do you possibly include <avr/power.h>?

Is there a difference between the two?

I'll definitely check it out.

I'm using an Adafruit trinket. I got that from an example code and didn't experiment if I needed it or not.

Yes, as the spec states

An EEPROM write takes 3.3 ms to complete. The EEPROM memory has a specified life of 100,000 write/erase cycles, so you may need to be careful about how often you write to it

what update() does is to read the memory first and if this is a new value, then write it.


I’m using an Adafruit trinket

probably some clean up needed but the most important work is probably to make the display functions asynchronous and non blocking

Thanks for the help and tips guys. With the help of the OneButton library I finally got it to work. Some of your other tips helped me get it down to less than half the lines of code.

congrats !!

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