326 lines
9.6 KiB
C++
326 lines
9.6 KiB
C++
#include "Wifi.hpp"
|
|
#include "sdkconfig.h"
|
|
#include "esp_log.h"
|
|
#include "esp_netif.h"
|
|
#include "nvs_flash.h"
|
|
#include "nvs.h"
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
|
|
static const char* TAG = "Wifi";
|
|
|
|
Wifi& Wifi::instance() {
|
|
static Wifi instance;
|
|
return instance;
|
|
}
|
|
|
|
void Wifi::eventHandler(void* arg, esp_event_base_t event_base,
|
|
int32_t event_id, void* event_data) {
|
|
Wifi& wifi = Wifi::instance();
|
|
|
|
if (event_base == WIFI_EVENT) {
|
|
switch (event_id) {
|
|
case WIFI_EVENT_STA_START:
|
|
ESP_LOGI(TAG, "WiFi station started");
|
|
break;
|
|
case WIFI_EVENT_STA_DISCONNECTED:
|
|
ESP_LOGI(TAG, "WiFi disconnected");
|
|
wifi.connected_ = false;
|
|
wifi.currentSSID_ = "";
|
|
if (wifi.statusCallback_) {
|
|
wifi.statusCallback_(false);
|
|
}
|
|
xEventGroupSetBits(wifi.wifiEventGroup_, WIFI_FAIL_BIT);
|
|
break;
|
|
case WIFI_EVENT_SCAN_DONE:
|
|
ESP_LOGI(TAG, "WiFi scan done");
|
|
xEventGroupSetBits(wifi.wifiEventGroup_, WIFI_SCAN_DONE_BIT);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (event_base == IP_EVENT) {
|
|
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));
|
|
wifi.connected_ = true;
|
|
if (wifi.statusCallback_) {
|
|
wifi.statusCallback_(true);
|
|
}
|
|
xEventGroupSetBits(wifi.wifiEventGroup_, WIFI_CONNECTED_BIT);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Wifi::init() {
|
|
if (initialized_) {
|
|
return;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Initializing WiFi");
|
|
|
|
wifiEventGroup_ = xEventGroupCreate();
|
|
|
|
ESP_ERROR_CHECK(esp_netif_init());
|
|
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
|
esp_netif_create_default_wifi_sta();
|
|
|
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
|
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
|
|
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
|
ESP_EVENT_ANY_ID,
|
|
&eventHandler,
|
|
nullptr,
|
|
nullptr));
|
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
|
IP_EVENT_STA_GOT_IP,
|
|
&eventHandler,
|
|
nullptr,
|
|
nullptr));
|
|
|
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
|
ESP_ERROR_CHECK(esp_wifi_start());
|
|
|
|
initialized_ = true;
|
|
|
|
auto savedNetworks = getSavedNetworks();
|
|
if (!savedNetworks.empty()) {
|
|
std::string password;
|
|
if (getSavedPassword(savedNetworks[0].c_str(), password)) {
|
|
ESP_LOGI(TAG, "Auto-connecting to saved network: %s", savedNetworks[0].c_str());
|
|
connect(savedNetworks[0].c_str(), password.c_str());
|
|
}
|
|
}
|
|
|
|
ESP_LOGI(TAG, "WiFi initialized");
|
|
}
|
|
|
|
void Wifi::scan(std::function<void(std::vector<wifi_ap_record_t>&)> callback) {
|
|
scanCallback_ = callback;
|
|
|
|
xEventGroupClearBits(wifiEventGroup_, WIFI_SCAN_DONE_BIT);
|
|
|
|
wifi_scan_config_t scan_config = {};
|
|
scan_config.ssid = nullptr;
|
|
scan_config.bssid = nullptr;
|
|
scan_config.channel = 0;
|
|
scan_config.show_hidden = false;
|
|
scan_config.scan_type = WIFI_SCAN_TYPE_ACTIVE;
|
|
scan_config.scan_time.active.min = 100;
|
|
scan_config.scan_time.active.max = 300;
|
|
|
|
ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, false));
|
|
|
|
xTaskCreate([](void* arg) {
|
|
Wifi& wifi = Wifi::instance();
|
|
|
|
xEventGroupWaitBits(wifi.wifiEventGroup_, WIFI_SCAN_DONE_BIT,
|
|
pdTRUE, pdFALSE, pdMS_TO_TICKS(10000));
|
|
|
|
uint16_t ap_count = 0;
|
|
esp_wifi_scan_get_ap_num(&ap_count);
|
|
|
|
std::vector<wifi_ap_record_t> ap_records(ap_count);
|
|
if (ap_count > 0) {
|
|
esp_wifi_scan_get_ap_records(&ap_count, ap_records.data());
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Found %d access points", ap_count);
|
|
|
|
if (wifi.scanCallback_) {
|
|
wifi.scanCallback_(ap_records);
|
|
}
|
|
|
|
vTaskDelete(nullptr);
|
|
}, "wifi_scan", 4096, nullptr, 5, nullptr);
|
|
}
|
|
|
|
void Wifi::connect(const char* ssid, const char* password) {
|
|
ESP_LOGI(TAG, "Connecting to %s", ssid);
|
|
|
|
xEventGroupClearBits(wifiEventGroup_, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT);
|
|
|
|
wifi_config_t wifi_config = {};
|
|
strncpy((char*)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid) - 1);
|
|
strncpy((char*)wifi_config.sta.password, password, sizeof(wifi_config.sta.password) - 1);
|
|
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
|
|
wifi_config.sta.pmf_cfg.capable = true;
|
|
wifi_config.sta.pmf_cfg.required = false;
|
|
|
|
currentSSID_ = ssid;
|
|
|
|
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
|
ESP_ERROR_CHECK(esp_wifi_connect());
|
|
}
|
|
|
|
void Wifi::disconnect() {
|
|
ESP_LOGI(TAG, "Disconnecting");
|
|
esp_wifi_disconnect();
|
|
connected_ = false;
|
|
currentSSID_ = "";
|
|
}
|
|
|
|
bool Wifi::isConnected() {
|
|
return connected_;
|
|
}
|
|
|
|
std::string Wifi::getCurrentSSID() {
|
|
return currentSSID_;
|
|
}
|
|
|
|
void Wifi::setStatusCallback(std::function<void(bool connected)> callback) {
|
|
statusCallback_ = callback;
|
|
}
|
|
|
|
void Wifi::saveNetwork(const char* ssid, const char* password) {
|
|
nvs_handle_t handle;
|
|
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "Error opening NVS: %s", esp_err_to_name(err));
|
|
return;
|
|
}
|
|
|
|
char key[32];
|
|
snprintf(key, sizeof(key), "pw_%s", ssid);
|
|
|
|
size_t key_len = strlen(key);
|
|
if (key_len > 15) {
|
|
key[15] = '\0';
|
|
}
|
|
|
|
err = nvs_set_str(handle, key, password);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "Error saving password: %s", esp_err_to_name(err));
|
|
}
|
|
|
|
auto networks = getSavedNetworks();
|
|
bool found = false;
|
|
for (const auto& net : networks) {
|
|
if (net == ssid) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
networks.insert(networks.begin(), ssid);
|
|
if (networks.size() > MAX_SAVED_NETWORKS) {
|
|
networks.pop_back();
|
|
}
|
|
|
|
std::string networkList;
|
|
for (size_t i = 0; i < networks.size(); i++) {
|
|
if (i > 0) networkList += ";";
|
|
networkList += networks[i];
|
|
}
|
|
nvs_set_str(handle, "networks", networkList.c_str());
|
|
}
|
|
|
|
nvs_commit(handle);
|
|
nvs_close(handle);
|
|
|
|
ESP_LOGI(TAG, "Saved network: %s", ssid);
|
|
}
|
|
|
|
void Wifi::removeNetwork(const char* ssid) {
|
|
nvs_handle_t handle;
|
|
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "Error opening NVS: %s", esp_err_to_name(err));
|
|
return;
|
|
}
|
|
|
|
char key[32];
|
|
snprintf(key, sizeof(key), "pw_%s", ssid);
|
|
size_t key_len = strlen(key);
|
|
if (key_len > 15) {
|
|
key[15] = '\0';
|
|
}
|
|
nvs_erase_key(handle, key);
|
|
|
|
auto networks = getSavedNetworks();
|
|
std::string ssidStr(ssid);
|
|
networks.erase(std::remove(networks.begin(), networks.end(), ssidStr), networks.end());
|
|
|
|
std::string networkList;
|
|
for (size_t i = 0; i < networks.size(); i++) {
|
|
if (i > 0) networkList += ";";
|
|
networkList += networks[i];
|
|
}
|
|
nvs_set_str(handle, "networks", networkList.c_str());
|
|
|
|
nvs_commit(handle);
|
|
nvs_close(handle);
|
|
|
|
ESP_LOGI(TAG, "Removed network: %s", ssid);
|
|
}
|
|
|
|
std::vector<std::string> Wifi::getSavedNetworks() {
|
|
std::vector<std::string> networks;
|
|
|
|
nvs_handle_t handle;
|
|
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle);
|
|
if (err != ESP_OK) {
|
|
return networks;
|
|
}
|
|
|
|
size_t required_size = 0;
|
|
err = nvs_get_str(handle, "networks", nullptr, &required_size);
|
|
if (err == ESP_OK && required_size > 1) {
|
|
char* buf = new char[required_size];
|
|
nvs_get_str(handle, "networks", buf, &required_size);
|
|
std::string networkList(buf);
|
|
delete[] buf;
|
|
|
|
if (!networkList.empty()) {
|
|
size_t start = 0;
|
|
size_t end = networkList.find(';');
|
|
while (end != std::string::npos) {
|
|
std::string net = networkList.substr(start, end - start);
|
|
if (!net.empty()) {
|
|
networks.push_back(net);
|
|
}
|
|
start = end + 1;
|
|
end = networkList.find(';', start);
|
|
}
|
|
if (start < networkList.size()) {
|
|
std::string net = networkList.substr(start);
|
|
if (!net.empty()) {
|
|
networks.push_back(net);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nvs_close(handle);
|
|
return networks;
|
|
}
|
|
|
|
bool Wifi::getSavedPassword(const char* ssid, std::string& password) {
|
|
nvs_handle_t handle;
|
|
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle);
|
|
if (err != ESP_OK) {
|
|
return false;
|
|
}
|
|
|
|
char key[32];
|
|
snprintf(key, sizeof(key), "pw_%s", ssid);
|
|
size_t key_len = strlen(key);
|
|
if (key_len > 15) {
|
|
key[15] = '\0';
|
|
}
|
|
|
|
size_t required_size = 0;
|
|
err = nvs_get_str(handle, key, nullptr, &required_size);
|
|
if (err == ESP_OK && required_size > 0) {
|
|
password.resize(required_size);
|
|
nvs_get_str(handle, key, &password[0], &required_size);
|
|
nvs_close(handle);
|
|
return true;
|
|
}
|
|
|
|
nvs_close(handle);
|
|
return false;
|
|
}
|