No, this isn’t a story about bio-rhythms :)
One of the challenges I’ll be up against with Room Nodes is how to keep track of time. The fact is that an ATmega is extraordinarily power efficient when turned off, and with it a JeeNode – under a few microamps if you get all the little details right. That leads to battery lifetimes which are essentially only determined by self-discharge!
But there are two problems with power off: 1) you need to be 100% sure that some external event will pull the ATmega out of this comatose state again, and 2) you can completely lose track of time.
Wireless packets are of no use for power-down mode: the RFM12B consumes many milliamps when turned on to receive packets. So you can’t leave the radio on and expect some external packets to tell you what time it is.
Meet the watchdog…
Fortunately, the ATmega has a watchdog, which runs on an internal oscillator. It isn’t quite as accurate, but it’ll let you wake up after 16 ms, 32ms, … up to 8 seconds. Accuracy isn’t such a big deal for Room Nodes: I don’t really need to know the temperature on that strict a schedule. Once every 4 .. 6 minutes is fine, who cares…
There’s a Sleepy class in the Ports library, which manages the watchdog. It can be used to “lose time” in a decently accurate way, and will use the slowest watchdog settings it can to get it out of power-down mode at just about the right time. To not disrupt too many activities, the “millis()” timer is then adjusted as if the clock had been running constantly. Great, works pretty well.
It’s not enough, though.
As planned for the next implementation, a Room Node needs to sleep one minute between wakeups to readout some sensors, but it also needs to wake up right away if the motion sensor triggers.
One solution would be to wake up every 100 ms or so, and check the PIR motion sensor to see whether it changes. Not perfect, but a 0.1s delay is not the end of the world. What’s worse though is that this node will now wake up 14,400 times per day, just to check a pin which occasionally might change. This sort of polling is bound to waste some power.
Meet the pin-change interrupt…
This is where pin-change interrupts come in, They allow going into full power-down, and then getting woken up by a change on any of a specified set of pins. Which is perfect, right?
Eh… not so fast:
Q: What time is it when the pin-change occurred?
A: No idea. Somewhere between the last watchdog and the one which will come next?
IOW, the trouble with the watchdog is that you still don’t really track time. You just know (approximately) what time it is when the watchdog fires again.
If the watchdog fires say every 8 seconds, then all we know at the time of a pin-change interrupt, is that we’re somewhere inside that 8s cycle.
We can only get back on track by waiting for that next watchdog again (and what if the pin change fires a second time?). In the mean time, our best bet is to assume the pin change happened at the very start of the watchdog cycle. That way we only need to move the clock forward a little once the watchdog lets us deduce the correct moment. FYI: everything is better than adjusting a clock backwards (timers firing again, too fast, etc).
Now as I said before, I don’t really mind losing track of time to a certain extent. But if we’re using 8-second intervals to get from one important measurement time to the next, i.e. to implement a 1-minute readout interval, then we basically get an 8-second inaccuracy whenever the PIR motion detector triggers.
That’s tricky. Motion detection should be reported right away, with an ACK since it’s such an important event.
So we’re somehere inside that 8-second watchdog cycle, and now we want to efficiently go through a wireless packet send and an ACK cycle? How do you do that? You could set the watchdog to 16 ms and then start the receiver and power down. The reception of an ACK or the watchdog will get us back, right? This way we don’t spend too much time waiting for an ack with the receiver turned on, guzzling electrons.
The trouble is that the watchdog is not available at this point: we still want to let that original 8-second cycle complete to get our knowledge of time back. Remember that the watchdog was started to get us back out in 8 seconds, but that it got pre-empted by a pin-change.
Let me try an analogy: the watchdog is like throwing a ball straight up into the air and going to sleep, in the knowledge that the ball will hit us and wake us up a known amount of time from now. In itself a pretty neat trick to keep track of the passage of time, when you don’t have a clock. Well, maybe not for humans…
The scenario that messes this up is that something else woke us up before the ball came down. If we re-use that ball for something else, then we have lost our way to track time. If we let that ball bring us back into sync, fine, but then it’ll be unavailable for other timing tasks.
I can think of a couple solutions:
Dribble – never use the watchdog for very long periods of time. Keep waking up very frequently, then an occasional pin-change won’t throw us off by much.
Delegate – get back on track by asking for an ack which tells us what time it is. This relies on a central receiving node (which is always on anyway), to tell us how to adjust our clock again.
Fudge it – don’t use the watchdog timer, but go into idle mode to wait for the ack, and make that idle period as short as possible – perhaps 2 ms. IOW, the ACK has to reach us within 2 milliseconds, and we’re not dropping into complete powerdown during that time. We might even get really smart about this and require that the reply come back exactly 1 .. 2 ms after the send, and then turn off the radio for 1 ms, before turning it on for 1 ms. Sounds crazy, until you realize that 1 ms of radio time uses as much energy as 5 seconds of power down time – which adds up over a year! This is a bit like what TDMA does, BTW.
All three options look practical enough to consider. Dribbling uses slightly more power, but probably not that much. Delegation requires a central node which plays along and replies with an informative ack (but longer packets take longer to receive, oops!). Fudging it means the ATmega will be in idle mode a millisec or two, which is perhaps not that wasteful (I haven’t done the math on that yet).
So there you go. Low power stuff isn’t always trivial, once you start pushing for the real gains…