Pages: [1]   Go Down
Author Topic: Debounce on a switch with variable resistance  (Read 1256 times)
0 Members and 1 Guest are viewing this topic.
Seattle, WA
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have been playing with the debounce example found in the menubackend example.  I am able to get this working with four buttons.  

The problem.  I have a multi-directional tactile switch that outputs different resistance based on the direction you push the switch:  http://www.mouser.com/ProductDetail/ALPS/SKQUAAA010/?qs=oKW7zmyQiO62qWuFl5QVBw%3d%3d

I got the menubackend example I found here working with the switch:  http://www.coagula.org/content/pages/tutorial-manage-menu-and-lcd-display-arduino

The benefit of this button over four is that it only takes up one analog pin!  The only problem is that I cannot figure out how to adapt the debounce part of the code to work with this type of switch.  The switch is pretty unusable right now because the smallest touch causes it to drill all the way down into the menu.  I have tried a ton of things.  Any ideas on how I could approach this problem?  I am a hobbyist when it comes to programming so maybe that is why I am having trouble making the example code work with the special switch. A pointer in the right direction would be awesome!  Note I use a funky serial LCD so that is why the print to serial code is a little weird.  Here is the jumpy code I have without any attempt at debounce:

Code:
/*
IMPORTANT: to use the menubackend library by Alexander Brevig download it at http://www.arduino.cc/playground/uploads/Profiles/MenuBackend_1-4.zip and add the next code at line 195
void toRoot() {
setCurrent( &getRoot() );
}
*/
#include <MenuBackend.h>    //MenuBackend library - copyright by Alexander Brevig

#define buttonPinLeft (1<<0)
#define buttonPinRight (1<<1)
#define buttonPinEsc   (1<<2)
#define buttonPinEnter  (1<<3)

int lastButtonPushed = 0;

// LiquidCrystal display with:
// rs on pin 7
// rw on ground
// enable on pin 6
// d4, d5, d6, d7 on pins 5, 4, 3, 2


//Menu variables
MenuBackend menu = MenuBackend(menuUsed,menuChanged);
//initialize menuitems
    MenuItem menu1Item1 = MenuItem("Item1");
      MenuItem menuItem1SubItem1 = MenuItem("Item1SubItem1");
      MenuItem menuItem1SubItem2 = MenuItem("Item1SubItem2");
    MenuItem menu1Item2 = MenuItem("Item2");
      MenuItem menuItem2SubItem1 = MenuItem("Item2SubItem1");
      MenuItem menuItem2SubItem2 = MenuItem("Item2SubItem2");
      MenuItem menuItem3SubItem3 = MenuItem("Item2SubItem3");
    MenuItem menu1Item3 = MenuItem("Item3");


void setup()
{
 
  Serial.begin(9600);
 
  
  //lcd.begin(16, 2);

  //configure menu
  menu.getRoot().add(menu1Item1);
  menu1Item1.addRight(menu1Item2).addRight(menu1Item3);
  menu1Item1.add(menuItem1SubItem1).addRight(menuItem1SubItem2);
  menu1Item2.add(menuItem2SubItem1).addRight(menuItem2SubItem2).addRight(menuItem3SubItem3);
  menu.toRoot();
  //lcd.setCursor(0,0);  
  Serial.print("$GO 1 1\r\n");
  //lcd.print("Hello World!");
  Serial.print("$PRINT Hello World!\r\n");
}  // setup()...


void loop()
{

  readButtons();  //I splitted button reading and navigation in two procedures because
  navigateMenus();  //in some situations I want to use the button for other purpose (eg. to change some settings)
                  
} //loop()...


