3 Button Menu

Hey,
Been lurking for some time now - but havn't been able to get the logic to a menu system im trying to-do.

Iv been able to accomplish it for the most part though it's a little buggy and should be alot cleaner to build upon.

The project has 3 buttons (up-enter-down) which I want to drive a menu system. Main menu's that have submenu items of which you can toggle their state with the enter key. (On/off) with the last submenu item 'exit' returning you to the main menu.

Could someone point me in the right direction logic wise, much thanks in advanced!

Cheers, Brodie J

Why don't you post the code you have so far so we can critique it?
And are you "debouncing" these buttons?

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;
}

So it looks like you've got your direction (logic wise).
What specific problems are you having?

Mitch, the code posted wasn't from the OP..

That was me (A newb) trying to help another newb... which may just spell disaster, LOL

It's just something for him to consider. by no means do I claim it's the "best" way to do it. (As a matter of fact, I almost guarantee it's not. :wink:

Oops... thanks Troy. Sorry for the mistake.
In any case it looks like a good starting point for the OP.

Thanks, TroyO. That's a good function to have in the software toolbox.

Definetly something good to have but not really what I was after for this project - ill get what I have together and post it up.

Cheers all.
B.

Dummy Post for links

In my searching for a 3 button Menu System to run on my ATmega644P from Zbasic.net, I came up with three examples.

The first one comes with good Doc's, an easy to understand structure and it has a PC program that builds the menu structure constants and string index for you.
The program is written in a Pic basic style format and it was easy to convert the logic into Zbasic by examining each line of code and substituting the proper syntax.
With the logic easy to understand, the same method can be applied to converted into Arduino syntax.
http://www.sfcompiler.co.uk/wiki/pmwiki.php?n=SwordfishUser.LCDMenuModule

The second system is called TinyMinu and is found on AVR Freaks.
http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_id=249&item_type=project

Third system is on the Arduino playground.
http://www.arduino.cc/playground/Code/Menu

I know it's slightly off topic, but has anyone gotten that click/doubleclick/hold code working with multiple buttons?
I'm trying to set something up where I have 3 buttons, each with click/hold functionality, and a 4th button with click/double/hold.

I don't know how much of the code to duplicate. Do there need to be 4 versions of the variables/states/functions?

Have you tried qistoph's new (01 Feb 2010) button library from http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1265043477/0#0?

This DebounceButton library currently has

  • Debouncing
  • Event-like interface (onPress, onRelease, onHold)
  • Interval events for hold buttons
  • Supports 3 types of buttons: pull down, pull-up and internal pull-up using the
    Arduino internal pull-up resistor
  • Single call for updating all buttons

I have not used it myself, but qistoph seems to be an accommodating fellow, he could certainly advise you if it doesn't do exactly what you need done.

I looked at that, but it doesn't seem like it's built any different to accommodate multiple buttons. No more than the code in here earlier.

I showed the code to a friend earlier (who codes in other languages) and he said it should be easy to add a variable to the "checkButton" function(?), rather than duplicating the code 4 times and renaming all the variables.

Hi kriista,

I have the class which you need. But I am not the original author. I only implemented the class to Jeff's code link is http://jmsarduino.blogspot.com/2009/10/4-way-button-click-
double-click-hold.html. If you sent PM to me I can send the class files to you.

Regards,

Nadir
catalkn@hotmail.com