The ESP32 Lite operates unstably in deep sleep mode

I have a bunch of devices: ESP32 Lite, AHT10, INA228, and a Li-Polymer battery. The board is configured for deep sleep: 5 minutes of deep sleep, 30 seconds of operation to send sensor data to Home Assistant. But for some reason, this deep sleep schedule isn’t being followed. The board might go into sleep for 10-20-30 minutes and run for 10 seconds, or it might go into deep sleep for 5 minutes as scheduled. What could be the cause?

esphome:
  name: esp32-ina228
  friendly_name: esp32-ina228

esp32:
  board: esp32dev
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "***"

ota:
  - platform: esphome
    password: "***"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: True

  manual_ip: 
    static_ip: 192.168.0.***
    gateway: 192.168.0.***
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp32-Ina228 Fallback Hotspot"
    password: "nPKuaKVNZPdZ"

captive_portal:

i2c: 
  sda: GPIO23 
  scl: GPIO19 
  scan: true 
  id: bus_a 

sensor:
  - platform: aht10
    temperature:
      name: "AHT10 Temperature"
      id: aht10_temp
    humidity:
      name: "AHT10 Humidity"
      id: aht10_hum
    update_interval: 10s

  - platform: ina2xx_i2c
    id: my_charge_sensor
    model: INA228
    address: 0x40
    shunt_resistance: 0.010 ohm
    max_current: 10 A
    adc_range: 0
    update_interval: 10s

    current:
      name: "INA228 Current"
      id: ina_current
      accuracy_decimals: 6

    bus_voltage:
      name: "INA228 Bus Voltage"
      id: ina_voltage

    power:
      name: "INA228 Power"
      id: ina_power

    charge:
      name: "INA228 Charge"
      id: ina_charge

    energy:
      name: "INA228 Energy"
      id: ina_energy

  - platform: template
    name: "Battery Level"
    id: battery_level
    unit_of_measurement: "%"
    icon: "mdi:battery"
    update_interval: 10s
    lambda: |-
      if (id(ina_voltage).state < 3.0) {
        return 0;
      } else if (id(ina_voltage).state > 4.2) {
        return 100;
      } else {
        return (id(ina_voltage).state - 3.0) * (100.0 / (4.2 - 3.0));
      }

    accuracy_decimals: 1

binary_sensor: 
  - platform: status 
    name: "Work?"  

deep_sleep: 
  id: deep_sleep_5min 
  run_duration: 30s 
  sleep_duration: 5min

No time to sleep… :wink:

How you see this? What do you get on esphome logs?
Is 30s enough for wake time?
Did you try debug component or esp_sleep_get_wakeup_cause ?

Sorry, I made a mistake. 5 minutes of deep sleep and 30 seconds of wakefulness

The thing is that to view the log through ESPHome, I connect the USB to the computer and then open the logs on the ESPHome website. But in this case, everything works reliably. 30 seconds is enough to wake up, connect to Wi-Fi, and retrieve data from the sensors.

What will this give?

So when it doesn’t? Only while powered from battery?

Little bit more info about your problem.
If you don’t have any logs, debugging is pure guesswork.

Yes, running on battery power alone does cause problems, even though the battery is new and charged, so I don’t think it’s the problem. Furthermore, if disable deep sleep and keep all the same modules, it works just as reliably on battery power. However, I think that even if disable deep sleep and leave the data transfer intervals longer, the battery charge will still be significantly short.

Okay, I’ll try adding this and check what the output will be.

Just few days ago one forum member confirmed his board failing at battery voltage <4V (board with battery connector). Bad design or bad components.

Also I remember that Esp32 is especially sensitive for voltage during sleep/wakeup cycles.
If you have voltage meter, you could measure Esp 3.3V-pin voltage when battery powered.

My first thought is that 30 seconds isn’t very long. If your wi-fi channel is clear then it’s probably OK most of the time … but if there’s radio frequency interference, or a neighbour streaming a 4K movie on the same (or an adjacent) channel … your ESP32 might simply not be getting enough clear air to make the wi-fi connection and report values before it powers off (goes back to sleep again). This would certainly look as though it had slept for multiple cycles.

By “ESPHome website” do you mean the “ESPHome Builder” Add-on for Home Assistant ? And if so, did you select “Wirelessly”, “Plug into this computer”, or “Plug into computer running ESPHome Dashboard” ?

Yes, that’s correct. And to download, I selected “Plug into this computer”

Regarding this issue, I don’t know what to say. And how can I check it?
Perhaps if I increase the board’s battery life, it might have enough time to connect to my network, but in that case, it will also drain more battery power?

I added this feature to my firmware. It returns the number 4 in response, which, according to the instructions, indicates a timer wakeup. However, it can still go into deep sleep for 5 minutes, or even 10-20 minutes.

How you know it didn’t wake up? Maybe it just didn’t get connected.
Did you measure 3v3 pin voltage?

I measured the voltage 3v3, it always shows on the multimeter. I also set up a binary sensor that displays the ESP32 status and the on/off time:

binary_sensor:

  • platform: status
    name: “Works?”

I haven’t actually used this method myself … instead I installed ESPHome on my PC and have been using minicom to see the logs - particularly after the ttyACM0 disappears when the ESP32 goes to sleep.

I just tried “Plug into this computer” and its LOG does give the results you are wanting before wifi connects.

That is the $64,00 question*. There are plenty of tools which show the signal strength, but I have yet to see anything which conveniently shows how busy the channel is. Unfortunately this varies quite a lot over time, so what looks clear now might be filled with a streamed 8K video in 10 minutes time :frowning_face: I have contemplated setting up a RasPi running a promiscuous scanner just to monitor the actual traffic - but it could only monitor 1 channel at a time.

Some things you might like to research further:

  • ESPHome wi-fi component provides on_connect trigger, wifi.connected condition and wifi.enable action
  • some people minimise their awake time by doing all the work of checking sensors, etc, and finally calling deep_sleep - all in the on:boot; routine !

FWIW, this is my common code which includes much of my research from my own wi-fi problems:

###########################################################
#
# Start with the Wi-fi connection
#
#  As at Sept 2025 ESPHome still DOES NOT SUPPORT WI-FI ROAMING,
#    and will sometimes connect to a WAP with lower signal strength.
#
#  When an ESPHome device boots up it scans for the allowable 
#    network (SSID) with highest signal strength. Having made 
#    the connection it does not check for a stronger signal, 
#    even though a stronger signal may later become available. 
#  Some users reported that, even worse, if the scan finds multiple WAPs on the same WLAN
#    it connects to the WAP (BSSID) with lowest channel number 
#    - not the one with strongest signal strength !  
#
###########################################################

wifi:
  ssid:     $wifi_ssid
  password: $wifi_password
  manual_ip:
    static_ip: 192.168.1.${deviceIP}
    gateway: 192.168.1.1
    subnet: 255.255.255.0
#  fast_connect: True
  output_power: 10.5      # reduce output power MAY improve Wi-fi in study
##### add some debugging when compiled on my development PC
  on_connect:
    then:
      lambda: |-
        ESP_LOGI("TEST", "#####     >>>>>>>>>>> WIFI CONNECT");
  on_disconnect:
    then:
      lambda: |-
        ESP_LOGI("TEST", "#####     >>>>>>>>>>> WIFI DISCONNECT");

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "$devicename Fallback Hotspot"
    password: !secret wifi_ap_password

# allow updating firmware OTA (Over The Air = by wi-fi)
ota:
  platform: esphome
  password: !secret esphome_ota_password

#captive_portal:

# this displays the device's status at http:IP_Address
web_server:
  port: 80

api:
  encryption:
    key: !secret esphome_api_encryption
##### add some debugging when compiled on my development PC
  on_client_connected:
    - logger.log:
        format: "#####     >>>>>>>>>>  API Client '%s' connected with IP %s"
        args: ["client_info.c_str()", "client_address.c_str()"]
  on_client_disconnected:
    - logger.log: "#####     >>>>>>>>>> API client disconnected!"


###########################################################
#
#   Add some common sensors - wi-fi signal strength, 
#       uptime, ESPHome version & compile date/time
#
###########################################################

sensor:
  - platform: wifi_signal
    name: $devicename Wifi signal
    update_interval: $update_interval_network     # how often to report wifi signal strength

  - platform: template
    id: wifi_channel
    name: $devicename "Wifi connected channel"
    entity_category: "diagnostic"

  # human readable uptime sensor output to the text sensor 
  - platform: uptime
    id: uptime_sensor
    name: $devicename Uptime
    type: seconds
    update_interval: $update_interval_network     # how often to report
#    on_raw_value:

text_sensor:
  - platform: version
    name: $devicename ESPHome Version
    hide_timestamp: False

  - platform: template
    id: wifi_wap_loc
    name: $devicename Wifi connected WAP
    entity_category: "diagnostic"
#    on_value:
#      then:
#        - lambda: |-
#            ESP_LOGD("main", "The new value of wifi_wap_loc  is %s", x.c_str());


 # this section from https://github.com/esphome/feature-requests/issues/731

  - platform: wifi_info
#    ip_address:
#      name: $devicename ESP IP Address
#      update_interval: 360s  # We use static IPs everywhere, but using 'never' results in the sensor having no value
#    scan_results:
#      name: $devicename ESP Latest Scan Results
#      update_interval: 10s
    bssid:
      id: esp_connected_bssid
      name: $devicename Wifi Connected BSSID
      on_value:
        then:
          # determine a friendly name indicating which WAP we connected to
          - lambda: |-
              ESP_LOGD("main", "The current ESP Connected BSSID is %s", x.c_str());

              if ( x == "A0:36:BC:0E:29:38" ) {
                id(wifi_wap_loc).publish_state("LivingRoom");    // ASUS RT-AX55    192.168.1.1
                id(wifi_channel).publish_state(8);
              } else if ( x == "30:5A:3A:C5:B4:20" ) {
                id(wifi_wap_loc).publish_state("Laundry");       // ASUS RT-68U     192.168.1.2
                id(wifi_channel).publish_state(3);
              } else if ( x == "5C:E9:31:1C:C1:69") {
                id(wifi_wap_loc).publish_state("Bedroom");       // TP-Link WPA8631  192.168.1.3
                id(wifi_channel).publish_state(11);
              } else if ( x == "64:66:B3:ED:08:C4" ) {
                id( wifi_wap_loc ).publish_state("Study");       // TP-Link WR743    192.168.1.4
                id(wifi_channel).publish_state(13);
              } else {
                id(wifi_wap_loc).publish_state("Unknown");
                id(wifi_channel).publish_state(-99);
              };
              // std::string val = id(wifi_wap_loc).state;
              // ESP_LOGI("main", "Value of my sensor: %s", val.c_str());

time:
  - platform: sntp
    id: sntp_time
    update_interval: "120h"         # I guess this is to poll the SNTP server ?

footnote: * that’s an expression from an old USA TV game show

My power banks and also old USB chargers I use for power supply shut down in deep sleep mode because the circuit doesn’t draw enough current. How is the battery connected to the system?
If the supply voltage drops too low, the ESP won’t wake up. It might be right on the edge in your case, which would explain the fluctuating wake-up times.
Have you tried testing it with a lab power supply? If it works with a reliable power supply, then the problem is with the battery.