void menuChanged(MenuChangeEvent changed){
  
  MenuItem newMenuItem=changed.to; //get the destination menu
  
  //lcd.setCursor(0,1); //set the start position for lcd printing to the second row
  
  if(newMenuItem.getName()==menu.getRoot()){
      //lcd.print("Main Menu       ");
      Serial.print("$CLEAR 2 1\r\n");
       Serial.print("$GO 2 1\r\n");
       Serial.print("$PRINT Main Menu       \r\n");
  }else if(newMenuItem.getName()=="Item1"){
    Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1       \r\n");  
    //lcd.print("Item1           ");
  }else if(newMenuItem.getName()=="Item1SubItem1"){
    Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1SubItem1       \r\n");  
    //lcd.print("Item1SubItem1");
  }else if(newMenuItem.getName()=="Item1SubItem2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1SubItem2       \r\n");
    //lcd.print("Item1SubItem2   ");
  }else if(newMenuItem.getName()=="Item2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2       \r\n");  
    //lcd.print("Item2           ");
  }else if(newMenuItem.getName()=="Item2SubItem1"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem1       \r\n");  
    //lcd.print("Item2SubItem1   ");
  }else if(newMenuItem.getName()=="Item2SubItem2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem2       \r\n");  
    //lcd.print("Item2SubItem2   ");
  }else if(newMenuItem.getName()=="Item2SubItem3"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem3       \r\n");  
    //lcd.print("Item2SubItem3   ");
  }else if(newMenuItem.getName()=="Item3"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item3       \r\n");  
    //lcd.print("Item3           ");
  }
}

void menuUsed(MenuUseEvent used){
 //do something here
}


void  readButtons(){  //read buttons status
unsigned char button = analogRead(0) >> 2;
  if (button == 0){
    lastButtonPushed=0;
  }

  //Serial.print("BtnRaw ");
  //Serial.println(button, DEC);

  if (button > 20 && button < 60){
    lastButtonPushed=buttonPinLeft;
  }
  else if (button > 60 && button < 100){
    lastButtonPushed=buttonPinEnter;
  }
  else if (button > 140 && button < 160){
    lastButtonPushed=buttonPinEsc;
  }
  else if (button > 160 && button < 200){
    lastButtonPushed=buttonPinRight;  
  }
  else {
    lastButtonPushed=0;
  }            
}

void navigateMenus() {
  MenuItem currentMenu=menu.getCurrent();
  
  switch (lastButtonPushed){
    case buttonPinEnter:
      if(!(currentMenu.moveDown())){  //if the current menu has a child and has been pressed enter then menu navigate to item below
        menu.use();
      }else{  //otherwise, if menu has no child and has been pressed enter the current menu is used
        menu.moveDown();
       }
      break;
    case buttonPinEsc:
      menu.toRoot();  //back to main
      break;
    case buttonPinRight:
      menu.moveRight();
      break;      
    case buttonPinLeft:
      menu.moveLeft();
      break;      
  }
  
  lastButtonPushed=0; //reset the lastButtonPushed variable
}


« Last Edit: May 27, 2012, 02:55:10 am by GeoDave » Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 619
Posts: 33975
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Don't just take the analogue reading but compute a running average.
Logged

Seattle, WA
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Tried the running average.  That was fun to read about but it didn't work or I haven't identified the correct problem. The resistance values that come from the switch are very stable, however, when you toggle the switch it seems the commands (Enter, Esc, Left, Right) are coming so fast that that the code interprets one toggle as dozens of toggles.  Thus, it advances through the menu until it reaches the last item for that particular command.  At least I think that is what is going on.  Did I implement the running average correctly?  Any other ideas? Thanks! 

Here is the running average code I tried:
Code:
/*
IMPORTANT: to use the menubackend library by Alexander Brevig download it at http://www.arduino.cc/playground/uploads/Profiles/MenuBackend_1-4.zip and add the next code at line 195
void toRoot() {
setCurrent( &getRoot() );
}
*/
#include <MenuBackend.h>    //MenuBackend library - copyright by Alexander Brevig

#define buttonPinLeft (1<<0)
#define buttonPinRight (1<<1)
#define buttonPinEsc   (1<<2)
#define buttonPinEnter  (1<<3)

int lastButtonPushed = 0;

const int numReadings = 10;

int readings[numReadings];      // the readings from the analog input
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

int inputPin = 0;

// LiquidCrystal display with:
// rs on pin 7
// rw on ground
// enable on pin 6
// d4, d5, d6, d7 on pins 5, 4, 3, 2


//Menu variables
MenuBackend menu = MenuBackend(menuUsed,menuChanged);
//initialize menuitems
    MenuItem menu1Item1 = MenuItem("Item1");
      MenuItem menuItem1SubItem1 = MenuItem("Item1SubItem1");
      MenuItem menuItem1SubItem2 = MenuItem("Item1SubItem2");
    MenuItem menu1Item2 = MenuItem("Item2");
      MenuItem menuItem2SubItem1 = MenuItem("Item2SubItem1");
      MenuItem menuItem2SubItem2 = MenuItem("Item2SubItem2");
      MenuItem menuItem3SubItem3 = MenuItem("Item2SubItem3");
    MenuItem menu1Item3 = MenuItem("Item3");


