kitesurfer1404/WS2812FX

Button feature on auto cycle effect

cplamped opened this issue · 9 comments

Hello just getting started with working with arduino and leds and I came across your effect the auto cycle mode in ws2812fx example I was wondering if there was a way to make it where you could switch between modes with a button feature is there any way to accomplish this sorry again if it’s a dumb question just very new to all this

There are several "button" sketches in the examples section of the Arduino IDE. If you click File -> Examples -> Digital -> Button, you'll see a basic sketch to detect a button press. Merging the code from the button example sketch and the auto_mode_cycle sketch, I came up with this, which should get you started:

#include <WS2812FX.h>

#define LED_COUNT 144
#define LED_PIN     4
#define BTN_PIN     2  // the gpio number of the pushbutton

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

// variables to save the button state
int btnState         = HIGH;
int previousBtnState = HIGH;

void setup() {
  Serial.begin(115200);
  delay (500);
  
  pinMode(BTN_PIN, INPUT_PULLUP); // config BTN_PIN as an input using internal pullup resistor

  ws2812fx.init();
  ws2812fx.setBrightness(32);

  // parameters: index, start, stop, mode, color, speed, reverse
  ws2812fx.setSegment(0, 0, LED_COUNT-1, FX_MODE_STATIC, RED, 1000);

  ws2812fx.start();
}

void loop() {
  ws2812fx.service();

  btnState = digitalRead(BTN_PIN); // read the state of the pushbutton
  
  if(btnState == LOW && previousBtnState == HIGH) {                       // if button pressed...
    ws2812fx.setMode((ws2812fx.getMode() + 1) % ws2812fx.getModeCount()); // change LED effect
    Serial.println(ws2812fx.getModeName(ws2812fx.getMode()));             // print current effect
  }
  previousBtnState = btnState; // save the button state
}

Thank you for fast response it worked perfectly and thank you for showing me where I can learn more

Hi Keith,

LOVE your library. Wanted to expand on this example. I'm trying to combine the button press with your autocycle timer example. Here is m code below. Serial monitor is recognizing the button press but does not cycle thru the patterns (or recognize any time interval). I would greatly appreciate any guidance!
~Jon

`#include <WS2812FX.h>

#define LED_COUNT 100
#define LED_PIN 6
#define BTN_PIN 2 // the gpio number of the pushbutton
#define TIMER_MS 2000

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

// variables to save the button state
int btnState = HIGH;
int previousBtnState = HIGH;
unsigned long last_change = 0;
unsigned long now = 0;

void setup() {
Serial.begin(115200);
delay (500);

pinMode(BTN_PIN, INPUT_PULLUP); // config BTN_PIN as an input using internal pullup resistor

ws2812fx.init();
ws2812fx.setBrightness(32);

// parameters: index, start, stop, mode, color, speed, reverse
ws2812fx.setSegment(0, 0, LED_COUNT - 1, FX_MODE_STATIC, RED, 1000);

ws2812fx.start();
}

void loop() {
ws2812fx.service();

btnState = digitalRead(BTN_PIN); // read the state of the pushbutton

if (btnState == LOW && previousBtnState == HIGH) { // if button pressed...
if (now - last_change > TIMER_MS) {
ws2812fx.setMode((ws2812fx.getMode() + 1) % ws2812fx.getModeCount()); // change LED effect
Serial.println(ws2812fx.getModeName(ws2812fx.getMode()));
last_change = now;
} // print current effect
}
previousBtnState = btnState; // save the button state
Serial.println(btnState);
}`

You forgot to set the "now" variable in the loop() function.

