UPDATE 8/23/13
This page will stay since it explains the original scripts but I've made a number of updates and have packaged them up on the PyPI so they are easy to maintain for me and to distribute and install for everyone else. Let me know if there are any issues and I'll attempt to take care of them as time allows.
https://pypi.python.org/pypi/matrix_keypad
http://crumpspot.blogspot.com/p/keypad-matrix-python-package.html
Another module of my alarm interface is done. I picked up a 3x4 matrix keypad from Adafruit (http://www.adafruit.com/products/419) But couldn't find any good python code examples for it's use. I was able to find a few examples that lead me down the right path in terms of scanning rows first as inputs and then swapping pins to scan the columns. I wrote two libraries, the first just uses 7 of the Raspberry Pi's GPIO pins then I also wrote one that works for the MCP23008 chip since I'm really in a mode of saving pins when I can :) Then also just wrote a demo script to show calling the libraries and their use.
Here is the code as it stands now, I have some ideas on how to change it to be better but this is fully working for now.
UPDATE: I received a 4x4 keypad to test changes to the script and it worked like a charm once I changed the keypad and the Row and Column values like so:
KEYPAD = [
[1,2,3,"A"],
[4,5,6,"B"],
[7,8,9,"C"],
["*",0,"#","D"]
]
ROW = [7,6,5,4]
COLUMN = [3,2,1,0]
matrixKeypad_RPi_GPIO.py:
# ##################################################### # Python Library for 3x4 matrix keypad using # 7 of the avialable GPIO pins on the Raspberry Pi. # # This could easily be expanded to handle a 4x4 but I # don't have one for testing. The KEYPAD constant # would need to be updated. Also the setting/checking # of the colVal part would need to be expanded to # handle the extra column. # # Written by Chris Crumpacker # May 2013 # # main structure is adapted from Bandono's # matrixQPI which is wiringPi based. # https://github.com/bandono/matrixQPi?source=cc # ##################################################### import RPi.GPIO as GPIO class keypad(): # CONSTANTS KEYPAD = [ [1,2,3], [4,5,6], [7,8,9], ["*",0,"#"] ] ROW = [18,23,24,25] COLUMN = [4,17,22] def __init__(self): GPIO.setmode(GPIO.BCM) def getKey(self): # Set all columns as output low for j in range(len(self.COLUMN)): GPIO.setup(self.COLUMN[j], GPIO.OUT) GPIO.output(self.COLUMN[j], GPIO.LOW) # Set all rows as input for i in range(len(self.ROW)): GPIO.setup(self.ROW[i], GPIO.IN, pull_up_down=GPIO.PUD_UP) # Scan rows for pushed key/button # A valid key press should set "rowVal" between 0 and 3. rowVal = -1 for i in range(len(self.ROW)): tmpRead = GPIO.input(self.ROW[i]) if tmpRead == 0: rowVal = i # if rowVal is not 0 thru 3 then no button was pressed and we can exit if rowVal <0 data-blogger-escaped-or="" data-blogger-escaped-rowval="">3: self.exit() return # Convert columns to input for j in range(len(self.COLUMN)): GPIO.setup(self.COLUMN[j], GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # Switch the i-th row found from scan to output GPIO.setup(self.ROW[rowVal], GPIO.OUT) GPIO.output(self.ROW[rowVal], GPIO.HIGH) # Scan columns for still-pushed key/button # A valid key press should set "colVal" between 0 and 2. colVal = -1 for j in range(len(self.COLUMN)): tmpRead = GPIO.input(self.COLUMN[j]) if tmpRead == 1: colVal=j # if colVal is not 0 thru 2 then no button was pressed and we can exit if colVal <0 data-blogger-escaped-colval="" data-blogger-escaped-or="">2: self.exit() return # Return the value of the key pressed self.exit() return self.KEYPAD[rowVal][colVal] def exit(self): # Reinitialize all rows and columns as input at exit for i in range(len(self.ROW)): GPIO.setup(self.ROW[i], GPIO.IN, pull_up_down=GPIO.PUD_UP) for j in range(len(self.COLUMN)): GPIO.setup(self.COLUMN[j], GPIO.IN, pull_up_down=GPIO.PUD_UP) if __name__ == '__main__': # Initialize the keypad class kp = keypad() # Loop while waiting for a keypress digit = None while digit == None: digit = kp.getKey() # Print the result print digit
matrixKeypad_MCP230xx.py
# ##################################################### # Python Library for 3x4 matrix keypad using # the MCP23008 chip via I2C from the Raspberry Pi. # # This could easily be expanded to handle a 4x4 but I # don't have one for testing. The KEYPAD constant # would need to be updated. Also the setting/checking # of the colVal part would need to be expanded to # handle the extra column. # # Written by Chris Crumpacker # May 2013 # # main structure is adapted from Bandono's # matrixQPI which is wiringPi based. # https://github.com/bandono/matrixQPi?source=cc # ##################################################### from Adafruit_MCP230xx import Adafruit_MCP230XX class keypad(Adafruit_MCP230XX): # Constants INPUT = 0 OUTPUT = 1 HIGH = 1 LOW = 0 KEYPAD = [ [1,2,3], [4,5,6], [7,8,9], ["*",0,"#"] ] ROW = [6,5,4,3] COLUMN = [2,1,0] def __init__(self, address=0x21, num_gpios=8): self.mcp2 = Adafruit_MCP230XX(address, num_gpios) def getKey(self): # Set all columns as output low for j in range(len(self.COLUMN)): self.mcp2.config(self.COLUMN[j], self.mcp2.OUTPUT) self.mcp2.output(self.COLUMN[j], self.LOW) # Set all rows as input for i in range(len(self.ROW)): self.mcp2.config(self.ROW[i], self.mcp2.INPUT) self.mcp2.pullup(self.ROW[i], True) # Scan rows for pushed key/button # valid rowVal" should be between 0 and 3 when a key is pressed. Pre-setting it to -1 rowVal = -1 for i in range(len(self.ROW)): tmpRead = self.mcp2.input(self.ROW[i]) if tmpRead == 0: rowVal = i # if rowVal is still "return" then no button was pressed and we can exit if rowVal == -1: self.exit() return # Convert columns to input for j in range(len(self.COLUMN)): self.mcp2.config(self.COLUMN[j], self.mcp2.INPUT) # Switch the i-th row found from scan to output self.mcp2.config(self.ROW[rowVal], self.mcp2.OUTPUT) self.mcp2.output(self.ROW[rowVal], self.HIGH) # Scan columns for still-pushed key/button colVal = -1 for j in range(len(self.COLUMN)): tmpRead = self.mcp2.input(self.COLUMN[j]) if tmpRead == 1: colVal=j if colVal == -1: self.exit() return # Return the value of the key pressed self.exit() return self.KEYPAD[rowVal][colVal] def exit(self): # Reinitialize all rows and columns as input before exiting for i in range(len(self.ROW)): self.mcp2.config(self.ROW[i], self.INPUT) for j in range(len(self.COLUMN)): self.mcp2.config(self.COLUMN[j], self.INPUT) if __name__ == '__main__': # Initialize the keypad class kp = keypad() # Loop while waiting for a keypress r = None while r == None: r = kp.getKey() # Print the result print r
Demo code, matrixKeypad_test.py
# ##################################################### # Demo script showing the use of the Python # matrix Keypad library for both the Raspberry Pi # GPIO and the MSP230xx I2C Chip set. # # Librarys needed: # matrixKeypad_MCP230xx.py or matrixKeypad_RPi_GPIO.py # # Also needed is the Adafruit python libraries for the # MCP230xx chips (Adafruit_MCP230xx.py) and I2C (Adafruit_I2C.py) # # Written by Chris Crumpacker # May 2013 # # ##################################################### from matrixKeypad_MCP230xx import keypad #from matrixKeypad_RPi_GPIO import keypad from time import sleep # Initialize the keypad class kp = keypad() def digit(): # Loop while waiting for a keypress r = None while r == None: r = kp.getKey() return r print "Please enter a 4 digit code: " # Getting digit 1, printing it, then sleep to allow the next digit press. d1 = digit() print d1 sleep(1) d2 = digit() print d2 sleep(1) d3 = digit() print d3 sleep(1) d4 = digit() print d4 # printing out the assembled 4 digit code. print "You Entered %s%s%s%s "%(d1,d2,d3,d4)
Hi,
ReplyDeletethis tuto seems perfect, but I got a problem.
I have a raspi rev B, an MCP23017, and it's set to address 0x20, and matrix keyboard from GPIO 1 to 7 of my MCP23017.
So I changed :
ROW = [7,6,5,4]
COLUMN = [3,2,1]
def __init__(self, address=0x20, num_gpios=16):
But I only have *, 0 and # that works.
Any idea?
Well, I would venture a guess seeing as the last row works, that you have your wiring backwards. i.e. your column 1 is wired to row 7, column 2 to row 6, etc. SO either try flipping the wiring. or change the code:
DeleteROW = [1,2,3,4]
COLUMN = [5,6,7]
This comment has been removed by the author.
DeleteThis comment has been removed by the author.
DeleteI've got the same kind of problem, my code is:
Deletedef __init__(self, address=0x21, num_gpios=16, columnCount=3):
self.mcp2 = Adafruit_MCP230XX(address, num_gpios)
# Constants
self.INPUT = 0
self.OUTPUT = 1
self.HIGH = 1
self.LOW = 0
if columnCount is 3:
# CONSTANTS
self.KEYPAD = [
[1,2,3],
[4,5,6],
[7,8,9],
["*",0,"#"]
]
self.ROW = [6,5,4,3]
self.COLUMN = [2,1,0]
Only 3, 6, 9 and # works. And if I press any button from the 1st row the output will be 3, on the 2nd will be 6....etc
I've tried cabling, changed pins, but no luck. Anything to suggest?
PS. keypad is working fine on the GPIO ports
The problem seems to be that the variable colVal is always 0, I don't understand why. Any ideas?
DeleteWell something is going on that is affecting some people but not others. I worked with two people recently and solved it but have yet to update the package. Here is the solution. inside the MCP230xx.py file
DeleteFind line 77 highlighted below and change the "==" to a ">=" Save it then rerun the demo code
# Scan columns for still-pushed key/button
colVal = -1
for j in range(len(self.COLUMN)):
tmpRead = self.mcp2.input(self.COLUMN[j])
if tmpRead >= 1:
colVal=j
if colVal == -1:
self.exit()
return
Hi,
ReplyDeleteCan you please share the code for a 4*4 alternative i made the changes that you had required to be done but couldnt get it to work.
Regards,
Ashok
I could try but I don't have one for testing. Can you send me the script as you have it and I can see if it is what I was thinking and if I can spot anything easily
DeleteDone. Check the package linked above.
DeleteOK, I found your mistake :)
ReplyDeleteLines 59 et 60 are not correctly indented
# Scan rows for pushed key/button
# valid rowVal" should be between 0 and 3 when a key is pressed. Pre-setting it to -1
rowVal = -1
for i in range(len(self.ROW)):
tmpRead = self.mcp2.input(self.ROW[i])
if tmpRead == 0:
rowVal = i
Looks like some kind of issue with the blog formatting or maybe copy and paste. My .py file doesn't have it like this... Anyway glad you found the issue.
DeleteCode on the blog has been updated
DeleteCan u send the latest code for the keypad...I am getting error at GPIO.INPUT(self.ROW[i])
DeleteToo many people (including myself were having issues with coping the code from this post. Blogger messes with the indents etc. too much Anyway I posted it as an installable packages on the pypi page. try that out and let me know. Make sure you get at least 1.0.5
Deletehttps://pypi.python.org/pypi/matrix_keypad
Now it is working but i cannot loop to expect more digit.. i need to execute again... the demo code is also not working... it gives (program exited with code: 0)Press return to continue
DeleteI hadn't made a few needed changes to the demo code after renaming the package. I've updated it now and it should be working. current version is 1.1.1. If you installed it via pip you can use this:
Delete"sudo pip install --upgrade matrix_keypad"
Great stuff, Chris. Always good to see something 'else' working on the Pi :-)
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteGreat work Chris! Came across your blog when searching for a solution for the 3x4 keypad. I will need it for my home alarm/automation project. Thanks!
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeletequestion is it possible to use an interrupt with this instead of a loop?
ReplyDeleteNo Because there aren't enough interrupt pins on the MCP chips. You'd need at least 3.
DeleteThanks for the attribution to my work!
ReplyDeleteI had a late post on Python class for any m x n matrix keypad using WiringPi to wrap up the old deprecated library by the way:
http://lakm.us/logit/2014/02/any-m-x-n-matrix-keypad-raspberry-pi/
This comment has been removed by the author.
ReplyDeletePlease can u help me with the code for 4X4 keypad connected to Raspberry Pi (Model 2 B) with 8 GPIO pins so that I can read a 6 digit number without key debounce from that keypad and store it in some variable (Integer).
ReplyDeleteVery useful!!! Thanks a lot!! Couldnt leave without saying thanks!!
ReplyDeletehow can i use this to take multiple keys to create a multi digit number like 56 or 199? I've been trying to do this for several days now with no luck.
ReplyDeleteHello Hi. How can i use the 3*4 keypad matrix to get a input of 3 digit in size?
ReplyDeletethanks dude for your great post about membrane switch, it’s very helpful for me.
ReplyDeleteHI, I am new to Python and doing this keypad project with Raspberry Pi. I am trying to compile the matrixKeypad_RPi_GPIO.py code. However I got syntax error for line 56 and 77 which both code is deal with data blogger.
ReplyDeleteDoes anyone here know how should i solve this?
Thanks for sharing a code,this membrane keypad matrix code is helpful for me.
ReplyDelete