#include #include "esp_log.h" #include "esp_http_server.h" #include "esp_ota_ops.h" #include "http_service.h" #define MAX_CACHE_SIZE 1024u // HTTP服务接收数据的最大缓存大小 #define MAX_RETRY_COUNT 2 // HTTP服务超时后的最大重试次数 static const char *TAG = "http_service"; httpd_handle_t http_server = NULL; static void http_resp_favicon_ico(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); } static void http_resp_index_html(httpd_req_t *req) { extern const unsigned char index_html_start[] asm("_binary_index_html_start"); extern const unsigned char index_html_end[] asm("_binary_index_html_end"); const size_t index_html_size = (index_html_end - index_html_start); ESP_LOGI(TAG, "---- 固件升级首页"); httpd_resp_set_type(req, "text/HTML"); httpd_resp_send(req, (const char *)index_html_start, index_html_size); } static void http_resp_ota_app_html(httpd_req_t *req) { extern const unsigned char ota_app_html_start[] asm("_binary_ota_app_html_start"); extern const unsigned char ota_app_html_end[] asm("_binary_ota_app_html_end"); const size_t ota_app_html_size = (ota_app_html_end - ota_app_html_start); ESP_LOGI(TAG, "---- get into ota app page"); httpd_resp_set_type(req, "text/HTML"); httpd_resp_send(req, (const char *)ota_app_html_start, ota_app_html_size); } static esp_err_t http_resp_handler(httpd_req_t *req) { const char *uri_get = req->uri; ESP_LOGI(TAG, "---- request uri: http://192.168.4.1%s", req->uri); if (strcmp(uri_get, "/") == 0) { http_resp_ota_app_html(req); } else if (strcmp(uri_get, "/favicon.ico") == 0) { http_resp_favicon_ico(req); } else if (strcmp(uri_get, "/ota/app") == 0) { http_resp_ota_app_html(req); } else { ESP_LOGI(TAG, "---- unknown uri, back to index page"); http_resp_ota_app_html(req); } return ESP_OK; } static esp_err_t http_ota_app_handler(httpd_req_t *req) { int ret = 0; int recv_block = 0; int remaining = req->content_len; int total = remaining; double percent = 0.0; int err_timeout_retry_cnt = 0; ESP_LOGI(TAG, "---- expect ota data length(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); if (err == ESP_OK) { char *http_buffer = (char *)malloc(MAX_CACHE_SIZE); while (remaining > 0) { /* Read the data for the request */ if ((ret = httpd_req_recv(req, http_buffer, MIN(remaining, MAX_CACHE_SIZE))) <= 0) { if (ret == HTTPD_SOCK_ERR_TIMEOUT) { err_timeout_retry_cnt++; if (err_timeout_retry_cnt >= MAX_RETRY_COUNT) { httpd_resp_send_408(req); ESP_LOGE(TAG, "---- get ota data timeout, stop ota process, system will reboot after one second."); break; } else { /* Retry receiving if timeout occurred */ ESP_LOGW(TAG, "---- get ota data timeout, try it again."); continue; } } else { httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file, system will reboot after one second."); ESP_LOGE(TAG, "---- get ota data error, stop ota process, system will reboot after one second."); break; } } err_timeout_retry_cnt = 0; err = esp_ota_write(app_update_handle, http_buffer, ret); if (err != ESP_OK) { ESP_LOGI(TAG, "---- write ota data failed, error message:(%s), system will reboot after one second.", esp_err_to_name(err)); break; } else { remaining -= ret; recv_block++; if ((recv_block % 16) == 0) { percent = 100.0 - (double)(remaining * 100) / (double)total; ESP_LOGI(TAG, "---- ota process: %.2f%%", percent); } } } free(http_buffer); if (remaining == 0) { 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) { if (err == ESP_OK) { ESP_LOGI(TAG, "---- ota process: 100.00%%"); ESP_LOGI(TAG, "---- ota success, reboot after one seconds"); } else { ESP_LOGE(TAG, "---- ota success, but error occurred when write firmware info into eeprom."); } } else { ESP_LOGE(TAG, "---- set boot partition failed, error message:(%s), system will reboot after one second.", esp_err_to_name(err)); } } else { ESP_LOGE(TAG, "---- verify program fail, error message:(%s), system will reboot after one second.", esp_err_to_name(err)); } } else { ESP_LOGE(TAG, "---- ota data lost, stop ota process, system will reboot after one second."); } } else { ESP_LOGE(TAG, "---- can not find next app partition"); } httpd_resp_send_chunk(req, NULL, 0); vTaskDelay(pdMS_TO_TICKS(1000)); esp_restart(); return ESP_OK; } 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, "---- httpd server port: '%d'", config.server_port); if (httpd_start(&server, &config) == ESP_OK) { // Set URI handlers ESP_LOGI(TAG, "---- Set URI handlers"); // register client GET handle httpd_uri_t http_resp = { .uri = "/*", .method = HTTP_GET, .handler = http_resp_handler, .user_ctx = NULL}; httpd_register_uri_handler(server, &http_resp); // register client ota app handle httpd_uri_t http_ota_app = { .uri = "/ota/app/*", .method = HTTP_POST, .handler = http_ota_app_handler, .user_ctx = NULL}; httpd_register_uri_handler(server, &http_ota_app); return server; } ESP_LOGI(TAG, "---- error occurred during server startup"); return NULL; } esp_err_t stop_webserver(httpd_handle_t server) { return httpd_stop(server); } void start_webserver_handler(void *arg) { httpd_handle_t *server = (httpd_handle_t *)arg; if (*server == NULL) { ESP_LOGI(TAG, "---- start http server"); *server = start_webserver(); } } void stop_webserver_handler(void *arg) { httpd_handle_t *server = (httpd_handle_t *)arg; if (*server) { ESP_LOGI(TAG, "---- stop http server"); if (stop_webserver(*server) == ESP_OK) { *server = NULL; } else { ESP_LOGE(TAG, "Failed to stop http server"); } } }