Tuesday, January 29, 2019

Saving Arduino Inputs: Analog Readout of Keypad

While building the Time Circuits from "Back to the Future", I needed lots of Arduino pins for the nine 4-digit 7-segment displays, plus more pins for some LEDs, three serial connections (two for communication with other devices and one for sound). Therefore I decided to spend a few thoughts on how to reduce the number of pins required for the 3x4 keypad, plus the additional white button (bottom, left in the picture).

The principle

The solution, in two words is: "voltage divider". The principle of the voltage divider is indicated here:
The output voltage (Vout) is a certain fraction of the input voltage (VCC) that is defined by he ratio of the two resistors R1 and R2. If R2 is much larger than R1, then Vout will be close to VCC. If R2 is much smaller than R1, Vout will be close to zero. And if R1 = R2, then Vout = VCC/2.

Example: 2 switches

Let's start with an example where two switches are connected to a single analog input. In the sketch below we assume that R1 = R2 (maybe 3k - the exact value does not matter, only their ratio). 
The middle pin is connected to an Arduino analog input. When both switches S1 and S2 are open, the following line
  val = analogRead(A1);
returns a value of val=0 (as the input is connected to GND, via R2). When S1 is pressed (and the input is connected to VCC), it returns val=1023. When S2 is pressed (and the input is at VCC/2), it returns val=512. Notice: all these analog values are approximate. The values may slightly fluctuate with time, with temperature, etc. In this circuit I would simply check if val<250 (indicating that no button is pressed, or 250<val<750 (S2 is pressed), or val>750 (S1 is pressed). Please notice that this circuit cannot distinguish whether both S1 and S2 or only S1 are closed.

Another example: 3 switches

The principle should now be clear, so let's extend the circuit to three switches.
I would pick R1=R2=R3 (maybe between 1k and 3k, but all the same). Then we get the following analog readouts: 0 (no switch), 341 (S3 closed), 518 (S2 closed), or 1023 (S1 closed). Again, with this circuit one cannot identify simultaneous button pushes.

The keypad circuit

Now that the basic idea is clear, let's extend this to the matrix of a 3x4 keypad using a set of 1k and 3k resistors as indicated in the image below. The fractions of the supply voltage VCC, as seen at the output pin ("analog in") are as listed in the table. It is also possible to add additional push buttons, as indicated by the piece below the dashed line.
One restriction of this circuit is that it will only work if only one button is pushed at a time. With a little thought, one might be able to select the resistors such that one could identify multiple button presses at a time - but this was beyond the scope for this project.

Arduino code

The output ("analog in") is directly connected to one of the Arduino analog inputs, e.g. A1. The code then reads this input with
  value = analogRead(A1);
The readout will give values between 0 (if A1 is connected to GND) and 1023 (if A1 is connected to VCC). One has to take into account, that the readout values may vary a little (over time, maybe with temperature). The most robust approach is to press all the keys (one by one) and note the corresponding readout values. Then create a list of boundaries which are safely in between these values. For example, if the readout values were 190, 215, 236, 255 (which may vary by plus-minus one or two), I would pick boundaries of 202, 227, 245. The code would then simply check between which boundary values the readout value is. Example code where the result is stored in the "key" variable (key=99 means that no button was pressed):
  value = analogRead(A1);
  if (value < 150) {
    key = 99;
  } else if (value < 202) { key = 13;
  } else if (value < 227) { key = 3;
  } else if (value < 245) { key = 1;
    ...
  } else { key = 5;
  }