feat(ota): ArduinoOTA integration + platformio.ini refactor
- ArduinoOTA in web_server.cpp integriert (Hostname: lasercutter-display) - WebServerManager::loop() hinzugefuegt -> ArduinoOTA.handle() - webServer.loop() in main.cpp aufgerufen - platformio.ini: gemeinsamer [env]-Basisblock (lib_deps einmalig) - Neues env az-delivery-devkit-v4-ota (espota, upload_flags --auth Anleitung) - Neues env az-delivery-devkit-v4-ota-http (ElegantOTA HTTP Fallback) - upload_ota.py: HTTP-Multipart-Upload Script fuer ElegantOTA - README.md: Abschnitt Via WiFi (OTA) mit Passwort-Anleitung - Implementation-Plan.md: Task 9.9 dokumentiert
This commit is contained in:
parent
974616aee2
commit
6bef93210e
|
|
@ -305,6 +305,18 @@
|
|||
- Display: rate-limited (W 500 ms-Blinker, Minuten bei Änderung/60 s, Sekunden 1 s, M bei Statuswechsel)
|
||||
- Verifiziert: LaserTracker läuft sofort nach Neustart, unabhängig vom WiFi-Status ✅
|
||||
|
||||
- [x] **9.9** ArduinoOTA Integration + platformio.ini Refaktorierung
|
||||
- `ArduinoOTA` in `web_server.cpp` integriert (ESP32 built-in, kein lib_deps-Eintrag nötig)
|
||||
- Hostname: `lasercutter-display` → erscheint in Arduino IDE als Netzwerk-Port `lasercutter-display at <IP>`
|
||||
- Passwort: `ArduinoOTA.setPassword(cfg.webPassword)` – gleiche Auth wie Webinterface, kein separates OTA-Passwort
|
||||
- `WebServerManager::loop()` hinzugefügt → `ArduinoOTA.handle()` wird in `main.cpp` via `webServer.loop()` aufgerufen
|
||||
- `platformio.ini` auf gemeinsamen `[env]`-Basisblock umgestellt (lib_deps + build_flags einmalig, alle Environments erben)
|
||||
- Neues Environment `az-delivery-devkit-v4-ota`: `upload_protocol = espota`, IP konfigurierbar
|
||||
- Neues Environment `az-delivery-devkit-v4-ota-http`: ElegantOTA HTTP-Fallback via `upload_ota.py` extra_script
|
||||
- `upload_flags = --auth=...`: Kommentar + Anleitung für Passwort-Übergabe an espota (inkl. Umgebungsvariable-Option)
|
||||
- OTA-Passwort-Authentifizierung verifiziert: Upload via `pio run -e az-delivery-devkit-v4-ota --target upload` erfolgreich ✅
|
||||
- README.md: Abschnitt "Via WiFi (OTA)" mit vollständiger Anleitung ergänzt
|
||||
|
||||
---
|
||||
|
||||
## Phase 10 – Dokumentation & Abschluss
|
||||
|
|
|
|||
42
README.md
42
README.md
|
|
@ -334,6 +334,8 @@ Firmware-Updates können kabellos über die Weboberfläche unter `/update` einge
|
|||
|
||||
## Build & Flash
|
||||
|
||||
### Via USB (Standard)
|
||||
|
||||
```bash
|
||||
# Haupt-Firmware bauen und flashen
|
||||
pio run -e az-delivery-devkit-v4 --target upload
|
||||
|
|
@ -346,6 +348,46 @@ Ziel-Board: `az-delivery-devkit-v4` (ESP32), Upload-Port: `COM3`
|
|||
|
||||
---
|
||||
|
||||
### Via WiFi (OTA)
|
||||
|
||||
Sobald das Gerät einmal per USB geflasht wurde und im WLAN erreichbar ist, kann jeder weitere Flash-Vorgang kabellos erfolgen.
|
||||
|
||||
**Voraussetzung:** ESP32 läuft und ist im WLAN verbunden.
|
||||
|
||||
**1. IP-Adresse in `platformio.ini` eintragen** (einmalig):
|
||||
|
||||
```ini
|
||||
[env:az-delivery-devkit-v4-ota]
|
||||
upload_port = 192.168.2.62 ; <-- hier die IP des ESP32 eintragen
|
||||
```
|
||||
|
||||
Die aktuelle IP ist im Router (DHCP-Tabelle) oder über mDNS (`lasercutter-display.local`) im Browser erreichbar.
|
||||
|
||||
**2. Flashen via PlatformIO:**
|
||||
|
||||
```bash
|
||||
pio run -e az-delivery-devkit-v4-ota --target upload
|
||||
```
|
||||
|
||||
**3. Flashen via Arduino IDE:**
|
||||
|
||||
Im Menü **Tools → Port** erscheint nach ein paar Sekunden automatisch ein Netzwerk-Port:
|
||||
```
|
||||
lasercutter-display at 192.168.x.x
|
||||
```
|
||||
Diesen auswählen und normal über **Sketch → Hochladen** flashen.
|
||||
|
||||
> **Hinweis:** Falls ein Web-Passwort gesetzt ist, verwendet ArduinoOTA dasselbe Passwort. Es muss in `platformio.ini` per `upload_flags` übergeben werden:
|
||||
> ```ini
|
||||
> [env:az-delivery-devkit-v4-ota]
|
||||
> upload_flags = --auth=DEIN_WEBPASSWORT
|
||||
> ```
|
||||
> Arduino IDE fragt das Passwort beim Upload automatisch ab.
|
||||
|
||||
> **Wichtig (Erstinbetriebnahme):** Der erste OTA-Flash setzt voraus, dass die aktuelle Firmware bereits ArduinoOTA enthält. Nach einem Neuflash via USB (`az-delivery-devkit-v4`) ist OTA dauerhaft verfügbar.
|
||||
|
||||
---
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
|
|
|
|||
|
|
@ -30,9 +30,12 @@ class WebServerManager {
|
|||
public:
|
||||
WebServerManager();
|
||||
|
||||
// Routen registrieren und Server starten (einmalig in setup())
|
||||
// Routen registrieren und Server starten (einmalig nach WiFi-Connect)
|
||||
void begin();
|
||||
|
||||
// ArduinoOTA verarbeiten – in jedem loop()-Durchlauf aufrufen
|
||||
void loop();
|
||||
|
||||
private:
|
||||
AsyncWebServer* _server; // PIMPL: Pointer, full type only in web_server.cpp
|
||||
|
||||
|
|
|
|||
|
|
@ -8,27 +8,21 @@
|
|||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:az-delivery-devkit-v4]
|
||||
; =============================================================================
|
||||
; GEMEINSAME BASIS - wird von allen Environments geerbt
|
||||
; =============================================================================
|
||||
[env]
|
||||
platform = espressif32
|
||||
board = az-delivery-devkit-v4
|
||||
framework = arduino
|
||||
|
||||
; Upload & Monitor
|
||||
upload_port = COM3
|
||||
monitor_speed = 115200
|
||||
monitor_echo = yes
|
||||
monitor_filters = esp32_exception_decoder, default
|
||||
|
||||
; Partitionsschema: min_spiffs gibt mehr Platz für OTA (2x ~1.8 MB App)
|
||||
board_build.partitions = min_spiffs.csv
|
||||
|
||||
; Build-Flags
|
||||
build_flags =
|
||||
-DCORE_DEBUG_LEVEL=1 ; 0=keine, 1=Fehler, 3=Info, 5=Verbose
|
||||
-DARDUINO_LOOP_STACK_SIZE=8192
|
||||
-DELEGANTOTA_USE_ASYNC_WEBSERVER=1
|
||||
|
||||
; Bibliotheken
|
||||
lib_deps =
|
||||
majicDesigns/MD_Parola @ ^3.7.3
|
||||
majicDesigns/MD_MAX72XX @ ^3.5.1
|
||||
|
|
@ -39,6 +33,40 @@ lib_deps =
|
|||
bblanchon/ArduinoJson @ ^7.3.0
|
||||
https://github.com/ayushsharma82/ElegantOTA.git
|
||||
|
||||
; =============================================================================
|
||||
; HAUPT-ENVIRONMENT - USB Flash & Monitor
|
||||
; pio run -e az-delivery-devkit-v4 --target upload
|
||||
; =============================================================================
|
||||
[env:az-delivery-devkit-v4]
|
||||
upload_port = COM3
|
||||
|
||||
; =============================================================================
|
||||
; OTA ENVIRONMENT – Firmware via WiFi flashen (ArduinoOTA / espota)
|
||||
; Erscheint in Arduino IDE als Netzwerk-Port: lasercutter-display.local
|
||||
; PlatformIO: pio run -e az-delivery-devkit-v4-ota --target upload
|
||||
; IP anpassen: upload_port = <IP des ESP32> (oder lasercutter-display.local)
|
||||
;
|
||||
; Falls ein Web-Passwort gesetzt ist, muss es hier eingetragen werden:
|
||||
; upload_flags = --auth=DEIN_WEBPASSWORT
|
||||
; Oder als Umgebungsvariable (empfohlen, damit das Passwort nicht im Repo landet):
|
||||
; Vor dem Upload: set LASERCUTTER_OTA_PW=DEIN_PASSWORT (Windows)
|
||||
; upload_flags = --auth=${sysenv.LASERCUTTER_OTA_PW}
|
||||
; =============================================================================
|
||||
[env:az-delivery-devkit-v4-ota]
|
||||
upload_port = 192.168.2.62
|
||||
upload_protocol = espota
|
||||
; upload_flags = --auth=DEIN_WEBPASSWORT ; <-- auskommentieren und anpassen wenn Passwort gesetzt
|
||||
|
||||
; =============================================================================
|
||||
; OTA ENVIRONMENT (HTTP) – Firmware via ElegantOTA Webinterface
|
||||
; Nur als Fallback falls ArduinoOTA nicht erreichbar
|
||||
; pio run -e az-delivery-devkit-v4-ota-http --target upload
|
||||
; =============================================================================
|
||||
[env:az-delivery-devkit-v4-ota-http]
|
||||
upload_port = 192.168.2.62
|
||||
upload_protocol = custom
|
||||
extra_scripts = upload_ota.py
|
||||
|
||||
; =============================================================================
|
||||
; TEST ENVIRONMENT 1.4 – Dot-Matrix-Display Verdrahtungstest
|
||||
; Flash: pio run -e test-display --target upload
|
||||
|
|
|
|||
|
|
@ -112,6 +112,11 @@ void loop() {
|
|||
}
|
||||
}
|
||||
|
||||
// WebServer-Loop: ArduinoOTA verarbeiten
|
||||
if (webStarted) {
|
||||
webServer.loop();
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Display: rate-limited (Modul-Updates nur bei Bedarf)
|
||||
// ==========================================================================
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "web_server.h"
|
||||
#include <ESPAsyncWebServer.h> // voller Include nur in .cpp (PIMPL)
|
||||
#include <ArduinoOTA.h> // ESP32 built-in, kein lib_deps-Eintrag noetig
|
||||
#include "settings.h"
|
||||
#include "laser_tracker.h"
|
||||
#include "mqtt_client.h"
|
||||
|
|
@ -42,12 +43,32 @@ void WebServerManager::begin() {
|
|||
}
|
||||
ElegantOTA.begin(_server);
|
||||
_server->begin();
|
||||
|
||||
// ArduinoOTA – erscheint als Netzwerk-Port in Arduino IDE und PlatformIO
|
||||
const Settings& cfg = settings.get();
|
||||
ArduinoOTA.setHostname("lasercutter-display");
|
||||
if (cfg.webPassword[0] != '\0') {
|
||||
ArduinoOTA.setPassword(cfg.webPassword); // gleiche Auth wie Webinterface
|
||||
}
|
||||
ArduinoOTA.onStart([]() { LOG_I("OTA", "Start OTA-Update..."); });
|
||||
ArduinoOTA.onEnd([]() { LOG_I("OTA", "OTA fertig – Neustart"); });
|
||||
ArduinoOTA.onError([](ota_error_t e) { LOG_E("OTA", "Fehler [%u]", e); });
|
||||
ArduinoOTA.begin();
|
||||
LOG_I("WEB", "ArduinoOTA aktiv: lasercutter-display.local");
|
||||
|
||||
Serial.println(F("[WebServer] gestartet auf Port 80"));
|
||||
Serial.print(F("[WebServer] URL: http://"));
|
||||
Serial.println(WiFi.localIP());
|
||||
LOG_I("WEB", "Auth: %s", s.webPassword[0] ? "aktiv" : "deaktiviert (kein Passwort)");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// loop() – ArduinoOTA verarbeiten (in main loop() aufrufen)
|
||||
// -----------------------------------------------------------------------------
|
||||
void WebServerManager::loop() {
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// requireAuth() – HTTP Basic Auth pruefen (nur wenn Passwort konfiguriert)
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
|||
52
upload_ota.py
Normal file
52
upload_ota.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# upload_ota.py – OTA-Upload via ElegantOTA HTTP-Endpoint
|
||||
# PlatformIO extra_script: ersetzt den Upload-Befehl durch HTTP-POST an /update
|
||||
#
|
||||
# Verwendung: pio run -e az-delivery-devkit-v4-ota --target upload
|
||||
# IP-Adresse in platformio.ini: upload_port = 192.168.x.x
|
||||
|
||||
Import("env")
|
||||
|
||||
import urllib.request
|
||||
import os
|
||||
|
||||
def do_ota_upload(source, target, env):
|
||||
firmware_path = str(source[0])
|
||||
ip = env.get("UPLOAD_PORT", "")
|
||||
|
||||
if not ip:
|
||||
print("FEHLER: upload_port in platformio.ini nicht gesetzt!")
|
||||
env.Exit(1)
|
||||
|
||||
url = f"http://{ip}/update"
|
||||
print(f"\n=== OTA Upload ===")
|
||||
print(f" Firmware : {firmware_path}")
|
||||
print(f" Ziel : {url}")
|
||||
print(f" Groesse : {os.path.getsize(firmware_path)} Bytes")
|
||||
|
||||
boundary = "ElegantOtaBoundary"
|
||||
with open(firmware_path, "rb") as f:
|
||||
firmware_data = f.read()
|
||||
|
||||
body = (
|
||||
f"--{boundary}\r\n"
|
||||
f'Content-Disposition: form-data; name="firmware"; filename="firmware.bin"\r\n'
|
||||
f"Content-Type: application/octet-stream\r\n\r\n"
|
||||
).encode() + firmware_data + f"\r\n--{boundary}--\r\n".encode()
|
||||
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=body,
|
||||
method="POST",
|
||||
headers={"Content-Type": f"multipart/form-data; boundary={boundary}"}
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=60) as resp:
|
||||
result = resp.read().decode("utf-8", errors="ignore")
|
||||
print(f" Status : {resp.status} {resp.reason}")
|
||||
print(f" Antwort : {result}")
|
||||
print("=== Upload erfolgreich - ESP32 startet neu ===\n")
|
||||
except Exception as e:
|
||||
print(f"\nFEHLER beim OTA-Upload: {e}")
|
||||
env.Exit(1)
|
||||
|
||||
env.Replace(UPLOADCMD=do_ota_upload)
|
||||
Loading…
Reference in New Issue
Block a user