Code to detect single or double click

Hi All, does anyone know the logic behind how a PC can detect single and double click.

If anyone can share the logic how it works and to implement that logic in C-language.

Both single and double click logic should be in 1 code itself

Take the time at button press and the time at button release,
decide on the difference whether that should be a short or long click.

Double presses are little harder, because you have to delay the signaling of the short press,
if it is followed (again compare the times of the events) directly by another short press.

Everybody that knows about digitalRead and millis can code such an algorithm.

There are a lot of libraries out there that support the features you request,
you can search for them yourself.

start a timer on a button release.
if a 2nd button press is detected before the timer expire, disable the timer and report a dbl click
if the timer expires, disable the timer and report a single click

I solve problems like this with timing diagrams. Then you are simulating input signals and showing how they would/should be interpreted.

The essence of it, is defining things we take for granted, button clicks are a great example. This is why you can be asking about such a simple thing.

Ask yourself first, what absolutely defines a single button click? Then move on to the double.

Is this a school assignment? It has the odor of a challenge question.

No no no.

Do not delay giving me the single click outcome of a user interaction while you sit around waiting to see if there’s another click.

I remind you (not my words, 17 seconds of googling):

Typically, a single click initiates a user interface action and a double-click extends the action. For example, one click usually selects an item, and a double-click edits the selected item.

That this basic brilliant principle is violated all over the place is no excuse. It is bad user interface design. Bad.

Give me the single click instantly the switch is closed. Or as soon as I check to see if.

The fact that another click comes in time for the first (already doing something!) and the second to be handled as a double click is a future event, which may cause me to extend the action initiated by what turns out to have not been a single click.

Tog on Interface.

a7

Sure, then there is the quagmire of the click-and-drag, where you hold a click too long on a folder that you intended to open, your hand slips, and it's deposited in some other folder but you're not sure which one. :slight_smile:

Has anyone here ever offered IT support to relatives? :slight_smile:

1 Like

consider

const byte pinBut = A1;
byte butLst;

enum { None, SingleClick, DoubleClick };

// -----------------------------------------------------------------------------
int
chkButton (void)
{
    const  unsigned long ButTimeout  = 250;
    static unsigned long msecLst;
           unsigned long msec = millis ();

    if (msecLst && (msec - msecLst) > ButTimeout)  {
        msecLst = 0;
        return SingleClick;
    }

    byte but = digitalRead (pinBut);
    if (butLst != but)  {
        butLst = but;
        delay (10);           // **** debounce

        if (LOW == but)  {   // press
            if (msecLst)  { // 2nd press
                msecLst = 0;
                return DoubleClick;
            }
            else
                msecLst = 0 == msec ? 1 : msec;
        }
    }

    return None;
}

// -----------------------------------------------------------------------------
void
loop ()
{

    switch (chkButton ())  {
    case SingleClick:
        Serial.println ("single");
        break;

    case DoubleClick:
        Serial.println ("double");
        break;
    }
}

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

    pinMode (pinBut, INPUT_PULLUP);
    butLst = digitalRead (pinBut);
}

I did consider the code in #7 by running it in the wokwi.

EDIT: the code in #7 has been modified, rendering some of the statements below inoperative.

Only by using a debounced pushbutton could I get it to functional properly. This may be a wokwi problem. All I got were lotsa double click reports.

( attribute "bounce": "0" for the wokwi pushbutton component, very handy).

Once debounced externally:

The code reports a double click if you double click.

It reports a single click 250 ms after the button goes down. That is excruciating, especially the more brief one's click on the button.

In real life, I was able to see double clicks where a single click was issued, and click reports created when the button was released.

I conclude that the code is being confused by contact bouncing. If the code works better or as hoped for anyone, they are using better buttons than I am testing.


A single click report should come immediately the button goes down.

A double click report should come after a temporally proximate single click report. At the double click interval setting.

a7

Could you just share me the rough code for single and double click events ?!

then how do you recognize a double click ???

doupleClickEvent arose if counter==2 in timeframe X. :nerd_face:

but you shouldn't report a single click first

What do you think about this code?
So I added logic about the bounce so no need to set it up in wokwi.
When you do a single click it in the state of maybe a single click. If nothing is pressed after the set time (250ms) then its a confirmed single click.
If another click is pressed then its not a single its a double click.
Test it though this link SingleOrDouble.ino - Wokwi Arduino Simulator
or try the code below:

