# How can I seriously calibrate ADC voltage readings with Arduino Nano?

I have been using Arduino Nano Analog input to measure voltages, in the range between 22-30 Volts.

I want to measure it down to a tenth of a volt.

Following the instructions found here:

Read Analog Voltage (www.arduino.cc)

Which basically states that all I need is this code:

int sensorValue = analogRead(A0);
float voltage= sensorValue * (5.0 / 1023.0);


5.0/1023 = 0.004888 volts per increment in analog readings. Turning that around, voltage input divided by this value should give me the sensor value. Clearly they are presuming an input of 0-5 Volts.

My voltage divider is 1 Mohm (R1) between the voltage input and A2, then 200K (R2) from A2 to ground

30 Volts measures as 4.2 Volts. 22 Volts measures as 3.1 Volts.

I think the range between 22 volts and 30 Volts doesn't present enough variation to the ADC - every single-digit change in the analogRead really make a big difference.

The result is that it is giving me some crazy readings. It is definitely nonlinear, so I am having real trouble trying to get it to be accurate at both ends of my measurement range. Each slight ADC increment makes too much of a difference in the measured voltage because I am not using the full 0-5V input range.

So I have been taking 10 readings in a row, at 500 ms intervals, then averaging.

And yet it is not working well enough.

(The biggest problem was the reference voltage and resolution were assumed to be 5/1024 which is completely incorrect. Unless, as someone pointed out, I fed the Nanos directly at the 5V pin - which is still on the table - then it would be 5/1023)

I changed my resistor network to be 147K/23.5K

(Parts list: 1x 100K, 3x 47K)

R1 = 47K / 47K = 23.5K

R2 = 100K + 47K = 147K

Also added a 100uF 50V electrolytic across the input from the battery bank I am monitoring.

I am feeding 5V to Vin. And have figured out that the reference is measurable on the 5V pin, after the regulator, as 4.6V.

Knowing that

Resolution/Vref = ADC/Vin

I turned that around to be

Vin = ADC * (Vref/Resolution)

Or, Vin = A2 * (4.6/1023)

Using a laboratory power supply I was able to confirm that with those settings 22 Volts came in at 3 Volts, and 29.98 came in right at 4 Volts.

So

Vbat = A2 * (4.6/1023) / (R2/(R1+R2))

Vbat = A2 * (4.6/1023.0)/(147000/170500)

It is now operating reasonably well. (Yellow means the inverter is on)

--UPDATE-- Changed resistors to 100K/20K for the divider and brought power to the 5V pin, bypassing the regulator for a reference. It measures at 5.02 and is stable.

Results:

Charge controller meter says I am at 24.5 Volts.

Program says 24.64 (in the serial monitor).

--ADDENDUM: I want to pursue the Op-Amp setup. But if 741 won't work will need a suggestion.

Similarly, I went to 147K/47K for the 11-15 Volt circuit, and moved a higher-quality P/S over to the 5V pin, where it measures 5.03V; the results are better there, too. Just very slightly high.

So anyway, I'll put this to rest until I can figure out an op-amp circuit.

Thank you all for your help

• You asked the question twice without a link to the other one, now we are answering to both without knowing each other answers. electronics.stackexchange.com/questions/307586/… – Jot May 27 '17 at 3:01
• If your power supply is 5volt you should connect it to Vcc directly, instead of Vin. If you connect to vin it first goes through the regulator, lowering the voltage. – Gerben May 27 '17 at 6:29
• Hello @Gerben. By Vcc I assume you mean the 5V terminal. I thought about that but since the Nano connects to a Pi I didn't feel safe having it unregulated. The power supply is at least 5V, and no more than 5.1, and is regulated well enough for the Nano, for sure. But I don't think it would be useful as the reference since it might wander some. I am more comfortable trusting the regulator, especially since there have been Pi meltdowns at 5.15. – SDsolar May 27 '17 at 9:12
• The nano can handle up to 6V. Giving a regulator too low a voltage makes it unable to regulate, and the voltage will flunctiate bases once the load. This fluctuation will also affect the ADC. – Gerben May 27 '17 at 9:18

## 4 Answers

