#!/usr/bin/env python3 import RPi.GPIO as GPIO import time #class Key:Define some of the properties of Key class Key(object): NO_KEY = '\0' #Defines the four states of Key IDLE = 0 PRESSED = 1 HOLD = 2 RELEASED = 3 #define OPEN and CLOSED OPEN = 0 CLOSED =1 #constructor def __init__(self): self.kchar = self.NO_KEY self.kstate = self.IDLE self.kcode = -1 self.stateChanged = False class Keypad(object): NULL = '\0' LIST_MAX = 10 #Max number of keys on the active list. MAPSIZE = 10 #MAPSIZE is the number of rows (times 16 columns) bitMap = [0]*MAPSIZE key = [Key()]*LIST_MAX holdTime = 500 #key hold time holdTimer = 0 startTime = 0 #Allows custom keymap, pin configuration, and keypad sizes. def __init__(self,usrKeyMap,row_Pins,col_Pins,num_Rows,num_Cols): GPIO.setmode(GPIO.BOARD) self.rowPins = row_Pins self.colPins = col_Pins self.numRows = num_Rows self.numCols = num_Cols self.keymap = usrKeyMap self.setDebounceTime(10) #Returns a single key only. Retained for backwards compatibility. def getKey(self): single_key = True if(self.getKeys() and self.key[0].stateChanged and (self.key[0].kstate == self.key[0].PRESSED)): return self.key[0].kchar single_key = False return self.key[0].NO_KEY #Populate the key list. def getKeys(self): keyActivity = False #Limit how often the keypad is scanned. if((time.time() - self.startTime) > self.debounceTime*0.001): self.scanKeys() keyActivity = self.updateList() self.startTime = time.time() return keyActivity #Hardware scan ,the result store in bitMap def scanKeys(self): #Re-intialize the row pins. Allows sharing these pins with other hardware. for pin_r in self.rowPins: GPIO.setup(pin_r,GPIO.IN,pull_up_down = GPIO.PUD_UP) #bitMap stores ALL the keys that are being pressed. for pin_c in self.colPins: GPIO.setup(pin_c,GPIO.OUT) GPIO.output(pin_c,GPIO.LOW) for r in self.rowPins: #keypress is active low so invert to high. self.bitMap[self.rowPins.index(r)] = self.bitWrite(self.bitMap[self.rowPins.index(r)],self.colPins.index(pin_c),not GPIO.input(r)) #Set pin to high impedance input. Effectively ends column pulse. GPIO.output(pin_c,GPIO.HIGH) GPIO.setup(pin_c,GPIO.IN) #Manage the list without rearranging the keys. Returns true if any keys on the list changed state. def updateList(self): anyActivity = False kk = Key() #Delete any IDLE keys for i in range(self.LIST_MAX): if(self.key[i].kstate == kk.IDLE): self.key[i].kchar = kk.NO_KEY self.key[i].kcode = -1 self.key[i].stateChanged = False # Add new keys to empty slots in the key list. for r in range(self.numRows): for c in range(self.numCols): button = self.bitRead(self.bitMap[r],c) keyChar = self.keymap[r * self.numCols +c] keyCode = r * self.numCols +c idx = self.findInList(keyCode) #Key is already on the list so set its next state. if(idx > -1): self.nextKeyState(idx,button) #Key is NOT on the list so add it. if((idx == -1) and button): for i in range(self.LIST_MAX): if(self.key[i].kchar == kk.NO_KEY): #Find an empty slot or don't add key to list. self.key[i].kchar = keyChar self.key[i].kcode = keyCode self.key[i].kstate = kk.IDLE #Keys NOT on the list have an initial state of IDLE. self.nextKeyState(i,button) break #Don't fill all the empty slots with the same key. #Report if the user changed the state of any key. for i in range(self.LIST_MAX): if(self.key[i].stateChanged): anyActivity = True return anyActivity #This function is a state machine but is also used for debouncing the keys. def nextKeyState(self,idx, button): self.key[idx].stateChanged = False kk = Key() if(self.key[idx].kstate == kk.IDLE): if(button == kk.CLOSED): self.transitionTo(idx,kk.PRESSED) self.holdTimer = time.time() #Get ready for next HOLD state. elif(self.key[idx].kstate == kk.PRESSED): if((time.time() - self.holdTimer) > self.holdTime*0.001): #Waiting for a key HOLD... self.transitionTo(idx,kk.HOLD) elif(button == kk.OPEN): # or for a key to be RELEASED. self.transitionTo(idx,kk.RELEASED) elif(self.key[idx].kstate == kk.HOLD): if(button == kk.OPEN): self.transitionTo(idx,kk.RELEASED) elif(self.key[idx].kstate == kk.RELEASED): self.transitionTo(idx,kk.IDLE) def transitionTo(self,idx,nextState): self.key[idx].kstate = nextState self.key[idx].stateChanged = True #Search by code for a key in the list of active keys. #Returns -1 if not found or the index into the list of active keys. def findInList(self,keyCode): for i in range(self.LIST_MAX): if(self.key[i].kcode == keyCode): return i return -1 #set Debounce Time, The default is 50ms def setDebounceTime(self,ms): self.debounceTime = ms #set HoldTime,The default is 500ms def setHoldTime(self,ms): self.holdTime = ms # def isPressed(keyChar): for i in range(self.LIST_MAX): if(self.key[i].kchar == keyChar): if(self.key[i].kstate == self.self.key[i].PRESSED and self.key[i].stateChanged): return True return False # def waitForKey(): kk = Key() waitKey = kk.NO_KEY while(waitKey == kk.NO_KEY): waitKey = getKey() return waitKey def getState(): return self.key[0].kstate # def keyStateChanged(): return self.key[0].stateChanged def bitWrite(self,x,n,b): if(b): x |= (1<>n)&1 == 1): return True else: return False ROWS = 4 COLS = 4 keys = [ '1','2','3','A', '4','5','6','B', '7','8','9','C', '*','0','#','D' ] rowsPins = [12,16,18,22] colsPins = [19,15,13,11] def loop(): keypad = Keypad(keys,rowsPins,colsPins,ROWS,COLS) keypad.setDebounceTime(50) while(True): key = keypad.getKey() if(key != keypad.NULL): print ("You Pressed Key : %c "%(key) ) if __name__ == '__main__': # Program start from here print ("Program is starting ... ") try: loop() except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed. pass GPIO.cleanup()