Overview of the OTA Mechanism

For IoT devices, the first step of OTA is to acquire the new firmware. There are several ways to do this, among which using Wi-Fi is one of the simplest and most convenient. IoT devices can connect to the router via Wi-Fi, and then connect to the OTA server to download the firmware via application layer protocols (e.g., HTTP, FTP). Here, we will use advanced_https_ota as an example to introduce a way to download the firmware via HTTPS. The example performs OTA with the esp_https_ota component, which uses HTTPS as the download protocol. The following code is from advanced_https_ota_example.c, with some parts omitted for clarity:

static esp_err_t _http_client_init_cb(esp_http_client_handle_t http_client)
{
    esp_err_t err = ESP_OK;
    //Set HTTPS custom header
    //err = esp_http_client_set_header(http_client, "Custom-Header", "Value");
    return err;
}

void advanced_ota_example_task(void *pvParameter)
{
    ESP_LOGI(TAG, "Starting Advanced OTA example");

    //1. Configure the HTTPS connection and set the esp_https_ota parameter
    esp_err_t ota_finish_err = ESP_OK;
    esp_http_client_config_t config = {
        .url = CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL,
        .cert_pem = (char *)server_cert_pem_start,
        .timeout_ms = CONFIG_EXAMPLE_OTA_RECV_TIMEOUT,
        .keep_alive_enable = true,
    };
    ......
    esp_https_ota_config_t ota_config = {
        .http_config = &config,
        .http_client_init_cb = _http_client_init_cb,//Register esp_http_client callback function called after initialization
    };

    //2. Enable OTA via esp_https_ota_begin, returning HTTP/HTTPS results
    esp_https_ota_handle_t https_ota_handle = NULL;
    esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);
    if (err ! = ESP_OK) {
        ESP_LOGE(TAG, "ESP HTTPS OTA Begin failed");
        vTaskDelete(NULL);
    }

    //3. When connected, the information of new firmware can be obtained via esp_https_ota_get_img_desc, which can be used for verification
    esp_app_desc_t app_desc;
    err = esp_https_ota_get_img_desc(https_ota_handle, &app_desc);
    if (err ! = ESP_OK) {
        ESP_LOGE(TAG, "esp_https_ota_read_img_desc failed");
        goto ota_end;
    }
    err = validate_image_header(&app_desc);
    if (err ! = ESP_OK) {
        ESP_LOGE(TAG, "image header verification failed");
        goto ota_end;
    }

    //4. Receive and write firmware by iterating esp_https_ota_perform(), and jump out of the loop when HTTPS is not in receiving mode.
    while (1) {
        err = esp_https_ota_perform(https_ota_handle);
        if (err ! = ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
            break;
        }
        /.
        …
    }

    //5. Verify the firmware integrity, and call esp_https_ota_finish or esp_https_ota_abort to release the HTTP/HTTPS connection.
    //Esp_https_ota_finish will upgrade the OTA data partition and switch the next boot to the new firmware.
    if (esp_https_ota_is_complete_data_received(https_ota_handle) ! = true) {
        //OTA firmware not fully received. Users can customise handling options
        ESP_LOGE(TAG, "Complete data was not received." );
    } else {
        ota_finish_err = esp_https_ota_finish(https_ota_handle);
        if ((err == ESP_OK) && (ota_finish_err == ESP_OK)) {
            ESP_LOGI(TAG, "ESP_HTTPS_OTA upgrade successful.Rebooting ..." );
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            esp_restart();
        } else {
            if (ota_finish_err == ESP_ERR_OTA_VALIDATE_FAILED) {
                ESP_LOGE(TAG, "Image validation failed, image is corrupted");
            }
            ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed 0x%x", ota_finish_err);
            vTaskDelete(NULL);
        }
    }

ota_end:
    esp_https_ota_abort(https_ota_handle);
    ESP_LOGE(TAG, "ESP_HTTPS_OTA upgrade failed");
    vTaskDelete(NULL);
}
  1. Acquiring firmware

    The code above downloads the firmware using HTTPS protocol, and the HTTPS operations are encapsulated in ESP-IDF. You only need to configure the esp_http_client_config_t structure, where you can pass the certificate and enable the TLS protocol to secure the transmitted data. After configuring the HTTPS connection parameters, the configurations should be passed to the esp_https_ota_config_t structure, which provides a callback function to facilitate setting the HTTPS request header information. Header information is generally used to declare the length and data format of the HTTPS Body to the server. After the configuration, you can call esp_https_ota_begin() for connection. The function will determine the result based on the HTTPS status code returned. Upon successful HTTPS connection, subsequent calls to the esp_https_ota_perform() function to request firmware can be made recursively.

  2. Writing firmware

    The esp_https_ota_perform() function continuously sends request information to the server and writes each set of data returned by the server to the flash. This writing process is implemented within the function by calling esp_ota_write().

  3. Verifying firmware

    Considering the limited resources of the receiving device and the not-always-ideal network environment, it is often necessary to send HTTPS requests several times to download the firmware completely. ESP-IDF provides the esp_https_ota_is_complete_data_received() function to determine whether the firmware has been received completely by calculating the size of the firmware. Once completely received, the esp_https_ota_finish() function will be called to terminate the HTTPS connection and release any occupied resources. Simply comparing the firmware size to determine if the firmware is valid is not optimal, we need to also verify the SHA-256 value of the firmware to ensure that the downloaded firmware is identical to the original firmware. Calling esp_https_ota_finish() will complete the verification automatically. If secure boot is enabled, the relevant checks will also be performed at this point. For more details about secure boot, please refer to Section 13.4.2.

  4. Switching firmware

    The esp_https_ota_finish() function not only releases HTTPS resources and verifies the firmware, but also automatically rewrites the OTA partition upon successful verification. At the same time, the function updates the status of this downloaded firmware to "undefined" (ESP_OTA_IMG_UNDEFINED). After the preparations are done, the firmware that is rebooted by calling esp_restart() would be the new firmware. Undefined firmware in this case means that the firmware can be booted without restriction as long as the Bootloader rollback is not enabled.