Lowering Arduino Power Requirements Using Sleep Mode
Some Arduino projects you create may be required to run outside or far from power lines. This then requires portability causing you to rely on battery power, and possibly, solar charging, making it a bit more complicated. In this article, we will discuss methods to reduce power consumption in the Arduino. To illustrate, we will demonstrate the use of the LowPower.h library and create a low-power humidity and temperature tracking system as a sample project.
We will also talk about the hardware modifications and software resources available to drastically reduce power consumption in the UNO, NANO, and the Pro-Mini. We will give particular focus to the Arduino UNO and summarize the power consumption of each. Then, we’ll discuss the use of sleep mode to reduce power consumption.
Hardware Modifications
The Arduino UNO is a fantastic platform that allows users to create and test countless devices. However, the design did not concern itself with power consumption. So if you’re running your project from a 12-V power source, it will draw over 50ma of current. By lowering the voltage to a 9-V battery, you can reduce the current draw to about 33ma. At that rate, you’ll be changing batteries often, something not very convenient in remote locations.
There are modifications you can make to the hardware to help reduce the current draw. For instance, given that an LED can draw as much as 2ma, you might consider removing them altogether.
Also, powering the attached devices through an I/O port allows you to turn off the device when it’s not in use. For the sample project, I’ve connected moisture sensors to I/O ports for power and only turn them on when I need to do readings. This lowers power consumption and increases the life of the moisture sensor.
However, some devices cannot be powered down when idle. For example, if you have an SD card reader it won’t allow you to turn it off then back on using an I/O port. Additionally, some devices like a Real-Time Clock (RTC) need to remain powered on, and you must be aware of the current limitation of an I/O port (approximately 40 ma).
The Arduino uses a linear voltage regulator that is effective but not very efficient. Since the on-board power regulator is inefficient, it becomes easier to bypass. A linear regulator takes any additional voltage and dissipates it as heat. So by running your UNO with a 9-V battery, the regulator basically turns the extra 4V into heat. This wastes about 44% of the power as heat, even more, if you use a 12-V supply.
The device shown below is a DROK DC 4.5-24 Step-down Buck Converter, which is considerably more efficient than the linear regulator used on the UNO. The circuit should work with any step-down buck converter that has a 5V and or 3.3V output. It has an additional advantage because you can disable the power output by simply grounding the EN (enable) yellow.
In the diagram, the black wire is common ground, and the red is the input voltage. This particular unit works up to 24V (depending on your buck converter), the green one is the voltage output. Another nice feature is it is also capable of 3.3V. Bypassing the onboard voltage regulator drops the current from 52ma down to about 23ma at 5V.
Note that the output goes to the 5V plug from the buck converter. If you decide to run the Arduino on 3.3V, use a 5V connection also. When you bypass the power plug,, you are also bypassing the reverse polarity diode, so be certain you properly orient the wires.
Arduino Low Power Library
Humidity and temperature monitors are primarily designed for outside use, requiring it to be extremely power-conscious. The processors used in Arduinos are designed to take advantage of many power-saving techniques. Since periodic sampling is typically used, the LowPower Arduino library can be configured to disable certain processes and even shut down the processor as needed to save a considerable amount of power.
Here are the results of adding the Low Power Arduino Library to the Blink program. These are from identical UNOs running at 12V using the onboard regulator, and 12V into the buck converter.
Current Draw without Low-Power Software | Current Draw with Low-Power Software |
12V using onboard voltage regulator = 50 ma | 12V using onboard voltage regulator = 45 ma |
5V Buck Converter = 27 ma | 5V Buck Converter = 25 ma |
3.3V Buck Converter = 10 ma | 3.3V Buck Converter = 8 ma |
To demonstrate the power-saving capabilities, the blink program was modified, replacing the two delay()
functions with the LowPower library functions. Below shows the function, LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF);
, By replacing the delay()
function, it accomplished quite a bit.
#include <LowPower.h>
void setup() { pinMode(LED_BUILTIN, OUTPUT);
}
void loop() { digitalWrite(LED_BUILTIN, HIGH); LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF); digitalWrite(LED_BUILTIN, LOW); LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF); }
The first argument, SLEEP_2S, put the processor to sleep for two seconds. To free up the processor, the PWM signal is created on an external clock, which is disabled, turning off the ADC function saving additional power. The Brown Out Timer (BOT), used to ensure the proper voltage is provided, is also temporarily disabled.
Sleep duration can be set to 15 ms, 30 ms, 60 ms, 120 ms, 250 ms, 500 ms, 1 Second, 2 seconds, 4 seconds, or 8 seconds. There are methods of increasing the sleep duration well beyond 8 seconds, as shown later in this article.
There are numerous other methods available to control power consumption by the ATmega328P. For simplicity, we will use the LowPower.powerDown()
method.
Low-Power Temperature and Humidity Tracking
Components needed:
For the Temperature and Humidity project, we will utilize the real-time clock to add the date and time to all the data we will save on the EEPROM.
The AT24C256 comes pre-programmed from the factory with a hexadecimal address of 0x50.
EEPROM
As mentioned, we will use an Electrically Erasable Programmable Read-Only Memory (EEPROM) to store our data. Alternatively, we could use an SD card reader. However, an SD card reader requires nearly 30ma and would use more power than the entire project. The EEPROM is the best fit for storing data in remote locations. They are small, reliable, consume less than 3ma when writing to it, inexpensive, and, most importantly, non-volatile. The addressing scheme allows up to 8 devices on an I2C Bus.
The EEPROM pinout:
- Pin1 = Address0
- Pin2 = Address1
- Pin3 = Address2
- Pin4 = Gnd
- Pin5 = SDA
- Pin6 = SCL
- Pin7 = Write Protect
- Pin8 = VCC power
The AT24C256 comes pre-programmed from the factory with a hexadecimal I2C address of 0x5 with three additional addressing pins that can optionally be changed, A0, A1, and A2.
With three additional binary inputs (A0, A1, A2), you can create addresses for up to 8 additional EEPROMS on an I2C network. Since we will only be using a single chip for storage, we will set the last 3 (A0, A1, A2) address bits low. Grouping the bits into two hexadecimal pieces, we end up with an address of 0x50. Note the low order bit is the Read Write bit R/W. Since we want to write to the device, the R/W bit will be set low.
The SCL and SDA pins will connect to A4-SDA, and A5-SDA on Arduino UNO, NANO and PRO-MINI.
The program to track humidity and temperature is actually three separate programs. One is used to track and record data, the second is to read the data, the last is used to clear the data from the EEPROM.
The first is based on the part of the program found on the Arduino IDE program in Files — Examples — EEPROM — eeprom_update. To this very basic example, we add the code needed for the Real-Time Clock, the humidity and temperature monitor, the sampling routines, and the LowPower functions.
Writing the Code to Record Data
// Code For storing DHT Temp and Humidity to EEPROM
#include <DHT.h>
#include <LowPower.h>
#include <Wire.h>
#include <EEPROM.h>
#include <RTClib.h>
RTC_DS1307 rtc;
#define Led 13
#define DHTPIN 5 //#define DHTTYPE DHT11 #define DHTTYPE DHT22 DHT dht(DHTPIN, DHTTYPE);
int address = 0;
void setup() { Serial.begin(9600); if (! rtc.begin()) { Serial.println("Couldn't find RTC"); while (1); } //uncomment the next line to set date,then comment it out and recompile program //rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); if (!rtc.isrunning()) { Serial.println("RTC Not running!"); } dht.begin(); Wire.begin();
} // End setup() void loop() { delay(500); DateTime now = rtc.now(); float h = dht.readHumidity(); delay(500); float t = dht.readTemperature(); delay(500); float f = dht.readTemperature(true); delay(500); EEPROM.update(address, (now.month())); address++; delay(500); EEPROM.update(address,(now.day())); address++; delay(500); EEPROM.update(address,(now.hour())); address++; delay(500); EEPROM.update(address,(now.minute())); address++; delay(500); EEPROM.update(address,h); address++; delay(500); EEPROM.update(address,f); address++; delay(500);
//Included to allow you to view your output Serial.print("Humidity:" ); Serial.println(h); Serial.print("Temperature:"); Serial.println(f); Serial.print("Time "); Serial.print(now.hour()); Serial.print(":"); Serial.println(now.minute()); delay(1000); // Timer delays about 15 minutes between readings for (int i = 0; i < 100; i++) { LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); }
}
The #include statements load the required DHT sensor libraries, LowPower Library, Wire.h for the I2C bus, the EEPROM module, and the Real-Time Clock library. The DHTPIN is connected to pin 5; you need to uncomment the line that defines which device you’re using, DHT 22, in this example.
In the setup()
function, the serial port is enabled, and the Real-Time clock functionality is confirmed. Take note of the line //rtc.adjust(DateTime(F(DATE), F(TIME)));
, as this is used to set the RTC against the clock your computer is using. It must be un-commented once then uploaded to set the clock. Once you have reset the clock, insert the comment again and recompile the code. The final two lines in setup enable the DHT sensor and turn on the I2C Bus.
In the loop()
function after the delay()
, the first instruction is to get the time from the RTC with a call to rtc.how()
. The next two reads are from the DHT library using the dht.readHumidity(), dht.readTemperature()
functions, that store the output in variables “h” and “t” respectively. The delays that come after the read functions are added because they have a slight delay when they read. The function dht.readTemperature(true)
saves the temperature in Fahrenheit and stores it in “f”. If you prefer to use Celsius, change the variable to “t”, to replace the “f” EEPROM.update(address,f);
.
The variable address declared initially as an int “0” is used to address the first storage location in the EEPROM address 0. The syntax for writing to the EEPROM is EEPROM.update(address,(value)
. Using EEPROM.update
will first verify and write only if the data is different from what may have already been written at that address earlier. This prevents writing the same data to the location; if the data hasn’t changed, don’t rewrite it. Since you only have a finite number of times you can write to a location, this helps the device’s longevity. Memory cells can be written to approximately 100,000 times.
After writing to the first address “0”, the address is incremented, and the next cell can be written to “1”. Notice that we are writing exactly 6 chunks of data for each sampling.
The LowPower function has a maximum limit of eight seconds. We set up a for loop to loop 100 times through the LowPower stage giving us a delay of a little over 13 minutes. The times can be increased or decreased to account for whatever sampling time you want to use.
Writing the Code to Read the Data
#include <EEPROM.h>
int i = 0;
int address = 0;
byte value; void setup() { Serial.begin(9600); while (!Serial) { }
}
void loop() { value = EEPROM.read(address); while (value > 1) { for (i = 0; i < 6; i++) { Serial.print("Date-"); Serial.print(value); Serial.print("/"); address++; value = EEPROM.read(address); delay(50); Serial.println(value); address++; value = EEPROM.read(address); delay(50); Serial.print("Hours:"); Serial.print(value); address++; value = EEPROM.read(address); delay(50); Serial.print(" Minutes:"); Serial.println(value); address++; value = EEPROM.read(address); delay(50); Serial.print("Humidity:"); Serial.print(value); Serial.println("%"); address++; value = EEPROM.read(address); delay(50); Serial.print("Temperature:"); Serial.print(value); Serial.println(" Degrees Fahrenheit"); address++; Serial.println("\n"); break; } break; }
}
After breaking down the first program, this one is pretty basic. The function used to enter data was EEPROM.update()
. Now, we will use the function EEPROM.read()
. In the void loop()
, we set up a while loop based on the contents at address 0 of the EEPROM. The first piece of data entered is the month, which should be anything from 1-12. Be sure that your RTC is running; if it isn’t, the while loop would never start. The while loop will run until it reaches the last cell written to.
The for loop was configured to read six records. That’s how many entries are made each time the data capturing program runs. If you wanted to increase the amount of data captured, you would first modify the data capturing program by adding anything else you want to be recorded. Then increase the number on the if statement to account for the extra.
Writing the Program to Erase the EEPROM
#include <EEPROM.h> void setup() { // initialize the LED pin as an output. pinMode(13, OUTPUT); for (int i = 0 ; i < EEPROM.length() ; i++) { EEPROM.write(i, 0); } // turn the LED on when we're done digitalWrite(13, HIGH);
} void loop() { }
The last piece of the project is the Erase program. This is in File-Example-EEPROM-eeprom_clear
.
With reference to the LED digitalWrite(13, HIGH);
, the LED is turned on to indicate that the EEPROM has been completely erased. Run this program before starting any new sampling.
Power Consumption
- UNO: 3.3 Vdc applied to the 5V lead = 17.1 ma in sleep mode, 26.9 ma running
- Nano 3.3 V, 11.1 ma in sleep mode, 16.9 ma running
- Pro-Mini: 3.3 Vdc 8Mhz Clock, in sleep mode 6.5 ma, 12.2 ma running
Any of these examples could be powered with a lithium battery and charged by a small solar panel.