Here is a circuit that allows you to use five buttons; up, down, left, right and select, on one analogue pin with the Arduino. It is a adaptation of one used on this LCD shield manufactured by DF Robot. I have used a 10K pull-up resistor and added a 100nF capacitor to help with debounce. The downside is that this can only detect one button press at a time, the button with the lowest resistor value is returned, and the output varies when different voltages are used to power it requiring an update to your software.

In this post I am looking to explore the effect of the existing resistor values, and how to choose your own resistors. This kind of circuit is called a multiple voltage divider or voltage ladder where each rung of the ladder, the switches in the diagram above, produces a different output voltage. Pressing one of the buttons on our circuit changes the output to the analogue port from 5V to a lower one determined by the number of resistors (R1 to R4) in series before the button, as you go down the ladder the output voltage to the pin increases. Here is a short sketch I used to test the circuit and show the voltage reading for each button:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
//converts analogue reading to volts int sensorPin = A0; int sensorValue = 0; float volts = 5; float maxVal = 1023; // maximum value read for 5v float max3V3 = 671; // maximum value read when 3.3 volts used. void showVolts(int val) { float voltage = val * (volts / maxVal); Serial.print(val); Serial.print(", volts: "); Serial.println(voltage); } void setup() { // the arduino uno is set to read analogue ports at 5v. So has a lower maximum at 3.3 volts. if (volts == 3.3) { maxVal = max3V3; } Serial.begin(9600); delay(1000); showVolts(analogRead(sensorPin)); // with no button pressed, this should equal volts. Serial.println("--------------------"); delay(4000); } void loop() { sensorValue = analogRead(sensorPin); if (sensorValue <= (int) maxVal-1) { // ignore input when no button pressed showVolts(sensorValue); } delay(20); } |
The results from this are shown in the chart below, for the power I used the regulated 5V and 3.3V outputs on the Ardunio Uno, if you run this set-up yourself you may notice an anomalous reading when you release the button, more about this later. Note that a button press drops the power down to the recorded value, the pin is normally high (5V/3.3V) through the 10K resistor.
| Resistance1 | at 5v | at 3v3 | ||||
| Btn | Value | Meter | reading2 | volt | reading2 | volt |
| S1 | none | 0 | 0V | 0 | 0V | |
| S2 | 330Ω | 334Ω | 31 | 0.15V | 19 | 0.10V |
| S3 | 950Ω | 964Ω | 88 | 0.43V | 57 | 0.29V |
| S4 | 1950Ω | 1948Ω | 166 | 0.81V | 108 | 0.53V |
| S5 | 5250Ω | 5270Ω | 352 | 1.72V | 230 | 1.14V |

There are two columns for the resistance, the value marked on the resistors and the multimeter reading. It is best to use 1% tolerance resistors for this circuit to reduce the chances of the readings drifting close to the next. A good spread of output voltages ensures accurate switching.
All this works very well, but what to do if you wish to add more buttons, or have different output voltages? Looking at the circuit, each button can be seen as a voltage divider, such as shown in the diagram on the left. Where R1 is the 10K pull-up resistor and R2 is the sum of the resistors for the button being pressed.
With this simplified model we can calculate any missing value, but for now there are a couple of things to do; calculate the output voltage for the given input voltage and resistance and more usefully for us, work out a value for R2 to give us the desired output voltage.
Calculating the Output Voltage
Using Switch 2 in our existing circuit for example, with 5 volt power:
![]()
And again with Switch 4. This has three resistors in series so the value for R2 in the voltage divider is: 330R + 620R + 1K = 1950 ohm:
![]()
its rather satisfying when your calculations closely agree with the real world readings.
Setting the output Voltage
So, lets try working out some resistors based on the following specification: powered at 5V with five buttons, each 0.20V apart; S1: 0V, S2: 0.20V, S3: 0.40V etc. We know the input voltage, output voltage, and resistor R1, but not R2, here is the formula for finding the resistance of R2:
![]()
![]()
These values show the resistance required for each switch and to find the resistor the previous resistance value needs to be deducted. As these values probably won’t be in the E24 Standard Value range and won’t be available in the shops I have the nearest available value in the Standard Resistor column:
| Btn | Resistance | Resistor | Standard Resistor |
|
| S1 | none | |||
| S2 | 417Ω | 417Ω | R1: | 390Ω |
| S3 | 870Ω | 453Ω | R2: | 430Ω |
| S4 | 1364Ω | 494Ω | R3: | 560Ω1 |
| S5 | 1905Ω | 541Ω | R4: | 560Ω |
Obviously, we will need to get this onto breadboard for testing.

