Anpassung
- Readme mit Beschreibug der Software - style.css mit modernerem look und HOBBYHIMMEL Farben
This commit is contained in:
parent
1bf8c6545f
commit
c39fd04b07
131
README.md
131
README.md
|
|
@ -57,6 +57,137 @@ Codierung der Ports:
|
||||||
| 23 | 5 | 6 | weiß-blau | grün | 0x22-P06 |
|
| 23 | 5 | 6 | weiß-blau | grün | 0x22-P06 |
|
||||||
| 24 | 5 | 7 | weiß-blau | weiß-braun | 0x22-P07 |
|
| 24 | 5 | 7 | weiß-blau | weiß-braun | 0x22-P07 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Software-Features
|
||||||
|
|
||||||
|
### 🌐 Webserver & Benutzeroberfläche
|
||||||
|
|
||||||
|
Das System stellt einen vollständig konfigurierbaren Webserver bereit mit folgenden Seiten:
|
||||||
|
|
||||||
|
- **Dashboard** (`/`) — Übersicht und Kontrolzentrum
|
||||||
|
- **Admin-Panel** (`/admin`) — Systeminfo und Verwaltungsfunktionen
|
||||||
|
- **Dateisystem-Manager** (`/fs.html`) — Upload, Download und Verwaltung von Dateien auf der SD
|
||||||
|
- **Port-Konfiguration** (`/portconfig`) — Konfigurieren und Benennen der 16 RJ45-Ports
|
||||||
|
|
||||||
|
### 🔌 Port Management & LED-Anzeige
|
||||||
|
|
||||||
|
**Automatische Portüberwachung:**
|
||||||
|
- Echtzeit-Überwachung aller 16 Ports via PCF8575 IO-Expander
|
||||||
|
- Automatische Erkennung von korrekten/fehlerhaften Kabelverbindungen
|
||||||
|
- Polling-Zyklus alle ~50ms
|
||||||
|
|
||||||
|
**NeoPixel LED-Codierung:**
|
||||||
|
- **Grün**: Port-Zustand korrekt (Kabel richtig eingesteckt)
|
||||||
|
- **Rot (blinkend)**: Port-Zustand falsch (Kabel falsch oder nicht eingesteckt)
|
||||||
|
- **Aus**: Port ist deaktiviert
|
||||||
|
|
||||||
|
**Port-Konfiguration:**
|
||||||
|
- Individuelle Benennung jedes Ports möglich
|
||||||
|
- Aktivieren/Deaktivieren von Ports
|
||||||
|
- Persistente Speicherung der Konfiguration in `portconfig.json`
|
||||||
|
|
||||||
|
### 📡 WiFi & Netzwerkverbindung
|
||||||
|
|
||||||
|
**Hauptmodi:**
|
||||||
|
1. **Station-Modus** — Verbindung zu bestehendem WiFi-Netzwerk
|
||||||
|
2. **Soft-AP Modus (Captive Portal)** — Fallback-Konfigurationsmodus mit SSID `EspConfig`
|
||||||
|
|
||||||
|
**Automatische Verbindungsverwaltung:**
|
||||||
|
- Timeout nach 30 Sekunden bei fehlgeschlagener Verbindung
|
||||||
|
- Automatischer Fallback zur Soft-AP (Captive Portal)
|
||||||
|
- Auto-Reconnect alle 5 Minuten
|
||||||
|
- LED-Rückmeldung: Blinken während Verbindungsaufbau, leuchten im AP-Modus
|
||||||
|
|
||||||
|
**Captive Portal:**
|
||||||
|
- Automatische Umleitung auf Konfigurationsseite
|
||||||
|
- Sicheres Speichern von WiFi-Credentials (XOR-Verschlüsselte Speicherung in `/wifi.dat`)
|
||||||
|
- Validierung: Passwort 8-64 Zeichen erforderlich
|
||||||
|
|
||||||
|
### 💾 Dateisystem (LittleFS)
|
||||||
|
|
||||||
|
**Funktionen:**
|
||||||
|
- Upload von Dateien (Drag & Drop, Mehrfach)
|
||||||
|
- Löschen von Dateien und Ordnern (rekursiv)
|
||||||
|
- Erstellen neuer Ordner
|
||||||
|
- Speicherübersicht (genutzt/verfügbar/gesamt)
|
||||||
|
- Formatierung des gesamten Filesystems möglich
|
||||||
|
|
||||||
|
**Sortierung:**
|
||||||
|
- Nach Dateiname (A-Z)
|
||||||
|
- Nach Dateigröße
|
||||||
|
|
||||||
|
**Vordefinierte HTML-Assets:**
|
||||||
|
Alle HTML/CSS-Dateien sind ins Filesystem eingebunden und können über die Web-UI verwaltet werden:
|
||||||
|
- `index.html` — Dashboard
|
||||||
|
- `admin.html` — Admin-Panel
|
||||||
|
- `fs.html` — Dateisystem-Manager
|
||||||
|
- `portconfig.html` — Port-Konfiguration
|
||||||
|
- `style.css` — Styling
|
||||||
|
|
||||||
|
### ⚙️ Admin & System-Funktionen
|
||||||
|
|
||||||
|
**Systemübersicht:**
|
||||||
|
- Live-Laufzeit (Tage, Stunden, Minuten, Sekunden)
|
||||||
|
- WiFi-Signalstärke (RSSI in dBm)
|
||||||
|
- Heap-Speicher und Fragmentierung
|
||||||
|
- Flash-Speicher (Größe, Mode, Speed)
|
||||||
|
- CPU-Frequenz
|
||||||
|
- Reset-Grund
|
||||||
|
- Sketch-Build-Zeit
|
||||||
|
- ESP Core und SDK Version
|
||||||
|
|
||||||
|
**Systemfunktionen:**
|
||||||
|
- **WiFi-Reconnect** — Manuelle Neuverbindung zum Netzwerk
|
||||||
|
- **ESP-Restart** — Neustart des Gerätes
|
||||||
|
- **OTA-Updates** — Wireless Sketch-Updates über Arduino IDE oder PlatformIO
|
||||||
|
|
||||||
|
### 🔗 REST-API Endpoints
|
||||||
|
|
||||||
|
**Admin-Informationen:**
|
||||||
|
- `GET /admin/renew` — Laufzeit und WiFi-Signal
|
||||||
|
- `GET /admin/once` — Detaillierte Systeminfo
|
||||||
|
|
||||||
|
**Port-Verwaltung:**
|
||||||
|
- `GET /portconfig/data` — Konfiguration aller 16 Ports (JSON)
|
||||||
|
- `POST /portconfig` — Speichern von Port-Namen und Enable-Status
|
||||||
|
- `GET /status/data` — Live-Status aller Ports (Name, Enable, aktueller Zustand)
|
||||||
|
|
||||||
|
**Dateisystem:**
|
||||||
|
- `POST /upload` — Datei-Upload
|
||||||
|
- `GET /format` — Filesystem formatieren
|
||||||
|
|
||||||
|
**Netzwerk:**
|
||||||
|
- `GET /reconnect` — WiFi-Reconnect triggern
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technische Architektur
|
||||||
|
|
||||||
|
### Hardware-Kommunikation
|
||||||
|
|
||||||
|
**I2C-Bus (Datenleitung D1/D2):**
|
||||||
|
- PCF8575 IO-Expander @ Adresse 0x21
|
||||||
|
- Boot-Scan aller I2C-Adressen
|
||||||
|
|
||||||
|
**GPIO/Schnittstellen:**
|
||||||
|
- D6 — NeoPixel DIN (16 RGB-LEDs, 800 kHz)
|
||||||
|
- D1 — I2C SCL (mit 10kΩ PullUp)
|
||||||
|
- D2 — I2C SDA (mit 10kΩ PullUp)
|
||||||
|
|
||||||
|
**Serielle Schnittstelle:**
|
||||||
|
- Baudrate: 115200
|
||||||
|
- Umfangreiches Debug-Output für Troubleshooting
|
||||||
|
|
||||||
|
### Code-Organisation
|
||||||
|
|
||||||
|
Das Projekt ist in 7 Arduino-Tabs organisiert:
|
||||||
|
- `KeyPatch.ino` — Hauptsketch und Setup
|
||||||
|
- `Connect.ino` — WiFi-Management und Verbindungslogik
|
||||||
|
- `Webserver.ino` — HTTP-Endpoints und Web-UI
|
||||||
|
- `Admin.ino` — Adminpanel und Systeminfo
|
||||||
|
- `LittleFS.ino` — Dateisystem-Verwaltung
|
||||||
|
- `NeoPixelHandler.ino` — LED-Steuerung und Farb-Codierung
|
||||||
|
- `Config.ino` — Port-Konfiguration und Persistierung
|
||||||
|
|
||||||
|
---
|
||||||
|
|
|
||||||
260
data/style.css
260
data/style.css
|
|
@ -1,120 +1,284 @@
|
||||||
|
|
||||||
/* For more information visit:https://fipsok.de */
|
/* HOBBYHIMMEL KeyPatch - Modern CSS */
|
||||||
|
:root {
|
||||||
|
--color-primary: #76B043;
|
||||||
|
--color-dark: #3F4242;
|
||||||
|
--color-gray: #6D6E71;
|
||||||
|
--color-light: #F5F5F5;
|
||||||
|
--color-white: #FFFFFF;
|
||||||
|
--border-radius: 12px;
|
||||||
|
--shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
--shadow-hover: 0 6px 16px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
background-color: #87cefa;
|
background-color: var(--color-light);
|
||||||
|
color: var(--color-dark);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
h1,h2 {
|
|
||||||
color: #e1e1e1;
|
h1, h2 {
|
||||||
text-shadow: 2px 2px 2px black;
|
color: var(--color-dark);
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 15px 0;
|
||||||
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
background-color: #feb1e2;
|
background-color: var(--color-white);
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 12px;
|
||||||
padding: 2px 5px 1px 0;
|
padding: 12px 16px;
|
||||||
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
|
box-shadow: var(--shadow);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border-left: 4px solid var(--color-primary);
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li:hover {
|
||||||
|
box-shadow: var(--shadow-hover);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
li a:first-child, li b {
|
li a:first-child, li b {
|
||||||
background-color: #8f05a5;
|
background-color: var(--color-primary);
|
||||||
font-weight: bold;
|
font-weight: 600;
|
||||||
color: white;
|
color: var(--color-white);
|
||||||
text-decoration:none;
|
text-decoration: none;
|
||||||
padding: 2px 5px;
|
padding: 6px 12px;
|
||||||
text-shadow: 2px 2px 1px black;
|
border-radius: 6px;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-shadow: none;
|
||||||
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li a:first-child:hover, li b:hover {
|
||||||
|
background-color: var(--color-dark);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
li strong {
|
li strong {
|
||||||
color: red;
|
color: var(--color-primary);
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
height:35px;
|
height: 40px;
|
||||||
font-size:14px;
|
font-size: 14px;
|
||||||
padding-left: .3em;
|
padding: 10px 12px;
|
||||||
|
border: 2px solid var(--color-gray);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(118, 176, 67, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
label + a {
|
label + a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 + main {
|
h1 + main {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
width: 100%;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
aside {
|
aside {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 0.2em;
|
padding: 0;
|
||||||
|
background-color: var(--color-white);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
height:40px;
|
height: 40px;
|
||||||
width:130px;
|
font-size: 16px;
|
||||||
font-size:16px;
|
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
|
box-shadow: var(--shadow);
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
padding: 0 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
box-shadow: var(--shadow-hover);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
div button {
|
div button {
|
||||||
background-color: #7bff97;
|
background-color: var(--color-primary);
|
||||||
|
color: var(--color-white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div button:hover {
|
||||||
|
background-color: #5FA03A;
|
||||||
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
background-color: var(--color-white);
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#left {
|
#left {
|
||||||
align-items:flex-end;
|
align-items: flex-end;
|
||||||
text-shadow: 0.5px 0.5px 1px #757474;
|
text-shadow: none;
|
||||||
|
color: var(--color-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
#cr {
|
#cr {
|
||||||
font-weight: bold;
|
font-weight: 600;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
#up {
|
#up {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note {
|
.note {
|
||||||
background-color: #fecdee;
|
background-color: #E8F5E9;
|
||||||
padding: 0.5em;
|
padding: 15px;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
max-width: 320px;
|
max-width: 400px;
|
||||||
border-radius: 0.5em;
|
border-radius: var(--border-radius);
|
||||||
|
border-left: 4px solid var(--color-primary);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
color: var(--color-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.no {
|
.no {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
form [title] {
|
form [title] {
|
||||||
background-color: skyblue;
|
background-color: var(--color-primary);
|
||||||
|
color: var(--color-white);
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
width: 120px;
|
padding: 10px 12px;
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form [title]:hover {
|
||||||
|
background-color: #5FA03A;
|
||||||
|
}
|
||||||
|
|
||||||
form:nth-of-type(2) {
|
form:nth-of-type(2) {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
[value*=Format] {
|
[value*=Format] {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
|
box-shadow: var(--shadow);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
background-color: var(--color-white);
|
||||||
|
border: 2px solid var(--color-primary);
|
||||||
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[name="group"] {
|
[name="group"] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[name="group"] + label {
|
[name="group"] + label {
|
||||||
font-size: 1.5em;
|
font-size: 1.1em;
|
||||||
margin-right: 5px;
|
margin-right: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-dark);
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[name="group"] + label::before {
|
[name="group"] + label::before {
|
||||||
content: "\002610";
|
content: "\002610";
|
||||||
}
|
margin-right: 8px;
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
[name="group"]:checked + label::before {
|
[name="group"]:checked + label::before {
|
||||||
content: '\002611\0027A5';
|
content: '\002611';
|
||||||
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 500px) {
|
@media only screen and (max-width: 500px) {
|
||||||
.ip {
|
body {
|
||||||
right: 6em;
|
padding: 10px;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 + main {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ip {
|
||||||
|
position: relative;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
aside {
|
aside {
|
||||||
max-width: 50vw;
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,3 @@ lib_deps =
|
||||||
adafruit/Adafruit NeoPixel @ ^1.11.0
|
adafruit/Adafruit NeoPixel @ ^1.11.0
|
||||||
https://github.com/RobTillaart/PCF8575
|
https://github.com/RobTillaart/PCF8575
|
||||||
bblanchon/ArduinoJson @ ^6.21.0
|
bblanchon/ArduinoJson @ ^6.21.0
|
||||||
|
|
||||||
board_build.filesystem = littlefs
|
|
||||||
board_upload.maximum_size = 1048576
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include <PCF8575.h>
|
#include <PCF8575.h>
|
||||||
#include <Adafruit_NeoPixel.h>
|
#include <Adafruit_NeoPixel.h>
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
|
#include <ESP8266HTTPUpdateServer.h> // for web-based firmware upload
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
#include <LittleFS.h>
|
#include <LittleFS.h>
|
||||||
#include "Config.ino"
|
#include "Config.ino"
|
||||||
|
|
@ -10,6 +11,7 @@
|
||||||
|
|
||||||
// Globale Deklarationen für alle Tabs
|
// Globale Deklarationen für alle Tabs
|
||||||
ESP8266WebServer server(80);
|
ESP8266WebServer server(80);
|
||||||
|
ESP8266HTTPUpdateServer httpUpdater; // provides /update endpoint
|
||||||
const byte DNS_PORT = 53;
|
const byte DNS_PORT = 53;
|
||||||
DNSServer dnsServer;
|
DNSServer dnsServer;
|
||||||
|
|
||||||
|
|
@ -131,10 +133,33 @@ void setup() {
|
||||||
loadPortConfig(); // Lade Port-Konfiguration
|
loadPortConfig(); // Lade Port-Konfiguration
|
||||||
connectWifi();
|
connectWifi();
|
||||||
admin();
|
admin();
|
||||||
ArduinoOTA.onStart([]() {
|
|
||||||
|
// configure OTA service (IDE/network update)
|
||||||
|
ArduinoOTA.setHostname("KeyPatch"); // optional, default is esp8266-[ChipID]
|
||||||
|
ArduinoOTA.setPassword((const char *)"esp8266"); // change to a strong password or read from config
|
||||||
|
ArduinoOTA.onStart([]() {
|
||||||
|
Serial.println("OTA start");
|
||||||
//save(); // Wenn Werte vor dem Neustart gespeichert werden sollen
|
//save(); // Wenn Werte vor dem Neustart gespeichert werden sollen
|
||||||
});
|
});
|
||||||
|
ArduinoOTA.onEnd([]() {
|
||||||
|
Serial.println("OTA end");
|
||||||
|
});
|
||||||
|
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||||
|
Serial.printf("OTA progress: %u%%\r", (progress / (total / 100)));
|
||||||
|
});
|
||||||
|
ArduinoOTA.onError([](ota_error_t error) {
|
||||||
|
Serial.printf("OTA Error[%u]: ", error);
|
||||||
|
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
|
||||||
|
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
|
||||||
|
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
|
||||||
|
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
|
||||||
|
else if (error == OTA_END_ERROR) Serial.println("End Failed");
|
||||||
|
});
|
||||||
ArduinoOTA.begin();
|
ArduinoOTA.begin();
|
||||||
|
|
||||||
|
// allow firmware upload via HTTP (web page)
|
||||||
|
httpUpdater.setup(&server); // no auth
|
||||||
|
// httpUpdater.setup(&server, "admin", "secret"); // with basic auth
|
||||||
server.begin();
|
server.begin();
|
||||||
|
|
||||||
// Handler für Port-Konfiguration
|
// Handler für Port-Konfiguration
|
||||||
|
|
|
||||||
|
|
@ -1,120 +1,284 @@
|
||||||
|
|
||||||
/* For more information visit:https://fipsok.de */
|
/* HOBBYHIMMEL KeyPatch - Modern CSS */
|
||||||
|
:root {
|
||||||
|
--color-primary: #76B043;
|
||||||
|
--color-dark: #3F4242;
|
||||||
|
--color-gray: #6D6E71;
|
||||||
|
--color-light: #F5F5F5;
|
||||||
|
--color-white: #FFFFFF;
|
||||||
|
--border-radius: 12px;
|
||||||
|
--shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
--shadow-hover: 0 6px 16px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
background-color: #87cefa;
|
background-color: var(--color-light);
|
||||||
|
color: var(--color-dark);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
h1,h2 {
|
|
||||||
color: #e1e1e1;
|
h1, h2 {
|
||||||
text-shadow: 2px 2px 2px black;
|
color: var(--color-dark);
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0 15px 0;
|
||||||
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
background-color: #feb1e2;
|
background-color: var(--color-white);
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 12px;
|
||||||
padding: 2px 5px 1px 0;
|
padding: 12px 16px;
|
||||||
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
|
box-shadow: var(--shadow);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border-left: 4px solid var(--color-primary);
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li:hover {
|
||||||
|
box-shadow: var(--shadow-hover);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
li a:first-child, li b {
|
li a:first-child, li b {
|
||||||
background-color: #8f05a5;
|
background-color: var(--color-primary);
|
||||||
font-weight: bold;
|
font-weight: 600;
|
||||||
color: white;
|
color: var(--color-white);
|
||||||
text-decoration:none;
|
text-decoration: none;
|
||||||
padding: 2px 5px;
|
padding: 6px 12px;
|
||||||
text-shadow: 2px 2px 1px black;
|
border-radius: 6px;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-shadow: none;
|
||||||
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
li a:first-child:hover, li b:hover {
|
||||||
|
background-color: var(--color-dark);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
li strong {
|
li strong {
|
||||||
color: red;
|
color: var(--color-primary);
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
height:35px;
|
height: 40px;
|
||||||
font-size:14px;
|
font-size: 14px;
|
||||||
padding-left: .3em;
|
padding: 10px 12px;
|
||||||
|
border: 2px solid var(--color-gray);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
box-shadow: 0 0 0 3px rgba(118, 176, 67, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
label + a {
|
label + a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 + main {
|
h1 + main {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
width: 100%;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
aside {
|
aside {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 0.2em;
|
padding: 0;
|
||||||
|
background-color: var(--color-white);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
height:40px;
|
height: 40px;
|
||||||
width:130px;
|
font-size: 16px;
|
||||||
font-size:16px;
|
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
|
box-shadow: var(--shadow);
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
font-family: 'Open Sans', sans-serif;
|
||||||
|
padding: 0 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
box-shadow: var(--shadow-hover);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
div button {
|
div button {
|
||||||
background-color: #7bff97;
|
background-color: var(--color-primary);
|
||||||
|
color: var(--color-white);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div button:hover {
|
||||||
|
background-color: #5FA03A;
|
||||||
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
background-color: var(--color-white);
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#left {
|
#left {
|
||||||
align-items:flex-end;
|
align-items: flex-end;
|
||||||
text-shadow: 0.5px 0.5px 1px #757474;
|
text-shadow: none;
|
||||||
|
color: var(--color-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
#cr {
|
#cr {
|
||||||
font-weight: bold;
|
font-weight: 600;
|
||||||
cursor:pointer;
|
cursor: pointer;
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
#up {
|
#up {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note {
|
.note {
|
||||||
background-color: #fecdee;
|
background-color: #E8F5E9;
|
||||||
padding: 0.5em;
|
padding: 15px;
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
max-width: 320px;
|
max-width: 400px;
|
||||||
border-radius: 0.5em;
|
border-radius: var(--border-radius);
|
||||||
|
border-left: 4px solid var(--color-primary);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
color: var(--color-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.no {
|
.no {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
form [title] {
|
form [title] {
|
||||||
background-color: skyblue;
|
background-color: var(--color-primary);
|
||||||
|
color: var(--color-white);
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
width: 120px;
|
padding: 10px 12px;
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form [title]:hover {
|
||||||
|
background-color: #5FA03A;
|
||||||
|
}
|
||||||
|
|
||||||
form:nth-of-type(2) {
|
form:nth-of-type(2) {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
[value*=Format] {
|
[value*=Format] {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
|
box-shadow: var(--shadow);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
background-color: var(--color-white);
|
||||||
|
border: 2px solid var(--color-primary);
|
||||||
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[name="group"] {
|
[name="group"] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[name="group"] + label {
|
[name="group"] + label {
|
||||||
font-size: 1.5em;
|
font-size: 1.1em;
|
||||||
margin-right: 5px;
|
margin-right: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-dark);
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[name="group"] + label::before {
|
[name="group"] + label::before {
|
||||||
content: "\002610";
|
content: "\002610";
|
||||||
}
|
margin-right: 8px;
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
[name="group"]:checked + label::before {
|
[name="group"]:checked + label::before {
|
||||||
content: '\002611\0027A5';
|
content: '\002611';
|
||||||
|
color: var(--color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 500px) {
|
@media only screen and (max-width: 500px) {
|
||||||
.ip {
|
body {
|
||||||
right: 6em;
|
padding: 10px;
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 + main {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ip {
|
||||||
|
position: relative;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
aside {
|
aside {
|
||||||
max-width: 50vw;
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.note {
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user