void setup()
{
 
  Serial.begin(9600);
 
   for (int thisReading = 0; thisReading < numReadings; thisReading++)
    readings[thisReading] = 0; 
 
  //lcd.begin(16, 2);

  //configure menu
  menu.getRoot().add(menu1Item1);
  menu1Item1.addRight(menu1Item2).addRight(menu1Item3);
  menu1Item1.add(menuItem1SubItem1).addRight(menuItem1SubItem2);
  menu1Item2.add(menuItem2SubItem1).addRight(menuItem2SubItem2).addRight(menuItem3SubItem3);
  menu.toRoot();
  //lcd.setCursor(0,0); 
  Serial.print("$GO 1 1\r\n");
  //lcd.print("Hello World!");
  Serial.print("$PRINT Hello World!        \r\n");
}  // setup()...


void loop()
{

  readButtons();  //I splitted button reading and navigation in two procedures because
  navigateMenus();  //in some situations I want to use the button for other purpose (eg. to change some settings)
 
  // subtract the last reading:
  total= total - readings[index];         
  // read from the sensor: 
  readings[index] = analogRead(inputPin);
  // add the reading to the total:
  total= total + readings[index];       
  // advance to the next position in the array: 
  index = index + 1;                   

  // if we're at the end of the array...
  if (index >= numReadings)             
    // ...wrap around to the beginning:
    index = 0;                           

  // calculate the average:
  average = total / numReadings;         
  // send it to the computer as ASCII digits
  Serial.println(average);   
  delay(1);        // delay in between reads for stability             
                 
} //loop()...


void menuChanged(MenuChangeEvent changed){
 
  MenuItem newMenuItem=changed.to; //get the destination menu
 
  //lcd.setCursor(0,1); //set the start position for lcd printing to the second row
 
  if(newMenuItem.getName()==menu.getRoot()){
      //lcd.print("Main Menu       ");
      Serial.print("$CLEAR 2 1\r\n");
       Serial.print("$GO 2 1\r\n");
       Serial.print("$PRINT Main Menu       \r\n");
  }else if(newMenuItem.getName()=="Item1"){
    Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1       \r\n"); 
    //lcd.print("Item1           ");
  }else if(newMenuItem.getName()=="Item1SubItem1"){
    Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1SubItem1       \r\n"); 
    //lcd.print("Item1SubItem1");
  }else if(newMenuItem.getName()=="Item1SubItem2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1SubItem2       \r\n");
    //lcd.print("Item1SubItem2   ");
  }else if(newMenuItem.getName()=="Item2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2       \r\n");   
    //lcd.print("Item2           ");
  }else if(newMenuItem.getName()=="Item2SubItem1"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem1       \r\n");   
    //lcd.print("Item2SubItem1   ");
  }else if(newMenuItem.getName()=="Item2SubItem2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem2       \r\n");   
    //lcd.print("Item2SubItem2   ");
  }else if(newMenuItem.getName()=="Item2SubItem3"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem3       \r\n");   
    //lcd.print("Item2SubItem3   ");
  }else if(newMenuItem.getName()=="Item3"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item3       \r\n");   
    //lcd.print("Item3           ");
  }
}

void menuUsed(MenuUseEvent used){
 //do something here
}


void  readButtons(){  //read buttons status
 
unsigned char button = average >> 2;
  if (button == 0){
    lastButtonPushed=0;
  }

  //Serial.print("BtnRaw ");
  //Serial.println(button, DEC);

  if (button > 20 && button < 60){
    lastButtonPushed=buttonPinLeft;
  }
  else if (button > 60 && button < 100){
    lastButtonPushed=buttonPinEnter;
  }
  else if (button > 140 && button < 160){
    lastButtonPushed=buttonPinEsc;
  }
  else if (button > 160 && button < 200){
    lastButtonPushed=buttonPinRight; 
  }
  else {
    lastButtonPushed=0;
  }             
}

