knxdisplay/main/Wifi.cpp
2026-01-19 11:14:35 +01:00

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;
}