John L Errington MSc

John Errington's Experiments with an Arduino

Improved precision and accuracy from your ADC readings

About the Arduino ADC's (Analog to Digital Converters)

The ADC on Arduino boards based on the

  • AtMega 2560, (eg Mega)
  • ATMega 328, (Uno, Nano )
  • ATMega 4809 (Nano Every)
  • 32U4 (Leonardo, Pro Micro)
  • and Espressif ESP8266 (NodeMCU, Wemos D1 Mini)

give a 10 bit result based on the input voltage and the reference voltage.

Later boards based on other processors will have different ADC specs.

More bits (better "resolution") does not mean you will necessarily get a better more stable or more accurate reading; in fact the ADC's on these chips often have worse linearity and offset errors than the 10 bit ADC's

  • Espressif ESP32 (HUZZAH32, ESP32 DEV KIT,ESP32 Thing etc): 12 bit
  • SAM3X8E (Due); 12 bit (but 10 bit default)
  • SAMD21 (Arduino Zero, Arduino MKR1000): 12 bit


Why take multiple readings

You may wonder why many example programs read the SAME ADC pin a few times. This is because

  • electrical noise may be causing slight differences in your reading.
  • the voltage you are reading may be changing.
  • the voltage reference may be changing - especially the "DEFAULT" from your USB supply, or if you're using a battery.

By taking a SET of readings and averaging them you get more sensible results.

While you CAN take steps to reduce the noise in your system, noise isnt ALWAYS a bad thing.


Using noise to our advantage - It sounds strange but you can use the noise to give BETTER readings!

Suppose we have a 10 bit ADC reading a voltage of 2470 mV with a 5000 mV refererence. The reading SHOULD be 505.86 (505 or 506)

Stochastic ADCHere we see the results of having a small amount of random noise in the reading. Each reading on its own shows a little "jitter". Taking a single reading we could see 504 or 508 as our result.

By taking 16 readings and averaging them CORRECTLY as shown here we see a more precise value.

EXAMPLE: our Arduino uses the "DEFAULT" reference of 5V from the USB connection.

So the measurement range is 0 - 5000 mV in 1024 steps. To find the voltage we add 0.5 then multiply our reading by 5000/1024

Our lowest reading of 504 gives 504.5 * 5000 / 1024 = 2463.37890625 ?

NO. The result CAN NOT be more precise than the data!

We have three figure precision in our data (0 - 1024) so we need to round the result to three figures. 2460mV

(We can be a LITTLE more precise; The resolution is 1 part in 1024 - or about 1/1000 so the result is 2463 +- 3 mV)

Taking 16 readings gives us extra resolution of two more bits (taking the 10 bit ADC to 12 bits)

(The formula is extra bits = sqrt(number of readings)/2) so 4 readings adds 1 bit, 16 adds 2, and so on.)

So our result of 506.1 gives V = (506.1 + 0.5) * 5000 / 1024 = 2473 mV


Sketch to show this in operation

The sketch follows the example above, and takes a set of 16 readings, averages them, and scales the result to mV.

Get it here


Improving accuracy

Precision and accuracy are NOT the same. Our example above shows 4 figure precision - or actually 1 part in 4096.

Suppose the USB voltage was 4.800V (4800mV ); that is WELL within the specification for USB.

Our result of 506.1 leads us to believe the measured voltage was 2473mV.

However we should really have taken V = (506.1 + 0.5) * 4800 / 1024 = 2374mV

The result was PRECISE - but not ACCURATE.

Its not possible to improve the accuracy beyond that internal to the converter; the best we can do is to compare it with a good reference voltage.


Calibration against a reference voltage at the ADC input

Many newer boards, using chips such as the 32U4, ESP8266 and ESP32 dont provide a pin to apply an external reference. We can use a simple "trick" to get around this.

The idea is that the INTERNAL reference should not change much in a short period of time. So if we take a set of readings of Vin, as described above, using any available reference; then read Vref against the SAME reference, we can use the Vref reading to apply any necessary correction to our measurement.

You will of course need to choose a voltage reference within the range of the ADC. The LM4040AIZ-2.048 provides a voltage of 2.048V and you will need to choose a suitable resistor as shown above.
For a 3.3V supply a resistor in the range 4k7 - 47k should be fine, as the ADC input draws little current.



External Analog to Digital converters

The ADS1015 module offers 4 12 bit analog inputs and connects to the arduino via I2C. However the ADC is not a successive approximation type, but a Sigma-Delta converter.

This achieves high resolution (lots of bits) - but at the expense of much slower conversion rates.

Further reading

Nick Gammon's page has lots more very useful information.