As shown in this post, it is possible to read out the approximate level of VCC by comparing the internal 1.1 V bandgap with the current VCC level.
But since this is about tracking battery voltage on an ultra-low power node, I wanted to tinker with it a bit further, to use as little energy as possible when making that actual supply voltage measurement. Here’s an improved bandgap sketch which adds a couple of low-power techniques:
First thing to note is that the ADC is now run in noise-
canceling-reducing mode, i.e. a special sleep mode which turns off part of the chip to let the ADC work more accurately. With as nice side-effect that it also saves power.
The other change was to drop the 250 µs busy waiting, and use 4 ADC measurements to get a stable result.
The main delay was replaced by a call to loseSomeTime() of course – the JeeLib way of powering down.
Lastly, I changed the sketch to send out the measurement results over wireless, to get rid of the serial port activity which would skew the power consumption measurements.
Speaking of which, here is the power consumption during the call to vccRead() – my favorite graph :)
As usual, the red line is the integral of the current, i.e. the cumulative energy consumption (about 2300 nC).
And as you can see, it takes about 550 µs @ 3.5 mA current draw to perform this battery level measurement. The first ADC measurement takes a bit longer (25 cycles i.s.o. 13), just like the ATmega datasheet says.
The total of 2300 nC corresponds to a mere 2.3 µA average current draw when performed once a second, so it looks like calling vccRead() could easily be done once a minute without draining the power source very much.
The final result is pretty accurate: 201 for 5V and 147 for a 4V battery. I’ve tried a few units, and they all are within a few counts of the expected value – the 4-fold ADC readout w/ noise reduction appears to be effective!
Update – The latest version of the bandgap sketch adds support for an adjustable number of ADC readouts.