Button doesn't control 2812 strip correctly

Hi everyone-
First time User & poster here-
Been in the ITS (Intelligent Traffic Systems) industry for 20+ yrs. I can design, install, program & troubleshoot a traffic signal from start to finish and make it dance. BUT
I'm new to arduino and am sucking at getting this code to implement.

Wanting to be able to press a button and have the 1st code blink the LED strip (Leds 0-9) and continue blinking until I hit the button again.
Once I press the button again, then I want it to run the 2nd code (scan) the LED strip (Leds 10-19). If I press the button again, it will shut off and wait until another press and start over again this sequence.

Currently, if I hold the button down, it runs both sequences. If I unpress the button, the led strip is black. Any guidance and help is appreciated! see code below:

#include <WS2812FX.h>

#define LED_PIN    6  // digital pin used to drive the LED strip
#define LED_COUNT 240  // number of LEDs on the strip
#define Button 2

WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

       int Program = 0;
      int maxProgram = 2;
      int Num = 0;
      int Run = 0;
      int ButtonDelay = 500;
      int Program0Delay = 100;
      int Program1Delay = 100;
      int Program2Delay = 10;

      unsigned long ButtonTime = 0;
      unsigned long DelayTime = 0;
      
void setup() {
      pinMode(Button, INPUT_PULLUP);
  
  Serial.begin(115200);

  ws2812fx.init();
  ws2812fx.setBrightness(128);

  // parameters: index, start, stop, mode, color, speed, reverse
  //ws2812fx.setSegment(0,  0,  9, FX_MODE_BLINK, 0xFF0000, 1000, false); // segment 0 is leds 0 - 9
  //ws2812fx.setSegment(1, 10, 19, FX_MODE_SCAN,  0x00FF00, 1000, false); // segment 1 is leds 10 - 19
  //ws2812fx.setSegment(2, 20, 29, FX_MODE_COMET, 0x0000FF, 1000, true);  // segment 2 is leds 20 - 29

  ws2812fx.start();
}

void loop() {
  
  if(digitalRead(Button) == LOW && millis() - ButtonTime > ButtonDelay)
{
  Num = 0;
  Run = 0;
  if(Program < maxProgram)
  {
    Program++;
    for(int Num = 0; Num < 9; Num++)
    {
       ws2812fx.setSegment(0,  0,  9, FX_MODE_BLINK, 0xFF0000, 1000, false); // segment 0 is leds 0 - 9
       
    }
    
    ws2812fx.service();
  }
  else
  {
    Program = 0;
    for(int Num = 10; Num < 19; Num++)
    {
      ws2812fx.setSegment(1, 10, 19, FX_MODE_SCAN,  0x00FF00, 1000, false); // segment 1 is leds 10 - 19
    }
   ws2812fx.service();
  }
ButtonTime = millis();

}
  
}

Record the state in a different variable.
Then use an if for each condition.

Hello
The button read action may need an interlock.

Thanks for the reply

Am i to record the button state in a diferent var?

Could you point out in my code where I should do this at? Also, this code could be poorly written....LOL

Thanks Paul
Could you explain what you mean by interlock?

Look at the following conditional:

if (digitalRead(Button) == LOW && millis() - ButtonTime > ButtonDelay)

It basically says "If the button IS pressed AND 500ms has passed then..."

This is why you have to hold the button down.

The button handler take action once and no further action if the button is held pressend or be released.

The following tutorials should help:

StateChangeDetection

State Machine

Hey Todd,

Wow-That makes perfect sense. The button HAS to be LOW to proceed, correct?

Yes but what you really want to know is that the button BECOMES pressed not that it IS pressed. Look at the State Change Detection tutorial. You simply compare the current state of the button to the last state of the button.

awesome, ill look at that. Thanks Todd!

FYI. Here is my standard way of processing a button at the beginning of loop(). I use this pattern frequently:

void loop() 
{
  static int lastButtonState = digitalRead(Button);
  int currButtonState = digitalRead(Button);

  if (currButtonState != lastButtonState)
  {
    delay(50); // debounce
    lastButtonState = currButtonState;
    if (currButtonState == LOW)
    {
      // do what you need to do
    }
  }
  
  // ........rest of loop processing
}

Hey Todd,

I'm a newbie. So I've been piecing code as i learn. I'm sure my current code isn't the best. Yours looks clean. Thanks for the example

Hey Todd

I fixed it with your code! Kinda-
Now when I press the button, it does light up. But each code still lights up in a 'freeze' state. When i press the button again, both codes go to the next state and 'freeze'.
I'm thinking I'm needing a loop to keep the first code running continuously until i press the button again. Which will then go to the next code. Does this sound right? Not sure my coding skills are up to par...

#include <WS2812FX.h>

#define LED_PIN    6  // digital pin used to drive the LED strip
#define LED_COUNT 240  // number of LEDs on the strip
#define Button 2

WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

       int Program = 0;
      int maxProgram = 2;
      int Num = 0;
      int Run = 0;
      int ButtonDelay = 500;
      int Program0Delay = 100;
      int Program1Delay = 100;
      int Program2Delay = 10;

      unsigned long ButtonTime = 0;
      unsigned long DelayTime = 0;
      
