Issue using EZButton in while loop

I am a very new Arduino user and have been through a few basic projects.
My latest project is to try to create a basic UK traffic light system.
I have successfully created the circuit and the basic light changing works fine when left to loop automatically - i.e. Red, then Red & Amber, then Green, then Amber, then back to red with simple delays between the appropriate sections.
I then decided to introduce a pushbutton to emulate a pelican crossing which interrupts the Green light delay and goes through Amber, then Red etc. I have used the EZButton function to do this with some success.
This works ok when first pressed, but then when the lights cycles back through the sequence, when it should go back to green it very briefly flashed and then immediately goes back to Amber. To a novice it seems like it thinks the button is still being pressed.
Can anyone shed any light on why this may be? Is there some kind of reset which needs to be done after the button has been pressed?

int red = 9;
int amber = 8;
int green = 7;
ezButton pushbutton(12);  // create ezButton object attached to pin 12;
int counter = 1;
int countTo = 1;


void setup(){
 pinMode(red, OUTPUT);
 pinMode(amber, OUTPUT);
 pinMode(green,  OUTPUT);
 pinMode(12, INPUT_PULLUP);
 Serial.begin(9600);
 randomSeed(analogRead(0)); // Initialize random seed
 pushbutton.setDebounceTime(100); // set debounce time to 100 milliseconds
  }

void loop(){
 //Red Only
  digitalWrite(red, HIGH);
  delay(15000);
   
 // Red & Amber
  digitalWrite(amber, HIGH);
  delay(3000);
  digitalWrite(red,  LOW);
  digitalWrite(amber,  LOW);
   
 //green only)
  digitalWrite(green, HIGH);
  counter = 1;
  countTo = 500; //random(1000, 30000); // end plan is to generate random number of cycles between 1000 and 10000
  // This will stay on green for a random time (approx 30 seconds to 15 minutes) unless button pushed
  while (counter < countTo) {
    pushbutton.loop ();
    Serial.print("  Counter:");
    Serial.print(counter);
    Serial.print ("  Random Cycles:");
    Serial.println (countTo);
    counter = counter + 1;
    if(pushbutton.isPressed()){ //when button pushed, set counter to countTo which exits while loop to continue to Amber
    counter = countTo;
    }
  }
 
 //Amber only
  digitalWrite(green,  LOW);
  digitalWrite(amber, HIGH);
  delay(4000);
  digitalWrite(amber,  LOW);

}


How is the button connected to the Arduino?

Hi, I have it connected between pin12 and ground.