void navigateMenus() {
  MenuItem currentMenu=menu.getCurrent();
 
  switch (lastButtonPushed){
    case buttonPinEnter:
      if(!(currentMenu.moveDown())){  //if the current menu has a child and has been pressed enter then menu navigate to item below
        menu.use();
      }else{  //otherwise, if menu has no child and has been pressed enter the current menu is used
        menu.moveDown();
       }
      break;
    case buttonPinEsc:
      menu.toRoot();  //back to main
      break;
    case buttonPinRight:
      menu.moveRight();
      break;     
    case buttonPinLeft:
      menu.moveLeft();
      break;     
  }
 
  lastButtonPushed=0; //reset the lastButtonPushed variable
}
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 619
Posts: 33975
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Did I implement the running average correctly?
No.
You implemented an average not a running average.
If your readings are changing too fast then increase the number of terms in the average.

How are you wiring this up? You do have a pull up resistor on the analogue input as well do you?
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12631
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The problem is not the debouncing as such, it is that you are not detecting CHANGES in the button state. If the button remains 'pressed' you will process that as a separate command each time round the loop.

What you need to do is apply your range checks to determine the current button state, which will presumably be one of a set of discrete values representing the different directions your button can be operated, plus a value (let's say zero) for the 'not pressed' state.

In your readButtons() function you need to remember what poosition the button was in previously and do your range checks to see what position it is in now. If they are different, the button has been pressed or released so set lastButtonPushed to indicate the new position. Otherwise (the position has not changed) set lastButtonPushed to zero, or whatever value you're using to represent the 'not pressed' position.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 98
Posts: 4802
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Assuming that each position has an output voltage not close to the others (I look at the circuit and that seems like a safe assumption) that you can know each one,

Maybe when you detect a change, you keep reading over and over until the new value stays within +/- perhaps .2V of one of the accepted values for 20 to 50 reads in a row which should take less than 10 ms. You could be pretty certain the switch has settled by then.
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Seattle, WA
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Did I implement the running average correctly?
No.
You implemented an average not a running average.
If your readings are changing too fast then increase the number of terms in the average.

How are you wiring this up? You do have a pull up resistor on the analogue input as well do you?

Pin 1 on the button schematic goes to 5v.  Pin 2 goes to analog 0 and a 1K resistor tied to gnd.   I am going to play some more and see what I can come up with based on the comments.  Thanks again!
« Last Edit: May 27, 2012, 11:56:56 am by GeoDave » Logged

Seattle, WA
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

In your readButtons() function you need to remember what poosition the button was in previously and do your range checks to see what position it is in now. If they are different, the button has been pressed or released so set lastButtonPushed to indicate the new position. Otherwise (the position has not changed) set lastButtonPushed to zero, or whatever value you're using to represent the 'not pressed' position.

I got it working much better.  Thoughts on improvements?  Here is what I did: 

Code:
/*
IMPORTANT: to use the menubackend library by Alexander Brevig download it at http://www.arduino.cc/playground/uploads/Profiles/MenuBackend_1-4.zip and add the next code at line 195
void toRoot() {
setCurrent( &getRoot() );
}
*/
#include <MenuBackend.h>    //MenuBackend library - copyright by Alexander Brevig

#define buttonPinLeft (1<<0)
#define buttonPinRight (1<<1)
#define buttonPinEsc   (1<<2)
#define buttonPinEnter  (1<<3)

int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0; 
int buttonPushed;
int lastButtonPushed;

int inputPin = 0;

// LiquidCrystal display with:
// rs on pin 7
// rw on ground
// enable on pin 6
// d4, d5, d6, d7 on pins 5, 4, 3, 2


//Menu variables
MenuBackend menu = MenuBackend(menuUsed,menuChanged);
//initialize menuitems
    MenuItem menu1Item1 = MenuItem("Item1");
      MenuItem menuItem1SubItem1 = MenuItem("Item1SubItem1");
      MenuItem menuItem1SubItem2 = MenuItem("Item1SubItem2");
    MenuItem menu1Item2 = MenuItem("Item2");
      MenuItem menuItem2SubItem1 = MenuItem("Item2SubItem1");
      MenuItem menuItem2SubItem2 = MenuItem("Item2SubItem2");
      MenuItem menuItem3SubItem3 = MenuItem("Item2SubItem3");
    MenuItem menu1Item3 = MenuItem("Item3");


void setup()
{
 
  Serial.begin(9600);
 
 
  //lcd.begin(16, 2);

  //configure menu
  menu.getRoot().add(menu1Item1);
  menu1Item1.addRight(menu1Item2).addRight(menu1Item3);
  menu1Item1.add(menuItem1SubItem1).addRight(menuItem1SubItem2);
  menu1Item2.add(menuItem2SubItem1).addRight(menuItem2SubItem2).addRight(menuItem3SubItem3);
  menu.toRoot();
  //lcd.setCursor(0,0); 
  Serial.print("$GO 1 1\r\n");
  //lcd.print("Hello World!");
  Serial.print("$PRINT Hello World!        \r\n");
}  // setup()...


void loop()
{

  readButtons();  //I splitted button reading and navigation in two procedures because
  navigateMenus();  //in some situations I want to use the button for other purpose (eg. to change some settings)
     
                 
} //loop()...


void menuChanged(MenuChangeEvent changed){
 
  MenuItem newMenuItem=changed.to; //get the destination menu
 
  //lcd.setCursor(0,1); //set the start position for lcd printing to the second row
 
  if(newMenuItem.getName()==menu.getRoot()){
      //lcd.print("Main Menu       ");
      Serial.print("$CLEAR 2 1\r\n");
       Serial.print("$GO 2 1\r\n");
       Serial.print("$PRINT Main Menu       \r\n");
  }else if(newMenuItem.getName()=="Item1"){
    Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1       \r\n"); 
    //lcd.print("Item1           ");
  }else if(newMenuItem.getName()=="Item1SubItem1"){
    Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1SubItem1       \r\n"); 
    //lcd.print("Item1SubItem1");
  }else if(newMenuItem.getName()=="Item1SubItem2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1SubItem2       \r\n");
    //lcd.print("Item1SubItem2   ");
  }else if(newMenuItem.getName()=="Item2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2       \r\n");   
    //lcd.print("Item2           ");
  }else if(newMenuItem.getName()=="Item2SubItem1"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem1       \r\n");   
    //lcd.print("Item2SubItem1   ");
  }else if(newMenuItem.getName()=="Item2SubItem2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem2       \r\n");   
    //lcd.print("Item2SubItem2   ");
  }else if(newMenuItem.getName()=="Item2SubItem3"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem3       \r\n");   
    //lcd.print("Item2SubItem3   ");
  }else if(newMenuItem.getName()=="Item3"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item3       \r\n");   
    //lcd.print("Item3           ");
  }
}

void menuUsed(MenuUseEvent used){
 //do something here
}


void  readButtons(){  //read buttons status
unsigned char button = analogRead(inputPin) >> 2;
  if (button == 0){
    buttonPushed=0;
  }

  if (button > 20 && button < 60){
    buttonPushed=buttonPinLeft;
  }
  else if (button > 60 && button < 100){
    buttonPushed=buttonPinEnter;
  }
  else if (button > 140 && button < 160){
    buttonPushed=buttonPinEsc;
  }
  else if (button > 160 && button < 200){
    buttonPushed=buttonPinRight; 
  }
  else {
    buttonPushed=0;
  } 
 // read the pushbutton input pin:
  buttonState = buttonPushed;

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState != 0 ) {
      // if the current state is HIGH then the button
      // wend from off to on:
      buttonPushCounter++;
      lastButtonPushed = buttonPushed;     
    }
    else {
      // if the current state is LOW then the button
      // wend from on to off:
      lastButtonPushed = 0;
    }
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;
}

void navigateMenus() {
  MenuItem currentMenu=menu.getCurrent();
 
  switch (lastButtonPushed){
    case buttonPinEnter:
      if(!(currentMenu.moveDown())){  //if the current menu has a child and has been pressed enter then menu navigate to item below
        menu.use();
      }else{  //otherwise, if menu has no child and has been pressed enter the current menu is used
        menu.moveDown();
       }
      break;
    case buttonPinEsc:
      menu.toRoot();  //back to main
      break;
    case buttonPinRight:
      menu.moveRight();
      break;     
    case buttonPinLeft:
      menu.moveLeft();
      break;     
  }
 
  lastButtonPushed=0; //reset the lastButtonPushed variable
}

Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 610
Posts: 49077
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
unsigned char button = analogRead(inputPin) >> 2;
Unsigned char? Why? Why not byte?
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12631
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm a bit perplexed by the number of state variables you are using in readButtons.

Using buttonState and lastButtonState looks fine to hold the current detected state and previously detected state so you can detect changes. Using lastButtonPushed as a way to pass the change from readButtons() to navigateMenus() (and then clear it once it's been processed) is also OK. Personally I'd have preferred to have readButtons() return the key that was pressed (or 0 is nothing was pressed) rather than using a global variable, because this reduces the amount of global data and that is always a good thing, but that's mainly a matter of style. But then you also have buttonPushed and buttonPushCounter, and I have no idea what they're for. If you can eliminate them that would reduce the complexity by a useful amount.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 98
Posts: 4802
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I don't think that dividing the range of analog read values so widely is going to help debounce the multi-switch.

Code:
  if (button > 20 && button < 60){
    buttonPushed=buttonPinLeft;
  }
  else if (button > 60 && button < 100){
    buttonPushed=buttonPinEnter;
  }
  else if (button > 140 && button < 160){
    buttonPushed=buttonPinEsc;
  }
  else if (button > 160 && button < 200){
    buttonPushed=buttonPinRight; 
  }
  else {
    buttonPushed=0;
  } 

I think you want discrete intervals for a settled button press. The first two and last two are back to back. At the speed the Arduino does even analog reads you can see the voltage change before it is done changing, you can 'see the bounce'. When there's no room between two states, you will get a 'valid' state even as the state itself is changing.

If you press the Esc button and leave it for a while, do repeated analog reads fall in the range of 140 to 160 or something closer to 150?

Maybe you can find a way to widen the total range beyond 200?
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Seattle, WA
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Menu now works perfectly with the multi-directional button!  I hope this is useful to someone in the future.

Here is a better schematic:


Code:
unsigned char button = analogRead(inputPin) >> 2;
Unsigned char? Why? Why not byte?
Ehh...good point.  Thanks!  Still learning...

I'm a bit perplexed by the number of state variables you are using in readButtons.

Using buttonState and lastButtonState looks fine to hold the current detected state and previously detected state so you can detect changes. Using lastButtonPushed as a way to pass the change from readButtons() to navigateMenus() (and then clear it once it's been processed) is also OK. Personally I'd have preferred to have readButtons() return the key that was pressed (or 0 is nothing was pressed) rather than using a global variable, because this reduces the amount of global data and that is always a good thing, but that's mainly a matter of style. But then you also have buttonPushed and buttonPushCounter, and I have no idea what they're for. If you can eliminate them that would reduce the complexity by a useful amount.


Another good point.  Got the code from the ButtonStateChange example.  I was running out the door when I added it and didn't clean it up very well.  Not sure how it would look to eliminate the lastButtonPushed.  This is what I have now: 
Code:
/*
IMPORTANT: to use the menubackend library by Alexander Brevig download it at http://www.arduino.cc/playground/uploads/Profiles/MenuBackend_1-4.zip and add the next code at line 195
void toRoot() {
setCurrent( &getRoot() );
}
*/
#include <MenuBackend.h>    //MenuBackend library - copyright by Alexander Brevig
//#include <LiquidCrystal.h>  //this library is included in the Arduino IDE

#define buttonPinLeft (1<<0)
#define buttonPinRight (1<<1)
#define buttonPinEsc   (1<<2)
#define buttonPinEnter  (1<<3)

int buttonState = 0;         // current state of the button
int lastButtonState = 0;     //last button state
int lastButtonPushed = 0;


int inputPin = 0;

// LiquidCrystal display with:
// rs on pin 7
// rw on ground
// enable on pin 6
// d4, d5, d6, d7 on pins 5, 4, 3, 2
//LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

//Menu variables
MenuBackend menu = MenuBackend(menuUsed,menuChanged);
//initialize menuitems
    MenuItem menu1Item1 = MenuItem("Item1");
      MenuItem menuItem1SubItem1 = MenuItem("Item1SubItem1");
      MenuItem menuItem1SubItem2 = MenuItem("Item1SubItem2");
    MenuItem menu1Item2 = MenuItem("Item2");
      MenuItem menuItem2SubItem1 = MenuItem("Item2SubItem1");
      MenuItem menuItem2SubItem2 = MenuItem("Item2SubItem2");
      MenuItem menuItem3SubItem3 = MenuItem("Item2SubItem3");
    MenuItem menu1Item3 = MenuItem("Item3");


void setup()
{
 
  Serial.begin(9600);
 
 
  //lcd.begin(16, 2);

  //configure menu
  menu.getRoot().add(menu1Item1);
  menu1Item1.addRight(menu1Item2).addRight(menu1Item3);
  menu1Item1.add(menuItem1SubItem1).addRight(menuItem1SubItem2);
  menu1Item2.add(menuItem2SubItem1).addRight(menuItem2SubItem2).addRight(menuItem3SubItem3);
  menu.toRoot();
  //lcd.setCursor(0,0); 
  Serial.print("$GO 1 1\r\n");
  //lcd.print("Hello World!");
  Serial.print("$PRINT Hello World!        \r\n");
}  // setup()...


void loop()
{

  readButtons();  //I splitted button reading and navigation in two procedures because
  navigateMenus();  //in some situations I want to use the button for other purpose (eg. to change some settings)
                 
} //loop()...


void menuChanged(MenuChangeEvent changed){
 
  MenuItem newMenuItem=changed.to; //get the destination menu
 
  //lcd.setCursor(0,1); //set the start position for lcd printing to the second row
 
  if(newMenuItem.getName()==menu.getRoot()){
      //lcd.print("Main Menu       ");
      Serial.print("$CLEAR 2 1\r\n");
       Serial.print("$GO 2 1\r\n");
       Serial.print("$PRINT Main Menu       \r\n");
  }else if(newMenuItem.getName()=="Item1"){
    Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1       \r\n"); 
    //lcd.print("Item1           ");
  }else if(newMenuItem.getName()=="Item1SubItem1"){
    Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1SubItem1       \r\n"); 
    //lcd.print("Item1SubItem1");
  }else if(newMenuItem.getName()=="Item1SubItem2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item1SubItem2       \r\n");
    //lcd.print("Item1SubItem2   ");
  }else if(newMenuItem.getName()=="Item2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2       \r\n");   
    //lcd.print("Item2           ");
  }else if(newMenuItem.getName()=="Item2SubItem1"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem1       \r\n");   
    //lcd.print("Item2SubItem1   ");
  }else if(newMenuItem.getName()=="Item2SubItem2"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem2       \r\n");   
    //lcd.print("Item2SubItem2   ");
  }else if(newMenuItem.getName()=="Item2SubItem3"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item2SubItem3       \r\n");   
    //lcd.print("Item2SubItem3   ");
  }else if(newMenuItem.getName()=="Item3"){
      Serial.print("$CLEAR 2 1\r\n");
      Serial.print("$GO 2 1\r\n");
      Serial.print("$PRINT Item3       \r\n");   
    //lcd.print("Item3           ");
  }
}

