ESP32 E-Paper Provisioning System - Technische Analyse
Datum: 23. Oktober 2025
Anforderung: Benutzerfreundliches Setup-System für ESP32 E-Paper Displays
🎯 Anforderungen
Benutzer-Workflow
- Ausgangssituation:
- Benutzer erhält neues ESP32 E-Paper Display
- Laptop ist im gleichen WLAN wie PV-Anlage
-
Keine komplizierte Konfiguration gewünscht
-
Setup-Prozess (einmalig):
- ESP32 "flashen" mit allen Einstellungen
- WLAN-Zugangsdaten (SSID + Passwort)
- Inverter-Auswahl und IP-Adresse
- Update-Intervalle (Display + API)
-
Portal-Authentifizierung (UUID + Auth-Token)
-
Betrieb (dauerhaft):
- Automatischer WLAN-Verbindungsaufbau
- Polling vom Inverter
- Datenübermittlung an Portal (Cloudflare)
- Display-Aktualisierung
- Status im Portal sichtbar
Technische Anforderungen
- ESP32-S3 mit E-Paper Display (296×128 oder ähnlich)
- Kein/Minimale Eingabemöglichkeiten am Gerät selbst
- Browser-basierte Lösung bevorzugt (keine App-Installation)
- Maximale Benutzerfreundlichkeit
🔍 Technologie-Analyse
Option 1: ESP32 Provisioning via WiFi AP + WebBrowser ⭐ EMPFOHLEN
Technologie-Stack
- ESP32 WiFi Manager (z.B. WiFiManager Library)
- Captive Portal (automatische Browser-Umleitung)
- Web-basiertes Setup (HTML/JS im ESP32)
- WebSerial API (optional für Firmware-Upload)
Workflow
1. ESP32 startet im AP-Modus (Access Point)
└─> SSID: "SolarLog-Setup-XXXX" (XXXX = letzte 4 Stellen MAC)
└─> Kein Passwort oder Standard-Passwort
2. Benutzer verbindet Laptop mit ESP32-WLAN
└─> Automatische Captive Portal Umleitung
└─> Browser öffnet Setup-Seite automatisch
3. Setup-Webseite im Browser (gehostet auf ESP32)
┌─────────────────────────────────────────┐
│ 🔌 SolarLog ESP32 Setup │
├─────────────────────────────────────────┤
│ Schritt 1: WLAN Konfiguration │
│ ├─ Verfügbare Netzwerke scannen │
│ ├─ SSID auswählen │
│ └─ Passwort eingeben │
│ │
│ Schritt 2: Portal-Verbindung │
│ ├─ Portal-URL eingeben oder Standard │
│ │ (solarlog-api.karma.organic) │
│ └─ Login mit Portal-Account │
│ (erstellt automatisch Geräte-Token)│
│ │
│ Schritt 3: Inverter-Konfiguration │
│ ├─ Auto-Scan im lokalen Netzwerk │
│ ├─ Inverter aus Liste auswählen │
│ └─ Test-Verbindung │
│ │
│ Schritt 4: Einstellungen │
│ ├─ Display Update: 30 Sek (Standard) │
│ ├─ API Upload: 60 Sek (Standard) │
│ └─ Zeitzone: Europe/Berlin │
│ │
│ [✓ Konfiguration speichern & starten] │
└─────────────────────────────────────────┘
4. ESP32 speichert Konfiguration in Flash
└─> Verbindet sich mit konfigurierten WLAN
└─> Registriert sich automatisch im Portal
└─> Startet Normalbetrieb
5. Bei Fehler: Automatischer AP-Modus nach 3 Versuchen
└─> Benutzer kann Konfiguration korrigieren
Vorteile ✅
- Browser-basiert: Keine App-Installation nötig
- Plattform-unabhängig: Windows, Mac, Linux, Smartphone
- Captive Portal: Automatische Browser-Umleitung (bekannt von Hotel-WLAN)
- Netzwerk-Scan: Zeigt verfügbare WLANs und Inverter an
- Inverter Auto-Discovery: Scannt lokales Netzwerk nach Modbus/HTTP
- Portal-Integration: Direkter Login mit bestehendem Account
- Fail-Safe: Automatischer AP-Modus bei Verbindungsproblemen
- Keine Kabel nötig: Alles über WLAN
Nachteile ⚠️
- Benutzer muss WLAN wechseln (Laptop → ESP32 → PV-WLAN)
- Captive Portal funktioniert nicht auf allen Geräten perfekt
- ESP32 muss Web-Server hosten (Ressourcen)
Option 2: Web Bluetooth API (Chrome-basiert)
Technologie-Stack
- Web Bluetooth API (Chrome/Edge Browser)
- ESP32 BLE (Bluetooth Low Energy)
- Portal-Webseite hostet Setup-Tool
Workflow
1. Benutzer öffnet Portal-Webseite
└─> https://solarlog.karma.organic/setup
2. Klickt "Neues Gerät einrichten"
└─> Browser fragt nach Bluetooth-Berechtigung
└─> Zeigt verfügbare ESP32 Geräte
3. ESP32 wird ausgewählt
└─> Bluetooth-Verbindung aufgebaut
└─> Alle Einstellungen über BLE übertragen
4. ESP32 startet mit neuer Konfiguration
Vorteile ✅
- Kein WLAN-Wechsel nötig
- Direkt aus Portal startbar
- Elegant und modern
- Weniger Schritte für Benutzer
Nachteile ⚠️
- Nur Chrome/Edge Browser (Safari/Firefox nicht unterstützt)
- Bluetooth-Reichweite begrenzt (~10m)
- iOS Einschränkungen (Web Bluetooth nicht verfügbar)
- Komplexere Implementierung
Option 3: WebSerial API + USB-Kabel
Technologie-Stack
- WebSerial API (Chrome/Edge)
- ESP-IDF Flashing-Tool im Browser
- USB-Verbindung zum ESP32
Workflow
1. ESP32 per USB mit Laptop verbinden
2. Portal-Webseite öffnen: /flash
3. Firmware + Konfiguration hochladen
4. Automatisches Flashen über Browser
Vorteile ✅
- Zuverlässig: Direkte USB-Verbindung
- Schnell: Upload über USB
- Keine WLAN-Probleme während Setup
Nachteile ⚠️
- USB-Kabel erforderlich
- Nicht wireless
- Benutzer muss ESP32 physisch anschließen
🏆 Empfehlung: Option 3 (WebSerial API + USB-Kabel) ⭐ PROFESSIONELLE INSTALLATION
Begründung für Servicetechniker vor Ort
- Zuverlässigkeit: Direkte USB-Verbindung = Keine WLAN-Probleme während Setup
- Schnelligkeit: USB-Upload in Sekunden, kein Warten auf WLAN-Scans
- Sicherheit: Kein temporäres offenes WLAN, keine Fehlkonfiguration möglich
- Professionell: Servicetechniker hat Laptop mit USB-Kabel dabei
- Eindeutigkeit: Eine Methode, ein Workflow - kein Wireless-Troubleshooting
- Vor-Ort-Ansatz: Installation findet ohnehin am Wechselrichter statt
- Änderungen selten: Bei Änderungen kommt Techniker erneut vor Ort
Warum die "Nachteile" irrelevant sind
❌ USB-Kabel erforderlich → ✅ Techniker hat Laptop & USB-Kabel dabei
❌ Nicht wireless → ✅ Techniker steht direkt am ESP32, kein Wireless nötig
❌ Physischer Anschluss → ✅ Professionelle Installation, kein End-User-Setup
Zusätzliche Vorteile für professionelle Installation
- Kein Support-Aufwand: End-User kann nichts falsch machen
- Kein WLAN-Troubleshooting: "Warum verbindet sich ESP32 nicht?" entfällt
- Schneller: 2 Minuten statt 10 Minuten Setup-Zeit
- Dokumentiert: Techniker dokumentiert Installation im System
- Garantie: Bei Problemen kommt Techniker zurück (mit USB-Kabel)
📋 Implementierungsplan (WebSerial + USB)
Phase 1: Web-Tool für Servicetechniker
Technologien: - Web Serial API (Chrome/Edge Browser) - ESP-IDF Flashing Protocol (esptool.js) - React/TypeScript Frontend im Portal - Konfigurationsdatei-Generator (JSON → ESP32 Flash)
Setup-Tool im Portal: /setup/flash-device
// Portal-Webseite: https://solarlog.karma.organic/setup/flash-device
import { ESPLoader, Transport } from 'esptool-js';
async function flashESP32() {
// 1. Servicetechniker öffnet Portal auf Laptop
// 2. Navigiert zu "Neues Gerät installieren"
// 3. ESP32 per USB anschließen
// 4. Button "Mit ESP32 verbinden" klicken
const port = await navigator.serial.requestPort();
await port.open({ baudRate: 115200 });
const transport = new Transport(port);
const loader = new ESPLoader(transport);
// Flash Firmware + Konfiguration in einem Schritt
await loader.flashId();
await loader.write_flash(...);
}
Phase 1a: ESP32 Firmware (Vereinfacht)
Technologien: - ESP-IDF oder Arduino Framework - ArduinoJson (Konfigurationsverwaltung) - SPIFFS/LittleFS (Persistente Speicherung) - KEINE WiFiManager/WebServer mehr nötig!
Komponenten:
// 1. WiFi Manager mit Captive Portal
class ProvisioningManager {
- startAPMode() // Access Point starten
- startWebServer() // Setup-Webseite hosten
- scanNetworks() // Verfügbare WLANs listen
- scanInverters() // Modbus/HTTP Inverter finden
- saveConfiguration() // Config in Flash speichern
- validateAndConnect() // WLAN-Verbindung testen
}
// 2. Portal-Client
class PortalClient {
- registerDevice() // Gerät am Portal registrieren
- authenticate() // Login mit Token
- uploadData() // Produktionsdaten senden
- receiveCommands() // Remote-Befehle empfangen
- heartbeat() // Status-Updates
}
// 3. Inverter-Client
class InverterClient {
- autoDetect() // Inverter-Typ erkennen
- connect() // Modbus/HTTP Verbindung
- readData() // Produktionsdaten lesen
- reconnect() // Auto-Reconnect bei Fehler
}
// 4. Display-Manager
class DisplayManager {
- initialize() // E-Paper initialisieren
- updateDisplay() // Daten anzeigen
- showStatus() // Verbindungsstatus
- partialUpdate() // Schnelle Updates
}
Phase 2: Backend-Erweiterungen
Neue API-Endpoints:
# backend/app/api/v1/devices.py
POST /api/v1/devices/register
- Registriert neues ESP32-Gerät
- Erstellt Device-Token
- Input: {mac_address, model, user_id}
- Output: {device_id, auth_token, mqtt_credentials}
POST /api/v1/devices/{device_id}/data
- Empfängt Produktionsdaten vom ESP32
- Input: {current_power, daily_energy, ...}
- Authentifizierung via Device-Token
GET /api/v1/devices/{device_id}/config
- Liefert Konfiguration an ESP32
- Output: {poll_interval, display_settings, ...}
POST /api/v1/devices/{device_id}/heartbeat
- Status-Update vom ESP32
- Input: {rssi, uptime, memory, last_error}
GET /api/v1/devices/{device_id}/logs
- Debug-Logs für Support
- Letzten 100 Log-Einträge
Neue Datenbank-Models:
# backend/app/models/device.py
class Device(Base):
__tablename__ = "devices"
id: int # Primary Key
uuid: str # Eindeutige Geräte-UUID
mac_address: str # MAC-Adresse
device_token: str # Auth-Token (hashed)
user_id: int # Besitzer
inverter_id: int # Verknüpfter Inverter
# Status
status: str # online, offline, error
last_seen: datetime # Letzter Heartbeat
firmware_version: str # ESP32 Firmware
rssi: int # WLAN-Signalstärke
uptime: int # Sekunden seit Start
# Konfiguration
poll_interval: int = 60 # Sekunden
display_update_interval: int = 30
timezone: str = "Europe/Berlin"
# Debug
last_error: str
total_errors: int
total_uploads: int
created_at: datetime
updated_at: datetime
Phase 3: Frontend-Erweiterungen
Neue React-Komponenten:
// src/components/DeviceSetup.tsx
// Zeigt Anleitung für ESP32-Setup
// QR-Code für einfachen WLAN-Zugang zum ESP32-AP
// src/components/DeviceList.tsx
// Liste aller registrierten ESP32-Geräte
// Status-Anzeige (online/offline/error)
// src/components/DeviceDetails.tsx
// Detailansicht eines Geräts
// - Live-Daten
// - Debug-Logs
// - Konfiguration
// - Remote-Neustart
// src/components/DeviceProvisioning.tsx
// Optional: WebBluetooth-Alternative
// Nur für Chrome/Edge Browser
Workflow im Portal:
1. Benutzer: "Gerät hinzufügen" → Anleitung öffnet sich
├─> QR-Code wird generiert (WLAN-Zugangsdaten ESP32-AP)
├─> Temporäres Setup-Token wird erstellt
└─> Schritt-für-Schritt Anleitung
2. Benutzer scannt QR-Code mit Smartphone (optional)
└─> Oder: Manuelle Verbindung zu "SolarLog-Setup-XXXX"
3. ESP32 Captive Portal öffnet Setup-Seite
├─> Login mit Portal-Account (OAuth2)
├─> Setup-Token wird validiert
└─> Gerät wird automatisch dem Account zugeordnet
4. Nach erfolgreichem Setup:
├─> Gerät erscheint in Portal-Geräteliste
├─> Status: "Registriert - Warte auf erste Daten..."
└─> Nach 60 Sek: Erste Daten treffen ein → Status: "Online ✓"
🔐 Sicherheitskonzept
Device-Authentifizierung
# Registrierung
1. ESP32 → Portal: "Ich möchte mich registrieren"
- Sendet: MAC-Adresse, Setup-Token (vom Portal-Login)
2. Portal validiert Setup-Token
- Token nur 15 Minuten gültig
- Kann nur einmal verwendet werden
3. Portal generiert Device-Token (JWT)
- 256-bit Zufallsstring
- Gespeichert als Hash in DB
- Wird an ESP32 zurückgegeben
4. ESP32 speichert Device-Token in Flash
- Für alle zukünftigen API-Calls
- Automatische Token-Rotation alle 30 Tage
# Datenkommunikation
ESP32 → Portal: Authorization: Bearer <device_token>
Portal validiert Token → Akzeptiert Daten
Failover-Strategie
1. Primär: HTTPS REST API
└─> POST /api/v1/devices/{id}/data
└─> Intervall: 60 Sekunden
2. Fallback: MQTT (optional)
└─> Weniger Overhead
└─> Bessere Offline-Toleranz
└─> QoS 1 (mindestens einmal zugestellt)
3. Bei Verbindungsausfall:
└─> Lokales Buffering (letzte 100 Datenpunkte)
└─> Automatische Synchronisation bei Reconnect
└─> Exponential Backoff (1s, 2s, 4s, ..., max 60s)
🛠️ Setup-Webseite (im ESP32 gehostet)
HTML/JavaScript (Minimalistisch)
<!DOCTYPE html>
<html>
<head>
<title>SolarLog ESP32 Setup</title>
<style>
/* Responsive Design, funktioniert auf allen Geräten */
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: Arial; background: #f5f5f5; padding: 20px; }
.container { max-width: 500px; margin: 0 auto; background: white;
padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1 { color: #ff9800; margin-bottom: 20px; }
.step { display: none; }
.step.active { display: block; }
input, select { width: 100%; padding: 10px; margin: 10px 0; border: 1px solid #ddd; border-radius: 5px; }
button { width: 100%; padding: 12px; background: #ff9800; color: white; border: none;
border-radius: 5px; cursor: pointer; font-size: 16px; margin-top: 10px; }
button:hover { background: #f57c00; }
.network-item { padding: 10px; border: 1px solid #ddd; margin: 5px 0;
cursor: pointer; border-radius: 5px; }
.network-item:hover { background: #f5f5f5; }
.status { padding: 10px; margin: 10px 0; border-radius: 5px; }
.status.success { background: #4caf50; color: white; }
.status.error { background: #f44336; color: white; }
.spinner { border: 3px solid #f3f3f3; border-top: 3px solid #ff9800;
border-radius: 50%; width: 30px; height: 30px;
animation: spin 1s linear infinite; margin: 20px auto; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
</style>
</head>
<body>
<div class="container">
<h1>🔌 SolarLog Setup</h1>
<!-- Schritt 1: WLAN -->
<div class="step active" id="step1">
<h2>Schritt 1: WLAN Konfiguration</h2>
<p>Wähle das WLAN deiner PV-Anlage:</p>
<button onclick="scanNetworks()">📡 Netzwerke scannen</button>
<div id="networks"></div>
<br>
<label>Oder manuell eingeben:</label>
<input type="text" id="ssid" placeholder="WLAN Name (SSID)">
<input type="password" id="password" placeholder="WLAN Passwort">
<button onclick="nextStep(2)">Weiter →</button>
</div>
<!-- Schritt 2: Portal-Login -->
<div class="step" id="step2">
<h2>Schritt 2: Portal-Verbindung</h2>
<p>Melde dich mit deinem SolarLog-Account an:</p>
<input type="text" id="portalUrl" value="https://solarlog-api.karma.organic" readonly>
<input type="email" id="email" placeholder="E-Mail Adresse">
<input type="password" id="portalPassword" placeholder="Passwort">
<button onclick="loginPortal()">Login & Gerät registrieren</button>
<button onclick="nextStep(1)">← Zurück</button>
</div>
<!-- Schritt 3: Inverter -->
<div class="step" id="step3">
<h2>Schritt 3: Inverter-Auswahl</h2>
<p>Suche nach verfügbaren Invertern:</p>
<button onclick="scanInverters()">🔍 Inverter scannen</button>
<div id="inverters"></div>
<br>
<label>Oder manuell eingeben:</label>
<select id="inverterType">
<option value="SMA">SMA</option>
<option value="Kostal">Kostal</option>
<option value="Fronius">Fronius</option>
<option value="Huawei">Huawei</option>
<option value="Delta">Delta</option>
<option value="SolarEdge">SolarEdge</option>
</select>
<input type="text" id="inverterIp" placeholder="IP-Adresse (z.B. 192.168.1.100)">
<button onclick="testInverter()">🧪 Verbindung testen</button>
<button onclick="nextStep(4)">Weiter →</button>
<button onclick="nextStep(2)">← Zurück</button>
</div>
<!-- Schritt 4: Einstellungen -->
<div class="step" id="step4">
<h2>Schritt 4: Einstellungen</h2>
<label>Display Update Intervall:</label>
<select id="displayInterval">
<option value="10">10 Sekunden</option>
<option value="30" selected>30 Sekunden</option>
<option value="60">60 Sekunden</option>
</select>
<label>API Upload Intervall:</label>
<select id="apiInterval">
<option value="30">30 Sekunden</option>
<option value="60" selected>60 Sekunden</option>
<option value="300">5 Minuten</option>
</select>
<label>Zeitzone:</label>
<select id="timezone">
<option value="Europe/Berlin" selected>Europa/Berlin (MEZ)</option>
<option value="Europe/Vienna">Europa/Wien</option>
<option value="Europe/Zurich">Europa/Zürich</option>
</select>
<button onclick="saveAndFinish()">✓ Speichern & Starten</button>
<button onclick="nextStep(3)">← Zurück</button>
</div>
<!-- Schritt 5: Fertig -->
<div class="step" id="step5">
<h2>✅ Setup abgeschlossen!</h2>
<div class="status success">
<p>Dein ESP32 wurde erfolgreich konfiguriert.</p>
<p>Das Gerät startet jetzt neu und verbindet sich automatisch mit deinem WLAN.</p>
</div>
<p><strong>Nächste Schritte:</strong></p>
<ol>
<li>Verbinde deinen Laptop wieder mit deinem normalen WLAN</li>
<li>Öffne das Portal: <a href="https://solarlog.karma.organic">solarlog.karma.organic</a></li>
<li>Dein Gerät sollte nach ~30 Sekunden online sein</li>
</ol>
</div>
<div id="status"></div>
</div>
<script>
let config = {};
function nextStep(step) {
document.querySelectorAll('.step').forEach(s => s.classList.remove('active'));
document.getElementById('step' + step).classList.add('active');
}
function showStatus(message, type) {
const status = document.getElementById('status');
status.innerHTML = `<div class="status ${type}">${message}</div>`;
}
async function scanNetworks() {
showStatus('Scanne WLAN-Netzwerke...', 'info');
const resp = await fetch('/api/scan-wifi');
const networks = await resp.json();
const container = document.getElementById('networks');
container.innerHTML = networks.map(net =>
`<div class="network-item" onclick="selectNetwork('${net.ssid}')">
📶 ${net.ssid} (${net.rssi} dBm) ${net.secure ? '🔒' : ''}
</div>`
).join('');
}
function selectNetwork(ssid) {
document.getElementById('ssid').value = ssid;
document.getElementById('password').focus();
}
async function loginPortal() {
const email = document.getElementById('email').value;
const password = document.getElementById('portalPassword').value;
if (!email || !password) {
showStatus('Bitte E-Mail und Passwort eingeben', 'error');
return;
}
showStatus('Melde an...', 'info');
// Login am Portal
const resp = await fetch(document.getElementById('portalUrl').value + '/api/v1/auth/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email, password})
});
if (resp.ok) {
const data = await resp.json();
config.portalToken = data.access_token;
// Gerät registrieren
const macResp = await fetch('/api/mac-address');
const macData = await macResp.json();
const deviceResp = await fetch(document.getElementById('portalUrl').value + '/api/v1/devices/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${data.access_token}`
},
body: JSON.stringify({
mac_address: macData.mac,
model: 'ESP32-S3 E-Paper'
})
});
const deviceData = await deviceResp.json();
config.deviceId = deviceData.device_id;
config.deviceToken = deviceData.auth_token;
showStatus('✓ Erfolgreich angemeldet & registriert', 'success');
setTimeout(() => nextStep(3), 1500);
} else {
showStatus('Login fehlgeschlagen. Bitte Zugangsdaten prüfen.', 'error');
}
}
async function scanInverters() {
showStatus('Scanne lokales Netzwerk nach Invertern...', 'info');
const resp = await fetch('/api/scan-inverters');
const inverters = await resp.json();
const container = document.getElementById('inverters');
container.innerHTML = inverters.map(inv =>
`<div class="network-item" onclick="selectInverter('${inv.ip}', '${inv.type}')">
⚡ ${inv.type} @ ${inv.ip} (${inv.model})
</div>`
).join('');
}
function selectInverter(ip, type) {
document.getElementById('inverterIp').value = ip;
document.getElementById('inverterType').value = type;
}
async function testInverter() {
const ip = document.getElementById('inverterIp').value;
const type = document.getElementById('inverterType').value;
if (!ip) {
showStatus('Bitte IP-Adresse eingeben', 'error');
return;
}
showStatus('Teste Verbindung...', 'info');
const resp = await fetch('/api/test-inverter', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ip, type})
});
const result = await resp.json();
if (result.success) {
showStatus(`✓ Verbindung erfolgreich! Aktuell: ${result.current_power}W`, 'success');
} else {
showStatus('Verbindung fehlgeschlagen: ' + result.error, 'error');
}
}
async function saveAndFinish() {
const ssid = document.getElementById('ssid').value;
const password = document.getElementById('password').value;
const inverterIp = document.getElementById('inverterIp').value;
const inverterType = document.getElementById('inverterType').value;
if (!ssid || !password || !inverterIp) {
showStatus('Bitte alle Felder ausfüllen', 'error');
return;
}
const configuration = {
wifi: {
ssid: ssid,
password: password
},
portal: {
url: document.getElementById('portalUrl').value,
device_id: config.deviceId,
device_token: config.deviceToken
},
inverter: {
type: inverterType,
ip: inverterIp
},
intervals: {
display: parseInt(document.getElementById('displayInterval').value),
api: parseInt(document.getElementById('apiInterval').value)
},
timezone: document.getElementById('timezone').value
};
showStatus('Speichere Konfiguration...', 'info');
const resp = await fetch('/api/save-config', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(configuration)
});
if (resp.ok) {
nextStep(5);
// ESP32 startet in 3 Sekunden neu
setTimeout(() => {
fetch('/api/restart');
}, 3000);
} else {
showStatus('Fehler beim Speichern', 'error');
}
}
</script>
</body>
</html>
📊 Kosten-Nutzen-Analyse
Entwicklungsaufwand
| Komponente | Aufwand | Priorität |
|---|---|---|
| ESP32 Firmware (Provisioning) | 5-7 Tage | Hoch |
| ESP32 Firmware (Inverter Clients) | 3-5 Tage | Hoch |
| Backend API-Erweiterungen | 2-3 Tage | Hoch |
| Frontend Device Management | 2-3 Tage | Mittel |
| Testing & Debugging | 3-4 Tage | Hoch |
| Dokumentation | 1-2 Tage | Mittel |
| GESAMT | 16-24 Tage |
Hardware-Kosten (pro Gerät)
- ESP32-S3 DevKit: ~8-12 EUR
- E-Paper Display 2.9" (296×128): ~15-25 EUR
- Gehäuse (3D-Druck oder Kaufteil): ~5-10 EUR
- Netzteil 5V/1A: ~3-5 EUR
- GESAMT pro Gerät: ~31-52 EUR
Skalierbarkeit
- Backend kann tausende Geräte parallel verwalten
- MQTT-Broker für bessere Performance bei >100 Geräten empfohlen
- Cloudflare Tunnel skaliert automatisch
🚀 Alternativen & Erweiterungen
Phase 2 Features (später)
- Over-The-Air (OTA) Updates
- Firmware-Updates über Portal
-
Automatische Updates bei neuen Versionen
-
Remote-Konfiguration
- Einstellungen nachträglich im Portal ändern
-
ESP32 lädt neue Config beim nächsten Heartbeat
-
Multi-Inverter Support
- Ein ESP32 liest mehrere Inverter aus
-
Aggregierte Anzeige auf Display
-
Offline-Modus
- ESP32 funktioniert auch ohne Internet
- Lokales Dashboard im WLAN verfügbar
-
Automatische Sync wenn Internet wieder da
-
Smart Home Integration
- MQTT Broker für Home Assistant
- EVCC Integration für dynamisches Laden
✅ Nächste Schritte
- Entscheidung: WiFi AP + Captive Portal Ansatz bestätigen
- Hardware: ESP32-S3 Board + E-Paper Display bestellen
- MVP Entwicklung:
- Woche 1: ESP32 Provisioning System
- Woche 2: Backend Device Management
- Woche 3: Frontend Integration
-
Woche 4: Testing & Dokumentation
-
Pilotphase: Erste 3-5 Geräte testen
- Produktion: Skalierung & Optimierung