# Two Pushbuttons on One Input for More I/O

If you are using pushbuttons and don't quite have enough digital I/O, here's a little trick to free up a pin or two.

``````    +5V
+_|
-|_  PB1
+ |
\
/ 20K
\
/
\_______________\ To AVR
|      \        / Input
+_|      /
-|_  PB2  \ 150K
+ |      /
|      \
|      |
Gnd    Gnd
``````

If neither button is pressed, the input should read high when its internal pull-up (20K - 50K) is enabled, and low when its pull-up is disabled. If the top button is pressed, the input will read high with or without the pull-up. Similarly, if the bottom button is pressed, the input will "stick" low.

A drawback: if both buttons are pressed, the input will read the same as if only the bottom button were pressed.

But how can you see the diference between the high from the pull-up and the high from the button? :-?

But how can you see the diference between the high from the pull-up and the high from the button?

Do digitalWrite(buttons, LOW) to disable the pull-up, read the input, then digitalWrite(buttons, HIGH) to enable the pull-up and read again. The input will follow the written values only if neither button is pressed.

I would want to measure the actual voltage at the input pin when no switches are pressed to see if a valid logic voltage is present. A high value pull down with the internal pull up enabled may not be valid.

Using a triple voltage divider wired to a analog input pin is a better solution in my opinion.

Lefty

I would want to measure the actual voltage at the input pin when no switches are pressed to see if a valid logic voltage is present. A high value pull down with the internal pull up enabled may not be valid.

If the Atmel data sheet can be relied upon, input current (with pull-up disabled) for voltage between 10% to 90% of Vcc should be within +/- 10 uA. Through 150K to ground, that comes to +/- 1.5 V, well below the approx. 2.6 V threshold between LOW and HIGH readings. (The input protection diode limits negative voltage to approx. -.6 V.) The pull-up value is 20K - 50K, yielding a HIGH input voltage of 3.75 - 4.41 V. Again, this is well above the threshold. +/- 10 uA would affect this by less than +/- 0.02 V.

Using a triple voltage divider wired to a analog input pin is a better solution in my opinion.

After posting this, I was thinking of a possible improvement using a binary weighted voltage divider. I suspect one could unambiguously decode 4 buttons with a single analog input, and maybe 8. This approach has the advantage of detecting any combination of buttons, even if two or more are simultaneously pressed. Figuring out the necessary tolerances began to make my head hurt, though.

jdoll analog is such fun isn't it? :-D :P

I can't see that being good for more than 4 inputs without time, pain and precision parts but it is an option. For more inputs you may need to calibrate them for various temperatures. The atmega168 and other atmel chips have a built in temperature sensor which may be of use if it's calibrated.

It is a lot of work but it is a cool hack. :)

This is very good. :) Thanks for posting the trick.

Álvaro

Lady Ada uses 5 buttons on her Monochron in a resistor ladder, I believe. I'll check the schematic.

Oops, my mistake it’s three buttons with five resistors.

What if you could measure N buttons but using a digital input, rather than ADC input ??

Well, it’s theoretically possible, and so it is in practice. I just implemented it, and works fine.

But for you to use this approach, you must:

• Not mind waiting a few milliseconds for buttons to sample.
• Have either a counter/timer available (or a simple for-loop somewhere)

Theoretically you can even sample multiple buttons, but that is hard to do.

Let’s explain first how this works: here’s the schematic:

We have a capacitor there. What we will measure is “how long” the capacitor takes to charge until the input pin of arduino reads as 1. We also need to discharge the capacitor before we sample again.

Let’s assume no button is pressed, and our capacitor has a value of “VC” at its terminals. If we configure the PIN as OUTPUT LOW, then we’re actually feeding only a few microvolts, so we’ll sink current from the capacitor using R1.

The basic formula for a RC charge/discharge cycle is “V= V0 * e^(–t/RC)”. I’ll not write full equations here, you can ask me later for them.

So, after we wait some time (a few milliseconds) we put the PIN back in input mode. If any button is pressed, the capacitor will start charging using the specific resistor attached to that button. After a while, the input PIN will read as ‘1’. If we count time from when we put PIN as INPUT and PIN reading as ‘1’ we see we have different values depending on which button was pressed.

This requires calibration and some error tolerance. For best results, you should sample 3 times, and if those 3 samples are near a certain known value for a button, you can assume it is pressed.

Here’s a sample code I wrote just to demonstrate:

``````void setup()
{
Serial.begin(115200);
PORTB&=~(1);
}

void loop()
{
unsigned int c;
char measure[80];

// Reset pin.
DDRB|=1;
// Let discharge
delay(1);
DDRB&=~(1); // Input now.

for (c=0; c<65535; c++) {
if (PINB&1) {
sprintf(measure,"C: %u\r\n", c);
Serial.write(measure);
break;
}
}
delay(1000);
}
``````

And here’s output for 3 buttons, each pressed at a time and sampled three times:

``````C: 1795
C: 1780
C: 1798
C: 7357
C: 7550
C: 7381
C: 2162
C: 2173
C: 2139
``````

Cool, no ?

Best,
Álvaro

To the OP, There is already a tutorial on the arduino site for that two switch technique: http://www.arduino.cc/en/Tutorial/TwoSwitchesOnePin