Chapter 5: Arduino and AVR Sleep Modes

The AVR microcontrollers at the heart of most Arduino boards (ATmega328P on Uno/Nano, ATmega32U4 on Leonardo, ATtiny85) have a sleep system that predates the ESP32 by decades but is well-understood, reliable, and capable of very low quiescent current — down to 0.1 µA in power-down mode. This chapter covers the AVR sleep hierarchy and how to use it from Arduino code.


5.1 AVR Sleep Mode Hierarchy

Mode CPU ADC Timers SPI/UART/I2C Ext. Interrupts Typical Current (3.3 V)
Idle Off On On On Yes 1–5 mA
ADC Noise Reduction Off On Timer2 only Off Yes ~1 mA
Power-save Off Off Timer2 only Off Yes ~1 µA + Timer2
Power-down Off Off Off Off Yes (async) 0.1–1 µA
Standby Off Off Off Off Yes ~0.1 µA

For battery-powered projects where the AVR only needs to do something periodically, power-down is the target mode. The microcontroller draws under 1 µA and can only be woken by:


5.2 Watchdog Timer as Wake Source

The watchdog timer (WDT) can generate an interrupt at intervals from 16 ms to 8 s. This makes it the primary wake source for duty-cycle applications (e.g., take a sensor reading every 8 seconds).

The AVR avr/sleep.h and avr/wdt.h headers provide direct access to these modes from Arduino sketches:

#include <avr/sleep.h>
#include <avr/wdt.h>

volatile bool wdt_fired = false;

ISR(WDT_vect) { wdt_fired = true; }  // watchdog interrupt handler

void sleepSeconds(uint8_t wdp) {
    // wdp: WDTO_1S, WDTO_2S, WDTO_4S, WDTO_8S (from avr/wdt.h)
    noInterrupts();
    MCUSR &= ~(1 << WDRF);           // clear watchdog reset flag
    WDTCSR = (1 << WDCE) | (1 << WDE);
    WDTCSR = (1 << WDIE) | wdp;      // interrupt mode, not reset
    interrupts();
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sleep_cpu();                       // actually sleep here
    sleep_disable();
}

void loop() {
    // ... take measurement, transmit ...
    sleepSeconds(WDTO_8S);            // sleep ~8 seconds
}

The full example with sensor reading and serial output is in code/avr_sleep_demo.ino.


5.3 Disabling Unused Peripherals

Even in power-down mode, certain peripherals can add unwanted current. On the ATmega328P:

Brown-out Detection (BOD): The BOD circuit monitors supply voltage and resets the MCU if it drops too low. It draws ~20 µA continuously. Disable it for lowest power:

// Disable BOD before sleep (must be done in timed sequence)
MCUCR |= (1 << BODS) | (1 << BODSE);
MCUCR &= ~(1 << BODSE);
sleep_cpu();

ADC: The ADC draws ~300 µA when enabled. Disable it before sleep:

ADCSRA &= ~(1 << ADEN);  // disable ADC
// ... sleep ...
ADCSRA |= (1 << ADEN);   // re-enable before any analogRead()

Power Reduction Register (PRR): Clocks entire peripheral blocks off. Disabling SPI, USART, TWI, and Timer1 saves ~0.5–1 mA at active voltage:

PRR = (1 << PRTWI) | (1 << PRTIM1) | (1 << PRSPI) | (1 << PRUSART0);

5.4 GPIO Leakage

Just as on the ESP32, floating GPIO pins cause leakage current on AVR. Every unconnected pin in input mode with no pull-up enabled can leak through internal protection diodes. The fix: configure all unused pins as output low, or enable the internal pull-up.

// In setup(): make all unused pins output low
for (int i = 0; i < 20; i++) {
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);
}

5.5 ATtiny: Going Further

The ATtiny85 and its family members are optimized for minimal power consumption. The ATtiny85 in power-down mode at 1.8 V draws about 0.1 µA — one tenth of the ATmega328P. For fixed-function sensor nodes where the Arduino IDE convenience is not needed, an ATtiny with a single job (e.g., pulse counting, temperature sensing) and a power-down sleep loop can run on a coin cell for years.

Key differences from ATmega:


5.6 Estimating AVR Battery Life

Using the same duty-cycle formula from Chapter 4:

Example: ATmega328P reading a DHT22 sensor every 60 seconds, transmitting via a radio module.

I_avg = (15 mA × 0.25 s + 0.001 mA × 59.75 s) / 60 s
      = (3.75 + 0.06) / 60
      = 0.0635 mA

On 3× AA NiMH (2000 mAh at 1.2 V × 3 = 3.6 V, ~70% usable = 1400 mAh):

Runtime = 1400 mAh / 0.0635 mA ≈ 22,047 hours ≈ 918 days ≈ 2.5 years

← Chapter 4: ESP32 Deep Sleep and Wake Sources Table of Contents Chapter 6: Embedded Peripheral Management →