Many of us at time wish we have more IO pins for the Arduino. I got used to a 5-key keypad I made early on. But
using up 5 digital pins on the ATMEGA328 is using up a lot. So, I got into this experiment to see if I can
use one single analog pin to support 5 tact-switches with a voltage divider alone.
The design is simple:
just a voltage divider setup with each tact-switch shorting out the specific resistor of specific value for that specific key.
Net-net is, it works! The 10bit ADC has just good enough resolution to do the job for 5-keys. It can discern all 32 combinations 00000 to 11111 easily. Easily means
results are not ambiguous and with a reasonable margin for safety. The center key is ENTER (1K), then UP (2K), RIGHT (4K), DOWN (8K), LEFT (16K).

EDIT: before you ask... I use 3K+1K for the 31K, 2K+2K for the 4K, so on. That is why there are so many resistors.
I had some misconceptions before I actually thought this thought. So experience to share:
- Before I thought it through, with the USB power going through diode drop and deltas with different 5V regulator, I though that is a show stopper. If that isn't enough, high current draw causing a drop is another show stoppers. In actually doing the feasibility analysis, these kinds of voltage drop turns out to be irrelevant - as long as it is stable at the time of key press, that the
board is running a good bit below 5V doesn't matter. The ADC count is the
fractions of supplied voltage, and giving fractions of supplied voltage is exactly what voltage dividers do. So,
there is even no need to convert counts to actual volts. The ADC counts alone suffice.
- The logic of choosing resistor values as 2^n was obvious, so I use 1K, 2K, 4K, 8K, 16K. What was
less obvious to me, a novice, is the choice of R-Reference. The minimum change in voltage is when Key0 is pressed (ie: 1K resistor is shorted). I need the minimum change to be as big as possible so the ADC can easily see the change. When I actually do the math for the best R-Reference, I realized
function has a max: 31K. Even when maximized, the
smallest theoretical change is just 8.4 counts on the ADC. Per datasheet, ATMEGA328 ADC is rated +/- 2 counts, so it can comfortably discern an 8-counts change.
- Using 2^n resistor value makes the math easier. But,
8-count is 8/1023=0.78%. With 1% parts, I need to use the measured value instead of the nominal value - which means I need to store the measured values.
Net-net: using a data-table of results actually use less flash-space, faster, and easier to code. I ended up just
storing the 32 actual ADC values in an array int[32] rather than calculating it on-the-fly. For the compare, I use:
for (idx=0;idx<31;idx++) { // if nothing matched, assume it is 31 - no key pressed
if (stored[idx]-4 <= adcValue && adcValue <= stored[idx]+4) break;
}
// now idx has the key combination
- To see if the actual ADC counts varies against environment, I switched between different Arduino boards, different power source, different power drain, so on. Thus far, I have not seen the counts change more than 2 counts. So a tolerance of 4 works very nicely even when I switch the keypad and between different setups.
- Draw back is, for every such keypad (with its resistors), one needs to get the 32 actual ADC values specific to that keypad.