Arduino 6 input DVM 0 to +50V range

This is a project I built for work as an add on to our battery tester which is able to test up to six batteries at once. The battery tester is controlled by a PLC and does not show voltages in real time so we had no idea how long the battery runtime was remaining. A voltmeter that showed the battery voltage under test was required as our tester cuts off the battery at 10.5V for 12V and 21V for 24V lead acid batteries. At first I planned to use a standard panel meter and a six way switch to select the battery under test. This however proved to be quite (in comparison to alternatives) an expensive way of doing this.

Arduino 6 input voltmeter

I decided to make use of a few spare parts I had laying round in the workshop and make a digital voltmeter that could monitor six battery voltages at once on a single LCD. This would obviously have to be microcontroller based so I chose the Arduino over the 8051 due to it’s built in ADC and ease of use. As you can see from the photo the project uses a 20×4 character LCD and also monitors the temperature of the heatsink the load resistors are mounted to. A DS18B20 one wire digital thermometer was used for this and the battery voltage monitoring wires were connected to the Arduino’s ADC ports A0-A5 via a voltage divider.

Example voltage divider circuit.
Example voltage divider circuit.

With the voltage divider the circuit can measure positive voltages from zero to 55 volts although I specified 50V to avoid accidentally overvolting the ADC inputs. The divider gives an approximate 11:1 ratio and due to component tolerances must be calibrated. This is explained in the code but you will require a good quality digital voltmeter to calibrate this circuit. Here is a schematic which this project is based on; the divider resistor values are the same. This only shows 4 channels but you can work out by looking at the code and the example schematic what I did to get the extra 2 channels. I used a nano for the project but you could use a bare ATmega328 chip instead to save on components. I preferred to use a nano for ease of uploading firmware updates and calibration values. They are cheap enough to buy anyway.

Below is an extract of code showing calibration procedure which is done by measuring the input voltage and dividing it by the voltage present at the output of the voltage divider. I used 10.02V as the input voltage to each voltage divider. Do not connect the input voltage to be measured directly to the Arduino as this will destroy it. Note the 5V supply is critical – the LDO regulators built into the nano tend to be pretty wank so if it’s above 5V (I’ve had them output 5.2V) this will result in very inaccurate readings. Best use a 7805 from a reputable manufacturer (not chinese clone) and apply this to the 5V pin directly. These typically have an output of 5V +/- 0.01V. A third alternative is use a 5V precision reference IC and connect it to the Vref pin modifying code accordingly to get the external vref voltage rather than the default vcc.


Use 1% tolerance resistors for the voltage divider and calibrate as below:-

VREF= 5V supply voltage (measured with precision DVM to get best accuracy)

The voltage divider factor is calculated by dividing the first voltage by the second voltage or:
dividing factor = input voltage ÷ output voltage
For example, if the first or input voltage measured is 10.02V and the second or output voltage is 0.9V, then the division factor is:
10.02 ÷ 0.9 = 11.133
Voltages should be measured with a good calibrated DVM for best accuracy and should be recalibrated once every year.

#include <LiquidCrystal.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// number of analog samples to take per reading, per channel
#define NUM_SAMPLES 10
// voltage divider calibration values
#define DIV_1 10.9395
#define DIV_2 11.0352
#define DIV_3 11.0382
#define DIV_4 11.2222
#define DIV_5 11.2097
#define DIV_6 11.1602
// ADC reference voltage / calibration value
#define V_REF readVcc()  – I used a readvcc function but you can replace readvcc() with the actual measured VCC voltage if this is better.
#define ONE_WIRE_BUS 10

The code also dumps the data to the serial port for a future modification to the windows based battery test software that works in conjunction with the PLC. At the moment this feature isn’t used but the latest code sends data periodically every 10 seconds for the purpose of data logging if required.

This is intended to be an example of projects I’ve made – it’s fully working but was built to be part of a larger battery testing system. This 6 input voltmeter however can be used standalone for monitoring up to six voltages at once. The code can be downloaded here.

Parts required for this project:-

  • 20×4 LCD
  • Arduino nano (or bare ATmega328 with Arduino bootloader)
  • 7809 voltage regulator (omit if using a regulated 9V wall wart)
  • 7805 voltage regulator (if the nano’s LDO regulator is wank)
  • 0.1uf capacitors x4
  • 10uf capacitors x2
  • 220 ohm resistor
  • 50K pot
  • 100K resistor x6
  • 1K resistor x6
  • 4.7K resistor x1
  • DS18B20 IC
  • Suitable heatsink for the regulator
  • Suitable case to house it in
  • Veroboard or make your own PCB.

Note that if using a bare ‘328 you will need 22pf caps x2, 16Mhz crystal and 5v regulator. LDO type preferred as those switch mode ones introduce too much noise.

Accuracy is to 1 decimal place which was accurate enough for our application. You may be able to modify the code for 2 decimal place accuracy however this wouldn’t leave enough room on the LCD. I am unable to provide any info on the battery tester itself or the windows based software as this is in house software and cannot be publicly released due to intellectual property reasons. You can however use the Arduino code as you see fit. Just remember if you re-distribute it credit me in the code.


4 Replies to “Arduino 6 input DVM 0 to +50V range”

  1. A few helpful observations:

    1. Using the internal reference to measure the VCC is a very roundabout way of doing things, and wholly redundant to boot.
    Think about it – you’re calibrating VCC to the internal reference, and then using VCC as a transfer standard. Accuracy degradation ahoy!
    Why not use the AVR’s internal voltage reference for all the measurements? It’s free to use anyway, and a 7805 hardly does a precision regulator make.
    Powering other loads from the 7805 (the AVR, especially) doesn’t help, either – it causes it to run a bit warmer, which further worsens the accuracy, and the AVR also brings its own power rail noise into the mix.
    The AVR internal reference is actually decently stable with respect to temperature and supply voltage fluctuations; much better than a 7805 in any case.
    The actual reference voltage does vary slightly on a device-to-device basis, but it’s of no consequence if the input sensitivity gets calibrated anyway.

    2. It’s a good idea to put bypass capacitors (say, 100n or so) across R3, R5, R7 & R9.
    It cuts down on incoming noise a whole lot, and prevents possible aliasing issues which can arise in the presence of high frequency noise (eg. from switching power supplies).

    3. A minor aesthetic issue: in the photo, it appears that you’re left aligning the displayed voltages.
    I think this sort of display looks better when the decimal points are made to always stay aligned; it looks neater and is quicker to read.

    4. Adding a bunch of consecutive ADC conversions is certainly one way of reducing the impact of noise, but supersampling can do even better, and isn’t much more complicated to implement than simple averaging. (Atmel provides an application note on supersampling on AVR’s, complete with example code)

    5. Be careful flinging all these floating point operations around: each takes easily over a thousand clock cycles, due to the lack of a hardware FPU on these AVR’s.
    Not that it matters much in this particular case, but generally it can cause a severe processing power bottleneck if trying to do much more than about 100 FLOPS.

    Finally, an unrelated observation: I can infer that where you work, there seems to be a shortage of resources, but a relative abundance of available hands.
    At my workplace, it’s exactly the opposite: we’d use an off-the-shelf panel meter and a 6-way switch – or heck, just use 6 panel meters and no switches at all, costs be damned, simply to minimize the setup time; we’re overloaded enough as it is.

  2. Thanks for your comments, it has room for improvement, sure but for our application it worked OK. If I were to make it again for a purpose where accuracy is a must I’d use either the internal reference or a precision reference voltage IC. I’d also use the filter capacitors as in your suggestion. I’d improve the display layout too. Yeah, it does left align the display readouts

    This thing came about as during the winter months there isn’t as much to do at work and some days we have nothing to do at all. This was partly to give me something to do and learn something at the same time with a useful gadget in the end to boot.

  3. Eh, we hardly *ever* get any downtime. In fact, it’s quite the opposite – huge backlogs usually form in what should normally be the “quiet times”.

    A few more tips for improving accuracy with ADC measurements:
    – it’s best to limit the voltage divider’s equivalent Thevenin source resistance to reasonable values, in order to minimize errors caused by ADC input leakage currents and sample/hold capacitance charging requirements; ~100k is roughly the upper practical limit for maintaining a decent accuracy;
    – as with all analog circuitry, careful ground routing is absolutely essential, as well as ensuring proper separation of the analog and digital grounds: even just a few mV of voltage drop on the ground connection (between the voltage dividers and the MCU’s ground pins) can affect the ADC measurement accuracy by a few LSB;
    – when using the internal reference, it’s a good idea to connect a bypass capacitor to the Vref pin, if there isn’t one connected already: bandgap voltage references tend to be quite noisy, and the capacitor helps a lot with that.
    – supersampling can easily extend the ADC’s effective resolution (typically by 1 to 3 bits), to the point that 4 significant digits can be meaningfully displayed.

    • That’s really helpful, thanks. In this project though I made the ultimate sin; built it on veroboard as it was just a one-off. However it seems to work OK. I’ll definitely bear your comments in mind as I have further possible projects in mind and I may go back to my battery testers to see if they can be improved.

      As for work, we are becoming worried that senior management have noticed (in fact I’m certain they have) and with them being very tight and do not want to spend any money with the workshop at the bottom of their priority job cuts may be inevitable. One of the IT guys who build the networks wasted over £20,000 on a failed project & nothing was said. We wasted £100 on some tools to help with SMD device soldering that didn’t work out and got dragged in for a bollocking and explain ourselves.

Comments are closed.