move hardbeat handling into backend, add publish lanestate for lighttower

This commit is contained in:
Carsten Graf
2025-07-31 01:15:44 +02:00
parent 4a1e0b8bca
commit 0166e1a695
7 changed files with 123 additions and 20 deletions

View File

@@ -109,7 +109,6 @@ void readButtonJSON(const char *topic, const char *payload) {
* extrahiert MAC und Timestamp und sendet ein JSON an das Frontend.
*/
void handleHeartbeatTopic(const char *topic, const char *payload) {
// Topic-Format: heartbeat/alive/CC:DB:A7:2F:95:08
String topicStr(topic);
int lastSlash = topicStr.lastIndexOf('/');
if (lastSlash < 0)
@@ -119,17 +118,22 @@ void handleHeartbeatTopic(const char *topic, const char *payload) {
auto macBytes = macStringToBytes(macStr.c_str());
String buttonType = "unknown";
ButtonConfig *btn = nullptr;
if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0) {
buttonType = "start1";
btn = &buttonConfigs.start1;
} else if (memcmp(macBytes.data(), buttonConfigs.stop1.mac, 6) == 0) {
buttonType = "stop1";
btn = &buttonConfigs.stop1;
} else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0) {
buttonType = "start2";
btn = &buttonConfigs.start2;
} else if (memcmp(macBytes.data(), buttonConfigs.stop2.mac, 6) == 0) {
buttonType = "stop2";
btn = &buttonConfigs.stop2;
}
// Parse payload for timestamp (optional, falls im Payload enthalten)
// Parse payload for timestamp (optional)
uint64_t timestamp = millis();
StaticJsonDocument<128> payloadDoc;
if (payload && strlen(payload) > 0 &&
@@ -139,17 +143,22 @@ void handleHeartbeatTopic(const char *topic, const char *payload) {
}
}
// Update heartbeat info
if (btn) {
btn->lastHeartbeat = millis();
btn->heartbeatActive = true;
}
// JSON bauen
StaticJsonDocument<128> doc;
doc["button"] = buttonType;
doc["mac"] = macStr;
doc["timestamp"] = timestamp;
doc["active"] = true; // always true on heartbeat
String json;
serializeJson(doc, json);
pushUpdateToFrontend(
json); // Diese Funktion schickt das JSON an alle WebSocket-Clients
// Serial.printf("Published heartbeat JSON: %s\n", json.c_str());
pushUpdateToFrontend(json);
}
/**
@@ -213,7 +222,21 @@ void handleBatteryTopic(const char *topic, const char *payload) {
pushUpdateToFrontend(
json); // Diese Funktion schickt das JSON an alle WebSocket-Clients
// Serial.printf("Battery level for %s (%s): %d%%\n", buttonType.c_str(),macStr.c_str(), batteryLevelP);
// Serial.printf("Battery level for %s (%s): %d%%\n",
// buttonType.c_str(),macStr.c_str(), batteryLevelP);
}
void publishLaneStatus(int lane, String status) {
JsonDocument messageDoc;
messageDoc["lane"] = lane;
messageDoc["status"] = status;
String message;
serializeJson(messageDoc, message);
// Topic dynamisch nach Bahn wählen
String topic = "aquacross/lanes/lane" + String(lane);
mqtt.publish(topic.c_str(), message);
}
/**
@@ -366,3 +389,32 @@ void sendMQTTJSONMessage(const char *topic, const JsonDocument &doc) {
Serial.printf("Published JSON message to topic '%s': %s\n", topic,
jsonString.c_str());
}
void checkHeartbeatTimeouts(unsigned long timeoutMs = 10000) {
struct ButtonRef {
ButtonConfig &btn;
const char *type;
};
ButtonRef buttons[] = {{buttonConfigs.start1, "start1"},
{buttonConfigs.stop1, "stop1"},
{buttonConfigs.start2, "start2"},
{buttonConfigs.stop2, "stop2"}};
unsigned long now = millis();
for (auto &b : buttons) {
if (b.btn.isAssigned && b.btn.heartbeatActive &&
now - b.btn.lastHeartbeat > timeoutMs) {
b.btn.heartbeatActive = false;
// Send inactive status to frontend
StaticJsonDocument<128> doc;
doc["button"] = b.type;
doc["mac"] = b.btn.mac;
doc["active"] = false;
String json;
serializeJson(doc, json);
pushUpdateToFrontend(json);
}
}
}

View File

@@ -7,4 +7,11 @@ std::array<uint8_t, 6> macStringToBytes(const char *macStr) {
sscanf(macStr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &bytes[0], &bytes[1],
&bytes[2], &bytes[3], &bytes[4], &bytes[5]);
return bytes;
}
std::string macToString(const std::array<uint8_t, 6> &macBytes) {
char macStr[18];
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", macBytes[0],
macBytes[1], macBytes[2], macBytes[3], macBytes[4], macBytes[5]);
return std::string(macStr);
}

View File

@@ -31,6 +31,7 @@ void handleStart1(uint64_t timestamp = 0) {
timerData.localStartTime1 = millis(); // Set local start time
timerData.isRunning1 = true;
timerData.endTime1 = 0;
publishLaneStatus(1, "running");
Serial.println("Bahn 1 gestartet");
}
}
@@ -46,6 +47,7 @@ void handleStop1(uint64_t timestamp = 0) {
timerData.bestTime1 = currentTime;
saveBestTimes();
}
publishLaneStatus(1, "stopped");
Serial.println("Bahn 1 gestoppt - Zeit: " + String(currentTime / 1000.0) +
"s");
}
@@ -58,6 +60,7 @@ void handleStart2(uint64_t timestamp = 0) {
timerData.localStartTime2 = millis(); // Set local start time
timerData.isRunning2 = true;
timerData.endTime2 = 0;
publishLaneStatus(2, "running");
Serial.println("Bahn 2 gestartet");
}
}
@@ -73,6 +76,7 @@ void handleStop2(uint64_t timestamp = 0) {
timerData.bestTime2 = currentTime;
saveBestTimes();
}
publishLaneStatus(2, "stopped");
Serial.println("Bahn 2 gestoppt - Zeit: " + String(currentTime / 1000.0) +
"s");
}
@@ -85,6 +89,7 @@ void checkAutoReset() {
(currentTime - timerData.localStartTime1 > maxTimeBeforeReset)) {
timerData.isRunning1 = false;
timerData.startTime1 = 0;
publishLaneStatus(1, "ready");
Serial.println("Bahn 1 automatisch zurückgesetzt");
}
@@ -92,6 +97,7 @@ void checkAutoReset() {
(currentTime - timerData.localStartTime2 > maxTimeBeforeReset)) {
timerData.isRunning2 = false;
timerData.startTime2 = 0;
publishLaneStatus(2, "ready");
Serial.println("Bahn 2 automatisch zurückgesetzt");
}
@@ -114,6 +120,7 @@ void checkAutoReset() {
// Push the message to the frontend
pushUpdateToFrontend(message);
publishLaneStatus(1, "ready");
Serial.println("Bahn 1 automatisch auf 'Bereit' zurückgesetzt");
}
@@ -137,6 +144,7 @@ void checkAutoReset() {
// Push the message to the frontend
pushUpdateToFrontend(message);
publishLaneStatus(2, "ready");
Serial.println("Bahn 2 automatisch auf 'Bereit' zurückgesetzt");
}

View File

@@ -34,6 +34,8 @@ struct ButtonConfig {
uint8_t mac[6];
bool isAssigned = false;
float voltage = 0.0;
unsigned long lastHeartbeat = 0; // Zeit des letzten Heartbeats (millis)
bool heartbeatActive = false; // Status: aktiv/inaktiv
};
struct ButtonConfigs {