293 lines
10 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "wifi_station.h"
#include <cstring>
#include <algorithm>
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <esp_log.h>
#include <esp_wifi.h>
#include <nvs.h>
#include "nvs_flash.h"
#include <esp_netif.h>
#include <esp_system.h>
#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<void()> on_scan_begin) {
on_scan_begin_ = on_scan_begin;
}
void WifiStation::OnConnect(std::function<void(const std::string& ssid)> on_connect) {
on_connect_ = on_connect;
}
void WifiStation::OnConnected(std::function<void(const std::string& ssid)> on_connected) {
on_connected_ = on_connected;
}
void WifiStation::OnNoCandidates(std::function<void()> on_no_candidates) {
on_no_candidates_ = on_no_candidates;
}
void WifiStation::OnReconnectTimeout(std::function<void()> 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<WifiStation*>(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<WifiStation*>(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<WifiStation*>(arg);
auto* event = static_cast<ip_event_got_ip_t*>(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;
}