Matrix Keypad - Optimisation

Triggered by a question to reduce pin usage for matrix keypads, I was thinking of this...

  • To each input of the keypad connect a fixed resistor - a different known value with significant difference in resistance for each input. and connect them all in parallel to a digital output pin on arduino
  • Calculate what will be the drop in 5v when it passes through each of the resistors
  • wire the inputs up from the keypad in parallel and hook it up to an analog input
  • based on the values read you will be able to identify from where it originated

Will this work?

I will test it and post results however any suggestions are welcome.

Cheers,
Pracas

I believe that is how button matrices work, so yes, it should work as you described. Your don't necessarily even need to do the calculations, as you can just do a few test runs and see what values you get from the analog_in for each different button pressed.

EDIT: one thing I am not sure about is your step 2, the one involving the connection in parallel to a digital out... why do you need this?

to use a single pin as input and a single pin to read output. so we can work out all sizes of keypads on 2 pins. Thats the idea.

That would work fine, but how will you ensure that only 1 button is pressed at a time, and no one is hitting 2 or more - either by mistake or on purpose?

I think the initial idea wont work. Because there wont be any load the resistance might not actually show a decrease in voltage. So here what is currently being tested

Hook up 8 different resistance to the 8 pins of a 4x4 key pad. Wire 4 of the row or column pins to 5v supply directly and the rest 4 in parallel to an analog input and measure voltage / compare it to preset values. if we ensure that the resistance values don't add up to similar combinations, then we might even be able to avoid multiple presses. Will keep this updated.

Cheers,
Pracas

To each input of the keypad connect a fixed resistor - a different known value with significant difference in resistance for each input. and connect them all in parallel to a digital output pin on arduino

Why would you need a digital out pin? Can't you use Vcc instead?

EDIT: Should have read more carefully. You already told you won't use digital out after all.

Wire 4 of the row or column pins to 5v supply directly and the rest 4 in parallel to an analog input and measure voltage / compare it to preset values.

This is probably something like you were thinking of, although for 4x3 keypads:

Resistors R_row1 and R_col1 can be 0 ohms, so they are not really needed. R_d is a voltage divider resistor. It also works as a pull down resistor when no key is pressed. Here is also an OpenOffice spreadsheet for calculating the resistor values: http://www.siterunner.fi/liitteet/outijapekka/materiaalipankki/5.ods . I haven't tried it yet, so I am not really sure if it works.

EDIT: Small error in the schema.

I managed to put it together. I works in princible, but is barely usable. The problem is that the reading from analogIn is not stable. It takes a long time (like a second or maybe more) before the reading from ADC stabilise. I could be possible to implement some kind of filtering, but wouldn't still respond quicky enough to a key press.

Perhaps the problem is wrong kind of keypad. Mine was ripped from an old phone. Is has rubber keys that short circuit the contacts on the PCB when a key is pressed. A tactile keypad with proper switches may work better. I also used quite big resistor values and long wires. Maybe there is too much impedance that causes the lond delay before the reading from analog in stabilises.

I managed to try it and it did work. But noise can blow up the whole thing. I think it would be better than a regular keypad in terms of response rate as the scanning is limited to a single pin and then lookups.

I think it would be better than a regular keypad in terms of response rate as the scanning is limited to a single pin and then lookups.

What do you mean by response rate? Do you mean the number of keypresses per second you can read? In my implementation is was quite the opposite, the response rate was very slow because I had to wait the reading from analog input to stabilise, i,e., it took long time before the voltage rose after I pressed a key.

What resistor values did you use?

Maybe something else could be used like send a serial signal from the keypad. Did a quick search and found this.

http://www.rentron.com/Ser-Key.htm

Its not as simple as using only resistors but it would probably be more reliable and easier to code for. Or if not serial then maybe a morse code like signal could be sent from the keypad. Overall the circuit would be more complex but it would use less pins on the Arduino. Cheers!

The resistor approach would not work even if noise would not be present. There are some LCD shields with a similar setup (but less resistors). This works "mostly". The issue is temperature drift. You resistors can drift enough to be detected as the wrong one --> will fail especially in summer.

The standard approach is to go for shift registers. This is described in the playground. You can either use a shift register for reading or you can use a shift register for pulling low keys in order to read if they are pressed. The second approach would require one bit more but would allow to pull all low at the same time. Then this could be detected during an interrupt which then determines which was actually pressed.

