In this article, see how easy it is to connect a keypad to a Raspberry Pi 4 for a variety of uses.

There are numerous ways for users to input data on a Raspberry Pi. One of them is to use a 16-button keypad that contains the numbers from zero to nine as well as a few extra buttons:

RPi_keypad_connection2.jpg

A Raspberry Pi 4 and keypads.

In this article, I’ll explain how such keypads work and how you can easily incorporate them into your Raspberry Pi 4 projects.

Understanding the Keypad

The keyboard I used can be divided into four rows and four columns like this:

RPi_keypad_connection4.jpg

The keypad can be broken down into four columns and four rows.

To detect which button is pressed, the Raspberry Pi has to send a pulse to each of the four rows of the keyboard. When a user presses a button that’s connected to the line which is currently pulled high, the corresponding column is also pulled high. 

By decoding the combination of line and column, you can determine which button got pressed.

If a user, for example, presses the B button located on the second row in the fourth column, the Raspberry Pi detects this button press when it sends a pulse to the second line and then checks which of the four columns was pulled high.

Connecting the Keypad to the Raspberry Pi 4

Keypads that operate this way don’t need any power to work. This means that you can simply connect all eight data lines of the keypad to any eight GPIO pins on your Raspberry Pi:

RPi_keypad_connection1.jpg

Connections between the keypad and the Pi 4.

I used the same color scheme as in the image above. The blue connections correspond to the rows, and the orange ones represent the columns.

A Simple Code Example

Once you made the connections, as discussed above, it’s time to run a simple test program that will print the buttons, a user presses on the keypad, to the Raspberry Pi’s console:

        # GPIO setup and imports omitted

def readLine(line, characters):
	GPIO.output(line, GPIO.HIGH)
	if(GPIO.input(C1) == 1):
    	    print(characters[0])
	if(GPIO.input(C2) == 1):
        	    print(characters[1])
	if(GPIO.input(C3) == 1):
    	    print(characters[2])
	if(GPIO.input(C4) == 1):
    	    print(characters[3])
	GPIO.output(line, GPIO.LOW)

try:
	while True:
    	    readLine(L1, ["1","2","3","A"])
    	    readLine(L2, ["4","5","6","B"])
    	    readLine(L3, ["7","8","9","C"])
    	    readLine(L4, ["*","0","#","D"])
    	    time.sleep(0.1)
except KeyboardInterrupt:
	print("\nApplication stopped!")
    

As you can see, the test program contains a method called readLine. 

The readLine method sends the pulse discussed above to a single line and then checks which of the buttons got pressed while the line is pulled high. This is simply repeated for all four rows. The method also takes a list of symbols that the buttons correspond to.

RPi_keypad_connection5.png

The Pi is ready to accept information from the keypad.

This program is a simple example. It will not detect if a button is being held down. It will simply recognize a new keystroke with every pulse it sends on the output line. 

A program that correctly detects individual keypresses and that implements a simple code-lock is included at the end of this article.

RPi_keypad_connection3.png

The keypad is ready to go!

Keypads Work for Projects Basic to Advanced

Cheap keypads usually all operate the same way. They have four rows and four columns. Input can be detected by sending a pulse on every single line and then checking every column to determine the button that was pressed. 

A basic application is easy to implement, but if you want more advanced features, the code can quickly get quite complex.

Full Keypad Code

        # This example is a hello world example
# for using a keypad with the Raspberry Pi

import RPi.GPIO as GPIO
import time

L1 = 5
L2 = 6
L3 = 13
L4 = 19

C1 = 12
C2 = 16
C3 = 20
C4 = 21

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

GPIO.setup(L1, GPIO.OUT)
GPIO.setup(L2, GPIO.OUT)
GPIO.setup(L3, GPIO.OUT)
GPIO.setup(L4, GPIO.OUT)

