Arduino ADC as a Voltmeter

Arduino ADC as a Voltmeter

It seems like a single board computer would not make a suitable voltmeter, but almost all voltmeters have at their heart a resistive divider, an analog to digital converter, and a computer to read and display the results. This one is no different. What is different is the resolution of the ADC. Where many dedicated voltmeters have ADCs with 14 to 24-bit resolution, the Arduino only has 10 bits. We can only measure 1024 discreet voltages. We could add an external ADC that has higher resolution, like the AD976 or LT1605 16-bit converters, but that would defeat the purpose - to learn how to use the Arduino ADC for something useful.

All voltmeters work by measuring the voltage on a resistive ladder, designed to limit the voltage seen by the meter to the range of the ADC. The range of the Arduino ADC defaults to 5V. It can be changed, and we will do that later. For now, we will use 5V. The ATmega328 datasheet says the ATmega328's ADC doesn't work best when the source resistance is greater than about 10k Ohms. Since we have no control over the resistance of the voltage source we are measuring, we will keep the resistor divider values down to 1k Ohm. Later we will see how to get around that problem.

Arduino Uno 10-volt voltmeter project schematic diagram.

This circuit is good for up to 10V. The voltage on the port will be divided by two, which puts it within the 5V range of the ADC. So what happens if you connect 100V to the input? What happens is the ATmega328 in the Arduino fries. There are maximum voltage ratings on the chip of around 7V before things go up in smoke. The fix for this is to keep the applied voltage below that maximum. One way to do that is with a zener diode.

Arduino Uno 10-volt protected voltmeter project schematic diagram.

That zener diode will protect the Arduino from two things - over voltage and reverse voltage, by limiting the voltage to either +5.1V or -0.6V.

The problem with having the low value resistors in the ADC input is that they may actually change the voltage being read by loading the circuit. There is a way to get around that, by adding an op-amp as a buffer.

Arduino Uno 10-volt protected, buffered voltmeter project schematic diagram.

You can continue by adding range switches, changing the values to satisfy the maximum input requirement of 5V full range. The accuracy of the meter depends on the precision of the resistors used.

Voltmeter Software

The software to read the voltage divider and calculate the applied voltage is very simple. There is a formula which always applies to the ADC on Arduino:

  • ADU = (Vin * 1024)/Vref
  • -or-
  • Vin = ADU * (Vref/1024)

So to find the voltage at the Arduino, we need to read the analog input, multiply the value read by (5V/1024) or 0.0048828125V. The voltage divider cuts the voltage in half, so to find the applied voltage, multiply by two. A reading of 100 ADU is 0.488V at the port pin, or 0.976V on the voltage divider. Changing the voltage divider only changes that multiplier - the first calculation remains the same.

#define voltageInput A0
#define multiplier 2

void setup() {
  Serial.begin(9600);
}

void loop() {
  int ADU = analogRead(voltageInput);
  double voltage = ADU * 0.0048828125 * multiplier;
  Serial.println(voltage);
  delay(1000);
}
	
Arduino Uno 10-volt protected, buffered voltmeter project schematic diagram.

Changing the ATmega328 ADC Reference Voltage

We changed the resistor divider to 1M&560k, giving a divisor of 1.78 rather than 2, so the multiplier in the code needs to be adjusted to be 1.78.

Now for the reference voltage. There is a precaution you need to take with this, because the external reference and the internal reference are shorted together when a reading is taken. Before the first reading of any analog input, you must set the reference to external. Once that is done, it is safe to read any analog pin. It prevents the internal references from being connected to the pin.

Since we changed the reference voltage we have to change the formula. It is still Vin = ADU * (Vref/1024), but Vref has changed from 5V to 2.5V. 2.5/1024 = 0.0025V per ADU. However that resistor divider network has to be considered. You can do the math, or you can do what I did:

  1. Connect this meter to it's own reference voltage.
  2. Connect a known good meter to the reference voltage.
  3. Adjust the reference for 2.5 or as close as possible.
  4. Read the number of ADUs as seen on the LCD screen.
  5. Divide the exact reference voltage by the ADU number.
  6. Enter the result as the multiplier in the following Arduino program.
Arduino Uno buffered voltmeter project with LCD display of voltage.

The maximum voltage you can read with this configuration is 1023 * 0.0066100795756 = 6.76V. You can adjust the input divider to be anything you want, and the multiplier may be determined as shown above. The tolerance of the parts does not matter at all, if you calibrate it.

#include <LiquidCrystal.h>

int voltmeter_Pin = A3;
double multiplier = 0.0066100795756;

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
void setup() {

  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Voltage:");
  analogReference(EXTERNAL);

}

void loop() {

  double voltage;

  int ADU = analogRead(voltmeter_Pin);
  voltage = ADU * multiplier;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Voltage:");
  lcd.setCursor(0, 1);
  lcd.print(voltage,3);
  lcd.print(" V ");
  lcd.print(ADU);

  delay(1000);
}	

Autoranging Voltmeter

To make an autoranging voltmeter you need to have ranges that are in decades - 0.199V, 1.999V, 19.99V, 199.9V, etc. At least convention dictates decades should be used. To do that you need a set of dividers, a way to calibrate each, a way to switch them in and out, and a way to know when to do so.

The knowing when part is pretty easy. If the value reaches 1023 ADU, go to the next higher range. When the value gets below 100 ADU, switch to the next lower range. There should be some kind of timer to keep it from flipping back and forth.

To calibrate, you need to have a few things. For simplicity, the reference voltage should be 1.999V. That helps the rest of the process make sense. The dividers should be individually tweaked, using trimmer pots, to yield divisors of approximately 1, 10, and 100. I say approximately because the final adjustment will be made by having a multiplier for each range.

The dividers are switched with a MAX333 precision analog switch.

Arduino Board Logo

 

Arduino-Board is the go-to source for information on many available Arduino and Arduino-like boards, tutorials and projects.

Help and Support

Arduino-Board

Stay updated

Sign up if you would like to receive our once monthly newsletter.