While working on a toy for my dog, I wanted to set up an AP with captive portal. This took a little bit because a lot of examples out there were a bit out of date and did not work properly. The main issue was that the CaptiveRequestHandler class was not specifically handling Android and iOS captive handler endpoints.
I changed the class like this and it worked fine:
class CaptiveRequestHandler : public AsyncWebHandler {
public:
CaptiveRequestHandler() {}
virtual ~CaptiveRequestHandler() {}
bool canHandle(AsyncWebServerRequest *request){
String url = request->url();
return (url == "/generate_204" || url == "/hotspot-detect.html");
}
void handleRequest(AsyncWebServerRequest *request) {
if (!request) {
Serial.println("Received null request");
return; // Return early if the request is null
}
// Check for specific requests (e.g., Android's `/generate_204`)
if (request->url() == "/generate_204" || request->url() == "/hotspot-detect.html") {
// Redirect to the captive portal (ESP8266’s IP)
request->redirect("http://192.168.4.1/index.html");
}
}
};
The canHandle was modified to only handle these methods, otherwise to return false. This is important because later we want to handle requests with other handlers.
That said, I also added a static serve handler:
WiFi.softAP(SSID);
dnsServer.start(53, "*", WiFi.softAPIP());
server.addHandler(new CaptiveRequestHandler()).setFilter(ON_AP_FILTER);//only when requested from AP
server.serveStatic("/", LittleFS, "/").setDefaultFile("index.html");
This meant my index.html page would request other assets (images, css, js) and they would be served. One thing to note is that in order for this to work on a platformio project, you need to update your platform.io config with your (filesystemboard_build.filesystem = littlefs)
Finally because we are allowing handlers, we can setup the web sockets.
// Set up WebSocket
ws.onEvent(onEvent);
server.addHandler(&ws); // Add WebSocket handler to the server
// Start the server
server.begin();