Bug fixed, Wettkampfmodus Done. TODO: Zeitstempel der Statusampel im Master verwenden

This commit is contained in:
Carsten Graf
2025-08-06 22:47:31 +02:00
parent 4a04565878
commit ba1b86a053
7 changed files with 102 additions and 87 deletions

View File

@@ -355,18 +355,24 @@ body {
font-weight: 600; font-weight: 600;
} }
.status.ready { .status.finished {
background-color: rgba(52, 152, 219, 0.3); background-color: rgba(52, 152, 219, 0.3);
border: 2px solid #3498db; border: 2px solid #3498db;
} }
.status.running { .status.ready {
background-color: rgba(46, 204, 113, 0.3); background-color: rgba(46, 204, 113, 0.3);
border: 2px solid #2ecc71; border: 2px solid #2ecc71;
animation: pulse 1s infinite; animation: pulse 1s infinite;
} }
.status.finished { .status.armed {
background-color: rgb(197, 194, 0);
border: 2px solid #fbff00;
animation: pulse 1s infinite;
}
.status.running {
background-color: rgba(231, 76, 60, 0.3); background-color: rgba(231, 76, 60, 0.3);
border: 2px solid #e74c3c; border: 2px solid #e74c3c;
} }

View File

@@ -380,7 +380,7 @@
s1.textContent = "Beendet"; s1.textContent = "Beendet";
break; break;
case "armed": case "armed":
s1.textContent = "Armiert"; // Neuer Status für armiert s1.textContent = "Armiert";
break; break;
default: default:
s1.textContent = "Unbekannter Status"; s1.textContent = "Unbekannter Status";
@@ -391,7 +391,7 @@
if (!lane2Connected) { if (!lane2Connected) {
s2.className = "status standby"; s2.className = "status standby";
s2.textContent = "Standby: Bitte beide 1x betätigen"; s2.textContent = "Standby: Bitte beide Buttons 1x betätigen";
} else { } else {
s2.className = `status ${status2}`; s2.className = `status ${status2}`;

View File

@@ -344,6 +344,11 @@ void setupMqttServer() {
handleBatteryTopic(topic, payload); handleBatteryTopic(topic, payload);
} else if (strncmp(topic, "heartbeat/alive/", 16) == 0) { } else if (strncmp(topic, "heartbeat/alive/", 16) == 0) {
handleHeartbeatTopic(topic, payload); handleHeartbeatTopic(topic, payload);
} else if (strncmp(topic, "aquacross/competition/toMaster", 30) == 0) {
// Handle competition lane messages
// payload ist sendMQTTMessage("aquacross/competition/toMaster", "start");
startCompetition = (payload != nullptr && strcmp(payload, "start") == 0);
runCompetition();
} }
updateStatusLED(3); updateStatusLED(3);
}); });

View File