Welcome to the forum

  while (counter < countTo) {
    pushbutton.loop ();
    Serial.print("  Counter:");
    Serial.print(counter);
    Serial.print ("  Random Cycles:");
    Serial.println (countTo);
    counter = counter + 1;
    if(pushbutton.isPressed()){ //when button pushed, set counter to countTo which exits while loop to continue to Amber
    counter = countTo;
    }

How long do you expect this while loop to take when countTo is set to 500 ?

It's because your code is not polling pushbutton.loop() constantly. I'll leave it to you to figure out why; it's a good exercise in debugging.

Add this function to your sketch and replace delay(...) with myDelay(...) to get pushbutton.loop() called consistently and watch the difference.

void myDelay(unsigned long dly) {
   unsigned long now = millis();
   while( millis() - now < dly ) {
      pushbutton.loop();
   }
}

Ok.

I'm not familiar with that button library (surprised your code compiles without a #include for that).

How does the button library know that LOW means pressed and HIGH means not pressed?

I would recommend you forget that library and just use digitalRead(). You certainly don't need to worry about bouncing with all those delay()s in the code!

Probably about 2s once the serial output buffer is filled, assuming around 1ms per character?

Isn't it doing that when it needs to, ie. Inside the while-loop?

Think about what happens when the while loop is entered the next time after the button's been pressed. Think about debouncing time and what isPressed is going to return when pushbutton.loop() hasn't been called since the last time the button was pressed.

The next time the while-loop is entered, I guess pushbutton.loop() is going to see that the button is no longer pressed (assuming it somehow knows that LOW means pressed), and that several seconds have passed since the moment it was pressed last time. So I would expect the subsequent call of button.isPressed() to return false.

If it is returning true, that would explain the problem, but I don't get why it would do that.

A solution could be to remove use of the ezbutton library, I don't think it's beneficial in this code.

Giving away the ending: the first .loop() call the next time through the loop is going to see that it's no longer pressed, and the debounce time is going to start. But .isPressed is going to return true because the debounce time hasn't expired.

Which is why adding .loop into myDelay fixes the issue. The library sees the button released much earlier, and the debounce time has time to expire.

Why would it start the debounce time then? That should have started when it previously saw that the button was pressed, which would have been several seconds ago and the debounce time long since passed.

I guess you must have already read the library code, so you understand this library's eccentricities. So I believe you.

By making over-complicated code even more over complicated. I prefer to keep things simple. Get rid of that library, it's not doing anything useful here.

Try this

int red = 9;
int amber = 8;
int green = 7;
int pushbutton = 12;

void setup(){
 pinMode(red, OUTPUT);
 pinMode(amber, OUTPUT);
 pinMode(green,  OUTPUT);
 pinMode(pushbutton, INPUT_PULLUP);
 Serial.begin(9600);
 randomSeed(analogRead(0)); // Initialize random seed
}

void loop(){
 //Red Only
  digitalWrite(red, HIGH);
  delay(15000);
   
 // Red & Amber
  digitalWrite(amber, HIGH);
  delay(3000);
  digitalWrite(red,  LOW);
  digitalWrite(amber,  LOW);
   
 //green only)
  digitalWrite(green, HIGH);
  unsigned long greenStartTime = millis(); 
  unsigned long maxGreenTime = 5000; //random(1000, 10000); // end plan is to generate random number of ms between 1000 and 10000
  // This will stay on green for a random time (approx 1 seconds to 10 seconds) unless button pushed
  while (digitalRead(pushbutton) == HIGH and millis() - greenStartTime < maxGreenTime); // stay in loop until time is up or button is pressed

 //Amber only
  digitalWrite(green,  LOW);
  digitalWrite(amber, HIGH);
  delay(4000);
  digitalWrite(amber,  LOW);

}

Added: read it if you want, but maybe read the next post first. Sry sry sry. To much sun? Yeah, that's the ticket. :expressionless:


Post all the code. I don't see but you probably have the # include for the library.

Maybe good and bad news:

  while (counter < countTo) {
    pushbutton.loop ();
    Serial.print("  Counter:");
    Serial.print(counter);
    Serial.print ("  Random Cycles:");
    Serial.println (countTo);
    counter = counter + 1;
    if(pushbutton.isPressed()){ //when button pushed, set counter to countTo which exits while loop to continue to Amber
    counter = countTo;
    }
  }

The loop will be timed by your countTo and the limit that Serial printing will impose as the output buffer get full.

It's not a particularly good way to do something for some amount of time with the possibility of interrupting it by a button.

But if that's the ezButton.h I don't absolutely hate, you've deployed it in an odd way, but nothing about it should cause any difficulty. Its .loop() method looks at the button, and gives you one chance, until your next call to .looo(), to check for .isPressed().

Do you see the Serial output scrolling by rapidly?

Don't be tempted to use a delay in the while loop, ezButton ha some bad behaviour if it isn't called frequently.

Or maybe…

Try bumping the serial rate uo to 115200. At 1 millisecond per character, 9600 and the number of characters you print may be running into exzctly its issue.

I know, I know: at 115200 you'll have to bump the loop counter range way up to get your desired timing.

You can prolly make this work; we can suggest all the better ways to do this if/when you'd like.

Please know that we'd like to lead you down that path… a bit steep maybe, but with a huge layoff for your future if you anticipate having one in this hobby.

a7

I am having a bad day. I'd blame the meds, except I don't take any.

If you simply replace

if(pushbutton.isPressed()){

with

if (digitalRead(12) == LOW) {

the sketch behaves.

ezButton sucks. There, I said it and it is not what I once said "it does not suck", which for me is high praise for a button library.

I've previously spent way too long on ferreting out the reason behind its fundamental shirtcoming: it does not play well with delay(). ← Period.

a7

That was the pressed debounce. What hasn't happened at the top of loop is the released debounce, because loop hasn't been called in between. Which is why the button still reads as pressed the first time through the loop after the button is pressed the prior time.

I also ran this on an Uno to check it out.

ezButton provides use-it or lose-it methods .isPressed() and .isReleased(). Once per call to its .loop() method.

You can ignore or use either or both.

In circumstances that it likes, it's actually pretty easy to use.

See, and play with, this

Warning: this example fails to call .setDebounceTime(), so it defaults to 0, which basically makes it not work with real pushbuttons.

Add that call, which you can see in the OP's code.

@misterchew - this button handler library works and is just as simple to use:

Oddly it defaults to 100 millisecond debounce time, an eternity.

Set it in the constructor

Button button1(2, 20);  // 20 ms debounce

I have not yet placed it in your code, but I have worked with it enough to have good confidence it woukd operate they way ezButton looks like it shoukd in your code.

HTH

a7

Here is @misterchew's code, with a different button library, the madleech one I spoke of and posted a link to an example for. I don't know if it is official and on offer through the IDE library manager.

Get it here:

I changed some time constants, life is too short. I added printing, I like chatty sketches.

Try it here

a7

That seems a crazy way for the library to behave. The button has been released, and the debounce time for the press ended a long time ago, it's not a bounce following the press, so .isPressed() should return false. The debounce time after the release should be used to prevent any "phantom" presses being detected which are only bounces after the release.

(Of course, in this code, no explicit debouncing is needed because of all the long delay()s.)

Anyway, thanks for checking it out and explaining it. I shall certainly not recommend that library, now that I am a little more familiar with it!

Thank you all for your feedback. A lot to take in, but I when I have some time later today I hope to work my way through this and try to absorb as much as I can.

Oops! The #include was the top line, I somehow managed to miss that when copying the text! :man_facepalming: