Adding a Double Click Case Statement

This code uses a case statement and a single switch to redirect the current flow through a latching relay. The goal is to add a double click feature that will trigger another latching relay. Can anyone help me add to this code?

Attached is a zip file of the bounce library

Thanks!

  • Christian
#include <Bounce2.h>

/*  Alternating LED's  */
//#include <Bounce.h>


const int inputPin = 1;    // momentary switch
const int ledPin =  0;      // the number of the LED pin
const int relayPin1 =  3;
const int relayPin2 = 4;


byte mode = 0;

//settup debounce for first push button
Bounce  bouncer  = Bounce();

void setup() 
{ 
  pinMode(inputPin, INPUT);
  pinMode(ledPin, OUTPUT);
  pinMode(relayPin1, OUTPUT);
  pinMode(relayPin2, OUTPUT);

  
   // Setup the button
 pinMode( inputPin ,INPUT);
 // Activate internal pull-up (optional) 
 digitalWrite( inputPin ,HIGH);
 // After setting up the button, setup the object
 bouncer .attach( inputPin );
 bouncer .interval(5);
 
}

void loop() {
  bouncer.update ( );    // Update the debouncer for the push button
  switch( mode )
  {
    case 0://------------------------ I'm off and in restmode
      if ( bouncer.read() == HIGH )
      { 

        digitalWrite (ledPin, LOW);     // switch LED ON
        digitalWrite (relayPin1, LOW);
        digitalWrite (relayPin2, HIGH);  // switch relay ON

                                      
        mode = 1;
      }
      break;
    case 1://------------------------ I'm in ON mode, w8 4 keyrelease
      if ( bouncer.read() == LOW )
      
      { 
        digitalWrite (ledPin, LOW);     // switch LED ON
        digitalWrite (relayPin1, HIGH);
        digitalWrite (relayPin2, LOW);  // switch relay ON
        
      
        mode = 2;
      }
      break;
    case 2://------------------------ I'm ON and in restmode
      if ( bouncer.read() == HIGH )
      { 
        
        digitalWrite (ledPin, HIGH);     // switch LED ON
        digitalWrite (relayPin1, LOW);
        digitalWrite (relayPin2, LOW);      // switch relay ON
        
       
        mode = 3;
      }
      break;
    case 3://------------------------ I'm in OFF mode, w8 4 keyrelease
      if ( bouncer.read() == LOW )
      
      { 
        
        digitalWrite (ledPin, LOW);     // switch LED ON
        digitalWrite (relayPin1, LOW);
        digitalWrite (relayPin2, HIGH);      // switch relay ON
        
        
        mode = 0;
        
      }
      break;
  }//switch


}//loop

Bounce2.zip (60.4 KB)

You need an extra case after the first press that waits a few milliseconds (see blinkWithoutDelay technique for not blocking) to see if there is a second press (off then on cycle). If no second press then return single press after the timeout. 200ms is the threshold of human perception. If you wait longer people will perceive a delay if a single press.

Same technique can be used to detect a long press or auto repeat.

This is not an issue of the case statement - that part is easy.

What you need first is code which sets a variable (let's call it switchTimes) which can be 0 (for no press) 1 (for a single press) or 2 (for a double press). Then you can do if (switchTimes == 1) etc (or switch (switchTimes) ).

I would create a short function to do that which, as @marco_c says, reads the number of switch presses within whatever time is allowed for a double press.

...R

I found another code which does multiple features with a single button written by Jeff Saltzman. The only thing I cannot seem to modify is when the Positive Relay Pin is HIGH, the Negative should be LOW and visa versa for a single click. Can someone help me wrap my head around how to control two pins with one click?

/* 4-Way Button:  Click, Double-Click, Press+Hold, and Press+Long-Hold Test Sketch

By Jeff Saltzman
Oct. 13, 2009

To keep a physical interface as simple as possible, this sketch demonstrates generating four output events from a single push-button.
1) Click:  rapid press and release
2) Double-Click:  two clicks in quick succession
3) Press and Hold:  holding the button down
4) Long Press and Hold:  holding the button for a long time 
*/

#define buttonPin 5        // analog input pin to use as a digital input
#define ledPin1 3          // digital output pin for LED 1
#define ledPin2 4          // digital output pin for LED 2
#define ledPin3 0          // digital output pin for LED 3
#define ledPin4 13          // digital output pin for LED 4

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

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

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

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

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

void clickEvent() {
   ledVal1 = !ledVal1;
   digitalWrite(ledPin1, ledVal1);
}
void doubleClickEvent() {
   ledVal2 = !ledVal2;
   digitalWrite(ledPin2, ledVal2);
}
void holdEvent() {
   ledVal3 = !ledVal3;
   digitalWrite(ledPin3, ledVal3);
}
void longHoldEvent() {
   ledVal4 = !ledVal4;
   digitalWrite(ledPin4, ledVal4);
}
//=================================================
//  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 = 1000;        // ms hold period: how long to wait for press+hold event
int longHoldTime = 3000;    // ms long 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
boolean longHoldEventPast = false;// whether or not the long 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;
       longHoldEventPast = 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;
       }
       // Trigger "long" hold
       if ((millis() - downTime) >= longHoldTime)
       {
           if (not longHoldEventPast)
           {
               event = 4;
               longHoldEventPast = true;
           }
       }
   }
   buttonLast = buttonVal;
   return event;
}

