Preserve sensor state on ESP32 during deep sleep

Hi,

I have some waveshare epaper ESP32 driven devices.

I have some sensors in home assistant and their state is being updated from other devices, so they have always valid states in HA.

The devices are battery powered, so I need to use deep sleep. This works well.
But when the device comes back from deep sleep, it will at first show the sensor states as not available. They will come back after a few seconds, but what I am looking for is a way to store the last known value so it survives deep sleep.

I have not found out yet how to do this, can someone help?

(I only need the variables from HA on the ESP32, not the other way around, I found that there MQTT can be of use, but seem not to work the other way around).

No one able to help?

If you show the ESP home yaml someone may be able to help.

You are of course right. This is the (slightly shortened, took out fonts etc part) code.

esphome:
  name: klima-keller
  friendly_name: Klima Keller

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

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

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    static_ip: 192.168.0.82
    gateway: 192.168.0.1
    subnet: 255.255.255.0


captive_portal:
  sensor:
  
  # Counter

  - platform: homeassistant
    id: counter
    entity_id: sensor.counter_sensor_esp
    internal: true 

  # Temperaturen

  - platform: homeassistant
    id: TempAussen
    entity_id: sensor.htp_ausen_temperatur
    internal: true

  - platform: homeassistant
    id: TempSchlafen
    entity_id: sensor.htp_schlafzimmer_temperatur
    internal: true

  - platform: homeassistant
    id: TempWaschen
    entity_id: sensor.htp_waschekeller_temperatur
    internal: true

  - platform: homeassistant
    id: TempGast
    entity_id: sensor.htp_gastezimmer_temperatur
    internal: true    

  # Feuchtigkeit

  - platform: homeassistant
    id: FeuchtAussen
    entity_id: sensor.htp_ausen_luftfeuchtigkeit
    internal: true

  - platform: homeassistant
    id: FeuchtSchlafen
    entity_id: sensor.htp_schlafzimmer_luftfeuchtigkeit
    internal: true

  - platform: homeassistant
    id: FeuchtWaschen
    entity_id: sensor.htp_waschekeller_luftfeuchtigkeit
    internal: true

  - platform: homeassistant
    id: FeuchtGast
    entity_id: sensor.htp_gastezimmer_luftfeuchtigkeit
    internal: true    

  - platform: homeassistant
    id: FeuchtAussenAbs
    entity_id: sensor.aussen_absolute_luftfeuchtigkeit
    internal: true

  - platform: homeassistant
    id: FeuchtSchlafenAbs
    entity_id: sensor.schlafzimmer_absolute_luftfeuchtigkeit
    internal: true

  - platform: homeassistant
    id: FeuchtWaschenAbs
    entity_id: sensor.waschekeller_absolute_luftfeuchtigkeit
    internal: true

  - platform: homeassistant
    id: FeuchtGastAbs
    entity_id: sensor.gastezimmer_absolute_luftfeuchtigkeit
    internal: true    

  # Zeit

time:
  - platform: homeassistant
    id: time_homeassistant

  # Status Lüften

text_sensor:

  - platform: homeassistant
    id: lft_schlafen
    entity_id: input_text.lft_schlafzimmer
    internal: true

  - platform: homeassistant
    id: lft_gast
    entity_id: input_text.lft_gaestezimmer
    internal: true    

  - platform: homeassistant
    id: lft_waschen
    entity_id: input_text.lft_waeschekeller
    internal: true    

########################################################################    
spi: ###################################################################
########################################################################    
  clk_pin: GPIO13
  mosi_pin: GPIO14

deep_sleep:
  run_duration: 10s
  sleep_duration: 300s
  
