Debouncing de bouncing switches!

Being a beginner (please note) I am having trouble with the switches on my greenhouse controller.

As I need at least three switches, I am finding trouble with the debounce routine.

Initally, the program has only to respond to a reset switch each time it is polled in the loop function.

It has to be debounced of course, then a series of questions are displayed on an LCD, each with a 'yes' or

'no' response - so another two buttons are required, again needing debouncing.

I have seen the 'new' debounce examples but they seem to be for two buttons only and I wish I

understood them. I have also downloaded another debounce library: http://www.arduino.cc/playground

/Code/Buttons, but I am not clear how to add it.

I have deleted the lash-up I concocted based on the old library example and it is irrecoverable so I can't

attach it.

A gentle, there, there, there, reply would be appreciated.

Is your problem that the switches "bounce" - i.e. that one switch press gives several spurious readings?

Or is the problem that you need to detect when the switch changes from (say) HIGH to LOW and subsequent LOW readings must be ignored until the switch goes HIGH (i.e. is released) (assuming you are using INPUT_PULLUP)

To deal with the first problem you just need a short interval between successive digitalRead()s as illustrated in Several Things at a Time

To deal with the second problem you need to keep track of the state of the switch and only act if the previous state was HIGH and the current state is LOW.

And it may be necessary to deal with both problems.

...R

Or if it's both or one of the two, make it yourself easy and grab a library like Bounce2. Don't have to reinvent the wheel, easy to use, easy to scale for multiple buttons and clear and easy code.

I would only advice to make your own debounce / state change detect code to learn what's going on. After that, a library (downloaded or self written) is so much easier and cleaner to use.

The answer, Robin is simple - 'Yes'.

I've been bread-boarding the circuit which includes a Nano, a humidity sensor, an RTC, a temperature

sensor, a moisture detector, a light sensor and a fan speed switcher using a quad comparator driven by

analogWrite.

The RTC is the problem but I have got round programming it by incrementing the variables (minute,

hour, day, month year) until they were right. It worked well enough while I was using patch wires as

switches but as soon as I put in micro-switches things went wrong. Spurious inputs, yes but the routine

went haywire and it would not display as before.

Though unfinished as far as moisture sensing is concerned, the circuit is promising.

The switch reading routine was the first I tackled and I have learned a little since then - I wish I could

remember all of it!!!!

Septillion's suggestion to use libraries is in fact the way I work because I am not trained in the use of

C++.

What it comes down to is how to debounce three switches. Do I have to write a separate debounce

routine for each time a switch is used or can I use a single debounce function?

Give us a clue!

Funny - I find that the libraries offer a one-size-fits-all approach that does not permit optimization/simplification for a specific use case, and foster the development of bugs when people use them without understanding them. Often, it’s akin to using a bazooka to kill a fly, and you have to store the bazooka in your limited flash space.

Anyway - you can also debounce in hardware. One of the classics is just putting a 0.1uf cap between switch output and ground (you can adjust the values of cap to tweak how much debouncing you get) - this is often good enough. There are even a few dedicated IC’s for switch debouncing.

Adrifran39:
Give us a clue!

I thought I had with the link in Reply #1

…R

Perhaps that's what I have found with the library debounce DrAzzy. Sorry Robin2, I missed that - looking into it now but It will take me a moment or two!

You might be interested in using Mr. Gammon’s Switch Manager library here:
http://gammon.com.au/Arduino/SwitchManager.zip

End of reply #1
http://www.gammon.com.au/forum/?id=11955

Example of use:

/*SwitchManager skeleton 
 LarryD
 
 This is sketch is to introduce new people to the SwitchManager library written by Nick Gammon
 
 The library handles switch de-bouncing and provides timing and state change information in your sketch.
 The SwitchManager.h file should be placed in your libraries folder, i.e.
 C:\Users\YouName\Documents\Arduino\libraries\SwitchManager\SwitchManager.h
 You can download the library at:
 http://gammon.com.au/Arduino/SwitchManager.zip    Thank you Nick!
 
 In this example we have 2 normally open (N.O.) switches connected to the Arduino - increment and decrement.
 The increment switch will also be used as a "Reset" switch if pressed for more than two seconds.
 The two switches are connected between GND (0 volts) and an Arduino input pin.
 The library enables pull-up resistors for your switch inputs.
 Pushing a switch makes its pin LOW. Releasing a switch makes its pin HIGH.
 
 The SwitchManager library provides 10ms de-bounce for switches. 
 i.e. enum { debounceTime = 10, noSwitch = -1 };
 If you need more time, edit the SwitchManager.h file
 i.e. enum { debounceTime = 50, noSwitch = -1 }; //here it is changed to 50ms
 */