That is a neat piece of code. I have added it to my bookmarks.

Sorry, but I don't understand your question. It doesn't sound like it has anything to do with the button logic.

Have you tried adding your relay code into the clickEvent() function?

...R

I do that same thing, except I use an analog input, so I can have multiple buttons (up to 6 or 8) on a single input, all with that above functionality, plus optional auto-repeat when a button is held down.

Regards, Ray L.

Robin2:
That is a neat piece of code. I have added it to my bookmarks.

Sorry, but I don’t understand your question. It doesn’t sound like it has anything to do with the button logic.

Have you tried adding your relay code into the clickEvent() function?

…R

I have just applied my previous case statement code to clickEvent() but am not getting the results I want.

Here is the revised code:

/* 4-Way Button:  Click, Double-Click, Press+Hold, and Press+Long-Hold Test Sketch

By Jeff Saltzman
Oct. 13, 2009

To keep a physical interface as simple as possible, this sketch demonstrates generating four output events from a single push-button.
1) Click:  rapid press and release
2) Double-Click:  two clicks in quick succession
3) Press and Hold:  holding the button down
4) Long Press and Hold:  holding the button for a long time 
*/

#include <Bounce2.h>

#define buttonPin 1        // analog input pin to use as a digital input
#define relayPin1 3         // digital output pin for relaypin1
#define relayPin2 4         // digital output pin for relaypin2
#define ledPin2 13         // digital output pin for relaypin2
#define ledPin3 0          // digital output pin for LED 1
#define ledPin4 13          // digital output pin for LED 2

// LED variables
boolean relayVal1 = false;    // state of LED 1
boolean relayVal2 = false;   
boolean ledVal2 = false;    // state of LED 2
boolean ledVal3 = false;    // state of LED 3
boolean ledVal4 = false;    // state of LED 4


byte mode = 0;
Bounce  bouncer  = Bounce();

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

void setup() {
   // Set button input pin
   pinMode(buttonPin, INPUT);
   digitalWrite(buttonPin, HIGH );
   // Set LED output pins
   
   pinMode(relayPin1, OUTPUT);
   digitalWrite(relayPin1, relayVal1);
   pinMode(relayPin2, OUTPUT);
   digitalWrite(relayPin2, relayVal2);
   pinMode(relayPin2, OUTPUT);
   pinMode(ledPin2, OUTPUT);
   digitalWrite(ledPin2, ledVal2);
   pinMode(ledPin3, OUTPUT);    
   digitalWrite(ledPin3, ledVal3);
   pinMode(ledPin4, OUTPUT);    
   digitalWrite(ledPin4, ledVal4);
   
 // After setting up the button, setup the object
 bouncer .attach( buttonPin );
 bouncer .interval(5);
   
   
   
 
}

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

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

void clickEvent() {
  // relayVal1 = !relayVal1;
  // digitalWrite(relayPin1, relayVal1);
   
   
  bouncer.update ( );    // Update the debouncer for the push button
  switch( mode )
  {
    case 0://------------------------ I'm off and in restmode
      if ( bouncer.read() == HIGH )
      { 

        digitalWrite (ledPin2, LOW);  
        digitalWrite (relayPin1, LOW);
        digitalWrite (relayPin2, HIGH);  // switch relay ON

                                      
        mode = 1;
      }
      break;
    case 1://------------------------ I'm in ON mode, w8 4 keyrelease
      if ( bouncer.read() == LOW )
      
      { 
        digitalWrite (ledPin2, LOW);
        digitalWrite (relayPin1, HIGH);
        digitalWrite (relayPin2, LOW);  // switch relay ON
        
      
        mode = 2;
      }
      break;
    case 2://------------------------ I'm ON and in restmode
      if ( bouncer.read() == HIGH )
      { 
        
        digitalWrite (ledPin2, HIGH);
        digitalWrite (relayPin1, LOW);
        digitalWrite (relayPin2, LOW);      // switch relay ON
        
       
        mode = 3;
      }
      break;
    case 3://------------------------ I'm in OFF mode, w8 4 keyrelease
      if ( bouncer.read() == LOW )
      
      { 
        
        digitalWrite (ledPin2, LOW);
        digitalWrite (relayPin1, LOW);
        digitalWrite (relayPin2, HIGH);      // switch relay ON
        
        
        mode = 0;
        
      }
      break;
  }//switch
   
   
   
   
   
   
   
   
   
}
void doubleClickEvent() {
   ledVal2 = !ledVal2;
   digitalWrite(ledPin2, ledVal2);
}
void holdEvent() {
   ledVal3 = !ledVal3;
   digitalWrite(ledPin3, ledVal3);
}
void longHoldEvent() {
   ledVal4 = !ledVal4;
   digitalWrite(ledPin4, ledVal4);
}

