Allora, ho approfondito un po' il discorso del re-indirizzamento automatico con apertura automatica del browser (e ho aggiunto la funzionalità anche alla mia libreria).
In poche parole quando ti colleghi all'ESP configurato come access point, ci sono dei servizi che fanno una serie di web request ad indirizzi specifici.
Per poter intercettare queste richieste è necessario far "girare" un DNS server sull'ESP configurato sulla porta prevista per gestire tutti i tipi di richieste su qualsiasi dominio.
Ricapitolando sull'esp è necessario:
- Avviare un DNS server che intercetta le richieste verso qualsiasi dominio e le rigira sull'indirizzo IP dell'ESP.
- Avviare un Web server che gestisce le richieste /redirect , /connecttest.txt, /hotspot-detect.html, /generate_204 reindirizzando alla pagina di setup voluta.
Queste richieste vengono eseguite dai servizi che gestiscono la connessione all'hot-spot e quindi lo specifico indirizzo usato dipende dal sistema operativo.
Al momento io ho identificato questi 4 indirizzi usati su Windows, iPhone, Android facendo prove e stampando sul monitor seriale tutte le richieste ricevute dai client. Devo ancora verificare qual è l'indirizzo richiesto su sistemi Linux, ma presumo sia lo stesso usato con Android.
DNSServer dnsServer;
void setup() {
dnsServer.start(53, "*", WiFi.softAPIP());
...
}
void loop() {
dnsServer.processNextRequest();
}
Invece, nell'esempio della libreria ESPAsyncWebserver, viene usato un approccio diverso ovvero se si trova in modalità AP reindirizza tutto a prescindere.
Francamente non mi piace granché, perché perdi l'automatismo offerto dai suddetti servizi che aprono il Captive Portal e soprattutto non puoi navigare il webserver per raggiungere eventuali altre pagine perché verrai sempre reindirizzato alla stessa.
Per questo motivo nella mia libreria ho deciso di gestire in questo modo (non far caso a std::bind, serve per associare il metodo captivePortal()
incluso nella stessa libreria come funzione di callback). Potresti replicare il funzionamento di base anche nel tuo caso.
// Captive Portal redirect
webserver->on("/redirect", HTTP_GET, std::bind(&FSWebServer::captivePortal, this));
webserver->on("/connecttest.txt", HTTP_GET, std::bind(&FSWebServer::captivePortal, this));
webserver->on("/msdownload", HTTP_GET, std::bind(&FSWebServer::captivePortal, this));
Il metodo captivePortal() è quello che si occupa del redirect vero e proprio ed è scritto cosi (webserver è un puntatore alla classe WebServer esp32 o esp8266, per questo c'è la notazione ->
):
// Redirect to captive portal if we got a request for another domain.
bool FSWebServer::captivePortal() {
IPAddress ip = webserver->client().localIP();
char serverLoc [sizeof("https:://255.255.255.255/")+ sizeof(pageName) + 1];
snprintf(serverLoc, sizeof(serverLoc), "http://%d.%d.%d.%d%s", ip[0], ip[1], ip[2], ip[3] );
// redirect only if hostheader is different from server ip
if (strcmp(serverLoc, webserver->hostHeader().c_str())) {
webserver->sendHeader(F("Location"), serverLoc, true);
webserver->send(302, F("text/html"), ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
webserver->client().stop(); // Stop is needed because we sent no content length
return true;
}
return false;
}