Deepsleep on the ESP8266 - Powersaving for Battery Operation

ESP8266 deepsleep

When you plan to operate your ESP8266 with batteries you can use the deepsleep functionality of the ESP. This will enable powersaving and a longer livecycle of your batteries.

What you get here

On this page you will find some insights and code snippets in context of the ESP8266 deepsleep functionality.

  • Deepsleep basics
  • How to call deepsleep
  • Maximum deepsleep interval
  • Necessary hardware connection
  • Insights to the reset reason codes
  • How to use even longer deepsleeps intervals

The information is very specific to the ESP8266 - the ESP32 will differ.

Deepsleep Basics

Main reason to put the ESP8266 into deepsleep is the reduced power consumption. A ESP8266 will consume just 20µA in deepsleep. But keep in mind, that this figure is only for the ESP8266 itself. A LED, a voltage regulator or any other circuit can draw power even the ESP8266 is in deepsleep. When you plan the longest battery operation with an ESP8266 - you should use the ESP as barebone - not on a makerfriendly board like the NodeMCU or Wemos D1.

How to Call Deepsleep

When you came here you might already know: To put a ESP8266 into deepsleep you have to call

 ESP.deepSleep(sleepIntervalSeconds * 1000000UL); // Sleep for x seconds https://github.com/esp8266/Arduino/blob/master/cores/esp8266/Esp.h#L97
 delay(100);

To make the call of deepSleep reliable you need the delay(100).

The Maximum Deepsleep Interval

Since ESP8266 Core 2.4.1 the parameter for ESP.deepSleep() is a 64bit unsigned variable. This means the former limit of "~72 minutes" is not valid anymore. Unfortunately even if it is a 64bit variable, you can't use it's full potential. To get the exact maximum deepsleep interval you can call the function ESP.deepSleepMax(). It will return the microseconds in a 64bit unsigned variable. The maximum will range around 200 minutes (3h 20min) . If you exceed the limit it is very likely that the ESP8266 doesn't awake any more. As the Arduino Print.h can't print 64bit variables out of the box, you could either write a dedicated function to print 64bit or just use fractions of the returned value.

You can print ESP.deepSleepMax() in a more human readable seconds or minutes format with this snippet:

uint32_t deepSleepMaxSeconds = ESP.deepSleepMax() / 1000000; // returns an uint64_t see https://github.com/esp8266/Arduino/blob/master/cores/esp8266/Esp.cpp#L135
Serial.printf("max deep sleep s: %d\n", deepSleepMaxSeconds);
Serial.printf("max deep sleep m: %d\n", deepSleepMaxSeconds / 60);

Connection RST - GPIO16

To make deepsleep working you must connect RST with IO16. Several resources recommend to use a resistor. The proposed values range between 360 and 1K. This should also avoid the need of disconnecting the wire for the upload. Most of my experiments I'm doing with a NodeMCU. I'm getting the best results when I replace the resistor with a rectifier i.e. I connect the RST to IO16 with a rectifier:

  RST o--->|---o IO16

This gives me stable deepsleep wakeup and IDE Upload without disconnecting or reset/flash button presses.

Reset Reason Codes: What caused the ESP8266 to reset?

If you want to know why the ESP8266 has resetted, you can use ESP.getResetReason(). It will print the reason in clear text. If you want to use the reset reason as numeric value you can read update a structure rst_info. Following snippet in the setup() will show you the reset reason and exception cause:

Serial.println(ESP.getResetReason());
rst_info *resetInfo;
resetInfo = ESP.getResetInfoPtr();
Serial.print(F("reason=")); Serial.println(resetInfo->reason);
Serial.print(F("exccause=")); Serial.println(resetInfo->exccause);

The possible reasons are:

enum rst_reason {
  REASON_DEFAULT_RST = 0,      /* normal startup by power on */
  REASON_WDT_RST = 1,          /* hardware watch dog reset */
  REASON_EXCEPTION_RST = 2,    /* exception reset, GPIO status won’t change */
  REASON_SOFT_WDT_RST = 3,     /* software watch dog reset, GPIO status won’t change */
  REASON_SOFT_RESTART = 4,     /* software restart ,system_restart , GPIO status won’t change */
  REASON_DEEP_SLEEP_AWAKE = 5, /* wake up from deep-sleep */
  REASON_EXT_SYS_RST = 6       /* external system reset */
}

By the way if you don't use any other ESP library (like WiFi.h for example) you must at least include the user_interface.h

#include <user_interface.h> // just in case no other ESP library was included

Distinguish Reason Codes: Deepsleep vs. User Button press

A frequently asked use case is how to get a deepsleep interval together with an wakeup button or sensor/trigger. The basic answer is to let the user/sensor reset the ESP8266.

If you need to distinguish on startup whether the reset was done due to deepsleep awake or the button, you can wire the button to pulldown EN. EN (sometimes labeled as IO7, CH_PD or CH_EN) has for usual pullup (10K - 12K)  to enable the ESP8266. When you connect EN to GND you cause a Power Down - when you release the button the ESP8266 will perfom a power on Reset. This reset method (EN to GND) was described in an older version of the Espressif technical reference.

Hint: If you use rtcUserMemory it can get lost during the EN - power down - reset.

Deepsleep longer than deepSleepMax()

If you need a deepsleep longer as ESP.deepSleepMax() you could split your deepsleeps in smaller intervals and just do nothing when the total length of your deepsleep is not reached. As all SRAM variables will get lost after the reset, you can use the RTC user memory to store a counter. This example shows how to get a long sleep interval of 12h broken down in 4 parts of 3 hours:

/* 
    Example how to use longer Deepsleeps than 3h
    with the ESP8266

    Connection RST o--->|---0 IO16 with resistor or rectifier necessary
*/

void setup() {
  Serial.begin(115200);
  delay(500);  // wait for Serial
  Serial.println(F("\r\nESP DeepSleep RTC"));
  Serial.println(ESP.getResetReason());

  uint32_t counter = 0;
  ESP.rtcUserMemoryRead(0, &counter, sizeof(counter));    // read counter from RTC user memory
  if (counter >= 4) {
    counter = 0;                                          // after power up the value can have a random number
    performAction();
  }
  counter++;                                              // increase counter for next time
  Serial.printf("counter=%d\n", counter);
  ESP.rtcUserMemoryWrite(0, &counter, sizeof(counter));   // write counter to RTC user memory
  
  // ESP.deepSleep(30 * 1000000ULL);                      // sleep (test interval of 30 seconds)
  ESP.deepSleep(3 * 60 * 60 * 1000000ULL);                // sleep 3 hours
  delay(100);
}

void loop() {
}

void performAction() {
  // put here anything you need to perform in the long interval
  Serial.println("Perform Action");
}
//

RTC user memory will survive a reset, but can get corrupted on power loss. I.e. you must validate your variables if they have a reasonable range. In the example above there is just a simple check if the value of counter is >= 4. When you need several variables follow the IDE Example ESP8266/RTCUserMemory and use the CRC32 validation.

(*) Disclosure: Some of the links above are affiliate links, meaning, at no additional cost to you I will earn a (little) comission if you click through and make a purchase. I only recommend products I own myself and I'm convinced they are useful for other makers.

History

First upload: 2023-11-xx | Version: 2024-03-22