ESP32-S3 HTTP OTA开发学习记录

OTA工作流程

请添加图片描述

ESP32 FLASH空间分区

通过menuconfig --> partition table 有三种分区选择:

  • single factory app
  • factory app , two ota
  • custom partition table

ESP32 SDK对应的flash分区配置的源码路径是:esp-idf-v4.4.2\components\partition_table

  • single factory:partition_singleapp.csv
  • single factory large: partitons_singleapp_large.csv
  • two ota: partitions_two_ota.csv

OTA 分区

  • OTA data区:决定运行哪个区的APP
  • factory app:出厂时的默认APP
  • OTA_0区:OTA_0 APP
  • OTA_1区:OTA_1 APP

系统重启时获取OTA data分区数据进行计算,决定此后加载哪个ota分区的app执行,从而实现升级

在不同阶段,esp32使用分区的情况
请添加图片描述

使用简化API升级

菜单配置

  1. flash大小配置

    serial flasher config --> flash size

  2. 配置分区表

    partition table --> factory app,two OTA definitions

  3. 配置服务器类型

    component config --> esp https ota --> allow http for ota

    (配置为http ota ,在代码中注释掉https使用的CA证书)

  4. 配置wifi信息

    example connection configuation --> wifi ssid -->wifi password

  5. 配置APP版本号

    application manger --> get the project version form kconfi

简化例程代码

代码源路径:\esp-idf-v4.4.2\components\esp_https_ota\src

main

创建ota升级任务

