From f9c9e7932ca61de1e79328fafa5857a4ad9e33c9 Mon Sep 17 00:00:00 2001 From: Andrew Dunai Date: Tue, 12 Mar 2024 22:43:07 +0200 Subject: [PATCH] keira: WIP on WiFi config app --- firmware/keira/src/apps/demos/wifi_scan.cpp | 73 ++++++++++-- firmware/keira/src/services/network.cpp | 121 +++++++++++++------- firmware/keira/src/services/network.h | 12 +- sdk/lib/lilka/src/lilka/ui.cpp | 8 ++ sdk/lib/lilka/src/lilka/ui.h | 6 + 5 files changed, 161 insertions(+), 59 deletions(-) diff --git a/firmware/keira/src/apps/demos/wifi_scan.cpp b/firmware/keira/src/apps/demos/wifi_scan.cpp index 8e5725c6..a9f314a1 100644 --- a/firmware/keira/src/apps/demos/wifi_scan.cpp +++ b/firmware/keira/src/apps/demos/wifi_scan.cpp @@ -1,6 +1,8 @@ #include #include "wifi_scan.h" +#include "servicemanager.h" +#include "services/network.h" WifiScanApp::WifiScanApp() : App("WiFi Scanner") { } @@ -35,24 +37,71 @@ void WifiScanApp::run() { networks[i] = WiFi.SSID(i); } - // TODO: FreeRTOS experiment - // lilka::ui_menu(canvas, "Мережі", networks, count, 0); lilka::Menu menu("Мережі"); for (int16_t i = 0; i < count; i++) { menu.addItem(networks[i]); } - menu.draw(canvas); - queueDraw(); + menu.addItem("<< Назад"); + count++; while (1) { - menu.update(); - menu.draw(canvas); + int index = -1; + while (index == -1) { + menu.update(); + menu.draw(canvas); + queueDraw(); + index = menu.getSelectedIndex(); + } + if (index == count - 1) { + return; + } + + String ssid = networks[index]; + + // Attempt to connect to the selected network + NetworkService* networkService = (NetworkService*)ServiceManager::getInstance()->getService(); + // TODO: assert networkService != nullptr + if (!networkService->connect(ssid)) { + // Password required + lilka::InputDialog passwordDialog("Введіть пароль:"); + passwordDialog.setMasked(true); + while (!passwordDialog.isDone()) { + passwordDialog.update(); + passwordDialog.draw(canvas); + queueDraw(); + } + String password = passwordDialog.getValue(); + networkService->connect(ssid, password); + } + + buffer.fillScreen(buffer.color565(0, 0, 0)); + buffer.setCursor(4, 150); + buffer.println("Під'єднуємось..."); + canvas->drawCanvas(&buffer); queueDraw(); - if (menu.getSelectedIndex() != -1) { - break; + + // Wait for the network to connect or fail + while (networkService->getNetworkState() == NETWORK_STATE_CONNECTING) { + taskYIELD(); } - } - // while (!lilka::controller.getState().a.justPressed) { - // delay(10); - // } + lilka::Alert alert("", ""); + bool success = networkService->getNetworkState() == NETWORK_STATE_ONLINE; + if (success) { + alert.setTitle("Успіх"); + alert.setMessage("Під'єднано до мережі " + ssid); + } else { + alert.setTitle("Помилка"); + alert.setMessage("Не вдалося під'єднатись до мережі " + ssid); + } + + alert.draw(canvas); + queueDraw(); + while (!alert.isDone()) { + alert.update(); + } + + if (success) { + return; + } + } } diff --git a/firmware/keira/src/services/network.cpp b/firmware/keira/src/services/network.cpp index b48a1e81..a183ad10 100644 --- a/firmware/keira/src/services/network.cpp +++ b/firmware/keira/src/services/network.cpp @@ -2,90 +2,129 @@ #include "network.h" +// EEPROM preferences used: +// - network.last_ssid - last connected SSID +// - network.[SSID]_password - password of known network with a given SSID + NetworkService::NetworkService() : Service("network") { state = NETWORK_STATE_OFFLINE; - _signalStrength = 0; + signalStrength = 0; + mutex = xSemaphoreCreateMutex(); } void NetworkService::run() { - ssid = ""; - password = ""; - Preferences prefs; + + // Check if there is a known network to connect to prefs.begin("network", true); - if (!prefs.isKey("ssid")) { - Serial.println("network worker: no SSID found in preferences"); - prefs.end(); - return; + if (!prefs.isKey("last_ssid")) { + Serial.println("network worker: no last SSID found, skipping startup connection"); + } else { + String currentSSID = prefs.getString("last_ssid"); + String currentPassword = prefs.getString(String(currentSSID + "_password").c_str()); + connect(currentSSID, currentPassword); } - ssid = prefs.getString("ssid"); - password = prefs.getString("password"); prefs.end(); WiFi.onEvent([this](WiFiEvent_t event, WiFiEventInfo_t info) { switch (event) { - case ARDUINO_EVENT_WIFI_STA_START: + case ARDUINO_EVENT_WIFI_STA_START: { Serial.println("network worker: connecting to WiFi"); state = NETWORK_STATE_CONNECTING; break; - case ARDUINO_EVENT_WIFI_STA_CONNECTED: + } + case ARDUINO_EVENT_WIFI_STA_CONNECTED: { Serial.println("network worker: connected to WiFi"); state = NETWORK_STATE_ONLINE; - // Get SSID from info - // char ssid[33]; - // memcpy(ssid, info.wifi_sta_connected.ssid, info.wifi_sta_connected.ssid_len); + Preferences prefs; + String connectedSSID = String(info.wifi_sta_connected.ssid, info.wifi_sta_connected.ssid_len); + prefs.begin("network", true); + if (!String(prefs.getString("last_ssids")).equals(connectedSSID)) { + // Set current SSID as last connected + prefs.putString("last_ssid", String(connectedSSID)); + Serial.println("network worker: last SSID set to " + connectedSSID); + } + if (!prefs.isKey(String(connectedSSID + "_password").c_str())) { + // Save password for the connected network + prefs.putString(String(connectedSSID + "_password").c_str(), connectedSSID); + Serial.println("network worker: password for " + connectedSSID + " saved"); + } + prefs.end(); break; - case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: + } + case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: { Serial.println("network worker: disconnected from WiFi"); state = NETWORK_STATE_OFFLINE; break; + } default: break; } }); - connect(); - while (1) { const int8_t rssi = WiFi.RSSI(); - const int8_t excellent = -50; - const int8_t good = -70; - const int8_t fair = -80; - - if (rssi >= excellent) { - _signalStrength = 3; - } else if (rssi >= good) { - _signalStrength = 2; - } else if (rssi >= fair) { - _signalStrength = 1; + if (rssi == 0) { + signalStrength = 0; } else { - _signalStrength = 0; + const int8_t excellent = -50; + const int8_t good = -70; + const int8_t fair = -80; + + if (rssi >= excellent) { + signalStrength = 3; + } else if (rssi >= good) { + signalStrength = 2; + } else if (rssi >= fair) { + signalStrength = 1; + } else { + signalStrength = 0; + } } vTaskDelay(1000 / portTICK_PERIOD_MS); } } -void NetworkService::configure(String ssid, String password) { - ssid = ssid; - password = password; - Preferences prefs; - prefs.begin("network", false); - prefs.putString("ssid", ssid); - prefs.putString("password", password); - prefs.end(); - connect(); -} +// bool NetworkService::connect(String ssid, String password) { +// ssid = ssid; +// password = password; +// Preferences prefs; +// prefs.begin("network", false); +// prefs.putString("ssid", ssid); +// prefs.putString("password", password); +// prefs.end(); +// connect(); +// } NetworkState NetworkService::getNetworkState() { return state; } int NetworkService::getSignalStrength() { - return _signalStrength; + return signalStrength; +} + +// Attempt to connect to a given network. +// If the network is not known (password is required), return false. +bool NetworkService::connect(String ssid) { + Preferences prefs; + prefs.begin("network", false); + if (!prefs.isKey(String(ssid + "_password").c_str())) { + Serial.println("network worker: no password found for SSID " + ssid); + prefs.end(); + return false; + } + prefs.end(); + // connect(ssid, prefs.getString(String(ssid + "_password").c_str())); + Serial.println("network worker: found password for SSID " + ssid); + connect(ssid, prefs.getString(String(ssid + "_password").c_str())); + return true; } -void NetworkService::connect() { +// Attempt to connect to a given network with a given password. +void NetworkService::connect(String ssid, String password) { + Serial.println("network worker: connecting to " + ssid); state = NETWORK_STATE_CONNECTING; WiFi.begin(ssid.c_str(), password.c_str()); } diff --git a/firmware/keira/src/services/network.h b/firmware/keira/src/services/network.h index 374b4c0f..3466e7bb 100644 --- a/firmware/keira/src/services/network.h +++ b/firmware/keira/src/services/network.h @@ -12,16 +12,16 @@ enum NetworkState { class NetworkService : public Service { public: NetworkService(); - void configure(String ssid, String password); + // void configure(String ssid, String password); NetworkState getNetworkState(); int getSignalStrength(); + bool connect(String ssid); + void connect(String ssid, String password); private: void run() override; - void connect(); - NetworkState state; - String ssid; - String password; - int8_t _signalStrength; // Value in range [0,3] + SemaphoreHandle_t mutex; + NetworkState state; + int8_t signalStrength; // Value in range [0,3] }; diff --git a/sdk/lib/lilka/src/lilka/ui.cpp b/sdk/lib/lilka/src/lilka/ui.cpp index 68f0ad29..738e6639 100644 --- a/sdk/lib/lilka/src/lilka/ui.cpp +++ b/sdk/lib/lilka/src/lilka/ui.cpp @@ -140,6 +140,14 @@ Alert::Alert(String title, String message) { this->done = false; } +void Alert::setTitle(String title) { + this->title = title; +} + +void Alert::setMessage(String message) { + this->message = message; +} + void Alert::update() { State state = controller.getState(); if (state.a.justPressed) { diff --git a/sdk/lib/lilka/src/lilka/ui.h b/sdk/lib/lilka/src/lilka/ui.h index 80a00cc5..50653d2e 100644 --- a/sdk/lib/lilka/src/lilka/ui.h +++ b/sdk/lib/lilka/src/lilka/ui.h @@ -116,6 +116,12 @@ class Alert { /// @param title Заголовок сповіщення. /// @param message Повідомлення сповіщення. Alert(String title, String message); + /// Змінити заголовок сповіщення. + /// @param title Новий заголовок сповіщення. + void setTitle(String title); + /// Змінити повідомлення сповіщення. + /// @param message Нове повідомлення сповіщення. + void setMessage(String message); /// Оновити стан сповіщення. /// /// Цю функцію потрібно викликати, щоб сповіщення опрацювало вхідні дані від користувача та оновило свій стан.