move hardbeat handling into backend, add publish lanestate for lighttower
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user