LCD Keypad Shield Debounce

Hello!

I’m using dfrobot’s LCD Keypad Shield.

Product Description: “It includes a 2×16 LCD display and 6 momentary push buttons. Pins 4, 5, 6, 7, 8, 9 and 10 are used to interface with the LCD and analog Pin 0 is used to read the pushbuttons.”

I’ll use it to display sensor number and reading. I want to make use of the buttons; by pressing up and down it can scroll through the 5 sensors, and by pressing left and right it can show min and maximum values.

Could you help me figure out how to do this? I don’t think switch case is applicable?

When I press ‘select’ button, should it read it once and go back ‘no button pressed’ [like the code below] or should it store the value and change only when I press a different button?

I found a code here http://linksprite.com/wiki/index.php5?title=16_X_2_LCD_Keypad_Shield_for_Arduino for debounce. I removed the second analog reading because it gives unstable reading to my sensors.

#include <LiquidCrystal.h>
#include <LCDKeypad.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
char msgs[5][16] = {"Right Key OK ",
                    "Up Key OK    ",               
                    "Down Key OK  ",
                    "Left Key OK  ",
                    "Select Key OK" };
                    
int adc_key_val[5] ={50, 250, 450, 650, 850};
int NUM_KEYS = 5;
int adc_key_in;
int key=-1;
int oldkey=-1;
 
void setup(){
  Serial.begin(9600);
  lcd.clear();
  lcd.setCursor(0,0); 
  lcd.print("ADC key testing"); 
}
void loop(){
  adc_key_in = analogRead(0);    // read the value from the sensor 
  ifSerial.print("reading: "); Serial.println(adc_key_in);
  key = get_key(adc_key_in);  // convert into key press
  Serial.print("key pressed: "); Serial.println(key);
   if (key != oldkey){   // if keypress is detected  
     lcd.setCursor(0, 1);
     oldkey = key;
     if (key >=0){
       lcd.print(msgs[key]);  
       Serial.println(msgs[key]);        
     }
   }
   delay(1000);
}
// Convert ADC value to key number
int get_key(unsigned int input){
  int k;
  for (k = 0; k < NUM_KEYS; k++){
    if (input < adc_key_val[k]){
        return k;
    }
  }   
  //if (k >= NUM_KEYS) k = -1;  // No valid key pressed
  //return k;
}

In my code libraries linked from the signature below, I have a small library to handle the analog buttons found in the LCD shield. You may be able to see how to do what you want from reading that code.

Basically you need to detect the transition from off to on rather than the on state itself.

Thank you, Sir marco_c!

I downloaded the library for multiple switches on one analog input: MD_AButton.h

When I run the LCD_Keypad example, I get this error

error: request for member 'getKey' in 'lcdButton', which is of non-class type 'MD_AButton ()()'

What does this mean?
Solved! I should put 'A0' in lcdButton();

I think it's similar with the code from linksprite.
Pros : shorter
Cons : more complex