########################################################################
display: ###############################################################
########################################################################
  - platform: waveshare_epaper
    id: eink_display
    cs_pin: GPIO15
    dc_pin: GPIO27
    busy_pin: GPIO25
    reset_pin: GPIO26 
    model: 2.13in-ttgo-dke
    full_update_every: 2
    update_interval: 60s
    rotation: 90°
    reset_duration: 2ms
    pages:
      - id: page1
        lambda: |-
 
          it.print(50, 5, id(openSansBold), "Außen");
          it.print(110, 5, id(openSansBold), "Schlafen");
          it.print(180, 5, id(openSansBold), "Gast");
          it.print(5, 35, id(materialdesign_icons_32), "\U000F050F");
          it.print(5, 65, id(materialdesign_icons_32), "\U000F058C");
          it.strftime(5, 95, id(openSansBold), "%H:%M", id(time_homeassistant).now());

            
          if(id(counter).has_state()){
            if(id(counter).state == 0){
              it.printf(50, 70, id(openSansBold14), "%.1f %%", id(FeuchtAussen).state);
              it.printf(110, 70, id(openSansBold14), "%.1f %%", id(FeuchtSchlafen).state);
              it.printf(180, 70, id(openSansBold14), "%.1f %%", id(FeuchtGast).state);
              }
            else if(id(counter).state == 1){
              it.printf(50, 70, id(openSansBold14), "%.1f g/m\U000000B3", id(FeuchtAussenAbs).state);
              it.printf(110, 70, id(openSansBold14), "%.1f g/m\U000000B3", id(FeuchtSchlafenAbs).state);
              it.printf(180, 70, id(openSansBold14), "%.1f g/m\U000000B3", id(FeuchtGastAbs).state);
              }
            else {
              it.print (60, 65, id(openSansBold), "Debug: Counter Error");
              }
            }
      
          it. printf(50, 40, id(openSansBold14), "%.1f °C", id(TempAussen).state);
          it. printf(110, 40, id(openSansBold14), "%.1f °C", id(TempSchlafen).state);
          it. printf(180, 40, id(openSansBold14), "%.1f °C", id(TempGast).state);

          if(id(lft_schlafen).has_state()){
            if(id(lft_schlafen).state == "OK") {
              it.printf(110, 95, id(materialdesign_icons_32), "\U000F0A50");
            }
            else if(id(lft_schlafen).state == "Öffnen") {
              it.printf(110, 95, id(materialdesign_icons_32), "\U000F1846");
            }
            else if(id(lft_schlafen).state == "Schließen") {
              it.printf(110, 95, id(materialdesign_icons_32), "\U000F1847");
            }
            else {
              it.printf(110, 95, id(materialdesign_icons_32), "Fehler");
            }
          }

          if(id(lft_gast).has_state()){
            if(id(lft_gast).state == "OK") {
              it.printf(180, 95, id(materialdesign_icons_32), "\U000F0A50");
            }
            else if(id(lft_gast).state == "Öffnen") {
              it.printf(180, 95, id(materialdesign_icons_32), "\U000F1846");
            }
            else if(id(lft_gast).state == "Schließen") {
              it.printf(180, 95, id(materialdesign_icons_32), "\U000F1847");
            }
            else {
              it.printf(180, 95, id(materialdesign_icons_32), "Fehler");
            }
          }
      - id: page2
        lambda: |-
 
          it.print(50, 5, id(openSansBold), "Außen");
          it.print(110, 5, id(openSansBold), "Waschkeller");
          it.print(5, 35, id(materialdesign_icons_32), "\U000F050F");
          it.print(5, 65, id(materialdesign_icons_32), "\U000F058C");
          it.strftime(5, 95, id(openSansBold), "%H:%M", id(time_homeassistant).now());

            
          if(id(counter).has_state()){
            if(id(counter).state == 0){
              it.printf(50, 70, id(openSansBold14), "%.1f %%", id(FeuchtAussen).state);
              it.printf(110, 70, id(openSansBold14), "%.1f %%", id(FeuchtWaschen).state);
            }
            else if(id(counter).state == 1){
              it.printf(50, 70, id(openSansBold14), "%.1f g/m\U000000B3", id(FeuchtAussenAbs).state);
              it.printf(110, 70, id(openSansBold14), "%.1f g/m\U000000B3", id(FeuchtWaschenAbs).state);
              }
            else {
              it.print (60, 65, id(openSansBold), "Debug: Counter Error");
              }
            }
      
          it. printf(50, 40, id(openSansBold14), "%.1f °C", id(TempAussen).state);
          it. printf(110, 40, id(openSansBold14), "%.1f °C", id(TempWaschen).state);
          
          if(id(lft_waschen).has_state()){
            if(id(lft_waschen).state == "OK") {
              it.printf(110, 95, id(materialdesign_icons_32), "\U000F0A50");
            }
            else if(id(lft_waschen).state == "Öffnen") {
              it.printf(110, 95, id(materialdesign_icons_32), "\U000F1846");
            }
            else if(id(lft_waschen).state == "Schließen") {
              it.printf(110, 95, id(materialdesign_icons_32), "\U000F1847");
            }
            else {
              it.printf(110, 95, id(materialdesign_icons_32), "Fehler");
            }
          }
       
