Strange pushbutton behavior

I have project where an arduino can add water to my pool. Every time I press a pushbutton, I want to add a few minutes to a timer that controls a water fill valve. To test I just have a little momentary PCB pushbutton switch. I’m using button.h library. The problem is, if press the button and hold it for half a second or so, it works fine. But if I press very quickly, it counts it as two presses and adds twice as much time to the timer. I’m using Arduino IDE 1.01 with and Uno R3. Any ideas why I’m getting this behavior? Here’s my program

#include <Button.h>     // For pushbutton http://github.com/carlynorama/Arduino-Library-Button

#define WATER_FILL_BP_MIN    242000   // minutes added to water fill timer
#define WATER_FILL_PB    8            // Push-button for water fill 
#define WATER_OUTPUT     7            // Adds water to pool
uint32_t WaterFillTimer;     // Timer to turn on water fill valve, adds 15 minutes every time pushbutton is pressed

Button btnWaterFill = Button(WATER_FILL_PB, LOW);    // Setup push button for water fill

void setup(){
  Serial.begin(9600);  
  pinMode(WATER_OUTPUT, OUTPUT);
  digitalWrite(WATER_OUTPUT, LOW);
  delay(100);
}

void loop(){
 
  // Check for water fill pushbutton, if detected and there is pressure in the line, add 15 minutes to timer
  btnWaterFill.listen();
  if ( btnWaterFill.onPress()) 
  {
    // Add time to water fill timer
    if(WaterFillTimer <= millis())  // need to check for pressure here becuse if valve is already open, pressure will be low
    { 
      WaterFillTimer = millis() + WATER_FILL_BP_MIN;  // First time button is pushed
      Serial.println("Initial timer");
    }   
    else if(digitalRead(WATER_OUTPUT) == HIGH)
    { 
      WaterFillTimer += WATER_FILL_BP_MIN;          // Timer is already on, add more minutes, but only if valve is already open
      Serial.println("Add more time");   
    }             
  }  

  if (digitalRead(WATER_OUTPUT) ==  HIGH)
  {
    Serial.print("water fill countdown:  ");
    Serial.println(((WaterFillTimer - millis()) / 60000.0));
    delay(100);
  }

  // Turn on water fill valve
  if(((long)(millis() - WaterFillTimer) <= 0))
  {
    digitalWrite(WATER_OUTPUT, HIGH);
  }
  else
  { // Turn Pump off
    digitalWrite(WATER_OUTPUT, LOW);
  }  
}

2 things that might help your issues are 1. Do you have the input pulled up or down as the logic requires and do you have a 100nF cap from the Pin to ground and 2. are you familiar with the bounce library or a delay without delay(). Both or either issue might well fix your problems.

Doc

Use btnWaterFill.getDebounceDelay() to find out what your current de-bounce delay is set to, and then use btnWaterFill.setDebounceDelay() to increase it.

Docedison: 2 things that might help your issues are 1. Do you have the input pulled up or down as the logic requires and do you have a 100nF cap from the Pin to ground and 2. are you familiar with the bounce library or a delay without delay(). Both or either issue might well fix your problems.

Doc

I have a 10k pull-up resistor. I don't have a cap installed. I'm not familiar with the bounce library.

Docedison: 2 things that might help your issues are 1. Do you have the input pulled up or down as the logic requires and do you have a 100nF cap from the Pin to ground and 2. are you familiar with the bounce library or a delay without delay(). Both or either issue might well fix your problems. Doc

Just tried the capacitor suggestion, that didn't work.

Forget the cap - look back at reply #2.

getDebounceDelay shows it’s set at 30 (I assume this is milliseconds). I tried increasing it in steps all the way to 200, but that didn’t solve the problem. For fun I lowered it to 10 and 20, but that didn’t help. I even tried to set it for 1000 - most of the time it missed the button press altogether, but sometimes I’d get a triple bounce.

Something seems wrong, if I have the delay set to 100 and press the button quickly, there I shouldn’t get two button presses. It seems I’m either using this library incorrectly, or the button.h library needs something fixed.

I think I’ll just use millis() and a timer to do a little additional debouncing.

I put my own 150mS delay (debounceTimer) in and it works well now.

