Hello all. Flashing new firmware to esphome in deep sleep OTA is quite complicated, because sensor is in deep sleep and does not have wifi active. You have to wait until it wakes up, but you never know when it will be because clock in esp chip is not accurate. The esphome uploader only waits few seconds and if it does not detect the chip awake, it fails.
What I am succesfully using is running esphome in loop and try to flash until it succeeds. I created this simple bash script that waits until esphome wakes up and then flashes sends firmware OTA. I leave it running for hours or over night.
I call it ota-esp.sh. The parameter is yaml file:
#!/bin/bash
esphome $1 compile
esphome $1 compile
while ! esphome $1 upload; do :; done
esphome $1 logs
I compile twice because I noticed that at first compilation there may be warnings that disappear when you compile for second time.
I am still struggling with OTA of sleeping sensor from within ESPhome add-on in HA OS Supervisor. I cannot create bash scripts there and neither it works from HA OS terminal. If anyone could help please. This would solve flashing sensors in deep sleep over internet, which would be kind of cool. I have port of Home Assistant open in my router, but now I can only flash if I am at same network as the sensor.
Not very experienced in the âdeep sleepâ of ESPs, but may I suggest another way, that at least in my head should work.
âDisconnectâ the ESP via your router from your standard WiFi. In theory the captive portal should step in and open its own AccessPoint. There you can flash via OTA whatever you want. After rebooting it should use the new firmware.
EDIT: There is a solution right in the ESPHome examples:
For example, if you want to upload a binary via OTA with deep sleep mode it can be difficult to catch the ESP being active.
You can use this automation to automatically prevent deep sleep when a MQTT message on the topic livingroom/ota_mode is received. Then, to do the OTA update, just use a MQTT client to publish a retained MQTT message described below. When the node wakes up again it will no longer enter deep sleep mode and you can upload your OTA update.
Remember to turn âOTA modeâ off again after the OTA update by sending a MQTT message with the payload OFF. To enter the the deep sleep again after the OTA update send a message on the topic livingroom/sleep_mode with payload ON. Deep sleep will start immediately. Donât forget to delete the payload before the node wakes up again.
I had exactly this same issue with a garden sensor that would sleep most of the time and wake every minute to send itâs readings over MQTT.
What I did was to publish an MQTT message to the sensor with the retain flag set to true. When the sensor woke up it would connect to the broker and retrieve the retained message. If the message was true, then the ESP would not go back to sleep and OTA works normally. If the message was false, then the ESP would go back into a normal sleep cycle.
I am VERY new to ESPHome, so I donât know if anything like this would work here.
So could there be script in Home Assistant that creates mqtt message for sensor to block it from going to sleep, then activates OTA and then sends another mqtt message to allow deep sleep? It seems to me a bit complicated because script in Home Assistant may need to wait many hours until sensor wakes up and then trogger OTA. It looks it is doing same thing as I do with one bash command.
Now, in order to do an update, you need to first turn on the switch in Home Assistant and then run the OTA in a loop until succeeds, as shown by @janbenes:
while ! esphome $1 upload; do :; done
When the upload is finished, the esp will automatically turn off the disable switch. So after the reboot, it will use deep sleep again as normal.
Unfortunately doesnât send any readings. Maybe it goes sleep before it can connect to WiFi and send readings? Pin is connected correctly, setup is working with deepsleep (run_duration 15s) without the prevent deepsleep things. Also it takes ages when I set the flag until the device comes from deep sleep and preventing it.
Itâs been a while since I did this. I have an ESP01 in my Jeep that I use for presence.
If the ESP connects, then the Jeep is present.
I use a binary helper that I turn on on the dashboard that is basically a semaphore to tell the ESP that I want it to not go to sleep. The test_ota script in the code below is where this is tested when the ESP boots.
So, my sequence is:
Turn on OTA mode.
Wait for the sleep period to end, then the ESP reboots and will not go to sleep.
Perform my OTA upload.
Turn off the OTA mode.
Press âRestartâ on the dashboard to reboot the ESP.
# This is the ESP device inside the Jeep to provide presence (status=connected).
substitutions:
device_name: jeep
esphome:
name: ${device_name}
on_boot:
priority: -100.0
then:
- delay: 1s
- script.execute: test_ota
esp8266:
board: esp01_1m
api:
ota:
safe_mode: True
packages:
wifi: !include common/wifi.yaml
logger:
level: VERBOSE # default is DEBUG
binary_sensor:
- platform: status
name: "Jeep Status"
- platform: homeassistant
id: otamode
entity_id: input_boolean.jeep_ota_mode
#################################################
# Get the WiFi details
text_sensor:
- platform: wifi_info
ip_address:
name: ${device_name} IP Address
ssid:
name: ${device_name} SSID
mac_address:
name: ${device_name} Mac Address
sensor:
- platform: wifi_signal
name: ${device_name} WiFi Signal Sensor"
update_interval: 60s
#################################################
# Script to test if the otamode switch is on or off
script:
- id: test_ota
mode: queued
then:
- logger.log: "Checking OTA Mode"
- if:
condition:
binary_sensor.is_on: otamode
then:
- logger.log: 'OTA Mode ON'
- deep_sleep.prevent: deep_sleep_handler
else:
- logger.log: 'OTA Mode OFF'
- delay: 2s
- script.execute: test_ota
#################################################
#Deep Sleep
deep_sleep:
id: deep_sleep_handler
run_duration: 5s
sleep_duration: 120s
################################################
#Make a button to reboot the ESP device
button:
- platform: restart
name: ${device_name} Restart
HI
Somehow I canât manage to integrate the OTO update code into my existing one
BR Markus
esphome:
name: "tanne"
platform: ESP8266
board: d1_mini
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "BH3dK3eMeT7ACjzYw3L2tNA+U/dWgwa/MJ8IBeAzV6c="
ota:
password: "soil"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
# Enable fallback hotspot (captive portal) in case wifi connection fails
#ap:
# ssid: "moisture-1 Fallback Hotspot"
#password: !secret fallback_wifi_password
captive_portal:
# the ads1115 is i2c
i2c:
sda: D2
scl: D1
scan: true
# Konfiguration der Batteriespannungssensoren
ads1115:
- address: 0x48
sensor:
- platform: ads1115
multiplexer: 'A3_GND'
gain: 6.144
name: "ADS1115 Channel A3-GND"
id: ads1115_1
update_interval: 5s
internal: true # Don't expose this sensor to HA, the template sensors will do that
# Dry 2.734 - taken from in the air, probably should do it directly in dry soil
# Wet 0.638 - taken from in a glass of water, probably should do it directly in saturated soil
filters:
- sliding_window_moving_average: # averages the last 10 results, probably overkill
window_size: 3
send_every: 3
- lambda: |-
if (x > 2.245) { // if over 2.734 volts, we are dry
return 0;
} else if (x < 0.862) { // if under 0.638 volts, we are fully saturated
return 100;
} else {
return (2.245-x) / (2.245-0.862) * 100.0; // use a linear fit for any values in between
}
- platform: template
name: "Soil Saturation"
id: zone1saturation
unit_of_measurement: "%"
icon: "mdi:water-percent"
accuracy_decimals: 0
lambda: |-
return id(ads1115_1).state;
- platform: ads1115
multiplexer: 'A2_GND'
gain: 6.144
name: "ADS1115 Channel A2-GND"
id: ads1115_2
update_interval: 5s
internal: true # Don't expose this sensor to HA, the template sensors will do that
# Dry 2.734 - taken from in the air, probably should do it directly in dry soil
# Wet 0.638 - taken from in a glass of water, probably should do it directly in saturated soil
filters:
- sliding_window_moving_average: # averages the last 10 results, probably overkill
window_size: 3
send_every: 3
- lambda: |-
if (x < 3.10) { // if over 2.734 volts, we are dry
return 0;
} else if (x > 4.18) { // if under 0.638 volts, we are fully saturated
return 100;
} else {
return (3.10-x) / (3.10-4.18) * 100.0; // use a linear fit for any values in between
}
- platform: template
name: "Akku Voltage"
id: zone2saturation
unit_of_measurement: "%"
# icon: "mdi:water-percent"
icon: "mdi:battery-80"
accuracy_decimals: 0
lambda: |-
return id(ads1115_2).state;
Hi, thank you very much for your example, I am trying to implement it, but I have one problem:
I created the boolean helper in HA with the entity name âinput_boolean.jeep_ota_modeâ, and I can also see the value change from ON to OFF, when I check the HA variables.
My problem is, that the script, running the check for otamode is not reacting to my boolean from above. it will not recognize the boolean state on and always tells me OTA Mode OFF in the logs.
Is there any additional implementation of the boolean necessary than just adding it to the dashboard and entities?
my code:
esphome:
name: esp32-c3-zockbock
platformio_options:
board_build.flash_mode: dio
on_boot:
priority: -100.0
then:
- delay: 1s
- script.execute: test_ota
esp32:
board: seeed_xiao_esp32c3
variant: esp32c3
framework:
type: arduino
platform_version: 5.4.0
# Enable logging
logger:
hardware_uart: UART0
level: VERBOSE
# Enable Home Assistant API
api:
encryption:
key: "EGBMB97pCl7k1wjavApwzw6oO8i5in8Cy6zre3F4tos="
ota:
- platform: esphome
password: "4d5cba2bf5b501ea29eff6624ea46b5a"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Esp32-C3-ZockBock"
password: "1a3yZx5BBtrO"
#################################################
# Erstelle einen binären Sensor, der mir den ESP Status zurßckgibt im Dashboard
binary_sensor:
- platform: status
name: "ZockBock status"
- platform: homeassistant
name: "ZockBock_OTA_mode"
id: otamode
entity_id: input_boolean.zockbock_ota_mode
#################################################
# Definiere den Switch, der die LED auf GPIO6 steuert
switch:
- name: LED
platform: gpio
pin: GPIO6
#################################################
# Definiere den Sensor, der die Batteirspannung misst
sensor:
- platform: adc
pin: GPIO2
name: BatteryVoltage
#raw : true
unit_of_measurement: V
update_interval: 60s
attenuation: 11db
filters:
- multiply: 2 # wegen Voltage divider
#################################################
# Script to test if the otamode switch is on or off
script:
- id: test_ota
mode: queued
then:
- logger.log: "Checking OTA flashing Mode"
- logger.log: "OTA flashing Mode state:" {{ states('input_boolean.zockbock_ota_mode') }}
- if:
condition:
binary_sensor.is_on: otamode
then:
- logger.log: 'OTA flashing Mode ON'
- deep_sleep.prevent: deep_sleep_handler
else:
- logger.log: 'OTA flashing Mode OFF'
- delay: 2s
- script.execute: test_ota
# Das richtige Vorgehehen fĂźr das Flashen des ESP ist nun:
# Turn on OTA mode with dashboard switch
# Wait for the sleep period to end, then the ESP reboots and will not go to sleep.
# Perform my OTA flash upload.
# Turn off the OTA mode.
# Press âRestartâ on the dashboard to reboot the ESP.
#################################################
#Deep Sleep
deep_sleep:
id: deep_sleep_handler
run_duration: 20s
sleep_duration: 120s
################################################
#Make a button to reboot the ESP device
button:
- platform: restart
name: ${device_name} Restart
So here is something new⌠but I do not understand it, maybe this helps you:
I just did a restart of the ESP32 and suddenly, the following appeared in the log (I did not change the code before, except adding this state logger of the input boolean entity, which obviously didnât print the value but just the dumb text {{ states(âbinary_sensor.otamodeâ) }} âŚ
But what its visible in the log: suddenly the Homeassistant api said hello
V][api.connection:1357]: Hello from client: âHome Assistant 2024.7.2â | 192.168.2.128 | API Version 1.10
[22:00:15][D][api.connection:1375]: Home Assistant 2024.7.2 (192.168.2.128): Connected successfully
does this have something to do with the update timer of the Homeassistant api and this time I have just been lucky to have restarted the Esp32 to this exact moment?
Or (what I actually suspect): The binary sensor update usually changes during a deep sleep phase of the ESP32. I suspect, the âONâ value is not recognized by the ESP, when it wakes up from deep sleep ans still sees âOFFâ from when it fell asleep.
I just tested a few toggles while the ESP has been on and this worked (recognized the value in the log)
22:00:11][D][main:378]: OTAMode is {{ states('binary_sensor.otamode') }}
[22:00:11][D][main:424]: OTA flashing Mode OFF
[22:00:13][D][script:100]: Script 'TestOTA' queueing new instance (mode: queued)
[22:00:13][D][main:375]: Checking OTA flashing Mode
[22:00:13][D][main:378]: OTAMode is {{ states('binary_sensor.otamode') }}
[22:00:13][D][main:424]: OTA flashing Mode OFF
[22:00:15][D][api:102]: Accepted 192.168.2.128
[22:00:15][V][api.connection:1357]: Hello from client: 'Home Assistant 2024.7.2' | 192.168.2.128 | API Version 1.10
[22:00:15][D][api.connection:1375]: Home Assistant 2024.7.2 (192.168.2.128): Connected successfully
[22:00:15][D][script:100]: Script 'TestOTA' queueing new instance (mode: queued)
[22:00:15][D][main:375]: Checking OTA flashing Mode
[22:00:15][D][main:378]: OTAMode is {{ states('binary_sensor.otamode') }}
[22:00:15][D][main:424]: OTA flashing Mode OFF
[22:00:15][D][homeassistant.binary_sensor:026]: 'input_boolean.zockbockotamode': Got state ON
[22:00:15][D][binary_sensor:034]: 'ZockBockOTAMode': Sending initial state ON
[22:00:17][D][script:100]: Script 'TestOTA' queueing new instance (mode: queued)
[22:00:17][D][main:375]: Checking OTA flashing Mode
[22:00:17][D][main:378]: OTAMode is {{ states('binary_sensor.otamode') }}
[22:00:17][D][main:383]: OTA flashing Mode ON
[22:00:19][D][script:100]: Script 'TestOTA' queueing new instance (mode: queued)
[22:00:19][D][main:375]: Checking OTA flashing Mode
[22:00:19][D][main:378]: OTAMode is {{ states('binary_sensor.otamode') }}
[22:00:19][D][main:383]: OTA flashing Mode ON
[22:00:21][D][script:100]: Script 'TestOTA' queueing new instance (mode: queued)
[22:00:21][D][main:375]: Checking OTA flashing Mode
[22:00:21][D][main:378]: OTAMode is {{ states('binary_sensor.otamode') }}
[22:00:21][D][main:383]: OTA flashing Mode ON
[22:00:23][D][script:100]: Script 'TestOTA' queueing new instance (mode: queued)
[22:00:23][D][main:375]: Checking OTA flashing Mode
I just tested my code again, and it is working. My hardware is an ESP8266. I have done very little with ESP32, so I have to wonder if the ESP32 sleep commands are different??