基于ESP32 IDF的WebServer实现以及OTA固件升级实现记录(三) ...

打印 上一主题 下一主题

主题 532|帖子 532|积分 1596

          经过前面两篇的前序铺垫,对webserver以及restful api架构有了大体相识后本篇形貌下最终的ota实现的代码以及调试中遇到的诡异bug。
         eps32的实际ota实现过程实在esp32官方都已经基本实现好了,我们要做到无非就是把要升级的固件搬运到对应ota flash分区里面,相对来说esp32 ota已经很完善了。esp32的ota相关接口可拜见官网的相关ota文档。本文重要讲解基于http post方式的本地webserver的ota实现。
        1、网页端web实现,重要完成固件选择以及上传post功能,该部门可以参考开源web,如esp32-vue3demo/vue-project/src/views/Upgrade.vue at main · lbuque/esp32-vue3demo · GitHub
  1. <template>
  2.   <form method='POST' action='/api/v1/update' enctype='multipart/form-data'>
  3.     <input type='file' name='update'>
  4.     <input type='submit' value='Update'>
  5.   </form>
  6. </template>
  7. <script lang="ts" setup >
  8. </script>
  9. <style scoped lang="scss"></style>
复制代码
如上代码的效果如下,重要是有一个文件选择框可以选择要升级的固件,点击update后即会向web后台url:/api/v1/updata举行post请求传输要升级的固件给运行webserver的后台即esp32,该部门即完成了待升级固件的网络传输。

        2、esp32 webserver的post文件请求接收以及实际的ota flash写入。
           ota过程需要先找到要写入的ota分区,然后接收文件后按esp32的ota api写入ota分区即可,文件传输完成后即可启动esp32举行固件升级。升级代码如下:
  1.     /* URI handler for light brightness control */
  2.     httpd_uri_t light_brightness_post_uri = {
  3.         .uri = "/api/v1/update",
  4.         .method = HTTP_POST,
  5.         .handler = light_brightness_post_handler,
  6.         .user_ctx = rest_context
  7.     };
  8.     httpd_register_uri_handler(server, &light_brightness_post_uri);
复制代码
注册一个用于接收post文件的rest api,回调名懒得改,实际项目中按编码要求举行修改。
  1. static esp_err_t light_brightness_post_handler(httpd_req_t *req)
  2. {
  3.    
  4.     esp_err_t err;
  5.     /* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
  6.     esp_ota_handle_t update_handle = 0 ;
  7.     const esp_partition_t *update_partition = NULL;
  8.     char filepath[FILE_PATH_MAX];
  9.     ESP_LOGI(REST_TAG, "light_brightness_post_handler");
  10.     /* Skip leading "/upload" from URI to get filename */
  11.     /* Note sizeof() counts NULL termination hence the -1 */
  12.     const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path,
  13.                                              req->uri + sizeof("/upload") - 1, sizeof(filepath));
  14.    
  15.     ESP_LOGI(REST_TAG, "Receiving file : %s...", filename);
  16.     /* Retrieve the pointer to scratch buffer for temporary storage */
  17.     char *buf = ((struct file_server_data *)req->user_ctx)->scratch;
  18.     int received;
  19.     /* Content length of the request gives
  20.      * the size of the file being uploaded */
  21.     int remaining = req->content_len;
  22.    
  23.     while (remaining > 0) {
  24.         if(remaining == req->content_len)
  25.         {
  26.             update_partition = esp_ota_get_next_update_partition(NULL);
  27.             assert(update_partition != NULL);
  28.             ESP_LOGI(REST_TAG, "Writing to partition subtype %d at offset 0x%"PRIx32,update_partition->subtype, update_partition->address);
  29.             err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
  30.             if (err != ESP_OK) {
  31.                 ESP_LOGE(REST_TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
  32.                 esp_ota_abort(update_handle);
  33.             }
  34.             ESP_LOGI(REST_TAG, "esp_ota_begin succeeded");
  35.         }
  36.         ESP_LOGI(REST_TAG, "Remaining size : %d", remaining);
  37.         /* Receive the file part by part into a buffer */
  38.         if ((received = httpd_req_recv(req, buf, MIN(remaining, SCRATCH_BUFSIZE))) <= 0) {
  39.             if (received == HTTPD_SOCK_ERR_TIMEOUT) {
  40.                 /* Retry if timeout occurred */
  41.                 continue;
  42.             }
  43.             /* In case of unrecoverable error,
  44.              * close and delete the unfinished file*/
  45.             
  46.             ESP_LOGE(REST_TAG, "File reception failed!");
  47.             /* Respond with 500 Internal Server Error */
  48.             httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
  49.             return ESP_FAIL;
  50.         }
  51.         err = esp_ota_write( update_handle, buf, received);
  52.         if (err != ESP_OK) {
  53.             esp_ota_abort(update_handle);
  54.         }
  55.         /* Keep track of remaining size of
  56.          * the file left to be uploaded */
  57.         remaining -= received;
  58.          ESP_LOGI(REST_TAG, " remaining = %d, received = %d", remaining, received);
  59.     }
  60.     /* Close file upon upload completion */
  61.    
  62.     ESP_LOGI(REST_TAG, "File reception complete");
  63.     /* Redirect onto root to see the updated file list */
  64.     httpd_resp_set_status(req, "303 See Other");
  65.     httpd_resp_set_hdr(req, "Location", "/");
  66. #ifdef CONFIG_EXAMPLE_HTTPD_CONN_CLOSE_HEADER
  67.     httpd_resp_set_hdr(req, "Connection", "close");
  68. #endif
  69.     httpd_resp_sendstr(req, "File uploaded successfully");
  70.     ESP_LOGI(REST_TAG, "======endota======");
  71.     err = esp_ota_end(update_handle);
  72.     if (err != ESP_OK) {
  73.         if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
  74.             ESP_LOGE(REST_TAG, "Image validation failed, image is corrupted");
  75.         }
  76.         ESP_LOGE(REST_TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
  77.     }
  78.     err = esp_ota_set_boot_partition(update_partition);
  79.     if (err != ESP_OK) {
  80.         ESP_LOGE(REST_TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
  81.     }
  82.     ESP_LOGI(REST_TAG, "Prepare to restart system!");
  83.     esp_restart();
  84.     return ESP_OK;
  85.    
  86. }
复制代码
        大体代码的含义就是接收post请求后持续读取文件知道传输完成,同时传输过程中写ota分区的flash。
        实际按如上代码举行build后即可举行ota升级。运行起来一切都ok,不外没有意外下还是出现了意外,web页面选择固件举行升级后esp32端接收到的bin文件总是格式不对,出现了如下错误,

错误提示传输的文件不是bin文件格式,因此猜疑是post传输问题,通过web的开辟者工具查看post的文件大小果真和实际文件大小不同等,

此时还没法确定是esp32嵌入式端代码问题还是web端前端代码问题,背面找了curl工具,

直接用命令行接口举行post试了后可以正常升级,post过来的大小也是实际的文件大小。至此基本可以确认是前端web页面 post的时候自己传输头等有修改了content-length导致其比实际的bin文件更大导致esp32接收的bin文件错误。
经过背面修改发现只有效原始的xmlrequest才不会像html大概axios那样会导致传输的content-length会比实际的bin文件大从而导致ota固件升级失败的问题。
改善后的web端的post关键代码如下:
  1. upload() {
  2.       // your upload logic here using XMLHttpRequest
  3.       const uploadPath = "/upload/" + this.filePath;
  4.       const fileInput = document.getElementById("newfile").files;
  5.       
  6.       // add your upload logic using XMLHttpRequest here
  7.       // be sure to use "this.filePath" and "fileInput" from Vue data
  8.       // Example
  9.       const file = fileInput[0];
  10.       const xhttp = new XMLHttpRequest();
  11.       xhttp.onreadystatechange = function() {
  12.         if (xhttp.readyState == 4) {
  13.           if (xhttp.status == 200) {
  14.             document.open();
  15.             document.write(xhttp.responseText);
  16.             document.close();
  17.           } else if (xhttp.status == 0) {
  18.             alert("Server closed the connection abruptly!");
  19.             location.reload();
  20.           } else {
  21.             alert(xhttp.status + " Error!\n" + xhttp.responseText);
  22.             location.reload();
  23.           }
  24.         }
  25.       };
  26.       xhttp.open("POST", "/api/v1/update", true);
  27.       xhttp.send(file);
  28.     }
复制代码
webserver ota传输升级没问题的log截图

esp32 的webserver ota的源代码放github上,有需要的自行取用,iot-lorawan (iot-lorawan) · GitHub
关于web端为何用xtmlrequest举行post的时候content-length才能是正确的bin文件大小,而axios大概html自带的post都会导致该字段长度偏大的目前原因还不得而知。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

千千梦丶琪

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表