interval:
  - interval: 6s
    then:
      - display.page.show_next: eink_display
      - component.update: eink_display

This works fine except that after coming back from deep_sleep, the sensors show “nan” state on the display first, at some point they will get their values.
What I want is that they always store the last value and update when there is new data. Without deep_sleep this works, with deep sleep it shows the described behaviour.

It looks like the epaper is updating before the new values are in. What about delaying he update a little longer so the sensors have values.

display:
  - platform: waveshare_epaper
    update_interval: never

Perhaps if you set the update interval to never then it would only update when
component.update: eink_display

or update only when sensor reads whatever values they may be

 on_value_range:
   - above: 0
     below: 15 
     then:
       - component.update: eink_display

put this in one of the sensors

1 Like

Thank you, this was really helpful.
Only that it did not update after the first run with value_range, so I just used on_value:, this worked good. Thanks for the help!

Glad you got it fixed. on_value of course works. I should have suggested that. I use it to change the deep sleep time based on voltage of my lithium cell.

  - platform: adc
    pin: A0
    name: cell
    id: sw_cell
    filters:
      - multiply: 4.55
    on_value:
      then:
      - if:
          condition:
            lambda: 'return id(sw_cell).state <3.6;'
          then:
          - lambda: |-
              id(deep_sleep_1).set_sleep_duration(3000000); 
          else:
          - lambda: |-
              id(deep_sleep_1).set_sleep_duration(300000);

Also as you are using battery you could send your esp32 into deep sleep earlier based on when it connects to HA rather than letting it sit connected to HA on wireless for full 10s.

wifi:
  # ...
  on_connect:
    - deep_sleep.enter: deep_sleep_1

deep_sleep:
  id: deep_sleep_1
  run_duration: 10s
  sleep_duration: 300s	

You could add in a small delay of 100ms or more so that you were sure you got the data sent to HA then send it to deep sleep. I myself use MQTT in deep sleep devices so when HA gets the value it send a message back to the ESP and on getting that message it goes straight into deep sleep. WIFI only connects for 200-300ms which is about 6 seconds after ESP begins to turn on.

That’s generally a good idea, but this lead to my problem with the readings of the adc on my Device (Waveshare 2,13 inch Cloud device with integrated ESP32).

The pinout scheme from https://www.waveshare.com/wiki/2.13inch_e-Paper_Cloud_Module#Pinout shows GPIO36 for adc pinout (“shows 1/3 of the battery voltage”), so I tried that out. Also tried out VCC, but both give me constant readings no matter what the battery status is really like. Maybe someone knows how to configure this?

This is my current config, but it continuously reads as 3,9 V…

  - platform: adc
    pin: A0
    name: "Klima EG A0 Voltage of 3.7V"
    update_interval: 15s
    accuracy_decimals: 3
    filters:
      - multiply: 3.7
    id: klima_eg_battery_voltage
    entity_category: diagnostic
    unit_of_measurement: "V"  
    device_class: battery
  
  - platform: template
    name: "klima_eg_Battery_Percentage"
    update_interval: 15s
    accuracy_decimals: 0
    lambda: |-
      return (((id(klima_eg_battery_voltage).state-3) /0.7) * 100.00);
    entity_category: diagnostic
    unit_of_measurement: "%"  
    device_class: battery

Have you tried calling it GPIO36 instead of A0?
Rereading your post that possibly is the first thing you did. If the reading is 1/3 then 3.9/3.2 x 3 = 3.16V. That would be too low for the battery and even for VCC

Have you tried setting attenuation: 11db or 2db. attenuation default is auto and may be causing the max value.

Yes I tried, but as I said, from GPIO36 I only get constant values that never change.

So if you power it by usb and disconnect the batterey does it read 3.9V?

Didnt try that, but it always shows constant values no matter if i power it via USB or not or if the battery’s draining.