Skip to content

IOT: Dynamic Deep Sleep Configuration for ESP8266 and ESP32 Boards

By Sebastian Günther

Posted in Iot, Esp8266, Home_assistant, Esp_home

Especially when running your ESP boards on battery, conserving energy is an important aspect. ESPHome controlled boards can be configured for deep sleep, in its most simple form two values for sleep duration and run duration. Yet there are some subtleties to regard: Ensure the run time is appropriate for making WIFI connections, activate all connected sensors, and send data e.g. via MQTT. Once configured, the deep sleep behavior repeats. What will you do if you need to change it? Do you get the sensor, connect it via USB, and upload a new program? Do you hope for an OTA update during the limited time that the sensor is online?

The goal of this article is to show how to configure deep sleep for an ESP8266 board statically and how to define sleep duration dynamically with the help of Home Assistant.

The technical context of this article is Home Assistant v2024.10.4 and ESPHome v2024.10.3, but it should work with newer versions as well.

Static Deep Sleep Configuration

The deep sleep configuration happens entirely in ESP Home. Essentially, you need to configure the deep sleep options in ESP Home, connect the WAKE/GPIO16 pin with the RST pin, and reset the board once.

Note: Interestingly, the ESP8266 has three different types of sleep modes: modem, light, and deep. In ESPHome, we only have the deep sleep component - a feature request for light deep sleep is still open.

The default deep sleep configuration is a compact configuration stanza detailing the time spent in deep sleep and the run time. Its value can be of any time unit like seconds, minutes or hours, and looks like this:

deep_sleep:
  run_duration: 2min
  sleep_duration: 28min

Before compiling this code, be sure to connect the GPIO16 pin with the RST pin - otherwise the board does not wake up. Check the pin layout of your board, for example the D1 mini or the NodeMCU Kit.

Finally upload the new code and press the reset button once on your device. In the log files, you should see this:

[19:33:59][C][deep_sleep:020]: Setting up Deep Sleep...
[19:33:59][C][deep_sleep:023]:   Sleep Duration: 180000 ms
[19:33:59][C][deep_sleep:026]:   Run Duration: 1620000 ms

Ensure the board goes correctly to sleep mode and awakens after the scheduled time. This is all you need for the most basic deep sleep configuration with fixed sleep cycles.

Dynamic Deep Sleep Configuration

Without any further modifications, the values stay as they are defined, and you only can change them by uploading a new firmware. To make them dynamically reconfigurable, the basic idea is to set the value via MqTT: Define a topic that communicates the deep sleep values, and configure the ESP board to listen to these topics and update its values accordingly. The board, upon emerging from deep sleep, checks and applies the values from the MQTT broker. This involves configuration in ESP Home and in Home Assistant.

EspHome Configuration

First, add the default MQTT configuration to your ESP board, like this one:

mqtt:
  broker: "192.168.3.178"
  port: 8883
  username: "USERNAME"
  password: "PASSWORD"

Next, we need to subscribe to the MQTT topic and implement a behavior. The following example shows how to disable deep sleep entirely: By listening to the topic esp/ota_mode for the payload ON.

mqtt:
 # ...
 on_message:
   - topic: esp/ota_mode
     payload: 'ON'
     then:
       - deep_sleep.prevent: default_deep_sleep

Upload this new code you ESP board, and head over to Home Assistant for the other part of the configuration.

HomeAssistant Configuration

In HomeAssistant, an MQTT subscriber needs to be defined in the sensor section. This definition needs to include the topic to listen to, the unit of measurement, and a then action that processes the value send via MQTT.

The complete configuration is this:

sensor:
- platform: mqtt_subscribe
  name: "Configure Sleep Time"
  id: custom_sleep_time
  unit_of_measurement: m
  accuracy_decimals: 0
  topic: esp/sleep_time
  on_value:
    then:
     - lambda: |-
        id(default_deep_sleep).set_sleep_duration(id(custom_sleep_time).state);