#include <SwitchManager.h>             
//object instantiations
SwitchManager myIncSwitch;
SwitchManager myDecSwitch;

unsigned long currentMillis;
unsigned long heartBeatMillis;
unsigned long heartFlashRate  = 500UL; // time the led will change state       
unsigned long incShortPress   = 500UL; // 1/2 second
unsigned long incLongPress    = 2000UL;// 2 seconds 
unsigned long decShortPress   = 500UL; // 1/2 second

const byte heartBeatLED       = 13;
const byte incSwitch          = 4; //increment switch is on Arduino pin 4
const byte decSwitch          = 5; //decrement switch is on Arduino pin 5

int myCounter;

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

void setup()
{
  Serial.begin(9600);

  //gives a visual indication if the sketch is blocking
  pinMode(heartBeatLED, OUTPUT);  

  myIncSwitch.begin (incSwitch, handleSwitchPresses); 
  myDecSwitch.begin (decSwitch, handleSwitchPresses);
  //the handleSwitchPresses() function is called when a switch changes state

} //                   E N D  O F  s e t u p ( )

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

void loop()
{
  //leave this line of code at the top of loop()
  currentMillis = millis();

  //***************************
  //some code to see if the sketch is blocking
  if (CheckTime(heartBeatMillis, heartFlashRate, true))
  {
    //toggle the heartBeatLED
    digitalWrite(heartBeatLED,!digitalRead(heartBeatLED));
  }

  //***************************
  //check to see what's happening with the switches
  //"Do not use delay()s" in your sketch as it will make switch changes unresponsive 
  //Use BlinkWithoutDelay (BWD) techniques instead.
  myIncSwitch.check ();  
  myDecSwitch.check (); 

  //***************************
  //put other non-blocking stuff here


} //                      E N D  O F  l o o p ( )


//======================================================================
//                          F U N C T I O N S
//======================================================================


//                        C h e c k T i m e ( ) 
//**********************************************************************
//Delay time expired function
//parameters:
//lastMillis = time we started
//wait = delay in ms
//restart = do we start again  

boolean CheckTime(unsigned long  & lastMillis, unsigned long wait, boolean restart) 
{
  //has time expired for this task?
  if (currentMillis - lastMillis >= wait) 
  {
    //should this start again? 
    if(restart)
    {
      //yes, get ready for the next iteration
      lastMillis += wait;  
    }
    return true;
  }
  return false;

} //                 E N D   o f   C h e c k T i m e ( )


//                h a n d l e S w i t c h P r e s s e s( )
//**********************************************************************

void handleSwitchPresses(const byte newState, const unsigned long interval, const byte whichPin)
{
  //  You get here "ONLY" if there has been a change in a switches state.

  //When a switch has changed state, SwitchManager passes this function 3 arguments:
  //"newState" this will be HIGH or LOW. This is the state the switch is in now.
  //"interval" the number of milliseconds the switch stayed in the previous state
  //"whichPin" is the switch pin that we are examining  

  switch (whichPin)
  {
    //***************************
    //are we dealing with this switch?
  case incSwitch: 

    //has this switch gone from LOW to HIGH (gone from pressed to not pressed)
    //this happens with normally open switches wired as mentioned at the top of this sketch
    if (newState == HIGH)
    {
      //The incSwitch was just released
      //was this a short press followed by a switch release
      if(interval <= incShortPress) 
      {
        Serial.print("My counter value is = ");
        myCounter++;
        if(myCounter > 1000)
        {
          //limit the counter to a maximum of 1000
          myCounter = 1000; 
        }
        Serial.println(myCounter);
      }

      //was this a long press followed by a switch release
      else if(interval >= incLongPress) 
        //we could also have an upper limit
        //if incLongMillis was 2000UL; we could then have a window between 2-3 seconds
        //else if(interval >= incLongMillis && interval <= incLongMillis + 1000UL) 
      {
        //this could be used to change states in a StateMachine
        //in this example however, we will just reset myCounter
        myCounter = 0;
        Serial.print("My counter value is = ");
        Serial.println(myCounter);
      }

    }

    //if the switch is a normally closed (N.C.) and opens on a press this section would be used
    //the switch must have gone from HIGH to LOW 
    else 
    {
      Serial.println("The incSwitch was just pushed");
    } 

    break; //End of case incSwitch

    //*************************** 
    //are we dealing with this switch?
  case decSwitch: 

    //has this switch gone from LOW to HIGH (gone from pressed to not pressed)
    //this happens with normally open switches wired as mentioned at the top of this sketch
    if (newState == HIGH)
    {
      //The decSwitch was just released
      //was this a short press followed by a switch release
      if(interval <= decShortPress) 
      {
        Serial.print("My counter value is = ");
        myCounter--;
        if(myCounter < 0) 
        {
          //don't go below zero
          myCounter = 0;
        }
        Serial.println(myCounter);
      }

    }

    //if the switch is a normally closed (N.C.) and opens on a press this section would be used
    //the switch must have gone from HIGH to LOW
    else 
    {
      Serial.println("The decSwitch switch was just pushed");
    } 

    break; //End of case decSwitch

    //*************************** 
    //Put default stuff here
    //default:
    //break; //END of default

  } //End switch (whichPin)

} //      E n d   o f   h a n d l e S w i t c h P r e s s e s ( )


