I ran in to a great snippet of code on a blog that may come in handy. It uses one button and differentiates between Single Click/Double Click/Hold/Long Hold.
I used it to create a "mini-menu" that lets you set RGB values one color at a time, then when you exit the menu it lets you single click-ON, double-click OFF. I only used three button options, though.
Logic wise, I stored the current menu state in a variable and used basic IF statements to change what it did depending on whether menuMode was True/False (The same logic could apply to 0,1,2,3 etc.)
The original had 4 states, click, double-click, hold, long-hold. The blog is here:
And the code I hacked together using it is here:
/* 3-Way Button: Click, Double-Click, Press+Hold RGB Test Sketch
By Jeff Saltzman
Oct. 13, 2009
Modified by Troy Ollom
Jan. 6, 2010
To keep a physical interface as simple as possible, this sketch demonstrates generating three 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
*/
#define buttonPin 2 // analog input pin to use as a digital input
#define redPin 3 // digital output pin for LED 1
#define grnPin 5 // digital output pin for LED 2
#define bluPin 6// digital output pin for LED 3
// RGB LED variables
int rgbX [3] = {
11,2,2}; // RGB Array
int rgbFactor = 11; // Value to factor by.. IE, 100 would provide 100 steps 255 is Max
int randFlicker = 10;
int fadeValue = 0; //tracks the current state of the fade value
boolean fadeDirection = false; // in this case, true means fade up, false means fade down
int colorMode = 0;
boolean menuMode = false;
//=================================================
// RGB Timing Variables
long rgbMillis = 0; // Store millis to keep track of the last time rgb was updated
int rgbInterval = 30; // How often to update the rgb values
//=================================================
void setup()
{
// Set button input pin
pinMode(buttonPin, INPUT);
digitalWrite(buttonPin, HIGH );
// Set LED output pins
pinMode(redPin, OUTPUT);
digitalWrite(redPin, 0);
pinMode(grnPin, OUTPUT);
digitalWrite(grnPin, 0);
pinMode(bluPin, OUTPUT);
digitalWrite(bluPin, 0);
}
void loop()
{
// Get button event and act accordingly
int b = checkButton();
if (b == 1) clickEvent();
if (b == 2) doubleClickEvent();
if (b == 3) holdEvent();
if (menuMode == true)
{
fadeValue =255;
}
if (millis () - rgbMillis >= rgbInterval){ // if it's been 30 ms since last time, lets update the LED
rgbMillis = millis (); //reset so we know the last time
if (fadeDirection == true){ //This is gonna happen is we are fading UP
if (fadeValue <=250) { // If fadeValue is less than or equalls 250
fadeValue = fadeValue +5; // Lets add 5 to it
}
else {
randFlicker = random(41);
fadeValue = 255 - randFlicker; // or else if it's already there, just make it 255 and flicker it there
}
}
if (fadeDirection == false){ //This is gonna happen if we are fading down
if (fadeValue >=5) { // if its over or = to 5
fadeValue = fadeValue - 5; // subtract 5 from it
}
else {
fadeValue = 0; // if it's less than 5 hold it at zero
}
}
updateLED ();
}
}
void updateLED ()
{
analogWrite(redPin, fadeValue * rgbX[0] / rgbFactor); //Now that the math has been done, update the LED
analogWrite(bluPin, fadeValue * rgbX[1] / rgbFactor);
analogWrite(grnPin, fadeValue * rgbX[2] / rgbFactor);
}
//=================================================
// Events to trigger by click and press+hold
void clickEvent() {
if (menuMode == false){
fadeDirection = true;
}
if (menuMode == true){
rgbX[colorMode] = rgbX[colorMode]++;
if (rgbX[colorMode] >= rgbFactor){
rgbX[colorMode] = 0;
}
updateLED();
}
}
void doubleClickEvent() {
if (menuMode == false){
fadeDirection = false;
}
if (menuMode == true) {
colorMode = colorMode ++;
if (colorMode >=3){
colorMode = 0;
}
}
}
void holdEvent() {
menuMode = !menuMode;
colorMode = 0;
}
/*
MULTI-CLICK: One Button, Multiple Events
Oct 12, 2009
Run checkButton() to retrieve a button event:
Click
Double-Click
Hold
Long Hold
*/
// 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 = 2000; // ms hold period: how long to wait for press+hold event
int longHoldTime = 5000; // ms long hold period: how long to wait for press+hold event
// Other 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;
// Read the state of the button
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;
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 = 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;
}
}
buttonLast = buttonVal;
return event;
}