MQTT-Display-LaserCutter/src/main.cpp
MaPaLo76 4dd4ce0620 feat(FR-002): Web Console via HTTP-Polling (/log + /log-data) -- v1.1.0
- 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
2026-02-28 17:24:56 +01:00

209 lines
7.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// =============================================================================
// 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
}