From ee046a001eaf745cde8ffeb9414e3fe07493fdbf Mon Sep 17 00:00:00 2001 From: dark98 Date: Thu, 3 Sep 2026 02:12:07 +0100 Subject: [PATCH] Refactor Web Server --- components/sys_wifi/CMakeLists.txt | 3 +- components/sys_wifi/sys_wifi.c | 82 +--------- components/web_server/CMakeLists.txt | 18 +++ components/web_server/include/web_server.h | 16 ++ .../web_server/include/web_server_pages.h | 9 ++ components/web_server/pages/ota.c | 56 +++++++ components/web_server/pages/wifi.c | 144 ++++++++++++++++++ components/web_server/web_server.c | 21 +++ 8 files changed, 271 insertions(+), 78 deletions(-) create mode 100644 components/web_server/CMakeLists.txt create mode 100644 components/web_server/include/web_server.h create mode 100644 components/web_server/include/web_server_pages.h create mode 100644 components/web_server/pages/ota.c create mode 100644 components/web_server/pages/wifi.c create mode 100644 components/web_server/web_server.c diff --git a/components/sys_wifi/CMakeLists.txt b/components/sys_wifi/CMakeLists.txt index ea52e1f..c4497ce 100644 --- a/components/sys_wifi/CMakeLists.txt +++ b/components/sys_wifi/CMakeLists.txt @@ -5,12 +5,11 @@ idf_component_register( "include" REQUIRES sys_core + web_server esp_wifi 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 23ee6c8..70c6702 100644 --- a/components/sys_wifi/sys_wifi.c +++ b/components/sys_wifi/sys_wifi.c @@ -1,34 +1,23 @@ #include "sys_wifi.h" +#include "web_server.h" #include "os_event.h" #include "os_status.h" #include "sdkconfig.h" #include "esp_event.h" -#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" -#include -#include #include #include static const char *TAG = "SYS_WIFI"; static esp_netif_t *s_sta_netif; static esp_netif_t *s_ap_netif; -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); @@ -43,18 +32,6 @@ static void set_status(wifi_state_t state, int8_t rssi) sys_event_post(SYS_EVENT_WIFI_STATUS_CHANGED, (int32_t)state, NULL); } -static esp_err_t save_credentials(const char *ssid, const char *password) -{ - nvs_handle_t nvs; - esp_err_t err = nvs_open("wifi", NVS_READWRITE, &nvs); - if (err != ESP_OK) return err; - err = nvs_set_str(nvs, "ssid", ssid); - if (err == ESP_OK) err = nvs_set_str(nvs, "password", password); - if (err == ESP_OK) err = nvs_commit(nvs); - nvs_close(nvs); - return err; -} - static esp_err_t load_credentials(void) { nvs_handle_t nvs; @@ -68,56 +45,7 @@ static esp_err_t load_credentials(void) return err; } -static int hex_value(char value) -{ - if (value >= '0' && value <= '9') return value - '0'; - if (value >= 'a' && value <= 'f') return value - 'a' + 10; - if (value >= 'A' && value <= 'F') return value - 'A' + 10; - return -1; -} - -static bool form_value(const char *body, const char *key, char *output, size_t output_size) -{ - size_t key_len = strlen(key); - const char *cursor = body; - size_t output_len = 0; - while (cursor && *cursor) { - if ((cursor == body || cursor[-1] == '&') && strncmp(cursor, key, key_len) == 0 && cursor[key_len] == '=') { - cursor += key_len + 1; - while (*cursor && *cursor != '&') { - int value = 0; - if (*cursor == '+') { - value = ' '; - cursor++; - } else if (*cursor == '%' && hex_value(cursor[1]) >= 0 && hex_value(cursor[2]) >= 0) { - value = (hex_value(cursor[1]) << 4) | hex_value(cursor[2]); - cursor += 3; - } else { - value = (unsigned char)*cursor++; - } - if (output_len + 1 >= output_size) return false; - output[output_len++] = (char)value; - } - output[output_len] = '\0'; - return output_len > 0; - } - cursor = strchr(cursor, '&'); - if (cursor) cursor++; - } - return false; -} - -static void append_json_string(httpd_req_t *request, const uint8_t *value, size_t length) -{ - for (size_t i = 0; i < length && value[i] != '\0'; i++) { - char escaped[2] = {(char)value[i], '\0'}; - if (value[i] == '"' || value[i] == '\\') { - httpd_resp_send_chunk(request, "\\", 1); - } - httpd_resp_send_chunk(request, escaped, 1); - } -} - +#if 0 static esp_err_t provisioning_scan(httpd_req_t *request) { wifi_scan_config_t scan_config = { @@ -342,6 +270,8 @@ static void start_provisioning_server(void) httpd_register_uri_handler(s_http_server, &post_uri); } +#endif + static void start_softap(void) { wifi_config_t ap_config = {0}; @@ -353,7 +283,7 @@ static void start_softap(void) esp_wifi_set_config(WIFI_IF_AP, &ap_config); esp_wifi_start(); set_status(WIFI_STATE_AP_MODE, 0); - start_provisioning_server(); + web_server_start(); ESP_LOGI(TAG, "Provisioning AP '%s', browse to http://192.168.4.1", CONFIG_SYS_WIFI_AP_SSID); } @@ -380,7 +310,7 @@ 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); - start_provisioning_server(); + web_server_start(); 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)); } diff --git a/components/web_server/CMakeLists.txt b/components/web_server/CMakeLists.txt new file mode 100644 index 0000000..33c3df9 --- /dev/null +++ b/components/web_server/CMakeLists.txt @@ -0,0 +1,18 @@ +idf_component_register( + SRCS + "web_server.c" + "pages/wifi.c" + "pages/ota.c" + INCLUDE_DIRS + "include" + REQUIRES + sys_core + esp_wifi + esp_event + esp_netif + nvs_flash + app_update + esp_http_server + freertos + log +) diff --git a/components/web_server/include/web_server.h b/components/web_server/include/web_server.h new file mode 100644 index 0000000..51e508f --- /dev/null +++ b/components/web_server/include/web_server.h @@ -0,0 +1,16 @@ +#ifndef WEB_SERVER_H +#define WEB_SERVER_H + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +esp_err_t web_server_start(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/web_server/include/web_server_pages.h b/components/web_server/include/web_server_pages.h new file mode 100644 index 0000000..efc3da5 --- /dev/null +++ b/components/web_server/include/web_server_pages.h @@ -0,0 +1,9 @@ +#ifndef WEB_SERVER_PAGES_H +#define WEB_SERVER_PAGES_H + +#include "esp_http_server.h" + +void web_server_register_wifi_pages(httpd_handle_t server); +void web_server_register_ota_page(httpd_handle_t server); + +#endif diff --git a/components/web_server/pages/ota.c b/components/web_server/pages/ota.c new file mode 100644 index 0000000..eaf2e80 --- /dev/null +++ b/components/web_server/pages/ota.c @@ -0,0 +1,56 @@ +#include "web_server_pages.h" +#include "esp_log.h" +#include "esp_ota_ops.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include + +static const char *TAG = "SYS_WEB_OTA"; + +static void reboot_task(void *arg) +{ + (void)arg; + vTaskDelay(pdMS_TO_TICKS(1000)); + esp_restart(); +} + +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 *partition = esp_ota_get_next_update_partition(NULL); + if (!partition || request->content_len == 0 || request->content_len > partition->size) return httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Firmware image is too large or unavailable"); + esp_ota_handle_t handle; + esp_err_t err = esp_ota_begin(partition, request->content_len, &handle); + if (err != ESP_OK) return httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not begin firmware update"); + uint8_t *buffer = malloc(4096); + if (!buffer) { esp_ota_abort(handle); return httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Not enough memory"); } + size_t remaining = request->content_len; + while (remaining) { + size_t requested = remaining < 4096 ? remaining : 4096; + int received = httpd_req_recv(request, (char *)buffer, requested); + if (received <= 0 || esp_ota_write(handle, buffer, received) != ESP_OK) { free(buffer); esp_ota_abort(handle); return httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Firmware upload failed"); } + remaining -= (size_t)received; + } + free(buffer); + err = esp_ota_end(handle); + if (err == ESP_OK) err = esp_ota_set_boot_partition(partition); + if (err != ESP_OK) return httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Invalid firmware image"); + httpd_resp_sendstr(request, "Firmware update installed. Rebooting."); + ESP_LOGI(TAG, "OTA image installed to %s; rebooting", partition->label); + xTaskCreate(reboot_task, "ota_reboot", 2048, NULL, 5, NULL); + return ESP_OK; +} + +void web_server_register_ota_page(httpd_handle_t server) +{ + const httpd_uri_t page = {.uri = "/ota", .method = HTTP_GET, .handler = ota_get}; + const httpd_uri_t upload = {.uri = "/ota", .method = HTTP_POST, .handler = ota_post}; + httpd_register_uri_handler(server, &page); + httpd_register_uri_handler(server, &upload); +} diff --git a/components/web_server/pages/wifi.c b/components/web_server/pages/wifi.c new file mode 100644 index 0000000..d340f55 --- /dev/null +++ b/components/web_server/pages/wifi.c @@ -0,0 +1,144 @@ +#include "web_server_pages.h" +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_wifi.h" +#include "nvs.h" +#include +#include +#include + +static const char *TAG = "SYS_WEB_WIFI"; +static char s_ssid[33]; +static char s_password[65]; + +static void copy_string(uint8_t *destination, size_t size, const char *source) +{ + size_t length = strlen(source); + if (length >= size) length = size - 1; + memcpy(destination, source, length); + destination[length] = '\0'; +} + +static int hex_value(char value) +{ + if (value >= '0' && value <= '9') return value - '0'; + if (value >= 'a' && value <= 'f') return value - 'a' + 10; + if (value >= 'A' && value <= 'F') return value - 'A' + 10; + return -1; +} + +static bool form_value(const char *body, const char *key, char *output, size_t size) +{ + size_t key_length = strlen(key); + const char *cursor = body; + size_t output_length = 0; + while (cursor && *cursor) { + if ((cursor == body || cursor[-1] == '&') && strncmp(cursor, key, key_length) == 0 && cursor[key_length] == '=') { + cursor += key_length + 1; + while (*cursor && *cursor != '&') { + int value; + if (*cursor == '+') { value = ' '; cursor++; } + else if (*cursor == '%' && hex_value(cursor[1]) >= 0 && hex_value(cursor[2]) >= 0) { + value = (hex_value(cursor[1]) << 4) | hex_value(cursor[2]); cursor += 3; + } else value = (unsigned char)*cursor++; + if (output_length + 1 >= size) return false; + output[output_length++] = (char)value; + } + output[output_length] = '\0'; + return output_length > 0; + } + cursor = strchr(cursor, '&'); + if (cursor) cursor++; + } + return false; +} + +static void append_json_string(httpd_req_t *request, const uint8_t *value, size_t length) +{ + for (size_t i = 0; i < length && value[i]; i++) { + if (value[i] == '"' || value[i] == '\\') httpd_resp_send_chunk(request, "\\", 1); + char character[2] = {(char)value[i], '\0'}; + httpd_resp_send_chunk(request, character, 1); + } +} + +static esp_err_t root_get(httpd_req_t *request) +{ + const char *page = "ABrobot Wi-Fi

ABrobot Wi-Fi setup





"; + httpd_resp_set_type(request, "text/html"); + return httpd_resp_send(request, page, HTTPD_RESP_USE_STRLEN); +} + +static esp_err_t scan_get(httpd_req_t *request) +{ + wifi_scan_config_t config = {.show_hidden = true, .scan_type = WIFI_SCAN_TYPE_ACTIVE}; + esp_err_t err = esp_wifi_scan_start(&config, true); + if (err != ESP_OK) return httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Wi-Fi scan failed"); + uint16_t count = 0; + esp_wifi_scan_get_ap_num(&count); + if (count > 32) count = 32; + wifi_ap_record_t *records = calloc(count, sizeof(wifi_ap_record_t)); + if (count && !records) return httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Not enough memory"); + if (count && esp_wifi_scan_get_ap_records(&count, records) != ESP_OK) { free(records); return httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not read scan results"); } + httpd_resp_set_type(request, "application/json"); + httpd_resp_send_chunk(request, "[", 1); + uint16_t visible = 0; + for (uint16_t i = 0; i < count; i++) { + if (!records[i].ssid[0]) continue; + char prefix[32]; + int length = snprintf(prefix, sizeof(prefix), "%s{\"ssid\":\"", visible ? "," : ""); + httpd_resp_send_chunk(request, prefix, length); + append_json_string(request, records[i].ssid, sizeof(records[i].ssid)); + char suffix[48]; + length = snprintf(suffix, sizeof(suffix), "\",\"rssi\":%d}", records[i].rssi); + httpd_resp_send_chunk(request, suffix, length); + visible++; + } + httpd_resp_send_chunk(request, "]", 1); + free(records); + return httpd_resp_send_chunk(request, NULL, 0); +} + +static esp_err_t save_credentials(const char *ssid, const char *password) +{ + nvs_handle_t nvs; + esp_err_t err = nvs_open("wifi", NVS_READWRITE, &nvs); + if (err != ESP_OK) return err; + err = nvs_set_str(nvs, "ssid", ssid); + if (err == ESP_OK) err = nvs_set_str(nvs, "password", password); + if (err == ESP_OK) err = nvs_commit(nvs); + nvs_close(nvs); + return err; +} + +static esp_err_t configure_post(httpd_req_t *request) +{ + char body[160] = {0}; + char ssid[sizeof(s_ssid)] = {0}; + char password[sizeof(s_password)] = {0}; + int received = httpd_req_recv(request, body, sizeof(body) - 1); + if (received <= 0 || !form_value(body, "ssid", ssid, sizeof(ssid)) || !form_value(body, "password", password, sizeof(password))) return httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "SSID and password are required"); + esp_err_t err = save_credentials(ssid, password); + if (err != ESP_OK) return httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Could not save credentials"); + copy_string((uint8_t *)s_ssid, sizeof(s_ssid), ssid); + copy_string((uint8_t *)s_password, sizeof(s_password), password); + httpd_resp_sendstr(request, "Credentials saved. The device is connecting; you can close this page."); + wifi_config_t config = {0}; + copy_string(config.sta.ssid, sizeof(config.sta.ssid), s_ssid); + copy_string(config.sta.password, sizeof(config.sta.password), s_password); + err = esp_wifi_set_mode(WIFI_MODE_STA); + if (err == ESP_OK) err = esp_wifi_set_config(WIFI_IF_STA, &config); + if (err == ESP_OK) err = esp_wifi_connect(); + if (err != ESP_OK) ESP_LOGE(TAG, "Station connection failed: %s", esp_err_to_name(err)); + return err; +} + +void web_server_register_wifi_pages(httpd_handle_t server) +{ + const httpd_uri_t root = {.uri = "/", .method = HTTP_GET, .handler = root_get}; + const httpd_uri_t scan = {.uri = "/scan", .method = HTTP_GET, .handler = scan_get}; + const httpd_uri_t configure = {.uri = "/configure", .method = HTTP_POST, .handler = configure_post}; + httpd_register_uri_handler(server, &root); + httpd_register_uri_handler(server, &scan); + httpd_register_uri_handler(server, &configure); +} diff --git a/components/web_server/web_server.c b/components/web_server/web_server.c new file mode 100644 index 0000000..2341b88 --- /dev/null +++ b/components/web_server/web_server.c @@ -0,0 +1,21 @@ +#include "web_server.h" +#include "web_server_pages.h" +#include "esp_http_server.h" +#include "esp_log.h" + +static const char *TAG = "SYS_WEB"; +static httpd_handle_t s_server; + +esp_err_t web_server_start(void) +{ + if (s_server) return ESP_OK; + + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + esp_err_t err = httpd_start(&s_server, &config); + if (err != ESP_OK) return err; + + web_server_register_wifi_pages(s_server); + web_server_register_ota_page(s_server); + ESP_LOGI(TAG, "Web server started"); + return ESP_OK; +}