The resistor approach would not work even if noise would not be present.

Perhaps will we can still have a usable solution if we have an own analog input for each column. For 4x3 keypad we will then need 4+1 resistors (one for each row and one divider/pulldown resistor) and three analog inputs, which is still less than seven pins it will require if we connect the keypad directly to Arduino's digital inputs.

The standard approach is to go for shift registers.

That is certainly a better way to do it. I will probably go that way if I am going to use the keypad for something real. Unfortunately I don't have a shift register around, but I have bunch of resistors instead.

Using 1 analog input, and 7 resistors

270 ohm between the column pins.
1K ohm between the row pins.
2.7K ohm between, the analog input and ground.

5v is connected to column 0 as follows:
5V--Col0--270--Col1--270--Col2--270--Col3

the analog input is connected to row 3 with a 2.7K resistor between it and ground as follows:

Row0--1k--row1--1k--row2--1k--row3--analog in--2.7K--GND

this will give the following values of resistance divided against the 10k keys listed are from col0, row0 to col3, row3

the Vdrop is the voltage drop across the keypad matrix

Vout is the voltage supplied to the analog input

the analog value is the calculated value the arduino will read

the range is what I figure could be defined as the min max for each button with a bit of a buffer between each range

I used a spread sheet to figure this all out and rounded it

Button R Vdrop Vout Analog Val. Range
1 - 0 ohms 5V 0V 0 0-38

2 - .27k ohms 4.55V .45 93 55-125

3 - .54k ohms 4.17V .83 170 138-190

4 - .81k ohms 3.85V 1.15 236 217-250

5 - 1K ohms 3.65V 1.35 276 270-298

6 - 1.27K ohm 3.4V 1.6 327 306 - 347

7 - 1.54K ohm 3.18V 1.82 371 353 -384

8 - 1.81K ohms 2.99V 2.01 410 399-420

9 - 2K ohms 2.87V 2.13 435 430-450

10 - 2.27k ohms 2.72V 2.28 467 455-480

11 - 2.54K ohms 2.58V 2.42 496 485 - 500

12 - 2.81k ohms 2.45V 2.55 522 515 - 525

13 - 3K ohms 2.37V 2.63 538 530 - 545

14 - 3.27k ohms 2.26V 2.74 560 550 - 565

15 - 3.54K ohms 2.16V 2.84 580 570 - 585

16 - 3.81K ohms 2.07V 2.93 599 590 - 610

perhaps it would be easier to just say Calculated value +/- 10 which is limited by the resolution toward the end of the list. I would think that would be enough of a range.

I haven't tested this yet, but plan to soon; There may be better values of resistors to use, but I think this will work.

Theoretically if there were any concern about the value of the resistors changing they all would likely do so and should keep the same output voltage

If you see any major flaws to my reasoning, please let me know.

-carl

PS i tried using the table tool, but it made little sense and couldn't find info on how to properly use it

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
https://www.jameco.com/Jameco/Products/ProdDS/171969.pdf

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

Carls list looks like a good one for detecting single button presses.
If you want to be able to detect multiple button presses, the Vout of a button needs to be more than the total of all previous Vout values combined (presenting a exponential curve).
This also means the last button has the biggest tolerance issue, since it needs to take into account the tolerances of all the buttons before it.

