OTA updates not working for D1 mini board / esp8266

I have some D1 mini boards and OTA updates do not work - with the same boards they worked like a year ago, then I stopped using them and now after trying again and updating to the newest esphome version, I can only flash them via serial.

This are the logs:

$ esphome -v upload config/soil-1.yaml
INFO ESPHome 2026.2.4
INFO Reading configuration config/soil-1.yaml…
WARNING The minimum WiFi authentication mode (wifi → min_auth_mode) is not set. This controls the weakest encryption your device will accept when connecting to WiFi. Currently defaults to WPA (less secure), but will change to WPA2 (more secure) in 2026.6.0. WPA uses TKIP encryption which has known security vulnerabilities and should be avoided. WPA2 uses AES encryption which is significantly more secure. To silence this warning, explicitly set min_auth_mode under ‘wifi:’. If your router supports WPA2 or WPA3, set ‘min_auth_mode: WPA2’. If your router only supports WPA, set ‘min_auth_mode: WPA’.
Found multiple options for uploading, please choose one:
[1] /dev/ttyUSB0 (USB Serial)
[2] Over The Air (192.168.178.39)
(number): 2
INFO Connecting to 192.168.178.39 port 8266…
INFO Connected to 192.168.178.39
INFO Uploading /home/a/Projects/esphome/2026/config/.esphome/build/soil-1/.pioenvs/soil-1/firmware.bin (473296 bytes)
DEBUG Device support OTA version: 2
INFO Compressed to 334424 bytes
DEBUG Auth: SHA256 Nonce is 1739d905aa9cb9261ea0d5758b6244e40d5041b534bd9a1bc69623833bc4c5cd
DEBUG Auth: SHA256 CNonce is 94ee64eb95feae690f9d60d849256c69f9c0677cb6d91f58b6fff8cbd05671ff
DEBUG Auth: SHA256 Result is a64eced715bcf1f6e3aa4cfe3d636e71eb9c3f1f64e5a0f2c7138f2f662ddebc
DEBUG MD5 of upload is 9b22479fcd2a9e6815481c406b04e51b
ERROR Error receiving acknowledge chunk OK: timed out
WARNING Failed to upload to [‘192.168.178.39’]

[17:33:08.111][D][esphome.ota:451]: Starting handshake from 192.168.178.29
[17:33:08.114][V][esphome.ota:182]: Features: 0x03
[17:33:08.125][V][esphome.ota:589]: Auth: Nonce is 1739d905aa9cb9261ea0d5758b6244e40d5041b534bd9a1bc6962
3833bc4c5cd
[17:33:08.175][V][esphome.ota:649]: Auth: CNonce is 94ee64eb95feae690f9d60d849256c69f9c0677cb6d91f58b6ff
f8cbd05671ff
[17:33:08.186][V][esphome.ota:653]: Auth: Result is a64eced715bcf1f6e3aa4cfe3d636e71eb9c3f1f64e5a0f2c713
8f2f662ddebc
[17:33:08.194][V][esphome.ota:655]: Auth: Response is a64eced715bcf1f6e3aa4cfe3d636e71eb9c3f1f64e5a0f2c7
138f2f662ddebc
[17:33:08.200][V][esphome.ota:267]: Size is 334424 bytes
[17:33:08.205][D][esphome.ota:451]: Starting update from 192.168.178.29
[17:33:08.211][W][component:422]: esphome.ota set Warning flag: unspecified
[17:33:08.214]sleep disable
[17:33:08.219][D][ota.esp8266:120]: OTA begin: start=0x003A9000, size=334424
[17:33:08.228][V][esphome.ota:294]: Update: Binary MD5 is 9b22479fcd2a9e6815481c406b04e51b

search brought up some similar issues, but they are older and I tried the suggested fixes: setting static ip and using other board identifiers than “d1_mini”, but none of this worked, OTA updates will not finish.

Anyone got an idea what to try next?

A year ago is like an eon ago in terms of changes to esphome and possibly your environment.

Everything has to be correct for an update to work. Your log(s) is confusing, since the first part has one IP address and the second has a different one. Are the parts from trying to update the same device?

If your goal is to get your device(s) working, it seems like using serial is the solution. This is the advantage of esp devices serial almost always works.

Once you get it working via serial are you able to update using OTA?

If so, your problem is solved.

If your intent was to complain that OTA update didn’t work on a device that had no updates for a year and was turned off for some portion of that time, maybe someone else will have something helpful to say.

Show us your yaml, someone then may be able to help.

+1 connect via usb / serial

Safe mode may work as well but, y gotta get in into safe mode which is likely not easier than serial

Did you retry an OTA after flashing through serial?
Might have been some partitions changes since your previous update, and those are only applied through serial.

