Skip to content

ESP32: FAIL_TO_AP mode unreachable after failover; getIP() reports the wrong interface #307

Open

Description

@ClutchplateDude

WIFI_MODE_ATTEMPT_INFRASTRUCTURE_FAIL_TO_AP has two defects, both pre-existing before #295 and explicitly deferred there (the PR fixed WIFI_MODE_AP_ONLY only). @rbhbokka analyzed both in this comment on #295 and offered to implement the follow-up; @tobiashennerbichler also noted having local changes ready for this mode. This issue tracks the work so those offers have a home.

1. The mount is unreachable after failover

In WifiControl::loop() (src/WifiControl.cpp:168), the non-AP branch is entered forever in failover mode because WIFI_MODE is a compile-time constant:

if (WIFI_MODE != WIFI_MODE_AP_ONLY)
{
    ...
    if (_status != WL_CONNECTED)
    {
        infraToAPFailover();
        return;              // fires on every pass once failed over
    }
}

_mount->loop();
tcpLoop();
udpLoop();

Once infraToAPFailover() has called startAccessPointMode(), the station is disconnected and WiFi.status() never reports WL_CONNECTED again — so _status != WL_CONNECTED stays true and the return fires on every subsequent pass. _infraStart is zeroed, so the failover call itself is a harmless no-op, but tcpLoop() / udpLoop() are never reached again. The soft-AP is up and establishServers() has bound the port, yet no client is ever serviced.

The underlying problem is that one condition answers two different questions: "should we attempt failover?" (policy) and "are the servers up?" (lifecycle). The sketch from the #295 discussion:

     if (WIFI_MODE != WIFI_MODE_AP_ONLY)
     {
         if (_status != WiFi.status())
         {
             ...
         }
         if (_status != WL_CONNECTED)
         {
             infraToAPFailover();
-            return;
         }
     }

+    if (_tcpServer == nullptr || _udp == nullptr)
+    {
+        return;
+    }
+
     _mount->loop();
     tcpLoop();
     udpLoop();

infraToAPFailover() is already self-guarding on _infraStart != 0, so it stays a no-op in AP-only mode and after a completed failover; the null check becomes a pure lifecycle guard. The per-loop null guards added by #295 already cover the dereference, so this is about reachability rather than safety.

2. getIP() reports the wrong interface after failover

WifiControl::getIP() (src/WifiControl.cpp:148) keys off the compile-time WIFI_MODE:

return WIFI_MODE == WIFI_MODE_DISABLED  ? "NONE"
       : WIFI_MODE == WIFI_MODE_AP_ONLY ? WiFi.softAPIP().toString()
                                        : WiFi.localIP().toString();

In failover mode it therefore always returns WiFi.localIP() — even after the radio has switched to AP — so the UDP discovery reply and getStatus() advertise 0.0.0.0 or the stale station IP in the failed-over state. This is the same class of bug #295 fixed for AP_ONLY. getStatus()'s AP-specific field handling has the same compile-time keying.

Suggested approach: track the runtime mode (e.g. a small enum or a _failedOver flag set by infraToAPFailover()) and consult it in loop(), getIP(), and getStatus(), keeping the compile-time WIFI_MODE as the policy input.

Scope note

WIFI_MODE_ATTEMPT_INFRASTRUCTURE_FAIL_TO_AP was excluded from #295 by design; everything above reproduces on develop before and after it. Testing requires an ESP32 build configured for failover mode plus an infrastructure network that refuses the credentials.

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions