0

i hope to get some help with my code.

I am trying to distinguish between a single press and a quick double press of the button with debouncing.

I am making some silly mistake in my code and i can't find out the problem. If I double press the button, both 'single' and 'double' gets printed on the serial monitor.

Thanks in advance for some help.

const int buttonPin = 5;
unsigned long lastPressTime;
int buttonState = 0;
int singlePress = 0;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);

  lastPressTime = millis();
  Serial.begin(9600);
}

void loop() {
  if (!digitalRead(buttonPin) == HIGH && buttonState == 0) {
    unsigned long currentPressTime = millis();

    //two presses within 500ms
    if (currentPressTime - lastPressTime < 500) {
      singlePress = 0;
      //Do double press action
      Serial.println("double");
    } else {
      singlePress = 1;
    }

    //update the last pressed time
    lastPressTime = currentPressTime;
    buttonState = 1;
  } else if (!digitalRead(buttonPin) == LOW) {
    buttonState = 0;
  }

  //check if 500ms have passed with no second button press
  if (singlePress == 1 && lastPressTime  - millis() > 500) {
    singlePress = 0;
    //DO single press stuff
    Serial.println("single");
  }
}

4
  • why not use a library? for example Bounce2 Commented Oct 31, 2022 at 18:55
  • @Juraj...I know there are some library's for this type of situations. I know the Bounce2 and generally I use the OneButton library. In this case I am trying to make it work without the use of a library. Commented Oct 31, 2022 at 19:13
  • 1
    both 'single' and 'double' gets printed on the serial monitor .... you are almost there ... add code to pick one, then print Commented Oct 31, 2022 at 21:52
  • 2
    This looks odd ` lastPressTime - millis() > 500` since lastPressTime will be less than millis() but I don't think that is your only problem. I restructured your solution as a more explicit finite state machine and put it in a simulator: wokwi.com/projects/347070000856564307 Commented Nov 1, 2022 at 0:17

2 Answers 2

1

The problem is this statement:

lastPressTime  - millis() > 500

Because time is stored as an unsigned long, this test as it is written now is always true.

You want to measure the time since the last press, so you should subtract the timestamp of the last press from the current time. Replace it with:

millis() - lastPressTime > 500

And the code will run fine.

1
  • 3
    If your "answer" is derived 100% from someone else's previous comment pointing out an obvious error, it would be courteous to at least acknowledge it. Even better would be to wait until the originator has had a chance to correct his code and report the results before making a statement such as "And the code will run fine." . (I don't believe it does) Commented Nov 1, 2022 at 8:07
0

Thank you all for your help...

Definitely the problem in my code, to get the one click and double click working like @mhopeng wrote, was this statement:

lastPressTime  - millis() > 500

changing it to this

millis() - lastPressTime > 500

maked it work.

But what i realize is that my approach is not enough responsive. Using the approach of @6v6gt is much better than mine. The reaction is much more responsive and smoother.

// note the simulation speed is about one third of real time
// adjust timings if using on real hardware.

const int buttonPin = 5;
unsigned long inStateAtMs = millis() ;
int buttonState ;  // 0 = waiting for press, 1= waiting for release 2= differentiate single or double 3= wait button release (double)

void setup() {
 pinMode(buttonPin, INPUT_PULLUP);
 Serial.begin(9600);
}

void loop() {

 if ( buttonState == 0 ) {
   // wait for button press
   if ( millis() - inStateAtMs > 100 ) {
     if (!digitalRead(buttonPin)) {
       buttonState = 1 ;
       inStateAtMs = millis();
     }
   }
 }
 else if ( buttonState == 1 ) {
   // wait for stable button release 1
   if ( millis() - inStateAtMs > 100 && digitalRead(buttonPin) ) {
     buttonState = 2 ;
     inStateAtMs = millis();
   }
 }
 else if ( buttonState == 2 ) {
   // differentiate between single and double press
   if ( millis() - inStateAtMs > 400 ) {
     // timeout - is a single press
     Serial.println("single");
     buttonState = 0 ;
     inStateAtMs = millis() ;
   }
   else if (  ( ! digitalRead(buttonPin) ) && (millis() - inStateAtMs > 100 ) ) {
     // got second press within timeout - double
     Serial.println("double");
     buttonState = 3 ;
     inStateAtMs = millis() ;
   }
 }
 else if ( buttonState == 3 ) {
   // wait for stable button release 2
   if ( millis() - inStateAtMs > 100 && digitalRead(buttonPin) ) {
     buttonState = 0 ;
     inStateAtMs = millis();
   }
 }
}

Thanks again all of you for the help

2
  • 1
    I'm glad you've got something which works for you. I put your original sketch, but with the correction, into the same simulator and it does not appear to work. However, it [the simulator] appears to implement an exaggerated button bounce which you may not encounter on a physical Arduino. It is here if you want to play with it: wokwi.com/projects/347070013621928531 Commented Nov 1, 2022 at 10:53
  • 2
    I noticed that in the simulator my code does not work, but on the physical Arduino it does. Commented Nov 1, 2022 at 10:59

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.