void menuUsed(MenuUseEvent used){
 //do something useful here
}

void  readButtons(){  //read buttons status
byte button = analogRead(inputPin) >> 2;
  if (button == 0){
    buttonState=0;
  }
  if (button > 20 && button < 60){
    buttonState=buttonPinLeft;
  }
  else if (button > 60 && button < 100){
    buttonState=buttonPinEnter;
  }
  else if (button > 140 && button < 160){
    buttonState=buttonPinEsc;
  }
  else if (button > 160 && button < 200){
    buttonState=buttonPinRight; 
  }
  else {
    buttonState=0;
  } 
 
  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    if (buttonState != 0 ) {
      lastButtonPushed = buttonState;     
    }
    else {
      lastButtonPushed = 0;
    }
  }
  // save the current state as the last state for next time through the loop
  lastButtonState = buttonState;
}

void navigateMenus() {
  MenuItem currentMenu=menu.getCurrent();
  switch (lastButtonPushed){
    case buttonPinEnter:
      if(!(currentMenu.moveDown())){  //if the current menu has a child and has been pressed enter then menu navigate to item below
        menu.use();
      }else{  //otherwise, if menu has no child and has been pressed enter the current menu is used
        menu.moveDown();
       }
      break;
    case buttonPinEsc:
      menu.toRoot();  //back to main
      break;
    case buttonPinRight:
      menu.moveRight();
      break;     
    case buttonPinLeft:
      menu.moveLeft();
      break;     
  }
 
  lastButtonPushed=0; //reset the lastButtonPushed variable
}
Logged

Seattle, WA
Offline Offline
Newbie
*
Karma: 0
Posts: 6
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you press the Esc button and leave it for a while, do repeated analog reads fall in the range of 140 to 160 or something closer to 150?

Doesn't seem to be a problem.  I got the code from Bryan Mayland's PID smoker controller the "Linkmeter" (Google it).  Built one! Love it!

hold down Esc = value varies between 151-153
« Last Edit: May 28, 2012, 12:53:01 am by GeoDave » Logged

Pages: [1]   Go Up
Jump to: