-
Notifications
You must be signed in to change notification settings - Fork 1
feat(moneo): add WiFi manager with auto network selection #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| #include "WiFiManager.h" | ||
|
|
||
| // Add your networks here — the device scans and connects to the first available. | ||
| // Do NOT commit real credentials; replace these placeholders with your own locally. | ||
| const char* WIFI_NETWORKS[WIFI_NETWORK_COUNT][2] = { | ||
| { "YOUR_WIFI_SSID", "YOUR_WIFI_PASSWORD" }, | ||
| { "SECOND_WIFI_SSID", "SECOND_WIFI_PASSWORD" }, // e.g. laptop hotspot | ||
| }; | ||
|
|
||
| WiFiManager::WiFiManager() : _lastReconnectAttempt(0) {} | ||
|
|
||
| bool WiFiManager::connect() { | ||
| // Try last known network first (from NVS) | ||
| String lastSSID, lastPass; | ||
| _loadLastNetwork(lastSSID, lastPass); | ||
|
|
||
| if (lastSSID.length() > 0) { | ||
| DLOGF("[WiFi] Trying last known: %s\n", lastSSID.c_str()); | ||
| if (_tryNetwork(lastSSID.c_str(), lastPass.c_str())) return true; | ||
| } | ||
|
|
||
| // Scan and try all configured networks | ||
| DLOG("[WiFi] Scanning for known networks..."); | ||
| int found = WiFi.scanNetworks(); | ||
|
|
||
| // Iterate over our known networks (priority order), not the scan list. | ||
| // Cost scales with the few provisioned networks, not the many nearby ones. | ||
| for (int i = 0; i < WIFI_NETWORK_COUNT; i++) { | ||
| bool available = false; | ||
| for (int j = 0; j < found; j++) { | ||
| if (WiFi.SSID(j) == String(WIFI_NETWORKS[i][0])) { | ||
| available = true; | ||
| break; // this known network is in range — stop scanning for it | ||
| } | ||
| } | ||
| if (!available) continue; | ||
|
|
||
| DLOGF("[WiFi] Found: %s\n", WIFI_NETWORKS[i][0]); | ||
| if (_tryNetwork(WIFI_NETWORKS[i][0], WIFI_NETWORKS[i][1])) { | ||
| _saveLastNetwork(WIFI_NETWORKS[i][0], WIFI_NETWORKS[i][1]); | ||
| return true; | ||
| } | ||
| } | ||
|
Comment on lines
+28
to
+43
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From computational complexity and determinacy perspective, it makes more sense to iterate over the known networks, and check if it's available on the available networks... instead of iterating over the available networks, to match with the known networks. For example, you have 2 networks provisioned, and there are 6 networks available when scanning... in the current approach, you iterate 6 times, in altered approach, you only iterate twice. And if the first iteration already finds the network, it's an oneshot hit. Whereas in the current approach even if the known network is in the available list, but is not the first one, it at least has to iterate as many times as the known network's order is in the scanned network list. This doesn't break software, and is fine for a demo; but these are the things (memory and time cost, i.e. computation complexity) if you're building for scale. |
||
|
|
||
| DLOG("[WiFi] No known network found."); | ||
| return false; | ||
| } | ||
|
|
||
| bool WiFiManager::ensureConnected() { | ||
| if (isConnected()) return true; | ||
| unsigned long now = millis(); | ||
| if (now - _lastReconnectAttempt < WIFI_RECONNECT_INTERVAL) return false; | ||
| _lastReconnectAttempt = now; | ||
| return connect(); | ||
| } | ||
|
|
||
| bool WiFiManager::_tryNetwork(const char* ssid, const char* password) { | ||
| WiFi.begin(ssid, password); | ||
| unsigned long start = millis(); | ||
| while (WiFi.status() != WL_CONNECTED && | ||
| millis() - start < WIFI_CONNECT_TIMEOUT_MS) { | ||
| delay(300); | ||
| } | ||
| if (WiFi.status() == WL_CONNECTED) { | ||
| DLOGF("[WiFi] Connected to %s — IP: %s\n", ssid, | ||
| WiFi.localIP().toString().c_str()); | ||
| return true; | ||
| } | ||
| WiFi.disconnect(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? |
||
| return false; | ||
| } | ||
|
|
||
| void WiFiManager::_saveLastNetwork(const char* ssid, const char* password) { | ||
| // Runtime state managed by the firmware (last connected network, future: | ||
| // last file synced, etc.) — kept separate from the provisioned network list. | ||
| _prefs.begin("moneo_runtime", false); | ||
| _prefs.putString("ssid", ssid); | ||
| _prefs.putString("pass", password); | ||
| _prefs.end(); | ||
| } | ||
|
|
||
| void WiFiManager::_loadLastNetwork(String& ssid, String& password) { | ||
| _prefs.begin("moneo_runtime", true); | ||
| ssid = _prefs.getString("ssid", ""); | ||
| password = _prefs.getString("pass", ""); | ||
| _prefs.end(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #ifndef WiFiManager_h | ||
| #define WiFiManager_h | ||
|
|
||
| #include <Arduino.h> | ||
| #include <WiFi.h> | ||
| #include <Preferences.h> | ||
| #include "Config.h" | ||
|
|
||
| // ============================================================ | ||
| // WiFiManager — Auto-connects to known networks. | ||
| // Uses NVS (Preferences) to remember last connected network. | ||
| // Tries last known first, then scans all configured networks. | ||
| // ============================================================ | ||
|
|
||
| class WiFiManager { | ||
| public: | ||
| WiFiManager(); | ||
| bool connect(); | ||
| bool isConnected() const { return WiFi.status() == WL_CONNECTED; } | ||
| bool ensureConnected(); | ||
| String currentSSID() const { return WiFi.SSID(); } | ||
| String localIP() const { return WiFi.localIP().toString(); } | ||
|
|
||
| private: | ||
| bool _tryNetwork(const char* ssid, const char* password); | ||
| void _saveLastNetwork(const char* ssid, const char* password); | ||
| void _loadLastNetwork(String& ssid, String& password); | ||
|
|
||
| Preferences _prefs; | ||
| unsigned long _lastReconnectAttempt; | ||
| }; | ||
|
|
||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
Config.his already there, right?Shouldn't we make use of that, or you're waiting to integrate it with the
moneo.inoproject file for orchestrating it?It's fine if that's the case... this is the rudimentary part that
Config.habstracts. So it's fine for now; just confirming the reasoning.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The final connectivity flow should be: