ESPHome: automatic retry for OTA-updates

Hello everyone,

I would wish for a ESPHome OTA retry-function. A counter could be used, which I would set e.g. to “50”, which automatically clicks “retry” for me, if an OTA update fails. By standard, it is set to “1”.

Of course, one solution could be minimizing the ESP32-program, but I would like to have all the currently implemented functionality.

Problem in my scenario: I have some quite packed ESP32 running wih a lot of peripherals. This causes the OTA-update to fail most of the time. After retrying ~15 times the OTA update succeeds. I added a collection of the various error messages I receive in the code section below.

I already implemented Safe Mode Button — ESPHome , but It did not help for having a successful OTA update on the first try (it also takes > 10 trys, until an OTA update succeeds).

The Wifi-connection seems to be good (Fritzbox 6490 Cable 2 m away from ESP32).

HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
Dependency Graph
|-- AsyncTCP-esphome @ 2.1.3
|-- WiFi @ 2.0.0
|-- FS @ 2.0.0
|-- Update @ 2.0.0
|-- ESPAsyncWebServer-esphome @ 3.1.0
|-- DNSServer @ 2.0.0
|-- ESPmDNS @ 2.0.0
|-- Wire @ 2.0.0
|-- ArduinoJson @ 6.18.5
|-- Improv @ 1.2.3
|-- FastLED @ 3.3.2
RAM:   [==        ]  16.5% (used 53944 bytes from 327680 bytes)
Flash: [========= ]  92.5% (used 1697605 bytes from 1835008 bytes)
========================= [SUCCESS] Took 10.38 seconds =========================
INFO Successfully compiled program.
INFO Connecting to 192.168.122.162
INFO Uploading /data/build/esphome-web-45e2b0/.pioenvs/esphome-web-45e2b0/firmware.bin (1703376 bytes)
Uploading: [                                                            ] 0% ERROR Error receiving acknowledge chunk OK: [Errno 104] Connection reset by peer

or

WARNING esphome-web-45e2b0 @ 192.168.122.162: Connection error occurred: [Errno 104] Connection reset by peer
INFO Processing unexpected disconnect from ESPHome API for esphome-web-45e2b0 @ 192.168.122.162
WARNING Disconnected from API

or 

ERROR Error receiving acknowledge chunk OK: [Errno 104] Connection reset by peer

or 

Uploading: [=                                                           ] 1% ERROR Error receiving acknowledge chunk OK: [Errno 104] Connection reset by peer

or 

ERROR Error receiving acknowledge version: timed out

or

Uploading: [=========                                                   ] 14% ERROR Error receiving acknowledge chunk OK: timed out

or

ERROR Error binary size: Unknown error from ESP

Just for information, this is the ESPHome-yaml file for the ESP32:

esp32:
  board: wemos_d1_mini32
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

# Allow Over-The-Air updates
ota:
  safe_mode: true
#  password: !secret CO2_pass

# Allow provisioning Wi-Fi via serial
improv_serial:

wifi:
  # Set up a wifi access point
  ap: {}

# In combination with the `ap` this allows the user
# to provision wifi credentials to the device via WiFi AP.
captive_portal:

dashboard_import:
  package_import_url: github://esphome/example-configs/esphome-web/esp32.yaml@main
  import_full_config: true

# Sets up Bluetooth LE (Only on ESP32) to allow the user
# to provision wifi credentials to the device.
esp32_improv:
  authorizer: none

# To have a "next url" for improv serial
web_server:

font:
  - file: "fonts/arial.ttf"
    id: font_arial
    size: 20

globals:
  - id: TM1637_display_status
    type: bool
    restore_value: no
    initial_value: 'true'

# Example configuration entry
i2c:
  sda: GPIO21
  scl: GPIO22
  scan: true
  frequency: 800kHz

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    id: SSD1306_display
#    icon: "mdi:clock-digital"
    reset_pin: D0
    address: 0x3C
    lambda: |-
      it.print(0, 0, id(font_arial), "TEMP:");
      it.printf(128,0, id(font_arial), TextAlign::TOP_RIGHT, "%.2f°C", id(temperature).state);
      it.print(0,16, id(font_arial), "FEUCHTE:");
      it.printf(128,16, id(font_arial), TextAlign::TOP_RIGHT, "%.2f%%", id(humidity).state);
      it.print(0,32, id(font_arial), "DRUCK:");
      it.printf(128,32, id(font_arial), TextAlign::TOP_RIGHT, "%.2fhpa", id(pressure).state);
      it.print(0,48, id(font_arial), "LICHT:");
      it.printf(128,48, id(font_arial), TextAlign::TOP_RIGHT, "%.2flux", id(lux).state);
      it.print(0,48, id(font_arial), "CO2:");
      it.printf(128,48, id(font_arial), TextAlign::TOP_RIGHT, "%.0f ppm", id(sensor_co2).state);
#      if (!id(SSD1306_oled_bc).current_values.get_brightness()) {        
#      }     
    update_interval: 1s
  - platform: tm1637
    id: TM1637_display
#    icon: "mdi:clock-digital"
    clk_pin: GPIO19
    dio_pin: GPIO18
    inverted: false
    update_interval: 1s
    length: 4
    lambda: |-
      if (id(TM1637_display_status) == true) {
        it.printf("%4.0f", id(sensor_co2).state);
        it.set_intensity(7);
      } else {
          printf("    ");
        }
#      it.printf( 0, 0, id(font_arial), "%.0f ppm", id(sensor_co2).state);
#      it.print(0, 0, id(font_arial), "Hello World!");


