In the last post I showed you how you can use cheap hardware to get a reasonable good power consumption test rig. In this post we are going to use this tool to improve the runtime of the ESPaper significantly. The short trial and error cycle possible with this tool will let us validate several ideas for improvement in only very little time.
The Test Subject – ESPaper Plus Kit
Last year we released the 2.9″ ESPaper Plus Kit to the maker community. It is our first near-end-user ready device, coming with an enclosure, batteries, programmer and USB cable. And everything packed up in a really nice box. The device has a built-in battery which complicates a lot of things. One being the safety requirements of our shipping providers.
Another new challenge for us was the new area for optimization. The ESP8266 Classic and Color Starter Kits were running only with a connected USB cable so energy consumption was not a primary concern. The ESPaper Plus on the other hand is supposed to run from batteries for many days without recharging. So eventually we had to figure out if there was room for improvement on the software side. And without spoiling the rest of the article I can say there was!
The ESPaper Plus Kit comes with a stock LiPo battery with 3.7V and 600mAh. We chose this capacity because of the limited space we had in the enclosure. A charging circuit and an LDO control charging the battery and bringing the voltage down to the ESP8266’s 3.3V for operation.
During the development of the ESPaper module we were concerned mostly about the sleeping current consumption of the device. But even back then a quick calculation showed that the current consumed during sleep phase was nearly negligible compared to the current consumed by the WiFi module during the awake phase.
The ESPaper Duty Cycle
Let’s have a look at a regular duty cycle of the ESPaper module. The device wakes up, connects to WiFi, fetches some data from a web server, updates the display and goes back to sleep. You can configure the duration until it wakes up next in the device settings. We usually use 20min intervals but that depends on your use case. Normally weather doesn’t change very quickly so 20min seem to be a good value.
Let’s further break down the awake phase. As a first step the device has to establish a connection with an WiFi access point on the physical level. Your WiFi credentials are validated. Then device and AP negotiate settings for the subsequent session. The device negotiates an IP with the router over the DHCP protocol. At this point we move one layer up in the network stack. The devices tries to establish a connection with the content service (e.g. Wunderground). In case we chose to use an encrypted channel our client negotiates encryption settings with the server.
Then we initiate a direct connection with the content service, usually by using the HTTP protocol. The server then sends a stream of bytes to our ESP8266, usually in a JSON representation. The parser on the client tries to get the interesting data out of that content. Using this information we draw text and symbols to the frame buffer residing in the ESP memory. Once we have drawn everything to the frame buffer the commit command transfers the pixels to the e-paper video memory. We disconnect from WiFi and send the device to sleep by defining the sleep duration.
Why do I explain this here in such a detail? Because the awake phase in total is the biggest factor in energy consumption. In order to save energy we have to understand this phase in detail.
“Baseline Code” is the name I gave to the setup from which I started to check the effects of the later described hacks. It is the espaper-client code I wrote for our upcoming espaper web application server. It uses best practices and no special tricks to fetch a JSON object from a secured (SSL) webserver over HTTPS. To get a good average I ran my monitoring tool 10 times and wrote down the forecasted runtime for the 600mAh battery. This average forecasted that the device would run about 34 days from one charge. Honestly I wouldn’t pay too much attention to the exact number. What has more significance is the relative change after applying the hack.
After implementing a hack I measured the change 10 times and reverted then back to the baseline. This way it was easy to identify the effect of a single change rather than just see the sum of a change.
Hack #1 – Statically define the ESPaper’s IP address
The first change I tried was to statically set the device’s IP address. Without additional code the device will use the Dynamic Host Configuration Protocol (DHCP) to get an IP address from the access point. This process is a negotiation between client and server. Several messages are exchanged before the the device knows its IP address:
While this is a very convenient thing to do it also takes time. And time in our case is energy. If you know for certain that you will never have two different devices using the same IP in your network you can define the IP at compile time. This will save you save you a lot of time while the device is online. Here’s how you can do that:
IPAddress ip(192, 168, 0, 51); IPAddress gateway(192, 168, 0, 1); IPAddress subnet(255, 255, 255, 0); IPAddress dns(8, 8, 8, 8); WiFi.begin(WIFI_SSID.c_str(), WIFI_PASS.c_str()); WiFi.config (ip, gateway, subnet, dns);
You define the ip, network gateway to the internet, the subnet segment and the name service. Then after connecting to WiFi you set this information to the WiFi object. Since the ESPaper now doesn’t have to negotiate the IP it can proceed with downloading data much faster than before. The average over 10 samples shows an improvement of 6.6 days longer battery runtime or 13% longer battery life compared to the baseline code!
One word of warning though: this like some of the following hacks come at a cost. By statically setting the IP at compile time you increase the chance of a conflict between two devices in your network. If you have a couple of devices with a static IP you are responsible to make sure that never two devices use the same address. Otherwise your devices will show strange behavior. Further you will have a hard time to find out what is going on. Basically you replace convenience and stability for a longer battery life.
Hack #2 – Avoid unnecessary storing and loading of WiFi settings
By default the WiFi object stores the WiFi credentials every time they are used to flash memory. By setting the WiFi.setPersistent(false) the credentials are only stored to flash if they are different than last time. This sounds actually like a good idea! Why the default is then to always persist the values? You can read more about this feature here.
You can do this by calling (see documentation):
However, the effect of this change is rather small and only improved the runtime by about half a day. Since I sampled only 10 times this might even be in the range of error. I still kept it here to show that sometimes many small improvements can have an impact together. And I think many of you might be interested that storing the WiFi credentials is the default behavior, even if you always initialize the WiFi module with a password stored elsewhere on flash. So why increase flash wear and waste valuable time to store something twice on flash, right?
Hack #3 – Reduce WiFi output power
Now this is also a code hack with a rather small impact. And I have to say I was surprised that it wasn’t bigger. We already established that the ESP8266 consumes most energy when it is awake. DUH! I also thought that most part of that comes from the WiFi module. So wouldn’t it be logical that reducing WiFi sending strength would reduce energy consumption? My measurements showed only a nearly insignificant improvement of not even a day (0.8d to precise). Why is that? Is it because the setting actually doesn’t work? Or because the WiFi protocol automatically adjusts the power output? At the moment I can’t tell for sure. Maybe you have an idea? Leave your explanation in the comments.
At the same time I also tried to fix the PHY mode to 802.11b. I hoped to limit the data rate to 11 Mbps and further reduce power. Here are the two lines I added to the code before starting WiFi:
Update: Ivan Grokhotkov wrote about this hack on Twitter:
Reducing TX power doesn’t help much in this case because the device spends most of the time receiving (listening). RX current of the 8266 (and 32) is relatively high, compared to some other WiFi chips, so the time spent in RX mode matters a lot.
— Ivan Grokhotkov (@i_grr) March 12, 2018
Hack #4 – Use Partial/ Fast Refresh with ePaper
Every ePaper module I worked with so far had different update characteristics: how fast can the update the screen? Do they allow to only update parts of the screen? How much control during the update cycle do they give to the driver? The GoodDisplay module we are using with the 2.9″ ESPaper gives a fair amount of control to the implementer of the display driver. By manipulating look up tables (LUT) you can speed up the refresh of the display content.
I added this feature a while back to the MiniGrafx library. It avoids to do several complete black/white refresh cycles and saves a lot of time with this. So why not activate this by default? If you are using only fast refresh mode the screen will deteriorate quickly. The screen will look more dirty with every refresh. In theory we could try to always use fast refresh and do a full update every few times to clean the screen properly. I haven’t done that yet since it complicates the code and it still is hard to predict when you need a full refresh.
So how much did we win by enabling the fast refresh? We gained about 3.4 days (or about 10%). Here is what you need to do to activate fast refresh mode:
while gfx is the MiniGrafx object. You can do this everywhere you like. For this test I ran the command in the setup() method.
Hack #5 – Turn off WiFi if you don’t need it
This should actually be a no brainer: only do the costly stuff if you actually have to, right? But what does this practically mean for your code? Every application has different needs when it comes to WiFi usage. Your application might have to connect to WiFi after wakeup. Or you might have to read out some sensors first before sending the data to a server. Similarly the application might have nothing left to do after the data has been exchanged with the server. Or a costly operation might still have to be completed before the device can go to sleep.
The ESPaper has to wake up, connect to WiFi, fetch data, update screen and go back to sleep. Originally I let the WiFi on the whole time. But when I started to analyze to analyze the energy consumption I also realized that only needed WiFi up to the moment the screen would be updated. Now this had to be tested! In the ESPaperParser class I added a forceSleepBegin() call to turn off WiFi:
Now hold your breath for the biggest revelation in this post yet! Adding this single line in the right spot added 9.4 days of battery life. This is a 29% improvement compared to the code without this line! So why do we have such a huge improvement? The commit() call alone takes about 3 seconds. Afterwards the device does some clean up on the SPIFFS file system and then goes to sleep. Every second without WiFi counts!
Summary and conclusion
I presented here five hacks to improve the time your ESPaper can run a battery. This could be helpful even for other devices than the ESPaper. In case you decide to apply some of these hacks please be aware that they come at the cost of quality or stability: setting a fix IP increases risk of network problems. Enabling the fast refresh mode has impact on the quality of the ePaper content. But I’m also very happy that the biggest improvement can be done without any side effects. Turning off WiFi when you don’t need it is a valuable trick up your sleeve for your ESP8266 and ESP32 devices driven by battery.
In this chart you can compare the 5 hacks and their impact on battery life. For each version I did 10 measurements with the ESP8266 Power Monitor. I also ran the test with all 5 hacks combined and the sum was that of the fix IP hack and the WiFi Off Before Commit hack combined. Not very surprisingly the fast refresh didn’t seem to make much of a difference.
I still have some more ideas I’d like to try in the future. What impact does encryption have on battery life? Would we save energy if we were to disable SSL encryption? After all encryption requires processing power and establishing a connection takes time. Do you also have ideas for how to improve battery run-time? Pleas leave a comment!