FluxDC/managed_components/78__esp-wifi-connect/wifi_station.cc
2025-03-14 13:42:15 +08:00

250 lines
8.5 KiB
C++

#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;
}
// Reset the WiFi stack
ESP_ERROR_CHECK(esp_wifi_stop());
ESP_ERROR_CHECK(esp_wifi_deinit());
// 取消注册事件处理程序
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;
}
}
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::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_));
}
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()) {
ESP_LOGI(TAG, "Found 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);
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_ERROR_CHECK(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_timer_start_once(this_->timer_handle_, 10 * 1000);
} 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;
}