diff --git a/components/sys_wifi/CMakeLists.txt b/components/sys_wifi/CMakeLists.txt index ab3ec4e..ea52e1f 100644 --- a/components/sys_wifi/CMakeLists.txt +++ b/components/sys_wifi/CMakeLists.txt @@ -9,6 +9,7 @@ idf_component_register( esp_event esp_netif nvs_flash + app_update esp_http_server freertos log diff --git a/components/sys_wifi/sys_wifi.c b/components/sys_wifi/sys_wifi.c index 4e7028d..23ee6c8 100644 --- a/components/sys_wifi/sys_wifi.c +++ b/components/sys_wifi/sys_wifi.c @@ -6,6 +6,7 @@ #include "esp_http_server.h" #include "esp_log.h" #include "esp_netif.h" +#include "esp_ota_ops.h" #include "esp_wifi.h" #include "nvs.h" #include "nvs_flash.h" @@ -21,6 +22,13 @@ static httpd_handle_t s_http_server; static char s_sta_ssid[33]; static char s_sta_password[65]; +static void ota_reboot_task(void *arg) +{ + (void)arg; + vTaskDelay(pdMS_TO_TICKS(1000)); + esp_restart(); +} + static void copy_wifi_string(uint8_t *destination, size_t destination_size, const char *source) { size_t length = strlen(source); @@ -189,6 +197,86 @@ static esp_err_t provisioning_get(httpd_req_t *request) return httpd_resp_send(request, page, HTTPD_RESP_USE_STRLEN); } +static esp_err_t ota_get(httpd_req_t *request) +{ + const char *page = + "" + "ABrobot OTA" + "

Firmware update

" + "
" + "

"; + httpd_resp_set_type(request, "text/html"); + return httpd_resp_send(request, page, HTTPD_RESP_USE_STRLEN); +} + +static esp_err_t ota_post(httpd_req_t *request) +{ + const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL); + if (!update_partition || request->content_len == 0 || request->content_len > update_partition->size) { + httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Firmware image is too large or unavailable"); + return ESP_ERR_INVALID_SIZE; + } + + esp_ota_handle_t ota_handle; + esp_err_t err = esp_ota_begin(update_partition, request->content_len, &ota_handle); + if (err != ESP_OK) { + httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not begin firmware update"); + return err; + } + + uint8_t *buffer = malloc(4096); + if (!buffer) { + esp_ota_abort(ota_handle); + httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Not enough memory for firmware update"); + return ESP_ERR_NO_MEM; + } + + size_t remaining = request->content_len; + while (remaining > 0) { + size_t chunk_size = remaining < 4096 ? remaining : 4096; + int received = httpd_req_recv(request, (char *)buffer, chunk_size); + if (received <= 0) { + free(buffer); + esp_ota_abort(ota_handle); + httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Firmware upload interrupted"); + return ESP_FAIL; + } + err = esp_ota_write(ota_handle, buffer, received); + if (err != ESP_OK) { + free(buffer); + esp_ota_abort(ota_handle); + httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not write firmware image"); + return err; + } + remaining -= (size_t)received; + } + free(buffer); + + err = esp_ota_end(ota_handle); + if (err != ESP_OK) { + httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Invalid firmware image"); + return err; + } + err = esp_ota_set_boot_partition(update_partition); + if (err != ESP_OK) { + httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not select new firmware"); + return err; + } + + httpd_resp_sendstr(request, "Firmware update installed. Rebooting."); + ESP_LOGI(TAG, "OTA image installed to %s; rebooting", update_partition->label); + xTaskCreate(ota_reboot_task, "ota_reboot", 2048, NULL, 5, NULL); + return ESP_OK; +} + static esp_err_t provisioning_post(httpd_req_t *request) { char body[160] = {0}; @@ -235,15 +323,21 @@ static esp_err_t provisioning_post(httpd_req_t *request) static void start_provisioning_server(void) { + if (s_http_server != NULL) return; + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); if (httpd_start(&s_http_server, &config) != ESP_OK) { ESP_LOGE(TAG, "Failed to start provisioning web server"); return; } const httpd_uri_t get_uri = {.uri = "/", .method = HTTP_GET, .handler = provisioning_get}; + const httpd_uri_t ota_get_uri = {.uri = "/ota", .method = HTTP_GET, .handler = ota_get}; + const httpd_uri_t ota_post_uri = {.uri = "/ota", .method = HTTP_POST, .handler = ota_post}; const httpd_uri_t scan_uri = {.uri = "/scan", .method = HTTP_GET, .handler = provisioning_scan}; const httpd_uri_t post_uri = {.uri = "/configure", .method = HTTP_POST, .handler = provisioning_post}; httpd_register_uri_handler(s_http_server, &get_uri); + httpd_register_uri_handler(s_http_server, &ota_get_uri); + httpd_register_uri_handler(s_http_server, &ota_post_uri); httpd_register_uri_handler(s_http_server, &scan_uri); httpd_register_uri_handler(s_http_server, &post_uri); } @@ -286,7 +380,9 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t e int8_t rssi = 0; if (esp_wifi_sta_get_ap_info(&record) == ESP_OK) rssi = record.rssi; set_status(WIFI_STATE_CONNECTED, rssi); - ESP_LOGI(TAG, "Wi-Fi connected"); + start_provisioning_server(); + const ip_event_got_ip_t *got_ip = (const ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG, "Wi-Fi connected, web server at http://" IPSTR, IP2STR(&got_ip->ip_info.ip)); } }