So Is Your Arduino Lying to You?

Is there really a problem with the Arduino analogRead() function?

Alasdair Allan
4 min readSep 2, 2019

At the tail end of last week, Alain Pannetrat, founder of Omzlo Electronics, took a look at the SAM D21 ADC while developing an Arduino shield and concluded that a four-year old bug in the ArduinoanalogRead() function could mean it is providing incorrect results when measuring voltages.

It turns out your Arduino might be lying to you.

Testing the analogRead() function on an Arduino MKR Zero using two resistors and a multi-meter. (📷: Omlzo)

Pannetrat observed the problem on his own CANZERO IoT nodes as well as on the Arduino MKR Zero, and a SparkFun SAM D21 breakout board.

“…we noticed that measurements were all offset by about +35mV. In other words, all ADC readings on these boards were overestimating their input by about 35mV. These boards have one thing in common: they use the Microchip/Atmel SAM D21 Arm Cortex M0+ 32-bit micro-controller. Further tests across multiple analog pins and multiple SAM D21 boards showed offset errors ranging between 25mV and 57mV.”Alain Pannetrat, Founder of Omzlo Electronics

I didn’t have any of these boards to hand, but I did have an Arduino MKR WiFi 1010, which is built around the same Microchip SAM D21 as the MKR Zero.

So I guess it was time to take a look myself.

Measuring the output of analogRead() on an Arduino MKR WiFi 1010. (📷: Alasdair Allan)

Testing voltages ranging from 0.0V through to 3.3V, Pannetrat saw an offset of approximately +34 mV on pin A0 consistently between the reading on his meter and the value returned by the analogRead() function.

The original results from Pannetrat’s measurements. (📈: Omlzo)

Unfortunately, I didn’t have a good enough voltmeter to hand—as the one on my bench has a 0.0 to 2.0 V, and a 0.0 to 20.0 V range—so I couldn’t quite cover the entire 0.0 to 3.3 V range to three significant figures as Pannetrat had. However, I could take a look between 0.0 to 2.0 V, so setting my bench power supply successively to 0V, 0.5V, 1.0V, 1.5V, and 2.0V I went ahead and did that, slightly modifying Pannetrat’s original script to provide a running average for the value reported by analogRead() to smooth out any blips.

analogRead() results from an Arduino MKF WiFi 1010. (📈: Alasdair Allan)

Interestingly, I didn’t get the same results as Pannetrat. Instead I saw the offset between the voltage I measured using my voltmeter and the value reported by the Arduino analogRead() function increasing with higher voltage.

analogRead() results from an Arduino MKF WiFi 1010. (📊: Alasdair Allan)

Which wasn’t what I was expecting at all.

So I replicated my experiment with an Arduino Nano 33 IoT, which is also based around the SAM D21, and this time I saw different results.

analogRead() results from an Arduino Nano 33 IoT. (📈: Alasdair Allan)

Unlike with the MKR WiFi 1010, where I saw an increasing offset between the value measured with the meter and the value reported by the Arduino analogRead() function, with the Nano 33 IoT I saw a more or less consistent offset of around +18 mV.

analogRead() results from an Arduino Nano 33 IoT. (📈: Alasdair Allan)

This result is more like the one Pannetrat observed. Although what I didn’t see is the same consistent offset when the A0 pin is directly connected to ground as he did.

I also tried an alternative method, using a two resistors to split the voltage in similar fashion to that suggested by Pannetrat. Connecting a 10 kΩ resistor between the GND and A0 pins, and a second 10 kΩ resistor between the VCC and A0 pins, I measured the voltage across the first resistor.

Measuring the output of analogRead() using the resistor splitter. (📷: Alasdair Allan)

With the MKR WiFi 1010, I obtained a reading 1.636 V using a multimeter, and 1.662 V using the analogRead() function. That’s an offset of +26 mV, and that is more or less in line with the previous results using the bench power supply.

It’s not actually that unusual for a micro-controller to have an ADC offset — for instance, the Espressif ESP32 has one and it’s discussed in the documentation, and while I’m not exactly sure what’s going on here, I’m not sure it’s really as serious as some people are making it out to be.

There’s definitely something interesting going on here, but my results are somewhat puzzling as they’re not directly comparable with those found by Pannetrat. So I’m going to keep poking at it for a bit and see if anything else turns up.

However, examining the ADC calibration code provided by Arduino, this looks to be a known problem, and one for a lot of people isn’t really going to be that problematic. Best to figure out what’s going on though, something that I’m sure will happen over the next day or two. The wisdom of crowds is about to make itself known.