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.