ESPHome 2026.6.2
Loading...
Searching...
No Matches
ota_backend_esp_idf.cpp
Go to the documentation of this file.
1#ifdef USE_ESP32
3
7#include "esphome/core/log.h"
8
9#include <esp_ota_ops.h>
10#include <esp_task_wdt.h>
11#include <spi_flash_mmap.h>
12
13namespace esphome::ota {
14
15static const char *const TAG = "ota.idf";
16
17std::unique_ptr<IDFOTABackend> make_ota_backend() { return make_unique<IDFOTABackend>(); }
18
20#ifdef USE_OTA_PARTITIONS
21 this->ota_type_ = ota_type;
22 if (this->ota_type_ == ota::OTA_TYPE_UPDATE_PARTITION_TABLE) {
23 // Reject any size other than ESP_PARTITION_TABLE_MAX_LEN
24 if (image_size != ESP_PARTITION_TABLE_MAX_LEN) {
25 ESP_LOGE(TAG, "Wrong partition table size: expected %u bytes, got %zu", ESP_PARTITION_TABLE_MAX_LEN, image_size);
27 }
28 memset(this->buf_, 0xFF, sizeof this->buf_);
29 this->buf_written_ = 0;
30 this->image_size_ = image_size;
31 this->md5_.init();
32 return OTA_RESPONSE_OK;
33 }
34 if (this->ota_type_ == ota::OTA_TYPE_UPDATE_BOOTLOADER) {
35 OTAResponseTypes result = this->prepare_bootloader_update_(image_size);
36 if (result != OTA_RESPONSE_OK) {
37 return result;
38 }
39 }
40 if (!this->is_app_or_bootloader_update_()) {
42 }
43#else
44 if (ota_type != ota::OTA_TYPE_UPDATE_APP) {
46 }
47#endif
48#ifdef USE_OTA_ROLLBACK
49 // If we're starting an OTA, the current boot is good enough - mark it valid
50 // to prevent rollback and allow the OTA to proceed even if the safe mode
51 // timer hasn't expired yet.
52 esp_ota_mark_app_valid_cancel_rollback();
53#endif
54
55 this->partition_ = esp_ota_get_next_update_partition(nullptr);
56 if (this->partition_ == nullptr) {
58 }
59
60 // esp_ota_begin() erases the destination region, which blocks loopTask and
61 // scales with the erase size -- a fixed watchdog overruns on large OTA slots.
62 // An unknown size (0, e.g. web_server uploads) erases the whole partition, so
63 // budget against the bytes actually erased. ~10ms/KiB (conservative
64 // ~100 KiB/s erase) over a 15s floor; panic stays on so a stuck erase still
65 // resets rather than hanging forever.
66 size_t erase_size = image_size;
67 if (erase_size == 0 || erase_size > this->partition_->size) {
68 erase_size = this->partition_->size;
69 }
70 const uint32_t erase_budget_ms = 15000 + (erase_size >> 10) * 10;
71 watchdog::WatchdogManager watchdog(erase_budget_ms);
72 esp_err_t err = esp_ota_begin(this->partition_, image_size, &this->update_handle_);
73
74 if (err != ESP_OK) {
75 ESP_LOGE(TAG, "esp_ota_begin failed (err=0x%X)", err);
76 esp_ota_abort(this->update_handle_);
77 this->update_handle_ = 0;
78 if (err == ESP_ERR_INVALID_SIZE) {
80 } else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) {
82 } else if (err == ESP_ERR_OTA_PARTITION_CONFLICT) {
83 // This error appears with 1 factory and 1 ota partition
85 }
87 }
88#ifdef USE_OTA_PARTITIONS
89 if (this->ota_type_ == ota::OTA_TYPE_UPDATE_BOOTLOADER) {
91 if (result != OTA_RESPONSE_OK) {
92 return result;
93 }
94 }
95#endif
96 this->md5_.init();
97 return OTA_RESPONSE_OK;
98}
99
100void IDFOTABackend::set_update_md5(const char *expected_md5) {
101 memcpy(this->expected_bin_md5_, expected_md5, 32);
102 this->md5_set_ = true;
103}
104
106#ifdef USE_OTA_PARTITIONS
107 if (this->ota_type_ == ota::OTA_TYPE_UPDATE_PARTITION_TABLE) {
108 if (len > PARTITION_TABLE_BUFFER_SIZE - this->buf_written_) {
109 ESP_LOGE(TAG, "Wrong partition table size");
111 }
112 memcpy(this->buf_ + this->buf_written_, data, len);
113 this->buf_written_ += len;
114 this->md5_.add(data, len);
115 return OTA_RESPONSE_OK;
116 }
117 if (!this->is_app_or_bootloader_update_()) {
119 }
120#endif
121 esp_err_t err = esp_ota_write(this->update_handle_, data, len);
122 this->md5_.add(data, len);
123 if (err != ESP_OK) {
124 ESP_LOGE(TAG, "esp_ota_write failed (err=0x%X)", err);
125 if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
127 } else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) {
129 }
131 }
132 return OTA_RESPONSE_OK;
133}
134
136 if (this->md5_set_) {
137 this->md5_.calculate();
138 if (!this->md5_.equals_hex(this->expected_bin_md5_)) {
139 this->abort();
141 }
142 }
143#ifdef USE_OTA_PARTITIONS
144 if (this->ota_type_ == ota::OTA_TYPE_UPDATE_PARTITION_TABLE) {
145 return this->update_partition_table();
146 }
147 if (!this->is_app_or_bootloader_update_()) {
149 }
150#endif
151 esp_err_t err = esp_ota_end(this->update_handle_);
152 this->update_handle_ = 0;
153 if (err != ESP_OK) {
154 ESP_LOGE(TAG, "esp_ota_end failed (err=0x%X)", err);
155 }
156#ifdef USE_OTA_PARTITIONS
157 if (this->ota_type_ == ota::OTA_TYPE_UPDATE_BOOTLOADER) {
158 return this->finalize_bootloader_update_(err);
159 }
160#endif
161 if (err == ESP_OK) {
162 err = esp_ota_set_boot_partition(this->partition_);
163 if (err == ESP_OK) {
164 return OTA_RESPONSE_OK;
165 }
166 }
167 if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
168#ifdef USE_OTA_SIGNED_VERIFICATION
169 ESP_LOGE(TAG, "OTA validation failed (err=0x%X) - possible signature verification failure", err);
171#else
173#endif
174 }
175 if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) {
177 }
179}
180
182#ifdef USE_OTA_PARTITIONS
183 if (this->partition_table_part_ != nullptr) {
184 esp_partition_deregister_external(this->partition_table_part_);
185 this->partition_table_part_ = nullptr;
186 }
187 if (this->bootloader_part_ != nullptr) {
188 esp_partition_deregister_external(this->bootloader_part_);
189 this->bootloader_part_ = nullptr;
190 }
191#endif
192 // esp_ota_abort with handle 0 returns ESP_ERR_INVALID_ARG harmlessly, so this is safe whether
193 // or not an update is in flight.
194 esp_ota_abort(this->update_handle_);
195 this->update_handle_ = 0;
196}
197
198} // namespace esphome::ota
199#endif // USE_ESP32
bool equals_hex(const char *expected)
Compare the hash against a provided hex-encoded hash.
Definition hash_base.h:35
void calculate() override
Compute the digest, based on the provided data.
Definition md5.cpp:16
void add(const uint8_t *data, size_t len) override
Add bytes of data for the digest.
Definition md5.cpp:14
void init() override
Initialize a new MD5 digest computation.
Definition md5.cpp:9
void set_update_md5(const char *md5)
OTAResponseTypes finalize_bootloader_update_(esp_err_t ota_end_err)
OTAResponseTypes begin(size_t image_size, ota::OTAType ota_type=ota::OTA_TYPE_UPDATE_APP)
OTAResponseTypes prepare_bootloader_update_(size_t image_size)
OTAResponseTypes write(uint8_t *data, size_t len)
@ OTA_TYPE_UPDATE_BOOTLOADER
Definition ota_backend.h:63
@ OTA_TYPE_UPDATE_PARTITION_TABLE
Definition ota_backend.h:62
@ OTA_RESPONSE_ERROR_MD5_MISMATCH
Definition ota_backend.h:41
@ OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE
Definition ota_backend.h:39
@ OTA_RESPONSE_ERROR_WRITING_FLASH
Definition ota_backend.h:33
@ OTA_RESPONSE_ERROR_UNSUPPORTED_OTA_TYPE
Definition ota_backend.h:44
@ OTA_RESPONSE_ERROR_UPDATE_END
Definition ota_backend.h:34
@ OTA_RESPONSE_ERROR_SIGNATURE_INVALID
Definition ota_backend.h:43
@ OTA_RESPONSE_ERROR_UNKNOWN
Definition ota_backend.h:49
@ OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION
Definition ota_backend.h:40
@ OTA_RESPONSE_ERROR_MAGIC
Definition ota_backend.h:30
@ OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY
Definition ota_backend.h:45
std::unique_ptr< ArduinoLibreTinyOTABackend > make_ota_backend()
const void size_t len
Definition hal.h:64
static void uint32_t