diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index d585029859..5ebab7a838 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -878,8 +878,9 @@ ota::OTAResponseTypes ESPHomeOTAComponent::inflate_data_(uint8_t *in, size_t ima session.dest_limit = session.window + OTA_INFLATE_WINDOW_SIZE; session.flushed = 0; res = ota_inflate(&session); - if (res < 0) { - // eof means the read callback failed, which is already logged + // A stored block keeps decoding zeros after the read callback failed, so + // eof is checked as well; that failure is already logged + if (res < 0 || session.eof) { if (!session.eof) { ESP_LOGW(TAG, "Inflate err %d", res); } diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index 6e0c7d20a7..63bb3c3075 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -124,12 +124,12 @@ class ESPHomeOTAComponent final : public ota::OTAComponent { // Upload accounting shared by the data loop and the inflate read callback struct DataTransfer { - size_t ota_size; // bytes the client sends - size_t total{0}; // bytes received so far + size_t ota_size{0}; // bytes the client sends + size_t total{0}; // bytes received so far #if USE_OTA_VERSION == 2 size_t acknowledged{0}; #endif - uint32_t last_data_ms; + uint32_t last_data_ms{0}; uint32_t last_progress{0}; }; // Receives up to OTA_BUFFER_SIZE bytes of upload data into buf, waiting up to diff --git a/esphome/components/esphome/ota/ota_esphome_inflate.c b/esphome/components/esphome/ota/ota_esphome_inflate.c index c3e1213989..58baecfc8c 100644 --- a/esphome/components/esphome/ota/ota_esphome_inflate.c +++ b/esphome/components/esphome/ota/ota_esphome_inflate.c @@ -334,6 +334,10 @@ static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) { return TINF_DATA_ERROR; } + if (sym < 0) { + return sym; + } + /* literal byte */ if (sym < 256) { TINF_PUT(d, sym); diff --git a/esphome/components/ota/ota_backend_arduino_rp2.h b/esphome/components/ota/ota_backend_arduino_rp2.h index cae523e2b3..8874800609 100644 --- a/esphome/components/ota/ota_backend_arduino_rp2.h +++ b/esphome/components/ota/ota_backend_arduino_rp2.h @@ -16,7 +16,8 @@ class ArduinoRP2OTABackend final { OTAResponseTypes end(); void abort(); // A gzip image is staged on LittleFS as is; the core's OTA stub inflates it - // into the app region at reboot, the same way the ESP8266 bootloader does. + // into the app region at reboot (arduino-pico 2.4.0 and later, RP2350 from + // 4.0.3; ESPHome requires 6.0.0), the same way the ESP8266 bootloader does. // begin() then sees the gzip size, so only the staging space is checked up // front; the inflated size is not known until the stub reads the trailer. static constexpr bool supports_compression() { return true; } diff --git a/esphome/espota2.py b/esphome/espota2.py index 379c4bd796..36a9b76659 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -72,8 +72,9 @@ SERVER_FEATURE_SUPPORTS_PARTITION_ACCESS = 0x02 SERVER_FEATURE_SUPPORTS_NOISE = 0x04 SERVER_FEATURE_SUPPORTS_DEFLATE = 0x08 -# Window of the raw deflate stream sent to a device that inflates on the fly; -# the device's OTA_INFLATE_WINDOW_SIZE (4 KB) must be at least 1 << this +# Window of the raw deflate stream sent to a device that inflates on the fly. +# Part of the protocol: the server's deflate bit promises a 4 KB ring window +# (OTA_INFLATE_WINDOW_SIZE), so a larger window needs a new feature bit DEFLATE_WINDOW_BITS = 12 NOISE_FRAME_INDICATOR = 0x01 diff --git a/tests/components/ota/test.bk72xx-ard.yaml b/tests/components/ota/test.bk72xx-ard.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/ota/test.bk72xx-ard.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/integration/test_host_ota.py b/tests/integration/test_host_ota.py index da5363ef2e..d25f9e872d 100644 --- a/tests/integration/test_host_ota.py +++ b/tests/integration/test_host_ota.py @@ -186,7 +186,7 @@ async def test_host_ota_self_update( if "OTA staged at" in line: staged.set() # The host backend has no gzip support, so the upload negotiates deflate - if " bytes from " in line: + if "Inflated " in line and " bytes from " in line: inflated.set() dev.on_log(line)