To give a small list (that doesn't take into account tolerances):
button1 - Vout: .1V
button2 - Vout: .2V
button3 - Vout: .4V
button4 - Vout: .8V
button5 - Vout: 1.6V
(notice how this resembles binary counting?)
.6V is measured, meaning button 2 and 3 are pressed. 2.1V? buttons 1, 3 and 5.

Wow - check the prices on those - I think it would be less expensive to just add another atmega to your design! And then you could program in other functionality as well. Do some searches in the forum, I believe the crystal can be shared between chips, as well as the reset pin, so shouldn't need much in the way of other components.

Found this article http://www.avr-asm-tutorial.net/avr_en/keypad/keyboard.html See Chapter 3: Connection to an ADC with a resistor matrix. Although code is in assembler the resistor values are interesting as they seemed quite well tuned.

I've modified a circuit from an instructable http://www.instructables.com/id/Arduino-3-wire-Matrix-Keypad/step2/Wiring-up-the-resistors/

it uses very specific valued resistors and puts out a pretty nice linear value, rather than exponential. the key was to make a divider out of the keypad with a separate array for the columns going from ground to the analog input; and another array for the rows coming from 5 volts. this puts the analog input between the two arrays

In other words:

One array is

5V - 180ohm - row A - 680ohm - row B - 3.3kohm - row C - 15kohm - row D

This puts from 180 ohms to 19160 ohms from 5V to the analog in depending on which row the button is pressed

the other array is

Analog input - col1 - 1.2K ohm - col 2 - 1K ohm - col3 - 820 ohm - col4 - 1k ohm - ground

this puts from 1000 ohms to 3020 ohms to the analog in to ground depending on which column the button is pressed.

this makes a voltage divider with 16 different values.

As for the problem with temperature drift, I would think it would be proportional. whereas the resistance on one side is going to change a similar percentage to the resistance on the other side, leaving the output essentially unchanged. at least unchanged enough that it won't be confuse which button is being pressed.

I put this into a spreadsheet to better get a baseline range, that may need a little tweaking once hooked up, but if you use if arguments, with the values halfway between the nominal values for each button it will give you a broad range for each button it to fall within and render the same results

I had to add four conditions and change the values since I added a 1.2K ohm resistor for the fourth column. This is the code from the instuctable modified for 16 keys versus the 12 keys it had.

int keypressed = 0;
int keyboardPin = 0; // Analog input pin that the keypad is attached to
int keyboardValue = 0; // value read from the keyboard

void setup(){

Serial.begin(9600); //hardware serial to PC

}

void loop(){

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

readkeyboard(); //get the value of key being pressed "keypressed" i.e. 0-9

}

//read the keyboard routine
void readkeyboard(){
keyboardValue = analogRead(keyboardPin); // read the value (0-1023)
if (keyboardValue <25){keypressed = 0;}
if (keyboardValue >25) && (keyboardValue < 68)){keypressed = 0;)
if (keyboardValue >68) && (keyboardValue < 108)){keypressed = 0;)
if (keyboardValue >108) && (keyboardValue < 153)){keypressed = 0;)
if (keyboardValue >153) && (keyboardValue < 186)){keypressed = 0;)
if ((keyboardValue >186) && (keyboardValue < 254)){keypressed = 1;}
if ((keyboardValue >254) && (keyboardValue < 361)){keypressed = 2;}
if ((keyboardValue >361) && (keyboardValue < 457)){keypressed = 3;}
if ((keyboardValue >457) && (keyboardValue < 525)){keypressed = 4;}
if ((keyboardValue >525) && (keyboardValue < 621)){keypressed = 5;}
if ((keyboardValue >621) && (keyboardValue < 738)){keypressed = 6;}
if ((keyboardValue >738) && (keyboardValue < 812)){keypressed = 7;}
if ((keyboardValue >812) && (keyboardValue < 854)){keypressed = 8;}
if ((keyboardValue >854) && (keyboardValue < 898)){keypressed = 9;}
if ((keyboardValue >898) && (keyboardValue < 945)){keypressed = 0;}
if ((keyboardValue >945) && (keyboardValue < 970)){keypressed = 0;}
if (keyboardValue >970){keypressed = 0;}
//NOTE: the values used above are all halfway between the value obtained with each keypress in previous test sketch

while (keyboardValue > 25) {
delay (100);
keyboardValue = analogRead(keyboardPin); // read the value (0-1023)
}//wait until key no longer being pressed before continuing

Serial.println(keypressed); // print the value back to the Serial view window on your PC
delay(1000); // wait 1000 milliseconds before the next loop
}
//end of read the keyboard routine

as the instructable says this is a bit clunky, but functional code.

the next thing I need to do is figure out what I want the values to correspond to. the values it uses pretty much gives you a bunch of extra 0 keys for the non-numeric keys. that's where it gets a little confusing for me. I'd need to assign names to each key and then reference that name in the code. since some keys will have functions other than numeric values

I was reading about the map function which separates an analog input into a predetermined number of steps, but I'm not sure how it would mesh with the values the keypad matrix will put out.

I have the spreadsheet I mentioned in open office, I also have a fritzing file with the schematic, It may not have the resistor values on it as fritzing only has certain resistance values. shoot me a message if you need these for better clarification

-carl

Could call the extras A, B, C, D, #, * like the velleman keypad uses.