Putting an ATmega328P into sleep
For a little project I want to take and send measurements
roughly about every 10 minutes. It will run on an ATmega328p
microcontroller powered by two AA batteries, so power consumption
will be an important issue. The ATmega328p is the heart of every
Arduino. But the Arduino with all its extra components draws a lot
of power. So it has be run “stand alone” (I’ll create another blog
post for that later). But the nice thing is, that you can easily
program it on an Arduino and then just pop out the chip from the
Arduino and put it onto your final circuit board.
But it’s not only the extra components of an Arduino which draws
unnecessary power, it’s also the constant “awake” state.
Therefore the chip has to be put into sleep mode and only wake up
every 10min to perform the measurements. In a typical Arduino sketch
you would use the delay
method to wait. Here’s a way to replace that
with a real sleep method.
The code was mostly taken from Wolles Elektronikkiste - Sleep Modes and Power Management.
An excellent resource if you want to know more about different microcontrollers,
sleep modes and their power consumptions. check it out!
#include <avr/wdt.h>
#include <avr/sleep.h>
// Taken from:
// https://wolles-elektronikkiste.de/en/sleep-modes-and-power-management
const unsigned int sleepInSec = 10u * 60u; // 10 min
void setup() {
sleepSetup();
}
void loop() {
// do something
// ...
sleep(sleepInSec);
}
void sleepSetup() {
cli();
wdt_reset();
WDTCSR |= (1<<WDCE) | (1<<WDE);
WDTCSR = (1<<WDIE) | (0<<WDE) | (1<<WDP3) | (1<<WDP0);
// 8s / interrupt, no system reset
sei();
}
void sleep(unsigned int timeInSec) {
unsigned int rounds = (unsigned int)(timeInSec / 8u);
for (unsigned int i=0; i<rounds; i++) {
wdt_reset();
set_sleep_mode(SLEEP_MODE_STANDBY);
sleep_mode();
}
}
ISR(WDT_vect){
}
Note: This is very approximate. It uses the ATmega328p’s maximum sleep time of 8 seconds,
and loops over it, so it will only work for sleep times > 8 sec and be only accurate for
multiples of 8. But for stuff like doing something every few minutes, that should be good
enough.
It needs some setup steps, wrapped in the sleepSetup
method, and then you can call
sleep(123)
instead of delay(123000)
.
By the way: Furthermore I tried to reduce the power consumption by running the chip
at 8 MHz and 3.3 V instead of 16 MHz. But unfortunately I couldn’t get the nRF24L01
component working in this setup. But surprisingly the ATmega328p seems to work fine with
3.3 V even at 16 MHz, although I read you should reduce the clock frequency when
running it on 3.3 V. But it’s a pity, as going down to 8 MHz would half the power
usage once more. Very roughly speaking it looks you’ll get a reduction in power
consumption to roughly 1/2 for using the ATmega328p stand-alone instead of in the
Arduino, another 1/2 by using 3.3 V instead of 5 V, another 1/10 by using sleep (standby)
and another 1/2 by using 8 MHz instead of 16 MHz. So in total 1/50 (16 MHz) to 1/100 (8 MHz)
compared to the Arduino.