Problem with certain button and 'double-click' function

Hello,

I'm having a problem with a certain kind of button not playing nicely with a 'double-click' detect function.

Code:

#define buttonPin 7        // analog input pin to use as a digital input
#define ledPin1 5          // digital output pin for LED 1
#define ledPin2 6          // digital output pin for LED 2
#define ledPin3 12          // digital output pin for LED 3

// LED variables
boolean ledVal1 = false;    // state of LED 1
boolean ledVal2 = false;    // state of LED 2
boolean ledVal3 = false;    // state of LED 3


//=================================================

void setup() {
   // Set button input pin
   pinMode(buttonPin, INPUT);
   digitalWrite(buttonPin, HIGH );
   // Set LED output pins (and turn all off)
   pinMode(ledPin1, OUTPUT);
   digitalWrite(ledPin1, ledVal1);
   pinMode(ledPin2, OUTPUT);
   digitalWrite(ledPin2, ledVal2);
   pinMode(ledPin3, OUTPUT);    
   digitalWrite(ledPin3, ledVal3);
}

void loop() {
   // Get button event and act accordingly
   int b = checkButton();
   if (b == 1) clickEvent();
   if (b == 2) doubleClickEvent();
   if (b == 3) holdEvent();
}

//=================================================
// Events to trigger

void clickEvent() {
   digitalWrite(ledPin1, HIGH);
   delay(3000);
   digitalWrite(ledPin1, LOW);
}
void doubleClickEvent() {
   digitalWrite(ledPin2, HIGH);
   delay(3000);
   digitalWrite(ledPin2, LOW);
}
void holdEvent() {
   digitalWrite(ledPin3, HIGH);
   delay(3000);
   digitalWrite(ledPin3, LOW);
}











//=================================================
//  MULTI-CLICK:  One Button, Multiple Events

// Button timing variables
int debounce = 20;          // ms debounce period to prevent flickering when pressing or releasing the button
int DCgap = 250;            // max ms between clicks for a double click event
int holdTime = 2000;        // ms hold period: how long to wait for press+hold event


// Button variables
boolean buttonVal = HIGH;   // value read from button
boolean buttonLast = HIGH;  // buffered value of the button's previous state
boolean DCwaiting = false;  // whether we're waiting for a double click (down)
boolean DConUp = false;     // whether to register a double click on next release, or whether to wait and click
boolean singleOK = true;    // whether it's OK to do a single click
long downTime = -1;         // time the button was pressed down
long upTime = -1;           // time the button was released
boolean ignoreUp = false;   // whether to ignore the button release because the click+hold was triggered
boolean waitForUp = false;        // when held, whether to wait for the up event
boolean holdEventPast = false;    // whether or not the hold event happened already


int checkButton() {    
   int event = 0;
   buttonVal = digitalRead(buttonPin);
   // Button pressed down
   if (buttonVal == LOW && buttonLast == HIGH && (millis() - upTime) > debounce)
   {
       downTime = millis();
       ignoreUp = false;
       waitForUp = false;
       singleOK = true;
       holdEventPast = false;
       
       if ((millis()-upTime) < DCgap && DConUp == false && DCwaiting == true)  DConUp = true;
       else  DConUp = false;
       DCwaiting = false;
   }
   // Button released
   else if (buttonVal == HIGH && buttonLast == LOW && (millis() - downTime) > debounce)
   {        
       if (not ignoreUp)
       {
           upTime = millis();
           if (DConUp == false) DCwaiting = true;
           else
           {
               event = 2;
               DConUp = false;
               DCwaiting = false;
               singleOK = false;
           }
       }
   }
   // Test for normal click event: DCgap expired
   if ( buttonVal == HIGH && (millis()-upTime) >= DCgap && DCwaiting == true && DConUp == false && singleOK == true && event != 2)
   {
       event = 1;
       DCwaiting = false;
   }
   // Test for hold
   if (buttonVal == LOW && (millis() - downTime) >= holdTime) {
       // Trigger "normal" hold
       if (not holdEventPast)
       {
           event = 3;
           waitForUp = true;
           ignoreUp = true;
           DConUp = false;
           DCwaiting = false;
           //downTime = millis();
           holdEventPast = true;
       }

   }
   buttonLast = buttonVal;
   return event;
}

Circuit:

What's happening is that this particular kind of button does not want to 'double-click' - the code above doesn't seem to pick up me double-clicking this button. I don't understand why that would be...this button works just fine - it picks up the double-clicks.
Both buttons do a regular, single click and a press-and-hold just fine.

This might be a question for the programing section.

Hi domiflichi

int DCgap = 250;            // max ms between clicks for a double click event

The software needs the switch to close, open and close again within 0.25 seconds to recognise a double click.

Maybe the switch, being larger and heavier and with a stronger spring, makes it difficult to double click this fast.

Try changing the 250 to 500 as a test.

By the way, your circuit diagram image is not loading, at least for me.

Regards

Ray

I totally forgot to mention that I did not write this code. I took it from:
http://forum.arduino.cc/index.php?topic=14479.0

Anyway, Ray, thanks for the reply. I tried 500 as you mentioned, but didn't seem to help. I also tried 750, 1000, 1500, still didn't seem to change. Maybe slightly better, but only works around 2 out of 20 times or so.

One strange thing I did notice is that when I tried double-clicking the first time just after uploading a code change, it worked. And I think this was every time.

(Sorry about the image not loading, it wasn't just you Ray...my computer went down for a few minutes - I'm hosting the image on my own machine / web server)

The schematic shows no GND going to the common rail on the breadboard.

:wink:

Doh! Thanks for the catch. I missed that when creating the Fritzing file...I do have common ground connected in 'real life' though.
(I also updated the image, so it should show the ground now)

The diagram does not show a pullup resistor on the switch. Try with this change to the program:

   // Set button input pin
   pinMode(buttonPin, INPUT_PULLUP);

They did a digitalWrite to the input pin, this turns on the PULLUP resistor.

.

Good point, @LarryD :slight_smile:

Which makes me think there has to be something mechanically wrong with the switch. Sticky contacts? Dirty contacts?

Since the same code and same wiring work reliably with the other switch.

@domiflichi, have you tried multiple instances of the problem switch type? And what happens if you tap two jumper leads together as a switch?

Hi Hackscribble,

I don't think anything is wrong with the switches (they're brand new) - yes, I have tried all 4 identical switches that I have and they all behaved the same.

When I tapped 2 jumper wires together, it works...as long as I'm really fast.

I've been trying to think of possible differences between the "bad" switch and "good" switch.

Say there were bugs in the debounce software. If the "good" switch has little or no contact bounce when closing and opening, the bugs might not show up. If the "bad" switch does have contact bounce, then the bugs would become apparent. Also, a software bug could explain why the first time you double click after reset it works OK but later it does not.

You could try downloading this library: Arduino OneButton Library

One of my clients used it in a project and it seems to work well. Conveniently, the sample program on that page toggles an LED on each double click.

Could the switch be a Push-ON/Push-OFF?

@Hackscribble - thank you for the link. I'll definitely look into that soon hopefully.

@boolrules - In the description of the button on the product page that I linked to in my original posting, it said 'SPST N.O.', so that rules that out, right?

Thank you everyone so far, I don't know why I didn't think of this before, but I think I'll try contacting Sparkfun. I'll report back if I find anything out.

Well I was finally able to download/test the OneButton library. To my surprise, the problematic pushbutton seems to work with this library! The example sketch just does a double-click, so I haven't tried a regular/single click or a push-and-hold, but I would think that those will work.

Regardless, I do want to try out the single and push-and-hold clicks to be sure. When I do, I'll report back here.

Thanks again everyone so far!

FYI single click and 'push-and-hold' also works

I thought I'd post an update about a couple of more buttons. I just bought the following 2 buttons:
16mm Illuminated Pushbutton - Green Momentary : ID 1440 : $1.50 : Adafruit Industries, Unique & fun DIY electronics and kits and
16mm Panel Mount Momentary Pushbutton - Green : ID 1504 : $0.95 : Adafruit Industries, Unique & fun DIY electronics and kits

So I tested them (doing 'double-clicks') with the code that I posted in my original post. They worked better (definitely not perfect) than the original (the cheap, problematic) button that I tried. As long as I double-click the button just right (correct speed, and force), then it actually works fairly well.

Then I tested it with the OneButton library, and it was extremely reliable.

I wonder what kind of secret sauce is in that library...

Thanks everyone!