void loop() {
  now = millis();
  ws2812fx.service();

Hi Keith,

Thanks so much for the quick reply. I changed the code to reflect your change. However, its still only changing the pattern when I hit the button. I may have not been clear in my explanation. I'm attempting to have the code setup so that the user hits the button once, go thru each patterns based on the interval of TIMER_MS between each pattern, then cycle back and wait for the button again. Any ideas?

Oh, sorry. I wasn't sure what the timer loop was supposed to do. Now that I understand what you're up to, I think I would have the button code just set a flag that enables the cycle code. Something like this:

#include <WS2812FX.h>

#define LED_COUNT 144
#define LED_PIN 14
#define BTN_PIN 12 // the gpio number of the pushbutton
#define TIMER_MS 2000 // cycle effects every 2 seconds

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

unsigned long last_change = 0;
unsigned long now = 0;
bool isCycling = false; // flag that enables cycling the effects

void setup() {
  Serial.begin(115200);
  delay (500);

  pinMode(BTN_PIN, INPUT_PULLUP); // config BTN_PIN as an input using internal pull-up resistor

  ws2812fx.init();
  ws2812fx.setBrightness(32);

  // parameters: index, start, stop, mode, color, speed, reverse
  ws2812fx.setSegment(0, 0, LED_COUNT - 1, FX_MODE_STATIC, RED, 1000);

  ws2812fx.start();
}

void loop() {
  now = millis();
  ws2812fx.service();

  if (digitalRead(BTN_PIN) == LOW) { // if button pressed, started cycling effects
    isCycling = true;
  }
  
  if (now - last_change > TIMER_MS && isCycling) {
    ws2812fx.setMode((ws2812fx.getMode() + 1) % ws2812fx.getModeCount()); // change LED effect
    Serial.println(ws2812fx.getModeName(ws2812fx.getMode())); // print current effect
    last_change = now;
    if(ws2812fx.getMode() == 0) isCycling = false; // if cycled through all effects, stop cycling
  }
}

Hi Keith
Sorry for my delay. It works! I adapted your sketch to my sketch and am stuck on one last thing. I have to wait whatever '#define TIMER_MS' is equal to before the button press is recognized. So even though the cases are running for the defined time that TIMER_MS is equal to, it also affects the button recognition by the exact same time. Anyway to break out the cases from the button so the sketch sees the button instantly?

`#include <WS2812FX.h>

#define LED_COUNT 182
#define LED_PIN 6
#define BTN_PIN 2 // the gpio number of the pushbutton
#define TIMER_MS 3000 // cycle effects every 3 seconds

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

const byte ALLoff = 0;
const byte HIGHLIGHT = 1;
const byte PATRIOTIC = 2;
const byte BREATH = 3;
const byte HOMERUN = 4;

unsigned long last_change = 0;
unsigned long now = 0;
bool isCycling = false; // flag that enables cycling the effects
int showType = 0;
byte oldShowType = 0;

void setup() {
Serial.begin(115200);
delay (500);

pinMode(BTN_PIN, INPUT_PULLUP); // config BTN_PIN as an input using internal pull-up resistor

ws2812fx.init();
ws2812fx.setBrightness(100);

//from working sketch
ws2812fx.setIdleSegment(0, 0, 161, FX_MODE_BLINK, COLORS(WHITE, RED), 200, false); // segment 0 is leds 0 - 162 flash (homerun?)
ws2812fx.setIdleSegment(1, 0, 161, FX_MODE_TRICOLOR_CHASE, COLORS(WHITE, RED, BLUE), 15000, false); // segment 1 is leds 0 - 162 (patriotic)
ws2812fx.setIdleSegment(2, 0, 161, FX_MODE_BREATH, 0xFF0000, 5000, false); // segment 2 is leds 0 - 162 0xFF0000-RED (breathing)
ws2812fx.setIdleSegment(3, 162, 184, FX_MODE_COLOR_WIPE, RED, 100, false); // segment 3 is leds (red-circle)

ws2812fx.strip_off();
}

void loop() {
now = millis();
//ws2812fx.service();

if (digitalRead(BTN_PIN) == LOW)
{ // if button pressed, started cycling effects
isCycling = true;
}

if (now - last_change > TIMER_MS && isCycling)
{
showType++;
if (showType > 4) //compares number of times the button is pushed. after # is reached, start over.
showType = 0; //which sequence do we start back at after we hit the limit on # of times button pushed.
last_change = now;
}
if (showType != oldShowType)
{
startShow(showType); //jumps to 'void startShow(int i)'
}
if (showType == 0) isCycling = false; // if cycled through all effects, stop cycling
oldShowType = showType;
ws2812fx.service();
}

void startShow(int i)
{
Serial.println(i);
switch (i)
{
case ALLoff:
ws2812fx.stop();
ws2812fx.removeActiveSegment(3);
ws2812fx.removeActiveSegment(2);
ws2812fx.removeActiveSegment(1);
ws2812fx.removeActiveSegment(0);
ws2812fx.start();
break;

case HIGHLIGHT:
  ws2812fx.stop();
  ws2812fx.addActiveSegment(0);
  //ws2812fx.addActiveSegment(1);
  ws2812fx.start();
  break;

case PATRIOTIC:
  ws2812fx.stop();
  ws2812fx.removeActiveSegment(0);
  ws2812fx.addActiveSegment(1);
  ws2812fx.start();
  break;

case BREATH:
  ws2812fx.stop();
  ws2812fx.removeActiveSegment(1);
  ws2812fx.addActiveSegment(2);
  ws2812fx.start();
  break;

case HOMERUN:
  ws2812fx.stop();
  ws2812fx.removeActiveSegment(2);
  ws2812fx.addActiveSegment(0);
  ws2812fx.addActiveSegment(3);
  ws2812fx.start();
  break;

}
ws2812fx.service();
}`

I see what you mean. The next button press isn't seen until TIMER_MS after the cycle has finished. I reworked the code a little to better separate the button logic from the LED logic. This is what I came up with:

#include <WS2812FX.h>

#define LED_COUNT 182
#define LED_PIN 6
#define BTN_PIN 2 // the gpio number of the pushbutton
#define TIMER_MS 3000 // cycle effects every 3 seconds

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

//const byte ALLoff = 0; // not used
const byte HIGHLIGHT = 1;
const byte PATRIOTIC = 2;
const byte BREATH = 3;
const byte HOMERUN = 4;

unsigned long last_change = 0;
unsigned long now = 0;
bool isCycling = false; // flag that enables cycling the effects
int showType = 0;

void setup() {
  Serial.begin(115200);
  delay (500);

  pinMode(BTN_PIN, INPUT_PULLUP); // config BTN_PIN as an input using internal pull-up resistor

  ws2812fx.init();
  ws2812fx.setBrightness(100);

  //from working sketch
  ws2812fx.setIdleSegment(0, 0, 161, FX_MODE_BLINK,          COLORS(WHITE, RED), 200, false); // segment 0 is leds 0 - 162 flash (homerun?)
  ws2812fx.setIdleSegment(1, 0, 161, FX_MODE_TRICOLOR_CHASE, COLORS(WHITE, RED, BLUE), 15000, false); // segment 1 is leds 0 - 162 (patriotic)
  ws2812fx.setIdleSegment(2, 0, 161, FX_MODE_BREATH,         0xFF0000, 5000, false); // segment 2 is leds 0 - 162 0xFF0000-RED (breathing)
  ws2812fx.setIdleSegment(3, 162, LED_COUNT-1, FX_MODE_COLOR_WIPE,     RED, 100, false); // segment 3 is leds (red-circle)

  ws2812fx.strip_off();
}

void loop() {
  now = millis();

  if (digitalRead(BTN_PIN) == LOW && !isCycling) { // if button pressed and not cycling, started cycling effects
    isCycling = true;
    showType = HIGHLIGHT; // set the first effect
    startShow(showType); // start the first effect
    last_change = now;
  }

  if (now - last_change > TIMER_MS && isCycling) {  // time to cycle to the next effect?
    showType++;
    if(showType > HOMERUN) { // time to stop cycling?
      ws2812fx.stop(); // all LEDs off
      isCycling = false;
    } else {
      startShow(showType); // cycle to the next effect
    }
    last_change = now;
  }

  ws2812fx.service();
}

void startShow(int i)
{
  Serial.println(i);
  switch (i)
  {
//    case ALLoff: // not used
//      ws2812fx.stop();
//      ws2812fx.removeActiveSegment(3);
//      ws2812fx.removeActiveSegment(2);
//      ws2812fx.removeActiveSegment(1);
//      ws2812fx.removeActiveSegment(0);
//      ws2812fx.start();
//      break;

    case HIGHLIGHT:
      ws2812fx.stop();
      ws2812fx.removeActiveSegment(3); // restarting cycle, so remove any leftover active segments
      ws2812fx.removeActiveSegment(2);
      ws2812fx.removeActiveSegment(1);
      ws2812fx.removeActiveSegment(0);

      ws2812fx.addActiveSegment(0);
      //ws2812fx.addActiveSegment(1);
      ws2812fx.start();
      break;

    case PATRIOTIC:
      ws2812fx.stop();
      ws2812fx.removeActiveSegment(0);
      ws2812fx.addActiveSegment(1);
      ws2812fx.start();
      break;

    case BREATH:
      ws2812fx.stop();
      ws2812fx.removeActiveSegment(1);
      ws2812fx.addActiveSegment(2);
      ws2812fx.start();
      break;

    case HOMERUN:
      ws2812fx.stop();
      ws2812fx.removeActiveSegment(2);
      ws2812fx.addActiveSegment(0);
      ws2812fx.addActiveSegment(3);
      ws2812fx.start();
      break;
  }
}

You're amazing Keith. That did it. Truly appreciate your time and help!