Skip to content

Commit 23ce1cc

Browse files
committed
Merge branch 'feature/add_ota_resumption_feature_in_advanced_ota_example' into 'master'
feat(app_utils): add ota resumption feature in advanced_ota_example Closes IDF-2455, IDF-10421, and IDFGH-12065 See merge request espressif/esp-idf!32927
2 parents 5950be0 + 50adc8b commit 23ce1cc

File tree

14 files changed

+588
-33
lines changed

14 files changed

+588
-33
lines changed

components/app_update/esp_ota_ops.c

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -47,6 +47,7 @@ typedef struct ota_ops_entry_ {
4747
bool need_erase;
4848
uint32_t wrote_size;
4949
uint8_t partial_bytes;
50+
bool ota_resumption;
5051
WORD_ALIGNED_ATTR uint8_t partial_data[16];
5152
LIST_ENTRY(ota_ops_entry_) entries;
5253
} ota_ops_entry_t;
@@ -119,6 +120,24 @@ static esp_ota_img_states_t set_new_state_otadata(void)
119120
#endif
120121
}
121122

123+
static ota_ops_entry_t* esp_ota_init_entry(const esp_partition_t *partition)
124+
{
125+
ota_ops_entry_t *new_entry = (ota_ops_entry_t *) calloc(1, sizeof(ota_ops_entry_t));
126+
if (new_entry == NULL) {
127+
return NULL;
128+
}
129+
130+
LIST_INSERT_HEAD(&s_ota_ops_entries_head, new_entry, entries);
131+
132+
new_entry->partition.staging = partition;
133+
new_entry->partition.final = partition;
134+
new_entry->partition.finalize_with_copy = false;
135+
new_entry->handle = ++s_ota_ops_last_handle;
136+
137+
return new_entry;
138+
}
139+
140+
122141
esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp_ota_handle_t *out_handle)
123142
{
124143
ota_ops_entry_t *new_entry;
@@ -153,17 +172,10 @@ esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp
153172
#endif
154173
}
155174

156-
new_entry = (ota_ops_entry_t *) calloc(1, sizeof(ota_ops_entry_t));
175+
new_entry = esp_ota_init_entry(partition);
157176
if (new_entry == NULL) {
158177
return ESP_ERR_NO_MEM;
159178
}
160-
161-
LIST_INSERT_HEAD(&s_ota_ops_entries_head, new_entry, entries);
162-
163-
new_entry->partition.staging = partition;
164-
new_entry->partition.final = partition;
165-
new_entry->partition.finalize_with_copy = false;
166-
new_entry->handle = ++s_ota_ops_last_handle;
167179
new_entry->need_erase = (image_size == OTA_WITH_SEQUENTIAL_WRITES);
168180
*out_handle = new_entry->handle;
169181

@@ -197,6 +209,54 @@ esp_err_t esp_ota_begin(const esp_partition_t *partition, size_t image_size, esp
197209
return ESP_OK;
198210
}
199211

