- Ring-Buffer _logBuf in web_server.cpp: webLogForward() schreibt auf Core 1 - GET /log-data liefert Puffer als Plain-Text (kein WebSocket, kein Core-Konflikt) - Browser pollt alle 2 s, Auto-Scroll, dunkles Terminal-Theme - LOG_I/LOG_E/LOG_D: Timestamp (HH:MM:SS nach NTP, sonst +Xs), webLogForward() - Alle Serial.* in laser_tracker.cpp, mqtt_client.cpp, web_server.cpp auf LOG_I/LOG_E - main.cpp: esp_reset_reason() beim Booten loggen (POWER_ON / WATCHDOG / PANIC...) - telnet_logger.h entfernt (war nur noch Deprecated-Stub) - Feature-Requests.md: FR-002 abgeschlossen
209 lines
7.0 KiB
C++
209 lines
7.0 KiB
C++
// =============================================================================
|
||
// main.cpp - Orchestrierung aller Module
|
||
//
|
||
// Prioritaeten:
|
||
// 1. LaserTracker + Display: starten SOFORT, laufen IMMER
|
||
// 2. WiFi: non-blocking, unterbricht niemals LaserTracker
|
||
// 3. MQTT + WebServer: Lazy-Init beim ersten WiFi-Connect
|
||
//
|
||
// Display-Takte (rate-limited):
|
||
// Modul 0 (W): blinkt 500ms wenn WiFi getrennt
|
||
// Module 1-3: Minutenanzeige, Update bei Wertwechsel oder max. alle 60 s
|
||
// Modul 4 (M): nur bei Statuswechsel
|
||
// Module 5-7: Countdown/Ring/Idle, Update alle 1 s
|
||
// =============================================================================
|
||
|
||
#include <Arduino.h>
|
||
#include <esp_task_wdt.h>
|
||
#include <esp_system.h> // esp_reset_reason()
|
||
#include "config.h"
|
||
#include "settings.h"
|
||
#include "wifi_connector.h"
|
||
#include "display_manager.h"
|
||
#include "laser_tracker.h"
|
||
#include "mqtt_client.h"
|
||
#include "web_server.h"
|
||
|
||
// Lazy-Init Flags (MQTT + WebServer erst wenn WiFi verfuegbar)
|
||
static bool mqttStarted = false;
|
||
static bool webStarted = false;
|
||
|
||
void setup() {
|
||
Serial.begin(SERIAL_BAUD_RATE);
|
||
|
||
// Reset-Grund auslesen – erscheint als erstes nach jedem Neustart im Log
|
||
const char* resetReason = "UNBEKANNT";
|
||
switch (esp_reset_reason()) {
|
||
case ESP_RST_POWERON: resetReason = "POWER_ON"; break;
|
||
case ESP_RST_SW: resetReason = "SOFTWARE (esp_restart)"; break;
|
||
case ESP_RST_PANIC: resetReason = "PANIC/EXCEPTION"; break;
|
||
case ESP_RST_INT_WDT: resetReason = "WATCHDOG (intern)"; break;
|
||
case ESP_RST_TASK_WDT: resetReason = "WATCHDOG (Task)"; break;
|
||
case ESP_RST_WDT: resetReason = "WATCHDOG"; break;
|
||
case ESP_RST_DEEPSLEEP: resetReason = "DEEP_SLEEP"; break;
|
||
case ESP_RST_BROWNOUT: resetReason = "BROWNOUT (Spannung zu niedrig!)"; break;
|
||
case ESP_RST_SDIO: resetReason = "SDIO"; break;
|
||
default: break;
|
||
}
|
||
|
||
// --- 1. Einstellungen laden (NVS) ---
|
||
settings.begin();
|
||
LOG_I("MAIN", "=== Neustart === Grund: %s", resetReason);
|
||
LOG_I("MAIN", "LaserCutter Display gestartet");
|
||
LOG_I("MAIN", "Laser GPIO: %d, Display CS: %d", LASER_SIGNAL_PIN, DISPLAY_CS_PIN);
|
||
settings.printToSerial();
|
||
|
||
// --- 2. Display initialisieren (sofort betriebsbereit) ---
|
||
display.begin();
|
||
display.printToSerial();
|
||
display.showWifiError(true); // W an bis WiFi-Status bekannt
|
||
display.showMqttError(false);
|
||
display.showLaserTime(0.0f);
|
||
display.showIdle();
|
||
|
||
// --- 3. LaserTracker starten (PRIORITAET 1 - muss sofort laufen) ---
|
||
laserTracker.begin();
|
||
laserTracker.printToSerial();
|
||
|
||
// --- 4. WiFi non-blocking starten ---
|
||
// begin() kehrt SOFORT zurueck (kein Blockieren mehr!)
|
||
// MQTT + WebServer starten spaeter (lazy, in loop())
|
||
wifiConnector.begin();
|
||
|
||
// --- 5. Watchdog ---
|
||
esp_task_wdt_init(WDT_TIMEOUT_S, true);
|
||
esp_task_wdt_add(NULL);
|
||
LOG_I("MAIN", "Setup abgeschlossen. Watchdog aktiv (%d s)", WDT_TIMEOUT_S);
|
||
}
|
||
|
||
void loop() {
|
||
// Watchdog zuruecksetzen
|
||
esp_task_wdt_reset();
|
||
|
||
// ==========================================================================
|
||
// PRIORITAET 1: LaserTracker (immer zuerst, niemals ueberspringen)
|
||
// ==========================================================================
|
||
laserTracker.loop();
|
||
|
||
// ==========================================================================
|
||
// PRIORITAET 2: WiFi (non-blocking, kein delay/while)
|
||
// ==========================================================================
|
||
wifiConnector.loop();
|
||
|
||
// ==========================================================================
|
||
// Lazy-Init: MQTT + WebServer beim ersten WiFi-Connect
|
||
// ==========================================================================
|
||
if (wifiConnector.isConnected()) {
|
||
if (!mqttStarted) {
|
||
mqttClient.begin();
|
||
mqttClient.printToSerial();
|
||
mqttStarted = true;
|
||
}
|
||
if (!webStarted) {
|
||
LOG_I("MAIN", "WebServer wird gestartet...");
|
||
webServer.begin();
|
||
webStarted = true;
|
||
}
|
||
}
|
||
|
||
// ==========================================================================
|
||
// MQTT (nur wenn gestartet)
|
||
// ==========================================================================
|
||
if (mqttStarted) {
|
||
mqttClient.loop();
|
||
|
||
// Session-Publish (nur wenn Netto-Zeit vorhanden, kein GRATIS-only)
|
||
if (laserTracker.consumeSessionEnd()) {
|
||
int lastSession = laserTracker.getLastSessionSeconds();
|
||
if (lastSession > 0) {
|
||
mqttClient.publishSession(lastSession, settings.get().gratisSeconds);
|
||
}
|
||
}
|
||
|
||
// Session-Reset-Publish: akkumulierte Sekunden vor dem Reset
|
||
if (laserTracker.consumeSessionReset()) {
|
||
int sec = laserTracker.getLastSessionSeconds();
|
||
if (sec > 0) {
|
||
mqttClient.publishSession(sec, settings.get().gratisSeconds);
|
||
}
|
||
}
|
||
}
|
||
|
||
// WebServer-Loop: ArduinoOTA verarbeiten
|
||
if (webStarted) {
|
||
webServer.loop();
|
||
}
|
||
|
||
// ==========================================================================
|
||
// Display: rate-limited (Modul-Updates nur bei Bedarf)
|
||
// ==========================================================================
|
||
|
||
// --- Modul 0: WiFi-Fehler (blinkt 500ms wenn getrennt) ---
|
||
{
|
||
static uint32_t wifiBlinkMs = 0;
|
||
static bool wifiBlinkOn = false;
|
||
bool wifiErr = !wifiConnector.isConnected();
|
||
if (wifiErr) {
|
||
if (millis() - wifiBlinkMs >= 500) {
|
||
wifiBlinkMs = millis();
|
||
wifiBlinkOn = !wifiBlinkOn;
|
||
display.showWifiError(wifiBlinkOn);
|
||
}
|
||
} else {
|
||
if (wifiBlinkOn) {
|
||
wifiBlinkOn = false;
|
||
display.showWifiError(false);
|
||
}
|
||
}
|
||
}
|
||
|
||
// --- Module 1-3: Minutenanzeige (Update bei Wertwechsel oder alle 60 s) ---
|
||
{
|
||
static uint32_t lastMinUpdate = 0;
|
||
static int lastMinValue = -1;
|
||
int curMin = laserTracker.getAllSessionsSumMinutes();
|
||
if (curMin != lastMinValue || (millis() - lastMinUpdate) >= 60000UL) {
|
||
display.showLaserTime((float)curMin);
|
||
lastMinValue = curMin;
|
||
lastMinUpdate = millis();
|
||
}
|
||
}
|
||
|
||
// --- Modul 4: MQTT-Fehler (nur bei Statuswechsel) ---
|
||
{
|
||
static bool lastMqttErr = false;
|
||
bool mqttErr = mqttStarted ? !mqttClient.isConnected() : false;
|
||
if (mqttErr != lastMqttErr) {
|
||
display.showMqttError(mqttErr);
|
||
lastMqttErr = mqttErr;
|
||
}
|
||
}
|
||
|
||
// --- Module 5-7: Countdown/Ring/Idle (Update alle 1 s) ---
|
||
{
|
||
static uint32_t lastSecUpdate = 0;
|
||
if (millis() - lastSecUpdate >= 1000UL) {
|
||
lastSecUpdate = millis();
|
||
int countdown = laserTracker.getCountdownRemaining();
|
||
if (countdown > 0) {
|
||
display.showCountdown(countdown);
|
||
} else if (laserTracker.isActive()) {
|
||
display.showSessionRing(laserTracker.getRunningSessionSeconds() % 60);
|
||
} else {
|
||
display.showIdle();
|
||
}
|
||
}
|
||
}
|
||
|
||
display.update();
|
||
|
||
// Heap-Monitor: alle 30 s loggen
|
||
static uint32_t lastHeapLog = 0;
|
||
if (millis() - lastHeapLog >= 30000) {
|
||
lastHeapLog = millis();
|
||
LOG_D("MAIN", "FreeHeap: %u Bytes", ESP.getFreeHeap());
|
||
}
|
||
|
||
delay(20); // 50Hz loop, kurz genug fuer fluessiges Blinken
|
||
}
|