diff --git a/data/index.css b/data/index.css index e919f20..bb1197d 100644 --- a/data/index.css +++ b/data/index.css @@ -355,18 +355,24 @@ body { font-weight: 600; } -.status.ready { +.status.finished { background-color: rgba(52, 152, 219, 0.3); border: 2px solid #3498db; } -.status.running { +.status.ready { background-color: rgba(46, 204, 113, 0.3); border: 2px solid #2ecc71; 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); border: 2px solid #e74c3c; } diff --git a/data/index.html b/data/index.html index 412c94f..c3e5607 100644 --- a/data/index.html +++ b/data/index.html @@ -380,7 +380,7 @@ s1.textContent = "Beendet"; break; case "armed": - s1.textContent = "Armiert"; // Neuer Status für armiert + s1.textContent = "Armiert"; break; default: s1.textContent = "Unbekannter Status"; @@ -391,7 +391,7 @@ if (!lane2Connected) { s2.className = "status standby"; - s2.textContent = "Standby: Bitte beide 1x betätigen"; + s2.textContent = "Standby: Bitte beide Buttons 1x betätigen"; } else { s2.className = `status ${status2}`; diff --git a/src/communication.h b/src/communication.h index 5e299a0..3aba2b6 100644 --- a/src/communication.h +++ b/src/communication.h @@ -344,6 +344,11 @@ void setupMqttServer() { handleBatteryTopic(topic, payload); } else if (strncmp(topic, "heartbeat/alive/", 16) == 0) { 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); }); diff --git a/src/gamemodes.h b/src/gamemodes.h index 72af23b..1a0e60f 100644 --- a/src/gamemodes.h +++ b/src/gamemodes.h @@ -14,10 +14,6 @@ void IndividualMode(const char *action, int press, int lane, uint64_t timestamp = 0); void CompetitionMode(const char *action, int press, int lane, 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, uint64_t _timestamp) { @@ -41,6 +37,7 @@ void IndividualMode(const char *action, int press, int lane, timerData.localStartTime1 = millis(); // Set local start time timerData.isRunning1 = true; timerData.endTime1 = 0; + timerData.isArmed1 = false; // Reset armed status publishLaneStatus(1, "running"); 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.isRunning2 = true; timerData.endTime2 = 0; + timerData.isArmed2 = false; // Reset armed status publishLaneStatus(2, "running"); Serial.println("Bahn 2 gestartet"); } @@ -137,26 +135,11 @@ void CompetitionMode(const char *action, int press, int lane, } if (timerData.isArmed1 && timerData.isArmed2) { - 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"); + sendMQTTMessage("aquacross/competition/toSignal", "armed"); } + + if ((action == "stop" && press == 1 && lane == 1)) { if (timerData.isRunning1) { 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() { 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; } \ No newline at end of file diff --git a/src/master.cpp b/src/master.cpp index bc784f7..8a5f7d9 100644 --- a/src/master.cpp +++ b/src/master.cpp @@ -107,56 +107,7 @@ int checkLicence() { 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() { Serial.begin(115200); @@ -180,6 +131,7 @@ void setup() { loadWifiSettings(); loadLocationSettings(); + setupWifi(); // WiFi initialisieren setupOTA(&server); diff --git a/src/master.h b/src/master.h index 27fce53..01ac3f1 100644 --- a/src/master.h +++ b/src/master.h @@ -58,7 +58,8 @@ unsigned long maxTimeBeforeReset = 300000; // 5 Minuten default unsigned long maxTimeDisplay = 20000; // 20 Sekunden Standard (in ms) bool wifimodeAP = false; // AP-Modus deaktiviert 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 void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len); diff --git a/src/webserverrouter.h b/src/webserverrouter.h index b1621a6..526a675 100644 --- a/src/webserverrouter.h +++ b/src/webserverrouter.h @@ -300,6 +300,7 @@ void setupRoutes() { String result; serializeJson(doc, result); request->send(200, "application/json", result); + saveSettings(); } else { request->send(400, "application/json", "{\"success\":false,\"error\":\"Modus fehlt\"}"); @@ -314,16 +315,6 @@ void setupRoutes() { 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 server.serveStatic("/", SPIFFS, "/"); server.begin();