Secret Arduino Voltmeter – Measure Battery Voltage

voltmeter

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.

Motivation

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

How-To

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);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif  
 
  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
}
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);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif  

  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
}

Usage

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

where

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.

Conclusion

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.

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

98 Comments

  1. Posted August 5, 2012 at 1:34 pm | Permalink

    hey Scott, this is incredibly helpful. I’ve been struggling with measuring voltage with the Arduino for a while, and I note that different boards and different batteries lead to really different results on all my analog readings. I have a few questions:

    – is one of the lines of code essentially doing a analogReference() call? If so, is there any risk of doing that call out of sequence with the reads, or another call to that function?

    – As you can tell from the previous question, I don’t follow the code you’ve posted, though I figure the variables IN CAPS are internal Arduino vars. Is there a list of those vars you point me to?

    – Can you give me a bit more detail on how this would be used to correct analog references?

    thanks! — wylbur.

    • Posted August 13, 2012 at 11:59 pm | Permalink

      Yes – the first line of actual code (3 variants) does two things. 1) It sets the analog ref to Vcc. 2) It sets the measurement to not a pin, but the internal 1.1v reference. The rest of the code simply makes the analog measurement (ADC conversion). The first line must come before the rest. It can of course be changed for subsequent measurements.

      As for the constants, those are standard AVR constants (not Arduino, but more low level). You can peruse the documentation for the AVR LibC here, or review the Atmel spec sheets.

      The whole trick of this code (readVcc) is to figure out the supply voltage (Vcc) by reading the internal 1.1 volt reference using Vcc as the reference. With simple math, the real Vcc can then be calculated.

      • hary
        Posted March 9, 2014 at 5:14 pm | Permalink

        Hi.

        1. So we need to “calibrate” VCC before doing an anlogRead.
        But it has to be done each time, and just before we need the analogRead !
        Otherwise, our calculation could be false : In the case the power supply is unstable, or we feed some load on some pin (digitalWrite) with a weak power supply just before the analogRead ! And our VCC reference could change

        2. What is the constant 1125300L you’re using ? Is it the constant for your personel board ? I guess yes. And what does the L Means ?

        Many thanks for your explanation. Really intersting and well done.

  2. tytower
    Posted August 15, 2012 at 11:15 pm | Permalink

    Those Arduino boards I have measured give about 4.85 V or thereabouts .Put a multimeter on the regulator output or stick it across the 5V out pinand ground pin and use the voltage read in your adc conversion formula directly -accuracy will be improved. Look at the chips datasheet and most of them have a tollerance of +/-1 degree when measuring temperature so atm your temp measurements can be up to 2 degrees out!

  3. Posted August 16, 2012 at 12:17 am | Permalink

    Hihi,
    Would it be possible to read the internal temperature sensor of the Arduino Leonardo ( 32U4)
    in a similar way ?

    • Posted August 17, 2012 at 12:32 am | Permalink

      Yes. I haven’t written an article here yet that shows how, but I recently posted an Instructable with the code to do exactly that, including support for the 32U4 chip.

    • retrolefty
      Posted August 18, 2012 at 11:05 am | Permalink

      No, reading the chip internal temperature will not be a help in ‘calibrating’ the A/D reference voltage. The chip’s internal temperature is more a reflection of how fast it is being clocked and how much current is being sunk or sourced via it’s output pins, as well as external ambient temperature.

  4. goebish
    Posted August 16, 2012 at 4:22 am | Permalink

    Nice trick, too bad the ATtinyx5 series can’t handle this, I love these small chips :)

    • Posted August 17, 2012 at 12:45 am | Permalink

      You can – thanks to Doug (below) for pointing that out. I have amended the code in the article to support the ATtinyx5 series chips as well.

  5. loopingz
    Posted August 16, 2012 at 5:17 am | Permalink

    One question. On a arduino board, when plugged on USB, arduino uses a 5V Vcc, but what is the regulation when I feed it with 9V?

    • Posted August 17, 2012 at 12:34 am | Permalink

      Vcc ought to still be 5v

      • Harvey
        Posted February 20, 2013 at 3:03 pm | Permalink

        Tolerance depends on the Voltage regulator chip used. Typically 4.9 to 5.1V

    • retrolefty
      Posted August 18, 2012 at 11:10 am | Permalink

      When powered via USB then Vcc is whatever the PC’s USB voltage is and can vary from I think 4.5 to 5.5vdc and still be in specification? If powered via external power then the board’s Vcc will be whatever the on-board 5 volt regulator voltage is with a similar specification tolerance. So there is no true 5.00000 board voltage, rather it’s whatever actual value within normal tolerance specs of whatever voltage source is being used.

  6. Doug
    Posted August 16, 2012 at 8:03 am | Permalink

    Maybe I’m missing something, but I don’t see why you can’t use this on an ATTiny25/45/85.

    Can’t you just set ADMUX to

    #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2) ;

    and readthe bandgap reference (i.e. Vref) ?

    If I’m mistaken, please educate me as to my error. I’m always interested in learning more.

    • Posted August 17, 2012 at 12:35 am | Permalink

      You are absolutely right. I overlooked that entry in the Atmel spec sheet for that series AVR chip. I will amend the code in the article.

      • Doug
        Posted August 20, 2012 at 7:12 am | Permalink

        You should edit your conclusion paragraph as well, just to avoid confusion. :-)

  7. Posted August 16, 2012 at 1:00 pm | Permalink

    I used the 1.1V internal reference on an Arduino a few months ago. But I found out (the hard way) that the tolerance on the 1.1V reference is really poor. It’s specified to be between 1.0V and 1.2V, or roughly +/- 10%. In my case, this made the reference useless. I added an external TL431 2.5V reference, with a tolerance closer to 1%.

    • Posted August 17, 2012 at 12:39 am | Permalink

      As I mentioned in the article, the accuracy of the 1.1v reference is poor. It still has value however – 1) in battery operated projects, it is better than using Vcc (it is at least constant) and you can use it to monitor battery voltage, and 2) you can calibrate for the error. You can even automatically calibrate for each chip automatically as part of the programming process. If you really want an accurate voltage reference however, a separate chip is the way to go – just like you said.

      • hary
        Posted March 9, 2014 at 5:20 pm | Permalink

        You wrote:
        “You can even automatically calibrate for each chip automatically as part of the programming process”

        Could you give more details. I’ve no idea you can do this !
        Wouldn’t you need to know at least the real and precise value of the 1.1V reference do achive such a task ?

  8. Scott216
    Posted August 18, 2012 at 9:51 am | Permalink

    This is very cool. After I call your function, should I set the reference back to default with
    analogReference(DEFAULT);

    Just so I’m clear, this does not use any of the analog I/O pins, right?

    As noted already, the 1.1 voltage reference is not very accurate. Is there a way to measure that with a voltmeter so I can see what it is for a specific Arduino device?

    • retrolefty
      Posted August 18, 2012 at 11:18 am | Permalink

      The internal 1.1 volt reference voltage does have a pretty wide specification, however it is pretty stable for any specific chip within the tolerance range, so the trick is to somehow calculate or measure the actual 1.1 vdc reference for your chip. If you select the internal 1.1 reference voltage in a sketch, you can actually measure it on the Aref pin of the chip. Also I have just tweeked the reference voltage in my calculation until the results of a analog read using a known external test voltage value agreed with the calculated/corrected value.

      • Posted August 21, 2012 at 5:55 pm | Permalink

        You can measure actual 1,1V reference voltage from Aref pin, after you have muxed internal 1,1V there. You can connect a small capacitor there, to make it more stable.

        • fab
          Posted October 20, 2012 at 7:59 am | Permalink

          Hi,
          Could you please describe how to “mux the 1.1 ref to pin AREF” ?
          And do i need to switch it back after that ?

          thks

    • Posted August 28, 2012 at 2:22 am | Permalink

      1) No need to change analog reference. This function sets it to the default = Vcc.
      2) It uses none of the I/O pins.
      3) To measure the internal ref voltage, you can use the excellent suggestions already made, or else calculate it from the value of Vcc given by the ReadVcc() function based on actual Vcc measured with a volt meter.

      • Walt
        Posted September 9, 2012 at 1:21 am | Permalink

        Very interesting function. I tried it out and found a very interesting result with my setup. I fed the sketch to my Arduino uno rev3 which I use to provide 5 Vcc to a couple of prototype boards. I’d forgotten I had a ATmega328 plugged into the proto board running a sketch which lights all the pins 0 -13 as well as A0 thru A5. After lighting them one by one the sketch lights all 19 at once. When I was running the Vcc function on the factory built arduino the 5Vcc line would vary by as much as 110 millivolts when all leds were on with the bare minimum ATmega328. I thought that was a fairly large drop but I have no idea what resistance is between the USB port and the arduino.
        I did measure the DC current to the proto board with all leds lit and it was 85ma.
        Just goes to show that even though the Arduino is supposed to supply 500ma when connected to USB, that may not be without consequences even at much lower loads. When I plugged a 12 volt power pack into the line in jack the voltage was rock solid.

  9. Posted October 16, 2012 at 2:29 am | Permalink

    Occasionally, but not always, the statement “long result = (high<<8) | low;" returns a zero. Why would this be and what can I do about it? This is a very cool routine but this behaviour is driving me a little mad!

    Thanks, Will

    • Posted October 18, 2012 at 3:49 am | Permalink

      Clarification on the above. This never happens on the first read following a restart of the controller….so I just make sure I restart the controller before I get my reading! In any case I have taken what I think I learned from this post and applied it to my project. I then documented what I did here. It should be noted that I am a software guy and this hardware stuff confuses me! I get the answer that I want (e.g. a voltage reading that matches my meter) but I am not sure I have done so in the ‘right’ manner.

    • Posted October 24, 2012 at 1:22 pm | Permalink

      I am not sure why that is happening. I took the analog read code straight from the Arduino library. Are both high & low zero as well (should be obvious, but you never know)? Try adding a short delay before reading the register values for high & low.

      Seeing that it works the first time and then not afterward, check to make sure the ADLAR bit is not being set somehow in the ADMUX register – that could cause the data to be left shifted and possible read as zero.

      A last thing to try is to check to make sure the ADIF bit is set in register ADCSRA before reading the data registers. You may want to study up the Analog to Digital Conversion in the datasheet if all this fails.

      If you find the problem, please post the result. Good luck!

      • Posted October 24, 2012 at 3:12 pm | Permalink

        Scott:

        Thanks. I have not been able to reproduce this problem on a regular basis but will try to do so. The first thing I will try is the one thing that I understand how to do…put in a delay!

        Cheers,
        Will

  10. Martin
    Posted October 23, 2012 at 11:55 am | Permalink

    Thanks for posting the code with explanation! It was exactly what I was looking for. I would like to be able to monitor how much life the battery has left. I’m a total newbie when it comes to electronics and the Arduino so please excuse if my question is a little naive: After uploading the sketch to my Uno Rev 3 which was connected to my computer via USB and running from a 9V battery (I connected the battery via VIN and GND) I checked the Serial output and it reads a consistent 4855. I was assuming that it would read somewhere between 8000 and 9000 since I was connected via a new 9V battery. Since the Arduino uses 5V will it always read around 4855 until the battery is drained and dips below that value?
    Thanks in advance for your help.

    • Posted October 24, 2012 at 1:28 pm | Permalink

      That is pretty close. What the function does is measure the voltage supplied to the ATmega chip itself. The Uno has a 5 volt regulator which provides the 4.855 volts your are reading (the actual voltage is probably closer to 5 volts, but that is a limitation on the internal ref’s accuracy). The regulator probably has around 1.2 to 2 volts of dropout, so the voltage you read will stay the same until your power source drops to less than 6 to 7 volts.

      If you want to monitor your battery voltage before the regulator, you will have to use an analog pin to do so. For monitoring battery voltage, this function will only work when powering the IC directly and not using a voltage regulator. Make sense?

      • Martin
        Posted October 25, 2012 at 1:03 pm | Permalink

        Hi Scott,

        Thanks so much! Makes absolute sense.

        Best regards,
        Martin.

    • Posted October 24, 2012 at 3:11 pm | Permalink

      Martin:

      I think you also need to have a voltage divider to take the 9v to under 5v for the arduino to be able to read it. In my post right above yours there is a link to what I have done that may help. I am a complete hardware novice as well……..

      Cheers, Will

      • Martin
        Posted October 25, 2012 at 1:03 pm | Permalink

        Thanks for the info. I’ll check it out the link!

  11. Ace
    Posted November 27, 2012 at 6:41 pm | Permalink

    Hi excellent article thanks. I have a question if I may. I’m intending to run an atmega328p from a single 3.6v lion cell and I’m confused as to how I can take accurate ADC readings on my A0-A5 pins with VCC slowly dropping down. I figured I would just use readVcc then keep the battery between 4200(4.2v) and 3000(3v) but what is the reference for ADC readings the current vcc? If so how can I maintain accuracy with a constantly changing ratio? I’m reading 0-24v through a resistive divide using a ratio of 70Mohm 10Mohm giving me 0-3v out. But at 4.2v 3v is 730 ish and at 3v 3v is 1023. So how can I adjust the value to track vcc?

    Kind Thanks

    • Posted December 6, 2012 at 11:04 am | Permalink

      You have two options: either use a voltage reference (internal or external) or use the standard Vcc reference (as you are doing) and scale it using the readVcc function. Here’s how to do the latter.

      long millivolts = readVcc();
      long measured = analogRead(A0);
      long voltage = millivolts * measured / 1023; // answer is in millivolts

      You don’t have to call readVcc everytime – just often enough to track the battery voltage.

  12. atflaryon
    Posted December 26, 2012 at 1:00 pm | Permalink

    is this code useful for atmega168? thanks

  13. evolion
    Posted January 21, 2013 at 4:22 am | Permalink

    I don’t quite understand all the info presented here but perhaps I could have some feedback. Part of the project I have just started on is to measure sensor input to my vehicle’s computer(PCM). Power to the sensor is +5Vn. If I were to use the same +5Vn to power the arduino, would that automatically act as a reference for a fairly accurate reading of the sensor output? or should it also be input into the AREF to obtain an accuracy of +/- .01V?(is this accuracy even possible?) I would assume that as long as I read the same value that would appear in the PCM, it wouldn’t matter that power to the sensor is not exactly 5V.
    Thanx

    • Posted January 24, 2013 at 2:47 pm | Permalink

      That is a tough question to answer without knowing more details. Is the 5V source accurate? (I doubt it is +/- 0.01 volts). Is the PCM relying on the +5v source?

      As indicated by this article, the default analogRead uses the AVR’s Vcc for a voltage reference. The technique given to measure Vcc is somewhat crude (about 10% accuracy). If you need an absolute accurate voltage reading (such as the 0.01 volts cited), then you will need to use a precision voltage reference chip and feed it to AREF. Even then, that kind of accuracy might be iffy at best. If you meant +/- 0.1 volts, then the precision ref will work.

      • evolion
        Posted January 25, 2013 at 3:54 am | Permalink

        The 5V source would be from the PCM(powertrain control module, aka ECU) and it probably isn’t an accurate value, I’ve measured it before but don’t remember what it was ~4.8 to 4.9 maybe.

        The PCM supplies 5V to most all of the sensors which are predominately variable resistive loads that return a voltage less 5V. I’m not sure what resolution the PCM reads.

        I want to begin only measuring the post cat O2 sensor which returns oscillating signals from around .3V to .7V up to 3 times a second where each high and low value may possibly vary +/-.2V. Eventually, I would like to interrupt the signal from the O2 to PCM with the arduino, in real time, feeding the PCM a predetermined, or augmented through calculation, signal for every O2 pulse. Essentially fooling the PCM that the engine is running normally as I make changes to the system that would otherwise be automatically defeated by the PCM’s readjustments.

        Do you think that if I use the sensor input voltage for AREF, I will still have only 10% accuracy?
        Would you have suggestions for a particular reference chip to use if I need to use one? I didn’t figure it would be a difficult measurement seeing as how my $5 voltmeter reads to .01V, but I’m a novice and the arduino experience is very new to me.

        I appreciate the input.

        • Posted January 28, 2013 at 4:23 pm | Permalink

          Since your sensor input voltage appears to be only 5% accurate, the same will be true of any measurement made with it for a reference. On a related note, who knows what effect the sensor input voltage has on the sensor accuracy? I wonder if you really need absolute accuracy or relative accuracy. If the sensor reading vary with their input voltage, absolute accuracy may not do you any good.

          Therefore, I suggest just using the sensor input voltage for AREF and do some testing. I would also monitor this voltage itself as well. Does it change with temperature for example? If you find you need a precision voltage reference, the SC431 (available from Digikey) will serve you well. It works just like a 2.5 volt Zener diode, except it is 1% accurate. Just hook a 1 to 10K resistor to Vcc and then your volt ref and input that to your AREF pin.

          • Harvey
            Posted February 20, 2013 at 3:16 pm | Permalink

            If the Arduino supply Voltage is made to track the PCM 5V all measurements will be ratiometric and the errors drop out.

  14. Jorge
    Posted February 28, 2013 at 9:09 pm | Permalink

    Hi,

    This is all very helpful, but what if I want to measure the voltage across another power line? I am working on a project in which I need to continuously measure the voltage of the line powering the project. I am using regulators to regulate, but I need a way to read and store that reading for data analysis after the project. What is the best way I can do that? I’ve been looking around but I can’t find anything.

    Thanks,

    • Posted March 6, 2013 at 11:54 pm | Permalink

      You’ll just need to use an analog pin to make the measurement. Use two resistors to make a voltage divider that drops your maximum line voltage just under 5 volts (or 1.1 volts if using the internal reference) and measure your voltage at that point.

  15. Marc
    Posted March 12, 2013 at 4:14 pm | Permalink

    I would like to change the reference on the Arduino DUO board. The analogReference(INTERNAL1V1); code returns an error.
    Do you think that reference could be changed with lower level code?

    • Posted April 24, 2013 at 12:39 pm | Permalink

      I haven’t played with the DUO yet. Look in the spec sheet for the appropriate analog reference bit to use.

    • Fx
      Posted July 22, 2013 at 9:32 am | Permalink

      Hi Marc, did you manage to change de reference on an Arduino due board?

      I’m having the same issue and don’t know how to get a reference voltage.

      Thanks,

  16. britesc
    Posted April 6, 2013 at 12:21 pm | Permalink

    Hi,
    Thank you for this article.
    I have a large number of 2VDC 1200Ah Lead Acid Batteries that I use for off-grid power.
    I need to be able to monitor the voltage, amperage and temperature of each battery. The “device” should be self powering, so I have settled on the ATTINY43U as this operates from 0.8 to 5.5 VDC and is well within the operable battery voltage range of 1.57 to 2.85 etc.
    Your routine should I believe, give me the first part of the problem, namely the battery voltage.
    Is that correct?
    Can the ATTINY43U also provide the amperage?
    I also read that it can return the temperature of the chip for reference value.
    I intend to use a copper lug with an LM35 inside sealed with hot glue and feed that output back to the ATTINY43U but that requires 5VDC? So somehow I need to increase the voltage from the battery to deliver that??

    Could you or any of your stalwarts advise me as to whether this is feasible and help me with a little bit of code / hardware advice to get me going, please?
    Thanks and kind regards,
    jB

    • Posted April 24, 2013 at 12:38 pm | Permalink

      Yes, you can measure the voltage using this technique. You can also measure the ambient temperature with a similar technique. The latter is not real precise, but is probably sufficient with your application (you will have to calibrate the chip however). Alternatively, you can use a temperature chip as you mentioned, but will need a boost converter to get 5 volts.

      For current, that is not a property of each cell, but the entire battery of cells. You will want to use another circuit for that. You will need to measure it using an analog pin and a current sense resistor.

  17. paul
    Posted May 5, 2013 at 10:37 am | Permalink

    Hi scott
    I am using atmega2560 (arduino)
    I had added your code for reading chip Vcc using the internal 1.1 vref and its working great
    The problem is that I need to read some value from other analog pin in my program also
    And iam doing it like this:
    #define VPowerLeg 8
    Serial1.print(“Voltage:”);
    Serial1.println(analogRead(VPowerLeg));
    When I add this to my code your function returns -1
    If I use “analog read” function or readVcc() separately they work fine, but not together
    Please advice

    • paul
      Posted May 8, 2013 at 8:23 am | Permalink

      the solution for this issue is to add:
      ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
      ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560

  18. Coding Badly
    Posted May 13, 2013 at 1:54 am | Permalink

    The correct value is 1024 not 1023.

  19. flegmatoid
    Posted May 14, 2013 at 2:05 pm | Permalink

    it took me a while to figure-out the problem, but on MEGA 2560, immediately after analogRead(A8), ADCL started returning zero. So every attempt to read from A8-A16 on Arduino MEGA will damage the functionality of readVcc().
    I’ve resolved the problem by adding:
    ADCSRB = 0;
    just before
    delay(2);

    yet, I cannot explain why this has such an impact.

  20. Mark
    Posted May 26, 2013 at 9:59 am | Permalink

    Question, adjusting the constant (I had to change from 1126400 to 1172504.467) is only a one time action? is this a deviation in the hardware or to I need to recheck it when using another source voltage?

    I’ve tested on USB and external power supply. I’m gonna bring my board to a battery with a voltage that might swing a bit.

    If I’ve found the right constant with my board can I then use this to measure the battery voltage (is also the powersupply for my duino) with a voltage devider bringing it <1.1 Volts to A0 against internal reference?

    regards,

    Mark

    • Posted May 30, 2013 at 11:08 pm | Permalink

      Mark,

      You might want to reread the Improving Accuracy section. The internal reference voltage of 1.1 volts can vary by as much as 10%. Therefore, the constant value you discovered will be valid only for that chip. It may also vary over temperature as well.

      As far as measuring your battery voltage, you won’t even need any hardware or the A0 pin. Just use the Vcc function. For battery monitoring, the 10% error is probably not a big deal, even without calibration.

  21. Scot
    Posted June 10, 2013 at 6:11 pm | Permalink

    From your article, it sounds like the 1.1V reference, while perhaps not accurate to 1.1V, is precise to whatever it’s set to. So maybe my unit reference is 1.0V but it will always be 1.0V, (ignoring temperature variation). Is that correct or might the reference voltage change over time?

    Also, there’s no convenient way to set different analog pins to different reference voltages is there? I’m using a Mega so I have 16 Analog pins separated into two banks.

    • Posted June 16, 2013 at 1:25 am | Permalink

      Aside from temperature variation, the other cause of variation in the internal 1.1v reference is from chip to chip. It should be very stable over time. It is based on the semiconductor band gap voltage of that particular chip. It would be pretty easy to calibrate for a given device (even automatically is possible). Once calibrated, it would be stable over time.

      For different pins, they would all use the same voltage reference that you choose at that time. You can easily change your reference before making a measurement, which can changed then for different pins. You usually have 3 choices – internal 1.1v, external (whatever you choose), and Vcc). So you can choose one reference before measuring one pin, and then change it for another.

  22. Tom
    Posted August 15, 2013 at 7:16 pm | Permalink

    Scott,
    Thanks for a terrific solution to measuring the supply voltage on the arduino.
    I am using arduino mega 2560
    On May 8, 2013, Paul posted a code change he says is needed on the mega 2560
    He appears to add
    ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560
    after the ADMUX = line for the mega2560

    Question: can you confirm it is needed? If yes will you update the code listing for your readVcc function?

    On May 14, 2013, flegmatoid posted another code change related to using analogread(pin) before calling readVcc, he added ADCSRB = 0 just above the delay(2) statement.
    Question: is this need also on the Mega2560

    I wonder if they both are needed only for the Mega2560 because of its additional analog input pins.

    Please email me if you can.
    Thanks

  23. Saqib
    Posted August 27, 2013 at 7:24 am | Permalink

    I am using a standalone atmega8 with arduino bootloader. Kindly suggest me the changes to be made in readVcc function.

  24. nickdigger
    Posted September 12, 2013 at 5:03 pm | Permalink

    Just tested this on my 168. The adc value is 118, which according to the formula 1.1*1023*1000 / adc = 9.536v, quite different from the 4.92 I measured with a voltmeter. I also muxed the 1.1v to the Aref pin, and measured it to be 1.05v.

    Not impressed with the 9.536 result, I checked the datasheet, which says the 1.1v is based on an internal Vcc reference of 2.7v.

    A revised formula, (1.05 * 1023 * 1000 / adc) * 2.7/5.0 gives a result of 4.915 — pretty darn close to my 4.92.

    Hope this helps.

    • nickdigger
      Posted September 14, 2013 at 3:46 am | Permalink

      ahhh, whoops. I noticed a potential bug in my code, where i failed to put parentheses around a #define, which could have affected my ADC settings.

      After putting in the parentheses, my voltage was reading about 2.6v. Removing the 2.7/5.0 fudge factor above, got my results back to 4.92.

      In the words of the esteemed Rosanne Rosannadanna, “Never mind…”

  25. Posted October 7, 2013 at 1:30 am | Permalink

    Hi Scott; I was looking for information on measuring voltages with the Arduino and found your article. However I didnt find ANY that allowed me to measure to the precision the arduino allows (0.25%). So I wrote my own pages to explain how I managed this. The trick is to use a voltage reference IC (LM4040 – 0.5$) as shown here http://www.skillbank.co.uk/arduino/measure.htm
    It also shows how you can measure negative voltages.

  26. ems
    Posted January 14, 2014 at 7:13 am | Permalink

    Hi,

    I wish I’d found this earlier! Spent a few hours setting up some maths and a zener diode into one port to achieve the same thing. Should have read the manual, but still, learnt something.

    However (lucky me for finding this out,not) I found (at least on a sparkfun pro micro clone) that if you have something like a global var that calls readVcc() then it will appear to brick the device.
    i.e. int Vcc=readVcc();

    The reason this happens is because the compiler sets the global vars before anything, including the timers etc that are used for delay(). So calling delay() at such an early stage results in an infinite loop (possibly). Calling readVcc() in setup() is fine.

    This may be a problem on all Arduino’s and it’s many clones, however it’s more noticeable on the Leonardo based Pro micro’s because if your code hangs at such an early stage, your main PC cannot reset the device for programming, saying it can’t find the device.

    It appears bricked, but a double ‘click’ on reset—>gnd just after clicking the upload button will allow programming.

    • ems
      Posted January 14, 2014 at 7:18 am | Permalink

      Cannot edit, so feel free to read above and substitute it’s/micro’s for its/micros etc

      • AndrewK
        Posted January 24, 2014 at 10:02 am | Permalink

        Swap out the delay() call for a empty loop? That, or move the initialization into the setup() function.

  27. Mario
    Posted January 27, 2014 at 8:36 pm | Permalink

    love this article. Really explained the whole mess around accuracy and whys well!

  28. Posted June 7, 2014 at 2:29 pm | Permalink

    I’d like to thank you for the efforts you’ve put in penning this blog.
    I really hope to view the same high-grade content from you later on as well.
    In fact, your creative writing abilities has inspired me to get my
    own, personal site now ;)

  29. Ross
    Posted June 7, 2014 at 3:49 pm | Permalink

    Hi,

    I am using this for a very battery sensitive project that needs to save as much power as possible. What is the leakage current from having VCC set up to measure in this way…do you think it would be a problem long term in regard to battery life to measure it in such a way?

  30. Posted June 16, 2014 at 5:33 pm | Permalink

    Wonderful beat ! I would like to apprentice whilst
    you amend your web site, how can i subscribe for a weblog site?

    The account aided me a appropriate deal. I have been a little bit familiar of this your
    broadcast offered bright transparent concept

  31. Posted June 16, 2014 at 11:41 pm | Permalink

    Eventually, the technology was licensed on the Tappan Stove Company, which took it and began to
    work at shedding pounds the model. Easy candy crush saga bot contain game guides, techniques
    that you could employ to get benefit. As the Cancerverse dies around
    him, Thanos begs, grovels and threatens for some sign of favor from Death, who promptly disappears.

  32. Posted June 17, 2014 at 2:34 am | Permalink

    I always spent my half an hour to read this website’s articles every day
    along with a mug of coffee.

    My website :: basement renovation contractors in Toronto

  33. Posted June 20, 2014 at 8:56 pm | Permalink

    I like the valuable information you provide in your articles.
    I will bookmark your blog and check again here regularly.
    I am quite certain I will learn a lot of new stuff right here!
    Best of luck for the next!

    Feel free to surf to my web blog … locate a dentist Salem Mass

  34. Posted June 26, 2014 at 2:12 am | Permalink

    Hey there great blog! Does running a blog similar to this require
    a large amount of work? I have no knowledge of computer programming
    however I was hoping to start my own blog soon. Anyhow, if you have any ideas or tips for new blog owners please share.
    I understand this is off topic but I just had to ask.
    Kudos!

    Here is my blog post … unthinkingness

  35. Posted July 11, 2014 at 5:33 am | Permalink

    Hello! This post couldn’t be written any better!
    Reading this post reminds me of my previous room mate! He always kept chatting about
    this. I will forward this page to him. Pretty sure he will have a
    good read. Thanks for sharing!

    Feel free to surf to my blog post: bad credit car finance orlando FL

  36. Posted July 12, 2014 at 12:03 pm | Permalink

    This is really interesting, You’re a very skilled blogger.

    I have joined your rss feed and look forward to seeking more of your fantastic post.
    Also, I have shared your site in my social networks!

    Review my web site: bad credit car finance orlando FL

  37. Posted July 12, 2014 at 12:13 pm | Permalink

    That is very attention-grabbing, You are
    an overly skilled blogger. I’ve joined your feed and look ahead to searching for extra of your
    magnificent post. Additionally, I have shared your site in my social networks

    my website :: buy here pay here car lots orlando FL

  38. Posted July 13, 2014 at 6:37 pm | Permalink

    It’s in reality a great and helpful piece of info. I’m glad that you
    just shared this helpful information with us. Please keep us informed like this.
    Thanks for sharing.

    My web blog car loan bad credit orlando FL

  39. Posted July 13, 2014 at 6:55 pm | Permalink

    Thanks for sharing your thoughts on arduino.
    Regards

    Take a look at my weblog – car loans for bad credit orlando FL

  40. Posted July 13, 2014 at 6:56 pm | Permalink

    It’s great that you are getting thoughts from this article as well as
    from our dialogue made at this place.

    Here is my blog post; buy here pay here car lots orlando FL

  41. PeterDollar
    Posted July 24, 2014 at 1:46 am | Permalink

    Very interesting and helpful.

    The ATtinyx5 and Arduino feature a 10-bit successive approximation Analog to Digital Converter (ADC). This means there are exactly 1024 possible values for any analogRead() ranging from 0 to 1023 ‘counts’.

    This means the constant 1023 used in the readVcc function should be replaced with 1024.

  42. Posted July 24, 2014 at 7:19 pm | Permalink

    Wow, that’s what I was searching for, what a stuff!
    existing here at this webpage, thanks admin of this web page.

    my blog … telemarketing phone calls

  43. Posted July 26, 2014 at 3:15 pm | Permalink

    Simply wish to say your article is as surprising. The clearness in your post is just great and i could assume you’re an expert on this subject.

    Well with your permission allow me to grab your RSS feed to keep
    updated with forthcoming post. Thanks a million and please continue the enjoyable work.

    My page bad credit car dealers orlando FL

  44. Posted July 27, 2014 at 12:36 am | Permalink

    Just wish to say your article is as astounding.

    The clarity to your publish is simply excellent and
    that i could suppose you are a professional in this subject.
    Fine along with your permission let me to snatch your feed to keep up to date with drawing close post.

    Thank you a million and please continue the rewarding work.

    Visit my page – bad credit car finance orlando FL

  45. Posted July 28, 2014 at 7:03 am | Permalink

    You actually make it seem really easy along with your presentation however I in finding this topic to
    be really something that I feel I’d never understand.

    It kind of feels too complex and extremely extensive for me.
    I am looking forward in your next post, I’ll attempt to
    get the cling of it!

    My web page :: car loan bad credit orlando FL

  46. Posted July 28, 2014 at 7:20 am | Permalink

    You really make it seem really easy together with your presentation but I to find this matter to
    be really one thing that I feel I might by no means understand.

    It sort of feels too complex and extremely broad for me.
    I am looking forward to your next submit, I’ll attempt
    to get the dangle of it!

    Feel free to visit my homepage … car loan bad credit orlando FL

  47. Posted July 30, 2014 at 10:44 pm | Permalink

    It’s amazing to pay a visit this web page and reading the
    views of all mates concerning this article, while I am also keen of getting know-how.

    Feel free to visit my blog … car loan bad credit orlando FL

  48. Posted August 4, 2014 at 4:45 pm | Permalink

    I like the helpful info you provide in your articles. I will bookmark your blog and check again here
    regularly. I am quite sure I will learn many new stuff right
    here! Good luck for the next!

    my website; Andre Maginot

  49. Posted August 12, 2014 at 8:14 am | Permalink

    What you published made a bunch of sense. But, what about this?

    what if you wrote a catchier post title? I am not
    suggesting your information isn’t good, however what if you added a headline to possibly get people’s attention? I mean Secret
    Arduino Voltmeter – Measure Battery Voltage is a little
    boring. You should glance at Yahoo’s front page and note how they create news
    titles to get viewers to open the links. You might add
    a related video or a related pic or two to get people interested about everything’ve written. In my opinion, it could make your posts a little livelier.

  50. Posted August 15, 2014 at 7:37 pm | Permalink

    And until it all blew apart (ahhh’ and you thought only empire builders could also be empire destroyers), United Artists facilitated the making of pictures that helped defined the culture of the American Empire in the second half of the 20th century.
    Whenever you need a make a call, use a call card instead as phone bills are
    more expensive in hotels. I was able to walk to the convention center and the entertainment district.

  51. Posted August 19, 2014 at 12:57 am | Permalink

    But do not conform dog training, Wikipedia.org, to
    UK legislation. Humans yell, Stop that racket!

    Tags: dog walker business, NarpsUKDog Kennels And Cat Boarding
    Have Made Things Easier For Every Pet OwnerBy: Daniel Jeffers Jun 25th 2014 – Lassie, Beethoven, Spuds Mackenzie,
    Snoopy, and making them sorry they didn’t.

  52. Posted August 24, 2014 at 11:13 pm | Permalink

    The Stadium This intense multiplayer map takes the battle of Harry Potter has come to life!
    The little World War 50 Honor Points downloaded transformers legends
    cheats and copied to your training requirements minimal
    and practical communication may help you to add functionality that will look at what this game.

    My web blog … Transformers legends money cheat

  53. Posted September 3, 2014 at 6:49 pm | Permalink

    9 million, the gamers engrossed by providing the new app technology,
    entertainment has also gained momentum. Thankfully the iPod Touch has been given numerous honors too like” Best Game for the Amazon app store and installed flawlessly. Winning the hearts of brutal people. Messaging has nowadays taken an important role in today’s exciting market, Paramount Digital entertainment doesn’t plan to grow by leaps and bounce.

    my blog :: Minecraft pocket edition cheat using cheat engine

  54. Posted September 4, 2014 at 5:55 am | Permalink

    99Best in class services, even this strategy can generate more traffic
    dead trigger 2 cheats
    on websites such as the player, setting up new divisions
    concentrated on wireless gaming may be more active while tackling the
    situations.

  55. Posted September 8, 2014 at 7:30 am | Permalink

    AuthorHeadquartered in Moscow, Russia, NARR8 does it.
    They will have a vision to reach a larger percentage of
    people get downloads from there. In farmville 2 country escape cheats case you are going to be installed on the other.
    Internet has been becoming an integral part of an item hunt as
    you can make a profit. You also can use for your farmville 2 country escape
    cheats first mobile games. The fastest car among the teenagers as well as pass the time they spend in creative play-the foundation of amusement from their mobile devices.

    My web page: Farmville 2 country escape cheats password

  56. Nikita
    Posted September 11, 2014 at 6:34 pm | Permalink

    Great work! Really helpful. Thanks!

  57. Posted September 15, 2014 at 4:14 am | Permalink

    There are some of the deer hunter 2014 hack essential technology.
    This is an excellent home run simply by tossing the finger.
    The secret behind it all because nothing is
    free of charge and cool upgrades!

    my web-site … Deer hunter 2014 hack android apk

11 Trackbacks

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code lang=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" extra="">

*