@@ -14,10 +14,6 @@ void IndividualMode(const char *action, int press, int lane,
uint64_t timestamp = 0); uint64_t timestamp = 0);
void CompetitionMode(const char *action, int press, int lane, void CompetitionMode(const char *action, int press, int lane,
uint64_t timestamp = 0); uint64_t timestamp = 0);
void handleStart1(uint64_t timestamp);
void handleStop1(uint64_t timestamp);
void handleStart2(uint64_t timestamp);
void handleStop2(uint64_t timestamp);
void triggerAction(const char *action, int press, int lane, void triggerAction(const char *action, int press, int lane,
uint64_t _timestamp) { uint64_t _timestamp) {
@@ -41,6 +37,7 @@ void IndividualMode(const char *action, int press, int lane,
timerData.localStartTime1 = millis(); // Set local start time timerData.localStartTime1 = millis(); // Set local start time
timerData.isRunning1 = true; timerData.isRunning1 = true;
timerData.endTime1 = 0; timerData.endTime1 = 0;
timerData.isArmed1 = false; // Reset armed status
publishLaneStatus(1, "running"); publishLaneStatus(1, "running");
Serial.println("Bahn 1 gestartet"); Serial.println("Bahn 1 gestartet");
} }
@@ -68,6 +65,7 @@ void IndividualMode(const char *action, int press, int lane,
timerData.localStartTime2 = millis(); // Set local start time timerData.localStartTime2 = millis(); // Set local start time
timerData.isRunning2 = true; timerData.isRunning2 = true;
timerData.endTime2 = 0; timerData.endTime2 = 0;
timerData.isArmed2 = false; // Reset armed status
publishLaneStatus(2, "running"); publishLaneStatus(2, "running");
Serial.println("Bahn 2 gestartet"); Serial.println("Bahn 2 gestartet");
} }
@@ -137,26 +135,11 @@ void CompetitionMode(const char *action, int press, int lane,
} }
if (timerData.isArmed1 && timerData.isArmed2) { if (timerData.isArmed1 && timerData.isArmed2) {
timerData.isReady1 = false; sendMQTTMessage("aquacross/competition/toSignal", "armed");
uint64_t startNow = getCurrentTimestampMs();
timerData.startTime1 = startNow;
timerData.localStartTime1 = millis(); // Set local start time
timerData.isRunning1 = true;
timerData.endTime1 = 0; // Reset end time for Bahn 1
timerData.isArmed1 = false; // Reset Bahn 1 armed status
publishLaneStatus(1, "running");
Serial.println("Bahn 1 gestartet");
timerData.isReady2 = false;
timerData.startTime2 = startNow;
timerData.localStartTime2 = millis(); // Set local start time
timerData.isRunning2 = true;
timerData.endTime2 = 0; // Reset end time for Bahn 2
timerData.isArmed2 = false; // Reset Bahn 2 armed status
publishLaneStatus(2, "running");
Serial.println("Bahn 2 gestartet");
} }
if ((action == "stop" && press == 1 && lane == 1)) { if ((action == "stop" && press == 1 && lane == 1)) {
if (timerData.isRunning1) { if (timerData.isRunning1) {
timerData.endTime1 = (timestamp > 0) ? timestamp : millis(); timerData.endTime1 = (timestamp > 0) ? timestamp : millis();
@@ -191,6 +174,32 @@ void CompetitionMode(const char *action, int press, int lane,
} }
} }
void runCompetition() {
if (timerData.isArmed1 && timerData.isArmed2 && startCompetition) {
timerData.isReady1 = false;
uint64_t startNow = getCurrentTimestampMs();
timerData.startTime1 = startNow;
timerData.localStartTime1 = millis(); // Set local start time
timerData.isRunning1 = true;
timerData.endTime1 = 0; // Reset end time for Bahn 1
timerData.isArmed1 = false; // Reset Bahn 1 armed status
publishLaneStatus(1, "running");
Serial.println("Bahn 1 gestartet");
timerData.isReady2 = false;
timerData.startTime2 = startNow;
timerData.localStartTime2 = millis(); // Set local start time
timerData.isRunning2 = true;
timerData.endTime2 = 0; // Reset end time for Bahn 2
timerData.isArmed2 = false; // Reset Bahn 2 armed status
publishLaneStatus(2, "running");
Serial.println("Bahn 2 gestartet");
}
else {
Serial.println("Bahn 1 und Bahn 2 müssen armiert sein, um den Wettkampf zu starten.");
}
}
void checkAutoReset() { void checkAutoReset() {
unsigned long currentTime = millis(); unsigned long currentTime = millis();
@@ -272,4 +281,55 @@ void checkAutoReset() {
} }
} }
} }
}
String getTimerDataJSON() {
DynamicJsonDocument doc(1024);
unsigned long currentTime = millis();
// Bahn 1
if (timerData.isRunning1) {
doc["time1"] = (currentTime - timerData.localStartTime1) / 1000.0;
doc["status1"] = "running";
} else if (timerData.endTime1 > 0) {
doc["time1"] = (timerData.endTime1 - timerData.startTime1) / 1000.0;
doc["status1"] = "finished";
} else if (timerData.isArmed1) {
doc["time1"] = 0;
doc["status1"] = "armed"; // Status für Bahn 1, wenn sie armiert ist
} else {
doc["time1"] = 0;
doc["status1"] = "ready";
}
// Bahn 2
if (timerData.isRunning2) {
doc["time2"] = (currentTime - timerData.localStartTime2) / 1000.0;
doc["status2"] = "running";
} else if (timerData.endTime2 > 0) {
doc["time2"] = (timerData.endTime2 - timerData.startTime2) / 1000.0;
doc["status2"] = "finished";
} else if (timerData.isArmed2) {
doc["time2"] = 0;
doc["status2"] = "armed"; // Status für Bahn 2, wenn sie armiert ist
} else {
doc["time2"] = 0;
doc["status2"] = "ready";
}
// Beste Zeiten
doc["best1"] = timerData.bestTime1 / 1000.0;
doc["best2"] = timerData.bestTime2 / 1000.0;
// Lernmodus
doc["learningMode"] = learningMode;
if (learningMode) {
String buttons[] = {"Start Bahn 1", "Stop Bahn 1", "Start Bahn 2",
"Stop Bahn 2"};
doc["learningButton"] = buttons[learningStep];
}
String result;
serializeJson(doc, result);
return result;
} }

