ESP8266 Deep Sleep Cycle Times and Power Consumption With WiFi Off

[@eyal’s post]

Following up on the esp8266 talk this evening, here are the timings that I see.

My esp8266 app starts 75ms after wakeup, WiFi is up 40ms later and deep sleep is requested at 320ms (from wakeup). The DSO shows power usage drops at around 550ms so it takes a long time (230ms) to enter deep sleep. Here is a DSO capture. Note the total power usage is just 41mAs for the full cycle, with idle WiFi current of 80mA and short peaks up to 288mA. The trigger is the reset (=wakeup) signal.

[later] Following up after the discussions of the 11/May meeting.

I am testing a setup where I do not make any WiFi connections (as Ken plans to do). First, here is what it looks like:

The full cycle takes 245ms. There is now a short pink (ch3) showing. This is the actual app, which is lua:

start_time = tmr.now()
-- node.setcpufreq(node.CPU160MHZ)
out_pin = 2     -- gpio4
gpio.mode (out_pin, gpio.OUTPUT)
gpio.write(out_pin, gpio.HIGH)

magic_pin = 1   -- gpio5
abort = false

if magic_pin and magic_pin >= 0 then
        gpio.mode (magic_pin, gpio.INPUT, gpio.PULLUP)
        if 0 == gpio.read (magic_pin) then
                print (("aborting by magic, start time %.6f"):format(start_time/1000))
                abort = true
        end
end

if not abort then
        gpio.write(out_pin, gpio.LOW)
        wifi.sta.autoconnect(0)
        wifi.sleeptype(wifi.MODEM_SLEEP)
        node.dsleep (500000, 4) -- 100ms, no WiFi
end

Note the ‘gpio.write()’ which sets a pin HIGH at the start and LOW at the end. This is what ch3 is showing. So the bulk of the time is not in the app at all.

I measured 145ms from the trigger to ch3 rising. It then takes about 100ms to enter deep sleep.

However, the more interesting point is that I also print the time (if magic_pin is low) and this shows 70ms, not 145 as the DSO shows. I do not understand why this is so, unless the timer does not start at power up.

I also do not know what the high peak (at 400-500mA) is, when the WiFi is supposed to be disabled. Generally, I expected the stable power usage to be more like 30-40mA rather than the 70-80 that I see (same as when WiFi is on).
I probably am not turning it off correctly.

BTW, if I do a node.restart() (not node.dsleep()) then it shows 267ms.

I now tested a bare esp8266 (rather than the NodeMCU module used here). Similar timing but less power used, peaking at below 330mA. Still unexplained though.

[later] I think I managed to turn off wifi somewhat with the two wifi.*() calls. Note that ch3 is inverted here.
70mA is still too high, the wifi engine is not fully turned off.

[end of @eyal’s post]

Very interesting @eyal but unlikely to be found under the heading “Can anyone run Electronics Wednesday this week?” I’ve been looking for this sort of information elsewhere and I haven’t found this level of detail. It deserves it’s own topic.

I’m having some trouble understanding though. For example I can’t understand where the 145 is in “However, the more interesting point is that I also print the time (if magic_pin is low) and this shows 70ms, not 145 as the DSO shows. I do not understand why this is so, unless the timer does not start at power up.”

I can suggest a reason the timer does not start at power up though. According to Boot Process · esp8266/esp8266-wiki Wiki · GitHub it has a BIOS. It “Boots into Espressif code in IROM0” . This must check the pins etc to decide what to do next e.g. go into flash mode, then it “Loads SPI ROM data” which means the Espressif SDK libraries from one address and your program code which resides in another Memory Map · esp8266/esp8266-wiki Wiki · GitHub. Finally it starts executing user code. I guess that the timer you are reading is started just after the “Loads SPI ROM” data step before starting user code.

I don’t understand this line either
“if I do a node.restart() (not node.dsleep()) then it shows 267ms.”
but perhaps a different startup time is due to not having to do the “Loads SPI ROM data” step in one version.

You mention that the radio is not fully off. There is an interesting Arduino deep sleep library at IoT/ESPDailyTask at master · HarringayMakerSpace/IoT · GitHub . I’m wondering whether the radio is fully off in their version. They also use the RTC timer to keep reasonably accurate time across deep sleeps and according to GitHub - liangchen-harold/nodelua Lua is interpreted rather than compiled which would suggest that Arduino, being compiled, should have a faster cycle time.

Looking forward to some more help interpreting this data at the next MHV meet up.

@Ken_Taylor, thank you for moving the discussion to here. Please use the tripple-back-tick to keep the lua code properly formatted.

RE 145ms) looking at the DSO trace it has the pink bleep at 145ms from the trigger, this is when the program starts:

