Super-Low Power Arduino (for battery powered application)

I'm working on a battery powered remote control. It uses a touch screen, so needs the A/D converter, and uses an RF transmitter, so I'm currently using VirtualWire for the data transmission. However, I'm willing to change anything to get this going. I'm using the Arduino board to program the chip, and then transplanting it to a breadboard, which has a 5V supply, 20MHz crystal, a few capacitors and resistors (nothing too unusual).

I'm struggling to get battery consumption below 20-40mA (even when very little is going on). This seems awfully high; I'd imagine <10ma would be much more suitable (remember: I'm not measuring the USB interfacing - just the atmega chip).

I can work out the specifics of my code, but I'm trying to find a way to get the nearly-idle current down first. I've tried the 'blink' code, and even with no LED connected, consumption is up at 40mA. I realise that delay() isn't very efficient, but even with a proper sleep and watchdog wake up, it's about 20mA some of the time, and around 40mA the rest of the time.

For example's sake, what's the lowest current consumption possible for a 'blink' style application (let's just say it waggles a digital pin, but doesn't light up an LED)? I've attached the basic code I'm using, but I'm looking for ways to turn stuff I don't need off, and stop the Arduino libraries doing anything that I'm not aware of. Any help much appreciated!

low_current_blink.pde (2.17 KB)

it's about 20mA some of the time, and around 40mA

How much of that does the LED take?

How much of that does the LED take?

None - I'm transplanting the chip to a breadboard which doesn't have the LED fitted.

What are you using to supply the board?

What are you using to supply the board?

Actually, I'm using the Arduino board (minus the chip), plugged into the USB of my computer. I'm then connecting the +5V from the Arduino to the breadboard (via an ammeter). Something like this:

Arduino (minus chip) -> ammeter -> breadboard (chip, crystal + a couple of caps/resistors etc)

I have a similar RF remote control, based on a promini.
I am running 8MHz from a LiPo battery ~4V.
It goes into power down sleep mode after each key press, a keypress interrupts it out of sleep to read the key, transmit, and go back to sleep.
Draws ~9mA in sleep, ~15mA while sending.
Power LED draws some, battery charging chip draws some, RF transmitter draws some, atmega draws some.

Can you post schematic of your design?
Why 20MHz vs 16MHz?

Oops - I should have said 16Mhz (not sure where I got 20 from, but it's definitely 16).

Right now, whilst trying to get this thing down to sleep, I've literally only got the chip on the board. I want to connect a touch screen (eg. a Nintendo DS screen), which requires 2 analogue pins and 2 digital. The RF module takes one data pin, although I've found it can be powered by an arduino digital pin (so I can control the power consumption of it). I'll see if I can knock up a diagram or something...

Having hacked about with this a bit more, I'm starting to wonder if I need a physical button to wake up the remote, and then use the touch screen. That's absolutely not the design I want, so maybe I'll look at some other interrupt source (tilt switch?). All of this isn't really where I want to go though - I'd prefer to find a way of stripping the atmega chip down to the bare bones so that it draws <10mA while idle (I'm not too worried what it draws when you're using it). To get that far, I'm going to need to work out some way keep the chip in sleep mode (as deep as possible), but with occasional (eg. every 500ms) interrupts to see if you've touched the screen.

Any idea what difference dropping to 8Mhz might make? Maybe I could use the internal oscillator and save a few components too...?

See section 9 & 28 of the data sheet.

Here's some bits that I found were needed. In my case, a keypress causes a hardware interrupt on pin 2.
I think hardware interrupt will let you put more stuff to sleep for lower current draw.

pre-setup

#include <VirtualWire.h>    // Wireless transmitter/receiver library
#include <Keypad.h>         // Matrix Keypad library
#include <avr/sleep.h>      // powerdown library
#include <avr/interrupt.h>  // interrupts library

functions

// *  Name:        pin2Interrupt, "ISR" to run when interrupted in Sleep Mode
void pin2Interrupt()
{
  /* This brings us back from sleep. */
}
// *  Name:        enterSleep
void enterSleep()
{
  /* Setup pin2 as an interrupt and attach handler. */
  attachInterrupt(0, pin2Interrupt, LOW);
  delay(50); // need this?
  /* the sleep modes
   SLEEP_MODE_IDLE - the least power savings
   SLEEP_MODE_ADC
   SLEEP_MODE_PWR_SAVE
   SLEEP_MODE_STANDBY
   SLEEP_MODE_PWR_DOWN - the most power savings
   */
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // setting up for sleep ...
  sleep_enable();                       // setting up for sleep ...

    // Disable ADC
  ADCSRA &= ~(1 << ADEN);

  // Power down functions
  PRR = 0xFF;


  sleep_mode();                         // now goes to Sleep and waits for the interrupt

  /* The program will continue from here after the interrupt. */
  detachInterrupt(0);                 //disable interrupts while we get ready to read the keypad 
  

    // Power up functions
  PRR = 0x00;
 

  /* First thing to do is disable sleep. */
  sleep_disable(); 

  // then go to the void Loop()
}

and finally, calling sleep from within void loop()

void loop(){
  if (sleep_count>1000){                      // check if we should go to sleep because of "time" i.e. 1000 times thru loop with no action
    sleep_count=0;                           // turn it off for when we wake up
  
    delay(100);                               // need this?
    enterSleep();                             // call Sleep function to put us out
    
                                              //  THE PROGRAM CONTINUEs FROM HERE after waking up in enterSleep()
  }                                           // end of checking to go to sleep

// code here that does other stuff ...

  sleep_count = sleep_count+1;                // start counting to go to sleep
}

Hey CrossRoads, you're awesome!

I've done a bit more experimenting... When in full sleep, the chip itself seems to take almost nothing (maybe 0.1mA or something). The really cool thing is that it seems to leave it's output stages enabled, so that means any pins left as outputs, and set to 0 or 1 stay that way when it's asleep (this is cool because I can set one axis of my touch screen up for reading, and have an op-amp provide me with the hardware interrupt).

I'm now looking at dropping the chip down to 8MHz - I tried to do it by bootloader/fuses etc, but for the life of me, couldn't get it working properly (it just refuses to be programmed) - that's another problem. I've done something similar by using the clock pre-scaler:

CLKPR = (1<<CLKPCE);
CLKPR = 1; // this is actually "divide by 2"

(Which I found here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1163418637 - this messes with delay() and millis(), Serial and pretty much everything else)

Anyway, the good news is that at 8Mhz, the chip takes about 75% current it does at 16Mhz (I measured 27mA at 8Mhz, versus 40mA at 16Mhz).

There you go!

In my case, I had to write some outputs Low so that the interrupt could be created by a key press, then write them back high again before the keypad library would work. Glad to see you found a way to get your interrupt created as well.

I have a 8MHz promini, and ended up doubling the serial.begin(speed) to have it talk correctly via serial port for debugging, and doubling the speed set for Virtualwire RF to have that work as well. Am guessing the chip is set for 16MHz operation (maybe something I did during downloading without realizing it?) but with 8MHz xtal, external comms need to be set to 2X so operate at the correct speed.

Your biggest power savings are reducing the voltage and frequency. Try 8mhz is easy because you can call it a lilypad but 1mhz will use way less power.

Thanks guys.

I found that messing with CLKPR messes with delay(), millis() and just about everything else. However, it's possible to 'fix' them by making a new boards.txt file with all the same details as your usual board, but with the correct clock speed in it (just use that stanza as your board - there's no need to burn a bootloader or anything - it seems that uploading a sketch is enough to specify the CPU speed). A bit of a hack, but saves having to put multipliers all through your code.

Now I've got to variable clock speeds, I'm thinking I should work out a low_power_delay() function for all those 50 and 100ms delays I'm ending up with. Even though clock scaling messes with millis(), I'm sure I can calculate compensation and end up delaying for the right amount of time. I'll put my thinking cap on...

I'll also look at dropping down to really low speeds for easy grunt work, and maybe going to 8MHz for VirtualWire or something.

Thanks for the help - I never did strip the chip down to the bare bones, but it's hard to improve on a current consumption of nearly zero!

Thanks everyone for your help with this. I've tried to put a few things together and test out various ways of lowering power consumption. This post http://arduino.cc/forum/index.php/topic,57067.0.html asks about unused pins, which as it turns out makes quite a lot of difference.

I've written up what I've found here: http://electronicsfordogs.com/node/15 . In short though, set all your unused pins as inputs, and enable the internal pull ups on them, use the PRR register for anything you absolutely don't use (eg. SPI, TWI, maybe the ADC, timers, etc), and think seriously about reducing the clock speed to 8Mhz or less if you can get away with it. Any long delay()s in code could be candidates for sleep mode, or else a slow clock and ordinary waiting. Obviously, any time you're hanging around waiting for something external to happen, then use an external interrupt whilst you put the CPU into a deep sleep.

In short, it's possible to make some serious current savings, so long as you're willing to spend a bit of time doing it. Reasonable savings take almost no effort at all, so it's really easy to save some power in most applications, if it's something you care about.

I have blatantly copied Crossroads' sleep mode collection on my current project ( except I have a separate interrupt pushbutton to ground ), and I also power my transmitter and 3 cmos counter chips from an output pin ( make sure you switch all inputs to the cmos chips to low before powering them down or the protection diodes will short the input to ground via the vcc being low - I blew one micro that way )

I am using a 9v battery and a micropower 5 volt regulator that is always on , and my unit draws 110 microamps in sleep mode, which with a 580mAh duracell should last about 5000 hours = about 7 months.

100uA is still pretty high. It really gets interesting once you go below 30uA. Depending on what you do you may even get below 1uA.

The canonical example for low power Arduino seems to be the nightingale example http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/.

You may also want to look here:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1273507808
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1278021334

And here
Power Switch - Mikrocontroller.net (in German but the code comments are English) http://www.mikrocontroller.net/attachment/81946/PowerSwitch20100711.zip

The setup of the last example will bring the controller down to 0.4uA while sleeping and 214uA while running.

With regard to unused pins:

  1. You must ensure that no pin floats. Floating pins will increase current consumption.
  2. It does not matter if you pull them low or high. Unless of course there is some external component connected to it. Then it does matter. Depending on the external components (e.g. external pull up or pull down) it may depend.

One thing though: being able to run with 0.4uA does not imply being able to start with 0.4uA. During startup the AVR will always draw significantly more current (>>10uA). This is the "over the hump" problem that prevents starting from very high impedance power sources unless you add some additional components for power control.