From 8ad8eeae88ca4de37960a97013c340c7d8cb6de8 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Tue, 15 Sep 2026 12:22:53 -0700 Subject: [PATCH] Fix Wi-Fi restart during asynchronous shutdown --- examples/companion_radio/main.cpp | 4 ++++ test/test_wireless_control.py | 25 +++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index dce517cb..f5a976cb 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -2461,6 +2461,10 @@ public: companion_wifi_requested = on; if (!on) { companion_wifi_setup_requested = false; + // Sockets stop before the asynchronous web teardown finishes. Keep + // this latched so a replacement on request completes that teardown + // before startCompanionWiFi's active-state shortcut can skip restart. + companion_wifi_disable_in_progress = true; if (!finishStoppingCompanionWiFi()) return Result::Pending; companion_wifi_disable_in_progress = false; #if defined(MESH_PRIMARY_ESPNOW) && MESH_PRIMARY_ESPNOW diff --git a/test/test_wireless_control.py b/test/test_wireless_control.py index aa450db8..f6fea681 100644 --- a/test/test_wireless_control.py +++ b/test/test_wireless_control.py @@ -158,10 +158,16 @@ bool stop_pending=false; struct {int mode_value=0;void mode(int value){mode_value=value;}void setAutoReconnect(bool){}} WiFi; #define WIFI_OFF 0 bool finishStoppingCompanionWiFi(){ + // Production tears down sockets before waiting for the web server, while + // companion_wifi_active remains true until the whole teardown completes. + wifi_interface.disable(); if(stop_pending)return false; - companion_wifi_active=false;wifi_interface.disable();return true; + companion_wifi_active=false;return true; +} +void startCompanionWiFi(){ + if(companion_wifi_active)return; + companion_wifi_active=true;wifi_interface.enable(); } -void startCompanionWiFi(){companion_wifi_active=true;wifi_interface.enable();} struct { bool active=true; bool isEnabled(){return active;} @@ -197,6 +203,21 @@ int main(){ now_ms+=250;mesh::wireless::control().service(now_ms); assert(radio_driver.active&&bluetooth_interface.enabled&&companion_wifi_active); assert(companion_wifi_requested); + // Reversing an asynchronous shutdown must finish teardown and restart the + // sockets, rather than treating the still-active flag as a healthy WiFi stack. + for(const char* family : {"wifi", "2.4ghz"}) { + char command[40];snprintf(command,sizeof(command),"set %s off",family); + handleCompanionWirelessCommand(command,reply,sizeof(reply),CompanionWirelessSource::Usb); + stop_pending=true;now_ms+=250;mesh::wireless::control().service(now_ms); + assert(mesh::wireless::control().pending()&&companion_wifi_active&&!wifi_interface.enabled); + snprintf(command,sizeof(command),"set %s on",family); + handleCompanionWirelessCommand(command,reply,sizeof(reply),CompanionWirelessSource::Usb); + now_ms+=250;mesh::wireless::control().service(now_ms); + assert(mesh::wireless::control().pending()&&!wifi_interface.enabled); + stop_pending=false;mesh::wireless::control().service(++now_ms); + assert(!mesh::wireless::control().pending()&&companion_wifi_active&&wifi_interface.enabled); + assert(companion_wifi_requested&&!companion_wifi_disable_in_progress); + } // WiFi's framed client must not be mistaken for independent USB. wifi_interface.frame=true;wifi_interface.connected=true;bluetooth_interface.connected=false; interface_manager.checkRecvFrame(frame);