Skip to content

Fixes and improvements for ESP32 in AP mode:#295

Open
tobiashennerbichler wants to merge 3 commits into
OpenAstroTech:developfrom
tobiashennerbichler:fix_AP_mode
Open

Fixes and improvements for ESP32 in AP mode:#295
tobiashennerbichler wants to merge 3 commits into
OpenAstroTech:developfrom
tobiashennerbichler:fix_AP_mode

Conversation

@tobiashennerbichler

Copy link
Copy Markdown
  • Now correctly reports the device's IP in AP mode for UDP discovery requests. Previously reported as 0.0.0.0.
  • Split logic between AP mode and Infra modes since WiFi.status() returns a meaningless value in AP mode. AP mode now immediately establishes TCP/UDP servers. Server establishment moved to own function.
  • Also adds null-pointer guards to tcp/udp loop while servers are not established yet. Not relevant for AP mode, but included here due to simplicity.

Tested on ESP32 in WIFI_MODE_AP_ONLY. This PR does NOT address the infrastructure modes or the WIFI_MODE_ATTEMPT_INFRASTRUCTURE_FAIL_TO_AP mode in particular, which has some issues in the failover trigger. Local changes are ready and can be included in future PRs if desired.

- Now correctly reports the device's IP in AP mode for UDP discovery
  requests. Previously reported as 0.0.0.0.
- Split logic between AP mode and Infra modes since WiFi.status() returns a meaningless value in AP mode. AP mode now immediately establishes TCP/UDP servers. Server establishment moved to own function.
- Also adds null-pointer guards to tcp/udp loop while servers are not established yet. Not relevant for AP mode, but included here due to simplicity.

Tested on ESP32 in WIFI_MODE_AP_ONLY. This PR does NOT address the infrastructure modes or the WIFI_MODE_ATTEMPT_INFRASTRUCTURE_FAIL_TO_AP mode in particular, which has some issues in the failover trigger. Local changes are ready and can be included in future PRs if desired.
@ClutchplateDude

Copy link
Copy Markdown
Member

Code Review — "Fixes and improvements for ESP32 in AP mode"

Scope: ESP32 Wi-Fi in WIFI_MODE_AP_ONLY (infra / WIFI_MODE_ATTEMPT_INFRASTRUCTURE_FAIL_TO_AP explicitly excluded per the PR description). src/WifiControl.cpp / .hpp, +86 / −57.

Overview

  • startAccessPointMode() — adds WiFi.mode(WIFI_AP) and reorders to mode → softAPConfig → softAP, then calls the new establishServers().
  • establishServers() — extracted helper that (re)creates the TCP/UDP servers (was inline in loop()).
  • getIP() — returns softAPIP() in AP-only, localIP() otherwise, "NONE" when disabled; used by getStatus() and udpLoop().
  • loop() — the status-tracking / server-setup / failover block is now gated by if (WIFI_MODE != WIFI_MODE_AP_ONLY), so AP mode skips it entirely.
  • tcpLoop() / udpLoop() — null-pointer guards (if (!_tcpServer) return; / if (!_udp) return;); available()accept().

The core AP-mode fix is correct and complete, and the refactor is clean. One real regression for the failover path, plus a couple of minor loose ends.

Strengths

  • Root-cause fix is right. Adding WiFi.mode(WIFI_AP) before softAP()/softAPConfig() is the documented ESP32 order; without WIFI_AP set, softAPIP() returns 0.0.0.0 — exactly the symptom described.
  • Null guards are the load-bearing piece of the refactor. Because tcpLoop/udpLoop now no-op until servers exist, it becomes safe to stop early-returning out of loop() when not yet connected. Internally coherent design.
  • establishServers() extraction removes duplication (the same code ran in loop() and now also runs in startAccessPointMode(), which is itself called from infraToAPFailover() — so failover correctly re-establishes servers too).
  • _status = WL_DISCONNECTED member init fixes a previously-indeterminate wl_status_t.
  • available()accept() is a correct modernization: it accepts on connect rather than waiting for data, then while (client.available()) waits for bytes. Slightly more correct than before.
  • Style matches house conventions (Allman braces, 4-space indent, right-aligned pointers, LOG(DEBUG_WIFI, …)).

Issues & Risks

1. [Important] Failover is now edge-triggered — likely regression for WIFI_MODE_ATTEMPT_INFRASTRUCTURE_FAIL_TO_AP

This is the headline finding. The old loop() polled infraToAPFailover() every iteration whenever _status != WL_CONNECTED:

// OLD
_mount->loop();
if (_status != WL_CONNECTED) {
    infraToAPFailover();   // called every loop — timeout gets re-evaluated each tick
    return;
}

The new code only calls it inside the status-change block:

// NEW
if (WIFI_MODE != WIFI_MODE_AP_ONLY) {
    if (_status != WiFi.status()) {
        _status = WiFi.status();
        ...
        if (_status != WL_CONNECTED) { infraToAPFailover(); return; }   // only on a transition
    }
}

infraToAPFailover() is timeout-gated (_infraStart + _infraWait < millis()). After a failed infra attempt, WiFi.status() typically settles at WL_DISCONNECTED and stops changing. At that point _status == WiFi.status() forever, the change-block never re-enters, and the timeout is never re-evaluated — so the fall-over-to-AP never fires. The device hangs on infra indefinitely.

The old every-loop poll is what made the timeout actually expire. The author notes FAIL_TO_AP "has some issues" and is out of scope, but this PR doesn't just leave it alone — it removes the mechanism that let the existing timeout work.

Suggested fix — keep a periodic (not edge) failover poll for the non-AP modes, e.g.:

if (WIFI_MODE != WIFI_MODE_AP_ONLY) {
    if (_status != WiFi.status()) {
        _status = WiFi.status();
        /* log */
        if (_status == WL_CONNECTED) establishServers();
    }
    if (_status != WL_CONNECTED) { infraToAPFailover(); return; }   // back to every-loop
}

The null guards mean re-adding the every-loop return is still safe.

2. [Minor] getStatus() still reports a meaningless Wi-Fi status in AP mode

getStatus() (unchanged) still calls wifiStatus(WiFi.status()). By the PR's own rationale (WiFi.status() is meaningless in AP mode), the status field in AP-only will show an arbitrary string. The PR fixed the IP half of this but not the status half. Consider returning a fixed connected/"Access Point" status for AP-only.

3. [Minor] getIP() returns the wrong IP after a FAIL_TO_AP failover

getIP() keys off the compile-time WIFI_MODE, so for WIFI_MODE_ATTEMPT_INFRASTRUCTURE_FAIL_TO_AP it always returns WiFi.localIP() — even after infraToAPFailover() has switched the radio to AP. The UDP discovery reply and getStatus() would then advertise 0.0.0.0/the stale STA IP in the failed-over state — the exact class of bug this PR is fixing for AP-only. Acceptable given the stated scope, but worth a // TODO or tracking runtime mode.

4. [Nit] Magic number 4031 now appears in two places

4031 (UDP discovery port) was already a bare literal; establishServers() duplicates it alongside the original udpLoop() use. A named constant (e.g. WIFI_DISCOVERY_PORT) would prevent the two from drifting. Pre-existing debt — optional.

Other Dimensions

  • Correctness (non-infra): AP-only path is sound end-to-end. establishServers() deletes before new-ing (null-delete is safe), so re-establishment on failover doesn't leak.
  • Performance: Negligible — one WiFi.mode() at startup; one extra branch per loop for the null guards.
  • Security: No new surface. readStringUntil('#') into a heap String is pre-existing and unchanged.
  • Test coverage: No unit tests added, but WifiControl is hardware-bound and the native suite doesn't cover Wi-Fi; manual ESP32 testing in AP-only is noted. Acceptable.
  • Conventions: Compliant with the project style rules; should pass clang-format and -Werror.

Verdict

Approve with one requested change: the AP-only fix is correct and well-executed, but issue #1 (failover polling) is a concrete behavioral regression for FAIL_TO_AP that's worth fixing or explicitly documenting as known-broken in this PR. Issues #2#4 are minor/cosmetic.

Comment thread src/WifiControl.cpp Outdated
- 1: Was included due to a bad reversion of local changes to the Infra mode. Suggested fix was applied.
- 2: getStatus() now neither uses WiFi.status() nor WIFI_INFRASTRUCTURE_MODE_SSID in AP mode (both skipped)
- 3: Not addressed in this PR. Will be included in future PR addressing infra modes.
- 4: Now uses a DEFINE as the UDP port placeholder
	- The tcpLoop now tracks the data sent across loop calls and only starts processing if the '#' character is received.
	- _mount->loop() is now called after every byte
	- Prevents memory overconsumption by defining a maximum size that should likely be large enough for commands.
	- Skips all '\n' and '\r' characters from the command string to prevent them from being processed. Should not influence the functionality since the Meade LX200 protocol does not seem to use them in any command.
- 2: Added a log message if the softAP call fails to improve debugging.

@ClutchplateDude ClutchplateDude left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your submission and fixes!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants