Single, double and hold button

I have a single button I want to do different things with based on press.

This is for use on my car - currently it has a mains flash on the indicator stalk - you press it and the lights flash.

I want to use this to control the overdrive gearbox and also the lights. The plan is this:

Single press = overdrive
Double press = toggle mains
Hold = momentary mains (flash)

I’ve worked out the double press, code is a bit of a jumble but I think it makes some sense. I am just a bit lost on how to do the single press and the hold all together.

const int button = 3;
const int led = 13;

long onTime = 0;

int lastReading = LOW;
int bounceTime = 50;
int LEDstate = 0;

long lastSwitchTime = 0;
long switchTime = 500;

void switchLED() {
  if ((millis() - lastSwitchTime) < switchTime) {
    if (LEDstate == 0) { LEDstate = 1; } else { LEDstate = 0; }
    digitalWrite(led, LEDstate);
  }
  lastSwitchTime = millis();
}

void setup() {
   pinMode(button, INPUT);
   pinMode(led, OUTPUT);
   digitalWrite(led, LOW);
}

void loop() {
  
  int reading = digitalRead(button);

  if (reading == HIGH && lastReading == LOW) {
    onTime = millis();
  }
  
  if (reading == LOW && lastReading == HIGH) {
    if ((millis() - onTime) > bounceTime) {
      switchLED();
    }
  }
    
  lastReading = reading;

}

Also it needs a bit of a timeout after a single or double press to stop you pressing it again too quickly

You might find the code here -- http://jmsarduino.blogspot.com/2009/05/click-for-press-and-hold-for-b.html -- quite useful. I adapted his algorithm in a project of mine very successfully.

Thanks for that - the hold or single is reasonably easy… have got that working now with my own code…

Double press is not quite working - as it always fires off a single press at the same time… My logic is broken somewhere… probably plainly obvious…

const int button = 3;
const int led = 13;

long onTime = 0;

int lastReading = LOW;
int bounceTime = 50;
int holdTime = 250;
int hold = 0;

long lastSwitchTime = 0;
long doubleTime = 500;

void setup() {
  pinMode(button, INPUT);
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
  Serial.begin(9600);
}

void loop() {

  int reading = digitalRead(button);

  if (reading == HIGH && lastReading == LOW) {
    onTime = millis();
  }

  if (reading == HIGH && lastReading == HIGH) {
    if ((millis() - onTime) > holdTime) {
      digitalWrite(led, 1);
      hold = 1;
    }
  }

  if (reading == LOW && lastReading == HIGH) {
    if (((millis() - onTime) > bounceTime) && hold != 1) {
      press();
    }
    if (hold = 1) {
      digitalWrite(led, 0);   
      hold = 0;
    }   
  }
  lastReading = reading;
}


void press() {
  if ((millis() - lastSwitchTime) < doubleTime) {
    Serial.println("double press");
  }
  if ((millis() - lastSwitchTime) > doubleTime) {
    Serial.println("single press");
  }
  lastSwitchTime = millis();
}

I haven't scrutinized your algorithm yet, but a quick glance says you'll need the debounce code in there to make it work smoothly. Otherwise you'll get lots of "flicker" in your app, because the switch will bounce around between HIGH and LOW a lot within, say, 10 ms.

I do have debounce in there - though I think hold might be an issue at some point. This code now works… not sure if 100% efficient but seems to be ok. Needed to trigger the single event after a time out rather than just presuming it wasn’t a double press.

const int button = 3;
const int led = 13;

long onTime = 0;

int lastReading = LOW;
int bounceTime = 50;
int holdTime = 250;
int hold = 0;
int single = 0;

long lastSwitchTime = 0;
long doubleTime = 750;

void setup() {
  pinMode(button, INPUT);
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
  Serial.begin(9600);
}

void loop() {

  int reading = digitalRead(button);

  if (reading == HIGH && lastReading == LOW) {
    onTime = millis();
  }

  if (reading == HIGH && lastReading == HIGH) {
    if ((millis() - onTime) > holdTime) {
      digitalWrite(led, 1);
      hold = 1;
    }
  }

  if (reading == LOW && lastReading == HIGH) {
    if (((millis() - onTime) > bounceTime) && hold != 1) {
      onRelease();
    }
    if (hold = 1) {
      digitalWrite(led, 0);   
      hold = 0;
    }   
  }
  lastReading = reading;

  if (single == 1 && (millis() - lastSwitchTime) > doubleTime) {
    Serial.println("single press");
    single = 0;
  }

}


void onRelease() {

  if ((millis() - lastSwitchTime) >= doubleTime) {
    single = 1;
    lastSwitchTime = millis();
    return;
  }  

  if ((millis() - lastSwitchTime) < doubleTime) {
    Serial.println("double press");
    single = 0;
    lastSwitchTime = millis();
  }  



}

You should take a look at using this Button library, created by AlphaBeta, really useful! http://www.arduino.cc/playground/Code/Button :D

That might make the code a bit simpler - but it doesn't really solve it :)

Final code - easy in the end :slight_smile:

Probably needs a bit of a clean up but it works :slight_smile: Need to play with the timings to get the double press and hold times perfect but I think I will do that on the car as it’s down to the switch and how it feels.

const int button = 3;
const int led = 13;

int bounceTime = 50;
int holdTime = 250;
int doubleTime = 500;

int lastReading = LOW;
int hold = 0;
int single = 0;
int LEDstate = 0;

long onTime = 0;
long lastSwitchTime = 0;


void setup() {
  pinMode(button, INPUT);
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
  Serial.begin(9600);
}

void loop() {

  int reading = digitalRead(button);

//first pressed
  if (reading == HIGH && lastReading == LOW) {
    onTime = millis();
  }

//held
  if (reading == HIGH && lastReading == HIGH) {
    if ((millis() - onTime) > holdTime) {
      invertLED();  
      hold = 1;
    }
  }

//released
  if (reading == LOW && lastReading == HIGH) {
    if (((millis() - onTime) > bounceTime) && hold != 1) {
      onRelease();
    }
    if (hold == 1) {
      Serial.println("held");
      digitalWrite(led, LEDstate);
      hold = 0;
    }   
  }
  lastReading = reading;

  if (single == 1 && (millis() - lastSwitchTime) > doubleTime) {
    Serial.println("single press");
    single = 0;
  }

}


void onRelease() {

  if ((millis() - lastSwitchTime) >= doubleTime) {
    single = 1;
    lastSwitchTime = millis();
    return;
  }  

  if ((millis() - lastSwitchTime) < doubleTime) {
    toggleLED();
    Serial.println("double press");
    single = 0;
    lastSwitchTime = millis();
  }  

}

void toggleLED() {
  if (LEDstate == 0) {
    LEDstate = 1;
  } else {
    LEDstate = 0;
  }
  digitalWrite(led, LEDstate);  
} 
  
void invertLED() {
  if (LEDstate == 0) {
  digitalWrite(led, 1);
  } else {
  digitalWrite(led, 0);
  }
}