Halt deep sleep until all sensors report the data

Hello, I have been struggling for the past day to understand how to make sensors work in combination with deep sleep.
I have two sensors connected to my ESP32 - DHT11 for humidity and BMP280 for temperature and pressure. My goal is to keep the ESP awake for enough time to read and report the values from both sensors and go to sleep after that.
I noticed that especially DHT11 is very inconsistent with reporting values. Based on the logs, it takes from 3s up to 50s between BMP280 and DHT11 reading. So the run_time should be set to at least 1 minute in order to get the readings, but my goal is to enter deep sleep automatically as soon as all the sensors are done sending their data.

My guess is the wanted behavior could be achieved by setting up automation in combination with some scripts. Maybe it could be done with on_value automation on sensor?

Since I have very little experience with ESPHome and setting up automations, I would really appreciate any help on this topic. Has anyone managed to achieve something similar?

Thank you!

The following is my current config:

esphome:
  name: "esp32-inside"

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:

ota:


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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: !secret ap_ssid
    password: !secret ap_password

captive_portal:
    
i2c:
  sda: 21
  scl: 22
  scan: true
  id: bus_a

sensor:
  - platform: dht
    model: DHT11
    pin: 15
    humidity:
      name: "Humidity DHT"
    update_interval: 60s

  - platform: bmp280
    temperature:
      name: "Temperature BMP"
      oversampling: 16x
      filters:
      - offset: -2.3
    pressure:
      name: "Pressure BMP"
    address: 0x76
    update_interval: 60s
    
# Update interval has no effect when deep sleep is in use and it is longer than run_duration. Esphome always sends the readings on boot.

deep_sleep:
  id: deep_sleep_1
  run_duration: 60s
  sleep_duration: 60s

mqtt:
  broker: !secret mqtt_broker
  port: 1883
  username: !secret mqtt_username
  password: !secret mqtt_password
  on_message:
    - topic: esp32-inside/ota_mode
      payload: 'ON'
      then:
        - deep_sleep.prevent: deep_sleep_1
        - logger.log: "OTA mode enabled - deep sleep mode disabled"
    - topic: esp32-inside/sleep_mode
      payload: 'ON'
      then:
        - deep_sleep.enter: deep_sleep_1
        - logger.log: "deep sleep mode enabled"

This does what you want.

You’ll need to dig into it as it’s quite long.

In summary you turn off auto updates for sensors and call them manually on boot. Then you create counters/binary sensors which track updates and user them to sleep when they’re done.

https://community.home-assistant.io/t/solar-powered-self-watering-plant-no-plumbing-no-power-wip/464263/14?u=mahko_mahko

1 Like

Thank you so much for this! I went through your config and I think I understand how it works. Will try to implement the logic for my use case and report the result once I am done.

1 Like

I made it! Thanks to your config example I managed to trigger deep sleep after all the sensors send their data. I can’t thank you enough!

Here is the config I have written with the help of your example:

# Huge thanks to user Mahko_Mahko on Home Assistant Community for providing his configuration example. I included his practices in my configuration to make it work.

# https://community.home-assistant.io/t/solar-powered-self-watering-plant-no-plumbing-no-power-wip/464263/14

substitutions:
  devicename: esp32-inside
  max_run_duration: 60s
  sleep_duration: 60s

esp32:
  board: esp32dev
  framework:
    type: arduino

esphome:
  name: $devicename
  # on_boot documentation and priorities: https://esphome.io/components/esphome.html#on-boot
  on_boot:
    - priority: -100
      then: # At this priority, pretty much everything should already be initialized.
        - logger.log: "Starting sensor updates..."
        - repeat:
            count: 5
            then:
              #Sensor updates are turned off and manually requested on boot. 
              #Then the ESP goes back to sleep when they're done (unless told to stay awake)

              # Request sensor updates
              - component.update: dht_inside
              - delay: 2000ms # DHT11 is a really slow sensor
              - component.update: bmp_inside
              - delay: 100ms

  on_shutdown:
    priority: -100
    then:
      - if:
          condition:
            - binary_sensor.is_on: all_updates_received
          then:
            - logger.log: "Data from all sensors received"
          else:
            - logger.log: "One or more sensor updates missing"

      # Reset data update sensors states
      - binary_sensor.template.publish:
          id: inside_temperature_dht_received
          state: OFF
      - binary_sensor.template.publish:
          id: inside_humidity_dht_received
          state: OFF
      - binary_sensor.template.publish:
          id: inside_temperature_bmp_received
          state: OFF
      - binary_sensor.template.publish:
          id: inside_pressure_bmp_received
          state: OFF

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:

ota:


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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: !secret ap_ssid
    password: !secret ap_password

captive_portal:
    
i2c:
  sda: 21
  scl: 22
  scan: true
  id: bus_a

deep_sleep:
  id: deep_sleep_1
  run_duration: $max_run_duration
  sleep_duration: $sleep_duration

mqtt:
  broker: 192.168.64.18
  port: 1883
  username: !secret mqtt_username
  password: !secret mqtt_password
  birth_message:
  will_message:
  on_message:
    - topic: esp32-inside/ota_mode
      payload: 'ON'
      then:
        - deep_sleep.prevent: deep_sleep_1
        - logger.log: "OTA mode enabled - deep sleep mode disabled"
    - topic: esp32-inside/sleep_mode
      payload: 'ON'
      then:
        - deep_sleep.enter: deep_sleep_1
        - logger.log: "deep sleep mode enabled"

binary_sensor:
  # Data update sensors. Implemented as barrier. Again huge thanks to user Mahko_Mahko on Home Assistant Community for providing his configuration example.
  # https://community.home-assistant.io/t/solar-powered-self-watering-plant-no-plumbing-no-power-wip/464263/14

  # Inside temperature DHT
  - platform: template
    name: Temperature DHT updated
    id: inside_temperature_dht_received
    internal: True 
    # Internal components will not be exposed to the frontend (like Home Assistant). 
    # Only specifying an id without a name will implicitly set this to true.
  # Inside humidity DHT
  - platform: template
    name: Humidity DHT updated
    id: inside_humidity_dht_received
    internal: True 
  # Inside temperature BMP
  - platform: template
    name: Temperature BMP updated
    id: inside_temperature_bmp_received
    internal: True
  # Inside pressure BMP
  - platform: template
    name: Pressure BMP updated
    id: inside_pressure_bmp_received
    internal: True 
  # All updates
  - platform: template
    name: Updates from all sensors received
    id: all_updates_received
    icon: mdi:database-refresh-outline
    # entity_category: diagnostic
    # If all sensors data is received. Will be false even if only one sensor fails.
    lambda: |-
      return
      id(inside_temperature_dht_received).state &&
      id(inside_humidity_dht_received).state &&
      id(inside_temperature_bmp_received).state &&
      id(inside_pressure_bmp_received).state;
    on_press:
    # Would this work with on_state as well?
      then:
        - logger.log: 
            format: "All sensors updated after %.1f seconds of uptime. Entering deep sleep."
            args: ['id(uptime_seconds).state']
        - delay: 100ms
        - deep_sleep.enter: 
            id: deep_sleep_1

sensor:
  # Uptime sensor
  - platform: uptime
    id: uptime_seconds
    name: Uptime sensor
    update_interval: 1s
    accuracy_decimals: 1
    unit_of_measurement: s

# DHT11 - temperature, humidity
  - platform: dht
    model: DHT11
    pin: 15
    id: dht_inside
    update_interval: never
    temperature:
      name: "Temperature DHT"
      on_value:
        then:
          - binary_sensor.template.publish:
              id: inside_temperature_dht_received
              state: ON
    humidity:
      name: "Humidity DHT"
      on_value:
        then:
          - binary_sensor.template.publish:
              id: inside_humidity_dht_received
              state: ON

  # BMP280 - temperature, pressure
  - platform: bmp280
    address: 0x76
    id: bmp_inside
    update_interval: never
    temperature:
      name: "Temperature BMP"
      oversampling: 16x
      filters:
        - offset: -2.3
      on_value:
        then:
          - binary_sensor.template.publish:
              id: inside_temperature_bmp_received
              state: ON
    pressure:
      name: "Pressure BMP"
      on_value:
        then:
          - binary_sensor.template.publish:
              id: inside_pressure_bmp_received
              state: ON

I will probably get rid of MQTT for preventing deep sleep and implement a button for this purpose. It is going to be much easier to turn it on and off that way.

Thank you so much again!

2 Likes