Dup Goto 📝

ButtonMatrix1

To Pop
126 lines, 463 words, 3346 chars Monday 2023-07-31 13:46:47

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:

#include <Keypad.h>

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

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:

#!/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 which aims to build a bigger matrix.