ESPHome - Deep Sleep with sliding_window_moving_average

Hi,

I am trying to use Deep Sleep with sliding_window_moving_average, in ESPHome (using an ESP32). I don’t think this works - the average value resets every time it goes to sleep.

Does anyone know a succinct solution for this?

I am keen to not rely on any server infrastructure (Home Assistant), so that my auto chicken door continues to work even if the server is not available.

My code is simple, currently just publishing a light sensor value to MQTT:

esphome:
  name: chicken-coop
  platform: ESP32
  board: esp32dev

# Enable logging
logger:

# Enable Home Assistant API
#api:

ota:
  password: "<REMOVED>"

wifi:
  ssid: "<REMOVED>"
  password: "<REMOVED>"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Chicken-Coop Fallback Hotspot"
    password: "<REMOVED>"

deep_sleep:
  id: deep_sleep_1
  run_duration: 3min
  sleep_duration: 30s

mqtt:
  broker: 192.168.1.110
  on_message:
    - topic: chickens/deepsleepactive
      payload: 'DISABLED'
      then:
        - deep_sleep.prevent: deep_sleep_1
    - topic: chickens/deepsleepactive
      payload: 'FORCE'
      then:
        - deep_sleep.enter: deep_sleep_1

sensor:
  - platform: adc
    pin: GPIO32
    name: "TEMT6000 Illuminance"
    device_class: illuminance
    unit_of_measurement: lx
    #update_interval: 5s
    filters:
      - sliding_window_moving_average:
          window_size: 60
          send_every: 1
      - lambda: |-
          return (x / 10000.0) * 2000000.0;

Thanks for your thoughts in advance.

Scott

I’m learning Deep Sleep right now as well. regarding variables, you may need to place it into the RTC memory.

Only data in the RTC memory is recoverable after restart, but note there is not much space to store data.

check this out: Deep sleep: Use RTC memory to store data (ESP32 + Arduino series) - YouTube

Please let me know how it goes, as I’m going to be in a similar situation soon.

I did also see that your sleep duration is only 30s? why is that?

Hope some of this helps.

Hi,

You are right Dallas; I had to create global variables to be able to store in RTC memory, and use those variables to re-write the sliding_window_moving_average myself. It’s working perfectly now, but does make the code a little more complex!

If it’s of use, here is my solution to this one:

esphome:
  name: chicken-coop
  platform: ESP32
  board: esp32dev

# Enable logging
logger:

# Enable Home Assistant API
#api:

ota:
  password: "<REMOVED>"

wifi:
  ssid: "<REMOVED>"
  password: "<REMOVED>"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "<REMOVED>"
    password: "<REMOVED>"

deep_sleep:
  id: deep_sleep_1
  run_duration: 3min
  sleep_duration: 30s

mqtt:
  broker: 192.168.1.110
  id: mqtt_client
  on_message:
    - topic: chickens/deepsleepactive
      payload: 'DISABLED'
      then:
        - deep_sleep.prevent: deep_sleep_1
    - topic: chickens/deepsleepactive
      payload: 'FORCE'
      then:
        - deep_sleep.enter: deep_sleep_1
    - topic: chickens/doorMode
      payload: 'MANUAL'
      then:
        - lambda: |-
            id(isInManualMode) = true;
    - topic: chickens/doorMode
      payload: 'AUTO'
      then:
        - lambda: |-
            id(isInManualMode) = false;
    - topic: chickens/manualDoorRequest
      payload: 'CLOSE'
      then:
        - script.execute: closeDoorScript
    - topic: chickens/manualDoorRequest
      payload: 'OPEN'
      then:
        - script.execute: openDoorScript

globals:
  - id: lightSensorReadingsArry
    type: int[5]
    restore_value: yes
  - id: valueIndex
    type: int
    restore_value: yes
    initial_value: '0'
  - id: sensorReadingMovingAverage
    type: int
    restore_value: yes
    initial_value: '0'
  - id: doorIsClosed
    type: bool
    restore_value: yes
    initial_value: 'true'
  - id: isInManualMode
    type: bool
    restore_value: yes
    initial_value: 'false'

script:
  - id: closeDoorScript
    then:
      - mqtt.publish:
          topic: chicken-coop/doorAction
          payload: "Door Closing"
      - switch.turn_on: closedoorswitch1
      - switch.turn_on: closedoorswitch2
      - delay: 4s
      - switch.turn_off: closedoorswitch1
      - switch.turn_off: closedoorswitch2
      - mqtt.publish:
          topic: chicken-coop/doorAction
          payload: "Door Closed"
      - lambda: |-
          id(doorIsClosed) = true;
  - id: openDoorScript
    then:
      - mqtt.publish:
          topic: chicken-coop/doorAction
          payload: "Door Opening"
      - switch.turn_on: opendoorswitch1
      - switch.turn_on: opendoorswitch2
      - delay: 4s
      - switch.turn_off: opendoorswitch1
      - switch.turn_off: opendoorswitch2
      - mqtt.publish:
          topic: chicken-coop/doorAction
          payload: "Door Open"
      - lambda: |-
          id(doorIsClosed) = false;