GPIO.setup(C1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C3, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C4, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

def readLine(line, characters):
    GPIO.output(line, GPIO.HIGH)
    if(GPIO.input(C1) == 1):
        print(characters[0])
    if(GPIO.input(C2) == 1):
        print(characters[1])
    if(GPIO.input(C3) == 1):
        print(characters[2])
    if(GPIO.input(C4) == 1):
        print(characters[3])
    GPIO.output(line, GPIO.LOW)

try:
    while True:
        readLine(L1, ["1","2","3","A"])
        readLine(L2, ["4","5","6","B"])
        readLine(L3, ["7","8","9","C"])
        readLine(L4, ["*","0","#","D"])
        time.sleep(0.1)
except KeyboardInterrupt:
    print("\nApplication stopped!")
    

Full Codepad Code

        # This program allows a user to enter a
# Code. If the C-Button is pressed on the
# keypad, the input is reset. If the user
# hits the A-Button, the input is checked.

import RPi.GPIO as GPIO
import time

# These are the GPIO pin numbers where the
# lines of the keypad matrix are connected
L1 = 5
L2 = 6
L3 = 13
L4 = 19

# These are the four columns
C1 = 12
C2 = 16
C3 = 20
C4 = 21

# The GPIO pin of the column of the key that is currently
# being held down or -1 if no key is pressed
keypadPressed = -1

secretCode = "4789"
input = ""

# Setup GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

GPIO.setup(L1, GPIO.OUT)
GPIO.setup(L2, GPIO.OUT)
GPIO.setup(L3, GPIO.OUT)
GPIO.setup(L4, GPIO.OUT)

# Use the internal pull-down resistors
GPIO.setup(C1, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C2, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C3, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(C4, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

# This callback registers the key that was pressed
# if no other key is currently pressed
def keypadCallback(channel):
    global keypadPressed
    if keypadPressed == -1:
        keypadPressed = channel

# Detect the rising edges on the column lines of the
# keypad. This way, we can detect if the user presses
# a button when we send a pulse.
GPIO.add_event_detect(C1, GPIO.RISING, callback=keypadCallback)
GPIO.add_event_detect(C2, GPIO.RISING, callback=keypadCallback)
GPIO.add_event_detect(C3, GPIO.RISING, callback=keypadCallback)
GPIO.add_event_detect(C4, GPIO.RISING, callback=keypadCallback)

# Sets all lines to a specific state. This is a helper
# for detecting when the user releases a button
def setAllLines(state):
    GPIO.output(L1, state)
    GPIO.output(L2, state)
    GPIO.output(L3, state)
    GPIO.output(L4, state)

def checkSpecialKeys():
    global input
    pressed = False

    GPIO.output(L3, GPIO.HIGH)

    if (GPIO.input(C4) == 1):
        print("Input reset!");
        pressed = True

    GPIO.output(L3, GPIO.LOW)
    GPIO.output(L1, GPIO.HIGH)

    if (not pressed and GPIO.input(C4) == 1):
        if input == secretCode:
            print("Code correct!")
            # TODO: Unlock a door, turn a light on, etc.
        else:
            print("Incorrect code!")
            # TODO: Sound an alarm, send an email, etc.
        pressed = True

    GPIO.output(L3, GPIO.LOW)

    if pressed:
        input = ""

    return pressed

# reads the columns and appends the value, that corresponds
# to the button, to a variable
def readLine(line, characters):
    global input
    # We have to send a pulse on each line to
    # detect button presses
    GPIO.output(line, GPIO.HIGH)
    if(GPIO.input(C1) == 1):
        input = input + characters[0]
    if(GPIO.input(C2) == 1):
        input = input + characters[1]
    if(GPIO.input(C3) == 1):
        input = input + characters[2]
    if(GPIO.input(C4) == 1):
        input = input + characters[3]
    GPIO.output(line, GPIO.LOW)

try:
    while True:
        # If a button was previously pressed,
        # check, whether the user has released it yet
        if keypadPressed != -1:
            setAllLines(GPIO.HIGH)
            if GPIO.input(keypadPressed) == 0:
                keypadPressed = -1
            else:
                time.sleep(0.1)
        # Otherwise, just read the input
        else:
            if not checkSpecialKeys():
                readLine(L1, ["1","2","3","A"])
                readLine(L2, ["4","5","6","B"])
                readLine(L3, ["7","8","9","C"])
                readLine(L4, ["*","0","#","D"])
                time.sleep(0.1)
            else:
                time.sleep(0.1)
except KeyboardInterrupt:
    print("\nApplication stopped!")
    
Daniel Hertz
Hi! I am a software engineer and owner of nerdhut.de who loves to experiment with electronics, gadgets and tech in general.