View File

@@ -107,56 +107,7 @@ int checkLicence() {
return tier; return tier;
} }
String getTimerDataJSON() {
DynamicJsonDocument doc(1024);
unsigned long currentTime = millis();
// Bahn 1
if (timerData.isRunning1) {
doc["time1"] = (currentTime - timerData.localStartTime1) / 1000.0;
doc["status1"] = "running";
} else if (timerData.endTime1 > 0) {
doc["time1"] = (timerData.endTime1 - timerData.startTime1) / 1000.0;
doc["status1"] = "finished";
} else if (timerData.isArmed1) {
doc["time1"] = 0;
doc["status1"] = "armed"; // Status für Bahn 1, wenn sie armiert ist
} else {
doc["time1"] = 0;
doc["status1"] = "ready";
}
// Bahn 2
if (timerData.isRunning2) {
doc["time2"] = (currentTime - timerData.localStartTime2) / 1000.0;
doc["status2"] = "running";
} else if (timerData.endTime2 > 0) {
doc["time2"] = (timerData.endTime2 - timerData.startTime2) / 1000.0;
doc["status2"] = "finished";
} else if (timerData.isArmed2) {
doc["time2"] = 0;
doc["status2"] = "armed"; // Status für Bahn 2, wenn sie armiert ist
} else {
doc["time2"] = 0;
doc["status2"] = "ready";
}
// Beste Zeiten
doc["best1"] = timerData.bestTime1 / 1000.0;
doc["best2"] = timerData.bestTime2 / 1000.0;
// Lernmodus
doc["learningMode"] = learningMode;
if (learningMode) {
String buttons[] = {"Start Bahn 1", "Stop Bahn 1", "Start Bahn 2",
"Stop Bahn 2"};
doc["learningButton"] = buttons[learningStep];
}
String result;
serializeJson(doc, result);
return result;
}
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
@@ -180,6 +131,7 @@ void setup() {
loadWifiSettings(); loadWifiSettings();
loadLocationSettings(); loadLocationSettings();
setupWifi(); // WiFi initialisieren setupWifi(); // WiFi initialisieren
setupOTA(&server); setupOTA(&server);

View File

@@ -58,7 +58,8 @@ unsigned long maxTimeBeforeReset = 300000; // 5 Minuten default
unsigned long maxTimeDisplay = 20000; // 20 Sekunden Standard (in ms) unsigned long maxTimeDisplay = 20000; // 20 Sekunden Standard (in ms)
bool wifimodeAP = false; // AP-Modus deaktiviert bool wifimodeAP = false; // AP-Modus deaktiviert
String masterlocation; String masterlocation;
int gamemode; // 0=Individual, 1=Wettkampf int gamemode; // 0=Individual, 1=Wettkampf
bool startCompetition = false; // Flag, ob der Timer gestartet wurde
// Function Declarations // Function Declarations
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len); void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len);

View File

@@ -300,6 +300,7 @@ void setupRoutes() {
String result; String result;
serializeJson(doc, result); serializeJson(doc, result);
request->send(200, "application/json", result); request->send(200, "application/json", result);
saveSettings();
} else { } else {
request->send(400, "application/json", request->send(400, "application/json",
"{\"success\":false,\"error\":\"Modus fehlt\"}"); "{\"success\":false,\"error\":\"Modus fehlt\"}");
@@ -314,16 +315,6 @@ void setupRoutes() {
request->send(200, "application/json", result); request->send(200, "application/json", result);
}); });
server.on("/api/get-mode", HTTP_GET, [](AsyncWebServerRequest *request) {
DynamicJsonDocument doc(32);
doc["mode"] = gamemode == 0 ? "individual" : "wettkampf";
String result;
serializeJson(doc, result);
request->send(200, "application/json", result);
});
// ...existing code...
// Statische Dateien // Statische Dateien
server.serveStatic("/", SPIFFS, "/"); server.serveStatic("/", SPIFFS, "/");
server.begin(); server.begin();