out_pin = 2     -- gpio4
gpio.mode (out_pin, gpio.OUTPUT)
gpio.write(out_pin, gpio.HIGH)

The program prints 70 at the same moment, so there is a 75ms discrepancy.

RE 267ms) I do not understand what you do not understand :-). I am just saying that it takes the module much longer to restart than to wakeup. I expected that memory copy of the flash is lost during deep sleep (hence the need to use RTC memory to maintain data across sleeps).

RE arduino IDE) I had a quick look and I do not see that code doing more that the same dsleep() API with WAKE_RF_DEFAULT or WAKE_RF_DISABLED. I do see that wifi.NULL_MODE is available but wifi.setmode() refuses (by design) to accept it.

Searching around it seems that with wifi off power usage should drop to 20mA, a significant saving.

RE lua being interpreted) I think that this is a negligible overhead. Each lua module is written in C and it is where the app spends most of the time (as are the start and termination phases). However I did rewrite my app in C (both noos and rtos) with almost identical results. I should retest these and record the stats.

cheers

I see a power spike at 145ms and then “the pink bleep” 3-4 ms later. If that pink bleep is your code it suggests the radio starts before the code.

But why aren’t you seeing that?

I do not understand why waking up from deep sleep is faster than a software restart but then maybe it isn’t and I’m not understanding.

I was also wondering if you can put the SPI chip select pin on your trace to see when the ESP8266 is loading from the SPI memory?

RE spying SPI: we can try this. I will try to remember to bring my DSO and a few esp8266 to test tomorrow.

Had another stab. pulled the latest dev branch and flashed an esp-12 afresh. I now see a difference. the current drops to 20mA after the system setup and later runs at around 15mA as it shuts down.

However it takes 210ms for the program to start, and my prints sees it as 135ms (so the same 75ms discrepancy is still there). The full cycle is about 340ms.

Note the 10mA/div used now (was 20 before). The cycle energy usage is just over 8mAs which is good.

start_time = tmr.now()
out_pin = 5     -- gpio14
gpio.mode (out_pin, gpio.OUTPUT)
gpio.write(out_pin, gpio.LOW)

magic_pin = 1   -- gpio5
abort = false

if magic_pin and magic_pin >= 0 then
        gpio.mode (magic_pin, gpio.INPUT, gpio.PULLUP)
        if 0 == gpio.read (magic_pin) then
                print (("aborting by magic, start time %.6f"):format(start_time/1000))
                abort = true
        end
end

if not abort then
        gpio.write(out_pin, gpio.HIGH)
        wifi.sta.autoconnect(0)
        node.dsleep (500000, 4) -- no WiFi
end

However, this one does not connect properly, looks like some bug? Flashing an older image does connect…
[later] Got it to connect. Updated the program to dynamically run with/without WiFi.

No WiFi

Looking at the program itself (when the pink trace is low):

You can see that the current rises part way through the program.

With WiFi:

The program now

local start_time = tmr.now()
local out_pin = 5     -- gpio14
gpio.mode (out_pin, gpio.OUTPUT)
gpio.write(out_pin, gpio.LOW)

local magic_pin = 1   -- gpio5
local abort = false

if magic_pin and magic_pin >= 0 then
        gpio.mode (magic_pin, gpio.INPUT, gpio.PULLUP)
        if 0 == gpio.read (magic_pin) then
                print (("aborting by magic, start time %.6f"):format(start_time/1000))
                abort = true
        end
end

if not abort then
        local wifi_pin = 7      -- gpio13
        gpio.mode (wifi_pin, gpio.INPUT, gpio.PULLUP)
        local wifi_mode = 4     -- 4=WiFi off
        if 0 == gpio.read (wifi_pin) then
                wifi_mode = 1   -- 1=WiFi on
        else
                wifi.sta.autoconnect(0)
                wifi.sleeptype(wifi.MODEM_SLEEP)
                wifi.setmode(wifi.NULLMODE)
        end
        print (("wifi = %d"):format(wifi_mode))

        gpio.write(out_pin, gpio.HIGH)
        node.dsleep (500000, wifi_mode)
end

Interestingly, when WiFi is on it runs faster. WiFi off looks like it is initially turned on then turned off. But the current stays high at around 70mA.

Checking the current following a ‘file.format()’ (with no WiFi set up) shows a rather stable 13.5mA.

If I set up a connection to my AP, and no program is running, I see peaks up to 155mA and two levels of operation, 15mA and 66mA

Hi everyone…in my case now i see a difference. the current drops to 20mA after the system setup and later runs at around 15mA as it shuts down.However it takes 210ms for the program to start, and my prints sees
it as 135ms (so the same 75ms discrepancy is still there). The full cycle is about 340ms.

pcb cost

1 Like

Some deep sleep cycle, with WiFi off on wake up, comparison testing from last night.


Interpretation
The yellow trace was captured when waking from deep sleep under control of an Arduino program with WiFi off then going back to sleep using a nodeMCU development board. With a paint program the image was overlaid with the trace, coloured red from the image above under the text “Had another stab” which shows a deep sleep cycle with WiFi off under control of a Lua app. The cycle time for the red trace is longer. To help with interpretation, the red trace was cut in two places and slid leftwards by the distance of the solid red line.

The time axis is identically scaled for both traces. Vertically a division can be roughly read as 10 mA but only roughly. Current was measured in both case as the voltage drop across a 1 ohm resistor. In the nodeMCU/Arduino case power was supplied from a 3.7V lipo battery direct to the 3.3 volt output pin on the nodeMCU and for the Lua device current was measured from the 5 volt supply to a 3.3v regulator.

Observations
The Lua version is pulling 12mA in deep sleep but the nodeMCU device is pulling about 30mA in deep sleep suggesting the other circuitry on the nodeMCU is pulling most of the deep sleep current.

User code execution time is tiny compared to overheads, about 2 ms out of a 55ms cycle time.

Shutdown time is about 20ms in both cases.

Startup time is variable.

Speculation
The first 8ms is ESP8266 ROM code executing,

The next variable length period is stuff loading from the SPI flash with the variation coming from the amount that has to be loaded.

The second spot where the Lua version takes longer is the Lua application called Init loading from SPI flash.

Implications
A nodeMCU board is impractical for long life battery powered devices.

Timing from wake up to execute is long and imprecise.

Deep sleep cycles of more than about 2Hz are not very practical and power savings from deep sleeping rapidly decline above that frequency.

Consuming 12mA in deep sleep mode without waking up, a ESP8266 powered by a 3200mAh lithium battery can last about 11 days. Espressif claims deep sleep i.e. RTC mode can be as low as 60 uA see Enginursday: Deep Sleep Adventures - News - SparkFun Electronics. At that rate it could last 6 years.

Ignoring power consumed in deep sleep, waking up with WiFi off, doing little else, and at approximately 8mAs cycle energy it can do about 1,440,000 cycles of deep sleep which at 2hz would take about 8.5 days. At once per minute this extends to about 2.5 years.

With a fixed IP address and waking up with WiFi on required about 41mAs which is about 5 times more energy than a wake up cycle with WiFi disabled. With DHCP I find it takes about 3 seconds to get on the network requiring a cycle time 5-6 times greater than a fixed IP which would increase cycle energy much more again.

@eyal I’d like to get something that would last a year on a single battery, waking up once per minute. Do you know what the minimum achievable current in deep sleep is?

1 Like

This is not the case, where do you see this? In the lua graph the deep sleep current was essentially at zero (too low to even see).

My measurement shows a much lower deep sleep current when using a “proper” LDO. The ESP uses under 20uA and the LDO (MCP1700) uses about 1.5uA. Let’s call it total 20uA. An active cycle (with WiFi) uses about 40mAs, so the sleep current can be ignored (60*1.5uA → 0.09mAs).

40mAs/minute is 2400mAs/hour or 0.67mAh/hour. A 3400mAh battery should run (ideally) for 3400/0.67 = 5075 hours, or 211 days.

As I recall your (arduino) test, it used less power (we need to capture it again with “area” measurement turned on). We can also use one of my bare ESPs (with a good LDO) rather than the NodeMCU and see what we get.

[later] I measured the deep sleep current (in the ground wire from the LDO to the esp) using the uCurrent and two meters and they all agree that it is about 19uA.

We saw that a trivial cycle that does nothing already uses around 13mAs. If we can turn off the wifi completely then maybe this can be reduced a bit more.

Let us assume some optimistic numbers. 15mAs for a no wifi cycle (3 in a minute) and 30mAs for a wifi cycle (1 a minute). The energy budget is then 3*15+30=75mAs/minute (dleep current is negligible) = 4500mAs/hour = 1.25mAh/hour. A 3400mAh battery will (ideally) run for 3400/1.25=2720h or 113 days. So you need to reduce the number of wakeups if you want to last a year. Or use a larger battery. Or a solar panel (could last practically forever).

I took some careful traces with the dummy program (see at the bottom). It does practically nothing and I use it to understand how the ESP behaves.

A reminder:
ch4 (navy blue) is the current as measured from the LDO to the ESP.
ch3 (pink?) is the program toggling a GPIO14 to signal start(low) and end(high).
ch2 (sky blue, at the top) is the reset pin which I forgot to hide.

This is when it wakes up and does nothing. The current drops to 13mA and stays there.

