Very DeepSleep and energy saving on ESP8266 – Part 1: Basics

  1. Intro: For the seasoned ESP user I might be over explaining things here, but apparently some people struggle with basic things around deepsleep, hence my perhaps somewhat overly long explanation.

For battery fed projects, low energy consumption is a must.
De ESP8266 can help with that, using DeepSleep.

Earlier I published a rather simple DeepSleep program, combined with MQTT, but from an energy conserving point of view  there are more things in setting up DeepSleep that can make a difference. There are also couple of other things to take into consideration.

The ideas here are not new. I have gotten some inspiration from Erik Bakke’s OppoverBakke site, even though there was a mistake in his code snippets.

I will just describe what I did to bring my ESP8266 to minimal energy consumption.

1. Pick the proper ESP8266 board.
You want your ESP board as lean as possible. No other power hungry other components such as FTDI chip or LEDs. This basically excludes most modules such as Wemos D1 mino or NodeMCU and only leaves the bare chips such as ESP-03, ESP-04 and ESP12 on a carrier board. The ESP12 is the easiest though as the ESP-03 and ESP-04 for instance do not have the GPIO16 or RST pin broken out to a side connector, but to a pad on the PCB.
The ESP8266-01 would be perfect after you remove the LED, but it does not have the DO/GPIO16 pin broken out. If you have young eyes and a steady hand you can try to solder a wire on the GPIO16 pin.

2. Pick the right voltage regulator.
If you are using 2×1.5V AA cells (unlikely) you may get by without a regulator, but if for instance you are using 3×1.2V NiCd-s or a 3.7 Lipo (that can go up as high as 4.2Volt), you are going to need a regulator. You dont want a regulator that needs a big input voltage in order to maintain 3.3Volt, so you will need a Low drop regulator, a so called ‘LDO’
Possible candidates are:

Type Quiescent current Drop Voltage Max Input Voltage Max Output Package
Torex XC6206 1uA ‎ 160mV@100mA 6Volt 200mA SOT23-3, SOT89,USP-6B
Analog Devices LTC3525-3.3 ‎ 7uA ‎ Step up converter ‎ 6Volt ‎ 60mA at 3.3V from a 1V Input, or 140mA at 3.3V from a 1.8V Input‎
Holtek HT7333 4uA 90mV@40mA 12V 250mA SOT89,TO92
Holtek HT7833 4uA 360mV@500mA 8.5V 500mA SOT89,TO92
ME6211 40uA 100mV@100mA 6.5V 300-500mA SOT23, SOT89
SPX3819M5 90uA 340mV@full load 16V 800mA SOT23-5, SOIC8, DFN8
Microchip MCP1700 1.6uA 178 mV@ 250 mA 6V 250mA SOT23, SOT89, TO92
Microchip MCP1825 120uA 210mV@ 500 mA 6V 500mA SOT23, SOT89, TO92
LT-1763 30uA 300 20V 500mA S8
TPS783xx 500nA 130-175 6V 150mA SOT5

The problem here is that many of the LDO’s that have a very low quiescent current, also have an output current that is on the edge of what the ESP8266 needs at startup.

The MCP1700 as well as the HT7333 are often advised,but they are really at the verge of what an ESP8266 needs in current using WiFi. if the 90mV drop of the HT7333 are important for you then consider adding a beefy capacitor.

In all honesty I would advise AGAINST using any regulator giving less than 300mA.

Apparently it is possible to use two HT7333 chips in parallel. The HT7833 seems a pretty good choice. It typically needs a 1uF elco on the input and a 2.2uF elco on the output to be stable. A higher value is ofcourse a good idea.

The ME6211 is used on the Wemos D1 mini board (which does not mean it is the best choice for low-drop/low Iq). Same goes for the SPX3819M5 on the Lolin NodeMCU.

3. Pick the right internet connection
Often we pick a DHCP connection by default. A DHCP connection usually  takes longer to establish than a static IP connection. It is therefore best to chose for the latter. With the ESP it is simply a matter of providing the following details (mind you, the IPnrs are just an example)

//We will use static ip
IPAddress ip(192, 168, 1, 35);// 
IPAddress dns(192.168.1.1);
IPAddress gateway(192, 168, 1, 1); //
IPAddress subnet(255, 255, 255, 0); // this one is pretty standard

and then make the connection with

WiFi.config( ip, dns, gateway, subnet );
WiFi.begin( WLAN_SSID, WLAN_PASSWD );

That is not the only thing we can do making the connection. We can try circumvent some oddities that the ESP8266 shows in connecting to the WiFi network:

