Essentially, if you have a 4x4 matrix, you have 4 columns and 4 rows. You apply a test signal to each of the rows in turn, then inspect the columns to see if they signal appears on any of them. (Possibly rows/columns interchanged.) If we have \(m\) rows and \(n\) colums, we need \(m+n\) pins to handle the \(mn\) buttons. So for a 4x4 matrix that is \(4+4=8\) pins. ## Basics There are four pins: C1..C4 and R1..R4. 1. Set one row pin high, the rest low. 2. Read the colum pins to see if any are high. ## Code We use the Keypad library. In the Arduino IDE, go to Sketch→Include Library→Manage Libraries (C-S-i), search for Keypad, and you want the one by Mark Stanley and Alexander Brevig. This is the roughly the example code from the Circuit Basics page linked below: ```cpp #include const byte ROWS = 4; const byte COLS = 4; char theKeys[ROWS][COLS] = { {'0','1','2','3'}, {'4','5','6','7'}, {'8','9','A','B'}, {'C','D','E','F'} }; byte rowPins[ROWS] = {7, 6, 5, 4}; byte colPins[COLS] = {8, 9, 10, 11}; Keypad customKeypad = Keypad(makeKeymap(theKeys), rowPins, colPins, ROWS, COLS); void setup(){ Serial.begin(9600); } void loop(){ char customKey = customKeypad.getKey(); if (customKey){ Serial.print(customKey); } } ``` ## Tutorials * At [Circiut Basics](https://www.circuitbasics.com/how-to-set-up-a-keypad-on-an-arduino/) * At [Last Minute Engineers](https://lastminuteengineers.com/arduino-keypad-tutorial/) ## Questions 1. If we have two button arrays, and connect the row pins (so that we probe the rows on both arrays at the same time), do we need only 12 IO pins in total? (If we get one working and like it, get a second and do the experiment.) ## Pyserial We can read from the serial port using Pyserial. This is a toy example that associates shell commands to the buttons: ```py #!/usr/bin/env python import serial from time import sleep import sys from subprocess import run from threading import Thread class Runner: def __init__(self,cmd): self.cmd = cmd self.thread = Thread(target=self) self.result = None def __call__(self): self.result = run(self.cmd) def start(self): self.thread.start() #COM = 'COM3'# windows non cygwin #COM = '/dev/ttyACM0' # Linux COM = '/dev/ttyUSB0' # Linux #COM = '/dev/ttyS2' # cygwin #COM = '/dev/tty.usbserial-1460' # mac BAUD = 9600 #BAUD = 256000 # fine over a USB cable ser = serial.Serial(COM, BAUD, timeout = .1) print('Waiting for device'); sleep(3) print(ser.name) class Handler: def __init__(self): self.cmds = {} self.cmds[15] = ["echo","hello world"] self.cmds[3] = ["xeyes"] def __call__(self,x): if x >= ord("0") and x <= ord("9"): return self.handleIndex(x-ord("0")) if x >= ord("A") and x <= ord("F"): return self.handleIndex(10+x-ord("A")) return self.handleFunnyChar(x) def handleIndex(self,i): print(f"Button {i}") if i in self.cmds: Runner(self.cmds[i]).start() def handleFunnyChar(self,x): print(f"Funny char {x}") handler = Handler() try: while True: b = ser.read(1) if len(b) == 0: continue handler(b[0]) except KeyboardInterrupt: print(f"Ctrl-C") exit() ``` # More ambitions There is [this electronoobs page](http://electronoobs.com/eng_arduino_tut103.php) which aims to build a bigger matrix.