Using the same sketch as before I re-ran the tests with the new resistor values, the results are shown below; I have also included an Expected column to show the calculated voltage using the method shown earlier.
| at 5v | at 3v3 | ||||||||
| btn | Resistor | Resistance | expected | reading | volt | expected | reading | volt | |
| S1 | none | – | 0 | 0V | – | 0 | 0V | ||
| S2 | R1 | 390Ω | 390Ω | 0.19V | 39 | 0.19V | 0.12V | 25 | 0.12V |
| S3 | R2 | 430Ω | 820Ω | 0.38V | 76 | 0.37V | 0.25V | 50 | 0.25V |
| S4 | R3 | 560Ω | 1380Ω | 0.61V | 122 | 0.60V | 0.40V | 81 | 0.40V |
| S5 | R4 | 560Ω | 1940Ω | 0.81V | 165 | 0.81V | 0.53V | 108 | 0.53V |
That, to me, looks close enough to the 0.20V separation at 5V specified to provide some stable hardware. Here is a short sketch to demonstrate the use of analogue buttons, it returns a number corresponding to the button pressed. When you release a button its resistance changes momentarily to give the Arduino a false reading, to fix this I have added de-bounce functionality to this program:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
// analog button test #define ANALOG_BTN A0 byte btnVal = 0; byte lastButton = 0; unsigned long lastBtnPress; // changes the sensorValue into the number of the button pressed byte getButton(int sensorValue) { // change these sensorValue numbers to suit your own button readings if (sensorValue > 150) { return 5; } if (sensorValue > 110) { return 4; } if (sensorValue > 70) { return 3; } if (sensorValue > 30) { return 2; } if (sensorValue >= 0) { return 1; } return 0; } // which analog select button has been pressed? // byte imp analog input byte whichSelectBtn(byte imp) { const int sensorMax = 1023; // 671 for 3.3 volts // return when no buttons are being pressed if (analogRead(imp) >= sensorMax-1) { return 0; } byte buttonOut = 0; const int samples = 150; const int debounce = 30; // no of milisecs to wait before accepting a button change // take an average reading unsigned int sensorValue = 0; for (int i = 0; i < samples; i++) { sensorValue = sensorValue + analogRead(imp); } sensorValue = sensorValue / samples; buttonOut = getButton(sensorValue); // if buttonOut changes too rapidly, then ignore the change - debounce if (buttonOut != lastButton && lastBtnPress > millis()) { buttonOut = 0; } lastBtnPress = millis() + debounce; lastButton = buttonOut; return buttonOut; } void setup() { Serial.begin(9600); delay(1000); Serial.println("ready"); } void loop() { btnVal = whichSelectBtn(ANALOG_BTN); if (btnVal > 0) { Serial.print("button "); Serial.println(btnVal); if (btnVal == 5) { Serial.println("select button"); } // etc... or use a switch statement } } |
With this substitution on R3, changing the desired 510 ohm resistor for a 560 ohm, It may be interesting to see what kind of tolerance range we may be able to use. So, for the 0.60V output the optimal resistor would be 494 ohm but we only have Standard Resistor values available and to find the resistance value for our calculation to find the output voltage we need to add 820 ohm (R1 + R2) to R3:
| Resistor | Resistance | Volts |
| 430Ω | 1250Ω | 0.5556V |
| 470Ω | 1290Ω | 0.5713V |
| 510Ω | 1330Ω | 0.5869V |
| 560Ω | 1380Ω | 0.6063V |
| 620Ω | 14400Ω | 0.6294V |
Clearly, using four decimal places shows is that a 510 ohm resistor is rounded up to 0.60V, while the 560 ohm rounds up to 0.61V, so for our purposes both are suitable. The other resistors will be outside the specification.
Finding the Power Consumption
We also need to look at how much power our circuit is consuming, this is for two reasons: to ensure that our resistors are properly rated and that the power supply can supply enough power. The Ardunio Uno provides a regulated supply of 20mA at 5V and 50mA at 3.3V. The calculation is done with Ohms law:
![]()
So, in the original circuit the shortest path to ground is through Switch 1 and the 10K resistor:
![]()
a very low current, this is the maximum the circuit can use. Optionally, to work out the power use for another button add up all the resistors before the switch, for example in the original circuit Switch 5 comes to: 15250 ohm
![]()
Voltage Divider Calculations
Just to round things off, here are four formulae for finding any resistor and voltage in the divider circuit:
![]()
![]()
Links and Sources
- https://www.arduino.cc/en/Tutorial/ReadAnalogVoltage
- http://www.freetronics.com.au/pages/16×2-lcd-shield-quickstart-guide
- https://en.wikipedia.org/wiki/Voltage_ladder and https://en.wikipedia.org/wiki/Resistor_ladder
- http://www.diyaudioandvideo.com/Electronics/ResistorColorCodes/
- https://learn.sparkfun.com/tutorials/voltage-dividers
- Practical Electronic for Inventors 3rd Ed. 2013, isbn:9780071771337. p7
- Art of Electronics 3rd Ed. 2015, isbn:9780521809269. p61
Hello,
I already have a ladder circuit but I couldn’t create nor find the code to debounce. Your code will do it hopefully, however I think there’s a mistake in this part:
if (buttonOut != lastButton && lastBtnPress > millis()) {
out = 0;
}
The “out” variable was not declared anywhere. What’s its purpose?
Thank you in advance.
My mistake, the out=0; should read buttonOut=0; I’ve updated the post, thanks for spotting that.