Five Button Keypad with Four Wires

Hey everyone. I recently picked up a new keypad for my project and it works a bit differently than the one I was using before. My first keypad had 5 buttons and 6 wires. Each button had it's own wire, and they all shared a common wire. Easy enough to read with digitalRead.

This new keypad has four wires for five buttons. I've taken the keypad down as far as I can without breaking something ($75!), I can see everything for A, D, and E, but the other ones are on the backside of the board, away from what I can see.

As far as I can tell, the circuit is as follows:

Buttons A, D, and E are directly connected to pins 1, 3, and 7, respectively

Button B reads ~645 ohms to both 1 and 3, but when measuring ohms between pins 1 and 3, it is open. (that why I guess the diodes)

Button C is same as B, but with pins 1 and 7.

The only way I can think of to differentiate the pins is to do an analog read of each and determine by where the values are.

For example, if pins 1 3 or 7 read 1023 or close, then buttons A, D, or E is pressed, respectively.

If pins 3 or 7 read something in the middle, lets say between 300 and 700, then buttons B or C are pressed. (I'll have to read them to see where they actually measure at)

Would this be a good method, or is there some better standard way for this?

Are you sure that there are resistors in there as well as diodes? That is, can you see resistors on the PCB? It's just that a diode's forward voltage drop will show up on a meter just like a few hundred Ohms of resistance. Looking at the circuit, I'd expect the Pin 6 to be grounded and the other three pins to be connected to pull-ups and digital inputs. Then, you'd have various combinations of pins pulled LOW when you press buttons.

If you have analog inputs to spare, you could use 3 of them to read the keyboard.

Tie the common pin to ground.

Add a "pull-up" of about the same size as the resistors on the keypad to each of the other 3 pins, and connect those pins to analog input.

If the input is about 5V, no key is pressed. If it's about 0V, one of the keys with no series resistor is pressed. If it's about 2.5V, one of the keys with a resistor is pressed.

Take special not of that "about": you'll have to do some tweaking to account for variations in resistor values, etc. I'd start with thresholds of <1.5V for "low", 1.5-3.5V for "middle" (i.e., "key with resistor pressed"), and 3.5V for "no key".

If you don't want to use up analog pins, you could use comparators (like a couple of LM339s) to translate the analog levels into a set of digital signals ("1 low", "1 medium", "3 low", "3 medium", "7 low", and "7 medium"). To minimize the number of Arduino pins used, you could read those digital signals with an I2C or SPI port expander chip.

Ran

I cannot see the resistors or diodes, I'm assuming this only. I can't look at the backside of the board without possibly breaking something.

I know only the following:

From switch B to Pin 1, I get ~645 ohms. If I switch leads, I read open. I assume a diode from this.

From switch B to Pin 3, I get ~645 ohms. If I switch leads, I read open. I assume a diode from this.

From Pin 1 to Pin 3, I read open, not 1290 ohms.

Would a diode alone read ~645 ohms?

Pin usage is not of much concern. I only need three other pins for digital output for my project. (backlight, lock, unlock) So I can use all but those 3 for reading the keypad. I plan to use the Arduino Nano for it's size and power consumption (not powering FTDI, etc).

Anachrocomputer, I didn't even think of that. Would there be a downside of reading them with digitalRead only? I assume you mean something like this:

if (digitalRead(Keypad1) == LOW) {button A is pressed}
if (digitalRead(Keypad1) == LOW && digitalRead(Keypad3) == LOW) {button B is pressed}

Is it possible that when you let go of button B, it would show as button A for one cycle? I suppose I can account for that with a different arrangement (nesting) of readings.

Ran, I'm not interested in adding more parts, as I want this board to be as small as possible, and I have more than enough pins available. The analog read was the first thing that came to my mind, though.

I actually had this project all but finished, but I decided to use a different keypad. This project seems like it has taken forever, but it seems as though learning more has become more important than finishing in a timely matter. I'm thinking of having my board built through BatchPCB or similar, to make it smaller, nicer, and learn a little more. I just received by book on Eagle today!


For some reason it just always seemed more logical for me to pull all the pins LOW, and bring them HIGH with a button press. I see people make circuits both ways, but my method seems to be the opposite of the norm. I suppose it's better to have more wires around as ground, to lessen the likelihood of a power to ground short, but are there other benefits? Are there any differences in power consumption, having pins always pulled high versus low? Or are they negligible? I've been meaning to 'conform' and this board will be my first one.

I'll be able to use the sleep modes this way, I suppose.


This is a Ford keypad, btw.

Would a diode alone read ~645 ohms?

A diode will give you a 0.7 Volt (approx) drop, which on some meters would read like a resistor of a few hundred Ohms. Could you try measuring a known diode with the same meter? See if it reads the same?

Would there be a downside of reading them with digitalRead only?

No, don't think so!

if (digitalRead(Keypad1) == LOW) {button A is pressed}
if (digitalRead(Keypad1) == LOW && digitalRead(Keypad3) == LOW) {button B is pressed}

You'll need to check for the combinations first, or else pressing "B" will look like pressing "A". The first 'if' will be true even when "B" is pressed. So either do this:

if (digitalRead(Keypad1) == LOW && digitalRead(Keypad3) == LOW) {button B is pressed}
if (digitalRead(Keypad1) == LOW) {button A is pressed}

or better still, test both pins, like this:

if (digitalRead(Keypad1) == LOW && digitalRead(Keypad3) == HIGH) {button A is pressed}
if (digitalRead(Keypad1) == LOW && digitalRead(Keypad3) == LOW) {button B is pressed}

Are there any differences in power consumption, having pins always pulled high versus low?

It's "traditional" to have the switch pull the pin low, perhaps because TTL has a higher noise immunity when used in an active-low configuration. But in the case of your switches and diodes, you must connect Pin 6 to Ground, because of the way the diodes are installed.

Thanks, you last example was something like what I had come up with on paper while sitting in Trig class. :slight_smile: I'm typing it up now, but I'll have to make another board to test on, since my original isn't compatible.

Now what do I do with my old one...

All done! I've made the changes (software AND hardware) and it seems to work perfectly! (I had my buttons B & C reversed in my diagram, btw) I'm using 10k ohm pull-ups, and 100ohm pull-downs.

I used something like this:

    if (digitalRead(KeyPin1) == LOW && digitalRead(KeyPin3) == HIGH && digitalRead(KeyPin7) == HIGH) {  
      // Button 1 is pressed
      }

Here's the whole code:

// Car Keypad
// Version 0.30 beta
//
// Michael Casson Rogers (michael.casson@yahoo.com)
// March 31st, 2009
//
// Takes input from keys, compares last 5 pressed keys to the pre-programmed code.
// If last 5 pressed keys are the same as the code, unlock door.
// If last two buttons are pressed simultaneously, lock door.
//
// Input keys are assigned values 1, 3, 5, 7, and 9.
// 
// Time out (reset input keys) after programmable time (default 4 seconds).
// 
// For this program I borrowed heavily from Simple Simon 2.0 by Gian Pablo Villamil
// http://itp.nyu.edu/~gpv206/2006/09/simple_simon_v2.html
//
// and the many helpful people at Arduino Forum!
// http://www.arduino.cc


// Define the pin assignments for the input switches
#define KeyPin1 2
#define KeyPin3 3
#define KeyPin7 4

// Define backlight pin.
#define BackLight 17

// Define the pins the lock and unlock relays are connected to.
#define UnlockPin 18
#define LockPin 19


#define DebounceTime 20 // debounce time in milliseconds
#define TimeOutTime 4000 // timeout in milliseconds


int codeLength = 5; // Set code length
int CodeArray[5] = {1, 3, 5, 7, 9}; // Enter desired combination code here, must match codeLength
int InputArray[5] = {0, 0, 0, 0, 0}; // Starts InputArray with all 0's

// Remember when last press was for timeout function.
// Also, set lastPress as having just timed out so that the backlight doesn't light for the first few
// seconds on initialization because lastPress and millis are both zero.
unsigned long lastPress = millis() - (TimeOutTime + 1);

// counter for debouncing routine
unsigned long SwitchDownTime ;

// Define inputScore (used to compare InputArray to CodeArray)
int inputScore = 0 ;
// Define rightAnswer (set to true when code is validated)
int rightAnswer = false ;


void setup () {
//Serial.begin(9600);           // set up Serial library at 9600 bps, used for debugging.
pinMode(BackLight, OUTPUT);
pinMode(LockPin, OUTPUT);
pinMode(UnlockPin, OUTPUT);

pinMode(KeyPin1, INPUT);
pinMode(KeyPin3, INPUT);
pinMode(KeyPin7, INPUT);
}

// Main loop - should be easy to follow
void loop () {
getAnswer () ;
timeOut () ;
checkAnswer () ;
if (rightAnswer) {
Success();
}
}

// Get the user's button presses and store it in InputArray
void getAnswer () {

//Button 1
    if (digitalRead(KeyPin1) == LOW && digitalRead(KeyPin3) == HIGH && digitalRead(KeyPin7) == HIGH) {  
      SwitchDownTime = millis() ; // Record time it went down (for debounce)
      digitalWrite(BackLight, HIGH); // Light backlight while pressed
      lastPress = millis() ; // Needed here to light as soon as button is pressed
      while (digitalRead(KeyPin1) == LOW && digitalRead(KeyPin3) == HIGH && digitalRead(KeyPin7) == HIGH) {
       // Wait while a button is pressed
      }
    buttonPress(1) ;
    } // end if

//Button 3
    if (digitalRead(KeyPin1) == LOW && digitalRead(KeyPin3) == HIGH && digitalRead(KeyPin7) == LOW) {  
      SwitchDownTime = millis() ; // Record time it went down (for debounce)
      digitalWrite(BackLight, HIGH); // Light backlight while pressed
      lastPress = millis() ; // Needed here to light as soon as button is pressed
      while (digitalRead(KeyPin1) == LOW && digitalRead(KeyPin3) == HIGH && digitalRead(KeyPin7) == LOW) {
       // Wait while a button is pressed
      }
    buttonPress(3) ;
    } // end if

//Button 5
    if (digitalRead(KeyPin1) == LOW && digitalRead(KeyPin3) == LOW && digitalRead(KeyPin7) == HIGH) {  
      SwitchDownTime = millis() ; // Record time it went down (for debounce)
      digitalWrite(BackLight, HIGH); // Light backlight while pressed
      lastPress = millis() ; // Needed here to light as soon as button is pressed
      while (digitalRead(KeyPin1) == LOW && digitalRead(KeyPin3) == LOW && digitalRead(KeyPin7) == HIGH) {
       // Wait while a button is pressed
      }
    buttonPress(5) ;
    } // end if

//Button 7
    if (digitalRead(KeyPin1) == HIGH && digitalRead(KeyPin3) == LOW && digitalRead(KeyPin7) == HIGH) {  
      SwitchDownTime = millis() ; // Record time it went down (for debounce)
      digitalWrite(BackLight, HIGH); // Light backlight while pressed
      lastPress = millis() ; // Needed here to light as soon as button is pressed
      while (digitalRead(KeyPin1) == HIGH && digitalRead(KeyPin3) == LOW && digitalRead(KeyPin7) == HIGH) {
       // Wait while a button is pressed
      }
    buttonPress(7) ;
    } // end if

//Button 9
    if (digitalRead(KeyPin1) == HIGH && digitalRead(KeyPin3) == HIGH && digitalRead(KeyPin7) == LOW) {  
      SwitchDownTime = millis() ; // Record time it went down (for debounce)
      digitalWrite(BackLight, HIGH); // Light backlight while pressed
      lastPress = millis() ; // Needed here to light as soon as button is pressed
      while (digitalRead(KeyPin1) == HIGH && digitalRead(KeyPin3) == HIGH && digitalRead(KeyPin7) == LOW) {
       // Wait while a button is pressed
      }
    buttonPress(9) ;
    } // end if

//Button 7&9 (Lock)
    if (digitalRead(KeyPin1) == HIGH && digitalRead(KeyPin3) == LOW && digitalRead(KeyPin7) == LOW) {  
      SwitchDownTime = millis() ; // Record time it went down (for debounce)
      digitalWrite(BackLight, HIGH); // Light backlight while pressed
      lastPress = millis() ; // Needed here to light as soon as button is pressed
      while (digitalRead(KeyPin1) == HIGH && digitalRead(KeyPin3) == LOW && digitalRead(KeyPin7) == LOW) {
      digitalWrite(LockPin, HIGH); // Wait while a button is pressed
      }
    digitalWrite(LockPin, LOW);
    lastPress = millis() - (TimeOutTime + 1);
    timeOut ();
    delay(200);
    } // end if

} // end GetAnswer

// Compare CodeArray with InputArray
void checkAnswer () {

//// Print current InputArray for debugging
//for (int i = 0; i < codeLength; i++) {
//  Serial.print(InputArray[i]);
//}
//  Serial.println(" ");
//// End print current InputArray
    
inputScore = 0;
for (int i = 0; i < codeLength; i++) {
if (CodeArray[i] == InputArray[i]) {
inputScore = inputScore + 1;
}
}
if (inputScore == codeLength) {rightAnswer = true;}

}

void Success () {
digitalWrite(UnlockPin, HIGH);
delay(100);
digitalWrite(UnlockPin, LOW);
clearArray () ;
rightAnswer = false;
}

void timeOut () {
if (millis() - lastPress > TimeOutTime) {
  clearArray() ;
  digitalWrite(BackLight, LOW);
  lastPress = millis() - (TimeOutTime + 1); //lastPress follows millis() just outside of timeout, to prevent problems with millis rollover lighting backlight after 50 days
}  else {
  digitalWrite(BackLight, HIGH);
}
}

void clearArray () {
  InputArray[4] = 0;
  InputArray[3] = 0;
  InputArray[2] = 0;
  InputArray[1] = 0;
  InputArray[0] = 0;
}

void buttonPress (int j) {
  if (millis() - SwitchDownTime > DebounceTime) { // If button was down longer than debounce
    InputArray[0] = InputArray[1];
    InputArray[1] = InputArray[2];
    InputArray[2] = InputArray[3];
    InputArray[3] = InputArray[4];
    InputArray[4] = j;
    lastPress = millis() ; //Needed here also, in case button is held down for more than timeout value
  } // end if
}