//======================================================================
//                      E N D  O F  C O D E
//======================================================================

DrAzzy, You wrote about using Capacitors with switches. I hadn't thought of that. It worries me though what is the upper limit on cap value. What is the input impedance of a digital input on the Nano? A meg or more? One more, can you identify the IC to which you referred

http://www.labbookpages.co.uk/electronics/debounce.html

MC14490 is the one I recently saw that made me say that. Eye-wateringly expensive. I really wonder who their intended market is at that price point…

DrAzzy:
Funny - I find that the libraries offer a one-size-fits-all approach that does not permit optimization/simplification for a specific use case

Yes of course. Using a library (or a higher level programming language even) is a trade off between optimized and easy to use. And yes, there are many libraries that have things build in no one but a few use etc. But like you use digitalRead library to read the pins most of the time (because he, thats already a pretty heavy library for just reading a pins state) I think it's fine to use a Libray for the buttons. And I find Bounce2 pretty easy to use all round and you don't have all the clutter of variables in your code to do so. Yes, you can do it different. And one way is maybe more efficient in some situations etc but overall it's a fine library. Okay, most of the time I use a fork of that library that doesn't use dgitalRead and doesn't use a long but the gain is limited and makes the code a whole lot more difficult to read for a n00b beginner.

I found the MC11190 on eBay at about seventy five cents but the SMD seems dearer, DrAzzy. I cant use SMD because my eyesight and fingers are too feeble these days!!!

As for my sketch, I have deleted the two functions I wrote dealing with switches and resetting the RTC putting in their place one based on Switch/case. In compiling I get the error ‘expected constructor, destructor or type conversion before ‘;’ token’, relating to the command ‘lcd.begin(20.4);’ in voidSetup()

This command hasn’t been changed in any way so I assume the problem lies in the function checkReset(). There are doubtless errors in the code but the compiler prevents me identifying them. I anticipate it has something to do with ‘curly braces’

Can anyone help? I attach relevant code but the entire sketch is over 600 lines long.

//SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS

void setup(){

Wire.begin();      

lcd.begin(20,4);       //here is the prob!!
Serial.begin(9600);
dht.begin();
lightMeter.begin();
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);

pinMode(2,OUTPUT); //Heat
pinMode(6,INPUT);  //reset
pinMode(7,INPUT);  //the no switch
pinMode(8,INPUT);  //the yes switch
pinMode(13,OUTPUT); //Rain

}  //End Setup

//LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL

void loop(){
   if(digitalRead(Reset)==HIGH)checkReset(); 
   displayTime();
   heating();
   humidity();
   //displayTime();   //only necessary if accurate seconds display is needed
   light();
   
   atmosphere();
}

//BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB

checkReset(){
   
    lcd.setCursor(0,2);
       lcd.clear();
          
              lcd.print(" Change Date/Time?");
                  lcd.setCursor(0,3);
                      lcd.print("No               Yes"); 
                      
   wait=millis();                  
    
   while (digitalRead(Yes)==LOW &&(digitalRead(No)==LOW){  //wait 5 seconds for switch press
      n=digitalRead(No);
         if((millis()-wait>5000 || n==HIGH)return;
         }
     
   RS=1;
   setTime();

        }
//TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
void setTime(){
  switch(RS){
    case 1:  //year
    
        lcd.display();  //necessary?
        lcd.home();  //necessary?
  lcd.setCursor(0,0);
        lcd.clear();
       

            lcd.print("Year is 20");
            lcd.print(year);
            lcd.setCursor(0,1);
            lcd.print("Is this correct?");
            lcd.setCursor(0,3);
            lcd.print("No               Yes");

            wait=millis();                  
    
                while (digitalRead(Yes)==LOW && digitalRead(No)==LOW || (millis()-wait)>10000){  //wait 10 seconds for switch press
                    n=digitalRead(No);
                        y=digitalRead(Yes);
                                 if(n==HIGH){
                                    ++year;
                                        if (year>20)year=10; //to limit year range
                                            setTime();
                                }
                                                if(y==HIGH){
                                                  RS=2;
                                                  setTime();
                                                }
                                                return;
                                          }
 break;
                                          
    case 2: //month
    lcd.setCursor(0,0);
        lcd.clear();
        
 
        lcd.print("Month is ");
        lcd.print(month);
        lcd.setCursor(0,1);
        lcd.print("Is this correct?");
        lcd.setCursor(0,3);
        lcd.print("No               Yes");


                wait=millis();                  
    
                while (digitalRead(Yes)==LOW && digitalRead(No)==LOW || (millis()-wait)<10000){  //wait 10 seconds for switch press
                    n=digitalRead(No);
                        y=digitalRead(Yes);
                            if(n==HIGH){
                                    ++month;
                                        if (month>12)month=1; //to limit month range
                                            setTime();
                                }
                            if(y==HIGH){
                                    RS=3;
                                        setTime();
                                                }
                                                return;
                                          }
 break;

    case 3: //date

     lcd.setCursor(0,0);
        lcd.clear();
       
 

        lcd.print("Day is ");
        lcd.print(date);
        lcd.setCursor(0,1);
        lcd.print("Is this correct?");
        lcd.setCursor(0,3);
        lcd.print("No               Yes");

                wait=millis();                  
    
                while (digitalRead(Yes)==LOW && digitalRead(No)==LOW || (millis()-wait)<10000){  //wait 10 seconds for switch press
                    n=digitalRead(No);
                        y=digitalRead(Yes);
                            if(n==HIGH){
                                    ++date;
                                        if (date>31)date=1; //to limit day range
                                            setTime();
                                }
                            if(y==HIGH){
                                    RS=4;
                                        setTime();
                                                }
                                                return;
                                          }
 break;

 
    case 4:  //hour
    lcd.setCursor(0,0);
        lcd.clear();
        
 
        lcd.print("Hour is ");
        lcd.print(hour);
        lcd.setCursor(0,1);
        lcd.print("Is this correct?");
        lcd.setCursor(0,3);
        lcd.print("No               Yes");


                wait=millis();                  
    
                while (digitalRead(Yes)==LOW && digitalRead(No)==LOW || (millis()-wait)<10000){  //wait 10 seconds for switch press
                    n=digitalRead(No);
                        y=digitalRead(Yes);
                            if(n==HIGH){
                                    ++hour;
                                        if (hour>23)hour=0; //to limit day range
                                            setTime();
                                }
                            if(y==HIGH){
                                    RS=5;
                                        setTime();
                                                }
                                                return;
                                          }
 break;

    case 5:  //minute
    lcd.setCursor(0,0);
        lcd.clear();
        
 
        lcd.print("Minute is ");
        lcd.print(minute);
        lcd.setCursor(0,1);
        lcd.print("Is this correct?");
        lcd.setCursor(0,3);
        lcd.print("No               Yes");
    
        
                wait=millis();                  
    
                while (digitalRead(Yes)==LOW && digitalRead(No)==LOW || (millis()-wait)<10000){  //wait 10 seconds for switch press
                    n=digitalRead(No);
                        y=digitalRead(Yes);
                            if(n==HIGH){
                                    ++minute;
                                        if (minute>59)minute=0; //to limit day range
                                            setTime();
                                }
                            if(y==HIGH){
                                    RS=6;
                                        setTime();
                                                }
                                                return;
                                          }
 break;


    case 6: //reset RTC
 
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(zero);
Wire.write (decToBcd(second));
Wire.write (decToBcd(minute));
Wire.write (decToBcd(hour));
Wire.write (decToBcd(day));
Wire.write (decToBcd(date));
Wire.write (decToBcd(month));
Wire.write (decToBcd(year));
Wire.write(zero);
Wire.endTransmission();
lcd.clear();

byte decToBcd(byte val){
 return((val/10*16)+(val%10));
}
byte bcdToDec(byte val){
  return ((val/16*10)+(val%16));
}
break;
}

Wow, you might want to press Ctrl+T to auto format it. The indentation is a terrible mess.

And next time, if you're sketch is big and you want to give us a snippet try to make a sketch that produces the same error/result but is smaller but IS compileable. That way we can compile it/read the error our self and even better, while you do so you probably find the error yourself.

But uhm, where and how did you define the lcd object? Should look like:

LiquidCrystal lcd(0);

Thanks Septillion. I take your point but this is a large sketch and I may as well write a new one rather than take all the time necessary to make a fragment compile-able.

LiquidCrystal_I2C lcd(0x3F,2,1,0,4,5,6,7);

However, the initial fault turned out to be in the first function after void loop(){ I had written checkReset() rather than void checkReset()!

This enabled me to discover more faults, the current one being in switch case 6, where the use of (decToBcd(second) seems to be interpreted as a variable in the command 'wire.write (decToBcd(second(); There seems no difference to how it appears in the earlier sketch which found no problem. I remember I had trouble with this a long time ago so I can't recall the answer.