Arduino – How Low Can You Go

Why bother?

Its fair to say I use a lot of micro controllers, and over the years an Arduino in some form or another has crept its way into many of rigs and prototypes I have worked on. They are cheap, reliable enough and have a army of open source contributors online. However, I have rarely had work within a power budget as typically they end up tethered to a laptop gathering data or interacting with a wider system with its own supply. 

When I began a home project for a battery powered outdoor weather station, I turned to my favourite Arduino; the “Nano”, as its brain. Measuring the current consumption of an off the shelf Nano indicated the hungry board would gobble up an AA battery in just a few days. 

Researching some techniques and power saving tips yielded such great results that I was compelled to compiled my findings here for the use in future, or to help others save some time.

Test Setup

To evaluate the reduction in power our changes make, we will be running the “bare minimum” of code required for a sketch to compile properly (Arduino IDE > Files > Examples > 01. Basics > BareMinimum). This is preferable as the default Blink sketch would have prevented stable current measurements.  So as a baseline the Arduino consumes 16.9 mA

Step 1: Remove the Power LED

A form of Muntzing can be applied to the Nano by removing some unnecessary components. First we can desolder the Power LED, or its current limiting resistor. This can save around 5 mA when running the BareMinium sketch. If hardware serial comms are required, power savings can be gained from also removing the TX & RX LED’s. 

Step 2: Lower the VCC Supply Voltage and Clock Frequency

Reducing the supply voltage is an easy way to decrease the Arduino’s power consumption. If we can power the board from a 3.3V supply rather than 5V, the current drops significantly from 11.8 mA, down to 4.2mA (reducing power consumption by 77% to 13.8 mW!) However when adjusting the supply voltage we must consult the Microchip oracle first (ATmega328P datasheet). 

The datasheet, Section 28.4 explains how the Maximum Clock Frequency is dependent on the supply voltage. Figure 28.1 describes the “Speed Grades” of the chip and provides a safe area in which the ATmega328P should function correctly.

If we lower the supply voltage below 4.5V we must also reduce the clock frequency to stay within the safe envelope.

It turns out that reducing this frequency also has substantial power savings, the Nano defaults to using a 16Mhz crystal. This means the ATmega microcontroller can execute up to 16 million instructions per second.  To reduce this number, we can once again refer to the datasheet and find the section regarding the “CLKPR – Clock Prescale Register” (described in section 8.11) more info.

The prescaler works by dividing the base clock frequency (16 MHz) by a division factor (1,2,4,8….256).  The factor is set by the 4 CLKPS bits in t he CLKPR register. Normally the CLKPS bits will be reset to “0000”, which if we look the table below gives a division factor of 1 ie full speed 16 MHz.

If the CLKPS bits are set to “0011”, giving a division factor of 8, so the Arduino nano will be executing instructions at (16/8) 2 MHz. This reduces power consumption and also allows us to stay within the safe zone (Figure 28.1) is we lower the VCC voltage.

Clock Prescaler Settings Table

To avoid unintentional changes of clock frequency, a special write procedure must be followed to change the CLKPS bits:

1. Write the clock prescaler change enable (CLKPCE) bit to one and all other bits in CLKPR to zero.

So in the setup section of our code, we need to write “10000000” into the register which = “0x80” (binary to hex). We write CLKPR = 0x80;

2. Within four cycles, write the desired value to CLKPS while writing a zero to CLKPCE.

Then immediately after we write the clock division factor by setting the 4 CLKPS, e.g. “0001” = Clock Division factor of 2, so the CPU runs at 8MHz. We write “0x01” (4 MHz = “0x02”, 2 MHz = “0x03”, 1 MHz = “0x04” and so on).

void setup() {
    CLKPR = 0x80; // enable a change to CLKPR
    CLKPR = 0x01; // change division factor
} 

The data below shows current consumption for a number of clock frequencies and supply voltage combinations, when running the BareMinimum code. 

NumberSupply VoltageClock FrequencyCurrent Consumption
15V16 MHz11.8 mA
25V8 MHz10.1 mA

3

3.3V

16 MHz

4.2 mA