void setup() {
      pinMode(Button, INPUT_PULLUP);
  
  Serial.begin(115200);

  ws2812fx.init();
  ws2812fx.setBrightness(128);

  // parameters: index, start, stop, mode, color, speed, reverse
  //ws2812fx.setSegment(0,  0,  9, FX_MODE_BLINK, 0xFF0000, 1000, false); // segment 0 is leds 0 - 9
  //ws2812fx.setSegment(1, 10, 19, FX_MODE_SCAN,  0x00FF00, 1000, false); // segment 1 is leds 10 - 19
  //ws2812fx.setSegment(2, 20, 29, FX_MODE_COMET, 0x0000FF, 1000, true);  // segment 2 is leds 20 - 29

  ws2812fx.start();
}

void loop() {
  
 static int lastButtonState = digitalRead(Button);
  int currButtonState = digitalRead(Button);

  if (currButtonState != lastButtonState)
  {
    delay(50); // debounce
    lastButtonState = currButtonState;
    if (currButtonState == LOW)
    {
  Num = 0;
  Run = 0;
  if(Program < maxProgram)
  {
    Program++;
    for(int Num = 0; Num < 9; Num++)
    {
       ws2812fx.setSegment(0,  0,  9, FX_MODE_BLINK, 0xFF0000, 1000, false); // segment 0 is leds 0 - 9
       
    }
    
    ws2812fx.service();
  }
  else
  {
    Program = 0;
    for(int Num = 10; Num < 19; Num++)
    {
      ws2812fx.setSegment(1, 10, 19, FX_MODE_SCAN,  0x00FF00, 1000, false); // segment 1 is leds 10 - 19
    }
   ws2812fx.service();
  }
ButtonTime = millis();

}
} 
}

I'm pressed for time and I don't have your hardware and only know what I read about the library. Try the following code but I have no hardware to confirm:

#include <WS2812FX.h>

#define LED_PIN    6  // digital pin used to drive the LED strip
#define LED_COUNT 240  // number of LEDs on the strip

const int buttonPin = 2;

WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);

  Serial.begin(115200);

  ws2812fx.init();
  ws2812fx.setBrightness(128);
  ws2812fx.start();
}

void loop() {
  static bool blinkFlag = true;
  
  static int lastButtonState = digitalRead(buttonPin);
  int currButtonState = digitalRead(buttonPin);

  if (currButtonState != lastButtonState)
  {
    delay(50); // debounce
    lastButtonState = currButtonState;
    if (currButtonState == LOW)
    {
      if (blinkFlag)
      {
        blinkFlag = false;
        ws2812fx.setSegment(0,  0,  9, FX_MODE_BLINK, 0xFF0000, 1000, false); // segment 0 is leds 0 - 9
      }
      else
      {
        blinkFlag = true;
        ws2812fx.setSegment(1, 10, 19, FX_MODE_SCAN,  0x00FF00, 1000, false); // segment 1 is leds 10 - 19
      }
    }
  }
  ws2812fx.service();
}

I will comment more in a while...

After looking at the ws2812fx library more I think the following will work:

#include <WS2812FX.h>

#define LED_PIN    6  // digital pin used to drive the LED strip
#define LED_COUNT 240  // number of LEDs on the strip

const int buttonPin = 2;

WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);

  Serial.begin(115200);

  ws2812fx.init();
  ws2812fx.setBrightness(128);
  ws2812fx.setIdleSegment(0,  0,  9, FX_MODE_BLINK, 0xFF0000, 1000, false); // segment 0 is leds 0 - 9
  ws2812fx.setIdleSegment(1, 10, 19, FX_MODE_SCAN,  0x00FF00, 1000, false); // segment 1 is leds 10 - 19
}

void loop() {
  static bool blinkFlag = true;
  
  static int lastButtonState = digitalRead(buttonPin);
  int currButtonState = digitalRead(buttonPin);

  if (currButtonState != lastButtonState)
  {
    delay(50); // debounce
    lastButtonState = currButtonState;
    if (currButtonState == LOW)
    {
      if (blinkFlag)
      {
        blinkFlag = false;
        ws2812fx.stop();
        ws2812fx.removeActiveSegment(1);
        ws2812fx.addActiveSegment(0);
        ws2812fx.start();
      }
      else
      {
        blinkFlag = true;
        ws2812fx.stop();
        ws2812fx.removeActiveSegment(0);
        ws2812fx.addActiveSegment(1);
        ws2812fx.start();
      }
    }
  }
  ws2812fx.service();
}

Todd,

You are a dang genius. IT IS WORKING!
THANK YOU SO MUCH

What's your background? Are you a programmer by trade?

Not to dampen the well deserved praise, and T might actually be a genius. But really, the kind of thing we're looking at here is done all the time by ordinary people that just studied and learned it. I first learned to program by sitting in front of a terminal with a big thick language manual by my side. Nothing else. Nobody else's code to even look at.

Me too! I'm an old goober. The first embedded system I worked on was a 3Mhz 8085 with 64K of memory. We controlled a generator in a nuclear power plant with it. :astonished:

3 MHz, wow! Speed demon!

1 Like