the ESP8266 persists the network connection information to flash, and then reads it back when it next starts the WiFi function. It does this every time, and it takes at least 1.2 seconds (source Erik Bakke).

The chip does this even when you pass connection information to WiFi.begin(), like:

WiFi.begin( WLAN_SSID, WLAN_PASSWD );

This will actually load the connection information from flash, ignore it and use the values you specify instead, connect to the WiFi and then finally write your values back to flash.

We can disable that with the command WiFi.persistent( false )

There is more we can do to save time though. The Wifi.begin(SSID,PASSWORD) command does a scan for the proper network amongst the ones advertised. It is possible to supply a WiFi channel number and a BSSID to the WiFi.begin() command that takes away the need for a scan.
The ESP does not know the channel number or BSSID, so we need to read that info from the first connection, store it, and submit it at the next connection.
For that we need to replace the Wifi.begin(); statement with a piece of code that retrieves and manages the necessary information.
Fortunately there is such code available, and as I saw it in various places, I would not know  who to credit for it.
We begin with defining a structure:

A structure aka. struct is a way to group variables together, possibly of different types. One can have several instances of a declared structure. The variables within a structure are called members.

We define the following structure:

struct {
  uint32_t crc32;   // 4 bytes
  uint8_t channel;  // 1 byte,   5 in total
  uint8_t ap_mac[6];// 6 bytes, 11 in total
  uint8_t padding;  // 1 byte,  12 in total
} rtcData;

We use a CRC checksum to see if the data we use perhaps got corrupted. We use 1 bte for the channel and 6 bytes for the Accespoint information.
We add one superfluous byte to come to a multiple of 4. (here is more about storing data in rtc memory).

Then on connecting we make a few checks:

        1. is the CRC data correct? If not, make a normal connection.
        2. if after 100 tries it is not working, we reset the WiFi and go make a normal connection
        3. If  after 600 tries still no connection, we put the ESP8266 and will try later again.

We do need a routine to do the CRC-check, but that’s easily done.
Once we made the WiFi connection, we can connect to where we need to go: an MQTT broker or make an HTTP request for instance.

4. Make sure you do not connect to WiFi before you have to.
Making the connection at the right moment is important though. You do not want to waste energy on your 2.4 GHz signal when you are still reading sensors. As it is, the ESP8266   already has its radio on by default.
So the first thing we need to do upon wake up, is to switch off the radio.
We do that like this:

