- esp_reset_reason() einmalig in begin() gespeichert (_resetReason[32]) - reset_reason-Feld in publishHeartbeat() JSON-Payload - Moegliche Werte: POWERON, SOFTWARE, PANIC, TASK_WDT, INT_WDT, WDT, BROWNOUT, EXT_PIN, DEEPSLEEP, SDIO, UNKNOWN - README.md: Status-JSON-Beispiel und Werteliste dokumentiert
113 lines
4.1 KiB
C++
113 lines
4.1 KiB
C++
#pragma once
|
||
|
||
// =============================================================================
|
||
// mqtt_client.h - MQTT-Verbindung, Publish und Subscribe
|
||
// Projekt: MQTT-Display LaserCutter
|
||
//
|
||
// Wrapper um PubSubClient mit Non-Blocking Reconnect.
|
||
//
|
||
// Publish:
|
||
// publishSession() - beim Ende eines Laser-Bursts (aus LaserTracker)
|
||
// publishStatus() - Heartbeat alle MQTT_HEARTBEAT_MS
|
||
//
|
||
// Subscribe:
|
||
// lasercutter/reset - Payload "1" oder {"reset":true} -> laserTracker.resetTotal()
|
||
// Payload {"reset_session":true} -> laserTracker.resetSessionSum()
|
||
//
|
||
// Verwendung:
|
||
// mqttClient.begin(); // einmalig in setup(), nach WiFi-Connect
|
||
// mqttClient.loop(); // in jedem loop()-Aufruf
|
||
// =============================================================================
|
||
|
||
#include <Arduino.h>
|
||
#include <PubSubClient.h>
|
||
#include <WiFi.h>
|
||
#include <WiFiClientSecure.h>
|
||
#include "config.h"
|
||
#include "settings.h"
|
||
#include <freertos/FreeRTOS.h>
|
||
#include <freertos/task.h>
|
||
#include <freertos/queue.h>
|
||
#include <esp_system.h>
|
||
|
||
// Session-Daten die per Queue von Core 1 -> Core 0 übergeben werden
|
||
struct SessionPayload {
|
||
int sessionSec;
|
||
int gratisSec;
|
||
time_t startTime; // exakter Startzeitpunkt – bei Reconnect korrekt, nicht "jetzt"
|
||
uint32_t sessionId; // aufsteigender Zähler, Reset bei resetSessionSum/resetTotal
|
||
};
|
||
|
||
class MqttClient {
|
||
public:
|
||
MqttClient();
|
||
|
||
// MQTT konfigurieren und erste Verbindung versuchen (einmalig in setup())
|
||
void begin();
|
||
|
||
// No-Op: Reconnect, Heartbeat und Publish werden vom internen FreeRTOS-Task erledigt.
|
||
// Diese Methode bleibt im Interface, damit main.cpp unveraendert bleibt.
|
||
void loop();
|
||
|
||
// Publish: Session-Ende (wird von LaserTracker-Logik in main aufgerufen)
|
||
// lastSessionSec : Netto-Sekunden der letzten Session
|
||
// gratisSec : konfigurierte Gratiszeit
|
||
// JSON-Felder: session_minutes (ceiling int), session_seconds (raw), freetime_s, ip
|
||
void publishSession(int lastSessionSec, int gratisSec);
|
||
|
||
// Verbindungsstatus
|
||
bool isConnected();
|
||
|
||
// Session-Zähler zurücksetzen (bei reset / reset_session Kommando)
|
||
void resetSessionCounter();
|
||
|
||
// Debug-Ausgabe auf Serial
|
||
void printToSerial();
|
||
|
||
private:
|
||
// WICHTIG: Diese Objekte werden als nullptr initialisiert und erst in
|
||
// _taskLoop() auf Core 0 per 'new' angelegt. mbedtls/WiFiClientSecure
|
||
// bindet Heap-Kontexte an den erstellenden Task/Core – daher KEIN
|
||
// Anlegen im globalen Konstruktor (Core 1)!
|
||
WiFiClient* _wifiClient;
|
||
WiFiClientSecure* _secureClient;
|
||
PubSubClient* _client;
|
||
|
||
uint32_t _lastReconnectMs;
|
||
uint32_t _lastHeartbeatMs;
|
||
char _clientId[32]; // MQTT_CLIENT_ID + MAC-Suffix (eindeutig auf Public Broker)
|
||
|
||
// FreeRTOS-Task: laeuft auf Core 0, erledigt Reconnect/Heartbeat/Publish
|
||
TaskHandle_t _taskHandle;
|
||
|
||
// Session-Queue: Core 1 schreibt per publishSession(), Core 0 liest und publiziert.
|
||
// 128 Slots = Worst Case ~2h Offline bei 1 Session/min. ~2 KB RAM.
|
||
QueueHandle_t _sessionQueue;
|
||
|
||
// Aufsteigender Session-Zähler; Reset bei resetSessionSum/resetTotal
|
||
// Zugriff nur von Core 1 (publishSession) → kein Mutex nötig
|
||
uint32_t _sessionCounter;
|
||
|
||
// Neustart-Grund (einmalig in begin() gespeichert, bevor er überschrieben wird)
|
||
char _resetReason[32];
|
||
|
||
// Verbindungsaufbau (blockierend - NUR aus MQTT-Task auf Core 0 aufrufen!)
|
||
bool reconnect();
|
||
|
||
// Heartbeat-Publish (lasercutter/status)
|
||
void publishHeartbeat();
|
||
|
||
// Eigentlicher Session-Publish (aus MQTT-Task auf Core 0)
|
||
// Gibt true zurück wenn Publish erfolgreich, false bei Fehler (Queue-Eintrag bleibt dann erhalten)
|
||
bool _doPublishSession(int lastSessionSec, int gratisSec, time_t startTime, uint32_t sessionId);;
|
||
|
||
// FreeRTOS-Task
|
||
static void _taskFn(void* param);
|
||
void _taskLoop();
|
||
|
||
// Callback fuer eingehende Nachrichten (static wegen PubSubClient-API)
|
||
static void onMessage(const char* topic, byte* payload, unsigned int length);
|
||
};
|
||
|
||
// Globale Instanz
|
||
extern MqttClient mqttClient; |