Debounce - Missing Button Presses

This is a pretty simple question regarding the use of Debounce vs Interupts. I have just gotten started with a neopixel strip. I am trying to setup a basic program to switch between various modes of display. I have a software debounced push button that when pressed should cause the main loop to switch to the next display type.

The issue is with missing button presses. I am pretty sure the cause is the use of delay() in the chase function. When I just run the sketch and count button presses it works great.

The primary question I have is, would the use of an interupt ensure I always get the correct count, or should I simply just remove the delay and use millis() i.e. the Blink without Delay examples.

#include <Adafruit_NeoPixel.h>
#include <avr/power.h>

#define PIN 6

// 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)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(30, PIN, NEO_GRB + 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.

const int pushButton = 8;
const int numButtonOptions = 6;
int currentButtonNum=0;
int buttonState = HIGH;
int lastButtonState = LOW;
// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers
int reading = LOW;

void setup() {

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

void loop() {
  
  debounceButton();
  
    switch (currentButtonNum) {
    case 0:
      //do something when var equals 1
      break;
    case 1:
      //do something when var equals 2
      chase(0xFF0000); // Red
      debounceButton();
      break;
    case 2:
      //do something when var equals 2
      chase(0x00FF00); // Green
      debounceButton();
      break;

    default: 
       break;
      // if nothing else matches, do the default
      // default is optional
  }
}


static void chase(uint32_t c) {
  for(uint16_t i=0; i<strip.numPixels()+4; i++) {
      strip.setPixelColor(i  , c); // Draw new pixel
      strip.setPixelColor(i-4, 0); // Erase pixel a few steps back
      strip.show();
      delay(12);
  }
}


void debounceButton()
{
   // read the state of the switch into a local variable:
  int reading = digitalRead(pushButton);

  // check to see if you just pressed the button 
  // (i.e. the input went from LOW to HIGH),  and you've waited 
  // long enough since the last press to ignore any noise:  

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  } 
  
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading; 
      
        // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH) {
     //   myledState = !myledState;
        currentButtonNum++;
        if(currentButtonNum > numButtonOptions)
          currentButtonNum = 0;
        Serial.print("Button has been pressed ");
        Serial.print(currentButtonNum);
        Serial.println(" Times.");
      }
    }
  }
lastButtonState = reading;
}

remove the delay and use millis()

I strongly suspect the problem is the way you are handling the chase() function.

Not only does it have a FOR loop which prevents the button being detected until it is finished, but also you have a delay() in the FOR loop.

Have a look at how the flashing is managed in several things at a time.

Your chase() function should only do one of the items each time (use a variable to keep track of which one) and if it needs an interval that should be managed using millis().

…R

chase is the problem. I have gotten it to switch modes easily when delay in removed from chase. I have yet to figure out how to restructure so that it has the 12ms delay in chase and still respond to the button press. This is why I thought an interupt would be the right technique.

gfbuyer:
I have yet to figure out how to restructure so that it has the 12ms delay in chase and still respond to the button press.

That is why I recommended the demo several things at a time - have you looked at it?
If there is something you don't understand please let me know and I will try to help.

There is no need for interrupts.

...R

Thanks. Yes, I am very familiar with the doing several things at a time example. I have put together a bunch of led projects using the technique. I am just starting with the neopixels and while the same techniques should and will work I was struggling with how to convert chase appropriately to remove the delay function and be non-blocking so my button presses are always acknowledged.

I was struggling with how to convert chase appropriately to remove the delay function and be non-blocking so my button presses are always acknowledged.

In your chase function, I think you’ll also need to remove the for loop (as well as the delay) and let the main loop iterate through the pixels.

Pseudocode:

  static void chase(uint32_t c) {
    uint16_t pixelCount;
    if (pixelCount < (strip.numPixels() + 4)) (
        strip.setPixelColor(i  , c); // Draw new pixel
        strip.setPixelColor(i - 4, 0); // Erase pixel a few steps back
        strip.show();
        pixelCount += 1;
        pixelInterval(12);
    } else {
    pixelCount = 0;
  }
}

Thanks. Getting rid of the for loop in chase is what I was thinking as well. The challenge I seem to have is that the time it takes to "animate" the display in the neopixel strip kind of through me off in my thinking about how to get this done. Should be simple and I'm sure it is but I don't want to end up with spaghetti code !

gfbuyer:
I don't want to end up with spaghetti code !

Then put the code into small functions each with a single job and a meaningful name.

...R