├── pad4pi ├── __init__.py └── rpi_gpio.py ├── MANIFEST.in ├── rpi_gpio_demo.py ├── .gitignore ├── setup.py ├── README.rst ├── rpi_gpio_demo2.py └── LICENSE /pad4pi/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | -------------------------------------------------------------------------------- /rpi_gpio_demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pad4pi import rpi_gpio 4 | import time 5 | 6 | def print_key(key): 7 | print(key) 8 | 9 | try: 10 | factory = rpi_gpio.KeypadFactory() 11 | keypad = factory.create_4_by_3_keypad() # makes assumptions about keypad layout and GPIO pin numbers 12 | 13 | keypad.registerKeyPressHandler(print_key) 14 | 15 | print("Press buttons on your keypad. Ctrl+C to exit.") 16 | while True: 17 | time.sleep(1) 18 | except KeyboardInterrupt: 19 | print("Goodbye") 20 | finally: 21 | keypad.cleanup() 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | def readme(): 4 | with open('README.rst') as f: 5 | return f.read() 6 | 7 | setup(name="pad4pi", 8 | version="1.1.5", 9 | description="Interrupt-based matrix keypad library for Raspberry Pi", 10 | long_description=readme(), 11 | url="https://github.com/brettmclean/pad4pi", 12 | keywords="raspberry pi matrix keypad", 13 | author="Brett McLean", 14 | author_email="brettrmclean@gmail.com", 15 | license="LGPLv3", 16 | packages=["pad4pi"], 17 | install_requires=[ 18 | "RPi.GPIO" 19 | ], 20 | classifiers = [ 21 | "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", 22 | "Development Status :: 5 - Production/Stable", 23 | "Intended Audience :: Education", 24 | "Intended Audience :: Developers", 25 | "Topic :: Education", 26 | "Topic :: System :: Hardware", 27 | "Programming Language :: Python :: 2", 28 | "Programming Language :: Python :: 3" 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | pad4pi 2 | ====== 3 | 4 | An interrupt-based Python 2/3 library for reading matrix_ keypad_ key presses using Raspberry Pi GPIO pins. 5 | 6 | .. _matrix: http://www.adafruit.com/products/419 7 | .. _keypad: http://www.adafruit.com/products/1824 8 | 9 | .. code-block:: python 10 | 11 | pip install pad4pi 12 | 13 | Tested on a Raspberry Pi B+ using a `4x3 matrix keypad`_ but it should work with 4x4 and other sizes. 14 | 15 | .. _4x3 matrix keypad: http://www.adafruit.com/products/419 16 | 17 | Usage 18 | ===== 19 | 20 | .. code-block:: python 21 | 22 | from pad4pi import rpi_gpio 23 | 24 | KEYPAD = [ 25 | [1, 2, 3], 26 | [4, 5, 6], 27 | [7, 8, 9], 28 | ["*", 0, "#"] 29 | ] 30 | 31 | ROW_PINS = [4, 14, 15, 17] # BCM numbering 32 | COL_PINS = [18, 27, 22] # BCM numbering 33 | 34 | factory = rpi_gpio.KeypadFactory() 35 | 36 | # Try factory.create_4_by_3_keypad 37 | # and factory.create_4_by_4_keypad for reasonable defaults 38 | keypad = factory.create_keypad(keypad=KEYPAD, row_pins=ROW_PINS, col_pins=COL_PINS) 39 | 40 | def printKey(key): 41 | print(key) 42 | 43 | # printKey will be called each time a keypad button is pressed 44 | keypad.registerKeyPressHandler(printKey) 45 | 46 | When your program exits, call ``keypad.cleanup()`` to ensure the Raspberry Pi's GPIO pins are reset. 47 | 48 | License 49 | ======= 50 | 51 | Licensed under `GNU Lesser General Public License Version 3`_ (LGPL v3). 52 | 53 | .. _GNU Lesser General Public License Version 3: https://github.com/brettmclean/pad4pi/blob/master/LICENSE 54 | -------------------------------------------------------------------------------- /rpi_gpio_demo2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from pad4pi import rpi_gpio 4 | import time 5 | import sys 6 | 7 | entered_passcode = "" 8 | correct_passcode = "1234" 9 | 10 | def cleanup(): 11 | global keypad 12 | keypad.cleanup() 13 | 14 | def correct_passcode_entered(): 15 | print("Passcode accepted. Access granted.") 16 | cleanup() 17 | sys.exit() 18 | 19 | def incorrect_passcode_entered(): 20 | print("Incorrect passcode. Access denied.") 21 | cleanup() 22 | sys.exit() 23 | 24 | def digit_entered(key): 25 | global entered_passcode, correct_passcode 26 | 27 | entered_passcode += str(key) 28 | print(entered_passcode) 29 | 30 | if len(entered_passcode) == len(correct_passcode): 31 | if entered_passcode == correct_passcode: 32 | correct_passcode_entered() 33 | else: 34 | incorrect_passcode_entered() 35 | 36 | def non_digit_entered(key): 37 | global entered_passcode 38 | 39 | if key == "*" and len(entered_passcode) > 0: 40 | entered_passcode = entered_passcode[:-1] 41 | print(entered_passcode) 42 | 43 | def key_pressed(key): 44 | try: 45 | int_key = int(key) 46 | if int_key >= 0 and int_key <= 9: 47 | digit_entered(key) 48 | except ValueError: 49 | non_digit_entered(key) 50 | 51 | try: 52 | factory = rpi_gpio.KeypadFactory() 53 | keypad = factory.create_4_by_3_keypad() # makes assumptions about keypad layout and GPIO pin numbers 54 | 55 | keypad.registerKeyPressHandler(key_pressed) 56 | 57 | print("Enter your passcode (hint: {0}).".format(correct_passcode)) 58 | print("Press * to clear previous digit.") 59 | 60 | while True: 61 | time.sleep(1) 62 | except KeyboardInterrupt: 63 | print("Goodbye") 64 | finally: 65 | cleanup() 66 | -------------------------------------------------------------------------------- /pad4pi/rpi_gpio.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import RPi.GPIO as GPIO 4 | import time 5 | from threading import Timer 6 | 7 | DEFAULT_KEY_DELAY = 300 8 | DEFAULT_REPEAT_DELAY = 1.0 9 | DEFAULT_REPEAT_RATE = 1.0 10 | DEFAULT_DEBOUNCE_TIME = 10 11 | 12 | class KeypadFactory(): 13 | 14 | def create_keypad(self, keypad=None, row_pins=None, col_pins=None, key_delay=DEFAULT_KEY_DELAY, repeat=False, repeat_delay=None, repeat_rate=None, gpio_mode=GPIO.BCM): 15 | 16 | if keypad is None: 17 | keypad = [ 18 | [1,2,3], 19 | [4,5,6], 20 | [7,8,9], 21 | ["*",0,"#"] 22 | ] 23 | 24 | if row_pins is None: 25 | row_pins = [4,14,15,17] 26 | 27 | if col_pins is None: 28 | col_pins = [18,27,22] 29 | 30 | return Keypad(keypad, row_pins, col_pins, key_delay, repeat, repeat_delay, repeat_rate, gpio_mode) 31 | 32 | def create_4_by_3_keypad(self): 33 | 34 | KEYPAD = [ 35 | [1,2,3], 36 | [4,5,6], 37 | [7,8,9], 38 | ["*",0,"#"] 39 | ] 40 | 41 | ROW_PINS = [4,14,15,17] 42 | COL_PINS = [18,27,22] 43 | 44 | return self.create_keypad(KEYPAD, ROW_PINS, COL_PINS) 45 | 46 | def create_4_by_4_keypad(self): 47 | 48 | KEYPAD = [ 49 | [1,2,3,"A"], 50 | [4,5,6,"B"], 51 | [7,8,9,"C"], 52 | ["*",0,"#","D"] 53 | ] 54 | 55 | ROW_PINS = [4,14,15,17] 56 | COL_PINS = [18,27,22,23] 57 | 58 | return self.create_keypad(KEYPAD, ROW_PINS, COL_PINS) 59 | 60 | class Keypad(): 61 | def __init__(self, keypad, row_pins, col_pins, key_delay=DEFAULT_KEY_DELAY, repeat=False, repeat_delay=None, repeat_rate=None,gpio_mode=GPIO.BCM): 62 | self._handlers = [] 63 | 64 | self._keypad = keypad 65 | self._row_pins = row_pins 66 | self._col_pins = col_pins 67 | self._key_delay = key_delay 68 | self._repeat = repeat 69 | self._repeat_delay = repeat_delay 70 | self._repeat_rate = repeat_rate 71 | self._repeat_timer = None 72 | if repeat: 73 | self._repeat_delay = repeat_delay if repeat_delay is not None else DEFAULT_REPEAT_DELAY 74 | self._repeat_rate = repeat_rate if repeat_rate is not None else DEFAULT_REPEAT_RATE 75 | else: 76 | if repeat_delay is not None: 77 | self._repeat = True 78 | self._repeat_rate = repeat_rate if repeat_rate is not None else DEFAULT_REPEAT_RATE 79 | elif repeat_rate is not None: 80 | self._repeat = True 81 | self._repeat_delay = repeat_delay if repeat_delay is not None else DEFAULT_REPEAT_DELAY 82 | 83 | self._last_key_press_time = 0 84 | self._first_repeat = True 85 | 86 | GPIO.setmode(gpio_mode) 87 | 88 | self._setRowsAsInput() 89 | self._setColumnsAsOutput() 90 | 91 | def registerKeyPressHandler(self, handler): 92 | self._handlers.append(handler) 93 | 94 | def unregisterKeyPressHandler(self, handler): 95 | self._handlers.remove(handler) 96 | 97 | def clearKeyPressHandlers(self): 98 | self._handlers = [] 99 | 100 | def _repeatTimer(self): 101 | self._repeat_timer = None 102 | self._onKeyPress(None) 103 | 104 | def _onKeyPress(self, channel): 105 | currTime = self.getTimeInMillis() 106 | if currTime < self._last_key_press_time + self._key_delay: 107 | return 108 | 109 | keyPressed = self.getKey() 110 | if keyPressed is not None: 111 | for handler in self._handlers: 112 | handler(keyPressed) 113 | self._last_key_press_time = currTime 114 | if self._repeat: 115 | self._repeat_timer = Timer(self._repeat_delay if self._first_repeat else 1.0/self._repeat_rate, self._repeatTimer) 116 | self._first_repeat = False 117 | self._repeat_timer.start() 118 | else: 119 | if self._repeat_timer is not None: 120 | self._repeat_timer.cancel() 121 | self._repeat_timer = None 122 | self._first_repeat = True 123 | 124 | def _setRowsAsInput(self): 125 | # Set all rows as input 126 | for i in range(len(self._row_pins)): 127 | GPIO.setup(self._row_pins[i], GPIO.IN, pull_up_down=GPIO.PUD_UP) 128 | GPIO.add_event_detect(self._row_pins[i], GPIO.FALLING, callback=self._onKeyPress, bouncetime=DEFAULT_DEBOUNCE_TIME) 129 | 130 | def _setColumnsAsOutput(self): 131 | # Set all columns as output low 132 | for j in range(len(self._col_pins)): 133 | GPIO.setup(self._col_pins[j], GPIO.OUT) 134 | GPIO.output(self._col_pins[j], GPIO.LOW) 135 | 136 | def getKey(self): 137 | 138 | keyVal = None 139 | 140 | # Scan rows for pressed key 141 | rowVal = None 142 | for i in range(len(self._row_pins)): 143 | tmpRead = GPIO.input(self._row_pins[i]) 144 | if tmpRead == 0: 145 | rowVal = i 146 | break 147 | 148 | # Scan columns for pressed key 149 | colVal = None 150 | if rowVal is not None: 151 | for i in range(len(self._col_pins)): 152 | GPIO.output(self._col_pins[i], GPIO.HIGH) 153 | if GPIO.input(self._row_pins[rowVal]) == GPIO.HIGH: 154 | GPIO.output(self._col_pins[i], GPIO.LOW) 155 | colVal = i 156 | break 157 | GPIO.output(self._col_pins[i], GPIO.LOW) 158 | 159 | # Determine pressed key, if any 160 | if colVal is not None: 161 | keyVal = self._keypad[rowVal][colVal] 162 | 163 | return keyVal 164 | 165 | def cleanup(self): 166 | if self._repeat_timer is not None: 167 | self._repeat_timer.cancel() 168 | GPIO.cleanup() 169 | 170 | def getTimeInMillis(self): 171 | return time.time() * 1000 172 | 173 | if __name__ == "__main__": 174 | from pad4pi import rpi_gpio 175 | 176 | KEYPAD = [ 177 | ["F1", "F2", "#", "*"], 178 | [1, 2, 3, "Up"], 179 | [4, 5, 6, "Down"], 180 | [7, 8, 9, "Esc"], 181 | ["Left", 0, "Right", "Ent"] 182 | ] 183 | ROW_PINS = [4, 17, 18, 27, 22] 184 | COL_PINS = [9, 10, 24, 23] 185 | kp = rpi_gpio.KeypadFactory().create_keypad(keypad=KEYPAD, row_pins=ROW_PINS, col_pins=COL_PINS, repeat=True, repeat_rate=5, key_delay=100) 186 | def printkey(key): 187 | print(key) 188 | kp.registerKeyPressHandler(printkey) 189 | i = raw_input('') 190 | kp.cleanup() 191 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | --------------------------------------------------------------------------------