| @@ -1,2 +1,3 @@ | |||
| eclipse.preferences.version=1 | |||
| encoding/HtmlPages.h=UTF-8 | |||
| encoding/LEDLamp.ino=UTF-8 | |||
| @@ -0,0 +1,340 @@ | |||
| /* | |||
| * HtmlPage.h | |||
| * | |||
| * Created on: 08.06.2026 | |||
| * Author: FSmilari | |||
| */ | |||
| #ifndef HTMLPAGES_H_ | |||
| #define HTMLPAGES_H_ | |||
| #include <WString.h> | |||
| const String getSetupPage(String macAddress) { | |||
| String html = | |||
| R"rawliteral( | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <script> | |||
| function togglePassword() { | |||
| const pass = document.getElementById("pass"); | |||
| const eye = document.querySelector(".toggle-eye"); | |||
| if (pass.type === "password") { | |||
| pass.type = "text"; | |||
| eye.textContent = "❌"; | |||
| } else { | |||
| pass.type = "password"; | |||
| eye.textContent = "👁"; | |||
| } | |||
| } | |||
| </script> | |||
| <head> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <title>Wemos LEDLamp Setup</title> | |||
| <style> | |||
| body { | |||
| margin: 0; | |||
| font-family: Arial, sans-serif; | |||
| background: linear-gradient(135deg, #1e1e2f, #2b5876); | |||
| color: white; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| height: 100vh; | |||
| } | |||
| * { | |||
| box-sizing: border-box; | |||
| } | |||
| .card { | |||
| background: rgba(255,255,255,0.08); | |||
| backdrop-filter: blur(10px); | |||
| padding: 25px; | |||
| border-radius: 16px; | |||
| width: 90%; | |||
| max-width: 360px; | |||
| box-shadow: 0 8px 20px rgba(0,0,0,0.3); | |||
| animation: fadeIn 0.8s ease; | |||
| } | |||
| h2 { | |||
| margin-top: 0; | |||
| text-align: center; | |||
| } | |||
| .info { | |||
| font-size: 12px; | |||
| opacity: 0.8; | |||
| margin-top: 8px; | |||
| margin-bottom: 15px; | |||
| text-align: center; | |||
| } | |||
| input { | |||
| width: 100%; | |||
| padding: 12px; | |||
| margin: 8px 0px; | |||
| border-radius: 10px; | |||
| border: none; | |||
| outline: none; | |||
| font-size: 14px; | |||
| transition: 0.2s; | |||
| } | |||
| input:focus { | |||
| transform: scale(1.02); | |||
| } | |||
| button { | |||
| width: 100%; | |||
| padding: 12px; | |||
| margin-top: 10px; | |||
| border: none; | |||
| border-radius: 10px; | |||
| background: #00c6ff; | |||
| color: white; | |||
| font-size: 16px; | |||
| cursor: pointer; | |||
| transition: 0.3s; | |||
| } | |||
| button:hover { | |||
| background: #0072ff; | |||
| transform: translateY(-2px); | |||
| } | |||
| .mac { | |||
| font-size: 11px; | |||
| text-align: center; | |||
| margin-bottom: 15px; | |||
| opacity: 0.7; | |||
| word-break: break-all; | |||
| } | |||
| .pw-wrapper { | |||
| position: relative; | |||
| width: 100%; | |||
| margin: 8px 0; | |||
| } | |||
| .pw-wrapper input { | |||
| width: 100%; | |||
| padding: 12px 40px 12px 12px; /* Platz für Icon rechts */ | |||
| border-radius: 10px; | |||
| border: none; | |||
| outline: none; | |||
| font-size: 14px; | |||
| box-sizing: border-box; | |||
| } | |||
| .toggle-eye { | |||
| position: absolute; | |||
| right: 12px; | |||
| top: 50%; | |||
| transform: translateY(-50%); | |||
| cursor: pointer; | |||
| font-size: 16px; | |||
| opacity: 0.6; | |||
| user-select: none; | |||
| transition: 0.2s; | |||
| } | |||
| .toggle-eye:hover { | |||
| opacity: 1; | |||
| } | |||
| @keyframes fadeIn { | |||
| from {opacity: 0; transform: translateY(10px);} | |||
| to {opacity: 1; transform: translateY(0);} | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <div class="card"> | |||
| <h2>Wemos LEDLamp Setup</h2> | |||
| <div class="mac">Device: )rawliteral" | |||
| + macAddress | |||
| + R"rawliteral(</div> | |||
| <form action="/save" method="POST"> | |||
| <input name="ssid" placeholder="WLAN Name (SSID)"> | |||
| <div class="pw-wrapper"> | |||
| <input id="pass" name="pass" type="password" placeholder="Passwort"> | |||
| <span class="toggle-eye" onclick="togglePassword()">👁</span> | |||
| </div> | |||
| <button type="submit">Speichern & Verbinden</button> | |||
| </form> | |||
| <div class="info">Verbinde den LEDLamp Wemos mit einem WLAN Netzwerk</div> | |||
| </body> | |||
| </html> | |||
| )rawliteral"; | |||
| return html; | |||
| } | |||
| const String getSTAControlPage(String macAddress) { | |||
| String html = | |||
| R"rawliteral( | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <head> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <title>Wemos LEDLamp Control</title> | |||
| <style> | |||
| body { | |||
| margin: 0; | |||
| font-family: Arial, sans-serif; | |||
| background: linear-gradient(135deg, #1e1e2f, #2b5876); | |||
| color: white; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| height: 100vh; | |||
| } | |||
| * { | |||
| box-sizing: border-box; | |||
| } | |||
| .card { | |||
| background: rgba(255,255,255,0.08); | |||
| backdrop-filter: blur(10px); | |||
| padding: 25px; | |||
| border-radius: 16px; | |||
| width: 90%; | |||
| max-width: 360px; | |||
| box-shadow: 0 8px 20px rgba(0,0,0,0.3); | |||
| animation: fadeIn 0.8s ease; | |||
| } | |||
| h2 { | |||
| margin-top: 0; | |||
| text-align: center; | |||
| } | |||
| .info { | |||
| font-size: 12px; | |||
| opacity: 0.8; | |||
| margin-bottom: 15px; | |||
| text-align: center; | |||
| } | |||
| input { | |||
| width: 100%; | |||
| padding: 12px; | |||
| margin: 8px 0; | |||
| border-radius: 10px; | |||
| border: none; | |||
| outline: none; | |||
| font-size: 14px; | |||
| transition: 0.2s; | |||
| } | |||
| input:focus { | |||
| transform: scale(1.02); | |||
| } | |||
| button { | |||
| width: 100%; | |||
| padding: 12px; | |||
| margin-top: 10px; | |||
| border: none; | |||
| border-radius: 10px; | |||
| background: #00c6ff; | |||
| color: white; | |||
| font-size: 16px; | |||
| cursor: pointer; | |||
| transition: 0.3s; | |||
| } | |||
| button:hover { | |||
| background: #0072ff; | |||
| transform: translateY(-2px); | |||
| } | |||
| .mac { | |||
| font-size: 11px; | |||
| text-align: center; | |||
| margin-bottom: 15px; | |||
| opacity: 0.7; | |||
| word-break: break-all; | |||
| } | |||
| @keyframes fadeIn { | |||
| from {opacity: 0; transform: translateY(10px);} | |||
| to {opacity: 1; transform: translateY(0);} | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <div class="card"> | |||
| <h2>Wemos LEDLamp Control</h2> | |||
| <div class="mac">Device: )rawliteral" | |||
| + macAddress | |||
| + R"rawliteral(</div> | |||
| <div class="btn-row"> | |||
| <button type="button" onclick="ledOn()">LED EIN</button> | |||
| <button type="button" onclick="ledOff()">LED AUS</button> | |||
| </div> | |||
| <script> | |||
| let ws; | |||
| function connectWS() { | |||
| ws = new WebSocket("ws://" + location.hostname + ":81/"); | |||
| ws.onopen = () => { | |||
| console.log("WebSocket connected"); | |||
| }; | |||
| ws.onmessage = (e) => { | |||
| console.log("ESP:", e.data); | |||
| }; | |||
| ws.onclose = () => { | |||
| console.log("WS disconnected → reconnect"); | |||
| setTimeout(connectWS, 1000); | |||
| }; | |||
| } | |||
| connectWS(); | |||
| function ledOn() { | |||
| if (ws && ws.readyState === 1) { | |||
| ws.send("/ledOn"); | |||
| } | |||
| } | |||
| function ledOff() { | |||
| if (ws && ws.readyState === 1) { | |||
| ws.send("/ledOff"); | |||
| } | |||
| } | |||
| </script> | |||
| </body> | |||
| </html> | |||
| )rawliteral"; | |||
| return html; | |||
| } | |||
| #endif /* HTMLPAGES_H_ */ | |||
| @@ -4,6 +4,7 @@ | |||
| #include <EEPROM.h> | |||
| #include <DNSServer.h> | |||
| #include "States.h" | |||
| #include "HtmlPages.h" | |||
| AppState state = STATE_BOOT; | |||
| unsigned long connectStart = 0; | |||
| @@ -44,238 +45,20 @@ void loadConfig() { | |||
| } | |||
| // ---------- CAPTIVE PORTAL PAGE ---------- | |||
| void handleRoot() { | |||
| void handleSetupPage() { | |||
| String mac = WiFi.macAddress(); | |||
| String html = | |||
| R"rawliteral( | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <script> | |||
| function togglePassword() { | |||
| const pass = document.getElementById("pass"); | |||
| const eye = document.querySelector(".toggle-eye"); | |||
| if (pass.type === "password") { | |||
| pass.type = "text"; | |||
| eye.textContent = "❌"; | |||
| } else { | |||
| pass.type = "password"; | |||
| eye.textContent = "👁"; | |||
| } | |||
| } | |||
| </script> | |||
| <head> | |||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
| <title>Wemos Setup</title> | |||
| <style> | |||
| body { | |||
| margin: 0; | |||
| font-family: Arial, sans-serif; | |||
| background: linear-gradient(135deg, #1e1e2f, #2b5876); | |||
| color: white; | |||
| display: flex; | |||
| justify-content: center; | |||
| align-items: center; | |||
| height: 100vh; | |||
| } | |||
| * { | |||
| box-sizing: border-box; | |||
| } | |||
| .card { | |||
| background: rgba(255,255,255,0.08); | |||
| backdrop-filter: blur(10px); | |||
| padding: 25px; | |||
| border-radius: 16px; | |||
| width: 90%; | |||
| max-width: 360px; | |||
| box-shadow: 0 8px 20px rgba(0,0,0,0.3); | |||
| animation: fadeIn 0.8s ease; | |||
| } | |||
| h2 { | |||
| margin-top: 0; | |||
| text-align: center; | |||
| } | |||
| .info { | |||
| font-size: 12px; | |||
| opacity: 0.8; | |||
| margin-bottom: 15px; | |||
| text-align: center; | |||
| } | |||
| input { | |||
| width: 100%; | |||
| padding: 12px; | |||
| margin: 8px 0; | |||
| border-radius: 10px; | |||
| border: none; | |||
| outline: none; | |||
| font-size: 14px; | |||
| transition: 0.2s; | |||
| } | |||
| input:focus { | |||
| transform: scale(1.02); | |||
| } | |||
| button { | |||
| width: 100%; | |||
| padding: 12px; | |||
| margin-top: 10px; | |||
| border: none; | |||
| border-radius: 10px; | |||
| background: #00c6ff; | |||
| color: white; | |||
| font-size: 16px; | |||
| cursor: pointer; | |||
| transition: 0.3s; | |||
| } | |||
| button:hover { | |||
| background: #0072ff; | |||
| transform: translateY(-2px); | |||
| } | |||
| .mac { | |||
| font-size: 11px; | |||
| text-align: center; | |||
| margin-bottom: 15px; | |||
| opacity: 0.7; | |||
| word-break: break-all; | |||
| } | |||
| .pw-wrapper { | |||
| position: relative; | |||
| width: 100%; | |||
| margin: 8px 0; | |||
| } | |||
| .pw-wrapper input { | |||
| width: 100%; | |||
| padding: 12px 40px 12px 12px; /* Platz für Icon rechts */ | |||
| border-radius: 10px; | |||
| border: none; | |||
| outline: none; | |||
| font-size: 14px; | |||
| box-sizing: border-box; | |||
| } | |||
| .toggle-eye { | |||
| position: absolute; | |||
| right: 12px; | |||
| top: 50%; | |||
| transform: translateY(-50%); | |||
| cursor: pointer; | |||
| font-size: 16px; | |||
| opacity: 0.6; | |||
| user-select: none; | |||
| transition: 0.2s; | |||
| } | |||
| .toggle-eye:hover { | |||
| opacity: 1; | |||
| } | |||
| @keyframes fadeIn { | |||
| from {opacity: 0; transform: translateY(10px);} | |||
| to {opacity: 1; transform: translateY(0);} | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <div class="card"> | |||
| <h2>Wemos Setup</h2> | |||
| <div class="mac">Device: )rawliteral" | |||
| + mac | |||
| + R"rawliteral(</div> | |||
| <form action="/save" method="POST"> | |||
| <input name="ssid" placeholder="WLAN Name (SSID)"> | |||
| <div class="pw-wrapper"> | |||
| <input id="pass" name="pass" type="password" placeholder="Passwort"> | |||
| <span class="toggle-eye" onclick="togglePassword()">👁</span> | |||
| </div> | |||
| <button type="submit">Speichern & Verbinden</button> | |||
| </form> | |||
| <div class="btn-row"> | |||
| <button type="button" onclick="ledOn()">LED EIN</button> | |||
| <button type="button" onclick="ledOff()">LED AUS</button> | |||
| </div> | |||
| </div> | |||
| <script> | |||
| let ws; | |||
| function connectWS() { | |||
| ws = new WebSocket("ws://" + location.hostname + ":81/"); | |||
| ws.onopen = () => { | |||
| console.log("WebSocket connected"); | |||
| }; | |||
| ws.onmessage = (e) => { | |||
| console.log("ESP:", e.data); | |||
| }; | |||
| ws.onclose = () => { | |||
| console.log("WS disconnected → reconnect"); | |||
| setTimeout(connectWS, 1000); | |||
| }; | |||
| } | |||
| connectWS(); | |||
| function ledOn() { | |||
| if (ws && ws.readyState === 1) { | |||
| ws.send("/ledOn"); | |||
| } | |||
| } | |||
| function ledOff() { | |||
| if (ws && ws.readyState === 1) { | |||
| ws.send("/ledOff"); | |||
| } | |||
| } | |||
| function togglePassword() { | |||
| const pass = document.getElementById("pass"); | |||
| const eye = document.querySelector(".toggle-eye"); | |||
| if (pass.type === "password") { | |||
| pass.type = "text"; | |||
| eye.textContent = "🚫"; | |||
| } else { | |||
| pass.type = "password"; | |||
| eye.textContent = "👁"; | |||
| } | |||
| String html = getSetupPage(mac); | |||
| webServer.send(200, "text/html", html); | |||
| } | |||
| </script> | |||
| </body> | |||
| </html> | |||
| )rawliteral"; | |||
| // ---------- Wemos Control PAGE ---------- | |||
| void handleSTAControlPage() { | |||
| String mac = WiFi.macAddress(); | |||
| String html = getSTAControlPage(mac); | |||
| webServer.send(200, "text/html", html); | |||
| } | |||
| // ---------- SAVE ---------- | |||
| void handleSave() { | |||
| String ssid = webServer.arg("ssid"); | |||
| @@ -314,7 +97,7 @@ void startAP() { | |||
| dnsServer.start(DNS_PORT, "*", apIP); | |||
| // Webserver Routes | |||
| webServer.on("/", handleRoot); | |||
| webServer.on("/", handleSetupPage); | |||
| webServer.on("/save", HTTP_POST, handleSave); | |||
| webServer.onNotFound(handleNotFound); | |||
| @@ -323,41 +106,6 @@ void startAP() { | |||
| Serial.println("Captive Portal gestartet"); | |||
| } | |||
| // ---------- CONNECT STA ---------- | |||
| bool connectSTA() { | |||
| WiFi.mode(WIFI_STA); | |||
| WiFi.begin(config.ssid, config.pass); | |||
| Serial.println(); | |||
| Serial.print("Verbinde mit WLAN: "); | |||
| Serial.println(config.ssid); | |||
| int tries = 0; | |||
| while (WiFi.status() != WL_CONNECTED && tries < 20) { | |||
| delay(500); | |||
| Serial.print("."); | |||
| tries++; | |||
| } | |||
| if (WiFi.status() == WL_CONNECTED) { | |||
| Serial.println(); | |||
| Serial.println("\nVerbunden!"); | |||
| Serial.print("IP: "); | |||
| Serial.println(WiFi.localIP()); | |||
| webServer.on("/", handleRoot); | |||
| webServer.on("/save", HTTP_POST, handleSave); | |||
| webServer.begin(); | |||
| webSocket.begin(); | |||
| webSocket.onEvent(webSocketEvent); | |||
| Serial.println("WebSocket gestartet auf Port 81"); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) { | |||
| @@ -403,7 +151,6 @@ void setState(AppState newState) { | |||
| } | |||
| void handleBoot() { | |||
| if (config.valid) { | |||
| WiFi.mode(WIFI_STA); | |||
| WiFi.begin(config.ssid, config.pass); | |||
| @@ -416,7 +163,6 @@ void handleBoot() { | |||
| } | |||
| void handleConnecting() { | |||
| if (WiFi.status() == WL_CONNECTED) { | |||
| startSTAWebServer(); | |||
| setState(STATE_STA_MODE); | |||
| @@ -456,7 +202,7 @@ void startSTAWebServer() | |||
| Serial.println("Starte STA Services"); | |||
| // Webseite | |||
| webServer.on("/", handleRoot); | |||
| webServer.on("/", handleSTAControlPage); | |||
| // optional | |||
| webServer.on("/save", HTTP_POST, handleSave); | |||
| @@ -2,7 +2,7 @@ | |||
| //This is a automatic generated file | |||
| //Please do not modify this file | |||
| //If you touch this file your change will be overwritten during the next build | |||
| //This file has been generated on 2026-06-08 17:59:19 | |||
| //This file has been generated on 2026-06-08 18:48:53 | |||
| #include "Arduino.h" | |||
| #include "Arduino.h" | |||
| @@ -11,14 +11,15 @@ | |||
| #include <EEPROM.h> | |||
| #include <DNSServer.h> | |||
| #include "States.h" | |||
| #include "HtmlPages.h" | |||
| void saveConfig() ; | |||
| void loadConfig() ; | |||
| void handleRoot() ; | |||
| void handleSetupPage() ; | |||
| void handleSTAControlPage() ; | |||
| void handleSave() ; | |||
| void handleNotFound() ; | |||
| void startAP() ; | |||
| bool connectSTA() ; | |||
| void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) ; | |||
| void setState(AppState newState) ; | |||
| void handleBoot() ; | |||