This then/lambda action might read intimidating the first time, but in essence, you are determining the ID of the deep sleep configuration, and then call the function set_sleep_duration() with the state of the configured topic.

Interestingly, this behavior is entirely defined in Home Assistant, no further ESP Home code is necessary.

Testing

With both configurations completed, reset your board once take a look at the log output. You shoud see that the MQTT topic is processed, and the deep sleep duration value is customized:

[19:49:22][C][mqtt_subscribe.sensor:028]: MQTT Subscribe 'Configure Sleep Time'
[19:49:22][C][mqtt_subscribe.sensor:028]:   State Class: ''
[19:49:22][C][mqtt_subscribe.sensor:028]:   Unit of Measurement: 'm'
[19:49:22][C][mqtt_subscribe.sensor:028]:   Accuracy Decimals: 0
[19:49:22][C][mqtt_subscribe.sensor:029]:   Topic: esp/sleep_time
// ...
[19:49:22][C][mqtt:582]: MQTT Message Trigger:
[19:49:22][C][mqtt:583]:   Topic: 'esp/sleep_mode'
[19:49:22][C][mqtt:584]:   QoS: 0

Then, send messages to the MQTT topic and you should see that deep sleep is prevented. If this works, continue wit the last part of this article to integrate the deep sleep activation and deactivation as an easy-to-use Home Assistant Dashboard card.

Home Assistant: Setting and Sending Deep Sleep Values

In essence, Home Assistant is a system to process and record the state of its defined sensors, but does not have a state itself. However, there is the concept of helpers, entities that represent boolean values, strings or numbers and that have a state. We need to define such helpers for representing the deep sleep values, and then we need a mechanism to send the state of this as an MQTT message.

First, create a new Home Assistant entity of type input_number via "Configuration" => "Helpers", and name it esp_sleep_time. Then, go to the main dashboard and create a new card of type entity, and select the entity input_number.esp_sleep_time.This should result in the following YAML configuration:

type: button
tap_action:
  action: more-info
entity: input_number.esp_sleep_time
show_state: true
icon_height: 50px

On the dashboard it looks like this:

Second, we will create a button that sends the sleep time. Go to the dashboard and configure a new button. The button has a tap_action section, that will perform the call-service action with the MQTT service. You specify which topic and payload to send. The payload_template checks for the current value of the above defined helper input_number.esp_sleep_time. The complete configuration is as follows:

# note: add double brackets around the payload_template expression
type: button
tap_action:
  action: call-service
  service: mqtt.publish
  service_data:
    topic: esp/sleep_time
    payload_template: 'states(''input_number.esp_sleep_time'') | int'
  target: {}
icon: hass:lightbulb
name: ESP Send Sleep Time
entity: input_number.esp_sleep_time

The button looks like this:

The button is represented as a rather big card - that’s fine with me. If you want a more compact design, read this article about custom cards.

Again, time for some testing. Set the sleep time to 2 minutes, and then press the sleep time button. The ESPHome logs should how this message:

[19:50:51][D][sensor:120]: 'Configure Sleep Time': Sending state 11.00000 m with 0 decimals of accuracy
[19:50:51][VV][api.service:140]: send_sensor_state_response: SensorStateResponse {
  key: 1349173429
  state: 11
  missing_state: NO
}

Cool! We can now remotely control the deep sleep time for ESPHome sensors via dashboard cards in Home Assistant.

As a final step, I put the slider, the button to send the sleep time, and two additional button for enabling and disabling sleep together in one card:

Conclusion

To activate deep sleep in ESPHome sensors, you just add a few lines of yaml to its configuration. But then, these values stay until you flash the board with new code. In this article, you learned how to dynamically configure the sleep time for all sensors via Home Assistant: a) configure the sensor to listen to MQTT topics, b) define a Home Assistant helper entity that represents the deep sleep values, c) define an action that sends the current value of the deep sleep value to the sensor. You also saw how to add Home Assistant dashboard cards to control all this graphically.