# ESP32: how to keep a pin high during deep sleep (RTC GPIO pull-ups are too weak)?

I'm using an ESP32 in a battery-powered device. The ESP spends most of the time in deep-sleep to save power. The device also has some 5V-powered sensors which I'm feeding from a MCP1640 step-up converter, which is on during the brief active part, and powered off via its "ENABLE" pin during deep sleep. The relevant part of the schematic is:

simulate this circuit – Schematic created using CircuitLab

So, I need to hold pin #4 high during sleep. Due to sloppy testing of this part, I didn't know that the ESP32 shuts down its GPIOs during deep sleep, so pin #4 isn't kept high, and the sensors remain active. This drains the battery at a much faster-than-expected rate.

I'm wondering whether it's possible to workaround this blunder by a software patch (of course, it's easy to just add an external pull-up resistor to pin 4 - but I have a few devices on the field, which I would hate to have to travel a few hundred kilometres just to solder a resistor to! And the people around aren't tech-savvy to do this themselves; on the contrary, remote software patching is easy and well-tested).

For example, I tried the RTC's pull-up resistors:

gpio_num_t pin = (gpio_num_t) PIN_DISABLE_5V;
rtc_gpio_set_direction(pin, RTC_GPIO_MODE_INPUT_OUTUT);
rtc_gpio_pulldown_dis(pin);
rtc_gpio_pullup_en(pin);                     // set the pin as pull-up
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH,
ESP_PD_OPTION_ON);       // keep the RTC IO domain powered


Executing this just before entering deep sleep almost worked, but it turns out the weak pull-up is too weak: probably ~38k if it is a resistance, or 90µA if it's a current source. This is insufficient to drive the ULN2003's pin. I also tried RTC_GPIO_MODE_OUTPUT_ONLY + rtc_gpio_set_level() too, but this didn't seem to increase the drive capability. Another possibility is light-sleep, which I'd avoid as it was buggy some time ago and I don't want to get my devices bricked.

So my question really is: have I exhausted all software options (since the hardware ones also require burning a tankful of gasoline)?

• What are the power saving capabilities of the sensors themselves? Can the sensors be set to a low power state and will this reduce the power needs? – Richard Chambers Jan 15 '18 at 17:44
• That was my first idea, but it proved untenable - one of the sensors was hooked up on an input only pin (since I wanted to only read it), and the others are 3-pin analog transducers... – anrieff Jan 15 '18 at 18:47
• What is the range of Vbatt? Min voltage to max voltage? – Dwayne Reid Dec 20 '18 at 17:16
• Li-Ion, so 2.7 to 4.2V – anrieff Dec 20 '18 at 17:35

It's not clear that making the resistor change you propose will be effective at solving your problem.

The MCP160 requires that the enable input is taken below 20% of Vin to effectively switch the device off. If Vin is (say) 5 volts then 20% is 1.0 volts and, because the ULN2003 is a darlington, it may not reliably switch this low. However assuming it does switch lower than 1 volt then there is still the possibility that my guess about the value of Vbatt of 5 volts doesn't cover the low end of the range.

For instance if Vbatt is expected to work down to say 2 volts then you can only switch the MCP160 off if the enable pin is taken to less than 0.4 volts. This sounds all to close for my liking and I would recommend you think about the problem a bit more.

• The whole setup there is a blunder caused by an overzealous attempt to reduce partcount. However, because the ULN2003 is a very peculiar chip, the configuration shown actually works with a pull-up. E.g., if you replace the wire between ESP32 and ULN2003 with a resistance, the saturation voltage decreases with increasing resistance (480mV @ 10kΩ, 350mV @ 20kΩ). The lowest Vin is 3.0V, so the design will work, albeit not by a large margin. – anrieff Jan 15 '18 at 19:26
• I'm also concerned that the device won't turn on too. The ULN2003 leakage current (when off) is quite high, maybe 50 uA so it does look a bit of a mess. My advice. Nail all the problems and make one hit on the customers to fix all things. If you have to make more than two visits to 1 customer you'll be seen as unreliable. – Andy aka Jan 15 '18 at 20:04
• Oh, of course, the next revision will have this (and other) issues fixed reliably. But that will be more than a month in the future and I wanted a stopgap solution, since the data being collected is important (the number of visits isn't). – anrieff Jan 15 '18 at 20:46
• You seem like a nice bloke under a bit of pressure to get this right and I’m sorry my answer and comments appear doom and gloom. But you owe it to your business to minimise disruption to your customers because, well, it should be obvious. I’ve only seen barely a block diagram of a snapshot of your circuit so I would highly recommend you get your design hardware reviewed professionally. I’m not in that business btw but you might find it pays dividends. – Andy aka Jan 15 '18 at 23:22
• "Customer impression" is unimportant here - it's more of a research project and it's not really for a customer. Eventually, I'd want to become an EE professional and this project is a perfect learning opportunity. – anrieff Jan 16 '18 at 9:23

Well, sorry to revert my acceptance to the answer by Harry Svensson! And I shouldn't really be adding to a question that is 14 months old by now.

However I just found the perfect software solution, which would have saved the tankful of gasoline I eventually burned (sorry, nature!).

Instead of setting the pin to be pulled-up during deep sleep, you can use the pin hold feature to keep the pin to whatever state it was before sleep:

#include "soc/rtc_cntl_reg.h"
#include "soc/rtc.h"
#include "driver/rtc_io.h"
...
digitalWrite(pin, HIGH);
gpio_hold_en(pin);
...


That's all you need! The pin will be kept as a CMOS output, and strongly driven to HIGH or LOW, whatever you need.

I verified it with the following schematic:

simulate this circuit – Schematic created using CircuitLab

While running, the current measured is approx 110µA (as expected). During deep sleep, with the old code (in the question, that sets up a weak pull-up), the current falls to ~50µA (so the pull-up resistor value is in the 30k range). With the suggested code here, it stays 110µA.

• Drawing 110µA during sleep of a battery powered device is not a good thing. It might be better than the alternative of letting your load switch on, but it indicates a rather unfortunate circuit design. – Chris Stratton Mar 25 '19 at 19:46
• I agree. The very fact that a weak pull-up is insufficient is a very strong indicator that "something's wrong, pal" :) – anrieff Mar 26 '19 at 7:39

Here's two other links of people who have tried. fail, probably fail.

So it doesn't look promising with solving it in software, as you have tried. So right now there's 3 failures, you and the 2 links.

Proposed solutions:

• A) Burn a tankful of gasoline
• Pro: Every device you fix you'll know be functional correctly.
• Con: A lot of your time and money will be wasted on gas and driving.

• B) Fix 1 device nearby and make a tiny tutorial for those who got your devices, pay them to solder a pull up resistor, e.g. follow the tutorial. Yes, pay them because they will be doing some work, work that you failed to do during the testing stage of your product.

• Pro: This will be cheaper, and whoever that has the devices might think less of you professionally, but higher of you in terms of trust.
• Con: Some people... just fail with simple things, including soldering. So there's a chance that someone will just ruin their device and have to buy another one off you, which you will give away for free, which might be a huge loss.

• C) Do the same thing as B, but instead of telling whoever that got your devices to do the job, hire some random people who live nearby and can do it for you.

• Pro: Whoever that has the devices will feel that you're professional.
• Con: Whatever random person that solves your problem now knows how to properly mess with your instrument.

• D) Do the same thing as C, but use friends (who might live nearby) or some colleagues instead of some randoms.

• Pro: Whoever that has the devices will feel that you're professional.
• Con: Your friends will think you're unprofessional.

• E) Do what the user DoxyLover proposes, "Fix some units locally and ship them to your users, along with pre-paid return shipping labels so they can ship the originals back to you."

If I were you I'd go backwards, start with E. If that is not an option then continue with D, if you can't then go with C, if you can't then go with B and lastly go with A.

• One more option. Fix some units locally and ship them to your users, along with pre-paid return shipping labels so they can ship the originals back to you. – DoxyLover Jan 15 '18 at 17:56
• @DoxyLover Probably the best solution. – Harry Svensson Jan 15 '18 at 17:58
• Feel free to edit it into your answer. – DoxyLover Jan 15 '18 at 18:00
• Thanks, my general feeling was that I've trapped myself in a situation where the software can't really fix the hardware mess. Your answer confirms that. As already discussed with @Andy aka, this is more of a research project and "customer impression" is unimportant. Other constraints eliminate options E, D, C. I'll think more about it, but I'm strongly leaning towards A. – anrieff Jan 15 '18 at 20:50

The problem that everyone is missing is that the input current needed to keep the the ULN2003 active sort of defeats any attempt at saving power by putting the microcontroller into deep sleep.

The proper solution is to replace that section of ULN2003 with something that doesn't consume any current such as a MOSFET.

However, I'm wondering why the ULN2003 is in the circuit in that location in the first place. Why can't the microcontroller drive the EN pin on the DC-DC converter directly?

The most that you may have to add is a high-value pull-down resistor from EN to ground.

• "Why can't the microcontroller drive the EN pin on the DC-DC converter directly?" - because the EN pin's logic 1 is Vbatt, while the MCU output is 3.3V, so level shifting is required. – anrieff Dec 20 '18 at 6:56

It seems that the lib has evolved from @anrieff's answer at Mar.25.

I have implemented this function rather using the RTC APIs of ESP32. Of course this functionality is still intended for low power usages, so I'm using a MOSFET & ESP32 to control another power source.

The official examples are quite useful: https://github.com/espressif/esp-iot-solution/tree/master/examples

#include <driver/rtc_io.h>
#define SWITCH_PIN GPIO_NUM_4
#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5        /* Time ESP32 will go to sleep (in seconds) */

RTC_DATA_ATTR int pinState = 0;

void setup() {
Serial.begin(115200);

pinState = (pinState+1)%2; //flipping between on & off

rtc_gpio_init(SWITCH_PIN); //initialize the RTC GPIO port
rtc_gpio_set_direction(SWITCH_PIN, RTC_GPIO_MODE_OUTPUT_ONLY); //set the port to output only mode
rtc_gpio_hold_dis(SWITCH_PIN); //disable hold before setting the level

rtc_gpio_set_level(SWITCH_PIN, pinState); //set high/low

esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

//gpio_deep_sleep_hold_en();
rtc_gpio_hold_en(SWITCH_PIN); // enable hold for the RTC GPIO port

Serial.println("Going to sleep now");
Serial.flush();

esp_deep_sleep_start(); //sleep
}