I want the display to be like this =(

  1. Start > "Press 'SELECT'"
  2. If 'SELECT', display: Device Ready! then proceed to Fan 1.
  3. If Fan1,
    display: Fan#1 Reading: 0.24 A
  • UP, Nothing happens
  • DOWN, Fan2
    If Fan2,
    display: Fan#2 Reading: 1.17 A
  • UP, Fan1
  • DOWN, Fan2
    If Fan3,
    display: Fan#3 Reading: 0.01 A
  • UP, Fan2
  • DOWN, Nothing happens

What can you suggest I do since the code only reads the change of button once?

I have just found out the same thing about the ‘A0’. It should not be like that so I will keep looking at it.

You need to implement a finite state machine. Basically the code will look something like this:

void updateDisplay()
{
  static uint 8_t state = 0;
  char c = lcd.getKey();

  switch (state)
  {
  case 0:
    if (c==SELECT)  state = 1;
      break;

  case 1:
    // display fan 1 reading
    state = 2;
    break;

  case 2:
    if (c == DOWN)   state = 3:
    break;

   case 3:
   // display fan 2 reading
   state = 4;
   break;

  case 4:
    if (c==UP)  state = 1;
    else if (c == DOWN)  state = 5:
    break;

   case 5:
   // display fan 3 reading
   state = 6;
   break;

  case 6:
    if (c==UP)   state = 3;
    else if (c == DOWN)   state = 7:
    break;

   case 7:
   // display fan 3 reading
   state = 8;
   break;

  case 8:
    if (c==UP)  state = 5;
    break;

  default:
    state = 1;
    break;
  }
}

loop()
{
   // do stuff
  updateDisplay();
  // do more stuff
}

This can be done more elegantly, but the layout will help you get an idea of what is happening. This initially looks like it is doing nothing, but you need to track what the code is doing every time through loop() to understand how it works. A lot of other Arduino programs work this way, especially when you eliminate the delay() functions that cause problems in beginner code.

arduinoTime:
I want the display to be like this

If you organize your code so that the business of reading the button is separate from the business of writing to the LCD things will be clearer.

void loop() {
   readAndSaveButtonState();
   updateLCD();
}

The button state is read and saved into a variable.
The updateLCD() function will check that variable to decide which stuff should be shown.

...R

Thank you for the replies, Sir Robin2 and Sir marco_c!

I don't have a strategy yesterday so I ended up doing this

void loop(){
  read_key(butnPin);
  Serial.println(read_key(butnPin));
   
  if (key != oldkey){   // if keypress is detected
    delay(50);  // wait for debounce time
    oldkey = key;
    if (key >=0){
      if(key == 4){  // SELECT
        butnStart++;
      } else if(key == 2){  // UP
        if(fan == 1) fan = 2; 
        else if(fan == 2) fan = 3;
        else if(fan == 3) fan = 3; 
      } else if(key == 1){  // DOWN
        if(fan == 1) fan = 1;
        else if(fan == 2) fan = 1; 
        else if(fan == 3) fan = 2;
      }
    }
  }
   
  if(butnStart == 1){  // START
    lcd.setCursor(0, 0); lcd.print("Stabilizing...  ");
    curSet1 = detAve(curPin1);   
    curSet2 = detAve(curPin2);
    curSet3 = detAve(curPin3);
    delay(150);

    butnStart++;
    fan = 1;    
  } 
  
  if(fan == 1){
    lcd.setCursor(0, 0); lcd.print("Fan#1           ");
    float curVal2 = readCurrent(curPin2);
    lcd.setCursor(0, 1); lcd.print(curVal1); lcd.print(" A,   ");
    lcd.setCursor(8, 1); lcd.print(rpmLast1, DEC); lcd.print(" rps   ");
  } else if(fan == 2){
    lcd.setCursor(0, 0); lcd.print("Fan#2           ");
    float curVal2 = readCurrent(curPin2);
    lcd.setCursor(0, 1); lcd.print(curVal2); lcd.print(" A,   ");
    lcd.setCursor(8, 1); lcd.print("no rps  "); 
    //lcd.print(rpmLast2, DEC); lcd.print(" rps   ");
  } else if(fan == 3){
    lcd.setCursor(0, 0); lcd.print("Fan#3           ");
    float curVal3 = readCurrent(curPin3);
    lcd.setCursor(0, 1); lcd.print(curVal3); lcd.print(" A,   ");
    lcd.setCursor(8, 1); lcd.print("no rps  "); 
    //lcd.print(rpmLast3, DEC); lcd.print(" rps   ");
  }
}

It works but... I thinks it's very sloppy

I'll try your suggestions :wink: will post the code later

The button state is read and saved into a variable.

Sir Robin2, what do you mean by button state? is it the current button pressed? will this cause a delay?

I forgot to mention that I want to place an error detection in the void loop(). The user can scroll up/down the three fans and see their reading when there is no error but if there is an error, the LCD will display "Error!" and will not allow the user to scroll up/down the fan until the error is fixed.

Right now I'm still working on this. I'm not good with coding so I start on the basic--algorithm. I hope you can help me again on this one

void loop()

  1. Does it have an error? (a do-while or an if-else?)
  2. If yes: display ERROR!
  3. If no: readButton(), determineFanNo(), updateLCD()
  4. Back to 1

readButton()

  1. Read A0
  2. Check if its a valid key (for loop)
  3. If yes: return key
  4. If no: return -1

determineFanNo()

  1. Key pressed is UP?
  2. Is it on Fan = 1?
  3. If yes: does nothing
  4. If no: Is it Fan = 2?
    ...

updateLCD()

  1. Is it Fan =1?
  2. If yes: display reading
    ...

I want to be able to press the select button (keeping track whether the button was pressed or not) on my LCD keypad shield and generate two random numbers(1-7), how would I do that?