Captive Portal für den ESP32 Teil 2 - AZ-Delivery

Ciao a tutti

la risposta positiva e l'interesse generale nel blog precedente a Portale prigioniero, mi ha spinto a scrivere un altro blog speciale sull'argomento e ad approfondire alcuni punti e desideri di te. Tra le molte altre domande dettagliate, che tratterò in seguito, c'era il desiderio di utilizzare il portale captive non solo su un ESP8266, ma anche sul ESP32  lasciar andare. Questo non è possibile in modo nativo. A tale scopo, è necessario apportare alcune modifiche, in particolare le librerie utilizzate. Il codice odierno è pertanto ESCLUSIVAMENTE eseguibile su ESP32. Oltre alle regolazioni della libreria, alcune ottimizzazioni sono state aggiunte in dettaglio in modo specifico per ESP32.

Inoltre, alcuni lettori del blog potrebbero aver scoperto che l'ESP non era più in grado di riconnettersi correttamente a un punto di accesso dopo il riavvio. Sono stato in grado di ricreare questo bug dopo una lunga ricerca. Ciò si verifica probabilmente in interazione con determinati punti di accesso, in cui ESP viene rifiutato nonostante i dati di accesso WLAN corretti quando si accede al punto di accesso e quindi passa dalla modalità stazione alla propria modalità punto di accesso. Durante questo processo, i dati di accesso WLAN precedenti vengono eliminati dalla EEPROM (questa è l'intenzione).

La soluzione a questo bug sul lato sistema è tentare di accedere più volte nonostante i dati di accesso presumibilmente "errati" prima di tornare alla modalità punto di accesso.

Come ulteriore piccolo miglioramento, l'attuale indirizzo IP, indipendentemente dalla modalità in cui si trova ESP32, viene emesso sull'interfaccia seriale. Quindi, se desideri visualizzare questo indirizzo IP sul tuo display, devi solo modificare l'output nel punto corrispondente nel codice.

Per essere compatibile con il maggior numero possibile di schede ESP, non emetto ancora lo stato su porte esterne dell'ESP.

 

Anche in questa parte, ESP32 utilizza il nostro codice del portale captive di seguito Portale prigioniero  on. La WLAN ha il nome "ESP_Config" e la password "12345678". Con questo siamo in grado di connetterci al nostro telefono cellulare e vengono quindi automaticamente indirizzati dal telefono cellulare al sito Web Captive Portal. Ciò corrisponde essenzialmente alla parte precedente in termini di design e funzionalità.

 

Su questo sito Web ora possiamo fare clic sul collegamento di sistema "Impostazioni WiFi" e ora siamo indirizzati a una pagina di configurazione WiFi estesa con la quale ora possiamo selezionare sia una rete a cui ESP32 dovrebbe connettersi:

 

La rete radio selezionata qui e la password inserita vengono salvate nella EEPROM. L'ESP32 proverà a connettersi a questa rete al successivo avvio. Se ciò fallisce dopo diversi tentativi, ad es. Perché la rete non è più accessibile o la password è stata modificata, l'ESP torna alla modalità punto di accesso e attende una nuova configurazione.

Il codice personalizzato per Captive Portal per ESP32 è:

[

#include <WiFi.B>
#include <WiFiClient.B> 
#include <Server Web.B>
#include <ESPmDNS.B>
#include <Server DNS.B>
#include <EEPROM.B>

#define GPIO_OUT_W1TS_REG (DR_REG_GPIO_BASE + 0x0008)
#define GPIO_OUT_W1TC_REG (DR_REG_GPIO_BASE + 0x000C)

statica const byte WiFiPwdLen = 25;
statica const byte APSTANameLen = 20;

struct WiFiEEPromData   {     bool APSTA = vero; // Punto di accesso o modalità stazione - modalità AP reale     bool PwDReq = falso; // Password obbligatoria     bool CapPortal = vero ; // CaptivePortal attivo in modalità AP     carbonizzare APSTAName[APSTANameLen]; // Nome punto STATION / AP TO cONNECT, se difeso     carbonizzare WiFiPwd[WiFiPwdLen]; // WiFiPAssword, se difeso     carbonizzare ConfigValid[3]; // Se Config è Vaild, è richiesto il tag "TK" "   };

/ * nome host per mDNS. Dovrebbe funzionare almeno su Windows. provare http://esp8266.local */
const carbonizzare *ESPHostname = "ESP32";

// server DNS
const byte DNS_PORT = 53;
DNSServer dnsserver;

// Conmmon Paramenters
bool SoftAccOK  = falso;

// Server web
Server web server(80);

/ * Parametri di rete Soft AP * /
Indirizzo IP APIP(172, 20, 0, 1);
Indirizzo IP netMsk(255, 255, 255, 0);

unsigned lungo currentMillis = 0;
unsigned lungo startMillis;

/ ** Stato WLAN corrente * /
corto stato = WL_IDLE_STATUS;

WiFiEEPromData MyWiFiConfig;
Corda Temp ="";


vuoto impostare() 
{   REG_WRITE(GPIO_OUT_W1TS_REG, PO(GPIO_NUM_16));     // Set di correzione dell'errore di meditazione Guru   ritardo(1);   REG_WRITE(GPIO_OUT_W1TC_REG, PO(GPIO_NUM_16));     // Eliminazione dell'errore di meditazione Guru   bool ConnectSuccess = falso;   bool CreateSoftAPSucc  = falso;   bool CInitFSSystem  = falso;   bool CInitHTTPServer  = falso;   byte len;    Seriale.inizio(9600);    mentre (!Seriale) {     ; // attendi che la porta seriale si connetta. Necessario per USB nativo   }   Seriale.println(F("Interfaccia seriale inizializzata a 9600 Baud."));    Wi-Fi.setAutoReconnect (falso);   Wi-Fi.persistente(falso);   Wi-Fi.disconnect();    Wi-Fi.SetHostName(ESPHostname); // Imposta il nome host DHCP assegnato alla stazione ESP.   Se (loadCredentials()) // Carica credenziali WLAN per le impostazioni WiFi   {       Seriale.println(F("Trovate credenziali valide."));         Se (MyWiFiConfig.APSTA == vero)  // Modalità AP       {          Seriale.println(F("Modalità punto di accesso selezionata."));          Seriale.println(MyWiFiConfig.APSTA);         len = strlen(MyWiFiConfig.APSTAName);         MyWiFiConfig.APSTAName[len+1] = '\0';          len = strlen(MyWiFiConfig.WiFiPwd);         MyWiFiConfig.WiFiPwd[len+1] = '\0';           CreateSoftAPSucc = CreateWifiSoftAP();       } altro       {         Seriale.println(F("Modalità stazione selezionata."));                len = strlen(MyWiFiConfig.APSTAName);         MyWiFiConfig.APSTAName[len+1] = '\0';          len = strlen(MyWiFiConfig.WiFiPwd);         MyWiFiConfig.WiFiPwd[len+1] = '\0';         len = ConnectWifiAP();              Se ( len == 3 ) { ConnectSuccess = vero; } altro { ConnectSuccess = falso; }            }   } altro   { // Imposta configurazione predefinita - Crea AP      Seriale.println(F("Nessuna credenziale valida trovata."));       SetDefaultWiFiConfig ();      CreateSoftAPSucc = CreateWifiSoftAP();       saveCredentials();      ritardo(500);        }   Se ((ConnectSuccess o CreateSoftAPSucc))     {                Seriale.Stampa (F("Indirizzo IP: "));       Se (CreateSoftAPSucc) { Seriale.println(Wi-Fi.softAPIP());}          Se (ConnectSuccess) { Seriale.println(Wi-Fi.LOCALIP());}       InitalizeHTTPServer();          }     altro     {       Seriale.setDebugOutput(vero); // Uscita di debug per WLAN su interfaccia seriale.       Seriale.println(F("Errore: impossibile connettersi alla rete WLAN. Impostare la configurazione DEFAULT."));       SetDefaultWiFiConfig();       CreateSoftAPSucc = CreateWifiSoftAP();       InitalizeHTTPServer();         SetDefaultWiFiConfig();       saveCredentials();        } 
}

vuoto InitalizeHTTPServer() 
 {   bool initok = falso;   / * Imposta pagine web: root, pagine di configurazione wifi, rilevatori di portale captive SO e non trovati. * /   server.su("/", handleRoot);   server.su("/Wi-Fi", handleWifi);   Se (MyWiFiConfig.CapPortal) { server.su("/ generate_204", handleRoot); } // Portale captive Android. Forse non è necessario. Potrebbe essere gestito da notFound handler.   Se (MyWiFiConfig.CapPortal) { server.su("/Favicon.ico", handleRoot); }   // Un altro portale captive per Android. Forse non è necessario. Potrebbe essere gestito da notFound handler. Controllato su Sony Handy   Se (MyWiFiConfig.CapPortal) { server.su("Fwlink /", handleRoot); }  // Portale captive Microsoft. Forse non è necessario. Potrebbe essere gestito da notFound handler.   //server.on("/generate_204 ", handleRoot); // Portale captive Android. Forse non è necessario. Potrebbe essere gestito da notFound handler.   //server.on("/favicon.ico ", handleRoot); // Un altro portale captive per Android. Forse non è necessario. Potrebbe essere gestito da notFound handler. Controllato su Sony Handy   //server.on("/fwlink ", handleRoot); // Portale captive Microsoft. Forse non è necessario. Potrebbe essere gestito da notFound handler.    server.onNotFound ( handleNotFound );   // Speicherung Header-Elemente anfordern   // server.collectHeaders (Headers, sizeof (Headers) / sizeof (Headers [0]));   server.inizio(); // Avvio del server Web
 }

booleano CreateWifiSoftAP() 
{   Wi-Fi.disconnect();   Seriale.Stampa(F("Inizializza SoftAP"));   Se (MyWiFiConfig.PwDReq)      {       SoftAccOK  =  Wi-Fi.SofTap(MyWiFiConfig.APSTAName, MyWiFiConfig.WiFiPwd); // Passwortlänge mindestens 8 Zeichen!     } altro     {       SoftAccOK  =  Wi-Fi.SofTap(MyWiFiConfig.APSTAName); // Punto di accesso SENZA password       // Funzione di sovraccarico :; WiFi.softAP (ssid, password, canale, nascosto)     }   ritardo(2000); // Senza indugio ho visto vuoto l'indirizzo IP   Wi-Fi.softAPConfig(APIP, APIP, netMsk);   Se (SoftAccOK)   {   / * Imposta il server DNS reindirizzando tutti i domini su apIP * /     dnsserver.setErrorReplyCode(DNSReplyCode::Nessun errore);   dnsserver.inizio(DNS_PORT, "*", APIP);   Seriale.println(F("riuscito."));   // Serial.setDebugOutput (true); // Uscita di debug per WLAN su interfaccia seriale.   } altro   {   Seriale.println(F("Errore soft AP."));   Seriale.println(MyWiFiConfig.APSTAName);   Seriale.println(MyWiFiConfig.WiFiPwd);   }   ritorno SoftAccOK;
}

byte ConnectWifiAP() 
{   Seriale.println(F("Inizializzazione del client Wifi".));     byte connRes = 0;   byte io = 0;   Wi-Fi.disconnect();   Wi-Fi.softAPdisconnect(vero); // La funzione imposterà l'SSID e la password attualmente configurati dell'AP-soft su valori null. Il parametro è facoltativo Se impostato su true, disattiva la modalità soft-AP.   ritardo(500);     Wi-Fi.inizio(MyWiFiConfig.APSTAName, MyWiFiConfig.WiFiPwd);   connRes  = Wi-Fi.waitForConnectResult();   mentre (( connRes == 0 ) e (io != 10))  // se connRes == 0 "IDLE_STATUS - cambia Statius"     {        connRes  = Wi-Fi.waitForConnectResult();       ritardo(2000);       io++;       Seriale.Stampa(F("."));       // istruzioni     }   mentre (( connRes == 1 ) e (io != 10))  // se connRes == 1 NO_SSID_AVAILin - SSID non può essere raggiunto     {        connRes  = Wi-Fi.waitForConnectResult();       ritardo(2000);       io++;       Seriale.Stampa(F("."));       // istruzioni     }     Se (connRes == 3 ) {                          Wi-Fi.setAutoReconnect(vero); // Imposta se il modulo tenterà di riconnettersi a un punto di accesso nel caso in cui sia disconnesso.                         // Imposta il risponditore MDNS                             Se (!MDNS.inizio(ESPHostname)) {                                 Seriale.println(F("Errore: MDNS"));                                 } altro { MDNS.addService("Http", "Tcp", 80); }                      }   mentre (( connRes == 4 ) e (io != 10))  // se connRes == 4 Password errata. A volte succede questo con PWD corrct     {        Wi-Fi.inizio(MyWiFiConfig.APSTAName, MyWiFiConfig.WiFiPwd);        connRes = Wi-Fi.waitForConnectResult();       ritardo(3000);       io++;       Seriale.Stampa(F("."));                      }   Se (connRes == 4 ) {                           Seriale.println(F("STA Pwd Err"));                                              Seriale.println(MyWiFiConfig.APSTAName);                         Seriale.println(MyWiFiConfig.WiFiPwd);                          Wi-Fi.disconnect();                       }   // if (connRes == 6) {Serial.println ("DISCONNECTED - Not in station station"); }   // WiFi.printDiag (seriale);
Seriale.println("");
ritorno connRes;
}

#definire SD_BUFFER_PIXELS 20
/ ** Carica credenziali WLAN da EEPROM * /
bool loadCredentials() 
{
 bool retvalue;
 EEPROM.inizio(512);
 EEPROM.ottenere(0, MyWiFiConfig);
 EEPROM.fine();
 Se (Corda(MyWiFiConfig.ConfigValid) = Corda("TK"))    {     retvalue = vero;   } altro   {     retvalue = falso; // Impostazioni WLAN non trovate.   }   ritorno retvalue;
}

/ ** Memorizza le credenziali WLAN su EEPROM * /

bool saveCredentials() 
{
bool retvalue;
// Verifica errori logici
retvalue = vero;
Se  (MyWiFiConfig.APSTA == vero ) // Modalità AP   {    Se (MyWiFiConfig.PwDReq e (taglia di(Corda(MyWiFiConfig.WiFiPwd)) < 8))     {           retvalue = falso;  // Config non valida     }    Se (taglia di(Corda(MyWiFiConfig.APSTAName)) < 1)     {       retvalue = falso;  // Config non valida     }   } 
Se (retvalue)   {   EEPROM.inizio(512);   per (int io = 0 ; io < taglia di(MyWiFiConfig) ; io++)       {       EEPROM.Scrivi(io, 0);      }   strncpy( MyWiFiConfig.ConfigValid , "TK", taglia di(MyWiFiConfig.ConfigValid) );   EEPROM.mettere(0, MyWiFiConfig);   EEPROM.commettere();   EEPROM.fine();   }   Ritorno RetValue (RetValue);
}

Vuoto SetDefaultWiFiConfig()
{    byte Len;    MyWiFiConfig.APSTA = Vero;    MyWiFiConfig.PwDReq = Vero;  PW predefinito richiesto    MyWiFiConfig.CapPortal = Vero;    Strncpy( MyWiFiConfig.Nome APSTA, "ESP_Config", Sizeof(MyWiFiConfig.Nome APSTA) );    Len = Strlen(MyWiFiConfig.Nome APSTA);    MyWiFiConfig.Nome APSTA[Len+1] = '\0';       Strncpy( MyWiFiConfig.WiFiPwd, "12345678", Sizeof(MyWiFiConfig.WiFiPwd) );    Len = Strlen(MyWiFiConfig.WiFiPwd);    MyWiFiConfig.WiFiPwd[Len+1] = '\0';      Strncpy( MyWiFiConfig.ConfigValid (Valido), "TK", Sizeof(MyWiFiConfig.ConfigValid (Valido)) );    Len = Strlen(MyWiFiConfig.ConfigValid (Valido));    MyWiFiConfig.ConfigValid (Valido)[Len+1] = '\0';     Seriale.println(F("Reimposta credenziali WiFi.")); 
}

Vuoto handleRoot() {
Pagina principale:
 Temp = "";
 byte Conteggio PicCount = 0;
 byte ServArgs = 0;   Intestazione HTML   Server.sendHeader (Intestazione)("Cache-Control", "no-cache, no-store, must-revalidate");   Server.sendHeader (Intestazione)("Pragma", "no-cache");   Server.sendHeader (Intestazione)("Scadenza", "-1");   Server.setContentLength (lunghezza in cui è impostato)(CONTENT_LENGTH_UNKNOWN);
Contenuto HTML   Server.Invia ( 200, "testo/html", Temp );   Speichersparen - Schon mal dem Cleint senden   Temp = "";   Temp += "<! HTML DOCTYPE><html lang>'de'><head><meta charset''UTF-8'><meta name'viewport content''width'device-width, initial-scale'1.0,'>";   Server.sendContent(Temp);   Temp = "";   Temp += "<tipo di stile>'testo/css'><!-- DIV.container : min-height: 10em; display:cella di tabella; vertical-align: middle .button : altezza:35px; width:90px; font-size:16px;   Server.sendContent(Temp);   Temp = "";   Temp += "corpo: colore di sfondo: powderblue; </style>";   Temp += "<head><title>Hauptseite</title></head>";   Temp += "<h2>Hauptseite</h2>";   Temp += "<corpo>";   Server.sendContent(Temp);   Temp = ""; 
Elaborazione della richiesta utente   Temp = "";    Temp += "<bordo tabella>2 bgcolor , larghezza del bianco , 500 cellpadding 5 ><didascalia><p><h3>Systemlinks:</h2></p></caption>";   Temp += "<tr><th><br>";   Temp += "<a href>'/wifi'>WIFI Einstellungen</a><br><br>";   Temp += "</th></tr></tabella><br><br>";   Temp += "<piè di pagina><p>Programmato e progettato da: Tobias Kuch</p><p>Informazioni di contatto: <a href>'mailto:tobias kuch@googlemail.com'>tobias.kuch@googlemail.com</a>.</p></footer>";   Temp += "</body></html>";   Server.sendContent(Temp);   Temp = "";   Server.Client().Fermare(); L'arresto è necessario perché non abbiamo inviato alcuna lunghezza del contenuto
}

Vuoto handleNotFound() {      Se (captivePortal())        { Se caprive portale reindirizzare invece di visualizzare la pagina di errore.         Ritorno;       }      Temp = "";      Intestazione HTML     Server.sendHeader (Intestazione)("Cache-Control", "no-cache, no-store, must-revalidate");     Server.sendHeader (Intestazione)("Pragma", "no-cache");     Server.sendHeader (Intestazione)("Scadenza", "-1");     Server.setContentLength (lunghezza in cui è impostato)(CONTENT_LENGTH_UNKNOWN);     Contenuto HTML     Temp += "<! HTML DOCTYPE><html lang>'de'><head><meta charset''UTF-8'><meta name'viewport content''width'device-width, initial-scale'1.0,'>";     Temp += "<tipo di stile>'testo/css'><!-- DIV.container : min-height: 10em; display:cella di tabella; vertical-align: middle .button : altezza:35px; width:90px; font-size:16px;     Temp += "corpo: colore di sfondo: powderblue; </style>";     Temp += "<capo><titolo>File non trovato</titolo></head>";     Temp += "<h2> 404 File non trovato</h2><br>";     Temp += "<h4>Informazioni di debug:</h4><br>";     Temp += "<corpo>";     Temp += "URI: ";     Temp += Server.Uri();     Temp += "NMetodo: ";     Temp+= ( Server.Metodo() == HTTP_GET ) ? "OTTIENI" : "Post" (Posta);     Temp += "<br>Argomenti: ";     Temp += Server.Args();     Temp += "N";       Per ( uint8_t Ho = 0; Ho < Server.Args(); Ho++ ) {         Temp += " " + Server.argName (nome arg) ( Ho ) + ": " + Server.Arg ( Ho ) + "N";         }     Temp += "<br>Intestazione host server: "+ Server.hostHeader (intestazione host)();     Per ( uint8_t Ho = 0; Ho < Server.Intestazioni(); Ho++ ) {         Temp += " " + Server.Headername ( Ho ) + ": " + Server.Intestazione ( Ho ) + "<br>";         }       Temp += "</tabella></form><br><br><bordo tabella 2 bgcolor lt;caption><p><h2>È possibile passare a:</h2></p></caption>";     Temp += "<tr><th>";     Temp += "<a href>Pagina principale</a><br>";     Temp += "<a href>Impostazioni WIFI</a><br>";     Temp += "</th></tr></tabella><br><br>";     Temp += "<piè di pagina><p>Programmato da: Tobias Kuch</p><p>Informazioni di contatto: <a href>'mailto:tobias.kuch@googlemail.com'>tobias.kuch@googlemail.com</a>.</p></footer>";     Temp += "</body></html>";     Server.Invia ( 404, "", Temp );     Server.Client().Fermare(); L'arresto è necessario perché non abbiamo inviato alcuna lunghezza del contenuto     Temp = "";      }

Se abbiamo ricevuto una richiesta per un altro dominio, reindirizza al portale captive. Restituisce true in questo caso in modo che il gestore di pagina non tenti di gestire nuovamente la richiesta. */
Boolean captivePortal() {   Se (!isIp (isIp)(Server.hostHeader (intestazione host)()) && Server.hostHeader (intestazione host)() != (Stringa(Nome host ESP)+".local")) {     Serial.println("Richiesta reindirizzata al portale captive");       Server.sendHeader (Intestazione)("Posizione", Stringa("http://") + toStringIp (Informazioni in base a toStringIp(Server.Client().localIP (informazioni in locale)()), Vero);     Server.Invia ( 302, "testo/semplice", ""); Il contenuto vuoto inibisce l'intestazione Content-length, quindi dobbiamo chiudere il socket da soli.     Server.Client().Fermare(); L'arresto è necessario perché non abbiamo inviato alcuna lunghezza del contenuto     Ritorno Vero;   }   Ritorno False;
}
 
/- Gestore di pagina di config Wifi .
Vuoto handleWifi() 
 {   Pagina: /wifi   byte Ho;   byte Len ;   Temp = "";   Verificare la presenza di parametri del sito         Se (Server.hasArg("Riavvia") )  Riavvia il sistema         {          Temp = "Riavvio del sistema in 5 secondi.";          Server.Invia ( 200, "testo/html", Temp );          Ritardo(5000);          Server.Client().Fermare();                Wifi.Scollegare();          Ritardo(1000);              }       Se (Server.hasArg("WiFiMode" (Modalità WiFi)) E (Server.Arg("WiFiMode" (Modalità WiFi)) == "1")  )  STA Station Mode Connettersi a un'altra stazione WIFI          {         startMillis = millis(); Reset Time Up Counter per evitare il funzionamento della modalità di inattività         Connettersi a STATION esistente         Se ( Sizeof(Server.Arg("WiFi_Network")) > 0  )           {             Seriale.println("Modalità STA");             MyWiFiConfig.APSTA = False; Modalità punto di accesso o stazione - modalità stazione falsa             Temp = "";                       Per ( Ho = 0; Ho < NomeAPSTALen;Ho++) { MyWiFiConfig.Nome APSTA[Ho] =  0; }             Temp = Server.Arg("WiFi_Network");             Len =  Temp.Lunghezza();             Per ( Ho = 0; Ho < Len;Ho++)              {                    MyWiFiConfig.Nome APSTA[Ho] =  Temp[Ho];                           }             Temp = "";             Per ( Ho = 0; Ho < WiFiPwdLen;Ho++)  { MyWiFiConfig.WiFiPwd[Ho] =  0; }                       Temp = Server.Arg("STAWLanPW");             Len =  Temp.Lunghezza();                                  Per ( Ho = 0; Ho < Len;Ho++)                 {                  Se (Temp[Ho] > 32) Steuerzeichen Raus                   {                    MyWiFiConfig.WiFiPwd[Ho] =  Temp[Ho];                     }                 }             Temp = "WiFi Connect to AP: -";              Temp += MyWiFiConfig.Nome APSTA;             Temp += "-<br>WiFi PW: -";              Temp += MyWiFiConfig.WiFiPwd;             Temp += "-<br>";                       Temp += "Connessione alla modalità STA in 2 secondi.. <br>";             Server.Invia ( 200, "testo/html", Temp );              Server.sendContent(Temp);             Ritardo(2000);             Server.Client().Fermare();              Server.Fermare();             Temp = "";             Wifi.Scollegare();             Wifi.softAPdisconnect (softAPdisconnect)(Vero);             Ritardo(500);            ConnectWifiAP (Connessione WifiAP)            Bool SaveOk (Ok) = saveCredentials (informazioni in base alle credenziali del servizio();                       Ho = ConnectWifiAP (Connessione WifiAP)();             Ritardo(700);             Se (Ho != 3) 4: WL_CONNECT_FAILED - Password non corretta 1: WL_NO_SSID_AVAILin - Impossibile raggiungere l'SSID configurato               {                  Seriale.Stampare(F("Impossibile connettersi alla rete specificata. Motivo: "));                  Seriale.println(Ho);                  Server.Client().Fermare();                  Ritardo(100);                               Wifi.setAutoReconnect (False);                  Ritardo(100);                       Wifi.Scollegare();                               Ritardo(1000);                  SetDefaultWiFiConfig();                  CreateWifiSoftAP (Informazioni in base ai pulsanti di sè)();                  Ritorno;                } Altro               {                  Configurazione sicura                  Bool SaveOk (Ok) = saveCredentials (informazioni in base alle credenziali del servizio();                  InitalizeHTTPServer();                  Ritorno;               }           }        }                 Se (Server.hasArg("WiFiMode" (Modalità WiFi)) E (Server.Arg("WiFiMode" (Modalità WiFi)) == "2")  )  Cambia modalità AP         {         startMillis = millis(); Reset Time Up Counter per evitare il funzionamento della modalità di inattività         Configurare il punto di accesso         Temp = Server.Arg("NomeAPPoint");               Len =  Temp.Lunghezza();         Temp =Server.Arg("APPW");         Se (Server.hasArg("PasswordReq"))             {                     Ho =  Temp.Lunghezza();             } Altro { Ho = 8; }                  Se (  ( Len > 1 ) E (Server.Arg("APPW") == Server.Arg("APPWRipeti")) E ( Ho > 7)          )           {             Temp = "";             Seriale.println(F("APMode" (Modalità API)));             MyWiFiConfig.APSTA = Vero; Punto di accesso o modalità di sonnia - vera modalità AP                                     Se (Server.hasArg("CaptivePortal"))             {               MyWiFiConfig.CapPortal = Vero ; CaptivePortal in modalità AP             } Altro { MyWiFiConfig.CapPortal = False ; }                          Se (Server.hasArg("PasswordReq"))             {               MyWiFiConfig.PwDReq = Vero ; Password richiesta in modalità AP             } Altro { MyWiFiConfig.PwDReq = False ; }             Per ( Ho = 0; Ho < NomeAPSTALen;Ho++) { MyWiFiConfig.Nome APSTA[Ho] =  0; }             Temp = Server.Arg("NomeAPPoint");             Len =  Temp.Lunghezza();             Per ( Ho = 0; Ho < Len;Ho++) { MyWiFiConfig.Nome APSTA[Ho] =  Temp[Ho]; }             MyWiFiConfig.Nome APSTA[Len+1] = '\0';               Temp = "";             Per ( Ho = 0; Ho < WiFiPwdLen;Ho++)  {  MyWiFiConfig.WiFiPwd[Ho] =  0; }                       Temp = Server.Arg("APPW");             Len =  Temp.Lunghezza();                                  Per ( Ho = 0; Ho < Len;Ho++)  { MyWiFiConfig.WiFiPwd[Ho] =  Temp[Ho];  }             MyWiFiConfig.WiFiPwd[Len+1] = '\0';               Temp = "";                      Se (saveCredentials (informazioni in base alle credenziali del servizio()) Salva AP ConfigCongfig                 {                         Temp = "Daten des AP Modes erfolgreich gespeichert. Riavviare notwendig.";               } Altro  { Temp = "Daten des AP Modes fehlerhaft.";  }           } Altro Se (Server.Arg("APPW") != Server.Arg("APPWRipeti"))                 {                   Temp = "";                   Temp = "WLAN Passwort nicht gleich. Abgebrochen ".;                 } Altro                 {                               Temp = "";                   Temp = "WLAN Passwort oder AP Nome zu kurz. Abgebrochen ".;                 }               }      Intestazione HTML   Server.sendHeader (Intestazione)("Cache-Control", "no-cache, no-store, must-revalidate");   Server.sendHeader (Intestazione)("Pragma", "no-cache");   Server.sendHeader (Intestazione)("Scadenza", "-1");   Server.setContentLength (lunghezza in cui è impostato)(CONTENT_LENGTH_UNKNOWN);
Contenuto HTML   Temp += "<! HTML DOCTYPE><html lang>'de'><head><meta charset''UTF-8'><meta name'viewport content''width'device-width, initial-scale'1.0,'>";   Server.Invia ( 200, "testo/html", Temp );    Temp = "";    Temp += "<tipo di stile>'testo/css'><!-- DIV.container : min-height: 10em; display:cella di tabella; vertical-align: middle .button : altezza:35px; width:90px; font-size:16px;   Temp += "corpo: colore di sfondo: powderblue; </style><head><title>Smartes Tuerschild - Impostazioni WiFi</title></head>";   Server.sendContent(Temp);   Temp = "";   Temp += "<h2>WiFi Einstellungen</h2><corpo><sinistra>";   Temp += "<bordo tabella>2 bgcolor , larghezza bianca , 500 ><td><h4>Impostazioni WiFi correnti: </h4>";   Se (Server.Client().localIP (informazioni in locale)() == apIP) {      Temp += "Modalità : Punto di accesso soft (AP)<br>";      Temp += "SSID : " + Stringa (MyWiFiConfig.Nome APSTA) + "<br><br>";   } Altro {      Temp += "Modalità: Stazione (STA) <br>";      Temp += "SSID : "+ Stringa (MyWiFiConfig.Nome APSTA) + "<br>";      Temp += "BSSID : " + Wifi.BSSIDstr()+ "<br><br>";   }   Temp += "</td></tabella><br>";   Server.sendContent(Temp);   Temp = "";   Temp += "<azione di forma''/wifi' metodo''post'>";   Temp += "<bordo tabella>2 bgcolor , larghezza del bianco , 500><tr><th><br>";   Se (MyWiFiConfig.APSTA == 1)     {       Temp += "<tipo di ingresso'valore 'radio' '1' nome''WiFiMode' > Modalità stazione WiFi<br>";     } Altro     {       Temp += "<tipo di ingresso'valore 'radio' '1' nome''WiFiMode' checked > Modalità stazione WiFi<br>";     }   Temp += "Reti WiFi disponibili:<bordo tabella>2 bgcolor ></tr></th><td>Numero </td><< td>SSID </td><td>Crittografia </td><td>Forza WiFi </td>";   Server.sendContent(Temp);   Temp = "";   Wifi.scanDelete();   Int N = Wifi.scanNetworks(False, False); WiFi.scanNetworks(async, show_hidden)   Se (N > 0) {     Per (Int Ho = 0; Ho < N; Ho++) {     Temp += "</tr></th>";     Stringa Nrb = Stringa(Ho);     Temp += "<td>" + Nrb + "</td>";     Temp += "<td>" + Wifi.Ssid(Ho) +"</td>";         Nrb = Tipo di crittografia GetEncryptionType(Wifi.encryptionType (tipo di crittografia)(Ho));     Temp += "<td>"+ Nrb + "</td>";     Temp += "<td>" + Stringa(Wifi.Rssi(Ho)) + "</td>";        }   } Altro {     Temp += "</tr></th>";     Temp += "<td>1 </td>";     Temp += "<td>Nessuna WLAN trovata</td>";     Temp += "<td> --- </td>";     Temp += "<td> --- </td>";   }   Temp += "</tabella><table border>2 bgcolor </tr></th><td>Connetti a SSID WiFi: </td><td><selezionare nome<'WiFi_Network''>";
Se (N > 0) {     Per (Int Ho = 0; Ho < N; Ho++) {     Temp += "<valore opzione'' + Wifi.Ssid(Ho) +"'>" + Wifi.Ssid(Ho) +"</opzione>";           }   } Altro {     Temp += "<valore opzione:'No_WiFi_Network'>Nessuna rete WiFi (nessuna rete WiFi)!/opzione>";   }   Server.sendContent(Temp);   Temp = "";   Temp += "</select></td></tr></th></tr></th><td>Password WiFi: </td><td>";   Temp += "<tipo di input''testo' nome''STAWLanPW' maxlength''40' dimensione ''40'>";    Temp += "</td></tr></th><br></th></tr></table></table><table border<2 bgcolor - 500 ><tr><th><<table border<<table border<2 bgcolor;   Server.sendContent(Temp);   Temp = "";   Se (MyWiFiConfig.APSTA == Vero)     {       Temp += "<tipo di ingresso:'radio' nome''WiFiMode' valore''2' checked> Modalità punto di accesso WiFi <br>";     } Altro     {       Temp += "<tipo di ingresso>'radio' nome'''WiFiMode' value''2' > Modalità punto di accesso WiFi <br>";     }   Temp += "<bordo tabella>2 bgcolor <</tr></th> <td>Nome punto di accesso WiFi: </td><td>";     Server.sendContent(Temp);   Temp = "";               Se (MyWiFiConfig.APSTA == Vero)     {       Temp += "<tipo di input''testo' nome''NomeAPPoint' maxlength''"+Stringa(NomeAPSTALen-1)+"' dimensione''30' valore'' + Stringa(MyWiFiConfig.Nome APSTA) + "'></td>";     } Altro     {       Temp += "<tipo di input''testo' nome''NomeAPPoint' maxlength''"+Stringa(NomeAPSTALen-1)+"' dimensione:'30' ></td>";     }   Server.sendContent(Temp);   Temp = "";          Se (MyWiFiConfig.APSTA == Vero)     {       Temp += "</tr></th><td>Password WiFi: </td><td>";       Temp += "<tipo di input''password' nome''APPW' maxlength''"+Stringa(WiFiPwdLen-1)+"' dimensione''30' valore'' + Stringa(MyWiFiConfig.WiFiPwd) + "'> </td>";       Temp += "</tr></th><td>Ripeti password WiFi: </td>";       Temp += "<td><tipo di input''password' nome''APPWRepeat' maxlength''"+Stringa(WiFiPwdLen-1)+"' dimensione''30' valore'' + Stringa(MyWiFiConfig.WiFiPwd) + "'> </td>";     } Altro     {       Temp += "</tr></th><td>Password WiFi: </td><td>";       Temp += "<tipo di input''password' nome''APPW' maxlength''"+Stringa(WiFiPwdLen-1)+"'dimensione:'30'> </td>";       Temp += "</tr></th><td>Ripeti password WiFi: </td>";       Temp += "<td><tipo di input''password' nome''APPWRepeat' maxlength''"+Stringa(WiFiPwdLen-1)+"'dimensione:'30'> </td>";     }       Temp += "</tabella>";   Server.sendContent(Temp);   Temp = "";         Se (MyWiFiConfig.PwDReq)     {       Temp += "<tipo di input:'casella di controllo' nome''PasswordReq' checked> Password per l'accesso richiesto. ";      } Altro     {       Temp += "<tipo di input:'checkbox' name'''PasswordReq' > Password per l'accesso richiesta. ";      }   Server.sendContent(Temp);   Temp = "";     Se (MyWiFiConfig.CapPortal)     {       Temp += "<tipo di input:'checkbox' name'''CaptivePortal' checked> Attiva Captive Portal';      } Altro     {       Temp += "<tipo di input:'checkbox' name''''CaptivePortal' > Attiva captive Portal";      }   Server.sendContent(Temp);   Temp = "";     Temp += "<br></tr></th></table><br> <button type''submit' submit name'''Settings' value''1' style''height: 50px; larghezza: autofocus di 140px>Imposta impostazioni WiFi</pulsante>";   Temp += "<tipo di pulsante''submit' name'''Reboot' value''1' style''height: 50px; larghezza: 200px' > Sistema di riavvio</pulsante>";   Server.sendContent(Temp);   Temp = "";   Temp += "<tipo di pulsante''reset' name'''action' value''1' style''height: 50px; larghezza: 100px' >Reimposta</pulsante></form>";   Temp += "<bordo tabella>2 bgcolor , larghezza del bianco , 500 cellpadding 5 ><didascalia><p><h3>Collegamenti di sistema:</h2></p></captionlt;tr><th><br>";   Server.sendContent(Temp);   Temp = "";   Temp += "<a href>Pagina principale</a><br><br></th></tr></tabella>";   Temp += "<piè di pagina><p>d;Programmato e progettato da: Tobias Kuch</p><p>Informazioni di contatto: <a href>'mailto:tobias.kuch@googlemail.com'>tobias kuch@googlemail.com</a>.</p</footer>";   Temp += "</body></html>";     Server.sendContent(Temp);   Server.Client().Fermare(); L'arresto è necessario perché non abbiamo inviato alcuna lunghezza del contenuto   Temp = "";
}


Si tratta di un indirizzo IP? */
Boolean Isip(Stringa via) {   Per (Int Ho. = 0; Ho. < via.Lunghezza(); Ho.++) {     Int C = via.charAt(Ho.);     Se (C != '.' && (C < '0' || C > '9')) {       Ritorno False;     }   }   Ritorno Vero;
}

Stringa Tipo di crittografia GetEncryptionType(Byte ThisType (tipo)) {   Stringa Output = "";    leggere il tipo di crittografia e stampare il nome:    Interruttore (ThisType (tipo)) {      Caso 5:        Output = "WEP";        Ritorno Output;        Pausa;      Caso 2:        Output = "WPA";        Ritorno Output;        Pausa;      Caso 4:        Output = "WPA2";        Ritorno Output;        Pausa;      Caso 7:        Output = "Nessuno";        Ritorno Output;        Pausa;      Caso 8:        Output = "Auto";        Ritorno Output;       Pausa;    }
}

da IP a stringa? */
Stringa toStringIp (Informazioni in base a toStringIp(Ipaddress Ip) {   Stringa Res = "";   Per (Int Ho. = 0; Ho. < 3; Ho.++) {     Res += Stringa((Ip >> (8 * Ho.)) & 0xff) + ".";   }   Res += Stringa(((Ip >> 8 * 3)) & 0xff);   Ritorno Res;
}

Stringa formatBytes (Byte di formato)(Size_t Byte) {            visualizzazione leggibile delle dimensioni di memoria    Se (Byte < 1024) {      Ritorno Stringa(Byte) + " Byte";    } Altro Se (Byte < (1024 * 1024)) {      Ritorno Stringa(Byte / 1024.0) + " KB";    } Altro Se (Byte < (1024 * 1024 * 1024)) {      Ritorno Stringa(Byte / 1024.0 / 1024.0) + " MB ";    }
 }

Vuoto Ciclo() 
 {     Se (SoftAccOK)   {     server dns.processNextRequest (richiesta di processosuccessivo)(); Dns   }   HTTP (informazioni in due   Server.handleClient();     }

]

 

Nella parte successiva vogliamo esaminare l'uso pratico del nostro codice e compilare un piccolo file server basato su questo codice.

Vi auguro un sacco di divertimento con il captive Portal e quando lo implemento nei miei progetti ESP32.

Esp-8266Projekte für fortgeschrittene

18 commenti

Walter

Walter

Hi, this code is bugged, please fix it. Thanks,

Jan

Jan

Hallo Gerald, herzlichsten Dank!!! Ich hatte da schon einige Stunden herumprobiert, aber immer die falsche Stelle erwischt. Es ist exakt so wie beschrieben: durch das Auskommentieren dieser Zeile in der setup() werden die Credentials nicht überschrieben, dennoch wird ein AP angelegt mit den hinterlegten Zugangsdaten. Dadurch ist es egal, wenn das Gerät mal nicht ins WLAN kommen sollte, der AP stört ja nicht und ließe sich auch durch einen Button aktivieren. Danke nochmal!

Gerald Lechner

Gerald Lechner

Hallo Jan
Ich würde in der Setup-Routine
if ((ConnectSuccess or CreateSoftAPSucc))
{
Serial.print (F("IP Address: “));
if (CreateSoftAPSucc) { Serial.println(WiFi.softAPIP());}
if (ConnectSuccess) { Serial.println(WiFi.localIP());}
InitalizeHTTPServer();
}
else
{
Serial.setDebugOutput(true); //Debug Output for WLAN on Serial Interface.
Serial.println(F(”Error: Cannot connect to WLAN. Set DEFAULT Configuration."));
SetDefaultWiFiConfig();
CreateSoftAPSucc = CreateWifiSoftAP();
InitalizeHTTPServer();
SetDefaultWiFiConfig();
saveCredentials();
}
}
die Zeile saveCredentials(); auskommentieren. Die wird nur dann aufgerufen, wenn keine Verbindung möglich war. Wenn die Credentials hier nicht gespeichert werden, geht der ESP32 zwar in den AP-Modus, speichert diesen Zustand aber nicht ab. Damit werden beim nächsten Start wieder die abgespeicherten Credentials für den Station-Mode genutzt.

Jan

Jan

Herzlichen Dank für den tollen Sketch, läuft mit den kleinen Korrekturen soweit sehr gut. Leider werden die manuell gespeicherten WLAN-Daten gelöscht, sobald das WLAN beim Start nicht verfügbar ist. Offenbar werden die Zugangsdaten für AP und STA an der gleichen Stelle im eeprom gespeichert, sodass die vorigen Zugangsdaten unwiederbringlich gelöscht werden, sobald das Gerät in den AP-Modus wechselt. Frage: wie ließen sich die AP-Zugangdaten hart festschreiben, um zu verhindern, dass die eingetragenen WLAN-Daten im AP-Modus gelöscht werden? Im eeprom möchte ich ausschließlich die Daten für den STA-Modus speichern.

Jan

Jan

Ein sehr schöner Sketch, herzlichen Dank für die Arbeit und Veröffentlichung. Mit den Tipps aus den Kommentaren läuft das soweit sehr zuverlässig auf meinem ESP32.
Leider stört ein ‘Feature’ in meinem Projekt ganz erheblich: Findet der ESP beim Starten das vorher konfigurierte WLAN nicht, werden die Zugangsdaten komplett gelöscht. Das ist bei temporärem oder instalbilem WLAN ziemlich störend. Meine Frage in die Runde: an welcher Stelle kann ich das am besten verhindern? Die Routine für den Reset-Knopf möchte ich ja nicht komplett löschen. Ziel: WLAN-Zugangsdaten werden nur durch Reset oder Überschreiben gelöscht, nicht aber automatisch.
Danke für Tipps!!!

Achim

Achim

Hi kann mir einer schreiben wie diese ESPmDNS.h finde und einbinde .
Normal kann ich Bibilotheken einbinden aber diese finde ich nicht oder die IDE meckert das die Zip keine Bibliothek enthält

Tobias

Tobias

Sehr geehrter Herr Holler,

Ich muss leider 2 Punkte korrigieren. Zum einen ist die Beschränkung auf 20 Zeichen der SSID kein Fehler sondern eine Limitierung, die im Sinne des open Source Gedankens angepasst werden kann. Zum anderen werden Tests der Software beim Entwickeln und kursorische Abschlusstests vor Veröffentlichung durchgeführt. Ihre generalisierte Aussage, das Tests überhaupt nicht durchgeführt werden ist daher nicht korrekt.
Jedoch sind generell leider auch zeitliche Limitierungen bei der Entwicklung einzuhalten, sodass eben Bugs nicht ganz vermieden werden können. Ich freue mich jedoch, das diese sich bisher auf ein Bug bei einem komplexem Code wie diesen beschränken. Sachliche Hinweise auf diese oder für alle relevante Verbesserungswünsche, ohne Wertung, sind daher immer willkommen und werden im nächsten Release ggf. berücksichtigt.

Walter Holler

Walter Holler

Wäre es zu viel verlangt, den veröffentlichten Code vorher mal selbst auszuprobieren?
Ich habe den Code auf meinen neu gekauften ESP32 geladen und mindestens 2 Fehler gefunden:
1) Der Vergleichs-Operator für Gleichheit ist “==”. Das einfache “=” ist eine Zuweisung.
Also in loadCredentials() :
if (String(MyWiFiConfig.ConfigValid) == String(“TK”))
statt
if (String(MyWiFiConfig.ConfigValid) = String(“TK”))
2) Die maximale Länge der SSID Namen ist 32 nicht 20, sonst funktioniert es nicht bei längeren Namen wie bei mir.
static const byte APSTANameLen = 32; //was 20
Dann funktioniert auch der AP Mode, Auswahl des WLANs und Eingabe des Passwords und Wechsel in mein WLAN.
Jedoch nach Reset des ESP32 startet er wieder im AP mode..?

MCL

MCL

|ich kann kein ESP_Config finden… Nur ein ESP_C9013b….

das liegt auch an ‘alten’ credentials.
‘C9013b’ ist der zweite Teil der MAC Adresse…
Man kann die Kommandos zum Überschreiben der Credantials ‘zu Fuss’ schreiben oder so:
// if (false) // overwrite only ONCE!!
if (loadCredentials()) // Load WLAN credentials for WiFi Settings

Also einmals mit ’ if (false) ’ kompilieren und laden.
=> so werden die Credentials garantiert korrekt überschrieben
Danach wieder auskommentieren und statt dessen ’ if (loadCredentials()) ’ verwenden und laden.

Manfred

Manfred

Einfach den Code in einen vernünftigen Editor (z.B. Notpad++) kopieren und schon siehst Du, dass diese Klammer das Gegenstück von Zeile 1 ist. Fehlersuche dauerte nicht länger als die Eingabe Deiner Kommentars.

Tobias

Tobias

Hallo Matthias,
Gleiches Problem und gleiche wie bei Christoph.
Gruß

Tobias

Tobias

Hallo Christoph,
Dies liegt darin begründet, das der ESP intern die Credentials speichert, (von deinem vorherigen Projekt) . Um diese loszuwerden, führe EINMALIG in der Setup Routine folgende Sequenz aus:
SetDefaultWiFiConfig();
saveCredentials();
CreateSoftAPSucc = CreateWifiSoftAP();
Damit werden die Daten von dem Vorprojekt überschrieben. Nachdem diese überschrieben wurden, können die Zeilen wieder entfernt werden.

Wolfgang

Wolfgang

Gibt es eigentlich auch einen Teil 1?

Ulrich Klaas

Ulrich Klaas

Hallo,
im Code für den 8266 ist die Fehlerbehebung aber noch nicht drin ??
Der hat sich nicht verändert. Geschieht das noch ?

mfG
Ulli

Peter Pitzeier

Peter Pitzeier

In bool loadCredentials() muss natürlich
if (String(MyWiFiConfig.ConfigValid) == String(“TK”))
und nicht
if (String(MyWiFiConfig.ConfigValid) = String(“TK”))

Christoph O

Christoph O

Danke für das CapturePortal-Besipiel!
Ich habe bei mir das Problem, dass ich vor einiger Zeit bereits einmal einen anderen WLAN-Accesspoint geflasht habe. Die SSID habe ich damals “Heinzelmännchen” genannt.
Nun erscheint nach dem flashen dieses Beispiels wieder der Name “Heinzelmännchen” als SSID und nicht der angegebene “ESP_Config”. Ich vermute, dass “Heinzelmännchen” noch aus alten Zeiten im EEPROM steht und nicht überschrieben wurde.
Ich kann mich leider nicht mit dem Passwort “12345678” verbinden. Das alte Passwort von Heinzelmännchen kenne ich leider nicht mehr.
Kann mir irgendwer weiterhelfen?
Klar, ich könnte wieder ein eigenen WLAN-Accesspoint flashen und alles überschreiben aber hier scheint trotzdem noch ein Bug im Quelltext zu sein.

Matthias

Matthias

Hallo Zusammen,

ich kann kein ESP_Config finden… Nur ein ESP_C9013b….
Das Passwort funktioniert bei dem letzteren auch nicht.
Ich habe nichts an dem Arduino scetch verändert.

Hat sonst jemand noch dieses Problem?

Ich benutze ein Lolin D32 Pro mit einem ESP32 Wrover Chip

Danke und Gruss

Matthias

Joe

Joe

Die

]

gehört wo hin ????

Lascia un commento

Tutti i commenti vengono moderati prima della pubblicazione