/** * ESP32 SvelteKit * * A simple, secure and extensible framework for IoT projects for ESP32 platforms * with responsive Sveltekit front-end built with TailwindCSS and DaisyUI. * https://github.com/theelims/ESP32-sveltekit * * Copyright (C) 2018 - 2023 rjwats * Copyright (C) 2023 theelims * * All Rights Reserved. This software may be modified and distributed under * the terms of the LGPL v3 license. See the LICENSE file for details. **/ #include WiFiSettingsService::WiFiSettingsService(PsychicHttpServer *server, FS *fs, SecurityManager *securityManager, EventSocket *socket) : _server(server), _securityManager(securityManager), _httpEndpoint(WiFiSettings::read, WiFiSettings::update, this, server, WIFI_SETTINGS_SERVICE_PATH, securityManager, AuthenticationPredicates::IS_ADMIN), _eventEndpoint(WiFiSettings::read, WiFiSettings::update, this, socket, EVENT_WIFI_SETTINGS), _fsPersistence(WiFiSettings::read, WiFiSettings::update, this, fs, WIFI_SETTINGS_FILE), _lastConnectionAttempt(0), _socket(socket) { addUpdateHandler([&](const String &originId) { reconfigureWiFiConnection(); }, false); } void WiFiSettingsService::initWiFi() { WiFi.mode(WIFI_MODE_STA); // this is the default. // Disable WiFi config persistance and auto reconnect WiFi.persistent(false); WiFi.setAutoReconnect(false); WiFi.onEvent( std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED); WiFi.onEvent(std::bind(&WiFiSettingsService::onStationModeStop, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_STOP); _fsPersistence.readFromFS(); reconfigureWiFiConnection(); } void WiFiSettingsService::begin() { _httpEndpoint.begin(); _eventEndpoint.begin(); } void WiFiSettingsService::reconfigureWiFiConnection() { // reset last connection attempt to force loop to reconnect immediately _lastConnectionAttempt = 0; // disconnect and de-configure wifi if (WiFi.disconnect(true)) { _stopping = true; } } void WiFiSettingsService::loop() { EXECUTE_EVERY_N_MS(WIFI_RECONNECTION_DELAY, manageSTA()); EXECUTE_EVERY_N_MS(RSSI_EVENT_DELAY, updateRSSI()); } String WiFiSettingsService::getHostname() { return _state.hostname; } void WiFiSettingsService::manageSTA() { // Abort if already connected, or if we have no SSID if (WiFi.isConnected() || _state.wifiSettings.empty()) { return; } // Connect or reconnect as required if ((WiFi.getMode() & WIFI_STA) == 0) { connectToWiFi(); } } void WiFiSettingsService::connectToWiFi() { // reset availability flag for all stored networks for (auto &network : _state.wifiSettings) { network.available = false; } // scanning for available networks int scanResult = WiFi.scanNetworks(); if (scanResult == WIFI_SCAN_FAILED) { ESP_LOGE("WiFiSettingsService", "WiFi scan failed."); } else if (scanResult == 0) { ESP_LOGI("WiFiSettingsService", "No networks found."); } else { ESP_LOGI("WiFiSettingsService", "%d networks found.", scanResult); // find the best network to connect wifi_settings_t *bestNetwork = NULL; int bestNetworkDb = FACTORY_WIFI_RSSI_THRESHOLD; for (int i = 0; i < scanResult; ++i) { String ssid_scan; int32_t rssi_scan; uint8_t sec_scan; uint8_t *BSSID_scan; int32_t chan_scan; WiFi.getNetworkInfo(i, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan); ESP_LOGV("WiFiSettingsService", "SSID: %s, RSSI: %d dbm", ssid_scan.c_str(), rssi_scan); for (auto &network : _state.wifiSettings) { if (ssid_scan == network.ssid) { // SSID match if (rssi_scan > bestNetworkDb) { // best network bestNetworkDb = rssi_scan; bestNetwork = &network; network.available = true; } else if (rssi_scan >= FACTORY_WIFI_RSSI_THRESHOLD) { // available network network.available = true; } } break; } } // if configured to prioritize signal strength, use the best network else use the first available network if (_state.priorityBySignalStrength == false) { for (auto &network : _state.wifiSettings) { if (network.available == true) { ESP_LOGI("WiFiSettingsService", "Connecting to first available network: %s", network.ssid.c_str()); configureNetwork(network); break; } } } else if (_state.priorityBySignalStrength == true && bestNetwork) { ESP_LOGI("WiFiSettingsService", "Connecting to strongest network: %s", bestNetwork->ssid.c_str()); configureNetwork(*bestNetwork); WiFi.begin(bestNetwork->ssid.c_str(), bestNetwork->password.c_str()); } else // no suitable network to connect { ESP_LOGI("WiFiSettingsService", "No known networks found."); } // delete scan results WiFi.scanDelete(); } } void WiFiSettingsService::configureNetwork(wifi_settings_t &network) { if (network.staticIPConfig) { // configure for static IP WiFi.config(network.localIP, network.gatewayIP, network.subnetMask, network.dnsIP1, network.dnsIP2); } else { // configure for DHCP WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); } WiFi.setHostname(_state.hostname.c_str()); // attempt to connect to the network WiFi.begin(network.ssid.c_str(), network.password.c_str()); #if CONFIG_IDF_TARGET_ESP32C3 WiFi.setTxPower(WIFI_POWER_8_5dBm); // https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi #endif } void WiFiSettingsService::updateRSSI() { char buffer[4]; snprintf(buffer, sizeof(buffer), "%d", WiFi.RSSI()); _socket->emit(EVENT_RSSI, buffer); } void WiFiSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) { WiFi.disconnect(true); } void WiFiSettingsService::onStationModeStop(WiFiEvent_t event, WiFiEventInfo_t info) { if (_stopping) { _lastConnectionAttempt = 0; _stopping = false; } }