
bookmark_borderCamera Orbit

As an amateur, I don’t know what I’m doing most of the time. When I set out to do this, all I knew was I wanted my device to automatically redirect users to a page after they connect to it’s wifi network, just like airports and hotels do. Turns out this is called a captive portal. I struggled for awhile to get this to work, so I’ll try to point out what does what in this example.
One (ab)use case for this is for projects that use a web page for configuration. Normally you need to navigate to that page with the IP address in a browser… which means you have to write it down somewhere, or print it on a display (if you have one), or my next best idea: print a QR code with a hyperlink to it. It is better if we can redirect the device right to the page we want without fussing around.
Modern devices (windows, android, probably apple) and web browsers like to check to see if a newly connected WIFI network has internet access. They reach out to some known location, and if they get the right response, decide to stay connected and do their thing. Luckily there is a behavior on these devices designed to send you to a network sign-on page if it doesn’t get the reply it was hoping for, and that’s what this takes advantage of. Basically:
An ESP32, nothing else. Easiest way is to use one of the dev boards available on amazon/ebay/whatever. It may be necessary to power it externally, as some modules have power issues over USB and may trigger the brownout detector when activating wifi. This is probably applicable to the ESP8266, or anything else using these libraries.
I am using VS Code and Platform.io. Should also work with the Arduino IDE as long as you add the required libraries. Builtin libraries: wifi.h, dnsserver.h Other libraries:
This is all available on Github here: https://github.com/elliotmade/ESP32-Captive-Portal-Example
/* ESP-32 Captive portal example
* github.com/elliotmade/ESP32-Captive-Portal-Example
* This isn't anything new, and doesn't do anything special
* just an example I would have appreciated while I was searching for a solution
*/
#include <Arduino.h>
#include <AsyncTCP.h>
#include "ESPAsyncWebServer.h"
#include "DNSServer.h"
const char* ssid = "test_captive_portal"; //Name of the WIFI network hosted by the device
const char* password = ""; //Password
AsyncWebServer server(80); //This creates a web server, required in order to host a page for connected devices
DNSServer dnsServer; //This creates a DNS server, required for the captive portal
void webServerSetup(){
//This is a super simple page that will be served up any time the root location is requested. Get here intentionally by typing in the IP address.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", "<!DOCTYPE html><html><head><title>Success</title></head><body><p>Hooray</p></body>");
Serial.println("requested /");
});
//This is an example of triggering for a known location. This one seems to be common for android devices
server.on("/generate_204", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", "You were sent here by a captive portal after requesting generate_204");
Serial.println("requested /generate_204");
});
//This is an example of a redirect type response. onNotFound acts as a catch-all for any request not defined above
server.onNotFound([](AsyncWebServerRequest *request){
request->redirect("/");
Serial.print("server.notfound triggered: ");
Serial.println(request->url()); //This gives some insight into whatever was being requested
});
server.begin(); //Starts the server process
Serial.println("Web server started");
}
void setup() {
Serial.begin(115200);
WiFi.softAP(ssid, password); //This starts the WIFI radio in access point mode
Serial.println("Wifi initialized");
Serial.println(WiFi.softAPIP()); //Print out the IP address on the serial port (this is where you should end up if the captive portal works)
dnsServer.start(53, "*", WiFi.softAPIP()); //This starts the DNS server. The "*" sends any request for port 53 straight to the IP address of the device
webServerSetup(); //Configures the behavior of the web server
Serial.println("Setup complete");
}
void loop() {
dnsServer.processNextRequest(); //Without this, the connected device will simply timeout trying to reach the internet
//or it might fall back to mobile data if it has it
}
Thanks to other humans that shared info about this already. I’m not doing anything new, but just trying to explain things in a way that makes sense to me…