In my opinion, this is a very Arduino specific question.

First of all, you can't map 22-30V to 0-4.4V with a voltage divider (unless you have a negative voltage available). A opamp introduces a little more inaccuracy but can map the voltage which increases the accuracy. I suggest to first get the maximum out of the voltage divider. If that is not enough, then you can try a opamp.

The impedance of the circuit at a analog pin should be 10k or less for a accurate ADC conversion. You can add a small capacitor (1nF to 100nF) from the analog pin to GND, to keep the value stable during the ADC conversion.

The 5V VCC as a reference is a pain. The best option is to use an external voltage reference, for example a LM4040. The second best option is the internal voltage reference of 1.1V. That voltage is never 1.1V, and you have to determine the actual voltage. That 1.1V also depends on the temperature and a little on VCC.

Only 10 readings for an average is a small number. Sometimes I use 5, sometimes I use thousands.

Is the Arduino Nano used indoors, with only a small variation in temperature ? Then select the analogReference(INTERNAL). Use for example 10k and 270k for the voltage divider and 10nF parallel with the 10k. Use the average of 100 samples. An accuracy to measure the 24-30V with 0.1V resolution should be easy to achieve and 50mV resolution should be possible.

If you prefer the 1M to measure the voltage, you can also use 1M and 33k. With the 10nF parallel to the 33k, the ADC conversion is probably still accurate enough.

[ADDED] You have not told us yet how accurate you want to read the battery voltage. Is ±500mV okay, or must you have ±20mV ? I say it again: 0.1V resolution is easy. But it all depends on a good reference. A bad reference means even worse results. Dit you take a look at analogReference to see what the INTERNAL reference is about ?

• The internal reference may even have better stability. Similar products using (I assume) the same or similar reference have temperature coefficients of 90 ppm or less. – Alex May 27 '17 at 3:05
• Yes, you are right. The LM4040 that I mentioned is not the best. I prefer a good overall absolute accuracy, so it can be used right out of the box. The bandgap voltage of the reference of the ATmega328p can be 1.0 to 1.2 volts. With a lot of tuning and averaging it is still very useful. – Jot May 27 '17 at 3:15
• You are right that it turned out to be a very specific-to-Arduino question after all. It is inside. Resolution can be within a tenth of a volt, or so. Yes I looked at the reference and like the DEFAULT - 5V pin is a great touch point to verify it. You said you sometimes take thousands - what interval can I use for the ADC to settle down in between readings if I wanted to take 100? – SDsolar May 29 '17 at 3:00
• For normal noise reduction I don't use a delay at all. Just a for-loop with analogRead. Only when trying to eliminate the 50Hz or 60Hz mains noise, a delay might be needed. – Jot May 29 '17 at 15:59
• I have .1uF in both ceramic and electrolytic. Also can build a 1147000/47000 divider with 1% parts to get 1.81 volts from 30. Building it now. – SDsolar Jun 5 '17 at 3:11

Rather than mapping to a range of 0 to 4.4 V, map to the range 0 to 1.1 V and use analogReference(INTERNAL) to select the internal band-gap 1.1 V reference source. This reference voltage might vary a small amount as Vcc varies, but the variation is likely to be just a few millivolts.

To get your input voltage into the range 0 to 1.1 V, use a voltage divider and a voltage subtractor. The voltage divider ratio should map the 8-volt range of 22 to 30 V to a 1.1 V range, ie, should have a ratio of 8:1.1, and should map 30 V to 4.125 V and 22 V to 3.025 V.

If you use a Unity Gain Differential Amplifier as described at electronics-tutorials.ws, the divider can use large resistors, ie, need not provide the under-10 KΩ resistance needed for ADC input; instead, the UGDA's low output impedance will meet that requirement when used as ADC input. For example, you could have 1 MΩ in series with 160 KΩ for the main divider.

Feed that divided voltage into V1 in the diagram below. Use a pot or another voltage divider to create a 3.025 V reference to feed into V2, perhaps based on the board's 3.3 V regulator output.