void app_main(void)
{
    // Initialize NVS.
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// 1.OTA app partition table has a smaller NVS partition size than the non-OTA
// partition table. This size mismatch may cause NVS initialization to fail.
// 2.NVS partition contains data in new format and cannot be recognized by this version of code.
// If this happens, we erase NVS partition and initialize NVS again.
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    ESP_ERROR_CHECK(example_connect());

#if CONFIG_EXAMPLE_CONNECT_WIFI
    /* Ensure to disable any WiFi power save mode, this allows best throughput
     * and hence timings for overall OTA operation.
     *确保禁用任何WiFi省电模式,这可以实现最佳吞吐量,从而实现整体OTA操作的计时
     */
    esp_wifi_set_ps(WIFI_PS_NONE);//设置当前WiFi省电模式
#endif // CONFIG_EXAMPLE_CONNECT_WIFI

    xTaskCreate(&simple_ota_example_task, "ota_example_task", 8192, NULL, 5, NULL);
}
simple_ota_example_task
void simple_ota_example_task(void *pvParameter)
{
    ESP_LOGI(TAG, "Starting OTA example");
    esp_http_client_config_t config = {
        .url = "http://xxx.xxx.xx.xx:8070/control.bin",
        .event_handler = _http_event_handler,
    };

    esp_err_t ret = esp_https_ota(&config);
    if (ret == ESP_OK) {
        esp_restart();
    } else {
        ESP_LOGE(TAG, "Firmware upgrade failed");
    }
    while (1) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
esp_https_ota
esp_err_t esp_https_ota(const esp_http_client_config_t *config)
{
    if (!config) {
        ESP_LOGE(TAG, "esp_http_client config not found");
        return ESP_ERR_INVALID_ARG;
    }

    esp_https_ota_config_t ota_config = {
        .http_config = config,
    };

    esp_https_ota_handle_t https_ota_handle = NULL;
    esp_err_t err = esp_https_ota_begin(&ota_config, &https_ota_handle);//启动OTA固件升级
    if (https_ota_handle == NULL) {
        return ESP_FAIL;
    }

    while (1) {
        err = esp_https_ota_perform(https_ota_handle);//从http流中读取并写入OTA分区
        if (err != ESP_ERR_HTTPS_OTA_IN_PROGRESS) {
            break;
        }
    }

    if (err != ESP_OK) {
        esp_https_ota_abort(https_ota_handle);//清理https ota固件升级并关闭https连接
        return err;
    }

    esp_err_t ota_finish_err = esp_https_ota_finish(https_ota_handle);
    if (ota_finish_err != ESP_OK) {
        return ota_finish_err;
    }
    return ESP_OK;
}

typedef struct {
	//ESP HTTP客户端配置
    const esp_http_client_config_t *http_config;   
    //初始化ESP HTTP客户端后的回调
    http_client_init_cb_t http_client_init_cb;     
    //在初始化过程中删除整个FLASH分区。默认情况下,flash分区在写操作时被擦除
    bool bulk_flash_erase;                         
    //允许通过多个http请求下载固件映像
    bool partial_http_download;  
    //部分http下载的最大请求大小
    int max_http_request_size;                     
} esp_https_ota_config_t;

例程中API说明

esp_https_ota_begin

初始化ESP HTTPS OTA上下文并建立HTTPS连接。
这个函数必须先被调用。如果这个函数成功返回,那么’ esp_https_ota_perform ‘应该被调用来继续OTA进程,并且在OTA操作完成或后续操作失败时应该调用’ esp_https_ota_finish '。

@param[in] ota_config
指向esp_https_ota_config_t结构的指针
@param[out] handle
指向’ esp_https_ota_handle_t '类型的已分配数据的指针,该数据将在此函数中初始化

esp_https_ota_perform

从HTTP流读取图像数据并写入OTA分区。只有esp_https_ota_begin()成功返回时,才必须调用此函数。
此函数必须在循环中调用,因为它在每个HTTP读操作后返回,从而使您可以灵活地中途停止OTA操作。

param[in] 指向esp_https_ota_handle_t结构体的指针

return:ESP_ERR_HTTPS_OTA_IN_PROGRESS: OTA更新正在进行中,再次调用此API继续。

​ ESP_OK: OTA更新成功

​ ESP_FAIL: OTA更新失败

​ ESP_ERR_INVALID_ARG:无效参数

​ ESP_ERR_OTA_VALIDATE_FAILED:无效的应用程序映像

​ ESP_ERR_NO_MEM:无法为OTA操作分配内存。

​ ESP_ERR_FLASH_OP_TIMEOUT或ESP_ERR_FLASH_OP_FAIL: Flash写入失败。

esp_https_ota_abort

清理 HTTPS OTA 固件升级并关闭 HTTPS 连接。此函数关闭 HTTP 连接并释放 ESP HTTPS OTA 上下文。

备注:esp_https_ota_abort不应在调用esp_https_ota_finish后调用

param[in] https_ota_handle– 指向esp_https_ota_handle_t结构的指针

return ESP_OK:清理成功

​ ESP_ERR_INVALID_STATE:无效的

​ ESP HTTPS OTA 状态ESP_FAIL:OTA 未启动

​ ESP_ERR_NOT_FOUND:找不到 OTA 句柄

​ ESP_ERR_INVALID_ARG:参数无效

esp_https_ota_finish

清理 HTTPS OTA 固件升级并关闭 HTTPS 连接。

此函数关闭 HTTP 连接并释放 ESP HTTPS OTA 上下文。此函数将引导分区切换到包含新固件映像的 OTA 分区。

备注:如果此 API 成功返回,则必须调用 esp_restart() 才能从新固件映像启动,esp_https_ota_finish调用后不应调用esp_https_ota_abort

param[in] https_ota_handle – 指向esp_https_ota_handle_t结构的指针

return : ESP_OK:清理成功

​ ESP_ERR_INVALID_STATEESP_ERR_INVALID_ARG:参数无效

​ ESP_ERR_OTA_VALIDATE_FAILED:应用图像无效

OTA升级具体流程

Ⅰ. 启动固件升级

OTA结构体
struct esp_https_ota_handle {
    esp_ota_handle_t update_handle;
    const esp_partition_t *update_partition;
    esp_http_client_handle_t http_client;
    char *ota_upgrade_buf;
    size_t ota_upgrade_buf_size;
    int binary_file_len;
    int image_length;
    int max_http_request_size;
    esp_https_ota_state state;
    bool bulk_flash_erase;
    bool partial_http_download;
};

typedef struct esp_https_ota_handle esp_https_ota_t;
初始化HTTP连接
https_ota_handle->http_client = esp_http_client_init(ota_config->http_config);//http初始化
if (https_ota_handle->http_client == NULL) {
    ESP_LOGE(TAG, "Failed to initialise HTTP connection");
    err = ESP_FAIL;
    goto failure;
}
err = _http_connect(https_ota_handle->http_client);//http连接
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Failed to establish HTTP connection");
        goto http_cleanup;
    }
