mprowe
(Martin)
November 28, 2022, 3:30pm
1
This is my current program (based on this very helpful post ):
esphome:
name: "esp32-adc-vehicle"
on_boot:
priority: 100 #Wait for everything(?) to be setup...
then:
- script.execute: consider_deep_sleep
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
ota:
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
script:
- id: consider_deep_sleep
mode: queued
then:
- delay: 10s
- if:
condition:
binary_sensor.is_on: prevent_deep_sleep
then:
- output.turn_on: blue_led
- logger.log: 'Skipping sleep'
else:
- output.turn_off: blue_led
- logger.log: 'Entering sleep'
- deep_sleep.enter: deep_sleep_control
- script.execute: consider_deep_sleep
deep_sleep:
id: deep_sleep_control
sleep_duration: 50s
output:
platform: gpio
pin: GPIO2 #The only led I've got
id: blue_led
binary_sensor:
- platform: homeassistant
id: prevent_deep_sleep
entity_id: input_boolean.prevent_deep_sleep
sensor:
- platform: adc
pin: GPIO36
name: "esp32-adc-vehicle"
update_interval: 1s
attenuation: auto
#filters:
#- median:
# window_size: 10
# send_every: 5
How can I get the sensor to take 10 x 0.1sec readings and average and send once within the wake cycle? Then go to sleep for another 59sec?
Playing with the script: - delay: , sleep_duration: and sensor: update_intervals: , produces no logical patterns that I can determine!
Is it possible to sequence the sensor sampling+filtering before entering or after exiting sleep?
Regards, Martin
I think you can reuse a lot from this.
The key thing is to trigger updates on boot and use a moving median filter to aggregate.
I then go to sleep when all the updates are received.
substitutions:
devicename: irrigation-balcony-quinled
friendly_name: Bedroom Balcony Irrigation
device_description: Bedroom Balcony Irrigation
#I find it easier to manage some settings up here.
sleep_time: 60min
auto_wake_time: 30s
pump_run_time: 10s
# internal_toggle: true
esp32:
board: mhetesp32devkit
esphome:
name: $devicename
comment: ${device_description}
on_boot:
- priority: 900
then:
- lambda: |-
Wire.begin();
#^^Seems to solve bug with BH1750 not waking up from deep sleep. https://community.home-assistant.io/t/bh1750-no-communication-after-switching-it-off-and-on-again/412282/3?u=mahko_mahko
- priority: -100
then:
#Sensor updates are turned off for most sensors and just manually requested on boot. Then the ESP goes back to sleep when they're done (unless told to stay
#Reset sensor update counters. These are for debugging.
- lambda: id(count_irrigation_lux).publish_state(0);
- lambda: id(count_batt_voltage).publish_state(0);
- delay: 1s
- logger.log: "....Starting sensor updates"
- repeat:
#Request sensor updates
count: 5
then:
- component.update: batt_voltage #for battery level
- delay: 100ms
- component.update: soil_moisture_voltage #for moisture level
- delay: 100ms
- component.update: tof #for water tank level
- delay: 100ms
- component.update: irrigation_lux #for light level
- delay: 100ms
#and again for ADC based sensors which benefit from more samples.
- component.update: batt_voltage
- delay: 250ms
- component.update: soil_moisture_voltage
- delay: 250ms
on_shutdown:
#Turn off 5v peripheral power. It will retore as on when it wakes.
priority: -100
then:
- logger.log: "Turning off peripheral power..."
- switch.turn_off: power_peripherals
#Turn off the "Fresh data recieved sensors"
- binary_sensor.template.publish:
id: water_tank_level_recieved
state: OFF
- binary_sensor.template.publish:
id: irrigation_lux_recieved
state: OFF
- binary_sensor.template.publish:
id: batt_level_recieved
state: OFF
- binary_sensor.template.publish:
id: solar_plant_moisture_level_recieved
state: OFF
- binary_sensor.template.publish:
id: all_updates_recieved
state: OFF
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
manual_ip:
static_ip: XXX
gateway: YYY
subnet: ZZZ
api:
ota:
logger:
# level: VERBOSE
baud_rate: 0
i2c:
#Shared by multiple devices (wires hopped)
sda: GPIO25 #data > green wire
scl: GPIO27 #clock > blue wire
scan: true
text_sensor:
# Reports the ESPHome Version with compile date
- platform: version
name: ${friendly_name} ESPHome Version
time:
- platform: homeassistant
id: esptime
on_time:
#Deep sleep at 8pm and wake up at 5am. No real need to measure overnight.
- hours: 20
then:
- deep_sleep.enter:
id: deep_sleep_1
until: "05:00:00"
time_id: esptime
deep_sleep:
id: deep_sleep_1
run_duration: ${auto_wake_time}
sleep_duration: ${sleep_time}
output:
##################################################
#Pump
##################################################
#5v pump attached to a l298n.
#Pump on/off
- platform: gpio
pin: GPIO18
id: pump_on
# #Pump Speed.
# - platform: ledc
# pin: GPIO26
# id: pump_speed
button:
##################################################
#Pump Control
##################################################
#Run pump for fixed time interval
- platform: template
name: ${friendly_name} Run Pump
id: run_pump_for_interval
icon: "mdi:water-pump"
on_press:
then:
- logger.log: Running irrigation pump for ${pump_run_time}
- output.turn_on: pump_on
# #Set speed
# - output.set_level:
# id: pump_speed
# level: 50%
# - output.ledc.set_frequency:
# id: pump_speed
# frequency: 500Hz
- delay: ${pump_run_time}
- output.turn_off: pump_on
# - output.turn_off: pump_speed
#Stop Pump
- platform: template
name: ${friendly_name} Stop Pump
id: stop_irrigation_pump
icon: "mdi:water-pump-off"
on_press:
then:
- output.turn_off: pump_on
# - output.turn_off: pump_speed
- logger.log: "Stopping Pump"
##################################################
#Deep Sleep
##################################################
# #Prevent Deep Sleep via HA button
# - platform: template
# name: ${friendly_name} Sleep Prevent
# id: prevent_deep_sleep
# icon: "mdi:sleep-off"
# on_press:
# then:
# - logger.log: "Deep Sleep PREVENT activated via HA button."
# - deep_sleep.prevent: deep_sleep_1
# #Allow Deep Sleep
# - platform: template
# name: ${friendly_name} Sleep Allow
# id: allow_deep_sleep
# icon: "mdi:sleep"
# on_press:
# then:
# - logger.log: "Deep Sleep ALLOWED activated via HA button."
# - deep_sleep.allow: deep_sleep_1
#Sleep if allowed.
- platform: template
name: ${friendly_name} Sleep If Allowed
id: sleep_if_allowed
icon: "mdi:sleep"
# internal: ${internal_toggle}
on_press:
then:
- if:
condition:
or:
- binary_sensor.is_on: remote_defeat
# - binary_sensor.is_on: defeat
then:
- logger.log: "Sleep requested but STAY AWAKE mode is on. Skipping sleep."
- deep_sleep.prevent: deep_sleep_1
else:
- logger.log: "Sleep requested and ALLOWED. Going to sleep..."
- deep_sleep.enter:
id: deep_sleep_1
sleep_duration: ${sleep_time}
#Force Deep Sleep
- platform: template
name: ${friendly_name} Force Sleep
id: force_deep_sleep
icon: "mdi:bell-sleep"
on_press:
then:
- logger.log: "FORCE SLEEP requested...Going to sleep"
- deep_sleep.enter:
id: deep_sleep_1
sleep_duration: ${sleep_time}
binary_sensor:
##################################################
#Data Update Sensors. These were useful during dev. Might remove in prod.
#They are all updated via other sensors.
##################################################
#Moisture
- platform: template
name: ${friendly_name} moisture updated
id: solar_plant_moisture_level_recieved
# internal: ${internal_toggle}
#Battery Level
- platform: template
name: ${friendly_name} battery level updated
id: batt_level_recieved
# internal: ${internal_toggle}
#Light
- platform: template
name: ${friendly_name} lux updated
id: irrigation_lux_recieved
# internal: ${internal_toggle}
#Water level
- platform: template
name: ${friendly_name} water level updated
id: water_tank_level_recieved
# internal: ${internal_toggle}
#All
#Once all updates are recieved, then chack if you can go back to sleep.
- platform: template
name: ${friendly_name} all updates recieved
id: all_updates_recieved
lambda: |-
return
id(solar_plant_moisture_level_recieved).state &&
id(batt_level_recieved).state &&
id(irrigation_lux_recieved).state &&
id(water_tank_level_recieved).state
;
on_press:
#Sleep esp if all data recieved
then:
- logger.log:
format: "All sensors updated after %.1f seconds of uptime. Checking if sleep is allowed"
args: [ 'id(uptime_sec).state']
# - delay: 1s
- button.press: sleep_if_allowed
##################################################
#Deep sleep control
##################################################
#A lot of my logic is remashes of:
#https://www.wirewd.com/make/blog/esphome_sleep_modes
#https://tatham.blog/2021/02/06/esphome-batteries-deep-sleep-and-over-the-air-updates/
#Some of it still needs a clean up.
#HA Deep sleep control
- platform: homeassistant
name: "Remote Defeat Sleep"
internal: True
id: "remote_defeat"
entity_id: input_boolean.defeat_sleep
on_press:
then:
- logger.log: "remote press defeat"
- deep_sleep.prevent: deep_sleep_1
# #HA Deep sleep control (not currently used)
# - platform: gpio
# name: "Defeat"
# id: "defeat"
# internal: True
# pin:
# number: 4
# mode: INPUT_PULLUP
# inverted: True
# on_press:
# then:
# - logger.log: "press defeat"
# - deep_sleep.prevent: deep_sleep_1
switch:
##################################################
#Control peripheral power (on solar power manager)
##################################################
- platform: gpio
pin:
number: 17
name: ${friendly_name} Power Peripherals
id: power_peripherals
restore_mode: ALWAYS_ON #This turns on the power on boot/wake in time for sensor set-up.
sensor:
#Uptime sensor
- platform: uptime
id: uptime_sec
name: ${friendly_name} Uptime Sensor
update_interval: 2s
accuracy_decimals: 1
unit_of_measurement: s
##################################################
#For counting data updates recieved for each wake cycle.
#Manually updated via publishing from other sensors.
##################################################
- platform: template
name: "Count Lux Updates"
id: count_irrigation_lux
unit_of_measurement: count
- platform: template
name: "Count Batt V Updates"
id: count_batt_voltage
unit_of_measurement: count
##########################################################################################
# Time of Flight sensor - i2c
##########################################################################################
#Powered via 5v
- platform: vl53l0x
id: tof
name: ${friendly_name} ToF
internal: false
address: 0x29
update_interval: never
#never
# enable_pin: GPIO17 #Did not work: https://github.com/esphome/issues/issues/3644
accuracy_decimals: 1
unit_of_measurement: 'cm'
filters:
#Convert to cm
- multiply: 100
#Then use moving median to smooth noise. Sample 5 points then send.
- median:
window_size: 5
send_every: 5
send_first_at: 5
#Convert the ToF distance to a water tank level (percent full)
- platform: copy
source_id: tof
id: water_tank_level
internal: false
# icon: "mdi:battery"
name: ${friendly_name} Water Level
unit_of_measurement: '%'
accuracy_decimals: 0
filters:
# Map from distance to % full. To calibrate.
- calibrate_linear:
- 3 -> 100
- 19.5 -> 0
# #Overide values less than 0% and more than 100%
# - lambda: |
# if (x < 3) return 100;
# else if (x > 20) return 0;
# else return ceil(x / 0.5) * 0.5;
on_value:
then:
- binary_sensor.template.publish:
id: water_tank_level_recieved
state: ON
# # ##########################################################################################
# # # bh1750 Lux/light sensor
# # ##########################################################################################
- platform: bh1750
id: irrigation_lux
name: ${friendly_name} Illuminance
address: 0x23
update_interval: never
filters:
#Use moving median to smooth noise. Sample 5 points then send.
- median:
window_size: 5
send_every: 5
send_first_at: 5
on_value:
then:
- binary_sensor.template.publish:
id: irrigation_lux_recieved
state: ON
on_raw_value:
then:
#Sensor update counter.
- lambda: id(count_irrigation_lux).publish_state(id(count_irrigation_lux).state +1);
#Notes:
#Voltage divider: Used 2 x 300K Ohm resistors
- platform: adc
id: batt_voltage
name: ${friendly_name} Battery Voltage
internal: false
pin: GPIO33 #ADC1
update_interval: never
accuracy_decimals: 3
attenuation: auto
filters:
# #The scale it back up from voltage divided value 2 x 300K > 2.1. 4.2/2.1 = 2.
- multiply: 2
on_raw_value:
then:
#Sensor update counter.
- lambda: id(count_batt_voltage).publish_state(id(count_batt_voltage).state +1);
#Intermediate sensor. Might consolidate them later.
- platform: copy
source_id: batt_voltage
id: batt_voltage_filtered
icon: "mdi:battery"
internal: false
name: ${friendly_name} Battery Voltage Filtered
unit_of_measurement: V
accuracy_decimals: 3
filters:
#Use moving median to deal with noise.
- median:
window_size: 10
send_every: 10
send_first_at: 10
#Convert the Voltage to a battery level (%)
- platform: copy
source_id: batt_voltage_filtered
id: batt_level
internal: false
icon: "mdi:battery"
name: ${friendly_name} Battery Level
unit_of_measurement: '%'
accuracy_decimals: 1
filters:
# Map from voltage to Battery level
- calibrate_linear:
- 3.0 -> 0 #Set 3.0 to 0% even though it can go lower (2.4V), for life extention. There's not much capacity below this anyway.
- 4.05 -> 100 #Set 4.05 to 100% even though it can go higher (~4.2V), for life extention.
#Overide values less than 0% and more than 100%
- lambda: |
if (x < 0) return 0;
else if (x > 100) return 100;
else return ceil(x / 5) * 5;
on_value:
then:
- binary_sensor.template.publish:
id: batt_level_recieved
state: ON
#Capacitive soil moisture sensor: https://www.aliexpress.com/item/32832538686.html?spm=a2g0o.order_list.0.0.55771802WgNqEA
#Voltage of the Capacitive soil moisture sensor
- platform: adc
pin: GPIO34
name: ${friendly_name} Soil Moisture Volts
id: soil_moisture_voltage
internal: false
accuracy_decimals: 1
update_interval: never
# never
attenuation: auto
filters:
#Use moving median to deal with noise.
- median:
window_size: 10
send_every: 10
send_first_at: 10
#Convert the Voltage to a moisture level (%)
- platform: copy
source_id: soil_moisture_voltage
id: solar_plant_moisture_level
name: ${friendly_name} Moisture Level
internal: false
icon: "mdi:battery"
unit_of_measurement: '%'
accuracy_decimals: 1
filters:
#max and min values taken from testing with glass of water. Prob need to do more in situ tests.
- calibrate_linear:
- 1.66 -> 100.0
- 2.90 -> 0.0
#Handle/cap boundaries
- lambda: |
if (x < 0) return 0;
else if (x > 100) return 100;
else return (x);
on_value:
then:
- binary_sensor.template.publish:
id: solar_plant_moisture_level_recieved
state: ON