# Example configuration entry
uart:
  rx_pin: GPIO16
  tx_pin: GPIO17
  baud_rate: 9600

button:
  - platform: safe_mode
    name: "CO2 01 (Safe Mode) for OTA"

binary_sensor:
  - platform: gpio
    pin: 
      number: GPIO23
      mode:
          input: true
          pullup: true
    name: "LED button left"
    id: "LED_button_left"
    filters:
      - delayed_on: 10ms
      - delayed_off: 10ms
  - platform: gpio
    pin:
      number: GPIO5
      mode:
          input: true
          pullup: true
    name: "LED button right"
    id: "LED_button_right"
    filters:
      - delayed_on: 10ms
      - delayed_off: 10ms
  - platform: gpio
    pin: GPIO35
    name: "PIR Sensor"
    device_class: motion                   

sensor:
  - platform: senseair
    co2:
      name: "SenseAir CO2 Value"
      id: sensor_co2
    update_interval: 60s
  - platform: bh1750
    id: "lux"
    name: "BH1750 Illuminance"
    address: 0x23
    update_interval: 60s
  - platform: bme680
    temperature:
      id: "temperature"
      name: "BME680 Temperature"
      oversampling: 16x
      filters:
        offset: -4
    pressure:
      id: "pressure"
      name: "BME680 Pressure"
    humidity:
      id: "humidity"
      name: "BME680 Humidity"
    gas_resistance:
      id: "gas_resistance"
      name: "BME680 Gas Resistance"
    address: 0x77
    update_interval: 60s
  - platform: template
    name: "BME680 Indoor Air Quality"
    id: iaq
    unit_of_measurement: 'AQI'
    icon: "mdi:gauge"
    # caulculation: comp_gas = log(R_gas[ohm]) + 0.04 log(Ohm)/%rh * hum[%rh]
    lambda: |-
      return log(id(gas_resistance).state) + 0.04 *  id(humidity).state;
  - platform: adc
    id: adc_mic_reading
    pin: 32
    name: "Mic Voltage out MAX9814"
    update_interval: 60s
    attenuation: auto
  - platform: ct_clamp
    sensor: adc_mic_reading
    name: "Mic Sound level out MAX9814"
    sample_duration: 500ms
    update_interval: 1000ms

output:
  - platform: template
    id: SSD1306_oled_bc
    type: float
    write_action:
      then:
      - lambda: id(SSD1306_display).set_contrast(state);
  - platform: template
    id: TM1637_bc
    type: float
    write_action:
      then:
      - lambda: id(TM1637_display).set_intensity(state);  
  - id: LED_button_left_output
    platform: gpio
    pin: GPIO4
  - id: LED_button_right_output
    platform: gpio
    pin: GPIO2      
  - id: USB_output
    platform: gpio
    pin: GPIO27         

switch:
  - platform: output
    name: "USB Output"
    output: USB_output     

light:
  - platform: monochromatic
    name: "Display SSD1306 "
    id: SSD1306_display_on_off
    output: SSD1306_oled_bc
    on_turn_on:
      then: 
      - lambda: id(SSD1306_display).turn_on();
    on_turn_off:
      then:
      - lambda: id(SSD1306_display).turn_off();
  - platform: monochromatic
    name: "Display TM1637 "
    id: TM1637_display_on_off
    output: TM1637_bc
    on_turn_on:
      then: 
      - lambda: id(TM1637_display_status) = 1; #id(TM1637_display).set_intensity(7);
    on_turn_off:
      then:
      - lambda: id(TM1637_display_status) = 0; #id(TM1637_display).set_intensity(0);          
  - platform: partition
    name: "Indicator bottom right"
    default_transition_length: 0s
    icon: "mdi:arrow-bottom-right-bold-outline"
    segments:
      - id: led_Taster_5mm_LED_Lights
        from: 0
        to: 0
  - platform: partition
    name: "Indicator bottom middle"
    default_transition_length: 0s
    icon: "mdi:arrow-down-bold-outline"
    segments:
      - id: led_Taster_5mm_LED_Lights
        from: 1
        to: 1
  - platform: partition
    name: "Indicator bottom left"
    default_transition_length: 0s
    icon: "mdi:arrow-bottom-left-bold-outline"
    segments:
      - id: led_Taster_5mm_LED_Lights
        from: 2
        to: 2
  - platform: partition
    name: "Indicator top left"
    icon: "mdi:arrow-top-left-bold-outline"
    default_transition_length: 0s
    segments:
      - id: led_Taster_5mm_LED_Lights
        from: 3
        to: 3
  - platform: partition
    name: "Indicator top middle"
    default_transition_length: 0s
    icon: "mdi:arrow-up-bold-outline"
    segments:
      - id: led_Taster_5mm_LED_Lights
        from: 4
        to: 4
  - platform: partition
    name: "Indicator top right"
    default_transition_length: 0s
    icon: "mdi:arrow-top-right-bold-outline"
    segments:
      - id: led_Taster_5mm_LED_Lights
        from: 5
        to: 5                             
  - platform: fastled_clockless
    chipset: WS2812B
    pin: GPIO33
    num_leds: 6
    rgb_order: RGB
    name: "led_Taster_5mm_LED"
    id: led_Taster_5mm_LED_Lights
    default_transition_length: 0s
    color_correct: [50%, 50%, 50%]
    restore_mode: ALWAYS_ON
  - platform: binary
    name: "LED button left"
    output: LED_button_left_output
  - platform: binary
    name: "LED button right"
    output: LED_button_right_output     

ESPHome feature requests should be made here: Issues · esphome/feature-requests · GitHub

1 Like