Keypad interface

I posted this to an older thread, but in case it get’s overlooked in that discussion, I’m posting it to a new thread.

I’ve been trying to figure out how to interface a keypad with fewer than 8 inputs on the arduino using a single analog value. upon playing with the values I found it very difficult to find the proper values as the difference in values toward one end becomes very miniscule. there’s an instructable for a 3x4 keypad, but modifying it to 4x4 makes it imperative to use very tight tolerance resistors (1%)

that said, I found two decoder IC’s, that will put out a 4bit parallel or serial value that runs off the arduino clock.

This one seems simpler ede1144/p by elab

and this one is 74c922 by Fairchild

anyone have any experience with these chips?

I’m leaning toward the elab,

Any input would be much appreciated.

-carl

It seems to me that 5% should be enough?

You've got 4*4 buttons = 16.

You have a precision of 10 bits which can represent 1024 values. This leaves each button headroom of 64values.

64 * 100 / 1024 = 6.25%

Well, that's in theory anyways. Even worse, it's suggested theory, I have no idea if it can be applied, but I would think so.

In theory, there is no difference between theory and practice.

You'd think that, but some values are closer than 2mv apart while the ones at the beginning are upwards of 30mv apart. If I figure in +-10% it causes overlap in the values where the low end tolerance of one value is higher than the previous value's low end tolerance. so there's the chance there would be ambiguity between the closer values or else there would be such a small range to look at 2mv that a slight change in value may throw it all out of whack and it not even looking in the right range for the corresponding key.

if you need to see my math, I have an open office document that allows you to see how I determined this.

thanks-

Carl

8 to 3 encoder

http://www.piclist.com/images/www/hobby_elec/e_cpld5_4.htm

For each of 8 inputs you get a binary representation out.

Since input 1 causes 0 out you only get to use seven keys unless you go through a keyscan routine. It saves a little I/O.

Even more would be a more complex I2C or other I/O expander.

I believe now with FPGA/CPLD solutions proprietary keyboard decoders are hard to get.

Saw similar question in another thread today, have a look at http://www.avr-asm-tutorial.net/avr_en/keypad/keyboard.html Chap 3.

I saw that tutorial, but I have a 4x4 matrix, which makes things 33% tighter. breaking 5v into 12 values seems easier than 16. I try to add another resistor for the fourth column and it gets confusing would I go smaller Analog -col1 - 1k - col2- 820 -col3- 640?- Col4 - 1k - ground or larger analog -col1- 1.2K - col2 - 1k - col3 - 820 - col4 - 1k - ground

Still playing with the values

thanks for the input,

Carl

I figured it out, I had it wired wrong, now it gives a pretty linear value for the fourth column I added a 1.2k

Now to see which resistor values I already have

thanks for the input,

Carl

hooked it up and used the serial monitor; the values are all over the place, I haven't really set it up with any complex program, but with the values all over the place when pressing the buttons, it hardly seems worth it until I get a more uniform result. I'm using a membrane type keypad and it seems to vary in resistance depending on how firmly you press the button, I may need to go with the interface chip; that has built in debouncing, but uses four inputs. it can send it as serial signal, but that is a little beyond me at this point.

I may try devising code to only recognize values that it stays at the same value ( with a tolerance range +/- twenty or so) for a certain amount of time, (say 50ms) then compare average value to the ranges I program in. I'm going to try the program as it stands but I'm pretty skeptical of the reliability, and expect false values on the increase or decrease. it will also give me practice on output as well, probably just turn on a different LED for each key and hold it there for a half second. we'll see where it goes from here.

I'm using a membrane type keypad

Which one? The switches under the membrane are either open or closed. They do not have partially pressed states.

they're normally open, but when I press it and the value It evens out at is 500 for a certain key, as it is pressed it shows a few different values like 120 250 and leveling out at around 500 if it is pressed a little harder it may increase to 510 it's difficult to read as the values scroll by so fast on serial monitor, is there a way to sample the values less frequently? perhaps I'm overthinking this, but if it reads those lower values, isn't it going to recognize those first?

I feel stupid for asking these hypotheticals, I need to get it hooked up to some means of output, perhaps the LCD, I need to figure that out too. pretty much the hello world program with different string for each button

I'll keep at this until I get it, I get a little discouraged when things don't work perfectly the first time; generation X instant gratification disorder

thanks for not just ignoring me, and making me think about it further

-carl

is there a way to sample the values less frequently?

After each Serial.print(), add a small delay (delay(100);).

This is a very legitimate scheme when you are using hard metal-to-metal contact switches.

as I understand it this is a metal to metal contact, http://www.satistronics.com/images/v/201002/12668583500.jpg

I think they are referred to as snap dome buttons,

the carbonized rubber buttons are the kind you see in tv remotes

It does click, I think what was happening was when I tested it originally I didn’t have a hard back so when I pressed it it wasn’t always clicking, it was pinching the back toward the dome if that makes any sense, when I placed it on a hard surface and tested it it was more uniform,

the lower values were a much tighter range with a pronouced dead band between the key values;

the last five overlap quite a bit which confuses me, it would make sense if the last four overlapped, the last row resistor would need to be adjusted; I figured this all out in a spreadsheet, and granted the resistor values varied some from the color code, but that was a idealized example, that said, I still would expect the values to be more separated. I may need to tweak some values, I suppose it could just be the last row, the overlap on the “menu” button might be due to the “.” button’s overlap

sometimes just writing this all down make things make more sense, I’ll probably just put a trimpot in series with the last row resistor to see If increasing that will make a big difference. but with overlap on all the last five it still confuses me; I’ll try tweaking the 1.2 k value which confuses me as I went from 3x4, to 4x4 adding the 1.2k. if it were that one, I would have guessed it would throw off a column, not a row as it is.

