Measuring li-ion that arduino is running from

Using the 1.1V internal analog reference to measure a draining VCC source by using a voltage divider on it

You could indeed use a voltage divider, and measure a scaled-down Vcc against the internal 1.1 V reference. This is, however, not what the code you posted is doing. It is instead measuring the internal reference against Vcc, as stated in the comment within the code. The REFS0 bit selects Vcc as the reference for the ADC. The bits MUX1...MUX3 select the internal reference as the input channel.

result = ADCL;
result |= ADCH<<8;

This idiom dates back from a time when the compiler did not know how to read 16-bit I/O registers: you had to explicitly read one byte at a time and then put these together into a 16-bit word. This time is long gone, and you can now just access the 16-bit register as ADC directly. I would even get rid of the result variable and just:

return 1126400L / ADC;  // Vcc in mV

Edit: As suggested by the busybee, here is the explanation of the factor 1126400: The ADC meaures the ratio of its input voltage to its reference voltage, and returns this ratio scaled by a factor 1024. In this case,

ADC = 1024 × (Vbg ÷ Vcc)

where Vbg is the voltage of the 1.1 V bandgap reference. Thus,

Vcc = 1024 × Vbg ÷ ADC

If we want Vcc in millivolts, we plus the Vbg voltage in millivolts in the above equation and get

Vcc (in mV) = 1024 × 1100 ÷ ADC = 1126400 ÷ ADC


If anyone else is confused by this:

Will it hurt your Arduino?

  • No, this is a internal voltage reference between VCC & the 1.1v internal analog reference.

Is a voltage divider necessary?

  • Not unless you have something external of the Arduino to measure!

Is the internal reference actually 1.1v?

  • No, it seems like each pro mini I have the the 1.1v reference is slightly different. My pro minis do not have a break out for the AREF pin, so you need to figure out what the value is for each individual Arduino.

I used this,

1.15 x 1023 x 1000 = [value in quotation from second to last line in the Function]

return "1125300L" / ADC; 

I just incremented the 1.1v reference by 0.01 a couple of times until the output matched the voltage reading on my digital multi-meter. I would think with a known voltage you could do this math backwards to find what AREF actually is. I am sure that would be easier.

A big thanks to JRobert & @ EdgarBonnet for your answers!


Instead of calling analogRead(), this sketch performs the equivalent actions by directly manipulating the hardware registers to begin a conversion, wait until the conversion is complete, and collect the converted value.

Just reading the final value is accomplished by the statements:

result = ADCL;
result |= ADCH<<8;

All of the statements following delay(2) up to and including the above 2, taken together, do what analogRead() does.