#include "wifi_service.h" #include #include #include namespace roro { int WifiService::rssi() const { return controller_.state() == WifiController::State::Connected ? WiFi.RSSI() : 0; } void WifiService::startScan(int channel) { if (!radioInitialised_) { WiFi.mode(WIFI_STA); WiFi.setAutoReconnect(false); // the controller decides when to reconnect esp_wifi_set_country_code("EU", true); radioInitialised_ = true; } if (scanRunning_) return; // Hidden networks are listed too (with an empty name); the controller skips them. if (WiFi.scanNetworks(true /* async */, true /* hidden */, false, channel ? 120 : 300, channel) == WIFI_SCAN_FAILED) return; scanRunning_ = true; } void WifiService::startListScan(int channel) { if (scanRunning_) { listScanPending_ = true; // results of the scan already running will do return; } listScanPending_ = true; startScan(channel); } void WifiService::endListScans() { listScanPending_ = false; if (controller_.state() == WifiController::State::Off && radioInitialised_ && !scanRunning_) { WiFi.mode(WIFI_OFF); radioInitialised_ = false; } } namespace { const char* security(wifi_auth_mode_t auth) { switch (auth) { case WIFI_AUTH_OPEN: return "open"; case WIFI_AUTH_WEP: return "WEP"; case WIFI_AUTH_WPA_PSK: return "WPA"; case WIFI_AUTH_WPA2_PSK: case WIFI_AUTH_WPA_WPA2_PSK: return "WPA2"; case WIFI_AUTH_WPA2_ENTERPRISE: return "WPA2-E"; case WIFI_AUTH_WPA3_PSK: case WIFI_AUTH_WPA2_WPA3_PSK: return "WPA3"; default: return "?"; } } } // namespace void WifiService::finishScan(uint32_t nowMs) { int n = WiFi.scanComplete(); if (n == WIFI_SCAN_RUNNING) return; scanRunning_ = false; std::vector results; std::vector entries; for (int i = 0; i < n; i++) { std::string ssid = WiFi.SSID(i).c_str(); wifi_auth_mode_t auth = WiFi.encryptionType(i); entries.push_back({ssid, WiFi.BSSIDstr(i).c_str(), WiFi.channel(i), WiFi.RSSI(i), auth == WIFI_AUTH_OPEN, security(auth)}); if (!ssid.empty()) results.push_back({ssid, WiFi.RSSI(i)}); } WiFi.scanDelete(); if (listScanPending_) { listScan_ = entries; listScanPending_ = false; listScanSeq_++; } apply(controller_.scanDone(results, nowMs)); } void WifiService::startNtp() { // Not configTime(): it would overwrite the POSIX TZ the Clock applies. if (!esp_sntp_enabled()) { esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL); esp_sntp_setservername(0, "pool.ntp.org"); esp_sntp_init(); } else { esp_sntp_restart(); } ntpWaiting_ = true; } void WifiService::apply(const WifiController::Step& step) { using Action = WifiController::Action; switch (step.action) { case Action::None: break; case Action::StartScan: startScan(); break; case Action::Connect: WiFi.begin(step.ssid.c_str(), step.password.empty() ? nullptr : step.password.c_str()); break; case Action::Disconnect: WiFi.disconnect(); break; case Action::RadioOff: WiFi.disconnect(true); WiFi.mode(WIFI_OFF); radioInitialised_ = scanRunning_ = false; break; case Action::StartMonitor: startScan(); // makes sure the radio is up WiFi.disconnect(); break; case Action::StopMonitorAndScan: esp_wifi_set_promiscuous(false); startScan(); break; case Action::StopMonitorAndOff: esp_wifi_set_promiscuous(false); WiFi.mode(WIFI_OFF); radioInitialised_ = scanRunning_ = false; break; } } void WifiService::tick(uint32_t nowMs) { using State = WifiController::State; if (scanRunning_) finishScan(nowMs); wl_status_t status = WiFi.status(); if (controller_.state() == State::Connecting) { if (status == WL_CONNECTED) { apply(controller_.connected(nowMs)); startNtp(); } else if (status == WL_CONNECT_FAILED || status == WL_NO_SSID_AVAIL) { apply(controller_.disconnected(nowMs)); } } else if (controller_.state() == State::Connected && status != WL_CONNECTED) { apply(controller_.disconnected(nowMs)); } apply(controller_.update(nowMs, settings_.getBool(Setting::WifiEnabled))); if (ntpWaiting_ && sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) { ntpWaiting_ = false; clock_.set(static_cast(time(nullptr)), TimeSource::Ntp); } } } // namespace roro