# Need help calibrating ADC input using a voltage divider

I have been using Arduino Nano analog input to measure voltages, in the range between 22-30 Volts. The Nano's ADC expects a 0-5 volt range and has 0-1023 as its output.

Following the instructions found here:

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.

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.

One thing that seems to really matter is the power supply. When I put 4.97 into the Vin it gets different readings than if I rely on getting power from the USB connection to the Raspberry Pi 3B.

Just yesterday I put it on the power supply for good - had been relying on the Pi all along. But it still is not giving accurate readings.

--EDIT from Answers (Thank you for showing me where to look)

Input impedance: I'm trying 147/23.5 for the next attempt. Getting 3.0-4.1 for the 22-30 range. Same as the 1M/100K, basically, but lower resistances.

The reference voltage issue: It measures 4.36 Volts on the 5V output from the Nano. Vin is 5V exactly.

Here is an article I found that says the 5/1023 formula should use that actual value:

Measuring DC Voltage using Arduino

And another that confirms it:

Analog to Digital Conversion

SO NOW the immediate problem is how can I map 22-30 volts to 0-4 or so, thus allowing the utilization of more than just a few % of the allowable values?

• I would use a voltage divider with lower value resistors. The analog input of the Arduino draws some current while measuring the voltage. Your divider can't really deliver enough current. Try using 100k and 20k or 10k and 2k.
– JRE
May 26 '17 at 22:10
• Not sure what went wrong - maybe my wiring, so I am now using 147/23.5 which gives me a range from 3 to 4 Volts. Now if I could only map 22-30 to 1-4 I could call it a day. May 27 '17 at 0:25
• You asked the question twice without a link to the other one, now we are answering to both without knowing each other answers. arduino.stackexchange.com/questions/38845/…
– Jot
May 27 '17 at 3:02

The best way is offset and less attenuation.

## Spec

• yes convert 20~30 to 0 to 5V with 0.1% accuracy if poss.

## Rev A

• 22~30 converted to 0~4 becomes Vin * 0.5000 -11.00V = Vout
• 11.000V = k* 4.380V using k= 2.5114
• with a non-inv OpAmp of gain 1+Rf/Rin
• one way: V+/2 - 11.00 V using precision Vref or programmable zener.
• using R array with matched R to <0.1%
• This will be 3x better resolution using 1/2 instead of 1/6 attenuation
• ensure your connections are twisted pair and filtered with low ESR RF cap

What supplies are avail?

p.s. I grew up in awe of Aurora , chasing smoke trails that appeared to be at ground level, watching sky waves like the ocean for hours at night and the fiery colours in Churchill Mb at the NRC rocket research range for Plasma Physics.

• Yes. That would be perfect. But I'm stuck on your first point, @Tony. That is the Holy Grail. Disregarding accuracy, how do I create that conversion? I really would like to get the 22-30 to map to 0-4, ideally. My Vref is going to be 4.38 with the new power supply, measured at the 5V pin. It measures right at 5V at Vcc. My connections are very short for this. The ADC is right at the source, connected to a Raspberry Pi that reports back to the mothership by WiFi. May 26 '17 at 23:58
• 8'volt delta to 4 is the same attenuation spec as I gave, which part can you not do? the 11.00Vref or the attenuation of 0.5000.. Matched R arrays of even 0.01% are cheap. Vin/2-11.000V = 0 to 4V May 27 '17 at 0:09
• OK, I see what you mean. I'll check my parts box. I know I have a 741 or ten, but not sure where they are. Resistors are all right here, though. May 27 '17 at 0:29
• don't use a uA741 unless you don't care about saturation error May 27 '17 at 0:34
• OK. Well, I don't stock many analog devices, so I'll have to go with what I have for now. And no, I can't do the 11Vref because the USB plugs into a Raspberry Pi and they are very particular about voltage. The Nano on its own would be fine, just not the way I use them. They take readings and the Pi is set up as a Nano programmer so I use the same cable to also collect the readings. The Pi also does scp and all that, via WiFi. At least learned precisely what my Vref is. I thought it was Vcc but it is actually measured at the 5V pin. That should help with the linearity, if not the noise. May 27 '17 at 1:16

Aside from reducing the voltage divider resistances, which is necessary, but not sufficient, you need to consider the reference if you want accurate readings. For the divider, 10K is a good maximum, so if you use a 49.9K 1% resistor and a 10.0K 1% resistor you'll be pretty close o the desired ratio and the impedance from the ADC's input will look like 8.3K. A 10n-100n ceramic cap across the 10K won't hurt.

The Arduino does not have an ADC voltage reference so the reading is going to be ratiometric to the supply voltage. A 5% difference in supply voltage means a 5% difference in reading. There is a bandgap reference on the ATmega chip, however it is loosely specified (+/-10%), is relatively low voltage (1.1V nominal) and can only be used as an input to the ADC. If you can measure an accurate voltage even with the ratiometric reading you can correct for supply voltage with calculations if the supply voltage stays constant enough.

Or you could attach a precision external ADC and reference to the Arduino. 

If you want to do an offset zero you can use a circuit like this one: simulate this circuit – Schematic created using CircuitLab

Use a RRIO op-amp such as Microchip MCP6001.

If you don't like my choice of input/output voltages, you can recalculate R1-R5 to get what you want, it's just simple algebra that anyone can do with a bit of patience.

R1/R5 are two resistors rather than one to get the 9:1 ratio with standard E96 series resistor values. R6 is not critical- it just ensures enough current gets to D1 to maintain regulation. The others should be precision types.

Since a fairly accurate and very stable voltage is subtracted from the input voltage you may find using the supply voltage to be acceptable as a reference, depending on what your actual requirements are. The reduction in sensitivity is 3:1 in this case.

• 1-4? or 0~4?? which? stick with specs first not design. May 27 '17 at 0:32
• OK, am now using a known reference, (5V on Vcc gives me 4.6 on the 5V pin which is the reference voltage for the ADCs. Will use 4.6 for the equation. Plus went to 147/23.5 for the divider. So far, so good. TNX. Am am still getting 3-4 Volts when I really want 0-4 Volts. The original voltage range is 8 volts, so a 4-volt range for the ADC. May 27 '17 at 9:20
• I looked up the 6001. If I could find a DIP package I would buy it right now. Getting too old for SMT even with my magnifier light. I remember a lot about op amps from school. I'm sure I can figure out the values once I have a chip in hand and a spec sheet on the screen. May 28 '17 at 13:41
• Both Arduino Nano and Uno do have a built-in reference. It's not terribly accurate, but it does exist. Nov 15 '20 at 13:49

As JRE says in the comments, Atmel (now Microchip :-( ), who makes the processors in Arduinos, generally suggest to not use any circuits with an output impedance higher than 100kOhm on most of its processors.

In fact with an impedance of 100kOhm they often suggest adding a capacitor as well, as follows: simulate this circuit – Schematic created using CircuitLab

Because the processor has an input impedance, which will pull the read voltage down (or possibly up in some cases?), which is likely more than 10Mohm. But it also has a small capacitance inside which it uses to "hold" the voltage while it does a conversion.

When it connects that capacitance a small amount of extra current will flow, which is likely the culprit for your loss of voltage. If you use 10k and 2k that current isn't large enough to create more error than is already in the ADC, but for larger resistances you will start noticing it. With 1MOhm on top I'm not sure if you can fully fix it with a capacitor (the internal leakage probably already becomes noticeable), so maybe the golden middle is 100k and a small capacitor.

The extra capacitor in the case of 100k impedance buffers the voltage all the time that the ADC is not connected to your network, and then when the ADC shortly samples your voltage with its hold capacitor, the capacitor you added will provide a lot of the energy to charge that internal hold capacitor, so the error will be much lower.

• Thank you for the primer about resistance values. My next attempt will be just as you show: 100K and 20K. I have thought about capacitors, too. I was thinking more like 10uF - I see you show 22nF, though. These are voltage readings that change on the order of a couple of minutes. May 26 '17 at 23:35

Thank you for your answers - there is a lot of collective knowledge there. It helped me find a way to stabilize and calibrate this system with what I had on hand. The Arduino people helped me with some concepts about the particular device but it is here that I am seeing the answers I really need.

(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 = \frac{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

$$\frac{Resolution}{Vref} = \frac{ADC}{Vin}$$

I turned that around to be

$$Vin = \frac{(ADC * Vref)}{Resolution}$$

Or, $$Vin = A2 * \frac{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 * \frac{(4.6/1023.0)}{(147000/170500)}$$

It is now operating reasonably well.

--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.

It is tracking pretty well with the reading on the Charge Controller's screen. (Yellow means the inverter is on)

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.

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

Thank you all for your help

• For what it's worth, Texas Instruments (and other electronic component OEMs) have free web apps on their websites to help you design op amp circuits, power supply circuits, etc. Go to ti.com and look for their WEBENCH Designer Center tools. IIRC, you'll need to sign up for a free user account if you want to save or send yourself a finished design. Jun 16 '17 at 0:52

Firstly, don't power your arduino through 5 volts into the VIN pin. Connect 5V to the 5V pin (If you are sure it is 5V) or power it though a higher voltage though the VIN pin. Second, I would recommend changing the divider to work with the 1.1 volt band-gap voltage reference (aim for 0-1V). It is probably the most accurate reference (on similar products it has a 90 ppm temperature coefficient) at hand and you can calibrate out the 10% uncertainty with a multi-meter. Third, mapping the 0 to 1024 value to 0V to 30V can be done using the builtin map function: map(value, 0, 1024, 0, 30000)/1000.0`

• OK, did it. It is measuring 5.02 - divider is now 100k/20k May 28 '17 at 12:51
• Excellent point. In fact, I think I have figured out that a big part of the instability I have been seeing is that I have been powering the board with 5V in the first place. I switched to 12-to-9V converters and now they are much more stable. I being it into the Vin. In any case, now the calibrations stick instead of wandering as the 5V input wandered. These 9V converters can take a wide range of inputs and always put out 9V, with inputs from about 11 to 20. So that was a great tip. I now use the 5V pin for things like LCD displays and the RTC module. Jul 20 '17 at 14:08