Timing issue with button press

Hi guys,

Getting stuck here. The following code is doing everything I want it to EXCEPT initial button press. I upload the sketch to arduino and open the serial window.
I press the button but nothing happens initially. I wait a few secs and press it again and it runs properly thru the case-switch statements. ONCE it starts running, the sketch runs each case for the 3 seconds and moves to the next one. Then it starts over again and waits for another button press. But, once again, i have to wait a few seconds, then if i hit the button, it works. What am i missing? Thanks in advance.

#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 2 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();
}

how is your button wired ?

how long does a call to s2812fx.service(); or ws2812fx.start(); takes?

Hi JML

It seems after i hit the button, i'll have to wait 3 secs (which is what TIMER_MS is set to) before another button press is recognized. Then it runs through each case for 3 seconds before returning to the beginning and waits for another button press. However, the button has to be pressed, wait for 3 secs, and then another press will finally be recognized.

Button is wired ground on one side and other side to PIN2 on arduino.

Possibly the call to the animation is blocking and thus it only reads the button

if (digitalRead(BTN_PIN) == LOW)

once in a while.

if you want the system to be reactive, you need to come read the button often and can't have a blocking animation call

as a first try, call ws2812fx.service() only when animation is due (I don't know that library)

also set last_change with a negative value (-TIMER_MS at least) so that it's taken into account

Upon rechecking, I actually don't have to hit the button wait 3 secs and then hit it again.
If i just wait 3 seconds after all the case statements are complete, it will recognize the button again. I feel that the '#define TIMER-MS 3000' is coming into play here with the button function too but haven't been able to figure it out yet

I removed the ws2812fx.service() from the beginning, but no change

This is why, when you start millis is 0 so is last_change, so for 3000 milliseconds you cannot enter this even if button is pressed

yep - try something like

unsigned long last_change = -TIMER_MS;

for the initialization

killzone_kid:

what would be the work around for this?

see #8

JML:

Ill give that a try

I don’t understand why you need that delay, means your button would react only every 3 seconds. How to solve it? try different approach

I was under the impression this statement allows each case to run for 3 seconds before moving to the next case

I defined:
unsigned long last_change = -TIMER_MS;

and removed:
unsigned long last_change = 0;

but still has same operation

that would possibly solve for a press at first but question on #12 is important.

I changed TIMER_MS from 3000 to 1000.

When running thru the cases, they run 1 sec each and then go to next case

Before they would run for 3 secs each.

Changing TIMER_MS changes how long each case runs

But button is more responsive! :sweat_smile:

try this

#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 2 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;

bool isCycling = false; // flag that enables cycling the effects
byte showType = 0;

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.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;
  }
}


void setup() {
  pinMode(BTN_PIN, INPUT_PULLUP); // config BTN_PIN as an input using internal pull-up resistor
  Serial.begin(115200);

  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() {
  unsigned long now = millis();

  if (!isCycling && digitalRead(BTN_PIN) == LOW)  { // if button pressed, started cycling effects
    last_change = now ;
    showType = 0;
    isCycling = true;
  }

  if (isCycling && (now - last_change > TIMER_MS)) { // go to next animation
    if (++showType > 4) {
      isCycling = false;
      ws2812fx.strip_off();
      ws2812fx.service();
    }
    else startShow(showType);
    last_change = now;
  }

  if (isCycling)  ws2812fx.service();
}

edited here, so no clue if this works.
You won't be able to interrupt a cycle once started (the way it's coded)

Thanks JML
It locks up after case 4, but if i hit the button again, it sees the button press instantly and it continues. However, it never goes thru case0...
Also i noticed on the initial upload to the ardunio, it takes the 3 secs before the button is recognized.

probably needs an startShow(showType); when you press the button

#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 2 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;

bool isCycling = false; // flag that enables cycling the effects
byte showType = 0;

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.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;
  }
}


void setup() {
  pinMode(BTN_PIN, INPUT_PULLUP); // config BTN_PIN as an input using internal pull-up resistor
  Serial.begin(115200);

  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() {
  unsigned long now = millis();

  if (!isCycling && digitalRead(BTN_PIN) == LOW)  { // if button pressed, started cycling effects
    last_change = now ;
    showType = 0;
    startShow(showType);
    isCycling = true;
  }

  if (isCycling && (now - last_change > TIMER_MS)) { // go to next animation
    if (++showType > 4) {
      isCycling = false;
      ws2812fx.strip_off();
      ws2812fx.service();
    }
    else startShow(showType);
    last_change = now;
  }

  if (isCycling)  ws2812fx.service();
}

that's intended. You press, it starts the animations and then terminates. A new press will start again.