Adapting standard keypad code to use N/C buttons

I want to use an Arduino to read a matrix of switches that are wired up like a standard keypad. The hitch is, the switches are normally closed. I am trying to modify the following code from the Arduino Cookbook to suit my purposes, but I can't get it to do what I want:

/*
Keypad sketch
prints the key pressed on a keypad to the serial port
*/

const int numRows = 2; // number of rows in the keypad
const int numCols = 2; // number of columns
const int debounceTime = 20; // number of milliseconds for switch to be stable

// keymap defines the character returned when the corresponding key is pressed
const char keymap[numRows][numCols] = {
{ '1', '2' } ,
{ '4', '5' }
};

// this array determines the pins used for rows and columns
const int rowPins[numRows] = { 7, 6 }; // Rows 0 through 3
const int colPins[numCols] = { 5, 4 }; // Columns 0 through 2

void setup()
{
Serial.begin(9600);
for (int row = 0; row < numRows; row++)
{
pinMode(rowPins[row],INPUT); // Set row pins as input
digitalWrite(rowPins[row],HIGH); // turn on Pull-ups
}
for (int column = 0; column < numCols; column++)
{
pinMode(colPins[column],OUTPUT); // Set column pins as outputs
// for writing
digitalWrite(colPins[column],HIGH); // Make all columns inactive
}
}

void loop()
{
char key = getKey();
if( key != 0) { // if the character is not 0 then
// it's a valid key press
Serial.print("Got key ");
Serial.println(key);
}
}

// returns with the key pressed, or 0 if no key is pressed
char getKey()
{
char key = 0; // 0 indicates no key pressed

for(int column = 0; column < numCols; column++)
{
digitalWrite(colPins[column],LOW); // Activate the current column.
for(int row = 0; row < numRows; row++) // Scan all rows for
// a key press.
{
if(digitalRead(rowPins[row]) == LOW) // Is a key pressed?
{
delay(debounceTime); // debounce
while(digitalRead(rowPins[row]) == LOW)
; // wait for key to be released
key = keymap[row][column]; // Remember which key
// was pressed.
}
}
digitalWrite(colPins[column],HIGH); // De-activate the current column.
}
return key; // returns the key pressed or 0 if none
}

Note that I did reduce the keypad size for my breadboard test. This works fine with N/O buttons.

I thought if I just changed the two "digitalRead(rowPins[row]) == LOW)" lines to "digitalRead(rowPins[row]) == HIGH)" it would work, but it just keeps spitting out "Got key 5" no matter what I'm pressing. I think I could invert the row outputs with hardware inverters, but it seems like it should be more efficient to do it in code. Any idea what I'm doing wrong?

Also, I don't understand what is happening with "char key = getKey()" and "char getKey()". "getKey" just seems to be a variable as I can change it to anything and the code still works, but I don't understand what the parenthesis are about.

Thank you for the free education!

A keyboard matrix usually works by finding the one X/Y position that is closed by a keypress. If you use N/C buttons they will be all closed, so I can’t see how it would work.

Also, I don’t understand what is happening with “char key = getKey()” and “char getKey()”. “getKey” just seems to be a variable as I can change it to anything and the code still works, but I don’t understand what the parenthesis are about.

This calls a function “getKey”:

char key = getKey();

This is a function prototype for getKey, but it doesn’t call anything:

char getKey();

Couldn't it look for the one x/y that is open just as easily, or am I thinking about this all wrong?

To avoid this issue I think you will need diodes. Having most switches closed gives multiple paths for the current when it tries to test which key is pressed. With diodes I think you can avoid that.

Draw a crosshatch pattern. 4 vertical lines, 4 horizontal lines.
Connect each vertical line with each horizontal line, where they cross, but leave 1 unconnected.
The unconnected one represents an open switch.
We assume all lines are pulled high.
Now, on the end of the horizontal line that's unconnected, place GND
On the other three, place 5V
Now, on the bottom of each vertical line, place a 5V or a GND, depending on whether or not 5V or a GND is connected.

If you placed 5V on any vertical lines, you're incorrect.
The single GND will drive all 4 vertical lines to GND.
Why? Look at the connected lines on the one with the GND
There is a path through three out of the four vertical lines, and from all those three vertical lines, back to the vertical one with the open switch, through the other three horizontal lines.

Slaps forehead D'oh! You are absolutly right. And here I am thinking I'm a hardware guy :stuck_out_tongue: Thanks for your help, folks!

So now that you're on track, let me ask you a few questions.

  1. Are you stuck using N/O switches?
  2. Do you realize that keypads are EXTREMELY low-cost?
  3. How many keys do you need in your keypad?

If #2 doesn't appeal to you, would you consider an additional circuit?

The MC14532B is an 8-bit priority encoder. It will not handle multi-key presses, but it will giive you 3 outputs that are the binary value of the highest priority input.

One of these, pluas a single switch, would give you a decimal keypad (0-9). Two would give you a hex keypad (0-F), and so on.

One bonus is that the pin usage on the Arduino is is lower than a standard matrix keypad .

Or, you could use analog inputs, with carefully chosen resistor values, creating voltage dividers, fed to an analog pin. See the attached image. You could use 1K or 2K resistors.

AnaKeys.GIF

Yes, well, you see it's not really a keypad at all--I was just trying to co-opt keypad principles. It is really an array of 27 reed switches in a 3x9 grid. There are 27 magnets above these switches, and I just need to know which one is pulled away. So they are actually N/O switches, but in a N/C configuration.

I know there are SPDT reed switches, but I've already got 40 normal N/O ones so I'll have to make it work. There's also a way to make N/O ones into N/C ones with a second magnet below, but it won't work with this setup.

I was thinking of doing it analog with resistors before I had the "brilliant" inspiration to mimic a keypad, so I'll probably go back to that.

I feel a bit foolish now! That analog scheme in the picture I posted won’t work with N/C switches. :~

Oh well, we just need to change the scheme a bit.

AnaPad.GIF

A row of N/C switches, each with a different resistor in parallel would work, yeah? 0 ohms if everything is closed, single resistor value when one is open.

The good thing is that there will never be more that one switch open at a time, so multiple "keypresses" are not an issue.

Got to love when you spend a lot of time trying to make your "improved" idea work, when your original plan would have worked better in less time :stuck_out_tongue:

Thanks again, Nick and lar3ry!

PS: Oh, I see you've posted just that. Brilliant! Now just a lot of dead-bug soldering in my future :slight_smile:

Don’t know if you thought about it or not, but the resistor values can’t all be the same, which is a biy of a drawback. Because of that, I’d recomment doing it on multiple pins, so the tolerances won’t add up to catch you. You could also use a calibrate function to zero in on the average value to give you a range of analog level window to check for.

Yup, definitely. The Arduino won't be doing much else, so I've got all the pins I want. And finally a use for all those weird value and out-of-tollerance resistors I've been collecting.