Fix web UI: captive portal DNS + chunked HTML delivery

Phone browsers do a captive portal check (DNS + HTTP) when joining a
new WiFi AP. Without a DNS server the DNS query hangs forever and the
phone blocks all HTTP traffic to the network — page never loads.

Added DNSServer resolving all queries to 192.168.4.1. handleNotFound
now 302-redirects to / so captive portal probes get the main page.

Replaced single 15 KB send_P() with chunked transfer encoding in 512B
pieces with yield() between each chunk, keeping the WiFi stack responsive.

Made-with: Cursor
This commit is contained in:
drjones
2026-04-03 07:53:06 -07:00
parent d2215097a7
commit 4e581954bb

View File

@@ -10,6 +10,7 @@
#include <RadioLib.h>
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>
#include <ESPmDNS.h>
#include <ArduinoOTA.h>
#include "driver/gpio.h"
@@ -35,6 +36,7 @@ CC1101 radio1(&mod1);
CC1101 radio2(&mod2);
static WebServer server(WEB_PORT);
static DNSServer dnsServer;
static Preferences preferences;
// CC1101 valid discrete power levels in dBm (RadioLib only accepts these exact values)
@@ -2240,7 +2242,18 @@ window.addEventListener('resize',capDrawWave);
)HTML";
static void handleRoot() {
server.send_P(200, "text/html; charset=utf-8", kHtml, sizeof(kHtml) - 1);
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html; charset=utf-8", "");
const size_t total = sizeof(kHtml) - 1;
size_t off = 0;
while (off < total) {
size_t n = total - off;
if (n > 512) n = 512;
server.sendContent_P(kHtml + off, n);
off += n;
yield();
}
server.sendContent("");
}
static void handleLog() {
@@ -2531,9 +2544,8 @@ static void handleCaptureWave() {
}
static void handleNotFound() {
const String uri = server.uri();
logLine("[HTTP] 404 " + uri);
server.send(404, "text/plain", "404: Not found");
server.sendHeader("Location", "http://192.168.4.1/");
server.send(302, "text/plain", "Redirect");
}
static void handleSelfTest() {
@@ -2706,6 +2718,9 @@ void setup() {
server.onNotFound(handleNotFound);
server.begin();
dnsServer.start(53, "*", WiFi.softAPIP());
logLine("[DNS] Captive portal DNS on *:53 → " + WiFi.softAPIP().toString());
// OTA firmware updates over WiFi (connect to 'killer' AP, upload via PlatformIO OTA)
ArduinoOTA.setHostname("killer");
ArduinoOTA.setPassword("killerpw");
@@ -2731,6 +2746,7 @@ void setup() {
}
void loop() {
dnsServer.processNextRequest();
espNowTick();
ArduinoOTA.handle();
server.handleClient();