Hallo juntos,
la resonancia positiva y el interés general a Vorgängerblog sobre uno Portal Captive, me ha dispuesto con eso, otro Sonderblog por el tema escriben y en algunos puntos y deseos llegar vo en el particular. Bajo muchas otras preguntas detalladas, a las cuales llego más tarde todavía, el deseo, Captive el portal no sólo en ESP8266, sino también en él existía ESP32 dejar correr. Esto no es posible nativ. Con eso algunos cambios, especialmente de Librarys utilizados, tienen que ser efectuados. Por eso, el código de hoy es capaz de carrera SÓLO en ESP32. En el detalle algunas optimizaciones se han juntado junto a Libraryanpassungen también especialmente para ESP32.
Además junto a algunos lectores de Blogs ha ocurrido bien que ESP no sabía más se bien con Point Access reconnecten, después que él se hacía rebootet. Podía postponer esta proa en después de una busca más larga. Éste da bien en el juego de equipo con Points Access determinados en los cuales ESP es rebatido a pesar de datos de acceso WLAN justos en el aviso en Point Access svond a éste y cambia lógico de la estación la moda en Access propio Point la moda. En este proceso los datos de acceso WLAN precedentes de EEPROM son apagados (esto está querido así).
La solución para esta proa de lados de sistema es intentar el aviso varias veces a pesar de datos de acceso "equivocados" al parecer, antes de que de nuevo en Access Point la moda sea cambiado.
La dirección IP actual le es distribuida el as la otra mejora pequeña igualmente en qué moda se encuentra de ESP32, en la interface en serie. Quién quisiera distribuir esta dirección IP con mucho gusto en su display, solamente el gasto tiene que modificar en el lugar correspondiente en el código.
Para ser compatible lo más posible con muchos ESP Boards, renuncio en adelante al gasto de Statis en puertos externos de ESP‘s.
También en esta parte el código monta de ESP32 con nuestro Captive siguiente el portal Captive portal en. WLAN tiene el nombre „ESP_Config“ y la contraseña „12345678“. Podemos combinarnos con éste con nuestro móvil, y somos acompañados luego por el móvil automáticamente en Captive el portal la página web. Éste corresponde en el diseño y manera funcional en esencial a la parte precedente.
Ahora en esta página web podemos hacer clic al enlace de sistema „WiFi de ajustes“, y llegamos ahora en un lado de configuración WLAN voluminoso con el cual podemos elegir ahora no sólo una red, con la cual debe combinarse de ESP32:
La red de estaciones de radio elegida aquí y la contraseña dada son guardadas en EEPROM. En la barca cercana trata de combinarse de ESP32 con esta red. Si esto naufraga, después de intentos repetidos porque la red no es accesible más, p. ej., o la contraseña era cambiada, conecta de ESP atrás en Access Point el modo y espera una nueva configuración.
El código adaptado para Captive el portal para ESP32 es:
[
#include <WiFi.h> #include <WiFiClient.h> #include <Servidor de Web.h> #include <ESPmDNS.h> #include <DNSServer.h> #include <EEPROM.h> #define GPIO_OUT_W1TS_REG (DR_REG_GPIO_BASE + 0x0008) #define GPIO_OUT_W1TC_REG (DR_REG_GPIO_BASE + 0x000c) static const byte WiFiPwdLen = 25; static const byte APSTANameLen = 20; struct WiFiEEPromData { bool APSTA = true; //Point Access or la estación la moda - true la moda AP bool PwDReq = false; //PasswordRequired bool CapPortal = true ; //CaptivePortal on en la moda AP char APSTAName[APSTANameLen]; //ESTACIÓN/AP Point el nombre TO cONNECT, if definded char WiFiPwd[WiFiPwdLen]; //WiFiPAssword, if definded char ConfigValid[3]; //If Config is Vaild, el día "TK" is required" }; /* hostname for mDNS. Should work at alquila con opción a compra on Windows. Try http://esp8266.local */ const char *ESPHostname = "ESP32"; //ADN el servidor const byte DNS_PORT = 53; DNSServer dnsServer; //Conmmon Paramenters bool SoftAccOK = false; //Web el servidor Servidor de Web servidor(80); /* Soft AP network del parámetro */ IPAddress apIP(172, 20, 0, 1); IPAddress netMsk(255, 255, 255, 0); unsigned long currentMillis = 0; unsigned long startMillis; / ** Current WLAN el estatus */ short estatus = WL_IDLE_STATUS; WiFiEEPromData MyWiFiConfig; Bramante temp =""; void Setup() { REG_WRITE(GPIO_OUT_W1TS_REG, BIT(GPIO_NUM_16)); //Gurú la meditación Error Remediation el juego delay(1); REG_WRITE(GPIO_OUT_W1TC_REG, BIT(GPIO_NUM_16)); //Gurú la meditación Error Remediation clear bool ConnectSuccess = false; bool CreateSoftAPSucc = false; bool CInitFSSystem = false; bool CInitHTTPServer = false; byte len; Serial.begin(9600); while (!Serial) { ; //wait for serial port to connect. Needed for native USB } Serial.println(F("Serial la interfaz initalized at 9600 Baud.")); WiFi.setAutoReconnect (false); WiFi.persistent(false); WiFi.disconnect(); WiFi.setHostname(ESPHostname); //Juego the DHCP hostname assigned to la estación ESP. if (loadCredentials()) //Load WLAN credentials for WiFi Settings { Serial.println(F("Valid Credentials found.")); if (MyWiFiConfig.APSTA == true) //Moda AP { Serial.println(F("Access Point la moda selected.")); Serial.println(MyWiFiConfig.APSTA); len = strlen(MyWiFiConfig.APSTAName); MyWiFiConfig.APSTAName[len+1] = '\0'; len = strlen(MyWiFiConfig.WiFiPwd); MyWiFiConfig.WiFiPwd[len+1] = '\0'; CreateSoftAPSucc = CreateWifiSoftAP(); } else { Serial.println(F("La estación la moda selected.")); len = strlen(MyWiFiConfig.APSTAName); MyWiFiConfig.APSTAName[len+1] = '\0'; len = strlen(MyWiFiConfig.WiFiPwd); MyWiFiConfig.WiFiPwd[len+1] = '\0'; len = ConnectWifiAP(); if ( len == 3 ) { ConnectSuccess = true; } else { ConnectSuccess = false; } } } else { //Juego default Config - Create AP Serial.println(F("NO Valid Credentials found.")); SetDefaultWiFiConfig (); CreateSoftAPSucc = CreateWifiSoftAP(); saveCredentials(); delay(500); } 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 la producción total for WLAN on Serial la interfaz. Serial.println(F("Error: Cannot connect to WLAN. Set DEFAULT Configuration.")); SetDefaultWiFiConfig(); CreateSoftAPSucc = CreateWifiSoftAP(); InitalizeHTTPServer(); SetDefaultWiFiConfig(); saveCredentials(); } } void InitalizeHTTPServer() { bool initok = false; /* El Setup teje pages: root, wifi config pages, SEGÚN el portal captive detectors y necesidad found. */ servidor.on("/", handleRoot); servidor.on("/wifi", handleWifi); if (MyWiFiConfig.CapPortal) { servidor.on("/generate_204", handleRoot); } //Android el portal captive. Maybe la necesidad needed. Might handled by notFound negociante. if (MyWiFiConfig.CapPortal) { servidor.on("/favicon.ico", handleRoot); } //Another Android el portal captive. Maybe la necesidad needed. Might handled by notFound negociante. Checked on Sony el móvil if (MyWiFiConfig.CapPortal) { servidor.on("/fwlink", handleRoot); } //Microsoft el portal captive. Maybe la necesidad needed. Might handled by notFound negociante. //server.on ("/generate_204", handleRoot);//Android el portal captive. Maybe la necesidad needed. Might handled by notFound negociante. //server.on ("/favicon.ico", handleRoot);//Another Android el portal captive. Maybe la necesidad needed. Might handled by notFound negociante. Checked on Sony el móvil //server.on ("/fwlink", handleRoot);//Microsoft el portal captive. Maybe la necesidad needed. Might handled by notFound negociante. servidor.onNotFound ( handleNotFound ); //El almacenamiento Header elementos piden //server.collectHeaders (Headers, sizeof (Headers) / sizeof (Headers [0])); servidor.begin(); //Web el servidor la salida } boolean CreateWifiSoftAP() { WiFi.disconnect(); Serial.print(F("Initalize SoftAP")); if (MyWiFiConfig.PwDReq) { SoftAccOK = WiFi.softAP(MyWiFiConfig.APSTAName, MyWiFiConfig.WiFiPwd); //¡Longitud de contraseña por lo menos 8 signos! } else { SoftAccOK = WiFi.softAP(MyWiFiConfig.APSTAName); //Access Point WITHOUT Word de puerto //Overload el Function:; WiFi.softAP (ssid, Word de puerto, canal, hidden) } delay(2000); //Without delay I've lagos the IP address limpiamente WiFi.softAPConfig(apIP, apIP, netMsk); if (SoftAccOK) { /* Setup ADN the el servidor redirecting todo the domains to the apIP */ dnsServer.setErrorReplyCode(DNSReplyCode::NoError); dnsServer.salida(DNS_PORT, "*", apIP); Serial.println(F("successful.")); //Serial.setDebugOutput (true);//Debug la producción total for WLAN on Serial la interfaz. } else { Serial.println(F("Soft AP Error.")); Serial.println(MyWiFiConfig.APSTAName); Serial.println(MyWiFiConfig.WiFiPwd); } return SoftAccOK; } byte ConnectWifiAP() { Serial.println(F("Initalizing Wifi el cliente.")); byte connRes = 0; byte i = 0; WiFi.disconnect(); WiFi.softAPdisconnect(true); //El Function quiere el juego currently configured SSID y Word de puerto of the soft-AP to cero values. The el parámetro is opcionalmente. If el juego to true it quiere switch the soft-AP la moda off. delay(500); WiFi.begin(MyWiFiConfig.APSTAName, MyWiFiConfig.WiFiPwd); connRes = WiFi.waitForConnectResult(); while (( connRes == 0 ) y (i != 10)) //if connRes == 0 "IDLE_STATUS - change Statius" { connRes = WiFi.waitForConnectResult(); delay(2000); i++; Serial.print(F(".")); //afirmación (s) } while (( connRes == 1 ) y (i != 10)) //if connRes == 1 NO_SSID_AVAILin - SSID cannot reached { connRes = WiFi.waitForConnectResult(); delay(2000); i++; Serial.print(F(".")); //afirmación (s) } if (connRes == 3 ) { WiFi.setAutoReconnect(true); //El juego de módulos whether va attempt to reconnect to en Access point en case it is disconnected. //Setup MDNS responder if (!MDNS.begin(ESPHostname)) { Serial.println(F("Error: MDNS")); } else { MDNS.addService("http", "tcp", 80); } } while (( connRes == 4 ) y (i != 10)) //if connRes == 4 baños Word de puerto. Sometimes del bocado this with corrct PWD { WiFi.begin(MyWiFiConfig.APSTAName, MyWiFiConfig.WiFiPwd); connRes = WiFi.waitForConnectResult(); delay(3000); i++; Serial.print(F(".")); } if (connRes == 4 ) { Serial.println(F("Err STA Pwd")); Serial.println(MyWiFiConfig.APSTAName); Serial.println(MyWiFiConfig.WiFiPwd); WiFi.disconnect(); } //if (connRes == 6) {Serial.println ("DISCONNECTED - la necesidad en la estación la moda");} //WiFi.printDiag (Serial); Serial.println(""); return connRes; } #define SD_BUFFER_PIXELS 20 / ** Load WLAN credentials from EEPROM */ bool loadCredentials() { bool RetValue; EEPROM.begin(512); EEPROM.get(0, MyWiFiConfig); EEPROM.end(); if (Bramante(MyWiFiConfig.ConfigValid) = Bramante("TK")) { RetValue = true; } else { RetValue = false; //Necesidad WLAN Settings found. } return RetValue; } / ** el visillo WLAN credentials to EEPROM */ bool saveCredentials() { bool RetValue; //Cheque logical Errors RetValue = true; if (MyWiFiConfig.APSTA == true ) //Moda AP { if (MyWiFiConfig.PwDReq y (sizeof(Bramante(MyWiFiConfig.WiFiPwd)) < 8)) { RetValue = false; //Inválido Config } if (sizeof(Bramante(MyWiFiConfig.APSTAName)) < 1) { RetValue = false; //Inválido Config } } if (RetValue) { EEPROM.begin(512); for (int i = 0 ; i < sizeof(MyWiFiConfig) ; i++) { EEPROM.write(i, 0); } strncpy( MyWiFiConfig.ConfigValid , "TK", sizeof(MyWiFiConfig.ConfigValid) ); EEPROM.put(0, MyWiFiConfig); EEPROM.commit(); EEPROM.end(); } return RetValue; } void SetDefaultWiFiConfig() { byte len; MyWiFiConfig.APSTA = true; MyWiFiConfig.PwDReq = true; //default PW required MyWiFiConfig.CapPortal = true; strncpy( MyWiFiConfig.APSTAName, "ESP_Config", sizeof(MyWiFiConfig.APSTAName) ); len = strlen(MyWiFiConfig.APSTAName); MyWiFiConfig.APSTAName[len+1] = '\0'; strncpy( MyWiFiConfig.WiFiPwd, "12345678", sizeof(MyWiFiConfig.WiFiPwd) ); len = strlen(MyWiFiConfig.WiFiPwd); MyWiFiConfig.WiFiPwd[len+1] = '\0'; strncpy( MyWiFiConfig.ConfigValid, "TK", sizeof(MyWiFiConfig.ConfigValid) ); len = strlen(MyWiFiConfig.ConfigValid); MyWiFiConfig.ConfigValid[len+1] = '\0'; Serial.println(F("Reset WiFi Credentials.")); } void handleRoot() { //Meno paje: temp = ""; byte PicCount = 0; byte ServArgs = 0; //HTML Header servidor.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); servidor.sendHeader("Pragma", "no-cache"); servidor.sendHeader("Expires", "-1"); servidor.setContentLength(CONTENT_LENGTH_UNKNOWN); //HTML Content servidor.envía ( 200, "texto / html", temp ); //Speichersparen - mientras tanto a Cleint envían temp = ""; temp += "<!DOCTYPE HTML><html mucho tiempo ='de'><head><meta charset el nombre ='UTF-8'><meta = viewport content ='width=device-width, inicial scale=1.0, '>"; servidor.sendContent(temp); temp = ""; temp += "<style type ='text / css'><! - DIV.container {min-height: 10em em; display: table-cell; vertical-align: middle}.button {height:35px; width:90px; font-size:16px}"; servidor.sendContent(temp); temp = ""; temp += "body {background-color: powderblue;} </style>"; temp += "<head><title>Hauptseite</title></head>"; temp += "<h2>Hauptseite</h2>"; temp += "<body>"; servidor.sendContent(temp); temp = ""; //Processing el usuario Request temp = ""; temp += "<table border=2 bgcolor = white width = 500 cellpadding =5 ><caption><p><h3>Systemlinks:</h2></p></caption>"; temp += "<tr><th><br>"; temp += "<a href="/es/ wifi">WIFI Einstellungen</a><br><br>"; temp += "</th></tr></table><br><br>"; temp += "<footer><p>Programmed y designed by: Tobias Kuch</p><p>Contact information: <a href ='mailto:tobias.kuch@googlemail.com'>tobias.kuch@googlemail.com</a>.</p></footer>"; temp += "</body></html>"; servidor.sendContent(temp); temp = ""; servidor.cliente().parada(); //Parada is needed because we sent no content length } void handleNotFound() { if (captivePortal()) { //If el portal caprive redirect instead of displaying the error paje. return; } temp = ""; //HTML Header servidor.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); servidor.sendHeader("Pragma", "no-cache"); servidor.sendHeader("Expires", "-1"); servidor.setContentLength(CONTENT_LENGTH_UNKNOWN); //HTML Content temp += "<!DOCTYPE HTML><html mucho tiempo ='de'><head><meta charset el nombre ='UTF-8'><meta = viewport content ='width=device-width, inicial scale=1.0, '>"; temp += "<style type ='text / css'><! - DIV.container {min-height: 10em em; display: table-cell; vertical-align: middle}.button {height:35px; width:90px; font-size:16px}"; temp += "body {background-color: powderblue;} </style>"; temp += "<head><title>File la necesidad found</title></head>"; temp += "<h2> 404 files la necesidad Found</h2><br>"; temp += "<h4>Debug Information:</h4><br>"; temp += "<body>"; temp += "URI:"; temp += servidor.uri(); temp += "\nMethod:"; temp+= ( servidor.method() == HTTP_GET ) ? "GET" : "CORREO"; temp += "<br>Arguments:"; temp += servidor.args(); temp += "\n"; for ( uint8_t i = 0; i < servidor.args(); i++ ) { temp += " " + servidor.nombre malo ( i ) + ": " + servidor.mucho ( i ) + "\n"; } temp += "<br>Server Hostheader:"+ servidor.hostHeader(); for ( uint8_t i = 0; i < servidor.headers(); i++ ) { temp += " " + servidor.headerName ( i ) + ": " + servidor.header ( i ) + "\n<br>"; } temp += "</table></form><br><br><table border=2 bgcolor = white width = 500 cellpadding =5 ><caption><p><h2>You may want to browse to:</h2></p></caption>"; temp += "<tr><th>"; temp += "<a href="/es">Main Page</a><br>"; temp += "<a href="/es/ wifi">WIFI Settings</a><br>"; temp += "</th></tr></table><br><br>"; temp += "<footer><p>Programmed by: Tobias Kuch</p><p>Contact information: <a href ='mailto:tobias.kuch@googlemail.com'>tobias.kuch@googlemail.com</a>.</p></footer>"; temp += "</body></html>"; servidor.envía ( 404, "", temp ); servidor.cliente().parada(); //Parada is needed because we sent no content length temp = ""; } / ** Redirect to el portal captive if we got a request for another domain. Return true en that case paje tan the handler do la necesidad try to actúa the request again. */ boolean captivePortal() { if (!isIp(servidor.hostHeader()) && servidor.hostHeader() != (Bramante(ESPHostname)+".local")) { //Serial.println ("Request redirected to captive portal"); servidor.sendHeader("Location", Bramante("http://") + toStringIp(servidor.cliente().localIP()), true); servidor.envía ( 302, "texto / plain", ""); //Empty content inhibits Content-length header así we have to close the socket ourselves. servidor.cliente().parada(); //Parada is needed because we sent no content length return true; } return false; } / ** Wifi config el paje negociante */ void handleWifi() { //Paje:/wifi byte i; byte len ; temp = ""; //Cheque for el site del parámetro if (servidor.hasArg("Reboot") ) //Reboot el sistema { temp = "Rebooting el sistema en 5 Seconds."; servidor.envía ( 200, "texto / html", temp ); delay(5000); servidor.cliente().parada(); WiFi.disconnect(); delay(1000); } if (servidor.hasArg("WiFiMode") y (servidor.mucho("WiFiMode") == "1") ) //Estación STA la moda Connect to another la estación WIFI { startMillis = millis(); //Reset Time Up el contador to avoid Idle Mode whiole operating //Connect to existing la ESTACIÓN if ( sizeof(servidor.mucho("WiFi_Network")) > 0 ) { Serial.println("La moda STA"); MyWiFiConfig.APSTA = false; //Point Access or la estación la moda - la estación false la moda temp = ""; for ( i = 0; i < APSTANameLen;i++) { MyWiFiConfig.APSTAName[i] = 0; } temp = servidor.mucho("WiFi_Network"); len = temp.length(); for ( i = 0; i < len;i++) { MyWiFiConfig.APSTAName[i] = temp[i]; } temp = ""; for ( i = 0; i < WiFiPwdLen;i++) { MyWiFiConfig.WiFiPwd[i] = 0; } temp = servidor.mucho("STAWLanPW"); len = temp.length(); for ( i = 0; i < len;i++) { if (temp[i] > 32) //Signos de impuesto fuera { MyWiFiConfig.WiFiPwd[i] = temp[i]; } } temp = "WiFi Connect to AP:-"; temp += MyWiFiConfig.APSTAName; temp += "-<br>WiFi PW:-"; temp += MyWiFiConfig.WiFiPwd; temp += "-<br>"; temp += "Connecting to la moda STA en 2 Seconds..<br>"; servidor.envía ( 200, "texto / html", temp ); servidor.sendContent(temp); delay(2000); servidor.cliente().parada(); servidor.parada(); temp = ""; WiFi.disconnect(); WiFi.softAPdisconnect(true); delay(500); //ConnectWifiAP bool SaveOk = saveCredentials(); i = ConnectWifiAP(); delay(700); if (i != 3) //4: WL_CONNECT_FAILED - Word de puerto is incorrect 1: WL_NO_SSID_AVAILin - Configured SSID cannot reached { Serial.print(F("Cannot Connect to specified Network. Reason:")); Serial.println(i); servidor.cliente().parada(); delay(100); WiFi.setAutoReconnect (false); delay(100); WiFi.disconnect(); delay(1000); SetDefaultWiFiConfig(); CreateWifiSoftAP(); return; } else { //Safe Config bool SaveOk = saveCredentials(); InitalizeHTTPServer(); return; } } } if (servidor.hasArg("WiFiMode") y (servidor.mucho("WiFiMode") == "2") ) //Change la moda AP { startMillis = millis(); //Reset Time Up el contador to avoid Idle Mode whiole operating //Configure Access Point temp = servidor.mucho("APPointName"); len = temp.length(); temp =servidor.mucho("APPW"); if (servidor.hasArg("PasswordReq")) { i = temp.length(); } else { i = 8; } if ( ( len > 1 ) y (servidor.mucho("APPW") == servidor.mucho("APPWRepeat")) y ( i > 7) ) { temp = ""; Serial.println(F("APMode")); MyWiFiConfig.APSTA = true; //Point Access or el Sation la moda - true la moda AP if (servidor.hasArg("CaptivePortal")) { MyWiFiConfig.CapPortal = true ; //CaptivePortal on en la moda AP } else { MyWiFiConfig.CapPortal = false ; } if (servidor.hasArg("PasswordReq")) { MyWiFiConfig.PwDReq = true ; //Word de puerto Required en la moda AP } else { MyWiFiConfig.PwDReq = false ; } for ( i = 0; i < APSTANameLen;i++) { MyWiFiConfig.APSTAName[i] = 0; } temp = servidor.mucho("APPointName"); len = temp.length(); for ( i = 0; i < len;i++) { MyWiFiConfig.APSTAName[i] = temp[i]; } MyWiFiConfig.APSTAName[len+1] = '\0'; temp = ""; for ( i = 0; i < WiFiPwdLen;i++) { MyWiFiConfig.WiFiPwd[i] = 0; } temp = servidor.mucho("APPW"); len = temp.length(); for ( i = 0; i < len;i++) { MyWiFiConfig.WiFiPwd[i] = temp[i]; } MyWiFiConfig.WiFiPwd[len+1] = '\0'; temp = ""; if (saveCredentials()) //Save AP ConfigCongfig { temp = "Datos de AP Modes afortunado guardado. Reboot necesariamente."; } else { temp = "Datos de AP Modes erróneamente."; } } else if (servidor.mucho("APPW") != servidor.mucho("APPWRepeat")) { temp = ""; temp = "WLAN la contraseña no inmediatamente. De modo caído."; } else { temp = ""; temp = "WLAN la contraseña o el nombre AP demasiado brevemente. De modo caído."; } } //HTML Header servidor.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); servidor.sendHeader("Pragma", "no-cache"); servidor.sendHeader("Expires", "-1"); servidor.setContentLength(CONTENT_LENGTH_UNKNOWN); //HTML Content temp += "<!DOCTYPE HTML><html mucho tiempo ='de'><head><meta charset el nombre ='UTF-8'><meta = viewport content ='width=device-width, inicial scale=1.0, '>"; servidor.envía ( 200, "texto / html", temp ); temp = ""; temp += "<style type ='text / css'><! - DIV.container {min-height: 10em em; display: table-cell; vertical-align: middle}.button {height:35px; width:90px; font-size:16px}"; temp += "body {background-color: powderblue;} </style><head><title>Smartes Tuerschild - WiFi Settings</title></head>"; servidor.sendContent(temp); temp = ""; temp += "<h2>WiFi Einstellungen</h2><body><left>"; temp += "<table border=2 bgcolor = white width = 500 ><td><h4>Current WiFi Settings: </h4>"; if (servidor.cliente().localIP() == apIP) { temp += "La moda: Soft Access Point (AP) <br>"; temp += "SSID:" + Bramante (MyWiFiConfig.APSTAName) + "<br><br>"; } else { temp += "La moda: La estación (STA) <br>"; temp += "SSID:"+ Bramante (MyWiFiConfig.APSTAName) + "<br>"; temp += "BSSID:" + WiFi.BSSIDstr()+ "<br><br>"; } temp += "</td></table><br>"; servidor.sendContent(temp); temp = ""; temp += "<form action = '/ wifi' method ='post'>"; temp += "<table border=2 bgcolor = white width = 500><tr><th><br>"; if (MyWiFiConfig.APSTA == 1) { temp += "<input type ='radio" value = '1' nombre del ='WiFiMode' > WiFi la estación Mode<br>"; } else { temp += "<input type ='radio" value = '1' nombre del ='WiFiMode' checked > WiFi la estación Mode<br>"; } temp += "Available WiFi Networks:<table border=2 bgcolor = white ></tr></th><td>Number </td><td>SSID </td><td>Encryption </td><td>WiFi Strength </td>"; servidor.sendContent(temp); temp = ""; WiFi.scanDelete(); int n = WiFi.scanNetworks(false, false); //WiFi.scanNetworks (async, show_hidden) if (n > 0) { for (int i = 0; i < n; i++) { temp += "</tr></th>"; Bramante Nrb = Bramante(i); temp += "<td>" + Nrb + "</td>"; temp += "<td>" + WiFi.SSID(i) +"</td>"; Nrb = GetEncryptionType(WiFi.encryptionType(i)); temp += "<td>"+ Nrb + "</td>"; temp += "<td>" + Bramante(WiFi.RSSI(i)) + "</td>"; } } else { temp += "</tr></th>"; temp += "<td>1 </td>"; temp += "<td>No WLAN found</td>"; temp += "<td>---</td>"; temp += "<td>---</td>"; } temp += "</table><table border=2 bgcolor = white ></tr></th><td>Connect to WiFi SSID: </td><td><select name ='WiFi_Network" >"; if (n > 0) { for (int i = 0; i < n; i++) { temp += "<option value = '" + WiFi.SSID(i) +"'>" + WiFi.SSID(i) +"</option>"; } } else { temp += "<option value ='No_WiFi_Network'>No WiFiNetwork found !/option>"; } servidor.sendContent(temp); temp = ""; temp += "</select></td></tr></th></tr></th><td>WiFi Word de puerto: </td><td>"; temp += Nombre "<input type ='text" del ='STAWLanPW' maxlength = en '40' size = '40'>"; temp += "</td></tr></th><br></th></tr></table></table><table border=2 bgcolor = white width = 500 ><tr><th><br>"; servidor.sendContent(temp); temp = ""; if (MyWiFiConfig.APSTA == true) { temp += Nombre "<input type ='radio" del ='WiFiMode' value = '2' checked> WiFi Access Point la moda <br>"; } else { temp += Nombre "<input type ='radio" del ='WiFiMode' value = '2' > WiFi Access Point la moda <br>"; } temp += "<table border=2 bgcolor = white ></tr></th> <td>WiFi Access Point Name: </td><td>"; servidor.sendContent(temp); temp = ""; if (MyWiFiConfig.APSTA == true) { temp += Nombre "<input type ='text" del ='APPointName' maxlength = '"+Bramante(APSTANameLen-1)+"' size = en '30' value = '" + Bramante(MyWiFiConfig.APSTAName) + "'></td>"; } else { temp += Nombre "<input type ='text" del ='APPointName' maxlength = '"+Bramante(APSTANameLen-1)+"' size = en '30' ></td>"; } servidor.sendContent(temp); temp = ""; if (MyWiFiConfig.APSTA == true) { temp += "</tr></th><td>WiFi Word de puerto: </td><td>"; temp += Nombre "<input type ='password" del ='APPW' maxlength = '"+Bramante(WiFiPwdLen-1)+"' size = en '30' value = '" + Bramante(MyWiFiConfig.WiFiPwd) + "'> </td>"; temp += "</tr></th><td>Repeat WiFi Password: </td>"; temp += Nombre "<td><input type ='password" del ='APPWRepeat' maxlength = '"+Bramante(WiFiPwdLen-1)+"' size = en '30' value = '" + Bramante(MyWiFiConfig.WiFiPwd) + "'> </td>"; } else { temp += "</tr></th><td>WiFi Word de puerto: </td><td>"; temp += Nombre "<input type ='password" del ='APPW' maxlength = '"+Bramante(WiFiPwdLen-1)+"' size = '30'> </td>"; temp += "</tr></th><td>Repeat WiFi Password: </td>"; temp += Nombre "<td><input type ='password" del ='APPWRepeat' maxlength = '"+Bramante(WiFiPwdLen-1)+"' size = '30'> </td>"; } temp += "</table>"; servidor.sendContent(temp); temp = ""; if (MyWiFiConfig.PwDReq) { temp += Nombre "<input type ='checkbox" del ='PasswordReq' checked> Word de puerto for Login required."; } else { temp += Nombre "<input type ='checkbox" del ='PasswordReq' > Word de puerto for Login required."; } servidor.sendContent(temp); temp = ""; if (MyWiFiConfig.CapPortal) { temp += Nombre "<input type ='checkbox" del ='CaptivePortal' checked> Activate Captive el portal"; } else { temp += Nombre "<input type ='checkbox" del ='CaptivePortal' > Activate Captive el portal"; } servidor.sendContent(temp); temp = ""; temp += "<br></tr></th></table><br> <button type el nombre de ='submit' del ='Settings' value = '1' da estilo ='height: 50px; width: 140px' autofocus>Set WiFi Settings</button>"; temp += El nombre "<button type ='submit" del ='Reboot' value = '1' da estilo ='height: 50px; width: 200px' >Reboot System</button>"; servidor.sendContent(temp); temp = ""; temp += El nombre "<button type ='reset" del ='action' value = '1' da estilo ='height: 50px; width: 100px' >Reset</button></form>"; temp += "<table border=2 bgcolor = white width = 500 cellpadding =5 ><caption><p><h3>Systemlinks:</h2></p></caption><tr><th><br>"; servidor.sendContent(temp); temp = ""; temp += "<a href="/es">Main Page</a><br><br></th></tr></table><br><br>"; temp += "<footer><p>Programmed y designed by: Tobias Kuch</p><p>Contact information: <a href ='mailto:tobias.kuch@googlemail.com'>tobias.kuch@googlemail.com</a>.</p></footer>"; temp += "</body></html>"; servidor.sendContent(temp); servidor.cliente().parada(); //Parada is needed because we sent no content length temp = ""; } / ¿** Is this en IP? */ boolean isIp(Bramante str) { for (int i = 0; i < str.length(); i++) { int c = str.charAt(i); if (c != '.' && (c < '0' || c > '9')) { return false; } } return true; } Bramante GetEncryptionType(byte thisType) { Bramante Producción total = ""; //read the encryption type y print el nombre the pasado de moda: switch (thisType) { case 5: Producción total = "WEP"; return Producción total; break; case 2: Producción total = "WPA"; return Producción total; break; case 4: Producción total = "WPA2"; return Producción total; break; case 7: Producción total = "None"; return Producción total; break; case 8: Producción total = "Coche"; return Producción total; break; } } / ¿** IP to el bramante? */ Bramante toStringIp(IPAddress ip) { Bramante res = ""; for (int i = 0; i < 3; i++) { res += Bramante((ip >> (8 * i)) & 0xFF) + "."; } res += Bramante(((ip >> 8 * 3)) & 0xFF); return res; } Bramante bytes de formato(size_t bytes) { //anuncio legible de los tamaños de almacén if (bytes < 1024) { return Bramante(bytes) + "Byte"; } else if (bytes < (1024 * 1024)) { return Bramante(bytes / 1024.0) + "KB"; } else if (bytes < (1024 * 1024 * 1024)) { return Bramante(bytes / 1024.0 / 1024.0) + "MB"; } } void loop() { if (SoftAccOK) { dnsServer.processNextRequest(); //ADN } //HTTP servidor.handleClient(); }]
En la parte cercana queremos mirar una vez el empleo práctico de nuestro código, y nos formamos, basando en este código un servidor de file pequeño.
Deseo mucha diversión con Captive el portal y en la implantación en proyectos ESP32 propios.
18 comentarios
Walter
Hi, this code is bugged, please fix it. Thanks,
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
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
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
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
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
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
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
|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
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
Hallo Matthias,
Gleiches Problem und gleiche wie bei Christoph.
Gruß
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
Gibt es eigentlich auch einen Teil 1?
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
In bool loadCredentials() muss natürlich
if (String(MyWiFiConfig.ConfigValid) == String(“TK”))
und nicht
if (String(MyWiFiConfig.ConfigValid) = String(“TK”))
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
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
Die
]
gehört wo hin ????