Secret Arduino Voltmeter – Measure Battery Voltage


A little known feature of Arduinos and many other AVR chips is the ability to measure the internal 1.1 volt reference. This feature can be exploited to improve the accuracy of the Arduino function – analogRead() when using the default analog reference. It can also be used to measure the Vcc supplied to the AVR chip, which provides a means of monitoring battery voltage without using a precious analog pin to do so.

I first learned of this technique from these articles – Making accurate ADC readings on the Arduino, and Secret Voltmeter. In this article, I have incorporated some additional improvements.


There are at least two reasons to measure the voltage supplied to our Arduino (Vcc). One is if our project is battery powered, we may want to monitor that voltage to measure battery levels. Also, when battery powered, Vcc is not going to be 5.0 volts, so if we wish to make analog measurements we need to either use the internal voltage reference of 1.1 volts, or an external voltage reference. Why?

A common assumption when using analogRead() is that the analog reference voltage is 5.0 volts, when in reality it may be quite different. The official Arduino documentation even leads us to this wrong assumption. The fact is the default analog reference is not 5.0 volts, but whatever the current level of Vcc is being supplied to the chip. If our power supply is not perfectly regulated or if we are running on battery power, this voltage can vary quite a bit. Here is example code illustrating the problem:

double Vcc = 5.0; // not necessarily true
int value = analogRead(0);
double volt = (value / 1023.0) * Vcc; // only correct if Vcc = 5.0 volts

In order to measure analog voltage accurately, we need an accurate voltage reference. Most AVR chips provide three possible sources – an internal 1.1 volt source (some have a 2.56 internal voltage source), an external reference source or Vcc. An external voltage reference is the most accurate, but requires extra hardware. The internal reference is stable, but has about a +/- 10% error. Vcc is completely untrustworthy in most cases. The choice of the internal reference is inexpensive and stable, but most of the time, we would like to measure a broader range, so the Vcc reference is the most practical, but potentially the least accurate. In some cases it can be completely unreliable!


Many AVR chips including the ATmega series and many ATtiny series provide a means to measure the internal voltage reference. Why would anyone want to do so? The reason is simple – by measuring the internal reference, we can determine the value of Vcc. Here’s how:

  1. First set the voltage reference to Vcc.
  2. Measure the value of the internal reference.
  3. Calculate the value of Vcc.

Our measured voltage is:

Vcc * (ADC-measurement) / 1023

which as we know is 1.1 volts. Solving for Vcc, we get:

Vcc = 1.1 * 1023 / ADC-measurement


Putting it altogether, here’s the code:

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);

  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both

  long result = (high<<8) | low;

  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts


Checking Vcc or Battery Voltage

You can call this function – readVcc(), if you want to monitor your Vcc. One example would be for checking your battery charge level. You could also use it to determine if you are connected to a power source or running from batteries.

Measuring Vcc for Analog Reference

You can also use it to get a correct value for Vcc to use with analogRead() when using the default (Vcc) voltage reference. Unless you are using a regulated supply, you can’t be sure Vcc is 5.0 volts or not. This function will provide the correct value to use. There is one caveat though…

One of the articles I cited earlier made the claim that this function could be used to improve the accuracy of the analog measurement in cases where Vcc wasn’t exactly 5.0 volts. Unfortunately, this procedure will not provide that result. Why? It is dependent on the accuracy of the internal voltage reference. The spec sheet gives a nominal value of 1.1 volts, but states that it can vary from 1.0 to 1.2 volts. That means that any measurement of Vcc could be off by as much as 10%. Such a measurement could be less accurate than our power supply for the Arduino!

Improving Accuracy

While the large tolerance of the internal 1.1 volt reference greatly limits the accuracy of this measurement, for individual projects we can compensate for greater accuracy. To do so, simply measure your Vcc with a voltmeter and with our readVcc() function. Then, replace the constant 1125300L with a new constant:

scale_constant = internal1.1Ref * 1023 * 1000


internal1.1Ref = 1.1 * Vcc1 (per voltmeter) / Vcc2 (per readVcc() function)

This calibrated value will be good for the AVR chip measured only, and may be subject to temperature variation. Feel free to experiment with your own measurements.


You can do a lot with this little function. You can use a stable voltage reference close to 5.0 volts without having to rely on your Vcc actually being 5.0 volts. You can measure your battery voltage or even see if you are running on battery or A/C power.

Lastly, the code provided will support all the Arduino variants, including the new Leonardo, as well as the ATtinyX4 and ATtinyX5 series chips.

Please share any corrections in the comments below.

Secret Arduino Voltmeter – Measure Battery Voltage by Provide Your Own is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

This entry was posted in Tech and tagged , . Section: . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.