From 9a46bfceaa352e4e0418833d166b3e647705cd08 Mon Sep 17 00:00:00 2001 From: hhvrc Date: Mon, 2 Oct 2023 04:10:59 +0200 Subject: [PATCH 01/22] Implement new scan message --- WebUI/src/lib/WebSocketClient.ts | 22 ++-------- WebUI/src/lib/WebSocketMessageHandler.ts | 52 ++++++++++++++++++++++++ src/WiFiManager.cpp | 8 ++-- 3 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 WebUI/src/lib/WebSocketMessageHandler.ts diff --git a/WebUI/src/lib/WebSocketClient.ts b/WebUI/src/lib/WebSocketClient.ts index b997a620..61ab8113 100644 --- a/WebUI/src/lib/WebSocketClient.ts +++ b/WebUI/src/lib/WebSocketClient.ts @@ -1,7 +1,5 @@ import { browser } from "$app/environment"; -import { getToastStore } from "@skeletonlabs/skeleton"; -import { WiFiStateStore } from "./stores"; -import type { WiFiNetwork } from "./types/WiFiNetwork"; +import { WebSocketMessageHandler } from "./WebSocketMessageHandler"; export enum ConnectionState { DISCONNECTED = 0, @@ -134,22 +132,8 @@ export class WebSocketClient { return; } - if (message.networks !== undefined) { - WiFiStateStore.setNetworks(message.networks as WiFiNetwork[]); - return; - } - - if (message.scanning !== undefined) { - const toastStore = getToastStore(); - if (message.scanning) { - toastStore.trigger({ message: 'Scanning for WiFi networks...', background: 'bg-blue-500' }); - } else { - toastStore.trigger({ message: 'Scanning for WiFi networks finished', background: 'bg-green-500' }); - } - - WiFiStateStore.setScanning(message.scanning as boolean); - return; - } + // Handle message + WebSocketMessageHandler(message); } private AbortWebSocket() { if (this._socket) { diff --git a/WebUI/src/lib/WebSocketMessageHandler.ts b/WebUI/src/lib/WebSocketMessageHandler.ts new file mode 100644 index 00000000..1f710e1f --- /dev/null +++ b/WebUI/src/lib/WebSocketMessageHandler.ts @@ -0,0 +1,52 @@ +import { getToastStore } from "@skeletonlabs/skeleton"; +import { WiFiStateStore } from "./stores"; +import type { WiFiNetwork } from "./types/WiFiNetwork"; + +interface ScanMessage { + type: 'scan'; + status: 'started' | 'finished'; + networks?: WiFiNetwork[]; +} + +interface InvalidMessage { + type: undefined | null; +} + +export type WebSocketMessage = InvalidMessage | ScanMessage; + +export function WebSocketMessageHandler(message: WebSocketMessage) { + const type = message.type; + if (!type) { + console.warn('[WS] Received invalid message: ', message); + return; + } + + switch (type) { + case 'scan': + handleScanMessage(message); + break; + default: + console.warn('[WS] Received invalid message: ', message); + return; + } +} + +function handleScanMessage(message: ScanMessage) { + const toastStore = getToastStore(); + switch (message.status) { + case 'started': + toastStore.trigger({ message: 'Scanning for WiFi networks...', background: 'bg-blue-500' }); + break; + case 'finished': + toastStore.trigger({ message: 'Scanning for WiFi networks finished', background: 'bg-green-500' }); + break; + default: + console.warn('[WS] Received invalid scan message: ', message); + return; + } + + WiFiStateStore.setScanning(message.status === 'started'); + if (message.networks) { + WiFiStateStore.setNetworks(message.networks); + } +} diff --git a/src/WiFiManager.cpp b/src/WiFiManager.cpp index a08b9691..254afb43 100644 --- a/src/WiFiManager.cpp +++ b/src/WiFiManager.cpp @@ -55,7 +55,7 @@ bool SaveCredentials() { bool ReadCredentials() { File file = LittleFS.open("/networks", FILE_READ); if (!file) { - ESP_LOGE(TAG, "Failed to open networks file for reading"); + ESP_LOGW(TAG, "Failed to open networks file for reading"); return false; } @@ -63,7 +63,7 @@ bool ReadCredentials() { if (deserializeJson(doc, file) != DeserializationError::Ok) { file.close(); - ESP_LOGE(TAG, "Failed to deserialize networks file, overwriting"); + ESP_LOGW(TAG, "Failed to deserialize networks file, overwriting"); SaveCredentials(); return false; @@ -102,6 +102,8 @@ void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info) { } DynamicJsonDocument doc(64 + numNetworks * 128); + doc["type"] = "scan"; + doc["status"] = "finished"; JsonArray networks = doc.createNestedArray("networks"); if (numNetworks == 0) { @@ -219,7 +221,7 @@ void WiFiManager::RemoveNetwork(const char* ssid) { bool WiFiManager::StartScan() { if (s_wifiState != WiFiState::Disconnected) return false; - CaptivePortal::BroadcastMessageTXT("{\"scanning\":true}"); + CaptivePortal::BroadcastMessageTXT("{\"type\":\"scan\",\"status\":\"started\"}"); WiFi.scanNetworks(true); SetWiFiState(WiFiState::Scanning); From 6c427977a223871fe68b3cdd9e590db6f513fdcc Mon Sep 17 00:00:00 2001 From: hhvrc Date: Tue, 3 Oct 2023 04:06:23 +0200 Subject: [PATCH 02/22] Implement WiFi selection and connection logic and communication --- WebUI/src/lib/WebSocketMessageHandler.ts | 48 ++-- WebUI/src/lib/components/WiFiList.svelte | 68 +++-- .../src/lib/components/modals/WiFiInfo.svelte | 26 +- WebUI/src/lib/types/WiFiNetwork.ts | 3 +- include/Mappers/EspWiFiTypesMapper.h | 12 + include/WiFiCredentials.h | 56 ++++ include/WiFiManager.h | 12 +- src/CaptivePortal.cpp | 69 ++++- src/Mappers/EspWiFiTypesMapper.cpp | 28 ++ src/WiFiCredentials.cpp | 189 ++++++++++++++ src/WiFiManager.cpp | 241 ++++++++++-------- 11 files changed, 579 insertions(+), 173 deletions(-) create mode 100644 include/Mappers/EspWiFiTypesMapper.h create mode 100644 include/WiFiCredentials.h create mode 100644 src/Mappers/EspWiFiTypesMapper.cpp create mode 100644 src/WiFiCredentials.cpp diff --git a/WebUI/src/lib/WebSocketMessageHandler.ts b/WebUI/src/lib/WebSocketMessageHandler.ts index 1f710e1f..0db6dfa1 100644 --- a/WebUI/src/lib/WebSocketMessageHandler.ts +++ b/WebUI/src/lib/WebSocketMessageHandler.ts @@ -1,10 +1,14 @@ -import { getToastStore } from "@skeletonlabs/skeleton"; -import { WiFiStateStore } from "./stores"; -import type { WiFiNetwork } from "./types/WiFiNetwork"; +import { WiFiStateStore } from './stores'; +import type { WiFiNetwork } from './types/WiFiNetwork'; -interface ScanMessage { - type: 'scan'; - status: 'started' | 'finished'; +interface WiFiMessage { + type: 'wifi'; + subject: 'scan'; +} + +interface WiFiScanMessage extends WiFiMessage { + subject: 'scan'; + status: 'started' | 'completed' | 'error'; networks?: WiFiNetwork[]; } @@ -12,7 +16,7 @@ interface InvalidMessage { type: undefined | null; } -export type WebSocketMessage = InvalidMessage | ScanMessage; +export type WebSocketMessage = InvalidMessage | WiFiScanMessage; export function WebSocketMessageHandler(message: WebSocketMessage) { const type = message.type; @@ -22,8 +26,8 @@ export function WebSocketMessageHandler(message: WebSocketMessage) { } switch (type) { - case 'scan': - handleScanMessage(message); + case 'wifi': + handleWiFiMessage(message); break; default: console.warn('[WS] Received invalid message: ', message); @@ -31,21 +35,35 @@ export function WebSocketMessageHandler(message: WebSocketMessage) { } } -function handleScanMessage(message: ScanMessage) { - const toastStore = getToastStore(); +function handleWiFiMessage(message: WiFiScanMessage) { + switch (message.subject) { + case 'scan': + handleWiFiScanMessage(message); + break; + default: + console.warn('[WS] Received invalid wifi message: ', message); + return; + } +} + +function handleWiFiScanMessage(message: WiFiScanMessage) { + let running: boolean; switch (message.status) { case 'started': - toastStore.trigger({ message: 'Scanning for WiFi networks...', background: 'bg-blue-500' }); + running = true; + break; + case 'completed': + running = false; break; - case 'finished': - toastStore.trigger({ message: 'Scanning for WiFi networks finished', background: 'bg-green-500' }); + case 'error': + running = false; break; default: console.warn('[WS] Received invalid scan message: ', message); return; } - WiFiStateStore.setScanning(message.status === 'started'); + WiFiStateStore.setScanning(running); if (message.networks) { WiFiStateStore.setNetworks(message.networks); } diff --git a/WebUI/src/lib/components/WiFiList.svelte b/WebUI/src/lib/components/WiFiList.svelte index 3b077abe..978382fa 100644 --- a/WebUI/src/lib/components/WiFiList.svelte +++ b/WebUI/src/lib/components/WiFiList.svelte @@ -1,40 +1,38 @@
@@ -32,7 +34,7 @@
{#if item.saved} - + {:else} diff --git a/WebUI/src/lib/types/WiFiNetwork.ts b/WebUI/src/lib/types/WiFiNetwork.ts index 68578859..33fb6d86 100644 --- a/WebUI/src/lib/types/WiFiNetwork.ts +++ b/WebUI/src/lib/types/WiFiNetwork.ts @@ -1,9 +1,8 @@ export type WiFiNetwork = { - index: number; ssid: string; bssid: string; rssi: number; channel: number; - secure: boolean; + security: 'Open' | 'WEP' | 'WPA PSK' | 'WPA2 PSK' | 'WPA/WPA2 PSK' | 'WPA2 Enterprise' | 'WPA3 PSK' | 'WPA2/WPA3 PSK' | 'WAPI PSK' | null; saved: boolean; }; diff --git a/include/Mappers/EspWiFiTypesMapper.h b/include/Mappers/EspWiFiTypesMapper.h new file mode 100644 index 00000000..1c4cb344 --- /dev/null +++ b/include/Mappers/EspWiFiTypesMapper.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace OpenShock::Mappers { + /* + * @brief Maps a WiFi auth mode to a human-readable string. + * @param authMode The auth mode to map. + * @return A human-readable string representing the auth mode, or nullptr if the auth mode is invalid. + */ + const char* GetWiFiAuthModeName(wifi_auth_mode_t authMode); +} diff --git a/include/WiFiCredentials.h b/include/WiFiCredentials.h new file mode 100644 index 00000000..8895fb94 --- /dev/null +++ b/include/WiFiCredentials.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +namespace fs { class File; } +class String; + +namespace OpenShock { + class WiFiCredentials { + WiFiCredentials() = default; + public: + static bool Load(std::vector& credentials); + + WiFiCredentials(std::uint8_t id, const wifi_ap_record_t* record); + WiFiCredentials(std::uint8_t id, const String& ssid, const String& password); + + constexpr std::uint8_t id() const noexcept { + return _id; + } + + constexpr nonstd::span ssid() const noexcept { + return nonstd::span(reinterpret_cast(_ssid), _ssidLength); + } + constexpr nonstd::span ssidBytes() const noexcept { + return nonstd::span(_ssid, _ssidLength); + } + void setSSID(const String& ssid); + void setSSID(const std::uint8_t* ssid, std::size_t ssidLength); + + constexpr nonstd::span password() const noexcept { + return nonstd::span(reinterpret_cast(_password), _passwordLength); + } + constexpr nonstd::span passwordBytes() const noexcept { + return nonstd::span(_password, _passwordLength); + } + void setPassword(const String& password); + void setPassword(const std::uint8_t* password, std::size_t passwordLength); + + bool save() const; + bool erase() const; + private: + bool _load(fs::File& file); + + std::size_t _ssidLength; + std::size_t _passwordLength; + std::uint8_t _password[64]; + std::uint8_t _ssid[33]; + std::uint8_t _id; + }; +} diff --git a/include/WiFiManager.h b/include/WiFiManager.h index 3225a3c3..a3333fe2 100644 --- a/include/WiFiManager.h +++ b/include/WiFiManager.h @@ -2,6 +2,10 @@ #include "WiFiState.h" +#include + +#include + #include namespace OpenShock::WiFiManager { @@ -9,9 +13,13 @@ namespace OpenShock::WiFiManager { WiFiState GetWiFiState(); - void AddOrUpdateNetwork(const char* ssid, const char* password); - void RemoveNetwork(const char* ssid); + bool Authenticate(nonstd::span bssid, const String& password); + void Forget(std::uint8_t wifiId); + + void Connect(std::uint8_t wifiId); + void Disconnect(); bool StartScan(); + void StopScan(); } // namespace OpenShock::WiFiManager diff --git a/src/CaptivePortal.cpp b/src/CaptivePortal.cpp index 776fff60..2defee6c 100644 --- a/src/CaptivePortal.cpp +++ b/src/CaptivePortal.cpp @@ -123,6 +123,69 @@ void handleWebSocketClientConnected(std::uint8_t socketId) { void handleWebSocketClientDisconnected(std::uint8_t socketId) { ESP_LOGD(TAG, "WebSocket client #%u disconnected", socketId); } +void handleWebSocketClientWiFiScanMessage(const StaticJsonDocument<256>& doc) { + bool run = doc["run"]; + if (run) { + WiFiManager::StartScan(); + } else { + ESP_LOGW(TAG, "WiFi scan is not implemented yet"); + } +} +void handleWebSocketClientWiFiAuthenticateMessage(const StaticJsonDocument<256>& doc) { + String bssidStr = doc["bssid"]; + if (bssidStr.isEmpty()) { + ESP_LOGE(TAG, "WiFi BSSID is missing"); + return; + } + if (bssidStr.length() != 17) { + ESP_LOGE(TAG, "WiFi BSSID is invalid"); + return; + } + + String password = doc["password"]; + + // Convert BSSID to byte array + std::uint8_t bssid[6]; + if (sscanf(bssidStr.c_str(), "%02X:%02X:%02X:%02X:%02X:%02X", bssid + 0, bssid + 1, bssid + 2, bssid + 3, bssid + 4, bssid + 5) != 6) { + ESP_LOGE(TAG, "WiFi BSSID is invalid"); + return; + } + + WiFiManager::Authenticate(bssid, password); +} +void handleWebSocketClientWiFiConnectMessage(const StaticJsonDocument<256>& doc) { + std::uint16_t wifiId = doc["id"]; + + WiFiManager::Connect(wifiId); +} +void handleWebSocketClientWiFiDisconnectMessage(const StaticJsonDocument<256>& doc) { + WiFiManager::Disconnect(); +} +void handleWebSocketClientWiFiForgetMessage(const StaticJsonDocument<256>& doc) { + WiFiManager::Forget(doc["bssid"]); +} +void handleWebSocketClientWiFiMessage(StaticJsonDocument<256> doc) { + // Parse "action" field + String actionStr = doc["action"]; + if (actionStr.isEmpty()) { + ESP_LOGE(TAG, "WiFi action is missing"); + return; + } + + if (actionStr == "scan") { + handleWebSocketClientWiFiScanMessage(doc); + } else if (actionStr == "authenticate") { + handleWebSocketClientWiFiAuthenticateMessage(doc); + } else if (actionStr == "connect") { + handleWebSocketClientWiFiConnectMessage(doc); + } else if (actionStr == "disconnect") { + handleWebSocketClientWiFiDisconnectMessage(doc); + } else if (actionStr == "forget") { + handleWebSocketClientWiFiForgetMessage(doc); + } else { + ESP_LOGE(TAG, "Unknown WiFi action: %s", actionStr.c_str()); + } +} void handleWebSocketClientMessage(std::uint8_t socketId, WStype_t type, std::uint8_t* data, std::size_t len) { if (type != WStype_t::WStype_TEXT) { ESP_LOGE(TAG, "Message type is not supported"); @@ -137,13 +200,13 @@ void handleWebSocketClientMessage(std::uint8_t socketId, WStype_t type, std::uin } String typeStr = doc["type"]; - if (typeStr.length() == 0) { + if (typeStr.isEmpty()) { ESP_LOGE(TAG, "Message type is missing"); return; } - if (typeStr == "startScan") { - WiFiManager::StartScan(); + if (typeStr == "wifi") { + handleWebSocketClientWiFiMessage(doc); } /* else if (typeStr == "connect") { WiFiManager::Connect(doc["ssid"], doc["password"]); } else if (typeStr == "disconnect") { diff --git a/src/Mappers/EspWiFiTypesMapper.cpp b/src/Mappers/EspWiFiTypesMapper.cpp new file mode 100644 index 00000000..755b05fa --- /dev/null +++ b/src/Mappers/EspWiFiTypesMapper.cpp @@ -0,0 +1,28 @@ +#include "Mappers/EspWiFiTypesMapper.h" + +using namespace OpenShock; + +const char* Mappers::GetWiFiAuthModeName(wifi_auth_mode_t authMode) { + switch (authMode) { + case WIFI_AUTH_OPEN: + return "Open"; + case WIFI_AUTH_WEP: + return "WEP"; + case WIFI_AUTH_WPA_PSK: + return "WPA PSK"; + case WIFI_AUTH_WPA2_PSK: + return "WPA2 PSK"; + case WIFI_AUTH_WPA_WPA2_PSK: + return "WPA/WPA2 PSK"; + case WIFI_AUTH_WPA2_ENTERPRISE: + return "WPA2 Enterprise"; + case WIFI_AUTH_WPA3_PSK: + return "WPA3 PSK"; + case WIFI_AUTH_WPA2_WPA3_PSK: + return "WPA2/WPA3 PSK"; + case WIFI_AUTH_WAPI_PSK: + return "WAPI PSK"; + default: + return nullptr; + } +} diff --git a/src/WiFiCredentials.cpp b/src/WiFiCredentials.cpp new file mode 100644 index 00000000..39e22a5b --- /dev/null +++ b/src/WiFiCredentials.cpp @@ -0,0 +1,189 @@ +#include "WiFiCredentials.h" + +#include +#include + +#include + +#include + +const char* const TAG = "WiFiCredentials"; + +using namespace OpenShock; + + + +bool WiFiCredentials::Load(std::vector& credentials) { + credentials.clear(); + + // Ensure the /wifi/creds directory exists + if (!LittleFS.exists("/wifi/creds")) { + if (!LittleFS.exists("/wifi")) { + if (!LittleFS.mkdir("/wifi")) { + ESP_LOGE(TAG, "Failed to create /wifi directory"); + return false; + } + } + if (!LittleFS.mkdir("/wifi/creds")) { + ESP_LOGE(TAG, "Failed to create /wifi/creds directory"); + return false; + } + ESP_LOGI(TAG, "No credentials directory found, created one"); + return true; + } + + File credsDir = LittleFS.open("/wifi/creds"); + if (!credsDir) { + ESP_LOGE(TAG, "Failed to open /wifi/creds"); + return false; + } + + while (true) { + File file = credsDir.openNextFile(); + if (!file) { + break; + } + + WiFiCredentials creds; + if (!creds._load(file)) { + ESP_LOGE(TAG, "Failed to load credentials from %s", file.name()); + continue; + } + + credentials.push_back(creds); + } + + return true; +} + +WiFiCredentials::WiFiCredentials(std::uint8_t id, const wifi_ap_record_t* record) { + _id = id; + + static_assert(sizeof(_ssid) == sizeof(record->ssid), "WiFiCredentials::_ssid and wifi_ap_record_t::ssid must be the same size"); + memset(_password, 0, sizeof(_password)); + memcpy(_ssid, record->ssid, sizeof(_ssid)); +} + +WiFiCredentials::WiFiCredentials(std::uint8_t id, const String& ssid, const String& password) { + _id = id; + + auto ssidLength = ssid.length(); + if (ssid.length() > sizeof(_ssid)) { + ESP_LOGW(TAG, "SSID is too long, truncating"); + ssidLength = sizeof(_ssid) - 1; + } + _ssidLength = ssidLength; + ssid.toCharArray(reinterpret_cast(_ssid), _ssidLength + 1); + + auto passwordLength = password.length(); + if (password.length() > sizeof(_password)) { + ESP_LOGW(TAG, "Password is too long, truncating"); + passwordLength = sizeof(_password) - 1; + } + _passwordLength = passwordLength; + password.toCharArray(reinterpret_cast(_password), _passwordLength + 1); +} + +void WiFiCredentials::setSSID(const String& ssid) { + auto ssidLength = ssid.length(); + if (ssid.length() > sizeof(_ssid)) { + ESP_LOGW(TAG, "SSID is too long, truncating"); + ssidLength = sizeof(_ssid) - 1; + } + _ssidLength = ssidLength; + ssid.toCharArray(reinterpret_cast(_ssid), _ssidLength + 1); +} + +void WiFiCredentials::setSSID(const std::uint8_t* ssid, std::size_t ssidLength) { + if (ssidLength > sizeof(_ssid)) { + ESP_LOGW(TAG, "SSID is too long, truncating"); + ssidLength = sizeof(_ssid) - 1; + } + _ssidLength = ssidLength; + memcpy(_ssid, ssid, _ssidLength); + _ssid[_ssidLength] = 0; +} + +void WiFiCredentials::setPassword(const std::uint8_t* password, std::size_t passwordLength) { + if (passwordLength > sizeof(_password)) { + ESP_LOGW(TAG, "Password is too long, truncating"); + passwordLength = sizeof(_password) - 1; + } + _passwordLength = passwordLength; + memcpy(_password, password, _passwordLength); + _password[_passwordLength] = 0; +} + +void WiFiCredentials::setPassword(const String& password) { + auto passwordLength = password.length(); + if (password.length() > sizeof(_password)) { + ESP_LOGW(TAG, "Password is too long, truncating"); + passwordLength = sizeof(_password) - 1; + } + _passwordLength = passwordLength; + password.toCharArray(reinterpret_cast(_password), _passwordLength + 1); +} + +bool WiFiCredentials::save() const { + char filename[16] = { 0 }; + sprintf(filename, "/wifi/creds/%u", _id); + File file = LittleFS.open(filename, "wb"); + if (!file) { + ESP_LOGE(TAG, "Failed to open file %s for writing", filename); + return false; + } + + file.write(_id); + file.write(reinterpret_cast(&_ssidLength), sizeof(_ssidLength)); + file.write(_ssid, _ssidLength); + file.write(reinterpret_cast(&_passwordLength), sizeof(_passwordLength)); + file.write(_password, _passwordLength); + + file.close(); + + return true; +} + +bool WiFiCredentials::erase() const { + char filename[16] = { 0 }; + sprintf(filename, "/wifi/creds/%u", _id); + if (!LittleFS.remove(filename)) { + ESP_LOGE(TAG, "Failed to remove file %s", filename); + return false; + } + + return true; +} + +bool WiFiCredentials::_load(fs::File& file) { + if (!file) { + ESP_LOGE(TAG, "File is not open"); + return false; + } + + file.read(reinterpret_cast(&_id), sizeof(_id)); + + file.read(reinterpret_cast(&_ssidLength), sizeof(_ssidLength)); + if (_ssidLength > sizeof(_ssid) - 1) { + const char* filename = file.name(); + ESP_LOGE(TAG, "Loading credentials for %s failed: SSID length is too long", filename); + ESP_LOGW(TAG, "Deleting credentials for %s", filename); + LittleFS.remove(filename); + return false; + } + file.read(_ssid, _ssidLength); + _ssid[_ssidLength] = 0; + + file.read(reinterpret_cast(&_passwordLength), sizeof(_passwordLength)); + if (_passwordLength > sizeof(_password) - 1) { + const char* filename = file.name(); + ESP_LOGE(TAG, "Loading credentials for %s failed: password length is too long", filename); + ESP_LOGW(TAG, "Deleting credentials for %s", filename); + LittleFS.remove(filename); + return false; + } + file.read(_password, _passwordLength); + _password[_passwordLength] = 0; + + return true; +} diff --git a/src/WiFiManager.cpp b/src/WiFiManager.cpp index 254afb43..101830a8 100644 --- a/src/WiFiManager.cpp +++ b/src/WiFiManager.cpp @@ -1,87 +1,62 @@ #include "WiFiManager.h" #include "CaptivePortal.h" +#include "WiFiCredentials.h" #include "VisualStateManager.h" +#include "Mappers/EspWiFiTypesMapper.h" #include #include #include +#include + #include +#include + const char* const TAG = "WiFiManager"; using namespace OpenShock; -struct WifiCredentials { - String ssid; - String password; +struct WiFiStats { + std::uint8_t credsId; std::uint16_t wifiIndex; - std::uint8_t attempts; + std::uint16_t reconnectCount; }; -static std::vector s_wifiCredentials; + static WiFiState s_wifiState; +static std::vector s_wifiStats; +static std::vector s_wifiCredentials; -void SetWiFiState(WiFiState state) { +void _setWiFiState(WiFiState state) { s_wifiState = state; VisualStateManager::SetWiFiState(state); } -bool SaveCredentials() { - File file = LittleFS.open("/networks", FILE_WRITE); - if (!file) { - ESP_LOGE(TAG, "Failed to open networks file for writing"); - return false; - } - - DynamicJsonDocument doc(1024); - JsonArray networks = doc.createNestedArray("networks"); - +bool _addNetwork(const char* ssid, const String& password) { + std::uint32_t bits = 0; for (auto& cred : s_wifiCredentials) { - JsonObject network = networks.createNestedObject(); - network["ssid"] = cred.ssid; - network["password"] = cred.password; - } - - if (serializeJson(doc, file) == 0) { - ESP_LOGE(TAG, "Failed to serialize networks file"); - file.close(); - return false; - } - - file.close(); - return true; -} -bool ReadCredentials() { - File file = LittleFS.open("/networks", FILE_READ); - if (!file) { - ESP_LOGW(TAG, "Failed to open networks file for reading"); - return false; + if (strcmp(cred.ssid().data(), ssid) == 0) { + cred.setPassword(password); + cred.save(); + return true; + } + bits |= 1u << cred.id(); } - DynamicJsonDocument doc(1024); - if (deserializeJson(doc, file) != DeserializationError::Ok) { - file.close(); - - ESP_LOGW(TAG, "Failed to deserialize networks file, overwriting"); - SaveCredentials(); - + // If we have 255 credentials, we can't add any more + if (s_wifiCredentials.size() == 255) { + ESP_LOGE(TAG, "Cannot add WiFi credentials: too many credentials"); return false; } - s_wifiCredentials.clear(); - - JsonArray networks = doc["networks"]; - for (int i = 0; i < networks.size(); i++) { - JsonObject network = networks[i]; - String ssid = network["ssid"]; - String password = network["password"]; - - s_wifiCredentials.push_back({ssid, password, UINT16_MAX, 0}); - ESP_LOGD(TAG, "Read credentials for %s", ssid.c_str()); + std::uint8_t id = 0; + while (bits & (1u << id)) { + id++; } - file.close(); + s_wifiCredentials.push_back(WiFiCredentials(id, ssid, password)); return true; } @@ -93,8 +68,9 @@ void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info) { if (numNetworks < 0) { if (numNetworks != WIFI_SCAN_RUNNING) { ESP_LOGE(TAG, "Scan failed"); - SetWiFiState(WiFiState::Disconnected); + _setWiFiState(WiFiState::Disconnected); CaptivePortal::Start(); + CaptivePortal::BroadcastMessageTXT("{\"type\":\"wifi\",\"subject\":\"scan\",\"status\":\"error\",\"error\":\"scan_failed\"}"); } else { ESP_LOGE(TAG, "Scan is still running"); } @@ -102,75 +78,90 @@ void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info) { } DynamicJsonDocument doc(64 + numNetworks * 128); - doc["type"] = "scan"; - doc["status"] = "finished"; + doc["type"] = "wifi"; + doc["subject"] = "scan"; + doc["status"] = "completed"; JsonArray networks = doc.createNestedArray("networks"); if (numNetworks == 0) { ESP_LOGD(TAG, "No networks found"); - SetWiFiState(WiFiState::Disconnected); + _setWiFiState(WiFiState::Disconnected); CaptivePortal::Start(); CaptivePortal::BroadcastMessageJSON(doc); return; } - for (auto& cred : s_wifiCredentials) { - cred.wifiIndex = UINT16_MAX; - } + std::vector oldStats = s_wifiStats; + s_wifiStats.clear(); - std::uint16_t recognizedNetworks = 0; for (std::uint16_t i = 0; i < numNetworks; i++) { - String ssid = WiFi.SSID(i); - bool saved = false; + wifi_ap_record_t* record = reinterpret_cast(WiFi.getScanInfoByIndex(i)); + if (record == nullptr) { + ESP_LOGE(TAG, "Failed to get scan info for network #%u", i); + break; + } + + // Fetch the saved credentials ID for this network + std::uint8_t credsId = UINT8_MAX; for (auto& cred : s_wifiCredentials) { - if (cred.ssid == ssid) { - cred.wifiIndex = i; - recognizedNetworks++; - saved = true; + if (strcmp(cred.ssid().data(), reinterpret_cast(record->ssid)) == 0) { + credsId = cred.id(); break; } } + + // If the credentials were saved, fetch or create the stats for this network + std::uint16_t reconnectCount = UINT16_MAX; + if (credsId != UINT8_MAX) { + for (auto& stat : oldStats) { + if (stat.credsId == credsId) { + reconnectCount = stat.reconnectCount; + break; + } + } + + if (reconnectCount == UINT16_MAX) { + reconnectCount = 0; + } + + s_wifiStats.push_back({ .credsId = credsId, .wifiIndex = i, .reconnectCount = reconnectCount }); + } + + char mac[18] = { 0 }; + sprintf(mac, "%02X:%02X:%02X:%02X:%02X:%02X", record->bssid[0], record->bssid[1], record->bssid[2], record->bssid[3], record->bssid[4], record->bssid[5]); + JsonObject network = networks.createNestedObject(); - network["index"] = i; - network["ssid"] = ssid; - network["bssid"] = WiFi.BSSIDstr(i); - network["rssi"] = WiFi.RSSI(i); - network["channel"] = WiFi.channel(i); - network["saved"] = saved; + network["ssid"] = record->ssid; + network["bssid"] = mac; + network["rssi"] = record->rssi; + network["channel"] = record->primary; + network["security"] = Mappers::GetWiFiAuthModeName(record->authmode); + network["saved"] = credsId != UINT8_MAX; } CaptivePortal::BroadcastMessageJSON(doc); - if (recognizedNetworks == 0) { + // Since wifiStats is a combination of detected networks and saved credentials, we can just check if it's empty to see if we recognized any networks + if (s_wifiStats.empty()) { ESP_LOGD(TAG, "No recognized networks found"); - SetWiFiState(WiFiState::Disconnected); + _setWiFiState(WiFiState::Disconnected); CaptivePortal::Start(); return; } - - // Attempt to connect to the first recognized network - for (auto& cred : s_wifiCredentials) { - if (cred.wifiIndex != UINT16_MAX) { - ESP_LOGD(TAG, "Attempting to connect to %s", cred.ssid.c_str()); - WiFi.begin(cred.ssid.c_str(), cred.password.c_str()); - SetWiFiState(WiFiState::Connecting); - return; - } - } } void _evWiFiConnected(arduino_event_id_t event, arduino_event_info_t info) { ESP_LOGD(TAG, "WiFi connected"); - SetWiFiState(WiFiState::Connected); + _setWiFiState(WiFiState::Connected); CaptivePortal::Stop(); } void _evWiFiDisconnected(arduino_event_id_t event, arduino_event_info_t info) { ESP_LOGD(TAG, "WiFi disconnected"); - SetWiFiState(WiFiState::Disconnected); + _setWiFiState(WiFiState::Disconnected); CaptivePortal::Start(); } bool WiFiManager::Init() { - ReadCredentials(); + WiFiCredentials::Load(s_wifiCredentials); WiFi.onEvent(_evScanCompleted, ARDUINO_EVENT_WIFI_SCAN_DONE); WiFi.onEvent(_evWiFiConnected, ARDUINO_EVENT_WIFI_STA_CONNECTED); @@ -181,10 +172,10 @@ bool WiFiManager::Init() { if (s_wifiCredentials.size() > 0) { WiFi.scanNetworks(true); - SetWiFiState(WiFiState::Scanning); + _setWiFiState(WiFiState::Scanning); } else { CaptivePortal::Start(); - SetWiFiState(WiFiState::Disconnected); + _setWiFiState(WiFiState::Disconnected); } return true; @@ -194,37 +185,79 @@ WiFiState WiFiManager::GetWiFiState() { return s_wifiState; } -void WiFiManager::AddOrUpdateNetwork(const char* ssid, const char* password) { - for (auto& cred : s_wifiCredentials) { - if (cred.ssid == ssid) { - cred.password = password; - cred.attempts = 0; - SaveCredentials(); - return; +bool WiFiManager::Authenticate(nonstd::span bssid, const String& password) { + char ssid[33] = { 0 }; + std::uint16_t wifiIndex = UINT16_MAX; + for (std::uint16_t i = 0; i < UINT16_MAX; i++) { // Yes, this is intentional (WiFi.getScanInfoByIndex returns nullptr when there are no more networks) + wifi_ap_record_t* record = reinterpret_cast(WiFi.getScanInfoByIndex(i)); + if (record == nullptr) { + ESP_LOGE(TAG, "Failed to get scan info for network #%u", i); + break; + } + + if (memcmp(record->bssid, bssid.data(), bssid.size()) == 0) { + wifiIndex = i; + static_assert(sizeof(record->ssid) == sizeof(ssid), "SSID size mismatch"); + memcpy(ssid, record->ssid, sizeof(ssid)); + break; } } - s_wifiCredentials.push_back({ssid, password, UINT16_MAX, 0}); - SaveCredentials(); + if (wifiIndex == UINT16_MAX) { + ESP_LOGE(TAG, "Failed to find network with BSSID %02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); + CaptivePortal::BroadcastMessageTXT("{\"type\":\"wifi\",\"status\":\"error\",\"error\":\"network_not_found\"}"); + return false; + } + + if (!_addNetwork(ssid, password)) { + CaptivePortal::BroadcastMessageTXT("{\"type\":\"wifi\",\"status\":\"error\",\"error\":\"too_many_credentials\"}"); + return false; + } + + CaptivePortal::BroadcastMessageTXT(String("{\"type\":\"wifi\",\"status\":\"added\", \"ssid\":\"") + ssid + "\"}"); + + return true; } -void WiFiManager::RemoveNetwork(const char* ssid) { +void WiFiManager::Forget(std::uint8_t wifiId) { for (auto it = s_wifiCredentials.begin(); it != s_wifiCredentials.end(); it++) { - if (it->ssid == ssid) { + if (it->id() == wifiId) { s_wifiCredentials.erase(it); - SaveCredentials(); + it->erase(); return; } } } +void WiFiManager::Connect(std::uint8_t wifiId) { + if (s_wifiState != WiFiState::Disconnected) return; + + for (auto& creds : s_wifiCredentials) { + if (creds.id() == wifiId) { + WiFi.begin(creds.ssid().data(), creds.password().data()); + _setWiFiState(WiFiState::Connecting); + return; + } + } + + ESP_LOGE(TAG, "Failed to find credentials with ID %u", wifiId); +} + +void WiFiManager::Disconnect() { + if (s_wifiState != WiFiState::Connected) return; + + WiFi.disconnect(true); + _setWiFiState(WiFiState::Disconnected); + CaptivePortal::Start(); +} + bool WiFiManager::StartScan() { if (s_wifiState != WiFiState::Disconnected) return false; - CaptivePortal::BroadcastMessageTXT("{\"type\":\"scan\",\"status\":\"started\"}"); + CaptivePortal::BroadcastMessageTXT("{\"type\":\"wifi\",\"subject\":\"scan\",\"status\":\"started\"}"); WiFi.scanNetworks(true); - SetWiFiState(WiFiState::Scanning); + _setWiFiState(WiFiState::Scanning); return true; } From 7e6dbe2340e581744ebe6b0ebafe54cfd9d7aae7 Mon Sep 17 00:00:00 2001 From: hhvrc Date: Tue, 3 Oct 2023 14:02:46 +0200 Subject: [PATCH 03/22] Refactor and improve scanning logic --- include/WiFiManager.h | 8 +- include/WiFiScanManager.h | 36 ++++++ include/WiFiState.h | 3 + src/CaptivePortal.cpp | 46 +++++++- src/VisualStateManager.cpp | 7 -- src/WiFiManager.cpp | 147 +++++------------------ src/WiFiScanManager.cpp | 234 +++++++++++++++++++++++++++++++++++++ src/WiFiState.cpp | 15 +++ src/main.cpp | 8 ++ 9 files changed, 366 insertions(+), 138 deletions(-) create mode 100644 include/WiFiScanManager.h create mode 100644 src/WiFiScanManager.cpp create mode 100644 src/WiFiState.cpp diff --git a/include/WiFiManager.h b/include/WiFiManager.h index a3333fe2..be0262f2 100644 --- a/include/WiFiManager.h +++ b/include/WiFiManager.h @@ -11,15 +11,9 @@ namespace OpenShock::WiFiManager { bool Init(); - WiFiState GetWiFiState(); - bool Authenticate(nonstd::span bssid, const String& password); void Forget(std::uint8_t wifiId); void Connect(std::uint8_t wifiId); void Disconnect(); - - bool StartScan(); - void StopScan(); - -} // namespace OpenShock::WiFiManager +} // namespace OpenShock::WiFiManager diff --git a/include/WiFiScanManager.h b/include/WiFiScanManager.h new file mode 100644 index 00000000..93a7e78f --- /dev/null +++ b/include/WiFiScanManager.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include +#include + +namespace OpenShock::WiFiScanManager { + bool Init(); + + bool IsScanning(); + + bool StartScan(); + void CancelScan(); + + typedef std::uint64_t CallbackHandle; + + typedef std::function ScanStartedHandler; + CallbackHandle RegisterScanStartedHandler(const ScanStartedHandler& handler); + void UnregisterScanStartedHandler(CallbackHandle id); + + enum class ScanCompletedStatus { + Success, + Cancelled, + Error, + }; + typedef std::function ScanCompletedHandler; + CallbackHandle RegisterScanCompletedHandler(const ScanCompletedHandler& handler); + void UnregisterScanCompletedHandler(CallbackHandle id); + + typedef std::function ScanDiscoveryHandler; + CallbackHandle RegisterScanDiscoveryHandler(const ScanDiscoveryHandler& handler); + void UnregisterScanDiscoveryHandler(CallbackHandle id); + + void Update(); +} // namespace OpenShock::WiFiScanManager diff --git a/include/WiFiState.h b/include/WiFiState.h index a60a2f4b..a1b5c054 100644 --- a/include/WiFiState.h +++ b/include/WiFiState.h @@ -7,4 +7,7 @@ namespace OpenShock { Connecting, Connected }; + + WiFiState GetWiFiState() noexcept; + void SetWiFiState(WiFiState state) noexcept; } // namespace OpenShock diff --git a/src/CaptivePortal.cpp b/src/CaptivePortal.cpp index 2defee6c..7370289c 100644 --- a/src/CaptivePortal.cpp +++ b/src/CaptivePortal.cpp @@ -2,6 +2,7 @@ #include "AuthenticationManager.h" #include "WiFiManager.h" +#include "WiFiScanManager.h" #include #include @@ -24,8 +25,11 @@ struct CaptivePortalInstance { AsyncWebServer webServer; WebSocketsServer socketServer; + OpenShock::WiFiScanManager::CallbackHandle wifiScanStartedHandlerId; + OpenShock::WiFiScanManager::CallbackHandle wifiScanCompletedHandlerId; + OpenShock::WiFiScanManager::CallbackHandle wifiScanDiscoveryHandlerId; }; -std::unique_ptr s_webServices = nullptr; +static std::unique_ptr s_webServices = nullptr; void handleWebSocketEvent(std::uint8_t socketId, WStype_t type, std::uint8_t* data, std::size_t len); void handleHttpNotFound(AsyncWebServerRequest* request); @@ -68,6 +72,38 @@ bool CaptivePortal::Start() { s_webServices->webServer.onNotFound([](AsyncWebServerRequest* request) { request->send(404, "text/plain", "Not found"); }); s_webServices->webServer.begin(); + s_webServices->wifiScanStartedHandlerId = WiFiScanManager::RegisterScanStartedHandler([]() { + ESP_LOGD(TAG, "WiFi scan started"); + StaticJsonDocument<256> doc; + doc["type"] = "wifi"; + doc["subject"] = "scan_started"; + doc["status"] = "ok"; + CaptivePortal::BroadcastMessageJSON(doc); + }); + s_webServices->wifiScanCompletedHandlerId = WiFiScanManager::RegisterScanCompletedHandler([](WiFiScanManager::ScanCompletedStatus status) { + ESP_LOGD(TAG, "WiFi scan completed"); + StaticJsonDocument<256> doc; + doc["type"] = "wifi"; + doc["subject"] = "scan_completed"; + doc["status"] = "ok"; + //doc["status"] = EspWiFiTypesMapper::MapScanCompletedStatus(status); + CaptivePortal::BroadcastMessageJSON(doc); + }); + s_webServices->wifiScanDiscoveryHandlerId = WiFiScanManager::RegisterScanDiscoveryHandler([](const wifi_ap_record_t* record) { + ESP_LOGD(TAG, "WiFi scan discovery"); + StaticJsonDocument<256> doc; + doc["type"] = "wifi"; + doc["subject"] = "scan_discovery"; + doc["status"] = "ok"; + doc["ssid"] = reinterpret_cast(record->ssid); + //doc["bssid"] = EspWiFiTypesMapper::MapBssid(record->bssid); + doc["channel"] = record->primary; + doc["rssi"] = record->rssi; + //doc["auth"] = EspWiFiTypesMapper::MapAuthMode(record->authmode); + //doc["hidden"] = record->is_hidden; + CaptivePortal::BroadcastMessageJSON(doc); + }); + ESP_LOGD(TAG, "Started"); return true; @@ -83,6 +119,10 @@ void CaptivePortal::Stop() { s_webServices->webServer.end(); s_webServices->socketServer.close(); + WiFiScanManager::UnregisterScanStartedHandler(s_webServices->wifiScanStartedHandlerId); + WiFiScanManager::UnregisterScanCompletedHandler(s_webServices->wifiScanCompletedHandlerId); + WiFiScanManager::UnregisterScanDiscoveryHandler(s_webServices->wifiScanDiscoveryHandlerId); + s_webServices = nullptr; WiFi.softAPdisconnect(true); @@ -126,9 +166,9 @@ void handleWebSocketClientDisconnected(std::uint8_t socketId) { void handleWebSocketClientWiFiScanMessage(const StaticJsonDocument<256>& doc) { bool run = doc["run"]; if (run) { - WiFiManager::StartScan(); + WiFiScanManager::StartScan(); } else { - ESP_LOGW(TAG, "WiFi scan is not implemented yet"); + WiFiScanManager::CancelScan(); } } void handleWebSocketClientWiFiAuthenticateMessage(const StaticJsonDocument<256>& doc) { diff --git a/src/VisualStateManager.cpp b/src/VisualStateManager.cpp index fcf216e8..86b5f02a 100644 --- a/src/VisualStateManager.cpp +++ b/src/VisualStateManager.cpp @@ -95,11 +95,6 @@ void VisualStateManager::SetCriticalError() { } void VisualStateManager::SetWiFiState(WiFiState state) { - static WiFiState _state = (WiFiState)-1; - if (_state == state) { - return; - } - ESP_LOGD(TAG, "SetWiFiStateState: %d", state); switch (state) { case WiFiState::Disconnected: @@ -111,8 +106,6 @@ void VisualStateManager::SetWiFiState(WiFiState state) { default: return; } - - _state = state; } #endif // OPENSHOCK_LED_GPIO diff --git a/src/WiFiManager.cpp b/src/WiFiManager.cpp index 101830a8..f392153c 100644 --- a/src/WiFiManager.cpp +++ b/src/WiFiManager.cpp @@ -19,21 +19,30 @@ const char* const TAG = "WiFiManager"; using namespace OpenShock; +void _broadcastWifiAddNetworkSuccess(const char* ssid) { + DynamicJsonDocument doc(64); + doc["type"] = "wifi"; + doc["subject"] = "add_network"; + doc["status"] = "success"; + doc["ssid"] = ssid; +} +void _broadcastWifiAddNetworkError(const char* error) { + DynamicJsonDocument doc(64); + doc["type"] = "wifi"; + doc["subject"] = "add_network"; + doc["status"] = "error"; + doc["error"] = error; +} + struct WiFiStats { std::uint8_t credsId; std::uint16_t wifiIndex; std::uint16_t reconnectCount; }; -static WiFiState s_wifiState; static std::vector s_wifiStats; static std::vector s_wifiCredentials; -void _setWiFiState(WiFiState state) { - s_wifiState = state; - VisualStateManager::SetWiFiState(state); -} - bool _addNetwork(const char* ssid, const String& password) { std::uint32_t bits = 0; for (auto& cred : s_wifiCredentials) { @@ -61,109 +70,20 @@ bool _addNetwork(const char* ssid, const String& password) { return true; } -void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info) { - ESP_LOGD(TAG, "Scan completed"); - - std::uint16_t numNetworks = WiFi.scanComplete(); - if (numNetworks < 0) { - if (numNetworks != WIFI_SCAN_RUNNING) { - ESP_LOGE(TAG, "Scan failed"); - _setWiFiState(WiFiState::Disconnected); - CaptivePortal::Start(); - CaptivePortal::BroadcastMessageTXT("{\"type\":\"wifi\",\"subject\":\"scan\",\"status\":\"error\",\"error\":\"scan_failed\"}"); - } else { - ESP_LOGE(TAG, "Scan is still running"); - } - return; - } - - DynamicJsonDocument doc(64 + numNetworks * 128); - doc["type"] = "wifi"; - doc["subject"] = "scan"; - doc["status"] = "completed"; - JsonArray networks = doc.createNestedArray("networks"); - - if (numNetworks == 0) { - ESP_LOGD(TAG, "No networks found"); - _setWiFiState(WiFiState::Disconnected); - CaptivePortal::Start(); - CaptivePortal::BroadcastMessageJSON(doc); - return; - } - - std::vector oldStats = s_wifiStats; - s_wifiStats.clear(); - - for (std::uint16_t i = 0; i < numNetworks; i++) { - wifi_ap_record_t* record = reinterpret_cast(WiFi.getScanInfoByIndex(i)); - if (record == nullptr) { - ESP_LOGE(TAG, "Failed to get scan info for network #%u", i); - break; - } - - // Fetch the saved credentials ID for this network - std::uint8_t credsId = UINT8_MAX; - for (auto& cred : s_wifiCredentials) { - if (strcmp(cred.ssid().data(), reinterpret_cast(record->ssid)) == 0) { - credsId = cred.id(); - break; - } - } - - // If the credentials were saved, fetch or create the stats for this network - std::uint16_t reconnectCount = UINT16_MAX; - if (credsId != UINT8_MAX) { - for (auto& stat : oldStats) { - if (stat.credsId == credsId) { - reconnectCount = stat.reconnectCount; - break; - } - } - - if (reconnectCount == UINT16_MAX) { - reconnectCount = 0; - } - - s_wifiStats.push_back({ .credsId = credsId, .wifiIndex = i, .reconnectCount = reconnectCount }); - } - - char mac[18] = { 0 }; - sprintf(mac, "%02X:%02X:%02X:%02X:%02X:%02X", record->bssid[0], record->bssid[1], record->bssid[2], record->bssid[3], record->bssid[4], record->bssid[5]); - - JsonObject network = networks.createNestedObject(); - network["ssid"] = record->ssid; - network["bssid"] = mac; - network["rssi"] = record->rssi; - network["channel"] = record->primary; - network["security"] = Mappers::GetWiFiAuthModeName(record->authmode); - network["saved"] = credsId != UINT8_MAX; - } - - CaptivePortal::BroadcastMessageJSON(doc); - - // Since wifiStats is a combination of detected networks and saved credentials, we can just check if it's empty to see if we recognized any networks - if (s_wifiStats.empty()) { - ESP_LOGD(TAG, "No recognized networks found"); - _setWiFiState(WiFiState::Disconnected); - CaptivePortal::Start(); - return; - } -} void _evWiFiConnected(arduino_event_id_t event, arduino_event_info_t info) { ESP_LOGD(TAG, "WiFi connected"); - _setWiFiState(WiFiState::Connected); + OpenShock::SetWiFiState(WiFiState::Connected); CaptivePortal::Stop(); } void _evWiFiDisconnected(arduino_event_id_t event, arduino_event_info_t info) { ESP_LOGD(TAG, "WiFi disconnected"); - _setWiFiState(WiFiState::Disconnected); + OpenShock::SetWiFiState(WiFiState::Disconnected); CaptivePortal::Start(); } bool WiFiManager::Init() { WiFiCredentials::Load(s_wifiCredentials); - WiFi.onEvent(_evScanCompleted, ARDUINO_EVENT_WIFI_SCAN_DONE); WiFi.onEvent(_evWiFiConnected, ARDUINO_EVENT_WIFI_STA_CONNECTED); WiFi.onEvent(_evWiFiDisconnected, ARDUINO_EVENT_WIFI_STA_DISCONNECTED); @@ -172,19 +92,15 @@ bool WiFiManager::Init() { if (s_wifiCredentials.size() > 0) { WiFi.scanNetworks(true); - _setWiFiState(WiFiState::Scanning); + OpenShock::SetWiFiState(WiFiState::Scanning); } else { CaptivePortal::Start(); - _setWiFiState(WiFiState::Disconnected); + OpenShock::SetWiFiState(WiFiState::Disconnected); } return true; } -WiFiState WiFiManager::GetWiFiState() { - return s_wifiState; -} - bool WiFiManager::Authenticate(nonstd::span bssid, const String& password) { char ssid[33] = { 0 }; std::uint16_t wifiIndex = UINT16_MAX; @@ -205,16 +121,16 @@ bool WiFiManager::Authenticate(nonstd::span bssid, const String if (wifiIndex == UINT16_MAX) { ESP_LOGE(TAG, "Failed to find network with BSSID %02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); - CaptivePortal::BroadcastMessageTXT("{\"type\":\"wifi\",\"status\":\"error\",\"error\":\"network_not_found\"}"); + _broadcastWifiAddNetworkError("network_not_found"); return false; } if (!_addNetwork(ssid, password)) { - CaptivePortal::BroadcastMessageTXT("{\"type\":\"wifi\",\"status\":\"error\",\"error\":\"too_many_credentials\"}"); + _broadcastWifiAddNetworkError("too_many_credentials"); return false; } - CaptivePortal::BroadcastMessageTXT(String("{\"type\":\"wifi\",\"status\":\"added\", \"ssid\":\"") + ssid + "\"}"); + _broadcastWifiAddNetworkSuccess(ssid); return true; } @@ -230,12 +146,12 @@ void WiFiManager::Forget(std::uint8_t wifiId) { } void WiFiManager::Connect(std::uint8_t wifiId) { - if (s_wifiState != WiFiState::Disconnected) return; + if (OpenShock::GetWiFiState() != WiFiState::Disconnected) return; for (auto& creds : s_wifiCredentials) { if (creds.id() == wifiId) { WiFi.begin(creds.ssid().data(), creds.password().data()); - _setWiFiState(WiFiState::Connecting); + OpenShock::SetWiFiState(WiFiState::Connecting); return; } } @@ -244,20 +160,9 @@ void WiFiManager::Connect(std::uint8_t wifiId) { } void WiFiManager::Disconnect() { - if (s_wifiState != WiFiState::Connected) return; + if (OpenShock::GetWiFiState() != WiFiState::Connected) return; WiFi.disconnect(true); - _setWiFiState(WiFiState::Disconnected); + OpenShock::SetWiFiState(WiFiState::Disconnected); CaptivePortal::Start(); } - -bool WiFiManager::StartScan() { - if (s_wifiState != WiFiState::Disconnected) return false; - - CaptivePortal::BroadcastMessageTXT("{\"type\":\"wifi\",\"subject\":\"scan\",\"status\":\"started\"}"); - - WiFi.scanNetworks(true); - _setWiFiState(WiFiState::Scanning); - - return true; -} diff --git a/src/WiFiScanManager.cpp b/src/WiFiScanManager.cpp new file mode 100644 index 00000000..4f493e6f --- /dev/null +++ b/src/WiFiScanManager.cpp @@ -0,0 +1,234 @@ +#include "WiFiScanManager.h" + +#include "CaptivePortal.h" + +#include + +#include + +#include + +const char* const TAG = "WiFiScanManager"; + +constexpr const std::size_t OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL = 150; + +using namespace OpenShock; + +enum class CurrentScanStatus { + Idle, + Starting, + Running, + Complete, + Cancelled, + Error, +}; + +static bool s_initialized = false; +static bool s_scanInProgress; +static std::uint8_t s_currentChannel; +static CurrentScanStatus s_currentScanStatus; +static std::unordered_map s_scanStartedHandlers; +static std::unordered_map s_scanCompletedHandlers; +static std::unordered_map s_scanDiscoveryHandlers; + +void _scanAllChannels(); +void _scanCurrentChannel(); +void _iterateChannel(); +void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info); +void _evSTAStopped(arduino_event_id_t event, arduino_event_info_t info); + +bool WiFiScanManager::Init() { + if (s_initialized) { + ESP_LOGE(TAG, "WiFiScanManager::Init() called twice"); + return false; + } + + s_currentChannel = 0; + s_scanInProgress = false; + s_currentScanStatus = CurrentScanStatus::Idle; + s_scanStartedHandlers.clear(); + s_scanCompletedHandlers.clear(); + s_scanDiscoveryHandlers.clear(); + + WiFi.onEvent(_evScanCompleted, ARDUINO_EVENT_WIFI_SCAN_DONE); + WiFi.onEvent(_evSTAStopped, ARDUINO_EVENT_WIFI_STA_STOP); + WiFi.enableSTA(true); + + // Since currentChannel is 0, this will do a full scan + _scanCurrentChannel(); + + s_initialized = true; + + return true; +} + +bool WiFiScanManager::StartScan() { + if (s_currentChannel != 0) { + ESP_LOGE(TAG, "Cannot start scan: scan is already in progress"); + return false; + } + + WiFi.enableSTA(true); + _iterateChannel(); + + for (auto& it : s_scanStartedHandlers) { + it.second(); + } + + return true; +} +void WiFiScanManager::CancelScan() { + if (s_currentChannel == 0) { + ESP_LOGE(TAG, "Cannot cancel scan: no scan is in progress"); + return; + } + + WiFi.scanDelete(); + s_currentChannel = 0; + + for (auto& it : s_scanCompletedHandlers) { + it.second(WiFiScanManager::ScanCompletedStatus::Cancelled); + } +} + +WiFiScanManager::CallbackHandle WiFiScanManager::RegisterScanStartedHandler(const WiFiScanManager::ScanStartedHandler& handler) { + static WiFiScanManager::CallbackHandle nextId = 0; + WiFiScanManager::CallbackHandle CallbackHandle = nextId++; + s_scanStartedHandlers[CallbackHandle] = handler; + return CallbackHandle; +} +void WiFiScanManager::UnregisterScanStartedHandler(WiFiScanManager::CallbackHandle id) { + auto it = s_scanStartedHandlers.find(id); + if (it == s_scanStartedHandlers.end()) { + ESP_LOGE(TAG, "Cannot unregister scan handler: no handler with ID %u", id); + return; + } + + s_scanStartedHandlers.erase(it); +} + +WiFiScanManager::CallbackHandle WiFiScanManager::RegisterScanCompletedHandler(const WiFiScanManager::ScanCompletedHandler& handler) { + static WiFiScanManager::CallbackHandle nextId = 0; + WiFiScanManager::CallbackHandle CallbackHandle = nextId++; + s_scanCompletedHandlers[CallbackHandle] = handler; + return CallbackHandle; +} +void WiFiScanManager::UnregisterScanCompletedHandler(WiFiScanManager::CallbackHandle id) { + auto it = s_scanCompletedHandlers.find(id); + if (it == s_scanCompletedHandlers.end()) { + ESP_LOGE(TAG, "Cannot unregister scan handler: no handler with ID %u", id); + return; + } + + s_scanCompletedHandlers.erase(it); +} + +WiFiScanManager::CallbackHandle WiFiScanManager::RegisterScanDiscoveryHandler(const WiFiScanManager::ScanDiscoveryHandler& handler) { + static WiFiScanManager::CallbackHandle nextId = 0; + WiFiScanManager::CallbackHandle CallbackHandle = nextId++; + s_scanDiscoveryHandlers[CallbackHandle] = handler; + return CallbackHandle; +} +void WiFiScanManager::UnregisterScanDiscoveryHandler(WiFiScanManager::CallbackHandle id) { + auto it = s_scanDiscoveryHandlers.find(id); + if (it == s_scanDiscoveryHandlers.end()) { + ESP_LOGE(TAG, "Cannot unregister scan handler: no handler with ID %u", id); + return; + } + + s_scanDiscoveryHandlers.erase(it); +} + +void WiFiScanManager::Update() { + if (!s_initialized) return; + + if (s_currentScanStatus == CurrentScanStatus::Starting) { + s_currentChannel = 0; + _iterateChannel(); + } + else if (s_currentScanStatus == CurrentScanStatus::Complete) { + _iterateChannel(); + } +} + +void _handleScanFinished() { + s_currentChannel = 0; + s_scanInProgress = false; + s_currentScanStatus = CurrentScanStatus::Idle; + ESP_LOGD(TAG, "Scan finished"); + for (auto& it : s_scanCompletedHandlers) { + it.second(WiFiScanManager::ScanCompletedStatus::Success); + } +} +void _handleScanError(std::int16_t retval) { + if (retval >= 0) return; // This isn't an error + + if (retval == WIFI_SCAN_RUNNING) { + ESP_LOGE(TAG, "Scan is still running"); + return; + } + + s_currentScanStatus = CurrentScanStatus::Error; + if (retval == WIFI_SCAN_FAILED) { + ESP_LOGE(TAG, "Failed to start scan on channel %u", s_currentChannel); + CaptivePortal::Start(); + for (auto& it : s_scanCompletedHandlers) { + it.second(WiFiScanManager::ScanCompletedStatus::Error); + } + return; + } + + ESP_LOGE(TAG, "Scan returned an unknown error"); +} +void _scanAllChannels() { + s_currentChannel = 0; + ESP_LOGD(TAG, "Scanning entire WiFi spectrum..."); + WiFi.scanNetworks(true, true, false, OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL); +} +void _scanCurrentChannel() { + std::int16_t retval = WiFi.scanNetworks(true, true, false, OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL, s_currentChannel); + s_scanInProgress = retval == WIFI_SCAN_RUNNING; + if (s_scanInProgress) { + s_currentScanStatus = CurrentScanStatus::Running; + ESP_LOGD(TAG, "Scanning channel %u", s_currentChannel); + return; + } + if (retval >= 0) { + s_currentScanStatus = CurrentScanStatus::Complete; + return; + } + + _handleScanError(retval); +} +void _iterateChannel() { + if (s_currentChannel >= 14) { + _handleScanFinished(); + return; + } + + s_currentChannel++; + _scanCurrentChannel(); +} +void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info) { + std::uint16_t numNetworks = WiFi.scanComplete(); + if (numNetworks < 0) { + _handleScanError(numNetworks); + return; + } + + ESP_LOGD(TAG, "Scan on channel %u complete, found %u networks", s_currentChannel, numNetworks); + for (std::uint16_t i = 0; i < numNetworks; i++) { + wifi_ap_record_t* record = reinterpret_cast(WiFi.getScanInfoByIndex(i)); + if (record == nullptr) { + ESP_LOGE(TAG, "Failed to get scan info for network #%u", i); + return; + } + + for (auto& it : s_scanDiscoveryHandlers) { + it.second(record); + } + } +} +void _evSTAStopped(arduino_event_id_t event, arduino_event_info_t info) { + // TODO: CLEAR RESULTS +} diff --git a/src/WiFiState.cpp b/src/WiFiState.cpp new file mode 100644 index 00000000..40bd912e --- /dev/null +++ b/src/WiFiState.cpp @@ -0,0 +1,15 @@ +#include "WiFiState.h" + +#include "VisualStateManager.h" + +static OpenShock::WiFiState s_wifiState = OpenShock::WiFiState::Disconnected; + +OpenShock::WiFiState OpenShock::GetWiFiState() noexcept { + return s_wifiState; +} +void OpenShock::SetWiFiState(WiFiState state) noexcept { + if (s_wifiState == state) return; + + s_wifiState = state; + VisualStateManager::SetWiFiState(state); +} diff --git a/src/main.cpp b/src/main.cpp index 293fe2b5..c1217772 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include "FileUtils.h" #include "SerialInputHandler.h" #include "WiFiManager.h" +#include "WifiScanManager.h" #include #include @@ -33,6 +34,11 @@ void setup() { delay(5000); ESP.restart(); } + if (!OpenShock::WiFiScanManager::Init()) { + ESP_LOGE(TAG, "PANIC: An Error has occurred while initializing WifiScanManager, restarting in 5 seconds..."); + delay(5000); + ESP.restart(); + } } void loop() { @@ -42,4 +48,6 @@ void loop() { if (s_apiConnection != nullptr) { s_apiConnection->Update(); } + + OpenShock::WiFiScanManager::Update(); } From 2a79a25daf29cb321a879bb8ab7e64b2c27e4b59 Mon Sep 17 00:00:00 2001 From: hhvrc Date: Tue, 3 Oct 2023 21:09:53 +0200 Subject: [PATCH 04/22] Formatting and hexing --- .clang-format | 1 - include/Utils/HexUtils.h | 45 ++++++++++++++++++++++++++++++++++++++++ src/CaptivePortal.cpp | 31 +++++++++++++-------------- src/WiFiScanManager.cpp | 30 +++++++++++++-------------- 4 files changed, 75 insertions(+), 32 deletions(-) create mode 100644 include/Utils/HexUtils.h diff --git a/.clang-format b/.clang-format index 5c891058..91f7bc2d 100644 --- a/.clang-format +++ b/.clang-format @@ -167,7 +167,6 @@ SpaceBeforeParens: Custom SpaceBeforeParensOptions: AfterControlStatements: true AfterForeachMacros: false - AfterFunctionDeclationName: false AfterIfMacros: false AfterOverloadedOperator: false AfterRequiresInClause: false diff --git a/include/Utils/HexUtils.h b/include/Utils/HexUtils.h new file mode 100644 index 00000000..357ec4a2 --- /dev/null +++ b/include/Utils/HexUtils.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include +#include + +namespace OpenShock::HexUtils { + constexpr void ToHex(std::uint8_t data, char* output, bool upper = true) noexcept { + const char* hex = upper ? "0123456789ABCDEF" : "0123456789abcdef"; + output[0] = hex[data >> 4]; + output[1] = hex[data & 0x0F]; + } + + template + constexpr void ToHex(nonstd::span data, nonstd::span output, bool upper = true) noexcept { + for (std::size_t i = 0; i < data.size(); ++i) { + ToHex(data[i], &output[i * 2], upper); + } + } + template + constexpr std::array ToHex(nonstd::span data, bool upper = true) noexcept { + std::array output {}; + ToHex(data, output, upper); + output[N * 2] = '\0'; + return output; + } + + template + constexpr void ToHexMac(nonstd::span data, nonstd::span output, bool upper = true) noexcept { + const std::size_t Last = N - 1; + for (std::size_t i = 0; i < Last; ++i) { + ToHex(data[i], &output[i * 3], upper); + output[i * 3 + 2] = ':'; + } + ToHex(data[Last], &output[Last * 3], upper); + } + template + constexpr std::array ToHexMac(nonstd::span data, bool upper = true) noexcept { + std::array output {}; + ToHexMac(data, nonstd::span(output.data(), output.size() - 1), upper); + output[(N * 3) - 1] = '\0'; + return output; + } +} // namespace OpenShock::HexUtils diff --git a/src/CaptivePortal.cpp b/src/CaptivePortal.cpp index 7370289c..633a6b94 100644 --- a/src/CaptivePortal.cpp +++ b/src/CaptivePortal.cpp @@ -1,7 +1,9 @@ #include "CaptivePortal.h" #include "AuthenticationManager.h" +#include "Utils/HexUtils.h" #include "WiFiManager.h" +#include "Mappers/EspWiFiTypesMapper.h" #include "WiFiScanManager.h" #include @@ -72,35 +74,35 @@ bool CaptivePortal::Start() { s_webServices->webServer.onNotFound([](AsyncWebServerRequest* request) { request->send(404, "text/plain", "Not found"); }); s_webServices->webServer.begin(); - s_webServices->wifiScanStartedHandlerId = WiFiScanManager::RegisterScanStartedHandler([]() { + s_webServices->wifiScanStartedHandlerId = WiFiScanManager::RegisterScanStartedHandler([]() { ESP_LOGD(TAG, "WiFi scan started"); StaticJsonDocument<256> doc; - doc["type"] = "wifi"; + doc["type"] = "wifi"; doc["subject"] = "scan_started"; - doc["status"] = "ok"; + doc["status"] = "ok"; CaptivePortal::BroadcastMessageJSON(doc); }); s_webServices->wifiScanCompletedHandlerId = WiFiScanManager::RegisterScanCompletedHandler([](WiFiScanManager::ScanCompletedStatus status) { ESP_LOGD(TAG, "WiFi scan completed"); StaticJsonDocument<256> doc; - doc["type"] = "wifi"; + doc["type"] = "wifi"; doc["subject"] = "scan_completed"; - doc["status"] = "ok"; - //doc["status"] = EspWiFiTypesMapper::MapScanCompletedStatus(status); + doc["status"] = "ok"; + // doc["status"] = EspWiFiTypesMapper::MapScanCompletedStatus(status); CaptivePortal::BroadcastMessageJSON(doc); }); s_webServices->wifiScanDiscoveryHandlerId = WiFiScanManager::RegisterScanDiscoveryHandler([](const wifi_ap_record_t* record) { ESP_LOGD(TAG, "WiFi scan discovery"); StaticJsonDocument<256> doc; - doc["type"] = "wifi"; + doc["type"] = "wifi"; doc["subject"] = "scan_discovery"; - doc["status"] = "ok"; - doc["ssid"] = reinterpret_cast(record->ssid); - //doc["bssid"] = EspWiFiTypesMapper::MapBssid(record->bssid); + auto data = doc.createNestedObject("data"); + data["ssid"] = reinterpret_cast(record->ssid); + data["bssid"] = HexUtils::ToHexMac<6>(record->bssid).data(); + doc["rssi"] = record->rssi; doc["channel"] = record->primary; - doc["rssi"] = record->rssi; - //doc["auth"] = EspWiFiTypesMapper::MapAuthMode(record->authmode); - //doc["hidden"] = record->is_hidden; + doc["security"] = Mappers::GetWiFiAuthModeName(record->authmode); + CaptivePortal::BroadcastMessageJSON(doc); }); @@ -157,8 +159,7 @@ bool CaptivePortal::BroadcastMessageBIN(const std::uint8_t* data, std::size_t le } void handleWebSocketClientConnected(std::uint8_t socketId) { - ESP_LOGD( - TAG, "WebSocket client #%u connected from %s", socketId, s_webServices->socketServer.remoteIP(socketId).toString().c_str()); + ESP_LOGD(TAG, "WebSocket client #%u connected from %s", socketId, s_webServices->socketServer.remoteIP(socketId).toString().c_str()); } void handleWebSocketClientDisconnected(std::uint8_t socketId) { ESP_LOGD(TAG, "WebSocket client #%u disconnected", socketId); diff --git a/src/WiFiScanManager.cpp b/src/WiFiScanManager.cpp index 4f493e6f..50111cea 100644 --- a/src/WiFiScanManager.cpp +++ b/src/WiFiScanManager.cpp @@ -43,8 +43,8 @@ bool WiFiScanManager::Init() { return false; } - s_currentChannel = 0; - s_scanInProgress = false; + s_currentChannel = 0; + s_scanInProgress = false; s_currentScanStatus = CurrentScanStatus::Idle; s_scanStartedHandlers.clear(); s_scanCompletedHandlers.clear(); @@ -54,8 +54,7 @@ bool WiFiScanManager::Init() { WiFi.onEvent(_evSTAStopped, ARDUINO_EVENT_WIFI_STA_STOP); WiFi.enableSTA(true); - // Since currentChannel is 0, this will do a full scan - _scanCurrentChannel(); + _scanAllChannels(); s_initialized = true; @@ -92,9 +91,9 @@ void WiFiScanManager::CancelScan() { } WiFiScanManager::CallbackHandle WiFiScanManager::RegisterScanStartedHandler(const WiFiScanManager::ScanStartedHandler& handler) { - static WiFiScanManager::CallbackHandle nextId = 0; + static WiFiScanManager::CallbackHandle nextId = 0; WiFiScanManager::CallbackHandle CallbackHandle = nextId++; - s_scanStartedHandlers[CallbackHandle] = handler; + s_scanStartedHandlers[CallbackHandle] = handler; return CallbackHandle; } void WiFiScanManager::UnregisterScanStartedHandler(WiFiScanManager::CallbackHandle id) { @@ -108,9 +107,9 @@ void WiFiScanManager::UnregisterScanStartedHandler(WiFiScanManager::CallbackHand } WiFiScanManager::CallbackHandle WiFiScanManager::RegisterScanCompletedHandler(const WiFiScanManager::ScanCompletedHandler& handler) { - static WiFiScanManager::CallbackHandle nextId = 0; + static WiFiScanManager::CallbackHandle nextId = 0; WiFiScanManager::CallbackHandle CallbackHandle = nextId++; - s_scanCompletedHandlers[CallbackHandle] = handler; + s_scanCompletedHandlers[CallbackHandle] = handler; return CallbackHandle; } void WiFiScanManager::UnregisterScanCompletedHandler(WiFiScanManager::CallbackHandle id) { @@ -124,9 +123,9 @@ void WiFiScanManager::UnregisterScanCompletedHandler(WiFiScanManager::CallbackHa } WiFiScanManager::CallbackHandle WiFiScanManager::RegisterScanDiscoveryHandler(const WiFiScanManager::ScanDiscoveryHandler& handler) { - static WiFiScanManager::CallbackHandle nextId = 0; + static WiFiScanManager::CallbackHandle nextId = 0; WiFiScanManager::CallbackHandle CallbackHandle = nextId++; - s_scanDiscoveryHandlers[CallbackHandle] = handler; + s_scanDiscoveryHandlers[CallbackHandle] = handler; return CallbackHandle; } void WiFiScanManager::UnregisterScanDiscoveryHandler(WiFiScanManager::CallbackHandle id) { @@ -145,15 +144,14 @@ void WiFiScanManager::Update() { if (s_currentScanStatus == CurrentScanStatus::Starting) { s_currentChannel = 0; _iterateChannel(); - } - else if (s_currentScanStatus == CurrentScanStatus::Complete) { + } else if (s_currentScanStatus == CurrentScanStatus::Complete) { _iterateChannel(); } } void _handleScanFinished() { - s_currentChannel = 0; - s_scanInProgress = false; + s_currentChannel = 0; + s_scanInProgress = false; s_currentScanStatus = CurrentScanStatus::Idle; ESP_LOGD(TAG, "Scan finished"); for (auto& it : s_scanCompletedHandlers) { @@ -161,7 +159,7 @@ void _handleScanFinished() { } } void _handleScanError(std::int16_t retval) { - if (retval >= 0) return; // This isn't an error + if (retval >= 0) return; // This isn't an error if (retval == WIFI_SCAN_RUNNING) { ESP_LOGE(TAG, "Scan is still running"); @@ -187,7 +185,7 @@ void _scanAllChannels() { } void _scanCurrentChannel() { std::int16_t retval = WiFi.scanNetworks(true, true, false, OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL, s_currentChannel); - s_scanInProgress = retval == WIFI_SCAN_RUNNING; + s_scanInProgress = retval == WIFI_SCAN_RUNNING; if (s_scanInProgress) { s_currentScanStatus = CurrentScanStatus::Running; ESP_LOGD(TAG, "Scanning channel %u", s_currentChannel); From 1621a301df071ed3d58aee8fe107378109a8e211 Mon Sep 17 00:00:00 2001 From: hhvrc Date: Tue, 3 Oct 2023 22:41:48 +0200 Subject: [PATCH 05/22] Bugfixes >:c --- WebUI/src/lib/WebSocketMessageHandler.ts | 33 +++++--- WebUI/src/lib/stores/WiFiStateStore.ts | 6 ++ src/CaptivePortal.cpp | 26 +++--- src/WiFiScanManager.cpp | 102 +++++++++++++---------- 4 files changed, 99 insertions(+), 68 deletions(-) diff --git a/WebUI/src/lib/WebSocketMessageHandler.ts b/WebUI/src/lib/WebSocketMessageHandler.ts index 0db6dfa1..c01b000d 100644 --- a/WebUI/src/lib/WebSocketMessageHandler.ts +++ b/WebUI/src/lib/WebSocketMessageHandler.ts @@ -8,8 +8,8 @@ interface WiFiMessage { interface WiFiScanMessage extends WiFiMessage { subject: 'scan'; - status: 'started' | 'completed' | 'error'; - networks?: WiFiNetwork[]; + status: 'started' | 'discovery' | 'completed' | 'error'; + data?: WiFiNetwork; } interface InvalidMessage { @@ -47,24 +47,37 @@ function handleWiFiMessage(message: WiFiScanMessage) { } function handleWiFiScanMessage(message: WiFiScanMessage) { - let running: boolean; switch (message.status) { case 'started': - running = true; + handleWiFiScanStartedMessage(message); + break; + case 'discovery': + handleWiFiScanDiscoveryMessage(message); break; case 'completed': - running = false; + handleWiFiScanCompletedMessage(message); break; case 'error': - running = false; + handleWiFiScanErrorMessage(message); break; default: console.warn('[WS] Received invalid scan message: ', message); return; } +} - WiFiStateStore.setScanning(running); - if (message.networks) { - WiFiStateStore.setNetworks(message.networks); - } +function handleWiFiScanStartedMessage(message: WiFiScanMessage) { + WiFiStateStore.setScanning(true); +} + +function handleWiFiScanDiscoveryMessage(message: WiFiScanMessage) { + WiFiStateStore.addNetwork(message.data!); +} + +function handleWiFiScanCompletedMessage(message: WiFiScanMessage) { + WiFiStateStore.setScanning(false); +} + +function handleWiFiScanErrorMessage(message: WiFiScanMessage) { + WiFiStateStore.setScanning(false); } diff --git a/WebUI/src/lib/stores/WiFiStateStore.ts b/WebUI/src/lib/stores/WiFiStateStore.ts index d98043d3..b97714d3 100644 --- a/WebUI/src/lib/stores/WiFiStateStore.ts +++ b/WebUI/src/lib/stores/WiFiStateStore.ts @@ -29,6 +29,12 @@ export const WiFiStateStore = { return store; }); }, + addNetwork(network: WiFiNetwork) { + update((store) => { + store.networks = [...store.networks, network]; + return store; + }); + }, clearNetworks() { update((store) => { store.networks = []; diff --git a/src/CaptivePortal.cpp b/src/CaptivePortal.cpp index 633a6b94..8e0e92cd 100644 --- a/src/CaptivePortal.cpp +++ b/src/CaptivePortal.cpp @@ -1,9 +1,9 @@ #include "CaptivePortal.h" #include "AuthenticationManager.h" +#include "Mappers/EspWiFiTypesMapper.h" #include "Utils/HexUtils.h" #include "WiFiManager.h" -#include "Mappers/EspWiFiTypesMapper.h" #include "WiFiScanManager.h" #include @@ -78,16 +78,16 @@ bool CaptivePortal::Start() { ESP_LOGD(TAG, "WiFi scan started"); StaticJsonDocument<256> doc; doc["type"] = "wifi"; - doc["subject"] = "scan_started"; - doc["status"] = "ok"; + doc["subject"] = "scan"; + doc["status"] = "started"; CaptivePortal::BroadcastMessageJSON(doc); }); s_webServices->wifiScanCompletedHandlerId = WiFiScanManager::RegisterScanCompletedHandler([](WiFiScanManager::ScanCompletedStatus status) { ESP_LOGD(TAG, "WiFi scan completed"); StaticJsonDocument<256> doc; doc["type"] = "wifi"; - doc["subject"] = "scan_completed"; - doc["status"] = "ok"; + doc["subject"] = "scan"; + doc["status"] = "completed"; // doc["status"] = EspWiFiTypesMapper::MapScanCompletedStatus(status); CaptivePortal::BroadcastMessageJSON(doc); }); @@ -95,13 +95,15 @@ bool CaptivePortal::Start() { ESP_LOGD(TAG, "WiFi scan discovery"); StaticJsonDocument<256> doc; doc["type"] = "wifi"; - doc["subject"] = "scan_discovery"; - auto data = doc.createNestedObject("data"); - data["ssid"] = reinterpret_cast(record->ssid); - data["bssid"] = HexUtils::ToHexMac<6>(record->bssid).data(); - doc["rssi"] = record->rssi; - doc["channel"] = record->primary; - doc["security"] = Mappers::GetWiFiAuthModeName(record->authmode); + doc["subject"] = "scan"; + doc["status"] = "discovery"; + + auto data = doc.createNestedObject("data"); + data["ssid"] = reinterpret_cast(record->ssid); + data["bssid"] = HexUtils::ToHexMac<6>(record->bssid).data(); + data["rssi"] = record->rssi; + data["channel"] = record->primary; + data["security"] = Mappers::GetWiFiAuthModeName(record->authmode); CaptivePortal::BroadcastMessageJSON(doc); }); diff --git a/src/WiFiScanManager.cpp b/src/WiFiScanManager.cpp index 50111cea..ebfb1061 100644 --- a/src/WiFiScanManager.cpp +++ b/src/WiFiScanManager.cpp @@ -10,7 +10,8 @@ const char* const TAG = "WiFiScanManager"; -constexpr const std::size_t OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL = 150; +constexpr const std::uint8_t OPENSHOCK_WIFI_SCAN_MAX_CHANNELS = 14; +constexpr const std::uint32_t OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL = 300; // Adjusting this value will affect the scan rate, but may also affect the scan results using namespace OpenShock; @@ -23,17 +24,41 @@ enum class CurrentScanStatus { Error, }; -static bool s_initialized = false; -static bool s_scanInProgress; -static std::uint8_t s_currentChannel; -static CurrentScanStatus s_currentScanStatus; +static bool s_initialized = false; +static bool s_scanInProgress = false; +static std::uint8_t s_currentChannel = 0; +static CurrentScanStatus s_currentScanStatus = CurrentScanStatus::Idle; static std::unordered_map s_scanStartedHandlers; static std::unordered_map s_scanCompletedHandlers; static std::unordered_map s_scanDiscoveryHandlers; -void _scanAllChannels(); -void _scanCurrentChannel(); +bool _isScanInProgress() { + return s_scanInProgress; +} +void _setScanInProgress(bool inProgress) { + if (s_scanInProgress != inProgress) { + s_scanInProgress = inProgress; + if (inProgress) { + ESP_LOGD(TAG, "Scan started"); + for (auto& it : s_scanStartedHandlers) { + it.second(); + } + } else { + ESP_LOGD(TAG, "Scan completed"); + for (auto& it : s_scanCompletedHandlers) { + it.second(WiFiScanManager::ScanCompletedStatus::Success); + } + } + } + + if (!inProgress) { + s_currentChannel = 0; + s_currentScanStatus = CurrentScanStatus::Idle; + } +} + void _iterateChannel(); +void _setScanInProgress(bool inProgress); void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info); void _evSTAStopped(arduino_event_id_t event, arduino_event_info_t info); @@ -43,18 +68,8 @@ bool WiFiScanManager::Init() { return false; } - s_currentChannel = 0; - s_scanInProgress = false; - s_currentScanStatus = CurrentScanStatus::Idle; - s_scanStartedHandlers.clear(); - s_scanCompletedHandlers.clear(); - s_scanDiscoveryHandlers.clear(); - WiFi.onEvent(_evScanCompleted, ARDUINO_EVENT_WIFI_SCAN_DONE); WiFi.onEvent(_evSTAStopped, ARDUINO_EVENT_WIFI_STA_STOP); - WiFi.enableSTA(true); - - _scanAllChannels(); s_initialized = true; @@ -82,12 +97,7 @@ void WiFiScanManager::CancelScan() { return; } - WiFi.scanDelete(); - s_currentChannel = 0; - - for (auto& it : s_scanCompletedHandlers) { - it.second(WiFiScanManager::ScanCompletedStatus::Cancelled); - } + // TODO: implement this } WiFiScanManager::CallbackHandle WiFiScanManager::RegisterScanStartedHandler(const WiFiScanManager::ScanStartedHandler& handler) { @@ -141,24 +151,23 @@ void WiFiScanManager::UnregisterScanDiscoveryHandler(WiFiScanManager::CallbackHa void WiFiScanManager::Update() { if (!s_initialized) return; - if (s_currentScanStatus == CurrentScanStatus::Starting) { - s_currentChannel = 0; - _iterateChannel(); - } else if (s_currentScanStatus == CurrentScanStatus::Complete) { - _iterateChannel(); + switch (s_currentScanStatus) { + case CurrentScanStatus::Starting: + s_currentChannel = 0; + case CurrentScanStatus::Error: + case CurrentScanStatus::Cancelled: + case CurrentScanStatus::Complete: + _iterateChannel(); + break; + [[likely]] case CurrentScanStatus::Idle: + [[likely]] case CurrentScanStatus::Running: + default: + break; } } -void _handleScanFinished() { - s_currentChannel = 0; - s_scanInProgress = false; - s_currentScanStatus = CurrentScanStatus::Idle; - ESP_LOGD(TAG, "Scan finished"); - for (auto& it : s_scanCompletedHandlers) { - it.second(WiFiScanManager::ScanCompletedStatus::Success); - } -} void _handleScanError(std::int16_t retval) { + ESP_LOGE(TAG, "Scan failed with error %d", retval); if (retval >= 0) return; // This isn't an error if (retval == WIFI_SCAN_RUNNING) { @@ -178,15 +187,12 @@ void _handleScanError(std::int16_t retval) { ESP_LOGE(TAG, "Scan returned an unknown error"); } -void _scanAllChannels() { - s_currentChannel = 0; - ESP_LOGD(TAG, "Scanning entire WiFi spectrum..."); - WiFi.scanNetworks(true, true, false, OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL); -} void _scanCurrentChannel() { + ESP_LOGD(TAG, "Starting scan on channel %u", s_currentChannel); std::int16_t retval = WiFi.scanNetworks(true, true, false, OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL, s_currentChannel); - s_scanInProgress = retval == WIFI_SCAN_RUNNING; - if (s_scanInProgress) { + + _setScanInProgress(retval == WIFI_SCAN_RUNNING); + if (_isScanInProgress()) { s_currentScanStatus = CurrentScanStatus::Running; ESP_LOGD(TAG, "Scanning channel %u", s_currentChannel); return; @@ -199,8 +205,8 @@ void _scanCurrentChannel() { _handleScanError(retval); } void _iterateChannel() { - if (s_currentChannel >= 14) { - _handleScanFinished(); + if (s_currentChannel >= OPENSHOCK_WIFI_SCAN_MAX_CHANNELS) { + _setScanInProgress(false); return; } @@ -223,10 +229,14 @@ void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info) { } for (auto& it : s_scanDiscoveryHandlers) { + ESP_LOGD(TAG, "Calling scan discovery handler"); it.second(record); } } + + s_currentScanStatus = CurrentScanStatus::Complete; } void _evSTAStopped(arduino_event_id_t event, arduino_event_info_t info) { + ESP_LOGD(TAG, "STA stopped"); // TODO: CLEAR RESULTS } From 3abda59d2802079af9d2506e873f759266083835 Mon Sep 17 00:00:00 2001 From: hhvrc Date: Wed, 4 Oct 2023 00:55:53 +0200 Subject: [PATCH 06/22] Fixed network scan times, buggy UI, and WS disconnects --- WebUI/src/lib/WebSocketMessageHandler.ts | 47 +++++--- WebUI/src/lib/components/WiFiList.svelte | 11 +- WebUI/src/lib/stores/WiFiStateStore.ts | 13 +-- WebUI/src/lib/types/WiFiState.ts | 4 +- src/WiFiScanManager.cpp | 142 ++++++++--------------- 5 files changed, 95 insertions(+), 122 deletions(-) diff --git a/WebUI/src/lib/WebSocketMessageHandler.ts b/WebUI/src/lib/WebSocketMessageHandler.ts index c01b000d..b8495f5a 100644 --- a/WebUI/src/lib/WebSocketMessageHandler.ts +++ b/WebUI/src/lib/WebSocketMessageHandler.ts @@ -1,22 +1,38 @@ import { WiFiStateStore } from './stores'; import type { WiFiNetwork } from './types/WiFiNetwork'; -interface WiFiMessage { +interface InvalidMessage { + type: undefined | null; +} + +interface WiFiScanStartedMessage { type: 'wifi'; subject: 'scan'; + status: 'started'; } -interface WiFiScanMessage extends WiFiMessage { +interface WiFiScanDiscoveryMessage { + type: 'wifi'; subject: 'scan'; - status: 'started' | 'discovery' | 'completed' | 'error'; - data?: WiFiNetwork; + status: 'discovery'; + data: WiFiNetwork; } -interface InvalidMessage { - type: undefined | null; +interface WiFiScanCompletedMessage { + type: 'wifi'; + subject: 'scan'; + status: 'completed'; +} + +interface WiFiScanErrorMessage { + type: 'wifi'; + subject: 'scan'; + status: 'error'; } -export type WebSocketMessage = InvalidMessage | WiFiScanMessage; +export type WiFiScanMessage = WiFiScanStartedMessage | WiFiScanDiscoveryMessage | WiFiScanCompletedMessage | WiFiScanErrorMessage; +export type WiFiMessage = WiFiScanMessage; +export type WebSocketMessage = InvalidMessage | WiFiMessage; export function WebSocketMessageHandler(message: WebSocketMessage) { const type = message.type; @@ -35,7 +51,7 @@ export function WebSocketMessageHandler(message: WebSocketMessage) { } } -function handleWiFiMessage(message: WiFiScanMessage) { +function handleWiFiMessage(message: WiFiMessage) { switch (message.subject) { case 'scan': handleWiFiScanMessage(message); @@ -49,13 +65,13 @@ function handleWiFiMessage(message: WiFiScanMessage) { function handleWiFiScanMessage(message: WiFiScanMessage) { switch (message.status) { case 'started': - handleWiFiScanStartedMessage(message); + handleWiFiScanStartedMessage(); break; case 'discovery': handleWiFiScanDiscoveryMessage(message); break; case 'completed': - handleWiFiScanCompletedMessage(message); + handleWiFiScanCompletedMessage(); break; case 'error': handleWiFiScanErrorMessage(message); @@ -66,18 +82,19 @@ function handleWiFiScanMessage(message: WiFiScanMessage) { } } -function handleWiFiScanStartedMessage(message: WiFiScanMessage) { +function handleWiFiScanStartedMessage() { WiFiStateStore.setScanning(true); } -function handleWiFiScanDiscoveryMessage(message: WiFiScanMessage) { - WiFiStateStore.addNetwork(message.data!); +function handleWiFiScanDiscoveryMessage(message: WiFiScanDiscoveryMessage) { + WiFiStateStore.addNetwork(message.data); } -function handleWiFiScanCompletedMessage(message: WiFiScanMessage) { +function handleWiFiScanCompletedMessage() { WiFiStateStore.setScanning(false); } -function handleWiFiScanErrorMessage(message: WiFiScanMessage) { +function handleWiFiScanErrorMessage(message: WiFiScanErrorMessage) { + console.error('[WS] Received WiFi scan error message: ', message); WiFiStateStore.setScanning(false); } diff --git a/WebUI/src/lib/components/WiFiList.svelte b/WebUI/src/lib/components/WiFiList.svelte index 978382fa..39d6c32b 100644 --- a/WebUI/src/lib/components/WiFiList.svelte +++ b/WebUI/src/lib/components/WiFiList.svelte @@ -10,7 +10,11 @@ let connectedBSSID: string | null = null; function wifiScan() { - WebSocketClient.Instance.Send('{ "type": "wifi", "action": "scan", "run": true }'); + if ($WiFiStateStore.scanning) { + WebSocketClient.Instance.Send('{ "type": "wifi", "action": "scan", "run": false }'); + } else { + WebSocketClient.Instance.Send('{ "type": "wifi", "action": "scan", "run": true }'); + } } function wifiAuthenticate(item: WiFiNetwork) { if (item.security !== 'Open') { @@ -40,7 +44,6 @@ component: { ref: WiFiInfo, props: { bssid: item.bssid }, - slot: '

Skeleton

', }, }); } @@ -49,7 +52,7 @@

Configure WiFi

-
- {#each $WiFiStateStore.networks as item (item.bssid)} + {#each Object.values($WiFiStateStore.networks) as item (item.bssid)}
{#if item.bssid === connectedBSSID} diff --git a/WebUI/src/lib/stores/WiFiStateStore.ts b/WebUI/src/lib/stores/WiFiStateStore.ts index b97714d3..b3b5c69e 100644 --- a/WebUI/src/lib/stores/WiFiStateStore.ts +++ b/WebUI/src/lib/stores/WiFiStateStore.ts @@ -5,7 +5,7 @@ import { writable } from 'svelte/store'; const { subscribe, update } = writable({ initialized: false, scanning: false, - networks: [], + networks: {}, }); export const WiFiStateStore = { @@ -22,22 +22,15 @@ export const WiFiStateStore = { return store; }); }, - setNetworks(networks: WiFiNetwork[]) { - update((store) => { - store.scanning = false; - store.networks = networks; - return store; - }); - }, addNetwork(network: WiFiNetwork) { update((store) => { - store.networks = [...store.networks, network]; + store.networks[network.bssid] = network; return store; }); }, clearNetworks() { update((store) => { - store.networks = []; + store.networks = {}; return store; }); }, diff --git a/WebUI/src/lib/types/WiFiState.ts b/WebUI/src/lib/types/WiFiState.ts index a78d900a..909be83d 100644 --- a/WebUI/src/lib/types/WiFiState.ts +++ b/WebUI/src/lib/types/WiFiState.ts @@ -1,7 +1,7 @@ -import type { WiFiNetwork } from "./WiFiNetwork"; +import type { WiFiNetwork } from './WiFiNetwork'; export type WiFiState = { initialized: boolean; scanning: boolean; - networks: WiFiNetwork[]; + networks: { [bssid: string]: WiFiNetwork }; }; diff --git a/src/WiFiScanManager.cpp b/src/WiFiScanManager.cpp index ebfb1061..cb19d49e 100644 --- a/src/WiFiScanManager.cpp +++ b/src/WiFiScanManager.cpp @@ -2,39 +2,27 @@ #include "CaptivePortal.h" -#include - #include +#include + #include const char* const TAG = "WiFiScanManager"; -constexpr const std::uint8_t OPENSHOCK_WIFI_SCAN_MAX_CHANNELS = 14; +constexpr const std::uint8_t OPENSHOCK_WIFI_SCAN_MAX_CHANNEL = 13; constexpr const std::uint32_t OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL = 300; // Adjusting this value will affect the scan rate, but may also affect the scan results using namespace OpenShock; -enum class CurrentScanStatus { - Idle, - Starting, - Running, - Complete, - Cancelled, - Error, -}; - -static bool s_initialized = false; -static bool s_scanInProgress = false; -static std::uint8_t s_currentChannel = 0; -static CurrentScanStatus s_currentScanStatus = CurrentScanStatus::Idle; +static bool s_initialized = false; +static bool s_scanInProgress = false; +static bool s_channelScanDone = false; +static std::uint8_t s_currentChannel = 0; static std::unordered_map s_scanStartedHandlers; static std::unordered_map s_scanCompletedHandlers; static std::unordered_map s_scanDiscoveryHandlers; -bool _isScanInProgress() { - return s_scanInProgress; -} void _setScanInProgress(bool inProgress) { if (s_scanInProgress != inProgress) { s_scanInProgress = inProgress; @@ -43,6 +31,7 @@ void _setScanInProgress(bool inProgress) { for (auto& it : s_scanStartedHandlers) { it.second(); } + WiFi.scanDelete(); } else { ESP_LOGD(TAG, "Scan completed"); for (auto& it : s_scanCompletedHandlers) { @@ -52,13 +41,45 @@ void _setScanInProgress(bool inProgress) { } if (!inProgress) { - s_currentChannel = 0; - s_currentScanStatus = CurrentScanStatus::Idle; + s_currentChannel = 0; + s_channelScanDone = false; } } -void _iterateChannel(); -void _setScanInProgress(bool inProgress); +void _handleScanError(std::int16_t retval) { + s_channelScanDone = true; + + if (retval == WIFI_SCAN_FAILED) { + ESP_LOGE(TAG, "Failed to start scan on channel %u", s_currentChannel); + CaptivePortal::Start(); + for (auto& it : s_scanCompletedHandlers) { + it.second(WiFiScanManager::ScanCompletedStatus::Error); + } + return; + } + + ESP_LOGE(TAG, "Scan returned an unknown error"); +} + +void _iterateChannel() { + if (s_currentChannel-- <= 1) { + s_currentChannel = 0; + _setScanInProgress(false); + return; + } + + s_channelScanDone = false; + + std::int16_t retval = WiFi.scanNetworks(true, true, false, OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL, s_currentChannel); + + if (retval == WIFI_SCAN_RUNNING) { + _setScanInProgress(true); + return; + } + + _handleScanError(retval); +} + void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info); void _evSTAStopped(arduino_event_id_t event, arduino_event_info_t info); @@ -77,27 +98,24 @@ bool WiFiScanManager::Init() { } bool WiFiScanManager::StartScan() { - if (s_currentChannel != 0) { + if (s_scanInProgress) { ESP_LOGE(TAG, "Cannot start scan: scan is already in progress"); return false; } WiFi.enableSTA(true); + s_currentChannel = OPENSHOCK_WIFI_SCAN_MAX_CHANNEL; _iterateChannel(); - for (auto& it : s_scanStartedHandlers) { - it.second(); - } - return true; } void WiFiScanManager::CancelScan() { - if (s_currentChannel == 0) { + if (!s_scanInProgress) { ESP_LOGE(TAG, "Cannot cancel scan: no scan is in progress"); return; } - // TODO: implement this + s_currentChannel = 0; } WiFiScanManager::CallbackHandle WiFiScanManager::RegisterScanStartedHandler(const WiFiScanManager::ScanStartedHandler& handler) { @@ -151,68 +169,11 @@ void WiFiScanManager::UnregisterScanDiscoveryHandler(WiFiScanManager::CallbackHa void WiFiScanManager::Update() { if (!s_initialized) return; - switch (s_currentScanStatus) { - case CurrentScanStatus::Starting: - s_currentChannel = 0; - case CurrentScanStatus::Error: - case CurrentScanStatus::Cancelled: - case CurrentScanStatus::Complete: - _iterateChannel(); - break; - [[likely]] case CurrentScanStatus::Idle: - [[likely]] case CurrentScanStatus::Running: - default: - break; + if (s_scanInProgress && s_channelScanDone) { + _iterateChannel(); } } -void _handleScanError(std::int16_t retval) { - ESP_LOGE(TAG, "Scan failed with error %d", retval); - if (retval >= 0) return; // This isn't an error - - if (retval == WIFI_SCAN_RUNNING) { - ESP_LOGE(TAG, "Scan is still running"); - return; - } - - s_currentScanStatus = CurrentScanStatus::Error; - if (retval == WIFI_SCAN_FAILED) { - ESP_LOGE(TAG, "Failed to start scan on channel %u", s_currentChannel); - CaptivePortal::Start(); - for (auto& it : s_scanCompletedHandlers) { - it.second(WiFiScanManager::ScanCompletedStatus::Error); - } - return; - } - - ESP_LOGE(TAG, "Scan returned an unknown error"); -} -void _scanCurrentChannel() { - ESP_LOGD(TAG, "Starting scan on channel %u", s_currentChannel); - std::int16_t retval = WiFi.scanNetworks(true, true, false, OPENSHOCK_WIFI_SCAN_MAX_MS_PER_CHANNEL, s_currentChannel); - - _setScanInProgress(retval == WIFI_SCAN_RUNNING); - if (_isScanInProgress()) { - s_currentScanStatus = CurrentScanStatus::Running; - ESP_LOGD(TAG, "Scanning channel %u", s_currentChannel); - return; - } - if (retval >= 0) { - s_currentScanStatus = CurrentScanStatus::Complete; - return; - } - - _handleScanError(retval); -} -void _iterateChannel() { - if (s_currentChannel >= OPENSHOCK_WIFI_SCAN_MAX_CHANNELS) { - _setScanInProgress(false); - return; - } - - s_currentChannel++; - _scanCurrentChannel(); -} void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info) { std::uint16_t numNetworks = WiFi.scanComplete(); if (numNetworks < 0) { @@ -220,7 +181,6 @@ void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info) { return; } - ESP_LOGD(TAG, "Scan on channel %u complete, found %u networks", s_currentChannel, numNetworks); for (std::uint16_t i = 0; i < numNetworks; i++) { wifi_ap_record_t* record = reinterpret_cast(WiFi.getScanInfoByIndex(i)); if (record == nullptr) { @@ -234,7 +194,7 @@ void _evScanCompleted(arduino_event_id_t event, arduino_event_info_t info) { } } - s_currentScanStatus = CurrentScanStatus::Complete; + s_channelScanDone = true; } void _evSTAStopped(arduino_event_id_t event, arduino_event_info_t info) { ESP_LOGD(TAG, "STA stopped"); From 38ca5ab8229ddbce75ff6ce45ca9be847b25d7bf Mon Sep 17 00:00:00 2001 From: hhvrc Date: Wed, 4 Oct 2023 01:00:06 +0200 Subject: [PATCH 07/22] Literally F --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index c1217772..c7609143 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,7 @@ #include "FileUtils.h" #include "SerialInputHandler.h" #include "WiFiManager.h" -#include "WifiScanManager.h" +#include "WiFiScanManager.h" #include #include From 01c3e84df5115c2324f4b5fd22e833f14ed3b974 Mon Sep 17 00:00:00 2001 From: hhvrc Date: Wed, 4 Oct 2023 01:21:13 +0200 Subject: [PATCH 08/22] Fix WiFiDetails lookup --- WebUI/src/lib/components/WiFiList.svelte | 2 +- .../components/modals/{WiFiInfo.svelte => WiFiDetails.svelte} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename WebUI/src/lib/components/modals/{WiFiInfo.svelte => WiFiDetails.svelte} (96%) diff --git a/WebUI/src/lib/components/WiFiList.svelte b/WebUI/src/lib/components/WiFiList.svelte index 39d6c32b..eeb8a9a3 100644 --- a/WebUI/src/lib/components/WiFiList.svelte +++ b/WebUI/src/lib/components/WiFiList.svelte @@ -1,6 +1,6 @@