ESP8266/NodeMCU Deep Sleep

By April 11, 2016ESP8266, IoT

Sometimes, an IoT device will perform small chunks of work, and then will otherwise be idle. For instance, a weather station with sensors for temperature, pressure, and humidity (such as the BME280) may only need to take a reading every 15 minutes and broadcast to the cloud – something that requires 10 seconds to accomplish. If the device is powered by a battery, then you want it to enter a deep sleep state in between the work cycles in order to maximize battery life.

Sleeping NodeMCUBut, how would you force an ESP8266 to go to sleep, but wake up when it needs to? The solution is to combine the low-power state of the ESP8266 with the internal Real-Time Clock (RTC) and the device’s Reset pin. While in deep sleep mode, the processor stops and the RAM is powered down (saving precious battery life), but the RTC continues to function. At a certain time, an alarm in the RTC changes the state of a GPIO pin in order to trigger a device reset, which has the effect of waking the device up (albeit, from a full reset instead of resuming from where it left off).

This may sound like a complicated orchestration, but fortunately, the SDK provides an abstraction layer over this functionality so that you only need to call a function (and NodeMCU provides a Lua wrapper around this SDK functionality).

The first thing is that you need to connect GPIO16 to the RESET pin using a low-valued resistor, like 330 to 1k ohm (see Rudy’s description in the comments). GPIO16 belongs to the RTC module on the silicon, so that’s the only GPIO that the RTC can control directly while in low-power mode. The RESET pin is triggered by a low logic signal, so when the RTC’s alarm fires, it will transition GPIO16 from high to low in order to perform the reset (i.e., wake the device up).

Finally, you need code to actually put the device to sleep. Your start up scripts should go through the motions of connecting to Wi-Fi, collecting sample data (or whatever work the device is to do), and then when it comes time to sleep, execute the following:

The dsleep() function accepts microseconds as its parameter (1,000,000 microseconds = 1 second), and the multiplication performed inline here is just to make it clearer that we’re sleeping for 15 minutes total. With the current 1.5.1 SDK, the maximum value accepted is 4,294,967,295, which is about 71 minutes. Earlier SDK versions had a lower limit, closer to 35 minutes. One common way to extend this is to maintain a counter in NVM (i.e., Flash or RTC Memory), and increment the counter each time that the device wakes up. If it reaches a certain threshold, then do the work, otherwise go back to sleep.

Note: As of the time of this writing, the Master branch of the NodeMCU Firmware project in GitHub is based on SDK 1.4, while the Dev branch is based on 1.5.1. You must be aware of the underlying SDK that the firmware is based on, because that impacts the maximum deep sleep timer value that can be used.

Another use of deep sleep is to sleep until externally triggered. So, instead of using the internal RTC, your device may have an external RTC, or a button that the user must press, or some other mechanism that runs while the ESP8266 is asleep, and then triggers a device reset in much the same way (by transitioning the RESET pin from high to low).

In this case, you don’t need to connect GPIO16 to the RESET pin, and you don’t need to specify an alarm value when calling dsleep(). Or, rather, use a value of zero to specify that the ESP should sleep forever (until reset, that is):

This permits very long idle periods (hours, days, or weeks possible) so long as you have a reliable method of waking the device up by triggering the RESET pin.

 

The following two tabs change content below.
  • Craig Larson

    Jason, thanks for the blog. I successfully put my NodeMCU Dev board to sleep after driving a SSD1306 display via I2C connections. While active the two devices draw over 80 mA. In deep sleep the draw goes to 10.9 mA. I was expecting a lower draw. I’ve bypassed the regulator, I think, by supplying power directly to the 3v3 pin. But still 10.9 mA. While sleeping I disconnected the SSD1306 and the power draw is 10.4 mA so it is partly to blame. The SSD1306 is powered by a NodeMCU pin that is driven low before sleeping.

    Is there backward leakage through the NodeMCU regulator? There is no regulator diode showing on the schematic.

    • Jason Follas

      My answer is that I don’t know. It’s very possible, though, that providing 3v3 directly to the pin could also be feeding the USB Serial chip on the DevKit (I know the CP2102, for example, has the ability to be powered that way instead of bus powered).

  • Giovanni Avola

    Dear Jason, thanks a lot for your clear information. To record
    temperature data every a known interval the dsleep() function is perfect
    (connecting GPIO16 to the RESET). But I also need to acquire data from a
    rain gauge (that have a reed switch). But I don’t understant how I can
    do it. I want use reed input as external trigger, so when the rain
    activate the reed, it can reset the ESP, without wait the RTC set time.
    Even more obscure for me, the possibility to use RTC memory to store
    data. Rain data could be stored in the RTC memory and send when the ESP
    wake from deepsleep. Do you have same advice?

    • Jason Follas

      Are you using Deep Sleep in order to periodically wake up and report status regardless of whether it rained or not, and then additionally looking to wake up right away once rain is detected (when the reed switch is triggered)?

      If you only need to wake when the reed switch is tripped, then I’d probably incorporate that as a gate and not even power up or enable the ESP until the switch is closed.

      RTC memory is very small and will be lost if there is complete power failure. I’m sure it’s useful in some scenarios, but for NodeMCU, I prefer to write to NVM if I have to keep it around between sleep cycles, or just send data to the network right away.

      • Giovanni Avola

        Dear Jason,
        Thanks lot for your fast reply. I’m an agronomist and I’m really new in programming ESP, just simple sketch in Arduino ide. To acquire temperature and humidity data it is simple. To preserve power in a system powered with battery with deepsleep is also elementary. When I added a rain gain started the problems. I can acquire temp or hum data every 30 min without problems (with deep sleep), but I wish to collect rain data as soos as possible the rain drop. Someone wrote me to connect a “10K pull-up on RES along with 1K serie resistor between GPIO16 and RES will let you do an external reset from a reed switch connected between GND and RES”. Is it possible to acquire also the rain data (just not he trigger) fron the same or other GPIO? Right now I writing the sketch. No problem to count the tipping bucket, but when I add the line relative to ESP, the data sent are not representative. In other word, I miss a lot of tipping bucket count. Thanks in advance for help you give me. Giovanni

  • sriniketh

    Dear Jason,

    Thank you for the detailed explanation. I have ESP-8266-07 chip and i have nodeMCU version (“nodemcu_float_0.9.6-dev_20150704.bin”). I have tied the GPIO 16 pin and RST pin together and i included node.dsleep function with a value of 60 million (1 min). But for some reason it is not sleeping and resetting. Is it something to do with the firmware? or do i need to include some other commands in the program. kindly suggest your views.

  • Rudy

    “The first thing is that you need to connect GPIO16 to the RESET pin.”

    This is bad advice and people who are finding your post are following and experiencing problems when doing an external reset. What happens is the port is at a high state because GPIO16 is set as an output. A manual reset with a switch causes that port pin to be shorted to ground. That is not good for the chip. If the reset line is connected to to a different device, other than a switch, it would have to be stronger than the port pin in order to cause the desired reset.

    The proper thing to do is to connect GPIO16 to RESET through a low value resistor. Typically between 330 – 1K Ohms.