Down the Rabbit Hole

By Philip Kane

Parts List:

Component Name Part Description Manufacturer Part No.
R1-R4 R1-R4 51KΩ Resistor CF1/4W513JRC
C1-C2 C1-C2 .0047µF Capacitor DC.0047
Q1-Q4 Q1-Q4 2N3904 Transistor 2N3904
Arduino Uno R3 (optional) A000066

My definition of "Rabbit Hole" is a distraction that leads to an unplanned DIY adventure. I was working on a future Club Jameco project that required input from a keypad. It's a standalone device programmer with a user interface that consists of a keypad and an LCD display. It is completely menu driven and requires just 8 keys to flip through the menu and make a selection, but I had previously obtained a12-key keypad. Why waste those extra keys? That led me to thinking about inexpensive custom DIY keypads, which led me to thinking about touch keypads, specifically, resistive touch keypads. Supplied with a few circuit components and a handful of thumbtacks I found myself jumping headfirst into the DIY rabbit hole.

Resistive Touch Switch Basics

Place a finger across two electrodes separated by a small gap, with one electrode connected to a small DC voltage source and the other connected to ground. A tiny current will flow through the skin of your finger from one electrode to the other. This is the principle behind a resistance touch switch. Figure 1 shows one way to build a resistance touch switch.

Basic resistance touch switchFigure 1: Basic Resistance Touch Switch (click to enlarge)

When you place your finger across the touch contacts, the Darlington Circuit formed by Q1 and Q2 amplifies the current flowing through your finger to drive LED1. Resistor R1 is included to protect against a short circuit at the touch contacts. Capacitor C1 helps to eliminate switch bounce; I found that it was more important for eliminating induced AC noise. Without it, touching the transistor side contact alone caused the LED to light. The addition of C1 turns this into a voltage transient of short duration which can be be filtered out in software. The capacitor causes a turn on and turn off delay. You may need to experiment with different values to find one that produces an acceptable delay while still reducing induced noise.

Keypad Matrix Circuit

The 4x2 resistance touch switch matrix circuit in Figure 2 works as follows. A high (VCC) is placed at each row input successively while keeping the other row inputs low. When a finger is placed across one pair of contacts in the row that is set to high, the output (Col1 or Col2) of the corresponding Darlington pair will go low (GND). So, to determine which contacts have been touched, you note the row input that is currently high and the resulting output that goes low.

Matrix circuit schematicFigure 2: Matrix Circuit Schematic (click to enlarge)

I was looking for a quick and cheap set of touch contacts. My wife graciously contributed a box of thumbtacks originally acquired for one of her art projects. As you can see in Figure 3, two thumbtacks were required to make each of the eight touch contacts in the 4x2 keypad matrix. Column 1 is at the bottom of the figure.

The table below shows the pattern for the circuit in Figure 3 Matrix Circuit:

Switch Coordinates
S1 Row1, Col2
S2 Row2, Col1
S3 Row2, Col2
S4 Row3, Col1
S5 Row3, Col2
S6 Row2, Col2
S7 Row4, Col1
S8 Row4, Col2

Breadboard matrix circuitFigure 3: Breadboard Matrix Circuit

Keypad Matrix Test Setup

For the Arduino test circuit I built a test version of the matrix keypad on a solderless breadboard and used it as the input for an Arduino Uno single board computer. Figure 4 shows the test circuit with the with the Arduino Uno pin connections.

Arduino test circuit schematicFigure 4: Arduino Test Circuit Schematic (click to enlarge)

The breadboard test circuit is shown in Figure 5.

Arduino test circuitFigure 5: Arduino Test Circuit

Arduino Test Software

The code in listing 1 is a simple test program that continually scans the keyboard matrix waiting for a "keypress." When a key is pressed it displays the value of the key. On startup the test program sets all row lines to Low. It then successively forces each row line high and, after a delay to account for switch bounce, checks each column output to determine if a contact has been touched.

If a contact has been touched, then the corresponding column output will be forced low. Upon detecting a low column output the test program uses the row and column values to index into a matrix that contains the the corresponding key value. It displays the key in the serial monitor. Then waits for the key to be released before it resumes scanning the keyboard.

const byte ROWS = 4; //Four rows
const byte COLS = 2; //Two columns
char keys[ROWS][COLS] = {
  {'1','2'},
  {'3','4'},
  {'5','6'},
  {'7','8'}
};

byte rowPins[ROWS] = {12, 11, 10, 9}; //Connect to the row pinouts of the keypad
byte colPins[COLS] = {8, 7};          //Connect to the column pinouts of the keypad

void setup(){
  // Init keypad

 // Set row ports to output
 for (int r = 0; r < ROWS; r++)
    {
      pinMode(rowPins[r], OUTPUT);
    }
 // set col ports to input
 for (int c = 0; c < COLS; c++)
     {
       pinMode(colPins[c], INPUT);
     }
  // set row ports output levels to low
  for(int r = 0; r < ROWS; r++)
     {
       digitalWrite(rowPins[r], LOW);
     }
  Serial.begin(9600);
}

  char k;
  boolean br;

  void loop(){
   k=NULL;
  // scan the keyboard until a key is pressed
  do
  {
   // Row scan - set the rows to high, one at a time
   for (int r = 0; r < ROWS; r++)
   {
     digitalWrite(rowPins[r], HIGH);
     delay(25); // Settling time

      // Scan the columns for a low
       for (int c = 0; c < COLS; c++)
         {
           if (digitalRead(colPins[c]) == LOW)delay(25); // Debounce
           if (digitalRead(colPins[c]) == LOW)
             {
               // Valid keypress. Get the key value and print it
	       k = keys[r] [c];
               Serial.println(k);

               // Wait until the key is released
                br = false;
                do {
                  if (digitalRead(colPins[c]) == HIGH)
                  {
                    delay(25);
                    if (digitalRead(colPins[c]) == HIGH)br=true;
                  }
                }while (br==false);
               // Exit
               break;
             }
         }
         digitalWrite(rowPins[r], LOW);
         delay(25); // Settling time
         if (k != NULL)break;
      }
  }while (k == NULL);
}



Phil Kane has been a technical writer in the software industry for more than 10 years. He has also occasionally authored articles for electronics enthusiast magazines.

If you have an electronics story or project you'd like to share, please email [email protected].