resolution with AREF not big enough

Hey all :slight_smile: I am trying to use the Arduino as a measuring device and I need it to as precise as possible. I am measuring it in the low mV range and so I put 1.05V (measured with DMM) on the AREF pin. The resolution should be 1.02mV but I measured at least 10 times with different resistors (all of them also measured with the DMM to make the analytic calculation better) and I'm missing at least 2.5mV in all of them and those 2.5mV (or actually 2.04V) are very important to me. I am using this sketch which is very basic :

float voltage ;

void setup() {

void loop() {
  voltage = analogRead(A0);
  voltage = voltage * 1.02 / 1023 ;
  voltage = voltage * 1000 ;
  Serial.print(voltage, 8);
  Serial.println ("mV") ;

I tried to use the internal reference but it didn't change much. I also tried from 3 different Arduinos (two Uno and one Nano, all of them are clones) and it was the same. Am I doing a stupid mistake ? or is it just not possible for some reason ?

Thank you all very much :slight_smile: :slight_smile: :slight_smile:

This is the wiring with an example for a bad measurement :

analogRead returns an int.
If you want the result in millivolts, do the calculation in millivolts.
And divide by 1024, not 1023 (INCOMING!)
Eight places of decimals with a float is optimistic.

Thank you for your answer.
But if I define "voltage" as a float it doesn't really matter that analogRead is an int no ? This is my new sketch :

float voltage ;

void setup() {

void loop() {
  voltage = float (analogRead(A0)) * 1020 / 1024 ;
  Serial.print(voltage, 4);
  Serial.println ("mV") ;

But I get again the same result (4.98mV instead of 6.72mV). Have I followed your advice right ?


So, where does “1020” (or “1010” as you had it, I believe) come from, if the reference is “1.05V”?

(Not enough to account for the discrepancy, I agree, but consistency helps)

Yeah, it was 1010 before. I know it's inconsistent here but I have around 20 measurements and it's all messed up when I write it here. It was consistent on the moment I did and wrote the measurements and their results. Sorry for the inconvenience

What’s the ADC input impedance? (I don’t remember, and I can’t easily see the datasheet on my phone)

Something I made: averageRead.ino with correction for the half-bit.
When using the average of many samples, the noise will help to get a better result. The hardware of the ADC of the Arduino Uno and Nano are only accurate for the 10-bits, but with the average one or two more bits resolution can be achieved.

Using the voltage regulator of 3.3V as reference (with resistor divider) is not accurate. I think that the internal voltage reference is better, although its value can be between 1.0 and 1.2V.

Can you calculate everything with real units ? For example the 'voltage' should be the Voltage, in my opinion :wink:

float milliVolts = voltage * 1000.0;
Serial.print( millisVolt, 4);
Serial.println ("mV") ;

I searched in the datasheets couldn't find it. What I found was that it is better to connect an impedance with less than 10KOhm for an optimal measurement. some people on the internet say it is 100MOhm.

I understand why the milliVolt thing bothering you guys, sorry, I totally forgot that when I uploaded it here. Sorry.

Thank you for your sketch, Koepel. It raised the voltage to V = 8.5mV +- 0.3mV (analytic 9.68mV). I am going to have now another series of measuring and see if that's good enough, thank you :slight_smile:

Edit :
Ok, I have a deviation of maximum 1.5mV which can do the job for me. Thank you all :slight_smile:

If your grounding is not perfect then the current used by the Arduino board itself can lift the signal of the analog measurement. I have that in a project where I put the voltage divider on a shield and also the power supply is on that shield. Try to measure 0V, and put the offset in the sketch.

The input impedance for a digital input is very high, it can almost not be expressed in a number. But the input impedance is not a resistor to ground, it is a impedance to something and that something can by anything.

The analog input impedance is also very high, but the ADC requires a little charge. With a circuit impedance of 10k, then there is enough for that charge. When the circuit impedance is 100k, everything still works, but it is less accurate. That can be solved with a small capacitor between the analog input and GND. Perhaps 1nF or 10nF is enough for that charge that the ADC needs.

You have a voltage divider with 98.5kΩ and 200Ω. So the circuit impedance is 199.6 Ω. That is lower than 10k.

When you have noise from the 50Hz / 60Hz mains voltage, that can be hard to get rid of. If you take 1000 samples and still have voltage that changes, then you have low frequency noise.

Why not get an external ADC?

Also an industrial op-amp to scale the values

I wanted a highly accurate ADC process for a project, so I used a TL431 to create a precision voltage reference. I applied that to one ADC pin, and used its measurement to calibrate the ADC conversion process before every read of the sampled voltage. It improved consistence a lot.

Also, I think the ADC processes don't maintain perfect linearity all the way to zero.


You should use the voltage reference for AREF, unless the voltage is too low. There is a minimum voltage for AREF, I can not find it in the datasheet at the moment.

The Arduino Uno is linear for the 10 bits of the ADC.
A external ADC, as dave-in-nj wrote, is for example: ADS1115 16-Bit ADC - 4 Channel with Programmable Gain Amplifier : ID 1085 : $14.95 : Adafruit Industries, Unique & fun DIY electronics and kits.

What about your project ? Why do you want to read such low voltages ?

There is no way you will measure 4.93mV accurately directly with an arduino.

The uno has a 10 bit ADC so the quantization interval with the "INTERNAL" reference (1100mV approx) is about 1100/1024 or 1mV.

Also while the ADC is REASONABLY linear it has a +- 2LSB absolute accuracy. So your measurement of 4.93mV would be "around 4 but maybe 2 or 6"

You dont say why you need to measure such small voltages. My page here shows how I did it.

However if the measurement is not quite so critical you could just use a simple op amp non-inverting amplifier circuit as shown in my tutorial.

Have you measured the actual volts at the analog input?
I see you quote "analytical" values, but have you used your DMM to check those "analytical" values?

Can you also print the raw analog input values please.

Can you please post a picture of your testing project so we can see your component layout?
Have you tried a 0.1uF or even a 10uF capacitor between the analog input pin and gnd?

Thanks.. Tom.. :slight_smile:
@johnnerrington :slight_smile: :slight_smile: :slight_smile: :slight_smile:

As previously said wiring , impedances , resistor tolerances are all important .

If you use the internal reference which is approx 1.1volts , then the resolution is. 1.1/1024=1.07mV

( If you use a voltage divider on the input to give a higher span then the resolution is less by that factor ( a 5volt full scale input resolves to 5/1024) )

If your input is 1.5mV then it will read 1.07 or 2.14 ideally , but could be another 1.07mV out.

Be careful that you maths is not throwing away resolution , dividing by 1000 , then multiplying by a similar number may well do that .

With any voltmeter made from an Arduino , you need to calibrate it .

Hey, thank you guys so much for all of your answers, I definitely learned from that. I added my wiring and my measurements. I calculated the analytical value of the voltage through the voltage divider (R / R+98.5KOhm). IThe values from the Arduino are Umeasured (I had an fluctuation of +-0.3mV from the value I wrote, which is the average one).

I need to measure a very small force and we plan to use a strain gauge and convert the force to a voltage. Calculation showed that the voltage in the Wheatstone Bridge should be 3mV-50mV.

I used the sketch of Koepel (thanks again) :

void setup()
  Serial.begin( 9600);


void loop()
  float average = averageRead( A0);
  float voltage = average / 1024.0 * 1.08;
  float milliVoltage = voltage * 1000.0 ;
  Serial.println( milliVoltage);
  delay( 300);

const int nSamples = 20;   // Usually from 5 to 10000

float averageRead( int pin)
  unsigned long total = 0;

  for ( int i = 0; i < nSamples; i++)
    total += (unsigned long) analogRead( pin);

  total += nSamples / 2;   // add half a bit

  return ( float( total) / float( nSamples));

I measured 20 times to make the average. I didn't raise it because I wanted to use as less clock signals as possible (the measured force will be fast, around couple of milli seconds. Do you think it's even possible ?). I will try again with 1000 tries just to see and update here. I will also add a capacitor to see how it goes.

Thank you guys for the the tips and advises. I will read it all and learn more, do the new experiments and come back here

While you could build your own circuit why not just use one of these?

Because I never knew about that :open_mouth: it looks very good, thank you so much :slight_smile: btw I am reading now your op amps guide and it's very good, thank you :slight_smile:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.