const byte pinBut = A1;
byte butLst;

//GERRY MOD
//enum { None, SingleClick, DoubleClick };
enum { None, SingleClick, DoubleClick, YesSingle};

// -----------------------------------------------------------------------------
int
chkButton (void)
{
  const  unsigned long ButTimeout  = 250;
  static unsigned long msecLst;
  unsigned long msec = millis ();
  //GERRY MOD
  const int debDuration = 100;
  static unsigned long  debStartTime = 0;

  if (msecLst && (msec - msecLst) > ButTimeout)  {
    msecLst = 0;
	//GERRY MOD
    //return SingleClick;	
    return YesSingle;
  }

  byte but = digitalRead (pinBut);
  if (butLst != but)  {
    //GERRY MOD
    if (millis() - debStartTime < debDuration) {
      return None;
    }
    debStartTime = millis();
	
    butLst = but;

    if (LOW == but)  {   // press
      if (msecLst)  { // 2nd press
        msecLst = 0;
        return DoubleClick;
      }
      else {
        msecLst = 0 == msec ? 1 : msec;
		//GERRY MOD
        return SingleClick; //SINGLE?
      }
    }
  }

  return None;
}

// -----------------------------------------------------------------------------
void
loop ()
{

  switch (chkButton ())  {
    case SingleClick:
      Serial.println ("single?");
      break;

    case DoubleClick:
      Serial.println ("Its double");
      break;

    //GERRY MOD
    case YesSingle:
      Serial.println ("YesSingle");
      break;

  }
}

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

  pinMode (pinBut, INPUT_PULLUP);
  butLst = digitalRead (pinBut);
}

a 10msec delay is usually good enough to debounce a mechanical switch. i'm sorry i forgot to include it in the code i posted. i've amended the code i posted to include it

msecLst = 0 == msec ? 1 : msec;

Can u pls explain me this line

I just copied that code. Haha. From what I know it translates to the following:

if (0 == msec) {
  msecLst =  1;
} else {
  msecLst =  msec;
}

I'm not sure why gcjr wanted msecLst = 1; and not msecLst = 0;

I disagree. Somewhere back there was an explanation of that. It's the application layer that is supposed to direct the commands to the appropriate actions. For example, if I click on an icon, it becomes highlighted immediately. If I double click on an icon, it is first immediately highlighted, then launches on the second click. The UI or application is not supposed to present double click actions that don't transition through a single click response first.

After a single click input is detected, it is not possible to determine whether or not it is a double click until some interval has passed. Waiting until that interval has passed, to make a determination of single or double click type, results in sluggish response time.

So, the proper way is for the UI to supply both events, a single click and then a double click, and the application responds appropriately. To implement any input event that is linked to a double click, where the preceding single click would have undesirable side effects, would be a bad design.

1 Like

you're right. the OP asked about a PC.
thought this was an Arduino forum

Well, I didn't worry about that. I'm sort of thinking that there is, or should be some cross over in concepts between the two spheres...

Neither policy is mandatory. It just means different outcomes. Postponed differentiation is appropriate when the single/double click outcomes must be exclusive, but the delay is tolerable. Separate response is more appropriate when the outcomes are compatible, since there won't be any delay for a single click.

I'm trying to think of more embedded system style examples, but I'm coming up blank. Need the second coffee for that.

Suppose I have a digital clock, a push button will increment one of the digits in some configuration state... as I push, the numbers on the display increment 1,2,3 etc. and I want that to happen swiftly. I want it to reset to zero if I double press. In this case, there is no advantage to waiting the double click time out before incrementing the display.

If the display currently shows "7", and I respond with a double click, it will first increment to "8", then to "0". That seems fine from a UI perspective so I give the implementation the okay.

You do have to avoid or use deferred response if the single click response can not precede the double - say, a single click will explode some dynamite and a double click will disarm it.

1 Like

The dynamite example was one I was about to give too, accept for launching missiles. It seems after some thought that single click events should in some sense always be 'passive' such as giving focus or selecting something.

I hadn't thought about this before so it's great to learn about this way of thinking about single/double press events.