switch:
  - platform: gpio
    name: "closedoorswitch1"
    id: closedoorswitch1
    pin: GPIO12
  - platform: gpio
    name: "closedoorswitch2"
    id: closedoorswitch2
    pin: GPIO13
  - platform: gpio
    name: "opendoorswitch1"
    id: opendoorswitch1
    pin: GPIO18
  - platform: gpio
    name: "opendoorswitch2"
    id: opendoorswitch2
    pin: GPIO19

sensor:
  - platform: adc
    pin: GPIO32
    name: "TEMT6000 Illuminance"
    device_class: illuminance
    unit_of_measurement: lx
    update_interval: 5s
    on_value:
        then:
            - if:
                condition:
                    lambda: |-
                      if (id(sensorReadingMovingAverage) < 30)
                      {
                          if (id(doorIsClosed) == false)
                          {
                              if(id(isInManualMode) == false)
                              {
                                 return true;
                              }
                              else
                              {
                                return false;
                              }
                          }
                          else
                          {
                              return false;
                          }
                      }
                      else
                      {
                          return false;
                      }
                then: 
                  - script.execute: closeDoorScript
            - if:
                condition:
                    lambda: |-
                      int newReading = static_cast<int>((x / 10000) * 2000000);
                      
                      if (newReading > 50)
                      {
                        if (id(doorIsClosed) == true)
                        {
                            if(id(isInManualMode) == false)
                            {
                                return true;
                            }
                            else
                            {
                                return false;
                            }
                        }
                        else
                        {
                            return false;
                        }
                      }
                      else
                      {
                        return false;
                      }
                then:
                    - script.execute: openDoorScript
            - lambda: |-
                int windowSize = 5;
                
                if (id(valueIndex) < windowSize) 
                {
                  id(lightSensorReadingsArry)[id(valueIndex)] = static_cast<int>((x / 10000) * 2000000);
                  std::string sensorReadingStr = to_string(id(lightSensorReadingsArry)[id(valueIndex)]);
                  id(mqtt_client).publish("chicken-coop/newSensorReading", sensorReadingStr);                  
                  
                  std::string currentIndexStr = to_string(id(valueIndex));
                  id(mqtt_client).publish("chicken-coop/valueIndex", currentIndexStr);                  
                  id(valueIndex) = id(valueIndex) + 1;
                }
                  else
                {
                  id(valueIndex) = 0;
                }
                
                id(sensorReadingMovingAverage) = 0;
                
                for (int i = 0; i < windowSize; i++)
                {
                  id(sensorReadingMovingAverage) = id(sensorReadingMovingAverage) + id(lightSensorReadingsArry)[i];
                }
                
                id(sensorReadingMovingAverage) = id(sensorReadingMovingAverage) / windowSize;
                
                int localReadingAverage = id(sensorReadingMovingAverage);
                
                std::string sensorReadingMovingAverageStr = to_string(localReadingAverage);
                id(mqtt_client).publish("chicken-coop/id(sensorReadingMovingAverage)",                  sensorReadingMovingAverageStr);

My sleep duration was previously on 30 seconds while I was developing / testing. In the real world, it’ll sleep for 28 minutes and wake for 2 minutes.

I see you are using a sliding window for your moving average. This requires a lot more complexity and state than an exponentially decaying average, which IMHO also gives a better value since it avoids the “hickup” effect when large values move out of your moving window.

The maths for this is just;

sensorAverage = (windowSize*sensorAverage + sensorReading)/(windowSize+1)

This gives the average over (approximately) the last windowSize readings and requires only a single value in state (sensorAverage). This is also known as a “Low Pass Filter”, and its behaviour has a bunch of well known (and useful) characteristics.

1 Like

@ScottH Thankyou for sharing - very helpful
@dbaarda Love the “exponentially decaying average” thankyou

I think there is a bug in the code to store the values in the array
The way I read it is every fifth value gets dropped

(Removing the MQTT to make it easier to read)

As I see it when id(valueIndex) == windowSize
The value is not stored in the array.

This is both simpler (always good IMHO) and stores all values:

            - lambda: |-
                int windowSize = 5;

                //store the value
                id(lightSensorReadingsArry)[id(valueIndex)] = static_cast<int>((x / 10000) * 2000000);                  

                //Loop index if required
                if (id(valueIndex) == windowSize) 
                {
                  id(valueIndex) = 0;
                }

[/quote]

(OMG, I’m going to be sorry embarrassed if I’m wrong, in which case: Sorry)