Ciao e benvenuto a un blog speciale:
Oggi non dovrebbe trattarsi di un progetto finito, ma di aiutare molti dei propri progetti intorno all'argomento dei dati di accesso WLAN e ESP8266. Spesso il problema esiste con i propri progetti, i dati di accesso WLAN sono programmati in modo permanente nel proprio codice, ma non possono più essere modificati in seguito o l'ESP "dimentica" i suoi dati di accesso WLAN non appena avviene un riavvio. Entrambi sono problematici per una certa flessibilità di configurazione. La soluzione per questo è quello di memorizzare i dati di accesso WLAN nell'EEPROM interno e per garantire che questi possono essere modificati in qualsiasi momento, anche alla durata del firmware.
A tale scopo, l'ESP utilizza il nostro successivo Codice Captive Portal come primo passo Portale Captive Wi-Fi con il nome "My_WLANDevice" e la password "12345678". Con questo possiamo connetterci al nostro telefono cellulare, e vengono quindi automaticamente diretti dal telefono cellulare al sito web Captive Portal. Questo aspetto è il seguente:
Su questo possiamo ora fare clic sul link di sistema "Impostazioni WiFi" e ora arriviamo a una pagina di configurazione WLAN completa, con la quale possiamo ora selezionare sia una rete con cui l'ESP dovrebbe connettersi:
La rete wireless selezionata qui e la password immessa vengono memorizzate nell'EEPROM. Sulla barca successiva, l'ESP tenta di connettersi a questa rete. Se il tentativo non riesce perché la rete non è più raggiungibile, ad esempio, o la password è stata modificata, l'ESP torna alla modalità punto di accesso e attende una riconfigurazione.
Il codice per il captive Portal è:
#include <ESP8266WiFi.H> #include <WiFiClient (Client WiFi).H> #include <Server Web ESP8266.H> #include <ESP8266mDNS (dns).H> #include <Server DNS.H> #include <Eeprom.H> Statico Const Byte WiFiPwdLen = 25; Statico Const Byte NomeAPSTALen = 20; Statico Const uint8_t D0 = 16; Statico Const uint8_t D1 = 5; Statico Const uint8_t D2 = 4; Statico Const uint8_t D3 = 0; Statico Const uint8_t D4 = 2; Statico Const uint8_t D5 = 14; Statico Const uint8_t D6 = 12; Statico Const uint8_t D7 = 13; Statico Const uint8_t D8 = 15; Statico Const uint8_t D9 = 3; Statico Const uint8_t D10 = 1; Struct WiFiEEPromData { Bool APSTA = Vero; Punto di accesso o modalità di sonnia - vera modalità AP Bool PwDReq = False; PasswordObbligatorio Bool CapPortal = Vero ; CaptivePortal in modalità AP Char Nome APSTA[NomeAPSTALen]; STATION /AP Nome punto A cONNECT, se defondato Char WiFiPwd[WiFiPwdLen]; WiFiPAssword, se detrovato Char ConfigValid (Valido)[3]; Se Config è Vaild, è necessario il tag "TK"" }; Statico Const Breve Int BUILTIN_LED0 = D0; GPIO0 /o nome host per mDNS. Dovrebbe funzionare almeno sulle finestre. Provare http://esp8266.local */ Const Char *Nome host ESP = "ESP"; Server DNS Const Byte DNS_PORT = 53; Server DNS server dns; Conmmon Paramenters Bool SoftAccOK = False; Server Web Server Web ESP8266 Server(80); // Parametri di rete Soft AP Ipaddress apIP(172, 20, 0, 1); Ipaddress netMsk(255, 255, 255, 0); Devo connettermi al Wi-Fi al più presto? */ Boolean connettersi; L'ultima volta che ho provato a connettermi a una rete Wi-Fi / lungo lastConnectTry - 0; Unsigned Lungo currentMillis = 0; Unsigned Lungo startMillis; Const Breve Periodo = 10; Sonno dopo questo minuti di inattività /z: stato Wi-Fi corrente // Breve Stato = WL_IDLE_STATUS; WiFiEEPromData MyWiFiConfig; Stringa Temp = ""; Vuoto Installazione() { Bool ConnectSuccess = False; Bool CreateSoftAPSucc (informazioni in base a un'applicazione di un'azione = False; Bool Sistema CInitFS = False; Bool CInitHTTPServer (server CInitHTTPServer) = False; Byte Len; PinMode (Modalità pin)(D0, Output); Inizializzare il pin BUILTIN_LED1 come output Seriale.Iniziare(9600); Seriale.println(); Wifi.Hostname(Nome host ESP); Impostare il nome host DHCP assegnato alla stazione ESP. Se (loadCredentials()) Caricare le credenziali WLAN per le impostazioni WiFi { Credenziali valide trovate. Se (MyWiFiConfig.APSTA == Vero) Modalità AP { Serial.println("Modalità PA"); Serial.println(MyWiFiConfig.APSTA); Len = Strlen(MyWiFiConfig.Nome APSTA); MyWiFiConfig.Nome APSTA[Len + 1] = '\0'; Len = Strlen(MyWiFiConfig.WiFiPwd); MyWiFiConfig.WiFiPwd[Len + 1] = '\0'; CreateSoftAPSucc (informazioni in base a un'applicazione di un'azione = CreateWifiSoftAP (Informazioni in base ai pulsanti di sè)(); } Altro { Serial.println("Modalità STA"); Len = Strlen(MyWiFiConfig.Nome APSTA); MyWiFiConfig.Nome APSTA[Len + 1] = '\0'; Len = Strlen(MyWiFiConfig.WiFiPwd); MyWiFiConfig.WiFiPwd[Len + 1] = '\0'; Len = ConnectWifiAP (Connessione WifiAP)(); Se ( Len == 3 ) { ConnectSuccess = Vero; } Altro { ConnectSuccess = False; } } } Altro { Imposta configurazione predefinita - Crea AP Seriale.println("DefaultWiFi Cnf"); SetDefaultWiFiConfig (); CreateSoftAPSucc (informazioni in base a un'applicazione di un'azione = CreateWifiSoftAP (Informazioni in base ai pulsanti di sè)(); saveCredentials (informazioni in base alle credenziali del servizio(); Lampeggiare digitalWrite (Scrittura digitale)(D0, Basso); Tirare a LOW _Led ON Ritardo(500); digitalWrite (Scrittura digitale)(D0, alto); Ritardo(500); digitalWrite (Scrittura digitale)(D0, Basso); Tirare a LOW _Led ON Ritardo(500); digitalWrite (Scrittura digitale)(D0, alto); Ritardo(500); digitalWrite (Scrittura digitale)(D0, Basso); Tirare a LOW _Led ON } Se ((ConnectSuccess O CreateSoftAPSucc (informazioni in base a un'applicazione di un'azione) E Sistema CInitFS) { InitalizeHTTPServer(); digitalWrite (Scrittura digitale)(D0, Basso); Tirare a LOW _Led ON Seriale.println("OK"); } Altro { Seriale.setDebugOutput(Vero); Output di debug per WLAN sull'interfaccia seriale. Seriale.Stampare("Err"); SetDefaultWiFiConfig (); CreateSoftAPSucc (informazioni in base a un'applicazione di un'azione = CreateWifiSoftAP (Informazioni in base ai pulsanti di sè)(); saveCredentials (informazioni in base alle credenziali del servizio(); InitalizeHTTPServer(); } startMillis = millis(); ora di inizio iniziale } Vuoto InitalizeHTTPServer() { Bool initok = False; /z Pagine web di installazione: root, pagine di configurazione wifi, così rilevatori di portali captive e non trovati. */ Server.su("/", handleRoot); Server.su("/wifi", handleWifi); Se (MyWiFiConfig.CapPortal) { Server.su("/generate_204", handleRoot); Portale captive Android. Forse non ne ha bisogno. Potrebbe essere gestito dal gestore notFound. } Se (MyWiFiConfig.CapPortal) { Server.su("/favicon.ico", handleRoot); Un altro portale captive Android. Forse non ne ha bisogno. Potrebbe essere gestito dal gestore notFound. Controllato su Sony Handy } Se (MyWiFiConfig.CapPortal) { Server.su("/fwlink", handleRoot); Portale captive Microsoft. Forse non ne ha bisogno. Potrebbe essere gestito dal gestore notFound. } server.on("/generate_204", handleRoot); Portale captive Android. Forse non ne ha bisogno. Potrebbe essere gestito dal gestore notFound. server.on("/favicon.ico", handleRoot); Un altro portale captive Android. Forse non ne ha bisogno. Potrebbe essere gestito dal gestore notFound. Controllato su Sony Handy server.on("/fwlink", handleRoot); Portale captive Microsoft. Forse non ne ha bisogno. Potrebbe essere gestito dal gestore notFound. Server.onNotFound (innotiere) ( handleNotFound ); Speicherung Header-Elemente anfordern server.collectHeaders(Intestazioni, sizeof(Headers)/ sizeof(Headers[0])); Server.Iniziare(); Avvio del server Web } Boolean CreateWifiSoftAP (Informazioni in base ai pulsanti di sè)() { Wifi.Scollegare(); Seriale.Stampare("SoftAP"); Wifi.softAPConfig (informazioni in base alle(apIP, apIP, netMsk); Se (MyWiFiConfig.PwDReq) { SoftAccOK = Wifi.softAP(MyWiFiConfig.Nome APSTA, MyWiFiConfig.WiFiPwd); Passwortl'nge mindestens 8 zeichen ! } Altro { SoftAccOK = Wifi.softAP(MyWiFiConfig.Nome APSTA); Punto di accesso senza password Funzione di overload:; WiFi.softAP(ssid, password, canale, nascosto) } Ritardo(600); Senza indugio ho visto l'indirizzo IP vuoto Se (SoftAccOK) { Seriale.println("OK"); Seriale.println(MyWiFiConfig.Nome APSTA); Seriale.println(MyWiFiConfig.WiFiPwd); // Impostare il server DNS reindirizzando tutti i domini all'apIP / server dns.setErrorReplyCode(Codice DI accesso A DNSReply::Nessunerrore); server dns.Iniziare(DNS_PORT, "*", apIP); } Altro { Seriale.println("errate"); Seriale.println(MyWiFiConfig.Nome APSTA); Seriale.println(MyWiFiConfig.WiFiPwd); } Ritorno SoftAccOK; } byte ConnectWifiAP (Connessione WifiAP)() { Serial.println("Inizializzazione del client Wifi."); byte connRes = 0; byte Ho = 0; Wifi.Scollegare(); Wifi.softAPdisconnect (softAPdisconnect)(Vero); Function imposterà SSID attualmente configurato e la password del soft-AP su valori null. Il parametro è facoltativo. Se impostato su true, la modalità soft-AP verrà disattivata. Wifi.Iniziare(MyWiFiConfig.Nome APSTA, MyWiFiConfig.WiFiPwd); connRes = Wifi.waitForConnectResult(); Ritardo(500); Mentre (( connRes == 0 ) E (Ho != 10)) se connRes : 0 "IDLE_STATUS - modificare Statius" { connRes = Wifi.waitForConnectResult(); Ritardo(1000); Ho++; Seriale.println("."); istruzione/i } Mentre (( connRes == 1 ) E (Ho != 10)) se non è possibile raggiungere connRes n. 1 NO_SSID_AVAILin - SSID { connRes = Wifi.waitForConnectResult(); Ritardo(1000); Ho++; Seriale.println("."); istruzione/i } Se (connRes == 3 ) { Seriale.Stampare("STA"); Wifi.setAutoReconnect(Vero); Impostare se il modulo tenterà di riconnettersi a un punto di accesso nel caso in cui sia disconnesso. Configurare il risponditore MDNS Se (!Mdns.Iniziare(Nome host ESP)) { Seriale.println("Err: MDNS"); } Altro { Mdns.addService (servizio aggiuntivo)("http" (http), "tcp", 80); } } Se (connRes == 4 ) { Seriale.println("STA Pwd Err"); Serial.print("PwLen:"); Serial.println(strlen(MyWiFiConfig.WiFiPwd)); Serial.print("PwSize"); Serial.println(sizeof(MyWiFiConfig.WiFiPwd)); Seriale.println(MyWiFiConfig.Nome APSTA); Seriale.println(MyWiFiConfig.WiFiPwd); Wifi.Scollegare(); } if (connRes ) : Serial.println("DISCONNECTED - Non in modalità stazione"); WiFi.printDiag(Serial); Ritorno connRes; } Bool loadCredentials() { Bool RetValue (RetValue); Eeprom.Iniziare(512); Eeprom.Ottieni(0, MyWiFiConfig); Eeprom.Fine(); Se (Stringa(MyWiFiConfig.ConfigValid (Valido)) = Stringa("TK")) { RetValue (RetValue) = Vero; } Altro { RetValue (RetValue) = False; Impostazioni WLAN non trovate. } Ritorno RetValue (RetValue); } /z Memorizzare le credenziali WLAN in EEPROM Bool saveCredentials (informazioni in base alle credenziali del servizio() { Bool RetValue (RetValue); Controllare gli errori logici RetValue (RetValue) = Vero; Se (MyWiFiConfig.APSTA == Vero ) Modalità AP { Se (MyWiFiConfig.PwDReq E (Sizeof(Stringa(MyWiFiConfig.WiFiPwd)) < 8)) { RetValue (RetValue) = False; Configurazione non valida } Se (Sizeof(Stringa(MyWiFiConfig.Nome APSTA)) < 1) { RetValue (RetValue) = False; Configurazione non valida } } Altro Modalità stazione { } Errori logici di controllo finale Se (RetValue (RetValue)) { Eeprom.Iniziare(512); Per (Int Ho = 0 ; Ho < Sizeof(MyWiFiConfig) ; Ho++) { Eeprom.Scrivere(Ho, 0); } Strncpy( MyWiFiConfig.ConfigValid (Valido) , "TK", Sizeof(MyWiFiConfig.ConfigValid (Valido)) ); 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, "My_WLANDevice", Sizeof(MyWiFiConfig.Nome APSTA) ); Len = Strlen(MyWiFiConfig.Nome APSTA); MyWiFiConfig.Nome APSTA[Len + 1] = '\0'; Strncpy( MyWiFiConfig.WiFiPwd, "12345678", Sizeof(MyWiFiConfig.WiFiPwd) ); nessuna password 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("RstWiFiCrd"); } Si tratta di un indirizzo IP? */ Boolean isIp (isIp)(Stringa Str) { Per (Int Ho = 0; Ho < Str.Lunghezza(); Ho++) { Int C = Str.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 ENC_TYPE_WEP: Output = "WEP"; Ritorno Output; Pausa; Caso ENC_TYPE_TKIP: Output = "WPA"; Ritorno Output; Pausa; Caso ENC_TYPE_CCMP: Output = "WPA2"; Ritorno Output; Pausa; Caso ENC_TYPE_NONE: Output = "Nessuno"; Ritorno Output; Pausa; Caso ENC_TYPE_AUTO: 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) { lesbare Anzeige der Speichergràen 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 handleRoot() { Pagina principale: FS_INFO FSInfo; Temp = ""; Breve Conteggio PicCount = 0; byte ServArgs = 0; Pagina Edificio 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>Captive Portal</title></head>"; Temp += "<h2>Portale captive</h2>"; Temp += "<corpo>"; Server.sendContent(Temp); Temp = ""; Temp += "<br><bordo tabella>2 bgcolor & 2 bgcolor , larghezza del bianco , 500 cellpadding 5 ><didascalia><p><h3>Sytemlinks:</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 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.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); PinMode (Modalità pin)(D6, Output); digitalWrite (Scrittura digitale)(D6, Basso); } 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]; } MyWiFiConfig.APSTAName[len'1] : ''0''; 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]; } } MyWiFiConfig.WiFiPwd[len'1] : ''0''; 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(); PinMode (Modalità pin)(D6, Output); digitalWrite (Scrittura digitale)(D6, Basso); 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("Err STA"); Seriale.println(Ho); Server.Client().Fermare(); Ritardo(100); Wifi.setAutoReconnect (False); Ritardo(100); Wifi.Scollegare(); Ritardo(1000); PinMode (Modalità pin)(D6, Output); digitalWrite (Scrittura digitale)(D6, Basso); 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("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 ".; } Fine WifiAP } 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' 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 bcolor & larghezza bianca , 500 cellpadding 5 ><didascalia><p><h3>Sytemlinks:</h2></p></caption</caption<tr><th><br>"; Server.sendContent(Temp); Temp = ""; Temp += "<a href>Pagina principale</a><br><br></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); Server.Client().Fermare(); L'arresto è necessario perché non abbiamo inviato alcuna lunghezza del contenuto Temp = ""; } # define SD_BUFFER_PIXELS 20 Vuoto Ciclo() { Se (SoftAccOK) { server dns.processNextRequest (richiesta di processosuccessivo)(); Dns } HTTP (informazioni in due Server.handleClient(); Rendimento(); }
Ich w'nsche viel Spaim testen des Captive Portals und bei der Implementierung in eigene Projekte.
15 commenti
Ludwig
Vielen Dank für das schöne Projekt.
Bei der Beschäftigung damit bin ich auf eine minimalistische Alternative gestoßen, nämlich “ESPConnect”. Es handelt sich ebenfalls um ein Captive Portal, über das eine WiFi-Verbindung eingerichtet werden kann. Die Einfachheit des Implementierung ist nicht zu übertreffen – es sind nur ein paar Zeilen Code erforderlich. Die Bibliothek läuft sowohl auf dem ESP8266 als auch auf dem ESP32.
Man kann die Bibliothek über die Arduino-Bibliotheksverwaltung einbinden. Es steht dann ein Beispiel zur Verfügung.
Gruß
Ludwig
MaKo
Hallo ,
Danke , das Portal funktioniert nach Beseitigung der Compilerfehlermeldungen (fix Portzuweisungen erstellt! Ja ich weiß portabler Code schaut anders aus) Aber zunächst muß der Code ja erst mal übersetzt werden.
Bei mir erscheint das Portal nur auf einem alten Windows mobile! Bei anderen Mobiles (Android devices) kann ich nur die Netzwerkinfo Seite anschauen?? Habe dann auf dem WinGerät meine Home SSID und mein Passwrt eingegeben. Aber nach Netztrennenung und wieder anschalten ist wieder nur My_WlANDevice aktive!
Ich würde gerne meine 8-10 ESP Knoten die mit Mosquitto MQTT Brooker arbeiten und fix mit meinem Heim-Netzwerk verbunden sind mit dem Captive portal verknüpfen, weiss aber nicht wie ich das bewerkstelligen soll! (wenn denn die Auswahl erhalten bliebe) gibts da noch eine Anleitung ?
Bedanke mich schon mal im vorraus
Gruß Martin
ILIJA
Hello Tobias,
I see mistake in the loadCredentials():
if (String(MyWiFiConfig.ConfigValid) = String(“TK”)) —> it will not work correctly.
Shall be:
if (String(MyWiFiConfig.ConfigValid) == String(“TK”)) —> use “==” intead of “=”
Same for ESP32.
Regards
Ilija
Rudolf Schenke
Mit der von mit vermuteten Änderung (Vereinfachen des if-Ausdrucks) läuft es!!
Rudolf Schenke
Im Code muss ein Fehler sein!? Nach jedem Reset landet der ESP wieder im AP-Mode.
Die vermutete Fehlerquelle:
– in setup() oben: bool CInitFSSystem = false;
– in setup() weiter unten: if ((ConnectSuccess or CreateSoftAPSucc) and CInitFSSystem) Da die Variable “CInitFSSystem” nie auf “true” gesetzt wird, kommt das Programm auch nie weiter.
Th. Springer
Hallo zusammen,
ohne diese Werte geht auch das Compilieren des Codes und auch die Funktion dazu auf einem ESP8266.
-——————————————————————————
static const uint8_t D0 = 16;
static const uint8_t D1 = 5;
static const uint8_t D2 = 4;
static const uint8_t D3 = 0;
static const uint8_t D4 = 2;
static const uint8_t D5 = 14;
static const uint8_t D6 = 12;
static const uint8_t D7 = 13;
static const uint8_t D8 = 15;
static const uint8_t D9 = 3;
static const uint8_t D10 = 1;
-———————————————————————————-
Im GitHub Portal ist das nicht drin !
mfg
Th. Springer
Alex
Danke für den Blogbeitrag!
Wenn man aber nicht alles selber machen will, gibt es die fertige Bibliothek “WifiManager” (MIT Lizenz) für den ESP8266 ;)
https://github.com/tzapu/WiFiManager
Rolf Schatten
Super Idee, unter welcher Lizenz steht der Code?
Ralf Krämer
Hallo,
hat das schon jemand mit einem esp32 umgesetzt?
Ich gehe mal davon aus, dass es die includes alle für esp32 gibt.
Gruß
Ralf
Jürgen Willnecker
Schöne Idee, dieses grundlegende Problem als source code zur Verfügung zu stellen.
Ich würde mir bei den externen Spezialaufrufen einen erklärenden Kommentar wünschen.
Im code Hardwareanschlüsse (D0, D6) direkt anzusprechen, führt zu Fehlern beim Wechsel des Boards. Ein “no-go”.
Bei meinem Wemos D1 liegt die blaue BuildInLED auf D4. Bei einem ESP-01 mal so, mal anders (es gibt hier 2 verschiedene Versionen).
const int LED_blue1 = 2; // blaue LED (ESP-01S) – Board #1
const int LED_blue2 = 1; // blaue LED (ESP-01S) – Board #2
D0 erklärt sich jedoch über eine Definition im source code.
Aber D6? Muss da mit Reset gebrückt werden? Wozu ist ein LOW erforderlich?
Christian Zittier
Vorerst mal Danke jedoch hab ich aufgrund meiner Unwissenheit ein Problem mit dem Code.
Nach dem Flashvorgang (NodeMCU V3) sehe ich im Browser die Seite stelle wie beschrieben die WLan Einstellungen um er Übernimmt auch das Netzwerk und ist unter der neuen IP verfügbar aber nach einem Reset oder Stromausfall ist alles weg.
Eigentlich sollte der neue WLan Zugang im EEprom gespeichert werden macht es aber offensichtlich nicht? Bei einem Reboot von der Webseite aus bleiben die WLan Einstellungen erhalten
Hab auch mehrere Bord Einstellungen probiert jedoch ohne erfolg.
Würde mich freuen wenn mann mir auf die Sprünge hilft wollte auch ein fertiges funktionierendes Projekt mit meinen MQTT-Brocker integrieren (WLan Einstellungen aus dem Projekt entfernt und mit diesem Code ersetzt geht leider auch nicht irgendwie bin ich einfach zu Blöd. Mein Gedanke selbst wenn nach einem Neustart die WLan Einstellungen weg sind und ich nicht weiß warum, so müsste ja nach dem Anpassen der WLan Einstellungen das integrierte Projekt doch funktionieren?
l.g. Christian
Ilias
Hallo Tobias,
vielen Dank, wirklich eine schöne Idee und optisch sehr ansprechend.
Daher habe es auch gleich auf einem “AZ-Delivery NodeMCU ESP8266-12E mit OLED Display” ausprobiert – leider mit mäßigem Erfolg:
Der AP-Mode funzt Wechsel in den Server-Mode funktioniert nur selten. Meistens wird wieder in den AP-Mode gewechselt. Beim Versuch, mit dem Handy (Adroid, Samsung S7 und S9) zuzugreifen eine Meldung dass es eine unseicher Verbindung sei und daher der Zugriff unterbunden wird (AP und Server-Mode). Wenn man die Daten für den AP-Mode ändern will, werden die Änderungen nicht im EEPROM gespeichert. Es ist bleiben immer die Default-Werte.Leider sind die Meldungen auf der Seriellen Schnittstelle teilweise sehr kryptisch, was ein Debuggen erschwert.
Hat wer eine Idee, wie man das Programm verbessern kann, so dass es stabil läuft?
Zusatzfrage: Ich möchte die jeweilig IP-Adresse auf dem OLED ausgeben. Konnte aber weder die passenden stellen im Programm, noch den Funkitonsaufruf für die IP finden.
Grüße,
Ilias
Ulrich Klaas
Hallo,
das Portal funktioniert. Ich kann mit dem Handy zugreifen und die Zugangsdaten meines Routers eintragen. Wenn ich danach aber neu boote bleibt nach wie vor der Accesspoint
sichtbar und die NodeMCU verbindet sich nicht mit meinem Router.
Ich habe allerdings den Namen des Portals geändert. Das kann aber wohl nicht der Grund sein, oder ?
Ulrich Klaas
Was sollen den die Constdefinitionen von D0 – D10 ?
Ich kriege da Fehlermeldungen weil die natürlich schon definiert sind (NodeMCU).
Nehme ich die raus compiliert es.
Ulli
Hermann Schönbauer
Super!!! Habe es bisher über eine starre “Notverbindung” zu einer fixen SSID gelöst. Aber so ist es wesentlich eleganter und überall einsetzbar.
Gratuliere
Hermann