//获取http响应头内的content_length
    if (!https_ota_handle->partial_http_download) {
        https_ota_handle->image_length = esp_http_client_get_content_length(https_ota_handle->http_client);
    }

获取新固件写入的下一个OTA应用程序分区
https_ota_handle->update_partition = esp_ota_get_next_update_partition(NULL);
if (https_ota_handle->update_partition == NULL) {
    ESP_LOGE(TAG, "Passive OTA partition not found");
    err = ESP_FAIL;
    goto http_cleanup;
}

Ⅱ . 从http流中读取并写入OTA分区

ESP_HTTPS_OTA_BEGIN
err = esp_ota_begin(handle->update_partition, erase_size, &handle->update_handle);//启动ota
if (err != ESP_OK) {
    ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
    return err;
}
handle->state = ESP_HTTPS_OTA_IN_PROGRESS;
ESP_HTTPS_OTA_IN_PROGRESS
data_read = esp_http_client_read(handle->http_client,
								handle->ota_upgrade_buf,
								handle->ota_upgrade_buf_size);//读取http数据
if (data_read == 0) //接收数据为0
{		
    //esp_http_client_is_complete_data_received用于检查是否接收到完整的镜像
    bool is_recv_complete = esp_http_client_is_complete_data_received(handle->http_client);

    /*如果没有收到完整的数据,但服务器发送了一个ENOTCONN或ECONNRESET,则返回失败。如果收到完整的数据,我们就成功结束。*/
    if ((errno == ENOTCONN || errno == ECONNRESET || errno == ECONNABORTED) && !is_recv_complete) 
    {
        ESP_LOGE(TAG, "Connection closed, errno = %d", errno);
        return ESP_FAIL;
    } 
    else if (!is_recv_complete) 
    {//未接收完成
        return ESP_ERR_HTTPS_OTA_IN_PROGRESS;
    }
    ESP_LOGD(TAG, "Connection closed");
} 
else if (data_read > 0) //接收数据大于0
{
    return _ota_write(handle, (const void *)handle->ota_upgrade_buf, data_read);//ota写入
} 
else 
{
    ESP_LOGE(TAG, "data read %d, errno %d", data_read, errno);
    return ESP_FAIL;
}

Ⅲ . 清理https ota固件升级并关闭https连接

//ota写入失败
if (err != ESP_OK) {
    esp_https_ota_abort(https_ota_handle);//清理https ota固件升级并关闭https连接
    return err;
}
//ota写入成功
esp_err_t ota_finish_err = esp_https_ota_finish(https_ota_handle);//清理https ota固件升级并关闭https连接
if (ota_finish_err != ESP_OK) {
    return ota_finish_err;
}

HTTP_OTA 开发中遇到的问题

  • 问题一:下载速度太慢

解决:OTA下载速度取决于HTTP流下载速度,所以更改 HTTP_Client配置中接收缓存的大小即可。

esp_https_ota :

esp_https_ota_config_t ota_config = {
    .http_config = config,
};

esp_https_ota_begin :

const int alloc_size = MAX(ota_config->http_config->buffer_size, DEFAULT_OTA_BUF_SIZE);

https_ota_handle->ota_upgrade_buf = (char *)malloc(alloc_size);
https_ota_handle->ota_upgrade_buf_size = alloc_size;

esp_https_ota_perform :

data_read = esp_http_client_read(handle->http_client,
                                 handle->ota_upgrade_buf,
                                 handle->ota_upgrade_buf_size);
  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值