212+
esp_err_t esp_ota_resume(const esp_partition_t *partition, const size_t erase_size, const size_t image_offset, esp_ota_handle_t *out_handle)
213+
{
214+
ota_ops_entry_t *new_entry;
215+
216+
if ((partition == NULL) || (out_handle == NULL)) {
217+
return ESP_ERR_INVALID_ARG;
218+
}
219+
220+
if (image_offset > partition->size) {
221+
return ESP_ERR_INVALID_ARG;
222+
}
223+
224+
partition = esp_partition_verify(partition);
225+
if (partition == NULL) {
226+
return ESP_ERR_NOT_FOUND;
227+
}
228+
229+
if (partition->type == ESP_PARTITION_TYPE_APP) {
230+
// The staging partition cannot be of type Factory, but the final partition can be.
231+
if (!is_ota_partition(partition)) {
232+
return ESP_ERR_INVALID_ARG;
233+
}
234+
}
235+
236+
const esp_partition_t* running_partition = esp_ota_get_running_partition();
237+
if (partition == running_partition) {
238+
return ESP_ERR_OTA_PARTITION_CONFLICT;
239+
}
240+
241+
new_entry = esp_ota_init_entry(partition);
242+
if (new_entry == NULL) {
243+
return ESP_ERR_NO_MEM;
244+
}
245+
246+
if (partition->type == ESP_PARTITION_TYPE_BOOTLOADER) {
247+
esp_image_bootloader_offset_set(partition->address);
248+
}
249+
if (partition->type == ESP_PARTITION_TYPE_BOOTLOADER || partition->type == ESP_PARTITION_TYPE_PARTITION_TABLE) {
250+
esp_flash_set_dangerous_write_protection(esp_flash_default_chip, false);
251+
}
252+
253+
new_entry->ota_resumption = true;
254+
new_entry->wrote_size = image_offset;
255+
new_entry->need_erase = (erase_size == OTA_WITH_SEQUENTIAL_WRITES);
256+
*out_handle = new_entry->handle;
257+
return ESP_OK;
258+
}
259+
200260
esp_err_t esp_ota_set_final_partition(esp_ota_handle_t handle, const esp_partition_t *final, bool finalize_with_copy)
201261
{
202262
ota_ops_entry_t *it = get_ota_ops_entry(handle);
@@ -206,9 +266,14 @@ esp_err_t esp_ota_set_final_partition(esp_ota_handle_t handle, const esp_partiti
206266
if (it == NULL) {
207267
return ESP_ERR_NOT_FOUND;
208268
}
209-
if (it->wrote_size != 0) {
269+
270+
// If OTA resumption is enabled, it->wrote_size may already contain the size of previously written data.
271+
// Ensure that wrote_size is zero only when OTA resumption is disabled, as any non-zero value in this case
272+
// indicates an invalid state.
273+
if (!it->ota_resumption && it->wrote_size != 0) {
210274
return ESP_ERR_INVALID_STATE;
211275
}
276+
212277
if (it->partition.staging != final) {
213278
const esp_partition_t* final_partition = esp_partition_verify(final);
214279
if (final_partition == NULL) {

components/app_update/include/esp_ota_ops.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,32 @@ int esp_ota_get_app_elf_sha256(char* dst, size_t size) __attribute__((deprecated
104104
*/
105105
esp_err_t esp_ota_begin(const esp_partition_t* partition, size_t image_size, esp_ota_handle_t* out_handle);
106106

107+
/**
108+
* @brief Resume an interrupted OTA update by continuing to write to the specified partition.
109+
*
110+
* This function is used when an OTA update was previously started and needs to be resumed after an interruption.
111+
* It continues the OTA process from the specified offset within the partition.
112+
*
113+
* Unlike esp_ota_begin(), this function does not erase the partition which receives the OTA update, but rather expects that part of the image
114+
* has already been written correctly, and it resumes writing from the given offset.
115+
*
116+
* @param partition Pointer to info for the partition which is receiving the OTA update. Required.
117+
* @param erase_size Specifies how much flash memory to erase before resuming OTA, depending on whether a sequential write or a bulk erase is being used.
118+
* @param image_offset Offset from where to resume the OTA process. Should be set to the number of bytes already written.
119+
* @param out_handle On success, returns a handle that should be used for subsequent esp_ota_write() and esp_ota_end() calls.
120+
*
121+
* @return
122+
* - ESP_OK: OTA operation resumed successfully.
123+
* - ESP_ERR_INVALID_ARG: partition, out_handle were NULL or image_offset arguments is negative, or partition doesn't point to an OTA app partition.
124+
* - ESP_ERR_NO_MEM: Cannot allocate memory for OTA operation.
125+
* - ESP_ERR_OTA_PARTITION_CONFLICT: Partition holds the currently running firmware, cannot update in place.
126+
* - ESP_ERR_NOT_FOUND: Partition argument not found in partition table.
127+
* - ESP_ERR_OTA_SELECT_INFO_INVALID: The OTA data partition contains invalid data.
128+
* - ESP_ERR_INVALID_SIZE: Partition doesn't fit in configured flash size.
129+
* - ESP_ERR_FLASH_OP_TIMEOUT or ESP_ERR_FLASH_OP_FAIL: Flash write failed.
130+
*/
131+
esp_err_t esp_ota_resume(const esp_partition_t *partition, const size_t erase_size, const size_t image_offset, esp_ota_handle_t *out_handle);
132+
107133
/**
108134
* @brief Set the final destination partition for OTA update
109135
*

components/esp_common/src/esp_err_to_name.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,10 @@ static const esp_err_msg_t esp_err_msg_table[] = {
659659
# endif
660660
# ifdef ESP_ERR_HTTP_NOT_MODIFIED
661661
ERR_TBL_IT(ESP_ERR_HTTP_NOT_MODIFIED), /* 28681 0x7009 HTTP 304 Not Modified, no update available */
662+
# endif
663+
# ifdef ESP_ERR_HTTP_RANGE_NOT_SATISFIABLE
664+
ERR_TBL_IT(ESP_ERR_HTTP_RANGE_NOT_SATISFIABLE), /* 28682 0x700a HTTP 416 Range Not Satisfiable,
665+
requested range in header is incorrect */
662666
# endif
663667
// components/esp-tls/esp_tls_errors.h
664668
# ifdef ESP_ERR_ESP_TLS_BASE

components/esp_http_client/include/esp_http_client.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ typedef enum {
227227
HttpStatus_Unauthorized = 401,
228228
HttpStatus_Forbidden = 403,
229229
HttpStatus_NotFound = 404,
230+
HttpStatus_RangeNotSatisfiable = 416,
230231

231232
/* 5xx - Server Error */
232233
HttpStatus_InternalError = 500
@@ -242,6 +243,7 @@ typedef enum {
242243
#define ESP_ERR_HTTP_EAGAIN (ESP_ERR_HTTP_BASE + 7) /*!< Mapping of errno EAGAIN to esp_err_t */
243244
#define ESP_ERR_HTTP_CONNECTION_CLOSED (ESP_ERR_HTTP_BASE + 8) /*!< Read FIN from peer and the connection closed */
244245
#define ESP_ERR_HTTP_NOT_MODIFIED (ESP_ERR_HTTP_BASE + 9) /*!< HTTP 304 Not Modified, no update available */
246+
#define ESP_ERR_HTTP_RANGE_NOT_SATISFIABLE (ESP_ERR_HTTP_BASE + 10) /*!< HTTP 416 Range Not Satisfiable, requested range in header is incorrect */
245247

246248
/**
247249
* @brief Start a HTTP session

components/esp_https_ota/include/esp_https_ota.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ typedef struct {
6464
bool partial_http_download; /*!< Enable Firmware image to be downloaded over multiple HTTP requests */
6565
int max_http_request_size; /*!< Maximum request size for partial HTTP download */
6666
uint32_t buffer_caps; /*!< The memory capability to use when allocating the buffer for OTA update. Default capability is MALLOC_CAP_DEFAULT */
67+
bool ota_resumption; /*!< Enable resumption in downloading of OTA image between reboots */
68+
size_t ota_image_bytes_written; /*!< Number of OTA image bytes written to flash so far, updated by the application when OTA data is written successfully in the target OTA partition. */
6769
#if CONFIG_ESP_HTTPS_OTA_DECRYPT_CB || __DOXYGEN__
6870
decrypt_cb_t decrypt_cb; /*!< Callback for external decryption layer */
6971
void *decrypt_user_ctx; /*!< User context for external decryption layer */

0 commit comments

Comments
 (0)