Note, use R1 = R2 = R3 = R4, with all of them some value like 10 KΩ or 20 KΩ. • Similar products using (I assume) a similar band-gap voltage reference have temperature coefficients of less than 90 ppm so the band-gap reference is probably the most accurate reference at hand. – Alex May 27 '17 at 3:03
• I hadnt heard of that. – SDsolar May 27 '17 at 9:17

Changed resistors to 100K/20K for the divider

you probably want to read the datasheet about the impedance requirement on the source and potential solutions there.

Thank you for your answers and there is a lot of collective knowledge there. It helped me find the way to stabilize and calibrate this system with what I had on hand. The biggest problem was that the reference voltage and resolution were 5/1024, which is completely incorrect.

I changed my resistor network to be 147K/23.5K (Parts list: 1x 100K, 3x 47K)

Also added a 100uF 50V electrolytic across the input from the battery bank I am monitoring.

I am feeding 5V to Vin. And have figured out that the reference it is using is measurable on the 5V pin.

Knowing that Resolution / Vref = ADC reading / Vin, I turned that around to be Vin = ADC * Vref / Resolution.

(Can someone please edit that previous line so the equations look right?

I found out that the proper resolution is 1023, not 1024

Then by measuring the 5V pin I found 4.6 from the 12v-to-5v converter going through the regulator. The 12 Volts is battery-backed and is very stable.

Vin = A2 * (4.6 / 1023)

Using a laboratory power supply I was able to confirm that with those settings 22 Volts came in at 3 Volts, and 29.98 came in right at 4 Volts.

Then I divided the Vin by (R2/R1+R2) to get the Vbat reading

Vbat = A2 * (4.6/1023) / (R2/(R1+R2))

Vbat = A2 * (4.6/1023.0)/(147000/170500)

You can see it better at

https://electronics.stackexchange.com/questions/307586/need-help-calibrating-adc-input-using-a-voltage-divider/307643#307643

It is now operating reasonably well.

(Yellow means the inverter is on)

Next step is to try 100K/20K for the divider and bring in 5V to the 5V pin, bypassing the regulator.

I want to pursue the Op-Amp setup. But if 741 won't work will need a suggestion. This setup I have been asking for measures a 500Ah 24 Volt deep-cycle Marine battery bank being charged by 6x 100W poly PV panels with a single-axis positioning system.

I now also need to measure exactly half the voltage, 11-15, for the positioning circuitry that has its own battery and dedicated 50 Watt panel to run the computer and motors. The regulated 5V out there is el-cheapo but I now have the parts on hand to give it better 5V just like I have in here with the storage bank. Should be able to finish that part today after church. I expect the ADC will see half the voltage. Instead of 3-4 Volts it should see 1.5-2 Volts.

Terrible loss of precision. I really want both systems to see 0-4 Volts at the ADC.

So my question is still the same. I think the answer is here.

• Note that your resistance network has an equivalent output resistance of 20.3 kΩ, which is above the maximum recommended at an ADC input (10 kΩ). Note also that the ADC gain is 1024/Vref according to the manufacturer's datasheet. I measured it recently on an ATmega328P and found it to be more like 1026.1/Vref. – Edgar Bonet May 27 '17 at 10:06
• The 4.6V can not be a good reference voltage. Use the internal 1.1V. – Jot May 27 '17 at 12:16
• OK, @Jot. I am still getting nonlinear readings. Up near 29.2 volts Boost charge it is OK, but it reads a bit high at 27.6 Float, then low near 25 Dark. I am using a meter to calibrate the 5V terminal in the V/1023 part of the equation. The supply voltage is stable, with about 50 Ah of battery behind it - it is not load-dependant. The main battery bank being measured is 500 Ah of battery plus 600W solar (degraded by temps, of course) and an MPPT controller. I see how to set it to INTERNAL but do not know how to code for it. Do I just use 5/1023 if I set it to INTERNAL? – SDsolar Jun 5 '17 at 1:08
• Also, I am asking myself why I am using the 5V pin instead of AREF and EXTERNAL. While I would love to use an op-amp to spread out the range, right now I am stuck with 3-4 Volts to represent 20-30 Volts, using 100K/20K as my divider. – SDsolar Jun 5 '17 at 1:32