Now the same program but it requests a 500ms deep sleep rather than stops (if GPIO5 is not grounded). Note how the current goes up to 60mA before dropping into deep sleep. The part leading to the program start is the same as above, sitting on 20mA as the program starts. The wakeup is still with wifi off.

Now the same program but it requests a wake up with wifi on (when GPIO13 is grounded). The power is now up to 70mA and there is also a large peak just before the program starts. Interestingly the program starts earlier here, 165ms rather than 232ms after the wakeup. Entering deep sleep seems to take the same amount of time so the whole cycle is shorter.

Here is the same trace with a larger 50mV/div. The peak reaches about 200mA. It runs at about 70mA while entering deep sleep. Note the 15mAs energy used by this do-nothing program.

The program, in case anyone cares:

local start_time = tmr.now()
local out_pin = 5     -- gpio14
gpio.mode (out_pin, gpio.OUTPUT)
gpio.write(out_pin, gpio.LOW)

local magic_pin = 1   -- gpio5
local abort = false

if magic_pin and magic_pin >= 0 then
        gpio.mode (magic_pin, gpio.INPUT, gpio.PULLUP)
        if 0 == gpio.read (magic_pin) then
                print (("aborting by magic, start time %.6f"):format(start_time/1000))
                abort = true
        end
end

if not abort then
        local wifi_pin = 7      -- gpio13
        local sleep_mode, sleep_type
        gpio.mode (wifi_pin, gpio.INPUT, gpio.PULLUP)

        if 0 == gpio.read (wifi_pin) then
                sleep_mode = 1  -- 1=WiFi on
                sleep_type = "on"
                wifi.sta.autoconnect(1)
                wifi.sleeptype(wifi.NONE_SLEEP)
                wifi.setmode(wifi.STATION)      -- was STATION_MODE
        else
                sleep_mode = 4  -- 4=WiFi off
                sleep_type = "off"
                wifi.sta.autoconnect(0)
                wifi.sleeptype(wifi.MODEM_SLEEP)
                wifi.setmode(wifi.NULLMODE)
        end
        print (("Wakeup WiFi is %s"):format(sleep_type))

        gpio.write(out_pin, gpio.HIGH)
        node.dsleep (500000, sleep_mode)
end

While I am capturing traces, here is what it looks like when I am in “peak” mode.

Ugly, right? I see 60mA above and below the signal with some ringing. Zooming in

What we see are bursts of peaks at 13us intervals. Initially I suspected my LDO but I get the same using 3.3v from three different TTL<->USB adaptors. It definitely comes from the ESP.

The background ripple is 400ns wide, so 2.5MHz, which is probably from the 5MSa/s of the DSO… Yep, when sampling at 10MSa/s the ripple is 5MHz.

One final note. I noticed that the start of the wakeup has an earlier event.

The reset signal it at the trigger point (one can just make the blue line on ch2). However the GPIO pin is powered up about 1.6ms earlier. This is expected, as everything should be properly active when the program wakes up.

Even more traces, now using a wmos D1 mini, fed 3.3v from the TTL<->USB adapter.

First, running with wifi off.

The program runs with 18mA (BY) and then drops to under 13mA when going to sleep.

Here is the same, but scaled to 100mA/div to allow the next comparo.

And now with wifi on.

We see a very high peak of 428mA and the program runs at 78mA (BY) (using 15mAs).

However, I see a rather large deep sleep current (around 700uA) so it may kill some the savings.
[correction] In a cleaner setup I measure 140uA. Very much higher than a naked ESP but manageable.

Now testing the Wemos D1 mini using arduino. Initially the timing.

ch4(blue) is the reset pin, so where the wakeup starts.
ch3(pink) is GPIO13 which the program blinks once, then leaves is LOW. When the ESP enters sleep if goes high, probably due to a default pullup.

So the timing is


  wakeup to program start   136ms
  program                     2ms
  program end to deep sleep 103ms
  total                     241ms

The timing is faster than the lua example which was 210ms to program start and 130ms to enter sleep.

ch4(sky blue) is the reset pin
ch3(pink) as before
ch2(navy blue) is the current

Here is a later trace with power measurement included

The sleep time is now 50ms but it is not essential. It is difficult to see the power usage as more than one cycle is included, here is a faster trace (and a cleaner (‘average’ capture) too)

Under 5mAs, which is very nice compared to 8mAs of lua. Reading the sensors and storing in RTC should be only a little extra.

Almost forgot, here is the sketch

/*
 * This sketch used to measure sleep/wakeup times
 */
#include <ESP8266WiFi.h>

void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  delay(1);
  digitalWrite(13, HIGH);
  delay(1);
  digitalWrite(13, LOW);
  ESP.deepSleep(50*1000,WAKE_RF_DISABLED); // 50ms sleep
}

void loop() {
  // sleeping so wont get here
}