void setup() {   // disable WiFi, coming from DeepSleep, as we do not need it right away
  WiFi.mode( WIFI_OFF );
  WiFi.forceSleepBegin();
  delay( 1 );

Only once we have done that, we let the program do what it must, like reading sensors, and only then do we start thinking about making the  WiFi connection. Ofcourse we first need to switch the modem/radio on and we do that with:

//Switch Radio back On
WiFi.forceSleepWake();
delay( 1 );

That brings us almost to the end of what we need to do, software wise:
We Switch off the radio, read the sensors, switch on the radio, try make a quick connection and if necessary a regular connection, we then connected to where we want to go (Thingspeak,  MQTTbroker, whatever)and then it is finally time to put the ESP8266 into DeepSleep.

5.Putting the ESP8266 into deepsleep
The simplest form of a DeepSleep program is to do the stuff you need to do  (read yr sensors,make connections and then call:
ESP.deepSleep(SLEEPTIME)
There is however more that we can do. remember that I said that on wake up the radio is on by default? Well we can give the ESP8266 a wake up instruction so it knows what to do when waking up. We can already tell the ESP8266 that it should NOT switch on the radio by default. We do this with:

ESP.deepSleep(SLEEPTIME, WAKE_RF_DISABLED);

I know that in an earlier step we started with switching the radio off and strictly speaking that step would no longer be necessary once we use the wake up instruction, but I left it in anyway, just to make sure/for illustration. You could opt to leave it out though.
Truthfully there is a bit more superfluous code. e.g. it is not really necessary to switch off the WiFi immediately before DeepSleep.

Only one thing remains now: How long will we put the ESP8266 to sleep?

That ofcourse is a very personal decision, but lets see how long we CAN put the ESP to sleep.

The max deepsleep USED to be max 71 minutes. That was simply because the system_deep_sleep parameter was defined as an unsigned 32 bit integer. The max value of a 32bit unsigned integer is 0xFFFFFFFF, that is 71 min in usecs.
Since the 2.4.1 core, it is a 64 byte unsigned integer. That comes to 18446744073709551615 us or 5,124,095,576 hrs or about half a million year. Nobody wants to wait that long, so expressif seem to have capped that to about 3.5 hrs

This capped number is defined in the ESP.deepSleep(Max) parameter. So when I defined my deepsleep call as ESP.deepSleep(ESP.deepSleepMax(),WAKE_RF_DISABLED );, my ESP went to sleep for 3hrs and 9 min. I noticed though that this cycle tended to get shorter over time. Also if you define a specific period, e.g. 1 hour, with 3.6e9 (=3.6×109) then you will find that that might not be exactly an hour. in my case it was about 58 min.

Testing on a Wemos D1 mini. Connect RST pin with a jumper tp pin D0, as shown here

You will find a DeepSleep program as described above, here. The only thing you need to add other than your web credentials is some code to read your sensors and some code to send your data to where you want to store it (e.g. Thingspeak or yr own webserver, or a broker.
Upload the code and AFTER your upload, connect D0/gpio16 to the RST pin.

6. Think about your sensors
Your sensors use power as well, even when your ESP8266 is sleeping. Perhaps there is a sensor that does the same as another sensor, but uses less power. Consider feeding yr sensors from a gpio pin that you switch LOW when the sensor is not used. Keep in mind that some sensors need a warm up or settling time.  If you do that, ALWAYS make sure your sensor does not draw more than an ESP8266 pin can deliver (12mA), but chances are that if it does, it isn’t one that you should feed from a battery anyway. If you really have to or want to, you can opt for a FET to switch on the sensor. A very popular ‘sensor’ in battery fed projects is measuring the battery voltage. As the ADC of the ESP8266 only goes up to 1 Volt, some form of resistor voltage divider is necessary. This means a constant drain on the battery, so it is best to chose high values for the divider. If you use a 420k divider, you would be constantly draining 10uA on a full battery. That may not seem much, but it is also what your entire ESP8266 uses in deepsleep. If you are not using a power regulator, for instance when you are using 2×1.5Volt batteries, then it is better to use the internal measurement of the Vcc. Do that with:

ADC_MODE(ADC_VCC);
float vccVolt = ESP.getVcc()/1024.0F;

Some sensors –like the BME280– can be programmed to go to sleep in between measurements. For the BME280 this is the ‘Forced Mode’ if you then have a 1/min reading (Bosch calls this the ‘weather monitoring state’) the power consumption is 0.16uA. Using forced mode is also a good way to prevent/limit internal warming up of the chip, which would give skewed readings.

7. Do you need to send your values?
Rather than sending every measurement when made, you could opt to store results in RTC/SPIFFS memory and only send them say once a day,  or you could decide that if a new measurement result is say within 5% of the previous reading, there is no need to send it. If you are weary of RAM wear, you could add some FRAM, e.g. the MB85RS64V (8k, Operating current 1.5 mA. Standby current 10 μA).

8. Choose your batteries wisely
Once you get to uA levels, self discharge of the batteries becomes an important competing factor.
Nick Gammon states the following self discharge for a number of batteries:

Type Capacity mAH Discharge %/month Self discharge (uA)
CR1212 18 1 0.250
CR1620 68 1 0.950
CR2032 210 1 3
NiCD AAA 350 20 98
NiMH AAA 900 30 375
NiCd AA 1000 20 270
Alkaline AAA 1250 2 35
NiMH AA 2400 30 1000
Alkaline AA 2890 2 80
Li-Ion 4400 10 600

Connecting D0/GPIO16 to RST
In order for the wake up pulse to work, D0 needs to be connected to RST. The drawback of that is that you need to remove that link when you want to reflash the ESP8266. some people advise connecting the pins via a resistor. That has mixed results though. A better way is to use a Skottky diode -e.g a 1N5819– with the cathode to pin gpio16/D0 and the anode to the RST pin.

In a follow up article, I will publish some code that uses this deepsleep framework to read a sensor and send it to a database.

Part 1 -DeepSleep General
Part 2 -DeepSleep HTTP publishing
Part 3 -DeepSleep MQTT Publishing
Part 4 -DeepSleep MQTT Subscribing
Part 5 -Deepsleep ESP-NOW

2 thoughts on “Very DeepSleep and energy saving on ESP8266 – Part 1: Basics”

  1. I have issues with compiling the sketch you provided, errors like (error: ‘calculateCRC32’ was not declared in this scope) with platformIO. Which libraries should I use for mqtt, crc32.

    1. The code in this article only uses the library. ESP8266WiFi
      That comes with the esp8266 core and does not need separate install when using arduino IDE. I do not know platformIO well enough to know how it works there.
      The error you get though doesnt point to a library issue but looks like perhaps there is a misdeclaration coz of typo or something check yr lines 121-139

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.