43.3V8 MHz3.5 mA
53.3V4 MHz2.1 mA
Figure 28.1 Maximum Frequency vs VCC

Step 3: Remove (or Replace) the Voltage regulator

On the back of the Nano (clone) is a AMS1117 SOT-223 Voltage regulator (datasheet). The efficiency of these of kind of Regulators is quite good under heavy load, however with a light load their quiescent current draw dominates the remaining power budget. If we can power the ATmega328p with a 3.3v source, removing the voltage regulator brings the current consumption down by a further 200 μA.

I managed to snip the smaller legs off the chip then wiggled it until the large pin broke free. 

If we cant supply the nominal voltage, we should consider an alternative voltage regulator that has been deigned specifically for low Quiescent Current Consumption. For example the MCP1700 has a current draw of only 1.6 µA!

Step 4: Software Power Savings

It you don’t need the Arduino to be doing things all of the time, (which is a safe bet for most battery powered projects) you can place the CPU into a “Sleep mode” when it is not being used. All ATmega MCU’s have built in sleep modes, in general, sleep modes should be used as much as possible, and the mode should be selected so that as few as possible of the device’s functions are operating.

For example, my project for an low power weather station only requires temperature readings to be taken every 15 minutes or so, therefore we can power down the CPU into a low power sleep state for 99.9% of the time. Drastically increasing battery life.

We can leverage these sleep modes though software, and a number of Libaries exist to help us do so. I recommend using the Low Power Library by RocketStream. It has functions for sleeping the ATMega328P in various configurations and also support the ATmega 88, 168, 168P, 32U4, 2560 or 256RFR2.

The library provides functions for writing to the various registers which control Sleep Modes. (See Section 9 of the ATmega328P datasheet). 

or visit the GitHub page ->

The Library provides two main functions allow us to enter modes:

Idle Mode
LowPower.idle(SLEEP_8S); 

This power saving mode does not reduce consumption as much as the powerDown mode, but can wake up far more quickly. This mode can allow the SPI, USART, analog comparator, ADC, 2-wire serial interface, Timer/Counters, watchdog, and the interrupt system to continue operating.  

To save power we can manually turn these off, by adding the flowing arguments.

LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF); 

The MCU is place into idle mode by writing Sleep Mode Control Register SM2..0 bits to 000. 

Power Down Mode
LowPower.powerDown(SLEEP_8S); 

This mode saves more power than the Idle condition, by stopping the external oscillator. The trade off however is that there is a delay on wake-up to allow the clock to become stable.

We can further reduce power consumption my manually disabling the Brown Out Detection module (BOD) and the Analog to Digital Convert module (ADC), by adding the following arguments.

LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);   

powerDown mode is enabled by writing the Sleep Mode Control Register SM2..0 bits to 010 

Using powerDown()

I would suggest that for most applications the “powerDown()” function should be used as it provides excellent power savings and the wake up time whilst longer an Idle wake, should not affect the majority of projects.

powerDown() can be used in two ways:

1. External interrupt

In this configuration the chip can be told to sleep forever, and we can use an external interrupt from any of the interrupt pins (pins 2 & 3 for the 328p), to wake up the MCU when a change is detected, and execute some code.  This would be ideal for anything can be activated with a button press, or sensor input. See example code.

LowPower.powerDown(SLEEP_FOREVER); 
2. Internal Timer

Or we can use the internal watchdog timer to create timed interrupts that periodically wake the MCU. As the watchdog timer uses the on-chip 128 kHz oscillator, the sleep period is unaffected by changes made to system clock prescaler from step 2. We can set this interval from 15μs to a maximum of 8s.

LowPower.powerDown(SLEEP_8S); 
Example: periodic sleeping for longer than 8 sec

For the weather station project, I would like the CPU to sleep for a period of time (for an hour or so), wake up, take a reading and send some data thengo back to sleep. However as explained above, the maximum sleep period is 8 seconds. We can effectively extend this sleep period by placing the function in a for loop and executing the maximum 8 seconds sleep function a number of times. The MCU will wake up and consume more power during when the for loop advances, however it is assumed that the CPU will be awake for such a short period of time, this should not drastically affect battery life. 

See Example code below:

#include "LowPower.h"
// How long should sleep last
const int sleep_hours = 1;

void setup()
{
    // No setup is required
}

void loop() 
{
    // Enter power down state for 8 s with ADC and BOD module disabled
    for(int i=0, i<=sleep_hours*480; i++){
      LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);  
    }
    // Do something here
    // Example: Read temperatrue, send data
} 

The library also provides functions for accessing other ATmega328p sleep modes including powerStandby, powerSave, adcNoiseReductionmode and functions for the SAMD21G18A more info here.

We can also use the AVR sleep library (The Arduino system is based on the avr-gcc compiler and can make use of the standard AVR libc libraries, these are open-source C libraries, specifically written for Atmel hardware), see here, but for most applications the RocketStream library will do just fine.

Step 5. Remove the FTDI chip

The final step to is a drastic one, removing the FTDI chip. This chip provides a USB to Serial interface, allowing the Arduino to communicate with a PC’s USB port as traditional serial ports are not available on most modern computers. (On many of these cheaper “cloned” boards this chip of often a CH340 rather than a proper FTDI chip). As there are no power saving features like an enable pin on offer, it pulls power even idle and not in use.

If you need super low power its got to go, as the last power hungry thing on the board removing it can save us ~ 200 μA. If you really do need to change the code after this step, you can always hook up an external USB to serial FTDI chip, see instructions here, it would be cheaper to just replace the whole Nano board. 

Conclusion & Results

By following the steps above, I was able to reduce the current draw of an unmodified Arduino Nano from 16.9 mA to 3.1 mA when active, and when sleeping reduce this to a final 4.5 μA.

Final Solution

The aim for the low power weather station was to run a single AA battery in the housing and have the Arduino periodically take readings, once every hour. As the nominal battery voltage is below the VCC range of the ATmega328P, we requires a boost converter to bring the supply voltage up to 3.3V. 

With low power in mind I found the MCP16251 Low Quiescent Current (4 µA) Synchronous Boost Converter. To work out how long we can run our system we must first choose a battery.

After some research the Energiser Ultimate Lithium AA battery (datasheet) looked like a good candidate.

Example: Runtime Calculation

To calculate how long the system will run, the first step is to look at the start up voltage of the boost converter. The data sheet states 0.82V, so rounding this up to 0.9V gives us the lowest voltage we can safely discharge the battery to and still supply 3.3V to the Arduino.

Looking at the discharge curve from the datasheet, we see that for the lowest constant current given of 1mA,when the battery reaches 0.9V this equates to capacity of 3500 mAh. great!


You can see from the “temperature effects on Capacity” plot, lithium cells are largely unaffected by temperatures changes, ideal for a battery running outside.

Next we need to consider the efficiency of the boost converter, as stated on the MCP16251 datasheet the quiescent current draw is as low 4 µA.  With a maximum output current of 125 mA that that should be more than sufficient to drive our Arduino.

The datasheet provides a plot which describes how the efficiency changes with load. As our system will be running at the lower end of the Iout scale, the efficiency can as high as 78% with a 1.5V input.

However as a our battery voltage will dip over time and to account for some losses we have not considered, I will reduce the overall efficiency to 60% to play it safe. 

MCP16251 Efficiency Vs Current

Based on some measurements I took, if we wake the system once an hour to take a reading,  the MCU draws 50 mA for 3 seconds. The remainder of time the system will stay in a sleep state consuming, 4.5 µA (along with the quiescent current of the voltage regulator 4 µA). On average running at 60% efficiency.

Taking those numbers and considering the capacity of the battery under the load conditions, we can estimate the runtime of our system using the following process:

 

RUNTIME 4+ Years

Not too bad

Honorable Mentions

The main reason for writing this post around the Arduino Nano, and the ATmega328P, is that it is my favourite format Arduino, and clones are so cheap I have about 50 of them lying around. If you are not tied to the Nano platform there a number of alternate Arduino compatible routes that provide even better power savings. Here are a few I came across when resahcing this topic.

Leave a Comment