LCD Keypad Shield – Entering and Storing Numbers

I have an LCD Keypad shield (16x2 with up,down,left,right,select buttons) and I want to prompt the user to enter a weight in kgs. The number will be greater than 10 thousand kgs. In my code below I use the up and down buttons to either add 1 or subtract 1 from the current value for that cursor space. The right button moves onto the next cursor space and the left one moves back a cursor space.

The problem is that the number displayed, ex: 12500, is not twelve thousand five hundred but yet one-two-five-zero-zero. It is not 1 number but yet a series of 5 numbers printed on the screen. It is important to store the number as it will be used later in the program.

Is it possible to enter and store a number like this? Or will I have to make multiple functions prompting the user to enter ten-thousands, thousands, hundreds etc., and then multiply the variable by its multiplier (x10 000, x1000, x*100) and add them all up?

ALSO:

  • The buttons are kind of “jumpy” when I press them. Is there a way to counteract this? Maybe by setting up delays?
  • How do I make the values only be a positive number? Where do I insert an abs function?
#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

int ans = 0;
int a = 0; //used for moving the cursor left or right. 

void setup() {
  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("Enter kgs:");
  
  
}

void loop() {

  int x;  
  x = analogRead (0);
  lcd.setCursor(a,1);

  
  if (x < 60) {
   //right button
   a++;
   ans = 0;
  
  
  }
  else if (x < 200) {
  //up button
    ans++;
    lcd.print(ans);

    
  }
  else if (x < 400){
  //down button
    ans--;
    lcd.print(ans);

  }
   else if (x < 600){
   //left button
     a--;

     
  }
  else if (x < 800){
 //want to use this to save the value and move onto another function. 
  
  }
  
  delay(100);
}

For your "jumpy" switches, you need to debounce them. All switches bounce; some more than others, which will cause multiple keypress events. The answers are all in Nick Gammon's tutorial, http://gammon.com.au/switches.

As for your number format.

You can display the numbers any way that the LCD will take them. The important thing is that you know which display position the cursor is on, and can change the number in that position, or move the cursor to another position. After you have the number displayed, you can change it to whatever format is best for its intended use.

Fior example, you might want to use an array of bytes. Let's call it numbr

byte numbr[4];  // This will hold 4 bytes, and if global, will init to all zeros
byte indx;  //This will hold an index to the array. If global, it will init to 0
int enteredValue;  // this will hold the final value entered

So, when you enter loop(), and detect keypresses, you do things... Example is pseudocode

if UP {
   if numbr[indx] < 9 {  // don't let it go above 9
      increment numbr[indx]
      display numbr[indx]
   }
}

if UP {
   if numbr[indx] > 0 {    // don't let it wrap around to 255
      decrement numbr[indx]
      display numbr[indx]
   }
}

if RIGHT {
   if indx < 3 {  // don't let it look past the end of numbr[]
      increment indx
      move cursor
   }
}

if LEFT {
   if indx > 0 {   // do't let it look before numbr[]
      decrement indx
      move cursor
   }
}

if SELECT {  // build integer from numbr[]
  enteredValue = 0
   for (int i = 0; i < 4; i++) {
      enteredValue = (EnteredValue * 10) +  numbr[i]
   }

Great thanks I got it to work!

So now that the user has inputted that number I want them to do the same with another number, but now it has an array of 5. I have my value stored but is there a way to have another function similar to this but now for a new number? Like can I do this loop again for a new number?

Here is my current code which works at storing one value.

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);


int a = 0; //used for moving the cursor left or right. 

byte numbr[4];  // This will hold 4 bytes, and if global, will init to all zeros
byte indx;  //This will hold an index to the array. If global, it will init to 0
int enteredValue;  // this will hold the final value entered

void setup() {
  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("Enter cup wt(g):");
  
}

void loop() {

  int x;  
  x = analogRead (0);
  lcd.setCursor(a,1);

  
  if (x < 60) {
   //right button
   if (indx < 3 ) { 
     indx++;
     a++;
   }
  
  
  }
  else if (x < 200) {
  //up button
   if (numbr[indx] < 9 ) {
     numbr[indx]++;
    lcd.print(numbr[indx]);
   }

    
  }
  else if (x < 400){
  //down button
    if (numbr[indx] > 0 ) {
    numbr[indx]--;
    lcd.print(numbr[indx]);
    }

  }
   else if (x < 600){
   //left button
     if (indx > 0) {
     indx--;
     a--;
     }

     
  }
  else if (x < 800){
 //want to use this to save the value and move onto another function. 
 enteredValue = 0;
   for (int i = 0; i < 4; i++) {
     enteredValue = (enteredValue * 10) + numbr[i];
    
    lcd.setCursor(0,1);
    lcd.print("Value saved");
    delay(2000);
    
 
  }
  }
  
  delay(100);
}

You need to change the way you are thinking about the structure of your program.

You should put the stuff to read a value from the keyswitches into a function that is called from loop() - a bit like this

void loop() {
   getValueFromKeySwitch();
   // other stuff
}

I hope it will now occur to you that is is easy to call getValueFromKeySwitch() whenever you need a value.

If you want to save several different values one way is to have the function return the value so that you can use it like this

void loop() {
   firstValue = getValueFromKeySwitch();
   secondValue = getValueFromKeySwitch();
   // other stuff
}

...R

hi, i tried your code, and it work. but when i just tried input 1 number, ex 2 become 2000, or 23 become 2300

hi, i tried your code

You tried who's code? Ypu need to post YOUR code!

sorry,,

i mean this code

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);


int a = 0; //used for moving the cursor left or right.

byte numbr[4];  // This will hold 4 bytes, and if global, will init to all zeros
byte indx;  //This will hold an index to the array. If global, it will init to 0
int enteredValue;  // this will hold the final value entered

void setup() {
  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("Enter cup wt(g):");
 
}

void loop() {

  int x; 
  x = analogRead (0);
  lcd.setCursor(a,1);

 
  if (x < 60) {
   //right button
   if (indx < 3 ) {
     indx++;
     a++;
   }
 
 
  }
  else if (x < 200) {
  //up button
   if (numbr[indx] < 9 ) {
     numbr[indx]++;
    lcd.print(numbr[indx]);
   }

   
  }
  else if (x < 400){
  //down button
    if (numbr[indx] > 0 ) {
    numbr[indx]--;
    lcd.print(numbr[indx]);
    }

  }
   else if (x < 600){
   //left button
     if (indx > 0) {
     indx--;
     a--;
     }

     
  }
  else if (x < 800){
//want to use this to save the value and move onto another function.
enteredValue = 0;
   for (int i = 0; i < 4; i++) {
     enteredValue = (enteredValue * 10) + numbr[i];
   
    lcd.setCursor(0,1);
    lcd.print("Value saved");
    delay(2000);
   

  }
  }
 
  delay(100);
}

when i just enter two number, it add 2 zero behind the number. ex: 25 -> 2500, or 123->1230. how i can remove the zeros?

   for (int i = 0; i < 4; i++) {
     enteredValue = (enteredValue * 10) + numbr[i];

If you haven't entered 4 values, why are you iterating 4 times?

patrixx:
when i just enter two number, it add 2 zero behind the number. ex: 25 -> 2500, or 123->1230. how i can remove the zeros?

Here I've made some "input editor" for the LCD Keypad Shield as an example sketch.

You can edit numbers (which are actually strings) from "00000" to "99999".

When pressing the "Select" button, the number is "stored" (in this example printed to Serial)

#include <LiquidCrystal.h>

LiquidCrystal lcd(8,9,4,5,6,7);

enum {btnNONE, btnSELECT, btnLEFT, btnUP, btnDOWN, btnRIGHT, NUM_KEYS };
const byte ButtonsPin= A0;

int read_LCD_buttons()
{
  int returnValue;
  // read ADC value of pressed button
  int adc_key_in =  analogRead (ButtonsPin);
  int adc_key_in1=  analogRead (ButtonsPin);
  // read again and check for stable ADC reading (software debouncing for analog input)
  if (abs(adc_key_in1-adc_key_in)>1) return btnNONE; // if ADC reading is not stable, return btnNONE
  if (adc_key_in <50) returnValue= btnRIGHT;
  else if (adc_key_in <195) returnValue= btnUP;
  else if (adc_key_in <410) returnValue= btnDOWN;
  else if (adc_key_in <650) returnValue= btnLEFT;
  else if (adc_key_in <999) returnValue= btnSELECT;
  else returnValue=btnNONE;
  // simple "blocking" code: "Busy waiting" until button is released by user
  while(adc_key_in<999) adc_key_in= analogRead(ButtonsPin);
  return returnValue;
}


void setup() 
{
  Serial.begin(9600);
  lcd.begin(16,2);
  lcd.clear();
  lcd.print ("Input value:");
  lcd.blink();
}


char value[]= "00000";
int cursorPos;
boolean lcdNeedsUpdate=true;

void loop() 
{
  char key=read_LCD_buttons();
  if (key!= btnNONE) lcdNeedsUpdate=true;
  switch (key)
  {
    case btnRIGHT:
      if (cursorPos<4) cursorPos++;
      break;
    case btnLEFT:  
      if (cursorPos>0) cursorPos--;
      break;
    case btnUP:
      if (value[cursorPos]<'9') value[cursorPos]++;
      break;
    case btnDOWN:
      if (value[cursorPos]>'0') value[cursorPos]--;
      break;
    case btnSELECT: 
      Serial.print("Saved value: ");
      Serial.println(value);
      strcpy(value,"00000");
      cursorPos=0;
      break;
  }
  if (lcdNeedsUpdate)
  {
    lcd.setCursor(0,1);
    lcd.print(value);
    lcd.setCursor(cursorPos,1);
    lcdNeedsUpdate=false;
  }
}

In case the buttons are not working, you need to work over the analog values in the "read_LCD_buttons()" function.

There are different types of LCD Keypad Shield from different manufacturers on the market that may provide different analog readings.

Hi Jurs

I ran you code on a leonardo and changed only the Liquidcrystal pins to:

"LiquidCrystal lcd(8,9,20,21,22,23);"

and the analogRead values to:

if (adc_key_in <50) returnValue= btnRIGHT;
else if (adc_key_in <150) returnValue= btnUP;
else if (adc_key_in <300) returnValue= btnDOWN;
else if (adc_key_in <450) returnValue= btnLEFT;
else if (adc_key_in <700) returnValue= btnSELECT;
else returnValue=btnNONE;

since that is what my boards values are.

None of the buttons respond and the cursor keeps n blinking after "Input Value:"

Am I doing something wrong?

I have tested other code and the lcd and keypad works. I do like the structure of your code though.
Have you tested your code?

WagnerJ:
if (adc_key_in <50) returnValue= btnRIGHT;
else if (adc_key_in <150) returnValue= btnUP;
else if (adc_key_in <300) returnValue= btnDOWN;
else if (adc_key_in <450) returnValue= btnLEFT;
else if (adc_key_in <700) returnValue= btnSELECT;
else returnValue=btnNONE;

since that is what my boards values are.

It's not correct to set the button 'split values' to the same values which are returned by the buttons actually.

You have to use 'split values' that are "in the middle between' the values returned by the buttons.

Please use this short test code to find out:

#include <LiquidCrystal.h>
LiquidCrystal lcd (8,9,4,5,6,7);

void setup() 
{
  lcd.begin(16,2);
}  

void loop() 
{
  int value= analogRead(A0);
  char buf[17];
  snprintf(buf,sizeof(buf), "%4d", value);
  lcd.setCursor(0,0);
  lcd.print(buf);
}

Which are the values returned from your buttons?
Then I can tell you which split values to use best in the read_LCD_buttons() function.

Those shields are produced by many different manufacturers, and some of them use slightly different resistors which lead to different button analogRead() values.

BTW: I do not understand that code:

"LiquidCrystal lcd(8,9,20,21,22,23);"

For which board and which LCD Keyboard Shield is that?

The Arduino Leonardo board does not have pins 20, 21, 22, 23, as far as I can see:

Hi Jurs

Thanks for replying, much appreciated. I am quite new to programming, but have been able to put together quite a few usefull projects.

I'm building quite a big project where I need many digital in and outputs, the board I'm using is a leonardo based board with onboard POE ethernet and SD card. The leonardo can use it's analog inputs as DI/DO thus A1 = D19, A2 = D20, etc.

I don't plug the shield into the board but connect it via a breadboard and thus don't use the shields predetermined DI/DO spots. I need those for the onboard ethernet and SD.
I have tested those DI/DO's and the lcd does work.

The values I get on your code is as follows:

Select = 605
Up = 113
Down = 253
Left = 387
Right = 19

To tell you a bit more about my project, I'm a farmer in South Africa and I find it quite difficult to keep track of all the vehicles's fuel filling data. So what I'm building is a fuel filling logger where the driver swipes his RFID tag, then the vehicles RFID tag and after that fill in the vehicles ODO Hours/Kms on the keypad. After that the pump goes active via a relay and a flowmeter measures the amount of diesel. Once the pump nozzle is put back into the cradle all the individual data is saved on the SD into a .csv file from where it can be loaded to a web server from where I can grab it to my computer for processing. Jeez that is a mouth full! :slight_smile:

Kind Regards
Wagner

WagnerJ:
I'm building quite a big project where I need many digital in and outputs, the board I'm using is a leonardo based board with onboard POE ethernet and SD card. The leonardo can use it's analog inputs as DI/DO thus A1 = D19, A2 = D20, etc.

Then to eliminate any confusion, you should use:

LiquidCrystal lcd(8,9,A2,A3,A4,A5);

WagnerJ:
The values I get on your code is as follows:

Select = 605
Up = 113
Down = 253
Left = 387
Right = 19

That's a bit different from the values on my LCD Keypad Shield.

With those values I'd change the button detection code in the example code from reply #8 to:

  if (adc_key_in <50) returnValue= btnRIGHT;
  else if (adc_key_in <195) returnValue= btnUP;
  else if (adc_key_in <330) returnValue= btnDOWN;
  else if (adc_key_in <450) returnValue= btnLEFT;
  else if (adc_key_in <999) returnValue= btnSELECT;

WagnerJ:
To tell you a bit more about my project, I'm a farmer in South Africa and I find it quite difficult to keep track of all the vehicles's fuel filling data. So what I'm building is a fuel filling logger where the driver swipes his RFID tag, then the vehicles RFID tag and after that fill in the vehicles ODO Hours/Kms on the keypad. After that the pump goes active via a relay and a flowmeter measures the amount of diesel. Once the pump nozzle is put back into the cradle all the individual data is saved on the SD into a .csv file from where it can be loaded to a web server from where I can grab it to my computer for processing. Jeez that is a mouth full! :slight_smile:

So at the moment I don't see any need in your application for manually entering numbers by using buttons.
But maybe you want to have some "special functions" which will become active after entering "magic numbers".

Just let me know if you have problems with entering data into the application.

After changing the 'split values' for button detection to fit your Keypad Shield, the code from reply #8 should also work with your Keypad Shield buttons.

I tried the new values you gave me, but still no response from the keypad. The Lcd hangs on "Input value: " with the cursor blinking after the ":"

I'll have to try one of my UNO boards since this leonardo is being pushed to the limits. I might end up having to go with a Mega since I have a feeling I'm running out of SRAM. Just for the record, I did run the code from reply #8 updated with the values from your last reply on the leonardo, but without all the other code I use in my program. All the wires are still connected though.

jurs:
So at the moment I don't see any need in your application for manually entering numbers by using buttons.
But maybe you want to have some "special functions" which will become active after entering "magic numbers".

The reason why I need the keypad and LCD is because there are no "magic way" of getting the ODO meter reading from the tractor's clock into my database. I need the ODO reading to keep track of the fuel consumption per hour or km. Or am I thinking about it wrong?

Kind Regards
Wagner

WagnerJ:
I tried the new values you gave me, but still no response from the keypad. The Lcd hangs on "Input value: " with the cursor blinking after the ":"

That's very strange. Because this code never seems to get executed:

  if (lcdNeedsUpdate)
  {
    lcd.setCursor(0,1);
    lcd.print(value);
    lcd.setCursor(cursorPos,1);
    lcdNeedsUpdate=false;
  }

Because of initialized "boolean lcdNeedsUpdate=true;" there should be initially two lines on the display:

Input value:
00000

with the cursor block blinking in the second row on the leftmost '0'.

If you cannot see those two lines on the display, you are most likely not running the same sketch I posted in reply #8 and did perhaps some modifications or copied part of the code into another sketch which is running.

I cannot explain why you don't get the "00000" in the second row of the display.

WagnerJ:
The reason why I need the keypad and LCD is because there are no "magic way" of getting the ODO meter reading from the tractor's clock into my database. I need the ODO reading to keep track of the fuel consumption per hour or km. Or am I thinking about it wrong?

Using digital or analog buttons to enter a 5-digit odometer reading should only consume very little RAM.
Other possibilities like sending codes from a IR reomote control would use much more RAM.

Hi Jurs

Ok, got it working perfectly on my spare UNO. Will have to figure out why the same code doesn't want to work on my Leonardo. The only thing I changed in the code are the lcd pin numbers. So many things to figure out and so little time :slight_smile:

Thanks for all your help.

WagnerJ:
Ok, got it working perfectly on my spare UNO. Will have to figure out why the same code doesn't want to work on my Leonardo. The only thing I changed in the code are the lcd pin numbers.

How long are the cables from the Arduino A0-pin to the shields buttons and resistors?

Probably long cables crossing other cables with mains power or chhanging currents, and the cables are catching electrical interference, so that no stable analog reading is possible.

With the LCD Keypad Shield directly stacked on the board, cable lengths are short und signals from the button to the A0-pin are undisturbed.

jurs:
How long are the cables from the Arduino A0-pin to the shields buttons and resistors?

Probably long cables crossing other cables with mains power or chhanging currents, and the cables are catching electrical interference, so that no stable analog reading is possible.

With the LCD Keypad Shield directly stacked on the board, cable lengths are short und signals from the button to the A0-pin are undisturbed.

2 short cables routed through bread board, but it does cross the RFID's serial1 cable, and the RTC's I2C cables

Thanks for all your trouble, at least I know where to look for the problem now.

WagnerJ:
2 short cables routed through bread board, but it does cross the RFID's serial1 cable, and the RTC's I2C cables

Thanks for all your trouble, at least I know where to look for the problem now.

If there is too much fluctuation and noise in the A0 ADC measurements, you could increase the tolerance for button detection, like perhaps:

  if (abs(adc_key_in1-adc_key_in)>3) return btnNONE; // if ADC reading is not stable, return btnNONE

But the more tolerance is allowed in consecutive measurements, the higher the risk for detection of a wrong button pressed.