The JeeLib library has a convenient loseSomeTime() function which puts the ATmega in low power mode for 16 to 60,000 ms of time. This is only 10% accurate, because it uses the hardware watchdog which is based on an internally RC-generated 128 KHz frequency.
But the worst bit is that when you use this in combination with interrupts to wake up the ATmega, then you can’t tell how much time has elapsed, because the clock is not running. All you know when waking up, is that no more than the watchdog timeout has passed. The best you can assume is that half of it has passed – but with loseSomeTime() accepting values up to 1 minute that’s horribly imprecise.
Can we do better? Yes we can…
Internally, loseSomeTime() works by cutting up the request time into smaller slices which the watchdog can handle. So for a 10000 ms request, for example, loseSomeTime() would wait 8192 + 1024 + 512 + 256 + 16 ms to reach the requested delay, approximately. Convenient, except for those long waits.
Here’s a test sketch which simply keeps waiting 8192 ms at a time:
The corresponding current consumption, measured via oscilloscope, shows this:
First of all, note how 8192 ms ends up being 8255 ms, due to the watchdog timer inaccuracy.
But the main result is that to perform this sketch, the ATmega will draw 5 mA during about 50 µs. The rest of the time it’ll be a few µA, i.e. powered down. These wake-ups draw virtually no current, when averaged.
The downside is that under these conditions, interrupts can cause us to lose track of time, up to 8192 ms.
So let’s try something else. Let’s instead run the watchdog as briefly as possible:
Current consumption now changes to this:
I don’t really understand why Because of a loop in the loseSomeTime() code which runs faster, the running time drops by half in this case (and hence total nanocoulomb charge halves too). But note that we’re now waking up about 60 times per second.
This means that interrupts can now only mess with our sense of time by at most 16 ms. Without interruptions (i.e. most of the time), the watchdog just completes and loseSomeTime() adds 16 ms to the millis() clock.
Let’s try and estimate the power consumption added by these very frequent wake-ups:
- each wake-up pulse draw 5.5 mA for about 25 µs
- the charge consumed by each pulse is 122 nC
- there are (roughly) 60 wake-up pulses per second
- so per second, these pulses consume 60 x 122 nC ≈ 7.3 µC in total
- that comes down to an average current consumption of 7.3 µA
That’s not bad at all! By waking up 60 times per second (and going back to sleep as quickly as possible), we add only 7.3 µA current consumption to the total. The reason this works, is that wake-ups only take 25 µs, which – even at 60 times per second – hardly adds up to anything.
So this technique might be a very nice way to keep approximate track of time while mostly in sleep mode, with the ability to wake up whenever some significant event (i.e. interrupt) happens!
PS. In case you’re wondering about the shape of these signals, keep in mind that I’m measuring current draw before the regulator and 10 µF capacitor on the JeeNode.