#include "wifi_station.h" #include #include #include #include #include #include #include #include "nvs_flash.h" #include #include #include "ssid_manager.h" #define TAG "wifi" #define WIFI_EVENT_CONNECTED BIT0 #define MAX_RECONNECT_COUNT 5 WifiStation& WifiStation::GetInstance() { static WifiStation instance; return instance; } WifiStation::WifiStation() { // Create the event group event_group_ = xEventGroupCreate(); } WifiStation::~WifiStation() { vEventGroupDelete(event_group_); } void WifiStation::AddAuth(const std::string &&ssid, const std::string &&password) { auto& ssid_manager = SsidManager::GetInstance(); ssid_manager.AddSsid(ssid, password); } void WifiStation::Stop() { if (timer_handle_ != nullptr) { esp_timer_stop(timer_handle_); esp_timer_delete(timer_handle_); timer_handle_ = nullptr; } if (reconnect_timer_handle_ != nullptr) { esp_timer_stop(reconnect_timer_handle_); esp_timer_delete(reconnect_timer_handle_); reconnect_timer_handle_ = nullptr; } // 取消注册事件处理程序 if (instance_any_id_ != nullptr) { ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id_)); instance_any_id_ = nullptr; } if (instance_got_ip_ != nullptr) { ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip_)); instance_got_ip_ = nullptr; } // Reset the WiFi stack ESP_ERROR_CHECK(esp_wifi_stop()); ESP_ERROR_CHECK(esp_wifi_deinit()); } void WifiStation::OnScanBegin(std::function on_scan_begin) { on_scan_begin_ = on_scan_begin; } void WifiStation::OnConnect(std::function on_connect) { on_connect_ = on_connect; } void WifiStation::OnConnected(std::function on_connected) { on_connected_ = on_connected; } void WifiStation::OnNoCandidates(std::function on_no_candidates) { on_no_candidates_ = on_no_candidates; } void WifiStation::OnReconnectTimeout(std::function on_reconnect_timeout) { on_reconnect_timeout_ = on_reconnect_timeout; } void WifiStation::Start() { // Initialize the TCP/IP stack ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &WifiStation::WifiEventHandler, this, &instance_any_id_)); ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &WifiStation::IpEventHandler, this, &instance_got_ip_)); // Create the default event loop esp_netif_create_default_wifi_sta(); // Initialize the WiFi stack in station mode wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); cfg.nvs_enable = false; ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_start()); // Setup the timer to scan WiFi esp_timer_create_args_t timer_args = { .callback = [](void* arg) { esp_wifi_scan_start(nullptr, false); }, .arg = this, .dispatch_method = ESP_TIMER_TASK, .name = "WiFiScanTimer", .skip_unhandled_events = true }; ESP_ERROR_CHECK(esp_timer_create(&timer_args, &timer_handle_)); esp_timer_create_args_t reconnect_timer_args = { .callback = [](void* arg) { auto* self = static_cast(arg); if (!self->IsConnected() && self->on_reconnect_timeout_) { self->on_reconnect_timeout_(); } }, .arg = this, .dispatch_method = ESP_TIMER_TASK, .name = "WiFiReconnectTimeout", .skip_unhandled_events = true }; ESP_ERROR_CHECK(esp_timer_create(&reconnect_timer_args, &reconnect_timer_handle_)); } bool WifiStation::WaitForConnected(int timeout_ms) { auto bits = xEventGroupWaitBits(event_group_, WIFI_EVENT_CONNECTED, pdFALSE, pdFALSE, timeout_ms / portTICK_PERIOD_MS); return (bits & WIFI_EVENT_CONNECTED) != 0; } void WifiStation::HandleScanResult() { uint16_t ap_num = 0; esp_wifi_scan_get_ap_num(&ap_num); wifi_ap_record_t *ap_records = (wifi_ap_record_t *)malloc(ap_num * sizeof(wifi_ap_record_t)); esp_wifi_scan_get_ap_records(&ap_num, ap_records); // sort by rssi descending std::sort(ap_records, ap_records + ap_num, [](const wifi_ap_record_t& a, const wifi_ap_record_t& b) { return a.rssi > b.rssi; }); auto& ssid_manager = SsidManager::GetInstance(); auto ssid_list = ssid_manager.GetSsidList(); for (int i = 0; i < ap_num; i++) { auto ap_record = ap_records[i]; auto it = std::find_if(ssid_list.begin(), ssid_list.end(), [ap_record](const SsidItem& item) { return strcmp((char *)ap_record.ssid, item.ssid.c_str()) == 0; }); if (it != ssid_list.end()) { // 新增解决 二网合一代码,待测试 // ESP32-S3 only supports 2.4GHz WiFi (channels 1-14) // Filter out 5GHz APs to avoid connection failures on dual-band routers if (ap_record.primary < 1 || ap_record.primary > 14) { ESP_LOGW(TAG, "Skipping 5GHz AP: %s, Channel: %d (ESP32-S3 only supports 2.4GHz)", (char *)ap_record.ssid, ap_record.primary); continue; } ESP_LOGI(TAG, "发现可连接 AP: %s, BSSID: %02x:%02x:%02x:%02x:%02x:%02x, RSSI: %d, Channel: %d, Authmode: %d", (char *)ap_record.ssid, ap_record.bssid[0], ap_record.bssid[1], ap_record.bssid[2], ap_record.bssid[3], ap_record.bssid[4], ap_record.bssid[5], ap_record.rssi, ap_record.primary, ap_record.authmode); WifiApRecord record = { .ssid = it->ssid, .password = it->password, .channel = ap_record.primary, .authmode = ap_record.authmode }; memcpy(record.bssid, ap_record.bssid, 6); connect_queue_.push_back(record); } } free(ap_records); if (connect_queue_.empty()) { ESP_LOGI(TAG, "Wait for next scan"); esp_timer_start_once(timer_handle_, 10 * 1000); if (on_no_candidates_) { on_no_candidates_(); } return; } StartConnect(); } void WifiStation::StartConnect() { auto ap_record = connect_queue_.front(); connect_queue_.erase(connect_queue_.begin()); ssid_ = ap_record.ssid; password_ = ap_record.password; if (on_connect_) { on_connect_(ssid_); } wifi_config_t wifi_config; bzero(&wifi_config, sizeof(wifi_config)); strcpy((char *)wifi_config.sta.ssid, ap_record.ssid.c_str()); strcpy((char *)wifi_config.sta.password, ap_record.password.c_str()); wifi_config.sta.channel = ap_record.channel; memcpy(wifi_config.sta.bssid, ap_record.bssid, 6); wifi_config.sta.bssid_set = true; ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); reconnect_count_ = 0; ESP_ERROR_CHECK(esp_wifi_connect()); } int8_t WifiStation::GetRssi() { // Get station info wifi_ap_record_t ap_info; ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info)); return ap_info.rssi; } uint8_t WifiStation::GetChannel() { // Get station info wifi_ap_record_t ap_info; ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info)); return ap_info.primary; } bool WifiStation::IsConnected() { return xEventGroupGetBits(event_group_) & WIFI_EVENT_CONNECTED; } void WifiStation::SetPowerSaveMode(bool enabled) { ESP_ERROR_CHECK(esp_wifi_set_ps(enabled ? WIFI_PS_MIN_MODEM : WIFI_PS_NONE)); } // Static event handler functions void WifiStation::WifiEventHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { auto* this_ = static_cast(arg); if (event_id == WIFI_EVENT_STA_START) { esp_wifi_scan_start(nullptr, false); if (this_->on_scan_begin_) { this_->on_scan_begin_(); } } else if (event_id == WIFI_EVENT_SCAN_DONE) { this_->HandleScanResult(); } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) { xEventGroupClearBits(this_->event_group_, WIFI_EVENT_CONNECTED); if (this_->reconnect_count_ < MAX_RECONNECT_COUNT) { esp_wifi_connect(); this_->reconnect_count_++; ESP_LOGI(TAG, "Reconnecting %s (attempt %d / %d)", this_->ssid_.c_str(), this_->reconnect_count_, MAX_RECONNECT_COUNT); return; } if (!this_->connect_queue_.empty()) { this_->StartConnect(); return; } ESP_LOGI(TAG, "No more AP to connect, wait for next scan"); ESP_LOGW(TAG, "网络断开后已重试%d次仍无可连接的Wi-Fi,等待下次扫描", this_->reconnect_count_); esp_timer_start_once(this_->timer_handle_, 10 * 1000); if (this_->reconnect_timer_handle_) { esp_timer_start_once(this_->reconnect_timer_handle_, 10 * 1000000); } } else if (event_id == WIFI_EVENT_STA_CONNECTED) { } } void WifiStation::IpEventHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { auto* this_ = static_cast(arg); auto* event = static_cast(event_data); char ip_address[16]; esp_ip4addr_ntoa(&event->ip_info.ip, ip_address, sizeof(ip_address)); this_->ip_address_ = ip_address; ESP_LOGI(TAG, "Got IP: %s", this_->ip_address_.c_str()); xEventGroupSetBits(this_->event_group_, WIFI_EVENT_CONNECTED); if (this_->on_connected_) { this_->on_connected_(this_->ssid_); } this_->connect_queue_.clear(); this_->reconnect_count_ = 0; }