My question using only 4k ohm resistors (connected in series in the
The circuit you are describing (a collection of identical resistors
connected in series) is quite different from the one discussed in the
link you provide. I assume you have in mind something analogous to a
digital pot, i.e. a chain of N resistors between GND and Vcc, with N
switches connecting the Arduino analog input to N taps along the chain:
N−1 taps in between consecutive resistors and one extra tap at GND¹.
simulate this circuit – Schematic created using CircuitLab
The push buttons could actually be the multiple contacts of a rotary
switch. If using real push buttons, it would be tricky to know whether a
button is being pushed or if you are just reading noise, though it can
be known by toggling the pin between output and input and using its
In this case, the worst case error happens when all the resistors on one
side of the tap are 5% too high, while all those at the other side are
5% too low. It can be easily shown that the minimum possible voltage at
tap k is
V−(k) = k(1−e)/(k(1−e) + (N−k)(1+e))×Vcc
where e = 0.05 is the 5% tolerance of the resistors. The
maximum possible voltage, V+(k), is given by the same formula
after replacing e by −e.
The graph below shows the expected voltage at each tap for a chain of
20 resistors, with the error bars showing the possible voltage
ranges. The blue curves are the analytical expressions of
V−(k) and V+(k).
As can be seen on the graph, the largest error bars are the ones closer
to the central tap. Thus, the buttons can be discriminated accurately if
the two taps closest to the center have non overlapping error bars. This
criterion could be translated into an exact formula, but the formula
would be quite complex and depend on the parity of N. I propose to do
instead some approximations, starting with a first order Taylor
expansion on the error. This expansion gives the length of the error
V+(k) − V−(k) ≈ 4/N2 k (N−k) e × Vcc
This error is maximal when k = N/2 (we will forget the parity issue):
V+(N/2) − V−(N/2k) ≈ e × Vcc
Then we say that the taps can be discriminated if this error is smaller
than the expected voltage difference between consecutive taps, namely
Vcc/N. Then the criterion is now simply
N ≤ 1/e
which is exactly what your intuition told you.
¹ The extra tap could be at Vcc rather than GND. It would be unwise,
however, to tap both GND and Vcc, as a user pressing both buttons
simultaneously would then short the power supply.
Edit to answer the questions in comments.
Yes, you could use the expressions of V+ and V− to
set the thresholds for discriminating the buttons. For example, you
could set the threshold between k and k+1 halfway between
V+(k) and V−(k+1), i.e.
threshold(k, k+1) = (V+(k) + V−(k+1)) / 2
However, if you try to compute that, you will see that it is extremely
close the average between the expected voltages. Then, you could use
that average as your threshold, which is given by a very simple formula:
threshold(k, k+1) = (k+½)/N×Vcc
Then, the pin number is just the integer closest to Vin/Vcc×N.
Assuming N = 20, this can be written as:
const float VCC = 5.0
float voltage = analogRead(PIN) * VCC / 1024;
int button_number = round(voltage / VCC * 20);
This can be optimized to use only integer arithmetics:
int button_number = (analogRead(PIN) * 5 + 128) >> 8;
Note that 20 buttons is kind of borderline. Your system could fail
if the errors due to resistor inaccuracies and those intrinsic to the
ADC (offset, gain error...) add up the wrong way. If you are building
only one device, then the safest fix would be to take note of the value
analogRead() returns for each button, and set the thresholds halfway
between consecutive values.
You could also measure the resistances of all your resistors, and
arrange them in a way that minimizes the errors. For example, if you
number the resistors from 0 to 19 in order of increasing resistance, I
would build the chain by arranging them in the following order:
0 19 2 17 4 15 6 13 8 11 9 10 7 12 5 14 3 16 1 18
Then you could most likely use the simple formula above to get the
button number at a minimal computing cost.