Add dump_config and error messages

This commit is contained in:
Mat931
2026-04-24 21:58:48 +02:00
parent 5a0cc68f69
commit 1dcf0bed56
5 changed files with 105 additions and 55 deletions
+19 -1
View File
@@ -100,6 +100,25 @@ void ESPHomeOTAComponent::dump_config() {
ESP_LOGCONFIG(TAG, " Password configured");
}
#endif
#ifdef USE_OTA_PARTITIONS
ESP_LOGCONFIG(TAG, " Partition access allowed");
uint32_t running_app_offset;
size_t running_app_size;
ota::get_running_app_position(running_app_offset, running_app_size);
ESP_LOGCONFIG(TAG, " Running app:\n Partition address: 0x%X\n Used size: %d bytes",
running_app_offset, running_app_size);
#ifdef USE_ESP32
ESP_LOGCONFIG(TAG, " Partition table:");
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
while (it != NULL) {
const esp_partition_t *p = esp_partition_get(it);
ESP_LOGCONFIG(TAG, " %s: type=0x%X, subtype=0x%X, address=0x%X, size=0x%X",
p->label, p->type, p->subtype, p->address, p->size);
it = esp_partition_next(it);
}
esp_partition_iterator_release(it);
#endif
#endif
}
void ESPHomeOTAComponent::loop() {
@@ -214,7 +233,6 @@ void ESPHomeOTAComponent::handle_handshake_() {
this->handshake_buf_[1] |= SERVER_FEATURE_SUPPORTS_COMPRESSION;
}
this->handshake_buf_[1] |= SERVER_FEATURE_SUPPORTS_PARTITION_ACCESS;
} else {
#endif
this->handshake_buf_[0] =
+2
View File
@@ -40,6 +40,8 @@ enum OTAResponseTypes {
OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = 0x8C,
OTA_RESPONSE_ERROR_SIGNATURE_INVALID = 0x8D,
OTA_RESPONSE_ERROR_UNSUPPORTED_OTA_TYPE = 0x8E,
OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY = 0x8F,
OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE = 0x90,
OTA_RESPONSE_ERROR_UNKNOWN = 0xFF,
};
+62 -54
View File
@@ -26,7 +26,8 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size, ota::OTAType ota_type)
if (this->ota_type_ == ota::OTA_TYPE_UPDATE_PARTITION_TABLE) {
if (image_size > ESP_PARTITION_TABLE_SIZE || image_size > ESP_PARTITION_TABLE_MAX_LEN ||
image_size > OTA_BUFFER_SIZE) {
return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE;
ESP_LOGE(TAG, "Wrong partition table size");
return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY;
}
memset(this->buf_, 0xFF, sizeof this->buf_);
this->buf_written_ = 0;
@@ -98,7 +99,8 @@ OTAResponseTypes IDFOTABackend::write(uint8_t *data, size_t len) {
#ifdef USE_OTA_PARTITIONS
if (this->ota_type_ == ota::OTA_TYPE_UPDATE_PARTITION_TABLE) {
if (len > OTA_BUFFER_SIZE - this->buf_written_) {
return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE;
ESP_LOGE(TAG, "Wrong partition table size");
return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY;
}
memcpy(this->buf_ + this->buf_written_, data, len);
this->buf_written_ += len;
@@ -176,49 +178,26 @@ void IDFOTABackend::abort() {
#ifdef USE_OTA_PARTITIONS
OTAResponseTypes IDFOTABackend::update_partition_table() {
esp_err_t err;
int num_partitions;
if (this->buf_written_ == 0 || this->image_size_ != this->buf_written_) {
ESP_LOGE(TAG, "not enough data received (%d/%d bytes)", this->buf_written_, this->image_size_);
return OTA_RESPONSE_ERROR_UNKNOWN;
ESP_LOGE(TAG, "Not enough data received");
return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY;
}
// Get running app partition and used size
static uint32_t running_app_offset = 0;
static size_t running_app_size = 0;
const esp_partition_t *running_app_part = nullptr;
if (running_app_size == 0) {
running_app_part = esp_ota_get_running_partition();
// esp_ota_get_running_partition() returns a pointer to invalid data after esp_partition_unload_all() was called on
// a previous run. Cache the running app offset and size.
running_app_size = ((running_app_size + running_app_part->erase_size - 1) / running_app_part->erase_size) *
running_app_part->erase_size;
running_app_offset = running_app_part->address;
const esp_partition_pos_t running_app_pos = {
.offset = running_app_part->address,
.size = running_app_part->size,
};
esp_image_metadata_t image_metadata;
image_metadata.start_addr = running_app_part->address;
err = esp_image_verify(ESP_IMAGE_VERIFY_SILENT, &running_app_pos, &image_metadata);
if (err == ESP_OK && image_metadata.image_len < running_app_part->size) {
running_app_size = image_metadata.image_len;
}
// Align running_app_size to flash sectors
running_app_size = ((running_app_size + running_app_part->erase_size - 1) / running_app_part->erase_size) *
running_app_part->erase_size;
ESP_LOGD(TAG, "Running app: address=0x%X partition_size=0x%X used_size=0x%X, aligned_size=0x%X",
running_app_part->address, running_app_part->size, image_metadata.image_len, running_app_size);
}
uint32_t running_app_offset;
size_t running_app_size;
get_running_app_position(running_app_offset, running_app_size);
// Get partition table partition
err = esp_partition_register_external(nullptr, ESP_PRIMARY_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_SIZE,
esp_err_t err = esp_partition_register_external(nullptr, ESP_PRIMARY_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_SIZE,
"PrimaryPrtTable", ESP_PARTITION_TYPE_PARTITION_TABLE,
ESP_PARTITION_SUBTYPE_PARTITION_TABLE_PRIMARY, &this->partition_table_part_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_partition_register_external failed (err=0x%X) ", err);
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY;
}
// Verify existing partition table
const esp_partition_info_t *existing_partition_table = NULL;
esp_partition_mmap_handle_t partition_table_map;
@@ -226,13 +205,13 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
(const void **) &existing_partition_table, &partition_table_map);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_partition_mmap failed (err=0x%X) ", err);
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY;
}
err = esp_partition_table_verify(existing_partition_table, true, &num_partitions);
esp_partition_munmap(partition_table_map);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_partition_table_verify failed (existing partition table) (err=0x%X) ", err);
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY;
}
// Verify new partition table
@@ -241,7 +220,7 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
err = esp_partition_table_verify(new_partition_table, true, &num_partitions);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_partition_table_verify failed (new partition table) (err=0x%X) ", err);
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY;
}
// Check if the required app and otadata partitions exist in the new partition table
@@ -283,15 +262,15 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
}
if (app_index == -1 && app_index_with_copy == -1) {
ESP_LOGE(TAG, "No compatible app partition found in the new partition table");
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY;
}
if (app_partitions_found < 2 || otadata_index == -1) {
ESP_LOGE(TAG, "New partition table is missing the required app or otadata partitions");
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY;
}
if (!otadata_no_overlap) {
ESP_LOGE(TAG, "New otadata partition overlaps with running app");
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_VERIFY;
}
ESP_LOGD(TAG, "Checks passed, starting partition table update");
@@ -301,18 +280,17 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
// Copy the running app partition to new position if needed
if (app_index == -1) {
if (running_app_part == nullptr) {
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
while (it != NULL) {
const esp_partition_t *p = esp_partition_get(it);
const esp_partition_info_t *new_part = &new_partition_table[app_index == -1 ? app_index_with_copy : app_index];
if (p->address == running_app_offset && p->size >= running_app_size) {
running_app_part = p;
}
it = esp_partition_next(it);
const esp_partition_t *running_app_part = nullptr;
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
while (it != NULL) {
const esp_partition_t *p = esp_partition_get(it);
const esp_partition_info_t *new_part = &new_partition_table[app_index == -1 ? app_index_with_copy : app_index];
if (p->address == running_app_offset && p->size >= running_app_size) {
running_app_part = p;
}
esp_partition_iterator_release(it);
it = esp_partition_next(it);
}
esp_partition_iterator_release(it);
ESP_LOGD(TAG, "Copying running app from 0x%X to 0x%X (size: 0x%X)", running_app_part->address,
app_copy_target_part->address, running_app_size);
@@ -341,7 +319,7 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_partition_copy failed (err=0x%X) ", err);
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
}
}
@@ -351,18 +329,18 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
esp_ota_abort(this->update_handle_);
this->update_handle_ = 0;
ESP_LOGE(TAG, "esp_ota_begin failed (err=0x%X) ", err);
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
}
err = esp_ota_write(this->update_handle_, this->buf_, this->image_size_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_write failed (err=0x%X) ", err);
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
}
err = esp_ota_end(this->update_handle_);
this->update_handle_ = 0;
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed (err=0x%X) ", err);
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
}
esp_partition_unload_all();
@@ -382,10 +360,40 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
err = esp_ota_set_boot_partition(new_boot_partition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (err=0x%X) ", err);
return OTA_RESPONSE_ERROR_UNKNOWN;
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
}
return OTA_RESPONSE_OK;
}
void get_running_app_position(uint32_t &offset, size_t &size) {
// Gets the start address and the used length aligned to sectors of the running app.
// This function needs to be called once before calling esp_partition_unload_all().
// The results are stored using static variables because esp_ota_get_running_partition()
// does not return valid data after calling esp_partition_unload_all().
static uint32_t running_app_offset = 0;
static size_t running_app_size = 0;
if (running_app_size == 0) {
const esp_partition_t *running_app_part = esp_ota_get_running_partition();
running_app_size = ((running_app_size + running_app_part->erase_size - 1) / running_app_part->erase_size) *
running_app_part->erase_size;
running_app_offset = running_app_part->address;
const esp_partition_pos_t running_app_pos = {
.offset = running_app_part->address,
.size = running_app_part->size,
};
esp_image_metadata_t image_metadata;
image_metadata.start_addr = running_app_part->address;
esp_err_t err = esp_image_verify(ESP_IMAGE_VERIFY_SILENT, &running_app_pos, &image_metadata);
if (err == ESP_OK && image_metadata.image_len < running_app_part->size) {
running_app_size = image_metadata.image_len;
}
// Align running_app_size to flash sectors
running_app_size = ((running_app_size + running_app_part->erase_size - 1) / running_app_part->erase_size) *
running_app_part->erase_size;
}
offset = running_app_offset;
size = running_app_size;
}
#endif
} // namespace esphome::ota
@@ -13,6 +13,10 @@ namespace esphome::ota {
static constexpr size_t OTA_BUFFER_SIZE = ESP_PARTITION_TABLE_MAX_LEN; // 0xC00
#endif
#ifdef USE_OTA_PARTITIONS
void get_running_app_position(uint32_t &offset, size_t &size);
#endif
class IDFOTABackend final {
public:
#ifdef USE_OTA_PARTITIONS
+18
View File
@@ -46,6 +46,9 @@ RESPONSE_ERROR_NO_UPDATE_PARTITION = 0x8A
RESPONSE_ERROR_MD5_MISMATCH = 0x8B
RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = 0x8C
RESPONSE_ERROR_SIGNATURE_INVALID = 0x8D
RESPONSE_ERROR_UNSUPPORTED_OTA_TYPE = 0x8E
RESPONSE_ERROR_PARTITION_TABLE_VERIFY = 0x8F
RESPONSE_ERROR_PARTITION_TABLE_UPDATE = 0x90
RESPONSE_ERROR_UNKNOWN = 0xFF
OTA_VERSION_1_0 = 1
@@ -206,6 +209,21 @@ def check_error(data: list[int] | bytes, expect: int | list[int] | None) -> None
"with the correct key. Ensure the signing key matches the one used to build "
"the firmware currently running on the device."
)
if dat == RESPONSE_ERROR_UNSUPPORTED_OTA_TYPE:
raise OTAError(
"Error: The requested OTA type is not supported by the device."
)
if dat == RESPONSE_ERROR_PARTITION_TABLE_VERIFY:
raise OTAError(
"Error: The partition table update could not be verified. No changes were "
"made to the flash content. Check the logs for more information and retry."
)
if dat == RESPONSE_ERROR_PARTITION_TABLE_UPDATE:
raise OTAError(
"Error: An error occurred while updating the partition table. The device may not "
"be able to reboot to a working application. Check the logs and retry the update "
"without rebooting the device."
)
if dat == RESPONSE_ERROR_UNKNOWN:
raise OTAError("Unknown error from ESP")
if not isinstance(expect, (list, tuple)):