diff --git a/include/zh_ota_server.h b/include/zh_ota_server.h index fb58253..ba1e550 100644 --- a/include/zh_ota_server.h +++ b/include/zh_ota_server.h @@ -22,9 +22,14 @@ #pragma once +#include "string.h" #include "esp_ota_ops.h" #include "esp_log.h" #include "esp_http_server.h" +#include "esp_heap_caps.h" +#include "esp_system.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #ifdef __cplusplus extern "C" @@ -48,6 +53,7 @@ extern "C" * * @note The path string is copied internally and must not be freed * by the caller. + * * @warning The memory allocated for the path is never freed. * Call this function only once during application lifetime. */ diff --git a/zh_ota_server.c b/zh_ota_server.c index 6ba9adb..2a8489d 100644 --- a/zh_ota_server.c +++ b/zh_ota_server.c @@ -1,7 +1,5 @@ #include "zh_ota_server.h" -static const char *TAG = "zh_ota_server"; - #define ZH_LOGI(msg, ...) ESP_LOGI(TAG, msg, ##__VA_ARGS__) #define ZH_LOGE(msg, err, ...) ESP_LOGE(TAG, "[%s:%d:%s] " msg, __FILE__, __LINE__, esp_err_to_name(err), ##__VA_ARGS__) @@ -13,10 +11,21 @@ static const char *TAG = "zh_ota_server"; return err; \ } +#define ZH_ERROR_CHECK_CONT(cond, cleanup, msg, ...) \ + if (!(cond)) \ + { \ + ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \ + cleanup; \ + } + #define MIN(a, b) (((a) < (b)) ? (a) : (b)) -static char *_ota_path = NULL; /*!< Internal storage for the OTA server URI path */ -static char *_ota_ws_path = "/zh_ota_server"; /*!< Fixed URI path for WebSocket-style firmware upload */ +static const char *TAG = "zh_ota_server"; + +static char *_ota_path = NULL; +static const char *_ota_ws_path = "/zh_ota_server"; + +volatile static bool _is_initialized = false; /** * @brief HTTP GET handler for the OTA web interface. @@ -51,13 +60,13 @@ static esp_err_t _ota_ws_page_handler(httpd_req_t *req); extern const uint8_t zh_ota_server_html_start[] asm("_binary_zh_ota_server_html_start"); extern const uint8_t zh_ota_server_html_end[] asm("_binary_zh_ota_server_html_end"); -httpd_uri_t _ota_page = { +static httpd_uri_t _ota_page = { .uri = NULL, .method = HTTP_GET, .handler = _ota_page_handler, .user_ctx = NULL}; -httpd_uri_t _ota_page_ws = { +static httpd_uri_t _ota_page_ws = { .uri = NULL, .method = HTTP_POST, .handler = _ota_ws_page_handler, @@ -67,13 +76,17 @@ esp_err_t zh_ota_server_init(httpd_handle_t server, const char *path) { ZH_LOGI("OTA server initialization started."); ZH_ERROR_CHECK(server != NULL && path != NULL, ESP_ERR_INVALID_ARG, NULL, "OTA server initialization failed. Invalid argument."); + ZH_ERROR_CHECK(_is_initialized == false, ESP_ERR_INVALID_STATE, NULL, "OTA server initialization failed. OTA server already initialized."); _ota_path = (char *)heap_caps_calloc(1, strlen(path) + 1, MALLOC_CAP_8BIT); ZH_ERROR_CHECK(_ota_path != NULL, ESP_ERR_NO_MEM, NULL, "OTA server initialization failed. Memory allocation fail or no free memory in the heap."); strcpy(_ota_path, path); _ota_page.uri = _ota_path; _ota_page_ws.uri = _ota_ws_path; - ZH_ERROR_CHECK(httpd_register_uri_handler(server, &_ota_page) == ESP_OK, ESP_FAIL, heap_caps_free(_ota_path), "OTA server initialization failed. Register uri handler failed."); - ZH_ERROR_CHECK(httpd_register_uri_handler(server, &_ota_page_ws) == ESP_OK, ESP_FAIL, heap_caps_free(_ota_path), "OTA server initialization failed. Register uri handler failed."); + ZH_ERROR_CHECK(httpd_register_uri_handler(server, &_ota_page) == ESP_OK, ESP_FAIL, heap_caps_free(_ota_path); _ota_path = NULL; _ota_page.uri = NULL, "OTA server initialization failed. Register uri handler failed."); + ZH_ERROR_CHECK(httpd_register_uri_handler(server, &_ota_page_ws) == ESP_OK, ESP_FAIL, + {ZH_ERROR_CHECK_CONT(httpd_unregister_uri_handler(server, _ota_page.uri, HTTP_GET) == ESP_OK, NULL, "Unregister uri handler failed.")}; + heap_caps_free(_ota_path); _ota_path = NULL; _ota_page.uri = NULL, "OTA server initialization failed. Register uri handler failed."); + _is_initialized = true; ZH_LOGI("OTA server initialization completed successfully."); return ESP_OK; } @@ -101,17 +114,19 @@ esp_err_t _ota_ws_page_handler(httpd_req_t *req) } else if (recv_len <= 0) { - ZH_ERROR_CHECK(httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Protocol Error") == ESP_OK, ESP_FAIL, NULL, "HTTP server internal error."); + ZH_ERROR_CHECK_CONT(httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Protocol Error") == ESP_OK, NULL, "HTTP server internal error."); + ZH_ERROR_CHECK_CONT(esp_ota_abort(ota_handle) == ESP_OK, NULL, "OTA abort failed."); return ESP_FAIL; } ZH_ERROR_CHECK(esp_ota_write(ota_handle, (const void *)buf, recv_len) == ESP_OK, ESP_FAIL, - {ZH_ERROR_CHECK(httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Flash error.") == ESP_OK, ESP_FAIL, NULL, "HTTP server internal error.")}, "OTA write failed."); + {ZH_ERROR_CHECK_CONT(httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Flash error.") == ESP_OK, NULL, "HTTP server internal error.")}; + {ZH_ERROR_CHECK_CONT(esp_ota_abort(ota_handle) == ESP_OK, NULL, "OTA abort failed.")}, "OTA write failed."); remaining -= recv_len; } ZH_ERROR_CHECK(esp_ota_end(ota_handle) == ESP_OK, ESP_FAIL, - {ZH_ERROR_CHECK(httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Validation / Activation Error.") == ESP_OK, ESP_FAIL, NULL, "HTTP server internal error.")}, "OTA end failed."); + {ZH_ERROR_CHECK_CONT(httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Validation / Activation Error.") == ESP_OK, NULL, "HTTP server internal error.")}, "OTA end failed."); ZH_ERROR_CHECK(esp_ota_set_boot_partition(ota_partition) == ESP_OK, ESP_FAIL, - {ZH_ERROR_CHECK(httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Validation / Activation Error.") == ESP_OK, ESP_FAIL, NULL, "HTTP server internal error.")}, "OTA set boot partition failed."); + {ZH_ERROR_CHECK_CONT(httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Validation / Activation Error.") == ESP_OK, NULL, "HTTP server internal error.")}, "OTA set boot partition failed."); ZH_ERROR_CHECK(httpd_resp_sendstr(req, "Firmware update complete, rebooting now!\n") == ESP_OK, ESP_FAIL, NULL, "HTTP server internal error."); vTaskDelay(500 / portTICK_PERIOD_MS); esp_restart();