#include <Button.h>     // For pushbutton http://github.com/carlynorama/Arduino-Library-Button
#define WATER_FILL_BP_MIN    242000   // minutes added to water fill timer
#define WATER_FILL_PB    8            // Push-button for water fill 
#define WATER_OUTPUT     7            // Adds water to pool
uint32_t WaterFillTimer;  // Timer to turn on water fill valve, adds 15 minutes every time pushbutton is pressed
Button btnWaterFill = Button(WATER_FILL_PB, LOW);  // Setup pushbutton for water fill

void setup()
{
  Serial.begin(9600);  
  pinMode(WATER_OUTPUT, OUTPUT);
  digitalWrite(WATER_OUTPUT, LOW);
  delay(100);
}

void loop()
{
  static uint32_t debounceTimer;
 
  // Check for water fill pushbutton, if detected and there is pressure in the line, add 15 minutes to timer
  btnWaterFill.listen();
  if ( btnWaterFill.onPress() && millis() > debounceTimer ) 
  {
    debounceTimer = millis() + 150;
    // Add time to water fill timer
    if(WaterFillTimer <= millis())  // need to check for pressure here becuse if valve is already open, pressure will be low
    { 
      WaterFillTimer = millis() + WATER_FILL_BP_MIN;  // First time button is pushed
      Serial.println("Initial timer");
    }   
    else if(digitalRead(WATER_OUTPUT) == HIGH)
    { 
      WaterFillTimer += WATER_FILL_BP_MIN;          // Timer is already on, add more minutes, but only if valve is already open
      Serial.println("Add more time");   
    }             
  }  

  if (digitalRead(WATER_OUTPUT) ==  HIGH)
  {
    Serial.print("water fill countdown:  ");
    Serial.println(((WaterFillTimer - millis()) / 60000.0));
    delay(100);
  }

  // Turn on water fill valve
  if(((long)(millis() - WaterFillTimer) <= 0))
  {
    digitalWrite(WATER_OUTPUT, HIGH);
  }
  else
  { // Turn Pump off
    digitalWrite(WATER_OUTPUT, LOW);
  }  
}
    if(WaterFillTimer <= millis())  // need to check for pressure here becuse if valve is already open, pressure will be low

The comment has NOTHING to do with the code.

      WaterFillTimer += WATER_FILL_BP_MIN;          // Timer is already on, add more minutes, but only if valve is already open

Adding unsigned longs is not a good idea. WaterFillTime might be a good name. WaterFillTimer is not.

  if(((long)(millis() - WaterFillTimer) <= 0))

Why are you casting an unsigned value to a signed value? The result of the computation will be unsigned, and therefore not at all what you expect, before the cast takes place. The result will NEVER be negative.

You need to rewrite your whole sketch to compare now (as returned by millis()) with then (when the water was turned on) and compare that to how long the water should be on. It is that last value that you should be adding to. And the variable that contains that last value needs a meaning name.

PaulS:

    if(WaterFillTimer <= millis())  // need to check for pressure here becuse if valve is already open, pressure will be low

The comment has NOTHING to do with the code.

My original code had an input from a pressure switch, when I took that out to do this test, I forgot to remove the comment.

      WaterFillTimer += WATER_FILL_BP_MIN;          // Timer is already on, add more minutes, but only if valve is already open

Adding unsigned longs is not a good idea. WaterFillTime might be a good name. WaterFillTimer is not.

WaterFillTime is a better name. What’s wrong with adding unsigned longs?

  if(((long)(millis() - WaterFillTimer) <= 0))

Why are you casting an unsigned value to a signed value? The result of the computation will be unsigned, and therefore not at all what you expect, before the cast takes place. The result will NEVER be negative.

I’m pretty new at this, and certainly no expert in casting. But the above code works. If I add Serial.println((long)(millis() - WaterFillTimer)) to see what’s going on, it prints out negative numbers. The output turns on for the specified amount of time in WaterFillTimer then turns off.

PaulS:

  if(((long)(millis() - WaterFillTimer) <= 0))

Why are you casting an unsigned value to a signed value? The result of the computation will be unsigned, and therefore not at all what you expect, before the cast takes place. The result will NEVER be negative.

Casting unsigned into signed will result in a negative number until millis() is larger than WaterFillTimer.

I see no problem with this, as it properly handles millis() rollover.