| eclipse.preferences.version=1 | eclipse.preferences.version=1 | ||||
| encoding/HtmlPages.h=UTF-8 | |||||
| encoding/LEDLamp.ino=UTF-8 | encoding/LEDLamp.ino=UTF-8 |
| /* | |||||
| * 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_ */ |
| #include <EEPROM.h> | #include <EEPROM.h> | ||||
| #include <DNSServer.h> | #include <DNSServer.h> | ||||
| #include "States.h" | #include "States.h" | ||||
| #include "HtmlPages.h" | |||||
| AppState state = STATE_BOOT; | AppState state = STATE_BOOT; | ||||
| unsigned long connectStart = 0; | unsigned long connectStart = 0; | ||||
| } | } | ||||
| // ---------- CAPTIVE PORTAL PAGE ---------- | // ---------- CAPTIVE PORTAL PAGE ---------- | ||||
| void handleRoot() { | |||||
| void handleSetupPage() { | |||||
| String mac = WiFi.macAddress(); | 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); | webServer.send(200, "text/html", html); | ||||
| } | } | ||||
| // ---------- SAVE ---------- | // ---------- SAVE ---------- | ||||
| void handleSave() { | void handleSave() { | ||||
| String ssid = webServer.arg("ssid"); | String ssid = webServer.arg("ssid"); | ||||
| dnsServer.start(DNS_PORT, "*", apIP); | dnsServer.start(DNS_PORT, "*", apIP); | ||||
| // Webserver Routes | // Webserver Routes | ||||
| webServer.on("/", handleRoot); | |||||
| webServer.on("/", handleSetupPage); | |||||
| webServer.on("/save", HTTP_POST, handleSave); | webServer.on("/save", HTTP_POST, handleSave); | ||||
| webServer.onNotFound(handleNotFound); | webServer.onNotFound(handleNotFound); | ||||
| Serial.println("Captive Portal gestartet"); | 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) { | void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) { | ||||
| } | } | ||||
| void handleBoot() { | void handleBoot() { | ||||
| if (config.valid) { | if (config.valid) { | ||||
| WiFi.mode(WIFI_STA); | WiFi.mode(WIFI_STA); | ||||
| WiFi.begin(config.ssid, config.pass); | WiFi.begin(config.ssid, config.pass); | ||||
| } | } | ||||
| void handleConnecting() { | void handleConnecting() { | ||||
| if (WiFi.status() == WL_CONNECTED) { | if (WiFi.status() == WL_CONNECTED) { | ||||
| startSTAWebServer(); | startSTAWebServer(); | ||||
| setState(STATE_STA_MODE); | setState(STATE_STA_MODE); | ||||
| Serial.println("Starte STA Services"); | Serial.println("Starte STA Services"); | ||||
| // Webseite | // Webseite | ||||
| webServer.on("/", handleRoot); | |||||
| webServer.on("/", handleSTAControlPage); | |||||
| // optional | // optional | ||||
| webServer.on("/save", HTTP_POST, handleSave); | webServer.on("/save", HTTP_POST, handleSave); |
| //This is a automatic generated file | //This is a automatic generated file | ||||
| //Please do not modify this file | //Please do not modify this file | ||||
| //If you touch this file your change will be overwritten during the next build | //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" | ||||
| #include "Arduino.h" | #include "Arduino.h" | ||||
| #include <EEPROM.h> | #include <EEPROM.h> | ||||
| #include <DNSServer.h> | #include <DNSServer.h> | ||||
| #include "States.h" | #include "States.h" | ||||
| #include "HtmlPages.h" | |||||
| void saveConfig() ; | void saveConfig() ; | ||||
| void loadConfig() ; | void loadConfig() ; | ||||
| void handleRoot() ; | |||||
| void handleSetupPage() ; | |||||
| void handleSTAControlPage() ; | |||||
| void handleSave() ; | void handleSave() ; | ||||
| void handleNotFound() ; | void handleNotFound() ; | ||||
| void startAP() ; | void startAP() ; | ||||
| bool connectSTA() ; | |||||
| void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) ; | void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) ; | ||||
| void setState(AppState newState) ; | void setState(AppState newState) ; | ||||
| void handleBoot() ; | void handleBoot() ; |