Hi, thanks for your reply.

Everything works fine via serial, I can flash the devices with a serial connection and the boards work. When I try to update them OTA afterwards, it fails. When I have it connected via serial to read the logs and try to update OTA I get the output from above, these logs are from the device via serial while tyring to update OTA:

[17:33:08.111][D][esphome.ota:451]: Starting handshake from 192.168.178.29
[17:33:08.114][V][esphome.ota:182]: Features: 0x03
[17:33:08.125][V][esphome.ota:589]: Auth: Nonce is 1739d905aa9cb9261ea0d5758b6244e40d5041b534bd9a1bc6962
3833bc4c5cd
[17:33:08.175][V][esphome.ota:649]: Auth: CNonce is 94ee64eb95feae690f9d60d849256c69f9c0677cb6d91f58b6ff
f8cbd05671ff
[17:33:08.186][V][esphome.ota:653]: Auth: Result is a64eced715bcf1f6e3aa4cfe3d636e71eb9c3f1f64e5a0f2c713
8f2f662ddebc
[17:33:08.194][V][esphome.ota:655]: Auth: Response is a64eced715bcf1f6e3aa4cfe3d636e71eb9c3f1f64e5a0f2c7
138f2f662ddebc
[17:33:08.200][V][esphome.ota:267]: Size is 334424 bytes
[17:33:08.205][D][esphome.ota:451]: Starting update from 192.168.178.29
[17:33:08.211][W][component:422]: esphome.ota set Warning flag: unspecified
[17:33:08.214]sleep disable
[17:33:08.219][D][ota.esp8266:120]: OTA begin: start=0x003A9000, size=334424
[17:33:08.228][V][esphome.ota:294]: Update: Binary MD5 is 9b22479fcd2a9e6815481c406b04e51b

So the device recognizes the connection and seems to start the update, but does not progress. From my computer where I start the update, the logs end with the timeout:

INFO Connecting to 192.168.178.39 port 8266…
INFO Connected to 192.168.178.39
INFO Uploading /home/a/Projects/esphome/2026/config/.esphome/build/soil-1/.pioenvs/soil-1/firmware.bin (473296 bytes)
DEBUG Device support OTA version: 2
INFO Compressed to 334424 bytes
DEBUG Auth: SHA256 Nonce is 1739d905aa9cb9261ea0d5758b6244e40d5041b534bd9a1bc69623833bc4c5cd
DEBUG Auth: SHA256 CNonce is 94ee64eb95feae690f9d60d849256c69f9c0677cb6d91f58b6fff8cbd05671ff
DEBUG Auth: SHA256 Result is a64eced715bcf1f6e3aa4cfe3d636e71eb9c3f1f64e5a0f2c7138f2f662ddebc
DEBUG MD5 of upload is 9b22479fcd2a9e6815481c406b04e51b
ERROR Error receiving acknowledge chunk OK: timed out
WARNING Failed to upload to [‘192.168.178.39’]

Here, it also connects fine and starts the update, but doesn’t get a response and times out as it seems.

As mentioned, OTA worked fine on those same boards some long time ago, so I do not assume a hardware failure.

My yaml is here:

ota:
  - platform: esphome
    password: "b19314a78d807e93102a1e0fbd9f612f"

substitutions:
  analog_update_interval: 1s

esphome:
  name: soil-1
  friendly_name: soil-1

esp8266:
  board: d1_mini

logger:
#   level: VERY_VERBOSE

api:
  encryption:
    key: "4Dit5XzeKwNyC3R+fnLGPJ9J0UU/kXRdqYBf8pOx54Y="

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  # manual_ip:
  #   static_ip: 192.168.178.39
  #   gateway: 192.168.178.1
  #   subnet: 255.255.255.0

i2c:
  scan: true
  id: bus_a

# button:
#   - platform: safe_mode
#     name: "Soil-1 Safe Mode Button"

ads1115:
  - address: 0x48
    i2c_id: bus_a

