#include <string.h> #include <stdlib.h> #include <unistd.h> #include <esp_log.h> #include <nvs_flash.h> #include <sys/param.h> #include "esp_netif.h" #include <esp_wifi.h> #include <esp_system.h> #include "nvs_flash.h" #include "esp_ota_ops.h" #include "http_server.h" #include "Protocol_User.h" static const char *TAG = "http_server"; char buf[MAX_OTA_BUFF]; // 接收服务端传来的文件缓存, 必须使用全局变量, 否则会触发看门狗复位, 原理未知 extern void Cache_data(unsigned char* p,uint32_t len); extern void SetUpgradeFlashSize(uint32_t size,uint32_t len); extern void SetUpgradeStart(void);//初始状态 static unsigned char softap_ota_start = 0; httpd_handle_t http_server = NULL; static esp_err_t index_html_get_handler(httpd_req_t *req) { ESP_LOGI(TAG, "---- 回到起始页"); httpd_resp_set_status(req, "307 Temporary Redirect"); httpd_resp_set_hdr(req, "Location", "/"); httpd_resp_send(req, NULL, 0); return ESP_OK; } static esp_err_t favicon_get_handler(httpd_req_t *req) { extern const unsigned char favicon_ico_start[] asm("_binary_favicon_ico_start"); extern const unsigned char favicon_ico_end[] asm("_binary_favicon_ico_end"); const size_t favicon_ico_size = (favicon_ico_end - favicon_ico_start); ESP_LOGI(TAG, "---- 下载网站图标"); httpd_resp_set_type(req, "image/x-icon"); httpd_resp_send(req, (const char *)favicon_ico_start, favicon_ico_size); return ESP_OK; } static esp_err_t http_resp_dir_html(httpd_req_t *req) { extern const unsigned char ota_page_start[] asm("_binary_ota_page_html_start"); extern const unsigned char ota_page_end[] asm("_binary_ota_page_html_end"); const size_t ota_page_size = (ota_page_end - ota_page_start); ESP_LOGI(TAG, "---- 下载网站起始页"); httpd_resp_set_type(req, "text/HTML"); httpd_resp_send(req, (const char *)ota_page_start, ota_page_size); return ESP_OK; } static esp_err_t download_get_handler(httpd_req_t *req) { const char *uri_get = req->uri; ESP_LOGI(TAG, "---- GET URI: 192.168.4.1%s", req->uri); if (strcmp(uri_get, "/") == 0) { return http_resp_dir_html(req); } else if (strcmp(uri_get, "/favicon.ico") == 0) { return favicon_get_handler(req); } else { ESP_LOGI(TAG, "---- 非预设GET, 不处理, 直接回到起始页"); return index_html_get_handler(req); } return ESP_OK; } static esp_err_t app_post_handler(httpd_req_t *req) { int ret = 0; int recv_block = 0; int remaining = req->content_len; int total = remaining; double percent = 0.0; ESP_LOGI(TAG, "---- 应用程序更新, 需接收数据长度(bytes): %d", remaining); esp_ota_handle_t app_update_handle = 0; const esp_partition_t *app_update_partition = esp_ota_get_next_update_partition(NULL); esp_err_t err = esp_ota_begin(app_update_partition, OTA_WITH_SEQUENTIAL_WRITES, &app_update_handle); while (remaining > 0) { /* Read the data for the request */ if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0) { if (ret == HTTPD_SOCK_ERR_TIMEOUT) { /* Retry receiving if timeout occurred */ continue; } return ESP_FAIL; } err = esp_ota_write(app_update_handle, buf, ret); if (err != ESP_OK) { ESP_LOGI(TAG, "---- 写入数据失败, 错误信息:%s", esp_err_to_name(err)); } else { remaining -= ret; recv_block++; if ((recv_block % 20) == 0) { percent = 100.0 - (double)(remaining * 100) / (double)total; ESP_LOGI(TAG, "---- 写入OTA升级数据: %.2f%%", percent); } } // 增加20ms延时, 解决CPU0看门狗超时的问题 vTaskDelay(pdMS_TO_TICKS(20)); } err = esp_ota_end(app_update_handle); if (err == ESP_OK) { esp_err_t err = esp_ota_set_boot_partition(app_update_partition); if (err != ESP_OK) { ESP_LOGI(TAG, "---- 设备启动分区失败, 错误信息:%s", esp_err_to_name(err)); } else { ESP_LOGI(TAG, "---- 应用程序更新完成, 3秒后自动重启."); httpd_resp_send_chunk(req, NULL, 0); // delele_tasks(); // httpd_resp_sendstr(req, "<p>应用程序更新完成,3秒后自动重启.</p>"); vTaskDelay(pdMS_TO_TICKS(3000)); //esp_restart(); SendCmd_ResetComond(); } } // End response // httpd_resp_sendstr(req, "<p>数据上传完成,但应用更新不成功,请重试.</p>"); ESP_LOGI(TAG, "---- 数据接收完成, OTA失败, 1秒后自动重启."); httpd_resp_send_chunk(req, NULL, 0); // delele_tasks(); vTaskDelay(pdMS_TO_TICKS(1000)); esp_restart(); return ESP_OK; } // static esp_err_t assert_post_handler(httpd_req_t *req) // { // int ret, remaining = req->content_len; // while (remaining > 0) // { // /* Read the data for the request */ // if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0) // { // if (ret == HTTPD_SOCK_ERR_TIMEOUT) // { // /* Retry receiving if timeout occurred */ // continue; // } // return ESP_FAIL; // } // remaining -= ret; // // ESP_LOGI(TAG, "=========== RECEIVED DATA =========="); // // 数据处理代码 // // ESP_LOGI(TAG, "===================================="); // } // // End response // httpd_resp_send_chunk(req, NULL, 0); // return ESP_OK; // } // static esp_err_t storage_post_handler(httpd_req_t *req) // { // int ret, remaining = req->content_len; // while (remaining > 0) // { // /* Read the data for the request */ // if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0) // { // if (ret == HTTPD_SOCK_ERR_TIMEOUT) // { // /* Retry receiving if timeout occurred */ // continue; // } // return ESP_FAIL; // } // remaining -= ret; // // ESP_LOGI(TAG, "=========== RECEIVED DATA =========="); // // 数据处理代码 // // ESP_LOGI(TAG, "===================================="); // } // // End response // httpd_resp_send_chunk(req, NULL, 0); // return ESP_OK; // } static void MSI_Print_mem16(size_t len,char *buf) { for (size_t i = 0; i < len; ) { ESP_LOGI("MSI","%02xh: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",i,\ buf[i+0],buf[i+1],buf[i+2],buf[i+3],buf[i+4],buf[i+5],buf[i+6],buf[i+7], buf[i+8],buf[i+9],buf[i+10],buf[i+11],buf[i+12],buf[i+13],buf[i+14],buf[i+15]); i+=16; } } /*上传文件名是BAT32A239dat.bin的处理函数*/ static esp_err_t nation_post_handler(httpd_req_t *req) { int recv_block = 0; double percent = 0.0; int ret, remaining = req->content_len; int total = remaining; int err_timeout_retry_cnt = 0; size_t erase_size = 0; size_t erase_offset = 0; size_t write_offset = 0; int Addr = 0; esp_err_t err = ESP_OK; const char *partition_label = "assert"; const esp_partition_t *assert_update_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, partition_label); // if (Sys_Run_hdl != NULL) // { // vTaskDelete(Sys_Run_hdl); // Sys_Run_hdl = NULL; // vTaskDelay(pdMS_TO_TICKS(100)); // } //SetUpgradeFlashSize(remaining); // 设定文件大小 ESP_LOGI(TAG, "---- 国民技术程序文件: 需要接收的数据(byte) = %d", remaining); while (remaining > 0) { /* Read the data for the request */ if ((ret = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)))) <= 0) { if (ret == HTTPD_SOCK_ERR_TIMEOUT) { err_timeout_retry_cnt++; if (err_timeout_retry_cnt >= MAX_RETRY_COUNT) { httpd_resp_send_408(req); httpd_resp_send_chunk(req, NULL, 0); ESP_LOGE(TAG, "---- 接收数据超时, 退出升级流程, 请保持无线连接稳定后再试."); return ESP_FAIL; } /* else { Retry receiving if timeout occurred continue; }*/ } else { ESP_LOGI("MSI","total : %d \n",total); ESP_LOGI("MSI","remaining : %d \n",remaining); err_timeout_retry_cnt = 0; } } if(total == remaining)//下载长度+地址 { Addr = 0; Addr |= buf[8]; Addr <<= 8; Addr |= buf[9]; Addr <<= 8; Addr |= buf[10]; Addr <<= 8; Addr |= buf[11]; SetUpgradeFlashSize(remaining, Addr); } while (erase_offset < write_offset + ret) { err = esp_partition_erase_range(assert_update_partition, erase_offset, assert_update_partition->erase_size); if (err == ESP_OK) { erase_offset += assert_update_partition->erase_size; } else { httpd_resp_send_chunk(req, NULL, 0); //ESP_LOGE(TAG, "---- assert 分区擦除失败(%s), 分区容量 = 0x%x, 偏移地址 = 0x%x, 擦除长度 = 0x%x", esp_err_to_name(err), assert_update_partition->size, erase_offset, assert_update_partition->erase_size); return ESP_FAIL; } } err = esp_partition_write(assert_update_partition, write_offset, buf, ret); if (err == ESP_OK) { remaining -= ret; write_offset += ret; recv_block++; if ((recv_block % 32) == 0) { percent = 100.0 - (double)(remaining * 100) / (double)total; ESP_LOGI(TAG, "---- 写入数据(%.2f%%), recv_block = %d", percent, recv_block); } } else { httpd_resp_send_chunk(req, NULL, 0); ESP_LOGE(TAG, "---- assert 分区写数据失败(%s)", esp_err_to_name(err)); return ESP_FAIL; } // 增加20ms延时, 解决CPU0看门狗超时的问题 vTaskDelay(pdMS_TO_TICKS(20)); } ESP_LOGI(TAG, "---- write_offset = %d", write_offset); if (write_offset == total) { ESP_LOGI(TAG, "---- assert 分区更新完成, 1秒后自动重启."); vTaskDelay(pdMS_TO_TICKS(1000)); } else { httpd_resp_send_chunk(req, NULL, 0); ESP_LOGE(TAG, "---- assert 分区更新数据部分丢失"); return ESP_FAIL; } SetUpgradeStart(); // 启动升级 ESP_LOGI(TAG, "---- 国民技术程序文件: 接收完成"); return ESP_OK; } void Nat32G031_ReqReset(void) { ESP_LOGI(TAG, "---- NAT32G031程序更新完成, 3秒后自动重启."); // delele_tasks(); // httpd_resp_sendstr(req, "<p>应用程序更新完成,3秒后自动重启.</p>"); vTaskDelay(pdMS_TO_TICKS(3000)); esp_restart(); } static esp_err_t upload_post_handler(httpd_req_t *req) { const char *uri_post = req->uri; ESP_LOGI(TAG, "---- POST URI: 192.168.4.1%s", req->uri); if (strcmp(uri_post, "/upload/RT200T-2.bin") == 0) { return app_post_handler(req); } // else if (strcmp(uri_post, "/upload/mmap_assert.bin") == 0) // { // return assert_post_handler(req); // } // else if (strcmp(uri_post, "/upload/storage.bin") == 0) //{ // return storage_post_handler(req); //} else if (strcmp(uri_post, "/upload/BAT32G139.bin") == 0) { return nation_post_handler(req); } else { ESP_LOGI(TAG, "---- 非预设POST, 不处理, 直接回到起始页"); return index_html_get_handler(req); } return ESP_OK; } void set_softap_ota_status(unsigned char status) { softap_ota_start = status; } unsigned char get_softap_ota_status(void) { return softap_ota_start; } httpd_handle_t start_webserver(void) { httpd_handle_t server = NULL; httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.lru_purge_enable = true; config.uri_match_fn = httpd_uri_match_wildcard; // Start the httpd server ESP_LOGI(TAG, "---- 服务器端口号: '%d'", config.server_port); if (httpd_start(&server, &config) == ESP_OK) { // Set URI handlers ESP_LOGI(TAG, "---- 注册URI管理程序"); // 注册客户端下载行为 httpd_uri_t download_cfg = { .uri = "/*", .method = HTTP_GET, .handler = download_get_handler, .user_ctx = NULL}; httpd_register_uri_handler(server, &download_cfg); // 注册客户端上传行为 httpd_uri_t upload_cfg = { .uri = "/upload/*", .method = HTTP_POST, .handler = upload_post_handler, .user_ctx = NULL}; httpd_register_uri_handler(server, &upload_cfg); return server; } ESP_LOGI(TAG, "---- 服务器启动时发生错误."); return NULL; } esp_err_t stop_webserver(httpd_handle_t server) { return httpd_stop(server); } /****************************************** *根据某种状态自动开关webserver的监听处理, *没有任何STA连接AP,自动关闭webserver, *任意STA连接AP,自动开启webserver. ******************************************/ void connect_handler(void *arg) { httpd_handle_t *server = (httpd_handle_t *)arg; if (*server == NULL) { ESP_LOGI(TAG, "---- 开启HTTP服务器"); *server = start_webserver(); } } void disconnect_handler(void *arg) { httpd_handle_t *server = (httpd_handle_t *)arg; if (*server) { ESP_LOGI(TAG, "---- 关闭HTTP服务器"); if (stop_webserver(*server) == ESP_OK) { *server = NULL; } else { ESP_LOGE(TAG, "Failed to stop http server"); } } } httpd_handle_t http_service(void) { static httpd_handle_t server = NULL; // 首次启动http_server server = start_webserver(); if (server) { ESP_LOGI(TAG, "---- HTTP服务器开启成功."); } else { ESP_LOGI(TAG, "---- HTTP服务器开启失败."); } return server; }