#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 const char WIFI_HTML[] = "" "" "ABrobot Wi-Fi Manager" "" "
" "
" "
" "
" "
ESP32-C3 Wi-Fi Setup
" "
" " " "
" "
" "
Wireless Network Configuration
" "
" "
" " " "
" " " " " "
" "
" " " "
" " " " " "
" "
" " " "
" " " " " "
" "
" " " "
" "
" "
" "
" "
Device Information
" "
" "
Provisioning APABrobot-OS
" "
Default AP IP192.168.4.1
" "
Hardware PortTX:GPIO21 / RX:GPIO20
" "
StatusOperational
" "
" "
" "
" ""; 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) { httpd_resp_set_type(request, "text/html"); return httpd_resp_send(request, WIFI_HTML, 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); }