sensor:
  - platform: ads1115
    multiplexer: 'A0_GND'
    gain: 6.144
    name: "A0"
    id: ads_A0
    icon: "mdi:flash-triangle-outline"
    update_interval: ${analog_update_interval}
  - platform: template
    name: "A0 Level"
    id: a0_lvl
    update_interval: ${analog_update_interval}
    icon: "mdi:water"
    unit_of_measurement: "%"
    lambda: |-
      return id(ads_A0).state;
    filters:
      - clamp:
          min_value: 2.0
          max_value: 3.8
      - calibrate_linear:
         method: least_squares
         datapoints:
          - 2.0 -> 0
          - 3.8 -> 100
      - round: 0
      - lambda: return 100 - x;
  - platform: ads1115
    multiplexer: 'A1_GND'
    gain: 6.144
    name: "A1"
    id: ads_A1
    icon: "mdi:flash-triangle-outline"
    update_interval: ${analog_update_interval}
  - platform: template
    name: "A1 Level"
    id: a1_lvl
    update_interval: ${analog_update_interval}
    icon: "mdi:water"
    unit_of_measurement: "%"
    lambda: |-
      return id(ads_A1).state;
    filters:
      - clamp:
          min_value: 2.0
          max_value: 3.8
      - calibrate_linear:
         method: least_squares
         datapoints:
          - 2.0 -> 0
          - 3.8 -> 100
      - round: 0
      - lambda: return 100 - x;
  - platform: ads1115
    multiplexer: 'A2_GND'
    gain: 6.144
    name: "B0"
    id: ads_B0
    icon: "mdi:flash-triangle-outline"
    update_interval: ${analog_update_interval}
  - platform: template
    name: "B0 Level"
    id: b0_lvl
    update_interval: ${analog_update_interval}
    icon: "mdi:water"
    unit_of_measurement: "%"
    lambda: |-
      return id(ads_B0).state;
    filters:
      - clamp:
          min_value: 2.0
          max_value: 3.8
      - calibrate_linear:
         method: least_squares
         datapoints:
          - 2.0 -> 0
          - 3.8 -> 100
      - round: 0
      - lambda: return 100 - x;
  - platform: ads1115
    multiplexer: 'A3_GND'
    gain: 6.144
    name: "B1"
    id: ads_B1
    icon: "mdi:flash-triangle-outline"
    update_interval: ${analog_update_interval}
  - platform: template
    name: "B1 Level"
    id: b1_lvl
    update_interval: ${analog_update_interval}
    icon: "mdi:water"
    unit_of_measurement: "%"
    lambda: |-
      return id(ads_B1).state;
    filters:
      - clamp:
          min_value: 2.0
          max_value: 3.8
      - calibrate_linear:
         method: least_squares
         datapoints:
          - 2.0 -> 0
          - 3.8 -> 100
      - round: 0
      - lambda: return 100 - x;
  - platform: ina219
    address: 0x40
    update_interval: ${analog_update_interval}
    current:
      name: "INA219 Current0"
      id: cur0
    power:
      name: "INA219 Power0"
      id: pow0
    bus_voltage:
      name: "INA219 Bus Voltage0"
      id: bus0
    
switch:
  - platform: gpio
    name: "Relais A"
    id: relaisA
    pin:
      number: D5
  - platform: gpio
    name: "Relais B"
    id: relaisB
    pin:
      number: D6

I tried setting manual ip config and the safe mode button, and still OTA updates just stop and the device becomes unresponsive.

I start the updates via cli with

$ esphome -v upload config/soil-1.yaml

On Github I found some older issues with the D1 mini boards and OTA updates, but they all were resolved with esphome updates, so I suspect some kind of regression and think about opening an issue on Github. Before that, I wanted to rule out any user error here :slight_smile:

Before all that playing around I updated my local esphome installation and my current version is 2026.2.4

Yes I successfully flashed it via serial with the current version, this works fine without any issues and the board itself also works as intended.

The only thing that fails is OTA updates, and on those same board the OTA updates worked fine some long time ago, so I think the hardware itself must be working correctly and there must be some configuration or code issue.

Please see my detailed reply here OTA updates not working for D1 mini board / esp8266 - #6 by aargh for the logs and yaml

That code lives here:

  sbuf[32] = '\0';
  ESP_LOGV(TAG, "Update: Binary MD5 is %s", sbuf);
  this->backend_->set_update_md5(sbuf);

  // Acknowledge MD5 OK - 1 byte
  this->write_byte_(ota::OTA_RESPONSE_BIN_MD5_OK);

  while (total < ota_size) {
    // TODO: timeout check

It looks like you are possibly getting bit by that TODO and the server side is not getting the acknowledgement that the client got the MD5 hash okay. There isn’t enough logging in the code to say for sure what is going wrong. The log from the server says it never got the ack. The client logged that it was at the point that it should have sent one. So something unusual is going on.

This is a useful hint, thanks.

I created an issue here OTA updates timeout with D1 mini boards / esp8266, device becomes unresponsive · Issue #14662 · esphome/esphome · GitHub

If this gets resolved I will reply back here in case others run into the same problem.

edit:

It seems like it is a code issue, a working fix done by AI is provided here OTA updates timeout with D1 mini boards / esp8266, device becomes unresponsive · Issue #14662 · esphome/esphome · GitHub

so this is solved, thanks for the participation and advice.