#include "wifi_configuration_ap.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ssid_manager.h" #define TAG "WifiConfigurationAp" #define WIFI_CONNECTED_BIT BIT0 #define WIFI_FAIL_BIT BIT1 extern const char index_html_start[] asm("_binary_wifi_configuration_html_start"); extern const char done_html_start[] asm("_binary_wifi_configuration_done_html_start"); WifiConfigurationAp& WifiConfigurationAp::GetInstance() { static WifiConfigurationAp instance; return instance; } WifiConfigurationAp::WifiConfigurationAp() { event_group_ = xEventGroupCreate(); language_ = "zh-CN"; sleep_mode_ = false; } std::vector WifiConfigurationAp::GetAccessPoints() { std::lock_guard lock(mutex_); return ap_records_; } WifiConfigurationAp::~WifiConfigurationAp() { if (scan_timer_) { esp_timer_stop(scan_timer_); esp_timer_delete(scan_timer_); } if (event_group_) { vEventGroupDelete(event_group_); } // Unregister event handlers if they were registered if (instance_any_id_) { esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id_); } if (instance_got_ip_) { esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip_); } } void WifiConfigurationAp::SetLanguage(const std::string &&language) { language_ = language; } void WifiConfigurationAp::SetSsidPrefix(const std::string &&ssid_prefix) { ssid_prefix_ = ssid_prefix; } void WifiConfigurationAp::Start() { // Register event handlers ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &WifiConfigurationAp::WifiEventHandler, this, &instance_any_id_)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &WifiConfigurationAp::IpEventHandler, this, &instance_got_ip_)); StartAccessPoint(); StartWebServer(); // Start scan immediately esp_wifi_scan_start(nullptr, false); // Setup periodic WiFi scan timer esp_timer_create_args_t timer_args = { .callback = [](void* arg) { auto* self = static_cast(arg); if (!self->is_connecting_) { esp_wifi_scan_start(nullptr, false); } }, .arg = this, .dispatch_method = ESP_TIMER_TASK, .name = "wifi_scan_timer", .skip_unhandled_events = true }; ESP_ERROR_CHECK(esp_timer_create(&timer_args, &scan_timer_)); } std::string WifiConfigurationAp::GetSsid() { // Get MAC and use it to generate a unique SSID uint8_t mac[6]; #if CONFIG_IDF_TARGET_ESP32P4 esp_wifi_get_mac(WIFI_IF_AP, mac); #else ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP)); #endif char ssid[32]; snprintf(ssid, sizeof(ssid), "%s-%02X%02X", ssid_prefix_.c_str(), mac[4], mac[5]); return std::string(ssid); } std::string WifiConfigurationAp::GetWebServerUrl() { // http://192.168.4.1 return "http://192.168.4.1"; } void WifiConfigurationAp::StartAccessPoint() { // Initialize the TCP/IP stack ESP_ERROR_CHECK(esp_netif_init()); // Create the default event loop ap_netif_ = esp_netif_create_default_wifi_ap(); // Set the router IP address to 192.168.4.1 esp_netif_ip_info_t ip_info; IP4_ADDR(&ip_info.ip, 192, 168, 4, 1); IP4_ADDR(&ip_info.gw, 192, 168, 4, 1); IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0); esp_netif_dhcps_stop(ap_netif_); esp_netif_set_ip_info(ap_netif_, &ip_info); esp_netif_dhcps_start(ap_netif_); // Start the DNS server dns_server_.Start(ip_info.gw); // Initialize the WiFi stack in Access Point mode wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // Get the SSID std::string ssid = GetSsid(); // Set the WiFi configuration wifi_config_t wifi_config = {}; strcpy((char *)wifi_config.ap.ssid, ssid.c_str()); wifi_config.ap.ssid_len = ssid.length(); wifi_config.ap.max_connection = 4; wifi_config.ap.authmode = WIFI_AUTH_OPEN; // Start the WiFi Access Point ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); ESP_ERROR_CHECK(esp_wifi_start()); #ifdef CONFIG_SOC_WIFI_SUPPORT_5G // Temporarily use only 2.4G Wi-Fi. ESP_ERROR_CHECK(esp_wifi_set_band_mode(WIFI_BAND_MODE_2G_ONLY)); #endif ESP_LOGI(TAG, "Access Point started with SSID %s", ssid.c_str()); // 加载高级配置 nvs_handle_t nvs; esp_err_t err = nvs_open("wifi", NVS_READONLY, &nvs); if (err == ESP_OK) { // 读取OTA URL char ota_url[256] = {0}; size_t ota_url_size = sizeof(ota_url); err = nvs_get_str(nvs, "ota_url", ota_url, &ota_url_size); if (err == ESP_OK) { ota_url_ = ota_url; } // 读取WiFi功率 err = nvs_get_i8(nvs, "max_tx_power", &max_tx_power_); if (err == ESP_OK) { ESP_LOGI(TAG, "WiFi max tx power from NVS: %d", max_tx_power_); ESP_ERROR_CHECK(esp_wifi_set_max_tx_power(max_tx_power_)); } else { esp_wifi_get_max_tx_power(&max_tx_power_); } // 读取BSSID记忆设置 uint8_t remember_bssid = 0; err = nvs_get_u8(nvs, "remember_bssid", &remember_bssid); if (err == ESP_OK) { remember_bssid_ = remember_bssid != 0; } else { remember_bssid_ = false; // 默认值 } // 读取睡眠模式设置 uint8_t sleep_mode = 0; err = nvs_get_u8(nvs, "sleep_mode", &sleep_mode); if (err == ESP_OK) { sleep_mode_ = sleep_mode != 0; } else { sleep_mode_ = true; // 默认值 } nvs_close(nvs); } } void WifiConfigurationAp::StartWebServer() { // Start the web server httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.max_uri_handlers = 24; config.uri_match_fn = httpd_uri_match_wildcard; ESP_ERROR_CHECK(httpd_start(&server_, &config)); // Register the index.html file httpd_uri_t index_html = { .uri = "/", .method = HTTP_GET, .handler = [](httpd_req_t *req) -> esp_err_t { httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_send(req, index_html_start, strlen(index_html_start)); return ESP_OK; }, .user_ctx = NULL }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &index_html)); // Register the /saved/list URI httpd_uri_t saved_list = { .uri = "/saved/list", .method = HTTP_GET, .handler = [](httpd_req_t *req) -> esp_err_t { auto ssid_list = SsidManager::GetInstance().GetSsidList(); std::string json_str = "["; for (const auto& ssid : ssid_list) { json_str += "\"" + ssid.ssid + "\","; } if (json_str.length() > 1) { json_str.pop_back(); // Remove the last comma } json_str += "]"; httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_send(req, json_str.c_str(), HTTPD_RESP_USE_STRLEN); return ESP_OK; }, .user_ctx = NULL }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &saved_list)); // Register the /saved/set_default URI httpd_uri_t saved_set_default = { .uri = "/saved/set_default", .method = HTTP_GET, .handler = [](httpd_req_t *req) -> esp_err_t { std::string uri = req->uri; auto pos = uri.find("?index="); if (pos != std::string::npos) { int index = -1; sscanf(&req->uri[pos+7], "%d", &index); ESP_LOGI(TAG, "Set default item %d", index); SsidManager::GetInstance().SetDefaultSsid(index); } // send {} httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_send(req, "{}", HTTPD_RESP_USE_STRLEN); return ESP_OK; }, .user_ctx = NULL }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &saved_set_default)); // Register the /saved/delete URI httpd_uri_t saved_delete = { .uri = "/saved/delete", .method = HTTP_GET, .handler = [](httpd_req_t *req) -> esp_err_t { std::string uri = req->uri; auto pos = uri.find("?index="); if (pos != std::string::npos) { int index = -1; sscanf(&req->uri[pos+7], "%d", &index); ESP_LOGI(TAG, "Delete saved list item %d", index); SsidManager::GetInstance().RemoveSsid(index); } // send {} httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_send(req, "{}", HTTPD_RESP_USE_STRLEN); return ESP_OK; }, .user_ctx = NULL }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &saved_delete)); // Register the /scan URI httpd_uri_t scan = { .uri = "/scan", .method = HTTP_GET, .handler = [](httpd_req_t *req) -> esp_err_t { auto *this_ = static_cast(req->user_ctx); std::lock_guard lock(this_->mutex_); // Send the scan results as JSON httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_sendstr_chunk(req, "["); for (int i = 0; i < this_->ap_records_.size(); i++) { ESP_LOGI(TAG, "SSID: %s, RSSI: %d, Authmode: %d", (char *)this_->ap_records_[i].ssid, this_->ap_records_[i].rssi, this_->ap_records_[i].authmode); char buf[128]; snprintf(buf, sizeof(buf), "{\"ssid\":\"%s\",\"rssi\":%d,\"authmode\":%d}", (char *)this_->ap_records_[i].ssid, this_->ap_records_[i].rssi, this_->ap_records_[i].authmode); httpd_resp_sendstr_chunk(req, buf); if (i < this_->ap_records_.size() - 1) { httpd_resp_sendstr_chunk(req, ","); } } httpd_resp_sendstr_chunk(req, "]"); httpd_resp_sendstr_chunk(req, NULL); return ESP_OK; }, .user_ctx = this }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &scan)); // Register the form submission httpd_uri_t form_submit = { .uri = "/submit", .method = HTTP_POST, .handler = [](httpd_req_t *req) -> esp_err_t { char *buf; size_t buf_len = req->content_len; if (buf_len > 1024) { // 限制最大请求体大小 httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Payload too large"); return ESP_FAIL; } buf = (char *)malloc(buf_len + 1); if (!buf) { httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to allocate memory"); return ESP_FAIL; } int ret = httpd_req_recv(req, buf, buf_len); if (ret <= 0) { free(buf); if (ret == HTTPD_SOCK_ERR_TIMEOUT) { httpd_resp_send_408(req); } else { httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Failed to receive request"); } return ESP_FAIL; } buf[ret] = '\0'; // 解析 JSON 数据 cJSON *json = cJSON_Parse(buf); free(buf); if (!json) { httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); return ESP_FAIL; } cJSON *ssid_item = cJSON_GetObjectItemCaseSensitive(json, "ssid"); cJSON *password_item = cJSON_GetObjectItemCaseSensitive(json, "password"); if (!cJSON_IsString(ssid_item) || (ssid_item->valuestring == NULL) || (strlen(ssid_item->valuestring) >= 33)) { cJSON_Delete(json); httpd_resp_send(req, "{\"success\":false,\"error\":\"Invalid SSID\"}", HTTPD_RESP_USE_STRLEN); return ESP_OK; } std::string ssid_str = ssid_item->valuestring; std::string password_str = ""; if (cJSON_IsString(password_item) && (password_item->valuestring != NULL) && (strlen(password_item->valuestring) < 65)) { password_str = password_item->valuestring; } // 获取当前对象 auto *this_ = static_cast(req->user_ctx); if (!this_->ConnectToWifi(ssid_str, password_str)) { cJSON_Delete(json); httpd_resp_send(req, "{\"success\":false,\"error\":\"Failed to connect to the Access Point\"}", HTTPD_RESP_USE_STRLEN); return ESP_OK; } this_->Save(ssid_str, password_str); cJSON_Delete(json); // 设置成功响应 httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_send(req, "{\"success\":true}", HTTPD_RESP_USE_STRLEN); return ESP_OK; }, .user_ctx = this }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &form_submit)); // Register the done.html page httpd_uri_t done_html = { .uri = "/done.html", .method = HTTP_GET, .handler = [](httpd_req_t *req) -> esp_err_t { httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_send(req, done_html_start, strlen(done_html_start)); return ESP_OK; }, .user_ctx = NULL }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &done_html)); // Register the reboot endpoint httpd_uri_t reboot = { .uri = "/reboot", .method = HTTP_POST, .handler = [](httpd_req_t *req) -> esp_err_t { auto* this_ = static_cast(req->user_ctx); // 设置响应头,防止浏览器缓存 httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Cache-Control", "no-store"); httpd_resp_set_hdr(req, "Connection", "close"); // 发送响应 httpd_resp_send(req, "{\"success\":true}", HTTPD_RESP_USE_STRLEN); // 创建一个延迟重启任务 ESP_LOGI(TAG, "Rebooting..."); xTaskCreate([](void *ctx) { // 等待200ms确保HTTP响应完全发送 vTaskDelay(pdMS_TO_TICKS(200)); // 停止Web服务器 auto* self = static_cast(ctx); if (self->server_) { httpd_stop(self->server_); } // 再等待100ms确保所有连接都已关闭 vTaskDelay(pdMS_TO_TICKS(100)); // 执行重启 esp_restart(); }, "reboot_task", 4096, this_, 5, NULL); return ESP_OK; }, .user_ctx = this }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &reboot)); auto captive_portal_handler = [](httpd_req_t *req) -> esp_err_t { auto *this_ = static_cast(req->user_ctx); std::string url = this_->GetWebServerUrl() + "/?lang=" + this_->language_; // Set content type to prevent browser warnings httpd_resp_set_type(req, "text/html"); httpd_resp_set_status(req, "302 Found"); httpd_resp_set_hdr(req, "Location", url.c_str()); httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_send(req, NULL, 0); return ESP_OK; }; // Register all common captive portal detection endpoints const char* captive_portal_urls[] = { "/hotspot-detect.html", // Apple "/generate_204*", // Android "/mobile/status.php", // Android "/check_network_status.txt", // Windows "/ncsi.txt", // Windows "/fwlink/", // Microsoft "/connectivity-check.html", // Firefox "/success.txt", // Various "/portal.html", // Various "/library/test/success.html" // Apple }; for (const auto& url : captive_portal_urls) { httpd_uri_t redirect_uri = { .uri = url, .method = HTTP_GET, .handler = captive_portal_handler, .user_ctx = this }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &redirect_uri)); } // Register the /advanced/config URI httpd_uri_t advanced_config = { .uri = "/advanced/config", .method = HTTP_GET, .handler = [](httpd_req_t *req) -> esp_err_t { // 获取当前对象 auto *this_ = static_cast(req->user_ctx); // 创建JSON对象 cJSON *json = cJSON_CreateObject(); if (!json) { httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to create JSON"); return ESP_FAIL; } // 添加配置项到JSON if (!this_->ota_url_.empty()) { cJSON_AddStringToObject(json, "ota_url", this_->ota_url_.c_str()); } cJSON_AddNumberToObject(json, "max_tx_power", this_->max_tx_power_); cJSON_AddBoolToObject(json, "remember_bssid", this_->remember_bssid_); cJSON_AddBoolToObject(json, "sleep_mode", this_->sleep_mode_); // 发送JSON响应 char *json_str = cJSON_PrintUnformatted(json); cJSON_Delete(json); if (!json_str) { httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to print JSON"); return ESP_FAIL; } httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_send(req, json_str, strlen(json_str)); free(json_str); return ESP_OK; }, .user_ctx = this }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &advanced_config)); // Register the /advanced/submit URI httpd_uri_t advanced_submit = { .uri = "/advanced/submit", .method = HTTP_POST, .handler = [](httpd_req_t *req) -> esp_err_t { char *buf; size_t buf_len = req->content_len; if (buf_len > 1024) { httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Payload too large"); return ESP_FAIL; } buf = (char *)malloc(buf_len + 1); if (!buf) { httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to allocate memory"); return ESP_FAIL; } int ret = httpd_req_recv(req, buf, buf_len); if (ret <= 0) { free(buf); if (ret == HTTPD_SOCK_ERR_TIMEOUT) { httpd_resp_send_408(req); } else { httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Failed to receive request"); } return ESP_FAIL; } buf[ret] = '\0'; // 解析JSON数据 cJSON *json = cJSON_Parse(buf); free(buf); if (!json) { httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); return ESP_FAIL; } // 获取当前对象 auto *this_ = static_cast(req->user_ctx); // 打开NVS nvs_handle_t nvs; esp_err_t err = nvs_open("wifi", NVS_READWRITE, &nvs); if (err != ESP_OK) { cJSON_Delete(json); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to open NVS"); return ESP_FAIL; } // 保存OTA URL cJSON *ota_url = cJSON_GetObjectItem(json, "ota_url"); if (cJSON_IsString(ota_url) && ota_url->valuestring) { this_->ota_url_ = ota_url->valuestring; err = nvs_set_str(nvs, "ota_url", this_->ota_url_.c_str()); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to save OTA URL: %d", err); } } // 保存WiFi功率 cJSON *max_tx_power = cJSON_GetObjectItem(json, "max_tx_power"); if (cJSON_IsNumber(max_tx_power)) { this_->max_tx_power_ = max_tx_power->valueint; err = esp_wifi_set_max_tx_power(this_->max_tx_power_); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to set WiFi power: %d", err); httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to set WiFi power"); return ESP_FAIL; } err = nvs_set_i8(nvs, "max_tx_power", this_->max_tx_power_); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to save WiFi power: %d", err); } } // 保存BSSID记忆设置 cJSON *remember_bssid = cJSON_GetObjectItem(json, "remember_bssid"); if (cJSON_IsBool(remember_bssid)) { this_->remember_bssid_ = cJSON_IsTrue(remember_bssid); err = nvs_set_u8(nvs, "remember_bssid", this_->remember_bssid_ ? 1 : 0); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to save remember_bssid: %d", err); } } // 保存睡眠模式设置 cJSON *sleep_mode = cJSON_GetObjectItem(json, "sleep_mode"); if (cJSON_IsBool(sleep_mode)) { this_->sleep_mode_ = cJSON_IsTrue(sleep_mode); err = nvs_set_u8(nvs, "sleep_mode", this_->sleep_mode_ ? 1 : 0); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to save sleep_mode: %d", err); } } // 提交更改 err = nvs_commit(nvs); nvs_close(nvs); cJSON_Delete(json); if (err != ESP_OK) { httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to save configuration"); return ESP_FAIL; } // 发送成功响应 httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Connection", "close"); httpd_resp_send(req, "{\"success\":true}", HTTPD_RESP_USE_STRLEN); ESP_LOGI(TAG, "Saved settings: ota_url=%s, max_tx_power=%d, remember_bssid=%d, sleep_mode=%d", this_->ota_url_.c_str(), this_->max_tx_power_, this_->remember_bssid_, this_->sleep_mode_); return ESP_OK; }, .user_ctx = this }; ESP_ERROR_CHECK(httpd_register_uri_handler(server_, &advanced_submit)); ESP_LOGI(TAG, "Web server started"); } bool WifiConfigurationAp::ConnectToWifi(const std::string &ssid, const std::string &password) { if (ssid.empty()) { ESP_LOGE(TAG, "SSID cannot be empty"); return false; } if (ssid.length() > 32) { // WiFi SSID 最大长度 ESP_LOGE(TAG, "SSID too long"); return false; } if (password.length() > 64) { ESP_LOGE(TAG, "Password too long"); return false; } is_connecting_ = true; esp_wifi_scan_stop(); xEventGroupClearBits(event_group_, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT); wifi_config_t wifi_config; bzero(&wifi_config, sizeof(wifi_config)); strlcpy((char *)wifi_config.sta.ssid, ssid.c_str(), 32); strlcpy((char *)wifi_config.sta.password, password.c_str(), 64); wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; wifi_config.sta.failure_retry_cnt = 1; ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); auto ret = esp_wifi_connect(); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to connect to WiFi: %d", ret); is_connecting_ = false; return false; } ESP_LOGI(TAG, "Connecting to WiFi %s", ssid.c_str()); // Wait for the connection to complete for 5 seconds EventBits_t bits = xEventGroupWaitBits(event_group_, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(10000)); is_connecting_ = false; if (bits & WIFI_CONNECTED_BIT) { ESP_LOGI(TAG, "Connected to WiFi %s", ssid.c_str()); esp_wifi_disconnect(); return true; } else { ESP_LOGE(TAG, "Failed to connect to WiFi %s", ssid.c_str()); return false; } } void WifiConfigurationAp::Save(const std::string &ssid, const std::string &password) { ESP_LOGI(TAG, "Save SSID %s %d", ssid.c_str(), ssid.length()); SsidManager::GetInstance().AddSsid(ssid, password); } void WifiConfigurationAp::WifiEventHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { WifiConfigurationAp* self = static_cast(arg); if (event_id == WIFI_EVENT_AP_STACONNECTED) { wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; ESP_LOGI(TAG, "Station " MACSTR " joined, AID=%d", MAC2STR(event->mac), event->aid); } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) { wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; ESP_LOGI(TAG, "Station " MACSTR " left, AID=%d", MAC2STR(event->mac), event->aid); } else if (event_id == WIFI_EVENT_STA_CONNECTED) { xEventGroupSetBits(self->event_group_, WIFI_CONNECTED_BIT); } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) { xEventGroupSetBits(self->event_group_, WIFI_FAIL_BIT); } else if (event_id == WIFI_EVENT_SCAN_DONE) { std::lock_guard lock(self->mutex_); uint16_t ap_num = 0; esp_wifi_scan_get_ap_num(&ap_num); self->ap_records_.resize(ap_num); esp_wifi_scan_get_ap_records(&ap_num, self->ap_records_.data()); // 扫描完成,等待10秒后再次扫描 esp_timer_start_once(self->scan_timer_, 10 * 1000000); } } void WifiConfigurationAp::IpEventHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { WifiConfigurationAp* self = static_cast(arg); if (event_id == IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; ESP_LOGI(TAG, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip)); xEventGroupSetBits(self->event_group_, WIFI_CONNECTED_BIT); } } void WifiConfigurationAp::StartSmartConfig() { // 注册SmartConfig事件处理器 ESP_ERROR_CHECK(esp_event_handler_instance_register(SC_EVENT, ESP_EVENT_ANY_ID, &WifiConfigurationAp::SmartConfigEventHandler, this, &sc_event_instance_)); // 初始化SmartConfig配置 smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT(); // cfg.esp_touch_v2_enable_crypt = true; // cfg.esp_touch_v2_key = "1234567890123456"; // 设置16字节加密密钥 // 启动SmartConfig服务 ESP_ERROR_CHECK(esp_smartconfig_start(&cfg)); ESP_LOGI(TAG, "SmartConfig started"); } void WifiConfigurationAp::SmartConfigEventHandler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { WifiConfigurationAp *self = static_cast(arg); if (event_base == SC_EVENT){ switch (event_id){ case SC_EVENT_SCAN_DONE: ESP_LOGI(TAG, "SmartConfig scan done"); break; case SC_EVENT_FOUND_CHANNEL: ESP_LOGI(TAG, "Found SmartConfig channel"); break; case SC_EVENT_GOT_SSID_PSWD:{ ESP_LOGI(TAG, "Got SmartConfig credentials"); smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data; char ssid[32], password[64]; memcpy(ssid, evt->ssid, sizeof(evt->ssid)); memcpy(password, evt->password, sizeof(evt->password)); ESP_LOGI(TAG, "SmartConfig SSID: %s, Password: %s", ssid, password); // 尝试连接WiFi会失败,故不连接 self->Save(ssid, password); xTaskCreate([](void *ctx){ ESP_LOGI(TAG, "Restarting in 3 second"); vTaskDelay(pdMS_TO_TICKS(3000)); esp_restart(); }, "restart_task", 4096, NULL, 5, NULL); break; } case SC_EVENT_SEND_ACK_DONE: ESP_LOGI(TAG, "SmartConfig ACK sent"); esp_smartconfig_stop(); break; } } } void WifiConfigurationAp::Stop() { // 停止SmartConfig服务 if (sc_event_instance_) { esp_event_handler_instance_unregister(SC_EVENT, ESP_EVENT_ANY_ID, sc_event_instance_); sc_event_instance_ = nullptr; } esp_smartconfig_stop(); // 停止定时器 if (scan_timer_) { esp_timer_stop(scan_timer_); esp_timer_delete(scan_timer_); scan_timer_ = nullptr; } // 停止Web服务器 if (server_) { httpd_stop(server_); server_ = nullptr; } // 停止DNS服务器 dns_server_.Stop(); // 注销事件处理器 if (instance_any_id_) { esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id_); instance_any_id_ = nullptr; } if (instance_got_ip_) { esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip_); instance_got_ip_ = nullptr; } // 停止WiFi并重置模式 esp_wifi_stop(); esp_wifi_deinit(); esp_wifi_set_mode(WIFI_MODE_NULL); // 释放网络接口资源 if (ap_netif_) { esp_netif_destroy(ap_netif_); ap_netif_ = nullptr; } ESP_LOGI(TAG, "Wifi configuration AP stopped"); }