epaper-display/display/esp32/include/captive_portal.h
2026-04-06 13:11:15 +02:00

128 lines
4.8 KiB
C

#pragma once
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include "config.h"
static const char CAPTIVE_PORTAL_HTML[] PROGMEM = R"rawhtml(
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>EPaper Setup</title>
<style>
body { font-family: Arial, sans-serif; max-width: 480px; margin: 40px auto; padding: 16px; }
h1 { font-size: 1.4em; }
label { display: block; margin-top: 12px; font-weight: bold; }
input { width: 100%; padding: 8px; box-sizing: border-box; margin-top: 4px; }
button { margin-top: 20px; padding: 10px 24px; background: #0078d4; color: #fff;
border: none; border-radius: 4px; font-size: 1em; cursor: pointer; }
button:hover { background: #005a9e; }
</style>
</head>
<body>
<h1>EPaper Display Setup</h1>
<form method="POST" action="/save">
<label for="ssid">WiFi SSID</label>
<input id="ssid" name="ssid" type="text" placeholder="Netzwerkname" required>
<label for="pass">WiFi Password</label>
<input id="pass" name="pass" type="password" placeholder="Passwort (leer lassen wenn kein Passwort)">
<label for="api_url">API URL</label>
<input id="api_url" name="api_url" type="url" placeholder="http://192.168.1.100:3000" required>
<label for="display_id">Display ID</label>
<input id="display_id" name="display_id" type="text" placeholder="1" value="1">
<label for="api_token">API Token (optional)</label>
<input id="api_token" name="api_token" type="text" placeholder="Bearer Token">
<button type="submit">Speichern &amp; Neustart</button>
</form>
</body>
</html>
)rawhtml";
static const char CAPTIVE_PORTAL_SAVED_HTML[] PROGMEM = R"rawhtml(
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Gespeichert</title>
<style>body{font-family:Arial,sans-serif;text-align:center;padding:40px;}</style>
</head>
<body>
<h1>Konfiguration gespeichert!</h1>
<p>Das Ger&#228;t startet jetzt neu...</p>
</body>
</html>
)rawhtml";
/**
* Start a captive WiFi access point for initial configuration.
* Creates AP "EPaper-Setup", serves a config form on port 80.
* POST /save persists the config and reboots.
* This function never returns.
*/
inline void startCaptivePortal(Config& config) {
const byte DNS_PORT = 53;
DNSServer dnsServer;
WebServer server(80);
// ── Start AP ──────────────────────────────────────────────────────────────
WiFi.mode(WIFI_AP);
WiFi.softAP("EPaper-Setup"); // open network, no password
IPAddress apIP = WiFi.softAPIP();
Serial.print("[Portal] AP started, IP: ");
Serial.println(apIP);
// ── DNS: redirect all queries to us ───────────────────────────────────────
dnsServer.start(DNS_PORT, "*", apIP);
// ── Routes ────────────────────────────────────────────────────────────────
server.on("/", HTTP_GET, [&server]() {
server.send_P(200, "text/html", CAPTIVE_PORTAL_HTML);
});
server.on("/save", HTTP_POST, [&server, &config]() {
if (server.hasArg("ssid") && server.hasArg("api_url")) {
config.wifi_ssid = server.arg("ssid");
config.wifi_password = server.arg("pass");
config.api_url = server.arg("api_url");
config.display_id = server.arg("display_id").length() > 0
? server.arg("display_id")
: "1";
config.api_token = server.arg("api_token");
config.save();
server.send_P(200, "text/html", CAPTIVE_PORTAL_SAVED_HTML);
delay(2000);
ESP.restart();
} else {
server.send(400, "text/plain", "Fehlende Felder: ssid und api_url sind Pflichtfelder.");
}
});
// ── Captive portal redirect (all unknown URLs → /) ────────────────────────
server.onNotFound([&server, &apIP]() {
// Some OSes check a specific URL; redirect them all to our form
server.sendHeader("Location", String("http://") + apIP.toString() + "/", true);
server.send(302, "text/plain", "");
});
server.begin();
Serial.println("[Portal] HTTP server started");
// ── Main loop (blocks forever) ────────────────────────────────────────────
for (;;) {
dnsServer.processNextRequest();
server.handleClient();
yield();
}
}