below is the layout of the keypad and the values I got on serial monitor, I pressed the button numerous times for varying duration and then took the highest and lowest values.

5v
|
1k
|
1 2 3 enter
|
820
|
4 5 6 erase
|
1k
|
7 8 9 menu
|
1.2K
|
. 0 - +

A0 – 180 ----- 670 ------ 3.3K ------- 14.8K -------Gnd

1 39 - 51
2 80-91
3 117-136
enter 139-178
4 181-196
5 277-309
6 356 - 411
erase 424-503
7 510 - 531
8 599-674
9 745-773
menu 792-833
. 745-829
0 749-911

  • 900-947
  • 912- 968

A few other things I’m curious of- when is the value It reads evaluated against switch/case conditions; is it the first value it puts out, is there a way to delay it’s reading by a few millisecond while it normalizes some; it seems the most extemely off the values is the first and last values read in the serial monitor,

If I program in a dead band between the closer values, when I press a button and the value reads within that band and outside that band will it still read the value when it is outside the band

for example between 6 and erase and erase and 7 there is little slack, but every time I pressed the button it would always show values outside the area I plan to exclude for example between erase and 7 there is only about 7 values, if I widen that gap in my cases to 20 in case it decides to overlap in the next thousand times versus the 20 or so times I pressed it. the only time the values could be confused were the first and last values it showed on serial monitor. but in all the button presses it always returns a value squarely within the ranges at least for the lower values (not the last five that need hardware tweaking not software)

I’ll try these things but this is new to me.

another thing that puzzles me is when I tried attaching an lcd that would output the value as it was pushed; It only seems to display the value once. and I need to reset it to get it to read the next key press
I’m missing some thing, but am not sure what. any insight would be much appreciated

-carl

char keypressed = 0;
int keyboardPin = 0;    // Analog input pin that the keypad is attached to
int keyboardValue = 0;   // value read from the keyboard
int k = 0; //
int kbvalue = 0;//
int kbv = 0;
#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,5,4,3,2);

void setup(){
Serial.begin(9600);// hardware serial to PC
lcd.begin(16, 2); //sets up columns and rows
}

void loop()
{
  int sensorValue = analogRead(A0);
  Serial.println(sensorValue, DEC);
  delay(200);
  
  lcd.setCursor(0,1);
  keyboardValue = analogRead(keyboardPin);   // read the keyboard value (0 - 1023)
  while (keyboardValue < 25)
  {
    //do nothing until a key is pressed
    keyboardValue = analogRead(keyboardPin);
    delay(50);
  } //end of do nothing till a key is pressed
                                            
  int k = kbValue(keyboardValue);   // maps the value of the key being pressed "keypressed" i.e. 0-9
  
  Serial.println(k);  // print the value back to the Serial view window on your PC
  delay(1000);
}

// interpret the keyboard routine
int kbValue(int kbv)
{
  if (keyboardValue <=25)
 {
delay (30);}//debounces keypress; does nothing until a key is pressed
  else if ((keyboardValue >25) && (keyboardValue <=68)){keypressed = 1;}
  else if ((keyboardValue >68) && (keyboardValue <=108)){keypressed = 2;}
  else if ((keyboardValue >108) && (keyboardValue <=153)){keypressed = 3;}
  else if ((keyboardValue >153) && (keyboardValue <=179)){keypressed = 'enter';}// a key
  else if ((keyboardValue >179) && (keyboardValue <=225)){keypressed = 4;}
  else if ((keyboardValue >225) && (keyboardValue <=325)){keypressed = 5;}
  else if ((keyboardValue >325) && (keyboardValue <=415)){keypressed = 6;}
  else if ((keyboardValue >415) && (keyboardValue <=508)){keypressed = 'erase';} //b key
  else if ((keyboardValue >508) && (keyboardValue <=560)){keypressed = 7;}
  else if ((keyboardValue >560) && (keyboardValue <=680)){keypressed = 8;} 
  else if ((keyboardValue >680) && (keyboardValue <=773)){keypressed = 9;}
  else if ((keyboardValue >773) && (keyboardValue <=834)){keypressed = 'menu';} // c key
  else if ((keyboardValue >834) && (keyboardValue <=898)){keypressed = '.';} // menu key
  else if ((keyboardValue >898) && (keyboardValue <=945)){keypressed = 0;}
  else if ((keyboardValue >945) && (keyboardValue <=970)){keypressed = '-';} //enter key
  else if (keyboardValue >970){keypressed = '+';} //d key 

switch(keypressed){

case 0:
lcd.print(0);
delay(250);
// do 0
break;
  
case 1:
lcd.print(1);
delay(250);
// do 1
break;

case 2:
lcd.print(2);
delay(250);
// do 2
break;

case 3:
lcd.print(3);
delay(250);
// do 3
break;

case 4:
lcd.print(4);
delay(250);
//do 4
break;

case 5:
lcd.print(5);
delay(250);
// do 5
break;

case 6:
lcd.print(6);
delay(250);
// do 6
break;

case 7:
lcd.print(7);
delay(250);
// do 7
break;

case 8:
lcd.print(8);
delay(250);
// do 8
break;

case 9:
lcd.print(9);
delay(250);
// do 9
break;

case 'a':
lcd.print("a");
delay(250);
// do a
break;

case 'b':
lcd.print("b");
delay(250);
// do b
break;

case 'c':
lcd.print("c");
delay(250);
//do c
break;

case 'menu':
lcd.print("menu");
delay(250);
// do menu
break;

case 'enter':
lcd.print("enter");
delay(250);
// do enter
break;

case 'd':
lcd.print("d");
delay(250);
// do d
break;
}
 
while (keyboardValue >=25);          //wait until key no longer being pressed before continuing
  
return keypressed;
}