From c69c3cce1cda7f831253330f79d6b20c9c673ac0 Mon Sep 17 00:00:00 2001 From: Nick Dunklee Date: Fri, 27 Feb 2026 11:28:57 -0700 Subject: [PATCH 1/4] Adding code to make wifi work better on companions: * Retry handling if disconnected * Properly uses WIFI_STA * Adding first pass CLI commands for provisioning * Gated only for ESP32 --- examples/companion_radio/MyMesh.cpp | 42 ++++++++++++++ examples/companion_radio/main.cpp | 90 ++++++++++++++++++++++++++++- 2 files changed, 131 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index c96f7e0175..618cdc09a5 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1811,9 +1811,17 @@ void MyMesh::enterCLIRescue() { _cli_rescue = true; cli_command[0] = 0; Serial.println("========= CLI Rescue ========="); + Serial.println("Commands: set pin , rebuild, erase, reboot, ls, cat, rm"); +#ifdef WIFI_SSID + Serial.println("WiFi: wifi_ssid , wifi_pwd , wifi_commit, wifi_clear"); +#endif } void MyMesh::checkCLIRescueCmd() { +#ifdef WIFI_SSID + static char staged_ssid[33] = {0}; + static char staged_pwd[65] = {0}; +#endif int len = strlen(cli_command); while (Serial.available() && len < sizeof(cli_command)-1) { char c = Serial.read(); @@ -1975,6 +1983,40 @@ void MyMesh::checkCLIRescueCmd() { } +#ifdef WIFI_SSID + } else if (memcmp(cli_command, "wifi_ssid ", 10) == 0) { + strncpy(staged_ssid, &cli_command[10], sizeof(staged_ssid) - 1); + staged_ssid[sizeof(staged_ssid) - 1] = 0; + Serial.printf(" > SSID staged: \"%s\" (not saved yet, use wifi_commit)\n", staged_ssid); + } else if (memcmp(cli_command, "wifi_pwd ", 9) == 0) { + strncpy(staged_pwd, &cli_command[9], sizeof(staged_pwd) - 1); + staged_pwd[sizeof(staged_pwd) - 1] = 0; + Serial.println(" > Password staged (not saved yet, use wifi_commit)"); + } else if (strcmp(cli_command, "wifi_commit") == 0) { + if (staged_ssid[0] == 0 || staged_pwd[0] == 0) { + Serial.println(" Error: stage both wifi_ssid and wifi_pwd before committing"); + } else { + File f = _store->getPrimaryFS()->open("/wifi_config", "w", true); + if (f) { + f.println(staged_ssid); + f.println(staged_pwd); + f.close(); + Serial.printf(" > Saved SSID \"%s\" to flash, rebooting...\n", staged_ssid); + board.reboot(); + } else { + Serial.println(" Error: failed to write /wifi_config"); + } + } + } else if (strcmp(cli_command, "wifi_clear") == 0) { + _store->getPrimaryFS()->remove("/wifi_config"); + Serial.println(" > /wifi_config removed, using compiled-in defaults on next boot, rebooting..."); + board.reboot(); + } else if (strcmp(cli_command, "wifi_show") == 0) { + Serial.printf(" Staged SSID: \"%s\"\n", staged_ssid[0] ? staged_ssid : "(none)"); + Serial.printf(" Staged PWD: %s\n", staged_pwd[0] ? "(set)" : "(none)"); + bool has_config = _store->getPrimaryFS()->exists("/wifi_config"); + Serial.printf(" /wifi_config on flash: %s\n", has_config ? "yes" : "no (using compiled-in defaults)"); +#endif } else if (strcmp(cli_command, "reboot") == 0) { board.reboot(); // doesn't return } else { diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index eff9efca47..7295364078 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -101,6 +101,11 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store /* END GLOBAL OBJECTS */ +#ifdef WIFI_SSID +static char _wifi_ssid[33]; +static char _wifi_pwd[65]; +#endif + void halt() { while (1) ; } @@ -195,7 +200,54 @@ void setup() { #ifdef WIFI_SSID board.setInhibitSleep(true); // prevent sleep when WiFi is active - WiFi.begin(WIFI_SSID, WIFI_PWD); + + // Load credentials from flash if provisioned via CLI rescue; fall back to compiled-in defaults + strncpy(_wifi_ssid, WIFI_SSID, sizeof(_wifi_ssid) - 1); + strncpy(_wifi_pwd, WIFI_PWD, sizeof(_wifi_pwd) - 1); + { + File wf = SPIFFS.open("/wifi_config", "r"); + if (wf) { + int n = wf.readBytesUntil('\n', _wifi_ssid, sizeof(_wifi_ssid) - 1); + _wifi_ssid[n] = 0; + n = wf.readBytesUntil('\n', _wifi_pwd, sizeof(_wifi_pwd) - 1); + _wifi_pwd[n] = 0; + wf.close(); + WIFI_DEBUG_PRINTLN("Loaded credentials from flash, SSID: %s", _wifi_ssid); + } else { + WIFI_DEBUG_PRINTLN("No /wifi_config found, using compiled-in SSID: %s", _wifi_ssid); + } + } + + WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) { + switch (event) { + case ARDUINO_EVENT_WIFI_STA_START: + WIFI_DEBUG_PRINTLN("STA started, connecting to SSID: %s", _wifi_ssid); + break; + case ARDUINO_EVENT_WIFI_STA_CONNECTED: + WIFI_DEBUG_PRINTLN("Associated with AP: %s, channel: %d", + (char*)info.wifi_sta_connected.ssid, + info.wifi_sta_connected.channel); + break; + case ARDUINO_EVENT_WIFI_STA_GOT_IP: + WIFI_DEBUG_PRINTLN("Got IP: %s", WiFi.localIP().toString().c_str()); + break; + case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: + WIFI_DEBUG_PRINTLN("Disconnected, reason: %d", + info.wifi_sta_disconnected.reason); + break; + case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: + WIFI_DEBUG_PRINTLN("Auth mode changed"); + break; + case ARDUINO_EVENT_WIFI_STA_LOST_IP: + WIFI_DEBUG_PRINTLN("Lost IP"); + break; + default: + break; + } + }); + WiFi.persistent(false); // don't use/overwrite NVS-cached credentials + WiFi.mode(WIFI_STA); + WiFi.begin(_wifi_ssid, _wifi_pwd); serial_interface.begin(TCP_PORT); #elif defined(BLE_PIN_CODE) serial_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin()); @@ -218,6 +270,39 @@ void setup() { #endif } +#ifdef defined(WIFI_SSID) && defined(ESP32) +static void wifi_reconnect_check() { + constexpr uint32_t CHECK_INTERVAL_MS = 5000; + constexpr uint32_t RECONNECT_AFTER_MS = 30000; + + static uint32_t last_check = 0; + static uint32_t down_since = 0; + + if (millis() - last_check < CHECK_INTERVAL_MS) return; + last_check = millis(); + + if (WiFi.status() == WL_CONNECTED) { + down_since = 0; + return; + } + + if (down_since == 0) { + down_since = millis(); + WIFI_DEBUG_PRINTLN("WiFi disconnected, waiting to reconnect..."); + return; + } + + if (millis() - down_since >= RECONNECT_AFTER_MS) { + WIFI_DEBUG_PRINTLN("WiFi reconnecting..."); + WiFi.disconnect(true); + WiFi.persistent(false); + WiFi.mode(WIFI_STA); + WiFi.begin(_wifi_ssid, _wifi_pwd); + down_since = 0; + } +} +#endif + void loop() { the_mesh.loop(); sensors.loop(); @@ -225,4 +310,7 @@ void loop() { ui_task.loop(); #endif rtc_clock.tick(); +#ifdef WIFI_SSID + wifi_reconnect_check(); +#endif } From d909f341e9496455e097bbbb2178ad3fe38a26e0 Mon Sep 17 00:00:00 2001 From: Nick Dunklee Date: Thu, 5 Mar 2026 12:15:15 -0700 Subject: [PATCH 2/4] Defensive programming addition for some terminal types --- examples/companion_radio/MyMesh.cpp | 4 ++-- examples/companion_radio/main.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 618cdc09a5..19b63d68d4 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -1998,8 +1998,8 @@ void MyMesh::checkCLIRescueCmd() { } else { File f = _store->getPrimaryFS()->open("/wifi_config", "w", true); if (f) { - f.println(staged_ssid); - f.println(staged_pwd); + f.print(staged_ssid); f.print('\n'); + f.print(staged_pwd); f.print('\n'); f.close(); Serial.printf(" > Saved SSID \"%s\" to flash, rebooting...\n", staged_ssid); board.reboot(); diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 7295364078..f460c5c388 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -208,8 +208,10 @@ void setup() { File wf = SPIFFS.open("/wifi_config", "r"); if (wf) { int n = wf.readBytesUntil('\n', _wifi_ssid, sizeof(_wifi_ssid) - 1); + if (n > 0 && _wifi_ssid[n - 1] == '\r') n--; // strip \r if present _wifi_ssid[n] = 0; n = wf.readBytesUntil('\n', _wifi_pwd, sizeof(_wifi_pwd) - 1); + if (n > 0 && _wifi_pwd[n - 1] == '\r') n--; // strip \r if present _wifi_pwd[n] = 0; wf.close(); WIFI_DEBUG_PRINTLN("Loaded credentials from flash, SSID: %s", _wifi_ssid); From 7db9292a1f4a2abb31405b36f3330a71771002f4 Mon Sep 17 00:00:00 2001 From: Nick Dunklee Date: Fri, 6 Mar 2026 11:30:52 -0700 Subject: [PATCH 3/4] Removed some ugly unneeded debug --- examples/companion_radio/main.cpp | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index f460c5c388..858ca5c6a7 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -220,33 +220,6 @@ void setup() { } } - WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) { - switch (event) { - case ARDUINO_EVENT_WIFI_STA_START: - WIFI_DEBUG_PRINTLN("STA started, connecting to SSID: %s", _wifi_ssid); - break; - case ARDUINO_EVENT_WIFI_STA_CONNECTED: - WIFI_DEBUG_PRINTLN("Associated with AP: %s, channel: %d", - (char*)info.wifi_sta_connected.ssid, - info.wifi_sta_connected.channel); - break; - case ARDUINO_EVENT_WIFI_STA_GOT_IP: - WIFI_DEBUG_PRINTLN("Got IP: %s", WiFi.localIP().toString().c_str()); - break; - case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: - WIFI_DEBUG_PRINTLN("Disconnected, reason: %d", - info.wifi_sta_disconnected.reason); - break; - case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE: - WIFI_DEBUG_PRINTLN("Auth mode changed"); - break; - case ARDUINO_EVENT_WIFI_STA_LOST_IP: - WIFI_DEBUG_PRINTLN("Lost IP"); - break; - default: - break; - } - }); WiFi.persistent(false); // don't use/overwrite NVS-cached credentials WiFi.mode(WIFI_STA); WiFi.begin(_wifi_ssid, _wifi_pwd); @@ -284,6 +257,9 @@ static void wifi_reconnect_check() { last_check = millis(); if (WiFi.status() == WL_CONNECTED) { + if (down_since != 0) { + WIFI_DEBUG_PRINTLN("Connected to %s, IP: %s", _wifi_ssid, WiFi.localIP().toString().c_str()); + } down_since = 0; return; } From f33bceb7a304ab88ae178d978da2b97601e1b8d8 Mon Sep 17 00:00:00 2001 From: NickDunklee <30531405+NickDunklee@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:36:41 -0600 Subject: [PATCH 4/4] Update examples/companion_radio/main.cpp beala fix suggestion to repair malformed ifdef Co-authored-by: Alex Beal --- examples/companion_radio/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 858ca5c6a7..63012939a5 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -245,7 +245,7 @@ void setup() { #endif } -#ifdef defined(WIFI_SSID) && defined(ESP32) +#if defined(WIFI_SSID) && defined(ESP32) static void wifi_reconnect_check() { constexpr uint32_t CHECK_INTERVAL_MS = 5000; constexpr uint32_t RECONNECT_AFTER_MS = 30000;