PVE-FANCTRL CAPTIVE PORTAL

First-boot WiFi setup + theme picker — no credentials in source

← FULL SKETCH CODE

CAPTIVE PORTAL FEATURES

WiFi Configuration User enters SSID + password in a browser form. No creds baked into the .ino — sketch is shareable.
🎨
Theme Picker Dropdown with all 11 themes. Pre-selects whatever's currently saved in NVS.
💾
NVS Persistence WiFi creds + theme selection saved to ESP32 flash — survives power cycle.
🚀
Auto Config Mode First boot launches the portal automatically. Hold BOOT for 5s at power-on to wipe creds and re-portal.

SETUP WORKFLOW

1
Power on ESP32
(no saved creds)
2
Connect phone/laptop
to "PVE-FANCTRL-Setup"
3
Captive portal opens
at 192.168.4.1
4
Enter SSID + pass
+ pick theme
→ reboot & join

// TFT in setup mode shows:

SETUP MODE
Join AP, browse to IP
PVE-FANCTRL-Setup
192.168.4.1

11 BUILT-IN THEMES

Lifted from CyberNetView's theme system plus a custom default and a Matrix theme with falling kanji rain. Pick during setup, or change live from the web UI later.

Defaultblack + purple/blue + teal
CyberNet Defaultblack + cyan + gold IP
Cyberpunk Neonmagenta on dark purple
Neon Gridcyan on dark blue
Retro Commandamber terminal
Military Opsdark green + amber
Noir Neonpink + cyan
Clinical LightLIGHT cream + slate
Holographicnavy + light cyan
Classic Greyflat + blue accent
Minimal Darkpure grayscale
Blood & Firecrimson + flame
Matrixphosphor green + falling katakana / kanji rain animation behind the gauges (uses LVGL's bundled Source Han Sans CJK font)

RUNTIME THEME SWITCHING

No need to re-enter the portal to change theme. After WiFi is configured, the regular web UI at http://<hostname>.local/ has a DISPLAY THEME dropdown at the bottom.

  • Pick a different theme → POSTs to the ESP32 → saved to NVS → LVGL widgets restyle live, no reboot.
  • Selection persists across reboots.
  • Same dropdown also works as a CLI: curl -X POST "http://PVE-FANCTRL.local/api/theme?name=matrix"

JSON API

METHODENDPOINTRETURNS / DOES
GET /api/status {"pct":50,"rpm":1450}
POST /api/set?pct=N Set fan PWM 0–100. Returns {"ok":true,"pct":N,"rpm":...}
GET /api/theme {"current":"...","themes":[{"id":"...","label":"..."},...]}
POST /api/theme?name=<id> Switch theme live. Returns {"ok":true,"current":"<id>"}

FACTORY RESET (re-launch portal)

  1. Power off the ESP32 (unplug USB-C).
  2. Press and hold the BOOT button on the dev board.
  3. Plug USB-C back in while still holding BOOT.
  4. Keep holding for at least 5 seconds.
  5. Release. Serial log prints [portal] wifi credentials wiped.
  6. Captive portal launches on the next boot — repeat the setup workflow.

The 5-second hold prevents accidental wipes from a momentary BOOT press during normal operation.

PORTAL HTML — SERVED BY THE ESP32

The portal page is rendered dynamically by renderPortalHtml() in the sketch — the theme dropdown is built from the in-memory themes[] array, with the currently-saved theme pre-selected. So adding a new theme to the array automatically adds it to the portal dropdown without HTML edits.

  • Form posts to /save with ssid, pass, theme
  • Handler saves both to separate NVS namespaces (wifi and ui)
  • Sends a "SAVED — rebooting" page, then calls ESP.restart() after 1.5s
  • Captive-portal redirect via DNSServer sends every other lookup to 192.168.4.1
VIEW FULL SKETCH CODE →