NetworkModule
Source:
NetworkModule.h
NetworkModule¶
src/core/NetworkModule.h:101Inherits:
MoonModule
Manages all device connectivity with an automatic priority cascade: Ethernet → WiFi STA → WiFi AP.
One MoonModule, one UI card — the user sees "Network", not three separate technologies. ESP32-specific (and Teensy later); desktop and RPi use OS-level networking and load no NetworkModule.
Priority cascade: Ethernet is always preferred (hardware detected, cable plugged), WiFi STA is next (SSID configured, Ethernet unavailable), WiFi AP is the last resort (STA fails or no SSID). When a higher-priority connection becomes available, lower ones are torn down to reclaim memory; when a higher-priority connection drops, the next activates automatically. The cascade tries each interface unconditionally and relies on the platform init calls to fail fast when hardware is absent — platform::ethInit() returns false without a PHY, and the WiFi paths return false on chips without a radio, so no interface hangs waiting on missing hardware.
AP shutdown delay: when STA connects successfully, AP stays active for ~10 s (with a UI message) before tearing down, giving the user time to reconnect via STA. AP always uses the fixed IP 4.3.2.1 — easy to remember, avoids 192.168.x.x conflicts with home routers.
State machine:State (Idle, WaitingEth, WaitingSta, ConnectedEth, ConnectedSta, AP) is driven from tick1s(). The mode control mirrors the state in plain language and is always present, even on the Ethernet-only build. A late-appearing interface (slow DHCP, cable plugged in after boot, saved WiFi credentials) is promoted from Idle / AP / ConnectedSta by the periodic upgrade checks — no reboot.
Ethernet: which PHY driver is compiled in is per chip (classic/P4 carry the internal-EMAC RMII driver, the S3 the W5500 SPI driver; a MM_NO_ETH build stubs ethInit() to return false). Which PHY a board uses and on which pins is runtime config — the ethType + pin controls, set per board in the device-model catalog and seeded from the per-chip default in platform_config.h. A W5500 change applies live (the SPI driver tears down and re-inits, no reboot); an RMII change saves and applies on the next boot (the EMAC/clock release is fiddlier). The eth controls, bound only on an Ethernet-capable build (platform::hasEthernet) and shown per PHY type:
-
ethType— PHY dropdown; the stored index maps 0=None, 1=LAN8720 (RMII), 2=IP101 (RMII), 3=W5500 (SPI), 4=YT8531 (RGMII, the S31's on-chip 1 Gb EMAC), matching theEthPhyTypeenum order. 0 shows no pin rows; a type reveals only its set. -
ethPhyAddr— SMI/PHY MDIO address (0..31, typically 0 or 1). -
ethRstGpio— PHY reset GPIO (−1 = none / module self-resets). -
ethMdcGpio/ethMdioGpio— RMII SMI clock / data GPIOs (−1 = IDF default). RMII only. -
ethClockGpio— RMII 50 MHz reference-clock GPIO;ethClockExtIn= clock direction (on = fed IN by the board, off = chip drives it OUT). RMII only. -
ethSpiMiso/ethSpiMosi/ethSpiSck/ethSpiCs/ethSpiIrq— W5500 SPI pins (ethSpiIrq−1 = polling). W5500 only.
mDNS: included here (not a separate module). Registers the deviceName on whichever interface is active and re-registers when the active interface changes or the name is renamed live. Uses ESP-IDF's mdns_init() / mdns_hostname_set().
Device name: the network name is owned solely by SystemModule; this module only READS it (see readDeviceName), and it is the single identity behind the mDNS <name>.local, the SoftAP SSID, and the DHCP hostname — so a device shows one name everywhere.
MM_IP= boot token: currentIp() writes the device's current LAN IP as octets; main.cpp formats it and appends a machine-parseable MM_IP=<ip> token to its once-per-second tick line — gated to the first 60 s of uptime (the installer reads at ~3–15 s after boot; afterwards the IP comes from the REST API, so a permanent token would just be noise on the perf line). The web installer reads this from the boot serial log right after flashing to auto-add the device to "Your devices" — timing-independent because the token rides the already-periodic tick line. Deliberately IP-only; once the installer has the IP it reads everything else from the live REST API.
Memory: the network stack cost varies by mode (Ethernet ~20 KB, STA ~40 KB, AP ~30 KB, STA+AP during the shutdown delay ~60 KB, fully reclaimed after release). This is why NetworkModule registers with the Scheduler BEFORE the light pipeline: network memory is claimed first so the light pipeline's adaptive allocation sees the real remaining heap. On a mode change the transition sequence checks heap, tears down light buffers first if heap is tight (display goes dark temporarily — acceptable, a crash is not), starts the new mode, then re-runs scheduler_->prepareTree() so allocation uses whatever heap remains. Reported via the standard per-module system; dynamicBytes updates after each mode change.
Ethernet-only build:esp32-eth compiles WiFi out entirely (platform::hasWiFi == false), branched via if constexpr. The cascade is Ethernet-only (no STA/AP states reachable), the ssid / password controls are not bound, but the addressing selector, static-IP controls, and mDNS toggle remain. The ssid_ / password_ buffers still exist (unconditional struct layout keeps persistence stable), simply never displayed or used.
Security: AP mode is open (no password) — a fallback for initial setup only. The STA password is stored in the controls. No HTTPS — an embedded device on the local network only.
Prior art: MoonLight — mDNS hostname advertising, REST API for network config, credentials persisted to SPIFFS. ESP-IDF — esp_wifi.h, mdns.h, esp_netif.h, esp_event.h.

Public Methods¶
inline void setScheduler(Scheduler * s)
inline void setSystemModule(SystemModule * s)
inline void setTxPowerSetting(uint8_t dBm)
: External entry-point for setting WiFi credentials at runtime — used by ImprovProvisioningModule when the browser/CLI pushes new credentials over USB-serial.
inline void setWifiCredentials(constchar * ssid, constchar * password)
virtual inline bool respectsEnabled() const override
: Networking is infrastructure — keep the cascade ticking even when the user toggled "enabled" off, otherwise the device would silently drop off the LAN and become unreachable.
virtual inline void setup() override
: Default lifecycle propagates to children.
virtual inline void defineControls() override
: defineControls MUST be idempotent and pure: only controls_.clear() + controls_.addX().
virtual inline void tick1s() override
virtual inline void release() override
inline void currentIp(uint8_t out) const
: Write the current LAN IP as octets into out[0..3] (all-zero = not connected).