I'm reading the voltage with voltage divider:


simulate this circuit – Schematic created using CircuitLab | Arduino is connected to both USB and a 9V adapter.

float r1 = 100000;
float r2 = 9980;
float adc, voltage, analog;
void setup() {
  pinMode(A0, INPUT);

void loop() {
  for (int i = 0; i < 256; ++i) {
    analog += analogRead(A0);
  analog = analog / 256;
  adc = analog * 1.228 / 1023;
  voltage = adc / (r2 / (r1 + r2));
  Serial.print(", ");
  Serial.print(", ");
  Serial.print(", ");
  Serial.println(" ");

Here's the accuracy test result:

Input | Arduino | Difference
 1V      0.89V     -110mV
 2V      1.90V     -100mV
 3V      2.92V     -80mV
 4V      3.93V     -70mV
 5V      4.95V     -50mV
 6V      5.97V     -30mV
 7V      6.98V     -20mV
 8V      8.00      0
 9V      9.01      +10mV
 10V     10.02     +20mV
 11V     11.04     +40mV
 12V     12.06     +60mV

The input voltages measured by DMM and was spot on voltage, like 1.000V.

I didn't test voltages above 12V but it seems the difference is gonna increase by 20mV with each 1V increase, that means at 50V voltage reading is gonna be off by ~1V!

Even with 10bit resolution of Arduino I should have had steady accuracy with some percentage of difference(right?), but as you can see the result shows that the reading is not steady at all!

Why the voltage difference decrease/increase when voltage changes at the input?


As stated by Majenko, the ADC can in principle have all sorts of errors. However, if you look at your data, you should notice that the error is essentially linear with the voltage. This means that the non-linearity error is quite small, and you are mainly seeing the offset and gain errors. This is fortunate, because these errors are easy to remove, given good calibration data.

The table you wrote in in your question happens to be a perfectly good calibration table. By fitting a straight line to it you can get a calibration function, and then correct the readings simply by adding this to your code:

voltage = 0.9848815 * voltage + 0.125354;

Or you could instead figure out the correction at the ADC level and replace

adc = analog * 1.228 / 1023;


adc = (analog + offset) / scale;

where offset and scale are computed from the calibration.

Edit: Here is how I extracted the calibration coefficients from the data.

First, I saved the data in a text file calibration.tsv and cleaned it up to remove the units and add a comment marker (#) in front of the column labels:

#Input | Arduino | Difference
 1      0.89     -110e-3
 2      1.90     -100e-3

Then I looked at the data with gnuplot. Since I want to know how to correct the readings, I plot the amount I have to add (minus column 3) v.s. the Arduino reading (column 2):

plot 'calibration.tsv' using 2:(-$3)

At this point it appears it's linear with what looks like mostly quantization noise. I model this as a line of equation a*x+b, and compare the line with the data:

a = -1e-2
b = 0.12
plot 'calibration.tsv' using 2:(-$3), a*x+b

The line is quite off the data, but it's a reasonable first approximation. Now I ask gnuplot to perform a least squares fit, then take another look:

fit a*x+b 'calibration.tsv' using 2:(-$3) via a, b

Now the fit looks quite good. When it completed the fit, gnuplot displayed:

Final set of parameters            Asymptotic Standard Error
=======================            ==========================
a               = -0.0151185       +/- 0.0002953    (1.953%)
b               = 0.125354         +/- 0.002173     (1.734%)

so now I know I can correct the data with:

voltage += -0.0151185 * volatage + 0.125354;

which is equivalent to what I wrote above.

  • How do you calculate the offset and scale? Oct 11 '19 at 11:10
  • 1
    @newbie, put the data in Excel/Calc, draw a chart with expected values and function and then change the offset and scale values for the function until the lines in the chart match
    – Juraj
    Oct 11 '19 at 11:27
  • 1
    @newbie: I am an old timer who likes text-mode programs rather than Excel, Calc and such, so I used gnuplot. See expanded answer. Oct 11 '19 at 12:18
  • 1
    @newbie, column B should have a function in cells. and the function should use two other cells with the coefficients you want to fine-tune
    – Juraj
    Oct 11 '19 at 12:47
  • 1
    @newbie, example github.com/jandrassy/Regulator/tree/master/util/Calc
    – Juraj
    Oct 15 '19 at 5:07

Yes, you are right that the Arduino ADC is not accurate. If you read the datasheet (ATMega328P section 28.9, ADC Characteristics) you can see a lot of "error" and "non-linearity" characteristics.

These all add up to inaccuracies in your readings. For example the "Offset error" at your VREF could be as much as ±4.4mV, and the "Gain error" the same. Non-linearity can be as bad as 2mV, etc.

The Arduino ADC is not meant to be accurate. It's meant to be simple and cheap to implement. (It is a "Successive Approximation ADC" - approximation here is the keyword...) If you want a higher accuracy then there are more suitable ADC chips available that give that accuracy.

  • I was expecting 100mV error to be steady like 1.100V, 2.100V ... Wasted so much time calibrating this ...! beside using external ADC do you think it's good idea to switch to a PIC micro-controller with 12bit ADC. i'm asking this because PIC is cheaper. Oct 11 '19 at 11:07
  • 1
    I would always say use a PIC over an ATMega, but then I am a PIC junkie- however increased resolution does not equate to increased accuracy. It's still an SAR with the inherent inaccuracies which that gives you. Since the inaccuracy looks to be pretty linear you can use calibration and calculations to remove it. You have a straight line, the formula for which (if you remember school maths classes) is y = mx + c
    – Majenko
    Oct 11 '19 at 11:14
  • Re “successive approximation ADC”: any measurement whatsoever is always an approximation. The keyword here is “successive”: it means the ADC works by iteratively refining the approximation. Oct 11 '19 at 12:40
  • @EdgarBonet And because the sampled voltage is stored in a tiny capacitor, and it's impossible to read a voltage without drawing some current, each successive comparison will affect the voltage across that capacitor making it less accurate with each bit that is approximated. So it's successively worse approximation. It's a successive approximation that can only ever be approximate.
    – Majenko
    Oct 11 '19 at 12:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.