//=================================================
//  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 = 1000;        // ms hold period: how long to wait for press+hold event
int longHoldTime = 3000;    // ms long 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
boolean longHoldEventPast = false;// whether or not the long 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;
       longHoldEventPast = 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;
       }
       // Trigger "long" hold
       if ((millis() - downTime) >= longHoldTime)
       {
           if (not longHoldEventPast)
           {
               event = 4;
               longHoldEventPast = true;
           }
       }
   }
   buttonLast = buttonVal;
   return event;
}

I do that same thing, except I use an analog input, so I can have multiple buttons (up to 6 or 8) on a single input, all with that above functionality, plus optional auto-repeat when a button is held down.

Regards,
Ray L.

That is something that I have been looking into for a dual button set up. You just have multiple pulldown resistor values to create a voltage divider on the analog pin, correct?

cterjesen: That is something that I have been looking into for a dual button set up. You just have multiple pulldown resistor values to create a voltage divider on the analog pin, correct?

Basically, yes. There are about a million different resistor networks you can use, depending on what you want to do. Simplest case for two switches is three resistors in series between +5V and GND. The A/D pin is connected to the bottom of the top-most resistor. One switch shorts out the lower resistor, the second one shorts out the two lower resistors. But you can have more elaborate resistor networks that allow 6-8 switches on a single pin.

Regards, Ray L.

cterjesen:
I have just applied my previous case statement code to clickEvent() but am not getting the results I want.

When you went to the trouble of getting a perfectly good piece of code written by Jeff Saltzman why did you cock it up with the unnecessary bounce library?

…R

Robin2: When you went to the trouble of getting a perfectly good piece of code written by Jeff Saltzman why did you cock it up with the unnecessary bounce library?

...R

It was an attempt at something I do not quite fully understand yet. Can you help correct my mistake? All I want to do is modifity what happens in the clickEvent().

cterjesen: It was an attempt at something I do not quite fully understand yet. Can you help correct my mistake? All I want to do is modifity what happens in the clickEvent().

Take the Salzman code (only) and add code into the clickEvent() function for what you want to happen when there is a click.

The Arduino is a great system for learning-by-doing.

...R

I just found this excellent code snippet, and adapted it for my ongoing radio project! Since I only want 2 functions (CLICK and HOLD) for one button, and to make it work with the radio project’s existing LCD (instead of LED’s) I modded the code snippet, thus:

/* 2-Way Button:  Click or Press+Hold, Test Sketch

By Jeff Saltzman (V1.0 originally 4 modes) 10/13/09
V2.0 Modded by Willie... 12/16/16 (Two modes, tested & working!)

This sketch demonstrates generating 2 output events from a single push-button.
1) Click:  rapid press and release
2) Press and Hold:  holding the button down 1 second
*/
#include <LiquidCrystal.h> //LCD control library
// LCD USEAGE with 2-line ctrl: LiquidCrystal lcd(rs, enable, d4, d5, d6, d7) Tie R/W LOW on LCD
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

#define buttonPin 11        // input pin for button

// Mode variables
boolean modeVal1 = false;    // state of Mode 1 (Tune / Mem)
boolean modeVal2 = false;    // state of Mode 2 (Mem mode=Store Mem / Tune mode = ignore)


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

void setup() {
  lcd.begin(20, 4); // Initialize the LCD (20 Cols, 4 Rows)
  
   // Set button input pin
pinMode(11, INPUT_PULLUP);

}

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

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

void clickEvent() {
   modeVal1 = !modeVal1;
     lcd.setCursor(0, 0);
   if (modeVal1==1) lcd.print ("Mode: TUNE");
   else lcd.print ("Mode: MEM ");
}
void holdEvent() {
   modeVal2 = !modeVal2;
     lcd.setCursor(0, 1);
   if (modeVal2==1 && modeVal1==0) lcd.print ("STORE");
   else lcd.print ("Read ");
}
//=================================================
//  MULTI-CLICK:  One Button, Multiple Events

// Button timing variables
int debounce = 10;          // ms debounce period to prevent flickering when pressing or releasing the button
int DCgap = 280;            // max ms between clicks for a double click event
int holdTime = 1000;        // 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;
       }
   }
   // 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 = 2;
           waitForUp = true;
           ignoreUp = true;
           DConUp = false;
           DCwaiting = false;
           holdEventPast = true;
       }
 
   }
   buttonLast = buttonVal;
   return event;
} // End of checkButton()

When I first integrated this into my existing radio control sketch, the compiler would fail. Just on a lark, I checked the version of the IDE, and it’s been updated a few times since my 1.6.8! So I downloaded and installed 1.6.13, and behold! It compiles and works! :slight_smile: Now, I just need to make this click/hold actually do something with the radio, not just change the words on the LCD. :wink:

I hope this simpler (2-mode) snippet is helpful to someone! :slight_smile: