Reducing power consumption is fairly tricky, as you can see in the many previous posts about this topic. And to be honest, I haven’t quite gotten to the point where I want to be with the Room Board. My first “major” (ahem) setup with about a dozen nodes around the house didn’t quite go as I had hoped. Most batteries were empty within a month. A few nodes are still going, but those are hooked up to power adapters…
I’d like to revisit this issue and try to improve things a bit. To make the rooms sketch perform better, and also to make the code structure a bit clearer. The current rooms code is quite complex and hard to follow.
But before messing with the rooms.pde sketch, let’s tackle something simpler: the wireless sensor node based on the Pressure Plug, as described here, and then simplified here. I’ll use that last version as starting point.
The first point to note is that to get a substantial first power reduction, you have to focus on the portion of the code where it’s spending most of the time. Which, in the case of “bmp085demo”, is here:
That’s right: it’s waiting for the next second to “happen”. And even with a slow’ish sensor such as the BMP085 at maximum resolution, it’s spending more than 90% of its time there… waiting!
I’ll use an approach which might be a bit surprising: let’s not change any of the current logic. The idea is that once we’ve done our thing for the current second, we can go into low power mode, as long as we make sure to get back to normal operating conditions in time for the next second.
So what we’re going to do is add some code to the end of the loop() function. It’s functionally equivalent to adding it at the start, since loop(), eh, loops – but I think it makes a better point.
The end of loop() used to look as follows:
I’m changing it to this:
IOW, at this stage all the hard work has been done. We wait a bit for all interrupt-driven I/O to complete (serial and RF12). And then we need to figure out how much time remains until the next second. The power saving happens by spending that time in power off mode.
But this requires some preparation. When inducing a comatose state like this, you have to make absolutely sure that something is still able to get you out of coma and back up and running again. This is what the ATmega’s “watchdog” is for: we set it up to wake us up in 16 milliseconds, just before entering sleep mode. And then we keep doing that until it’s almost time to take another reading. The actual watchdog interrupt handler does nothing, btw – all we want is to get back out of power down.
Note that the radio also needs to be turned off and back on again. It’s the biggest power consumer when enabled. Turning it off and going into power down mode is what lets us go from a tens-of-milliamps current drain to a tens-of-microamps current drain.
All the logic for this is located in the loseSomeTime() function, which was adapted from a slightly different version in the rooms.pde sketch:
And that’s about it. The average power consumption of this sensor node will go down by an order of magnitude. It’ll still be 1..2 mA, but it’s a major improvement: this node should now run 2..3 months on AA batteries. The source code for bmp085demo.pde has been updated.
I’d like to stress that such gains require very little effort for many types of sketches. All you have to do is figure out where the sketch is spending most of its time, and deal with just that part of the code. Getting into yet lower power consumption levels would require more work.
I adjusted it to wake up only once every second. Especially when waiting for longer periods, it’s more efficient to do longer sleeps.
Good point. Often, the exact reporting period really doesn’t matter that much anyway.
Besides, there’s probably some drift in the above code, since clock startup takes time too. I haven’t tweaked the fuse settings, to account for the fact that resonators can start up faster than crystals.
Cool, that’s almost the exact way I implemented low power on my jeenode with the 3 input plugs. But instead of the delay I just call rf12_canSend() again until it returns true. I figured if I can send again, the previous packet is surely away. Is this correct?
So in short I have this:
Yes – that will work. More details in tomorrow’s weblog post ;)
Nice work!
As long as you only require transmissions, these power down tricks seem to work quite well, but if I understand correctly, if you require message receiption, the RF12 is still the biggest power-hog in the system with it’s 15mA in receive mode.
What if you would scan the sensor each minute, or every 3 minutes. Would that give you a battery life of a year or more?
Could you use the watchdog to trigger the sensor reading every minute for instance, or can’t the watchdog timer do such long intervals?
Yep. For low power, the node has to be off the air. It can either poll another node and get data via an ACK if it needs that, or you can try to synchronize time slots so both parties only communicate at precise intervals. That’s what TDMA does. One node can tell the other how much its clock is drifting, so they can stay in sync indefinitely. But it’s more complex.
Your last 2 Q’s: yes, yes. I use a shorter watchdog time and then go back to sleep right away – no doubt you can squeeze some more savings there, but it won’t be that much, I expect. The watchdog can go up to 8 seconds.
I’m going to revisit this for the rooms node as well. At least a year battery life should really be doable.
Generally speaking, the best way to reduce power consumption is to sleep most of the time. Like squirrels do in the winter :)