diff --git a/src/buttonassigh.h b/src/buttonassigh.h index 829c387..0041be5 100644 --- a/src/buttonassigh.h +++ b/src/buttonassigh.h @@ -1,54 +1,57 @@ #pragma once -#include #include "master.h" +#include + // Aquacross Timer - ESP32 Master (Webserver + ESP-NOW + Anlernmodus) #include - - -void handleLearningMode(const uint8_t* mac) { +void handleLearningMode(const uint8_t *mac) { // Prüfen ob MAC bereits einem anderen Button zugewiesen ist - if (buttonConfigs.start1.isAssigned && memcmp(buttonConfigs.start1.mac, mac, 6) == 0) { + if (buttonConfigs.start1.isAssigned && + memcmp(buttonConfigs.start1.mac, mac, 6) == 0) { Serial.println("Diese MAC ist bereits zugewiesen - wird ignoriert"); return; } - if (buttonConfigs.stop1.isAssigned && memcmp(buttonConfigs.stop1.mac, mac, 6) == 0) { + if (buttonConfigs.stop1.isAssigned && + memcmp(buttonConfigs.stop1.mac, mac, 6) == 0) { Serial.println("Diese MAC ist bereits zugewiesen - wird ignoriert"); return; } - if (buttonConfigs.start2.isAssigned && memcmp(buttonConfigs.start2.mac, mac, 6) == 0) { + if (buttonConfigs.start2.isAssigned && + memcmp(buttonConfigs.start2.mac, mac, 6) == 0) { Serial.println("Diese MAC ist bereits zugewiesen - wird ignoriert"); return; } - if (buttonConfigs.stop2.isAssigned && memcmp(buttonConfigs.stop2.mac, mac, 6) == 0) { + if (buttonConfigs.stop2.isAssigned && + memcmp(buttonConfigs.stop2.mac, mac, 6) == 0) { Serial.println("Diese MAC ist bereits zugewiesen - wird ignoriert"); return; } // MAC ist noch nicht zugewiesen, normal fortfahren - switch(learningStep) { - case 0: // Start1 - memcpy(buttonConfigs.start1.mac, mac, 6); - buttonConfigs.start1.isAssigned = true; - Serial.println("Start1 Button zugewiesen"); - break; - case 1: // Stop1 - memcpy(buttonConfigs.stop1.mac, mac, 6); - buttonConfigs.stop1.isAssigned = true; - Serial.println("Stop1 Button zugewiesen"); - break; - case 2: // Start2 - memcpy(buttonConfigs.start2.mac, mac, 6); - buttonConfigs.start2.isAssigned = true; - Serial.println("Start2 Button zugewiesen"); - break; - case 3: // Stop2 - memcpy(buttonConfigs.stop2.mac, mac, 6); - buttonConfigs.stop2.isAssigned = true; - Serial.println("Stop2 Button zugewiesen"); - break; + switch (learningStep) { + case 0: // Start1 + memcpy(buttonConfigs.start1.mac, mac, 6); + buttonConfigs.start1.isAssigned = true; + Serial.println("Start1 Button zugewiesen"); + break; + case 1: // Stop1 + memcpy(buttonConfigs.stop1.mac, mac, 6); + buttonConfigs.stop1.isAssigned = true; + Serial.println("Stop1 Button zugewiesen"); + break; + case 2: // Start2 + memcpy(buttonConfigs.start2.mac, mac, 6); + buttonConfigs.start2.isAssigned = true; + Serial.println("Start2 Button zugewiesen"); + break; + case 3: // Stop2 + memcpy(buttonConfigs.stop2.mac, mac, 6); + buttonConfigs.stop2.isAssigned = true; + Serial.println("Stop2 Button zugewiesen"); + break; } - + learningStep++; if (learningStep >= 4) { learningMode = false; @@ -59,41 +62,46 @@ void handleLearningMode(const uint8_t* mac) { } void handleStartLearning() { - learningMode = true; - - // Count assigned buttons and set appropriate learning step - int assignedButtons = 0; - if (buttonConfigs.start1.isAssigned) assignedButtons++; - if (buttonConfigs.stop1.isAssigned) assignedButtons++; - if (buttonConfigs.start2.isAssigned) assignedButtons++; - if (buttonConfigs.stop2.isAssigned) assignedButtons++; - - learningStep = assignedButtons; - - Serial.printf("Learning mode started - %d buttons already assigned, continuing at step %d\n", - assignedButtons, learningStep); + learningMode = true; + + // Count assigned buttons and set appropriate learning step + int assignedButtons = 0; + if (buttonConfigs.start1.isAssigned) + assignedButtons++; + if (buttonConfigs.stop1.isAssigned) + assignedButtons++; + if (buttonConfigs.start2.isAssigned) + assignedButtons++; + if (buttonConfigs.stop2.isAssigned) + assignedButtons++; + + learningStep = assignedButtons; + + Serial.printf("Learning mode started - %d buttons already assigned, " + "continuing at step %d\n", + assignedButtons, learningStep); } void handleLearningStatus() { DynamicJsonDocument doc(256); doc["active"] = learningMode; doc["step"] = learningStep; - + String response; serializeJson(doc, response); } void unlearnButton() { - - memset(buttonConfigs.start1.mac, 0, 6); - buttonConfigs.start1.isAssigned = false; - memset(buttonConfigs.stop1.mac, 0, 6); - buttonConfigs.stop1.isAssigned = false; - memset(buttonConfigs.start2.mac, 0, 6); - buttonConfigs.start2.isAssigned = false; - memset(buttonConfigs.stop2.mac, 0, 6); - buttonConfigs.stop2.isAssigned = false; - - saveButtonConfig(); - Serial.println("Buttons wurden verlernt."); + + memset(buttonConfigs.start1.mac, 0, 6); + buttonConfigs.start1.isAssigned = false; + memset(buttonConfigs.stop1.mac, 0, 6); + buttonConfigs.stop1.isAssigned = false; + memset(buttonConfigs.start2.mac, 0, 6); + buttonConfigs.start2.isAssigned = false; + memset(buttonConfigs.stop2.mac, 0, 6); + buttonConfigs.stop2.isAssigned = false; + + saveButtonConfig(); + Serial.println("Buttons wurden verlernt."); } \ No newline at end of file diff --git a/src/communication.h b/src/communication.h index ac82f61..b507104 100644 --- a/src/communication.h +++ b/src/communication.h @@ -1,30 +1,30 @@ #pragma once -#include #include "master.h" +#include #include + #include +#include "buttonassigh.h" +#include "databasebackend.h" +#include "debug.h" +#include "helper.h" #include "statusled.h" #include "timesync.h" -#include "buttonassigh.h" -#include "helper.h" -#include "debug.h" -#include -#include "databasebackend.h" #include "webserverrouter.h" +#include + struct TimestampData { - uint64_t lastMessageTimestamp; // Timestamp from the device - uint64_t lastLocalTimestamp; // Our local timestamp when message was received - uint64_t drift; // Calculated drift + uint64_t lastMessageTimestamp; // Timestamp from the device + uint64_t lastLocalTimestamp; // Our local timestamp when message was received + uint64_t drift; // Calculated drift }; // Map to store timestamp data for each MAC address std::map deviceTimestamps; - - // Datenstruktur für ESP-NOW Nachrichten // Datenstruktur für ESP-NOW Nachrichten typedef struct { @@ -37,293 +37,292 @@ typedef struct { PicoMQTT::Server mqtt; -void readButtonJSON(const char * topic, const char * payload) { +void readButtonJSON(const char *topic, const char *payload) { - const char* prefix = "aquacross/button/"; - size_t prefixLen = strlen(prefix); - if (strncmp(topic, prefix, prefixLen) != 0) { - return; - } - - // MAC aus dem Topic extrahieren - String buttonId = String(topic + prefixLen); + const char *prefix = "aquacross/button/"; + size_t prefixLen = strlen(prefix); + if (strncmp(topic, prefix, prefixLen) != 0) { + return; + } - // Create a JSON document to parse the incoming message - JsonDocument doc; - DeserializationError error = deserializeJson(doc, payload); + // MAC aus dem Topic extrahieren + String buttonId = String(topic + prefixLen); - if (error) { - Serial.print("JSON parsing failed: "); - Serial.println(error.c_str()); - return; - } + // Create a JSON document to parse the incoming message + JsonDocument doc; + DeserializationError error = deserializeJson(doc, payload); - // Extract values from JSON - int pressType = doc["type"] | 0; - uint64_t timestamp = doc["timestamp"] | 0ULL; + if (error) { + Serial.print("JSON parsing failed: "); + Serial.println(error.c_str()); + return; + } - // Print received data - Serial.printf("Button Press Received:\n"); - Serial.printf(" Type: %d\n", pressType); - Serial.printf(" Button MAC: %s\n", buttonId.c_str()); - Serial.printf(" Timestamp: %llu\n", timestamp); + // Extract values from JSON + int pressType = doc["type"] | 0; + uint64_t timestamp = doc["timestamp"] | 0ULL; - - auto macBytes = macStringToBytes(buttonId.c_str()); + // Print received data + Serial.printf("Button Press Received:\n"); + Serial.printf(" Type: %d\n", pressType); + Serial.printf(" Button MAC: %s\n", buttonId.c_str()); + Serial.printf(" Timestamp: %llu\n", timestamp); - if (learningMode) { - handleLearningMode(macBytes.data()); - return; - } - - // Button-Zuordnung prüfen und entsprechende Aktion ausführen - if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0 && (pressType == 2)) { - handleStart1(timestamp); - } else if (memcmp(macBytes.data(), buttonConfigs.stop1.mac, 6) == 0 && (pressType == 1)) { - handleStop1(timestamp); - } else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0 && (pressType == 2)) { - handleStart2(timestamp); - } else if (memcmp(macBytes.data(), buttonConfigs.stop2.mac, 6) == 0 && (pressType == 1)) { - handleStop2(timestamp); + auto macBytes = macStringToBytes(buttonId.c_str()); + + if (learningMode) { + handleLearningMode(macBytes.data()); + return; + } + + // Button-Zuordnung prüfen und entsprechende Aktion ausführen + if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0 && + (pressType == 2)) { + handleStart1(timestamp); + } else if (memcmp(macBytes.data(), buttonConfigs.stop1.mac, 6) == 0 && + (pressType == 1)) { + handleStop1(timestamp); + } else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0 && + (pressType == 2)) { + handleStart2(timestamp); + } else if (memcmp(macBytes.data(), buttonConfigs.stop2.mac, 6) == 0 && + (pressType == 1)) { + handleStop2(timestamp); + } + + // Flash status LED to indicate received message + updateStatusLED(3); +} + +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) + return; + String macStr = topicStr.substring(lastSlash + 1); + + auto macBytes = macStringToBytes(macStr.c_str()); + + String buttonType = "unknown"; + if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0) { + buttonType = "start1"; + } else if (memcmp(macBytes.data(), buttonConfigs.stop1.mac, 6) == 0) { + buttonType = "stop1"; + } else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0) { + buttonType = "start2"; + } else if (memcmp(macBytes.data(), buttonConfigs.stop2.mac, 6) == 0) { + buttonType = "stop2"; + } + + // Parse payload for timestamp (optional, falls im Payload enthalten) + uint64_t timestamp = millis(); + StaticJsonDocument<128> payloadDoc; + if (payload && strlen(payload) > 0 && + deserializeJson(payloadDoc, payload) == DeserializationError::Ok) { + if (payloadDoc.containsKey("timestamp")) { + timestamp = payloadDoc["timestamp"]; + } + } + + // JSON bauen + StaticJsonDocument<128> doc; + doc["button"] = buttonType; + doc["mac"] = macStr; + doc["timestamp"] = timestamp; + + 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()); +} + +void handleBatteryTopic(const char *topic, const char *payload) { + int batteryLevel = 0; + String topicStr(topic); + int lastSlash = topicStr.lastIndexOf('/'); + if (lastSlash < 0) + return; + String macStr = topicStr.substring(lastSlash + 1); + + auto macBytes = macStringToBytes(macStr.c_str()); + + String buttonType = "unknown"; + if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0) { + buttonType = "start1"; + } else if (memcmp(macBytes.data(), buttonConfigs.stop1.mac, 6) == 0) { + buttonType = "stop1"; + } else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0) { + buttonType = "start2"; + } else if (memcmp(macBytes.data(), buttonConfigs.stop2.mac, 6) == 0) { + buttonType = "stop2"; + } + + // Parse payload for timestamp (optional, falls im Payload enthalten) + StaticJsonDocument<128> payloadDoc; + if (payload && strlen(payload) > 0 && + deserializeJson(payloadDoc, payload) == DeserializationError::Ok) { + if (payloadDoc.containsKey("voltage")) { + batteryLevel = payloadDoc["voltage"]; + } + } + + // Berechne die Prozentzahl des Batteriestands für eine 1S LIPO batteryLevel + // sind Volts + // Hier wird angenommen, dass 3.7V 100% entspricht und 3.0V 0% + batteryLevel = + batteryLevel * 1000; // Umwandlung von V in mV für genauere Berechnung + if (batteryLevel < 3200) { + batteryLevel = 0; // 0% bei 3.0V + } else if (batteryLevel > 3700) { + batteryLevel = 100; // 100% bei 3.7V + } else { + batteryLevel = map(batteryLevel, 3000, 3700, 0, 100); // Linear Mapping + } + + // JSON bauen + StaticJsonDocument<128> doc; + doc["button"] = buttonType; + doc["mac"] = macStr; + doc["batteryLevel"] = batteryLevel; + + 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()); +} + +void readRFIDfromButton(const char *topic, const char *payload) { + // Create a JSON document to hold the button press data + StaticJsonDocument<256> doc; + DeserializationError error = deserializeJson(doc, payload); + if (!error) { + const char *mac = doc["buttonmac"] | "unknown"; + const char *uid = doc["uid"] | "unknown"; + + Serial.printf("RFID Read from Button:\n"); + Serial.printf(" Button MAC: %s\n", mac); + Serial.printf(" UID: %s\n", uid); + + // Convert buttonmac to byte array for comparison + auto macBytes = macStringToBytes(mac); + + // Check if the buttonmac matches buttonConfigs.start1.mac + if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0) { + // Fetch user data + UserData userData = checkUser(uid); + if (userData.exists) { + // Log user data + Serial.printf("User found for start1: %s %s, Alter: %d\n", + userData.firstname.c_str(), userData.lastname.c_str(), + userData.alter); + + // Create JSON message to send to the frontend + StaticJsonDocument<128> messageDoc; + messageDoc["firstname"] = userData.firstname; + messageDoc["lastname"] = userData.lastname; + messageDoc["lane"] = "start1"; // Add lane information + + String message; + serializeJson(messageDoc, message); + + // Push the message to the frontend + pushUpdateToFrontend(message); + Serial.printf("Pushed user data for start1 to frontend: %s\n", + message.c_str()); + } else { + Serial.println("User not found for UID: " + String(uid)); } - - // Flash status LED to indicate received message - updateStatusLED(3); - - -} - -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) return; - String macStr = topicStr.substring(lastSlash + 1); - - auto macBytes = macStringToBytes(macStr.c_str()); - - String buttonType = "unknown"; - if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0) { - buttonType = "start1"; - } else if (memcmp(macBytes.data(), buttonConfigs.stop1.mac, 6) == 0) { - buttonType = "stop1"; - } else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0) { - buttonType = "start2"; - } else if (memcmp(macBytes.data(), buttonConfigs.stop2.mac, 6) == 0) { - buttonType = "stop2"; } + // Check if the buttonmac matches buttonConfigs.start2.mac + else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0) { + // Fetch user data + UserData userData = checkUser(uid); + if (userData.exists) { + // Log user data + Serial.printf("User found for start2: %s %s, Alter: %d\n", + userData.firstname.c_str(), userData.lastname.c_str(), + userData.alter); - // Parse payload for timestamp (optional, falls im Payload enthalten) - uint64_t timestamp = millis(); - StaticJsonDocument<128> payloadDoc; - if (payload && strlen(payload) > 0 && deserializeJson(payloadDoc, payload) == DeserializationError::Ok) { - if (payloadDoc.containsKey("timestamp")) { - timestamp = payloadDoc["timestamp"]; - } - } + // Create JSON message to send to the frontend + StaticJsonDocument<128> messageDoc; + messageDoc["firstname"] = userData.firstname; + messageDoc["lastname"] = userData.lastname; + messageDoc["lane"] = "start2"; // Add lane information - // JSON bauen - StaticJsonDocument<128> doc; - doc["button"] = buttonType; - doc["mac"] = macStr; - doc["timestamp"] = timestamp; + String message; + serializeJson(messageDoc, message); - 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()); -} - -void handleBatteryTopic(const char* topic, const char* payload) { - int batteryLevel = 0; - String topicStr(topic); - int lastSlash = topicStr.lastIndexOf('/'); - if (lastSlash < 0) return; - String macStr = topicStr.substring(lastSlash + 1); - - auto macBytes = macStringToBytes(macStr.c_str()); - - String buttonType = "unknown"; - if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0) { - buttonType = "start1"; - } else if (memcmp(macBytes.data(), buttonConfigs.stop1.mac, 6) == 0) { - buttonType = "stop1"; - } else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0) { - buttonType = "start2"; - } else if (memcmp(macBytes.data(), buttonConfigs.stop2.mac, 6) == 0) { - buttonType = "stop2"; - } - - // Parse payload for timestamp (optional, falls im Payload enthalten) - StaticJsonDocument<128> payloadDoc; - if (payload && strlen(payload) > 0 && deserializeJson(payloadDoc, payload) == DeserializationError::Ok) { - if (payloadDoc.containsKey("voltage")) { - batteryLevel = payloadDoc["voltage"]; - } - } - - //Berechne die Prozentzahl des Batteriestands für eine 1S LIPO batteryLevel sind Volts - // Hier wird angenommen, dass 3.7V 100% entspricht und 3.0V 0% - batteryLevel = batteryLevel * 1000; // Umwandlung von V in mV für genauere Berechnung - if (batteryLevel < 3200) { - batteryLevel = 0; // 0% bei 3.0V - } else if (batteryLevel > 3700) { - batteryLevel = 100; // 100% bei 3.7V + // Push the message to the frontend + pushUpdateToFrontend(message); + Serial.printf("Pushed user data for start2 to frontend: %s\n", + message.c_str()); + } else { + Serial.println("User not found for UID: " + String(uid)); + } } else { - batteryLevel = map(batteryLevel, 3000, 3700, 0, 100); // Linear Mapping - } - - // JSON bauen - StaticJsonDocument<128> doc; - doc["button"] = buttonType; - doc["mac"] = macStr; - doc["batteryLevel"] = batteryLevel; - - 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()); -} - - - -void readRFIDfromButton(const char * topic, const char * payload) { - // Create a JSON document to hold the button press data - StaticJsonDocument<256> doc; - DeserializationError error = deserializeJson(doc, payload); - if (!error) { - const char* mac = doc["buttonmac"] | "unknown"; - const char* uid = doc["uid"] | "unknown"; - - Serial.printf("RFID Read from Button:\n"); - Serial.printf(" Button MAC: %s\n", mac); - Serial.printf(" UID: %s\n", uid); - - // Convert buttonmac to byte array for comparison - auto macBytes = macStringToBytes(mac); - - // Check if the buttonmac matches buttonConfigs.start1.mac - if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0) { - // Fetch user data - UserData userData = checkUser(uid); - if (userData.exists) { - // Log user data - Serial.printf("User found for start1: %s %s, Alter: %d\n", - userData.firstname.c_str(), - userData.lastname.c_str(), - userData.alter); - - // Create JSON message to send to the frontend - StaticJsonDocument<128> messageDoc; - messageDoc["firstname"] = userData.firstname; - messageDoc["lastname"] = userData.lastname; - messageDoc["lane"] = "start1"; // Add lane information - - String message; - serializeJson(messageDoc, message); - - // Push the message to the frontend - pushUpdateToFrontend(message); - Serial.printf("Pushed user data for start1 to frontend: %s\n", message.c_str()); - } else { - Serial.println("User not found for UID: " + String(uid)); - } - } - // Check if the buttonmac matches buttonConfigs.start2.mac - else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0) { - // Fetch user data - UserData userData = checkUser(uid); - if (userData.exists) { - // Log user data - Serial.printf("User found for start2: %s %s, Alter: %d\n", - userData.firstname.c_str(), - userData.lastname.c_str(), - userData.alter); - - // Create JSON message to send to the frontend - StaticJsonDocument<128> messageDoc; - messageDoc["firstname"] = userData.firstname; - messageDoc["lastname"] = userData.lastname; - messageDoc["lane"] = "start2"; // Add lane information - - String message; - serializeJson(messageDoc, message); - - // Push the message to the frontend - pushUpdateToFrontend(message); - Serial.printf("Pushed user data for start2 to frontend: %s\n", message.c_str()); - } else { - Serial.println("User not found for UID: " + String(uid)); - } - } else { - Serial.println("Button MAC does not match start1.mac or start2.mac"); - } - } else { - Serial.println("Failed to parse RFID JSON"); + Serial.println("Button MAC does not match start1.mac or start2.mac"); } + } else { + Serial.println("Failed to parse RFID JSON"); + } } void setupMqttServer() { // Set up the MQTT server with the desired port // Subscribe to a topic pattern and attach a callback - mqtt.subscribe("#", [](const char * topic, const char * payload) { - //Message received callback - //Serial.printf("Received message on topic '%s': %s\n", topic, payload); - if (strncmp(topic, "aquacross/button/", 17) == 0) { - readButtonJSON(topic, payload); - } - else if (strncmp(topic, "aquacross/button/rfid/", 22) == 0) { - readRFIDfromButton(topic, payload); - // Handle RFID read messages - } - else if (strncmp(topic, "aquacross/battery/", 17) == 0) { - handleBatteryTopic(topic, payload); - } - else if (strncmp(topic, "heartbeat/alive/", 16) == 0) { - handleHeartbeatTopic(topic, payload); - } - updateStatusLED(3); - }); - - // Start the MQTT server - mqtt.begin(); + mqtt.subscribe("#", [](const char *topic, const char *payload) { + // Message received callback + // Serial.printf("Received message on topic '%s': %s\n", topic, payload); + if (strncmp(topic, "aquacross/button/", 17) == 0) { + readButtonJSON(topic, payload); + } else if (strncmp(topic, "aquacross/button/rfid/", 22) == 0) { + readRFIDfromButton(topic, payload); + // Handle RFID read messages + } else if (strncmp(topic, "aquacross/battery/", 17) == 0) { + handleBatteryTopic(topic, payload); + } else if (strncmp(topic, "heartbeat/alive/", 16) == 0) { + handleHeartbeatTopic(topic, payload); + } + updateStatusLED(3); + }); - Serial.println("MQTT server started on port 1883"); + // Start the MQTT server + mqtt.begin(); + + Serial.println("MQTT server started on port 1883"); } - - void loopMqttServer() { - mqtt.loop(); - - static unsigned long lastPublish = 0; - if (millis() - lastPublish > 5000) { - // Convert timestamp to string before publishing - char timeStr[32]; - snprintf(timeStr, sizeof(timeStr), "%llu", getCurrentTimestampMs()); - mqtt.publish("sync/time", timeStr); - lastPublish = millis(); - } - + mqtt.loop(); + + static unsigned long lastPublish = 0; + if (millis() - lastPublish > 5000) { + // Convert timestamp to string before publishing + char timeStr[32]; + snprintf(timeStr, sizeof(timeStr), "%llu", getCurrentTimestampMs()); + mqtt.publish("sync/time", timeStr); + lastPublish = millis(); + } } -void sendMQTTMessage(const char * topic, const char * message) { - // Publish a message to the specified topic - mqtt.publish(topic, message); - Serial.printf("Published message to topic '%s': %s\n", topic, message); +void sendMQTTMessage(const char *topic, const char *message) { + // Publish a message to the specified topic + mqtt.publish(topic, message); + Serial.printf("Published message to topic '%s': %s\n", topic, message); } -void sendMQTTJSONMessage(const char * topic, const JsonDocument & doc) { - String jsonString; - serializeJson(doc, jsonString); - - // Publish the JSON string to the specified topic - auto publish = mqtt.begin_publish(topic, measureJson(doc)); - serializeJson(doc, publish); - publish.send(); - Serial.printf("Published JSON message to topic '%s': %s\n", topic, jsonString.c_str()); +void sendMQTTJSONMessage(const char *topic, const JsonDocument &doc) { + String jsonString; + serializeJson(doc, jsonString); + + // Publish the JSON string to the specified topic + auto publish = mqtt.begin_publish(topic, measureJson(doc)); + serializeJson(doc, publish); + publish.send(); + Serial.printf("Published JSON message to topic '%s': %s\n", topic, + jsonString.c_str()); } - - - diff --git a/src/databasebackend.h b/src/databasebackend.h index 6cb16f6..7c18467 100644 --- a/src/databasebackend.h +++ b/src/databasebackend.h @@ -1,191 +1,187 @@ #pragma once +#include "master.h" #include #include -#include "master.h" - - -const char* BACKEND_SERVER = "http://db.reptilfpv.de:3000"; -String BACKEND_TOKEN = licence; // Use the licence as the token for authentication +const char *BACKEND_SERVER = "http://db.reptilfpv.de:3000"; +String BACKEND_TOKEN = + licence; // Use the licence as the token for authentication bool backendOnline() { - Serial.println(licence); - - if (WiFi.status() != WL_CONNECTED) { - Serial.println("No WiFi connection."); - return false; - } + Serial.println(licence); - HTTPClient http; - http.begin(String(BACKEND_SERVER) + "/api/health"); - http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN); - - int httpCode = http.GET(); - bool isOnline = (httpCode == HTTP_CODE_OK); - - if (isOnline) { - Serial.println("Database server connection successful"); - } else { - Serial.printf("Database server connection failed, error: %d\n", httpCode); - } - - http.end(); - return isOnline; + if (WiFi.status() != WL_CONNECTED) { + Serial.println("No WiFi connection."); + return false; + } + + HTTPClient http; + http.begin(String(BACKEND_SERVER) + "/api/health"); + http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN); + + int httpCode = http.GET(); + bool isOnline = (httpCode == HTTP_CODE_OK); + + if (isOnline) { + Serial.println("Database server connection successful"); + } else { + Serial.printf("Database server connection failed, error: %d\n", httpCode); + } + + http.end(); + return isOnline; } struct UserData { - String uid; - String firstname; - String lastname; - int alter; - bool exists; + String uid; + String firstname; + String lastname; + int alter; + bool exists; }; -// UserData checkUser(const String& uid) is defined only once to avoid redefinition errors. -UserData checkUser(const String& uid) { +// UserData checkUser(const String& uid) is defined only once to avoid +// redefinition errors. +UserData checkUser(const String &uid) { - UserData userData = {"", "", "", 0, false}; - - if (!backendOnline()) { - Serial.println("No internet connection, cannot check user."); - return userData; - } + UserData userData = {"", "", "", 0, false}; - HTTPClient http; - http.begin(String(BACKEND_SERVER) + "/api/users/find"); - http.addHeader("Content-Type", "application/json"); - http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN); - - // Create JSON payload - StaticJsonDocument<200> requestDoc; - requestDoc["uid"] = uid; - String requestBody; - serializeJson(requestDoc, requestBody); - - int httpCode = http.POST(requestBody); - - if (httpCode == HTTP_CODE_OK) { - String payload = http.getString(); - StaticJsonDocument<512> responseDoc; - DeserializationError error = deserializeJson(responseDoc, payload); - - if (!error) { - userData.uid = responseDoc["uid"].as(); - userData.firstname = responseDoc["firstname"].as(); - userData.lastname = responseDoc["lastname"].as(); - userData.alter = responseDoc["alter"] | 0; - userData.exists = true; - } - } else { - Serial.printf("User check failed, HTTP code: %d\n", httpCode); - } - - http.end(); + if (!backendOnline()) { + Serial.println("No internet connection, cannot check user."); return userData; + } + + HTTPClient http; + http.begin(String(BACKEND_SERVER) + "/api/users/find"); + http.addHeader("Content-Type", "application/json"); + http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN); + + // Create JSON payload + StaticJsonDocument<200> requestDoc; + requestDoc["uid"] = uid; + String requestBody; + serializeJson(requestDoc, requestBody); + + int httpCode = http.POST(requestBody); + + if (httpCode == HTTP_CODE_OK) { + String payload = http.getString(); + StaticJsonDocument<512> responseDoc; + DeserializationError error = deserializeJson(responseDoc, payload); + + if (!error) { + userData.uid = responseDoc["uid"].as(); + userData.firstname = responseDoc["firstname"].as(); + userData.lastname = responseDoc["lastname"].as(); + userData.alter = responseDoc["alter"] | 0; + userData.exists = true; + } + } else { + Serial.printf("User check failed, HTTP code: %d\n", httpCode); + } + + http.end(); + return userData; } -//Function to enter user data into the database -bool enterUserData(const String& uid, const String& firstname, const String& lastname, const String& geburtsdatum, int alter) { - if (!backendOnline()) { - Serial.println("No internet connection, cannot enter user data."); - return false; - } +// Function to enter user data into the database +bool enterUserData(const String &uid, const String &firstname, + const String &lastname, const String &geburtsdatum, + int alter) { + if (!backendOnline()) { + Serial.println("No internet connection, cannot enter user data."); + return false; + } - HTTPClient http; - http.begin(String(BACKEND_SERVER) + "/api/users/insert"); - http.addHeader("Content-Type", "application/json"); - http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN); - - // Create JSON payload - StaticJsonDocument<256> requestDoc; - requestDoc["uid"] = uid; - requestDoc["vorname"] = firstname; - requestDoc["nachname"] = lastname; - requestDoc["geburtsdatum"] = geburtsdatum; - requestDoc["alter"] = alter; - - String requestBody; - serializeJson(requestDoc, requestBody); - - int httpCode = http.POST(requestBody); - - if (httpCode == HTTP_CODE_CREATED) { - Serial.println("User data entered successfully."); - http.end(); - return true; - } else { - Serial.printf("Failed to enter user data, HTTP code: %d\n", httpCode); - http.end(); - return false; - } + HTTPClient http; + http.begin(String(BACKEND_SERVER) + "/api/users/insert"); + http.addHeader("Content-Type", "application/json"); + http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN); + + // Create JSON payload + StaticJsonDocument<256> requestDoc; + requestDoc["uid"] = uid; + requestDoc["vorname"] = firstname; + requestDoc["nachname"] = lastname; + requestDoc["geburtsdatum"] = geburtsdatum; + requestDoc["alter"] = alter; + + String requestBody; + serializeJson(requestDoc, requestBody); + + int httpCode = http.POST(requestBody); + + if (httpCode == HTTP_CODE_CREATED) { + Serial.println("User data entered successfully."); + http.end(); + return true; + } else { + Serial.printf("Failed to enter user data, HTTP code: %d\n", httpCode); + http.end(); + return false; + } } JsonDocument getAllLocations() { - JsonDocument locations; // Allocate memory for the JSON document + JsonDocument locations; // Allocate memory for the JSON document - if (!backendOnline()) { - Serial.println("No internet connection, cannot fetch locations."); - return locations; // Return an empty document + if (!backendOnline()) { + Serial.println("No internet connection, cannot fetch locations."); + return locations; // Return an empty document + } + + HTTPClient http; + http.begin(String(BACKEND_SERVER) + "/api/location/"); + http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN); + + int httpCode = http.GET(); + + if (httpCode == HTTP_CODE_OK) { + String payload = http.getString(); + DeserializationError error = deserializeJson(locations, payload); + + if (error) { + Serial.println("Failed to parse locations JSON."); } + } else { + Serial.printf("Failed to fetch locations, HTTP code: %d\n", httpCode); + } - HTTPClient http; - http.begin(String(BACKEND_SERVER) + "/api/location/"); - http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN); - - int httpCode = http.GET(); - - if (httpCode == HTTP_CODE_OK) { - String payload = http.getString(); - DeserializationError error = deserializeJson(locations, payload); - - if (error) { - Serial.println("Failed to parse locations JSON."); - } - } else { - Serial.printf("Failed to fetch locations, HTTP code: %d\n", httpCode); - } - - http.end(); - return locations; // Return the populated JSON document + http.end(); + return locations; // Return the populated JSON document } // Keep this for backward compatibility -bool userExists(const String& uid) { - return checkUser(uid).exists; -} +bool userExists(const String &uid) { return checkUser(uid).exists; } +// Routes from the Frontend into here and then into DB backend. -//Routes from the Frontend into here and then into DB backend. +void setupBackendRoutes(AsyncWebServer &server) { -void setupBackendRoutes(AsyncWebServer& server) { - - server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) { - - DynamicJsonDocument doc(64); - doc["status"] = backendOnline() ? "connected" : "disconnected"; - String response; - serializeJson(doc, response); - request->send(200, "application/json", response); - }); + server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) { + DynamicJsonDocument doc(64); + doc["status"] = backendOnline() ? "connected" : "disconnected"; + String response; + serializeJson(doc, response); + request->send(200, "application/json", response); + }); - server.on("/api/users", HTTP_GET, [](AsyncWebServerRequest *request) { - if (!backendOnline()) { - request->send(503, "application/json", "{\"error\":\"Database not connected\"}"); - return; - } - - // Handle user retrieval logic here - }); - //Location routes /api/location/ - server.on("/api/location/", HTTP_GET, [](AsyncWebServerRequest *request){ + server.on("/api/users", HTTP_GET, [](AsyncWebServerRequest *request) { + if (!backendOnline()) { + request->send(503, "application/json", + "{\"error\":\"Database not connected\"}"); + return; + } + // Handle user retrieval logic here + }); + // Location routes /api/location/ + server.on("/api/location/", HTTP_GET, [](AsyncWebServerRequest *request) { String result; serializeJson(getAllLocations(), result); request->send(200, "application/json", result); }); - - - // Add more routes as needed + // Add more routes as needed } diff --git a/src/debug.h b/src/debug.h index 2210b7b..88786f5 100644 --- a/src/debug.h +++ b/src/debug.h @@ -1,49 +1,41 @@ // Zeit-bezogene Variablen und Includes #pragma once #include -#include -#include #include -#include +#include +#include #include +#include + #include "communication.h" +void setupDebugAPI(AsyncWebServer &server); +void setupDebugAPI(AsyncWebServer &server) { - -void setupDebugAPI(AsyncWebServer& server); - -void setupDebugAPI(AsyncWebServer& server) { - -//DEBUG -server.on("/api/debug/start1", HTTP_GET, [](AsyncWebServerRequest *request){ + // DEBUG + server.on("/api/debug/start1", HTTP_GET, [](AsyncWebServerRequest *request) { handleStart1(0); request->send(200, "text/plain", "handleStart1() called"); -}); + }); -server.on("/api/debug/stop1", HTTP_GET, [](AsyncWebServerRequest *request){ + server.on("/api/debug/stop1", HTTP_GET, [](AsyncWebServerRequest *request) { handleStop1(0); request->send(200, "text/plain", "handleStop1() called"); -}); + }); -server.on("/api/debug/start2", HTTP_GET, [](AsyncWebServerRequest *request){ + server.on("/api/debug/start2", HTTP_GET, [](AsyncWebServerRequest *request) { handleStart2(0); request->send(200, "text/plain", "handleStart2() called"); -}); + }); -server.on("/api/debug/stop2", HTTP_GET, [](AsyncWebServerRequest *request){ + server.on("/api/debug/stop2", HTTP_GET, [](AsyncWebServerRequest *request) { handleStop2(0); request->send(200, "text/plain", "handleStop2() called"); -}); + }); - - -Serial.println("Debug-API initialisiert"); + Serial.println("Debug-API initialisiert"); } - - - - -//DEBUG END \ No newline at end of file +// DEBUG END \ No newline at end of file diff --git a/src/helper.h b/src/helper.h index 93b7725..c8529d0 100644 --- a/src/helper.h +++ b/src/helper.h @@ -1,10 +1,11 @@ #pragma once -#include #include "master.h" +#include -std::array macStringToBytes(const char* macStr) { - std::array bytes; - sscanf(macStr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", - &bytes[0], &bytes[1], &bytes[2], &bytes[3], &bytes[4], &bytes[5]); - return bytes; + +std::array macStringToBytes(const char *macStr) { + std::array bytes; + sscanf(macStr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &bytes[0], &bytes[1], + &bytes[2], &bytes[3], &bytes[4], &bytes[5]); + return bytes; } \ No newline at end of file diff --git a/src/licenceing.h b/src/licenceing.h index d06f0da..c42f7cc 100644 --- a/src/licenceing.h +++ b/src/licenceing.h @@ -1,102 +1,106 @@ #pragma once +#include "mbedtls/md.h" #include +#include #include +#include #include #include -#include -#include -#include "mbedtls/md.h" -const char* secret = "542ff224606c61fb3024e22f76ef9ac8"; + +const char *secret = "542ff224606c61fb3024e22f76ef9ac8"; // Preferences für persistente Speicherung Preferences preferences; String licence; -//Prototype für Funktionen +// Prototype für Funktionen String getUniqueDeviceID(); -String hmacSHA256(const String& key, const String& message); -bool checkLicense(const String& deviceID, const String& licenseKey); -void setupLicenceAPI(AsyncWebServer& server); +String hmacSHA256(const String &key, const String &message); +bool checkLicense(const String &deviceID, const String &licenseKey); +void setupLicenceAPI(AsyncWebServer &server); void saveLicenceToPrefs(); void loadLicenceFromPrefs(); - String getUniqueDeviceID() { - uint8_t mac[6]; - esp_wifi_get_mac(WIFI_IF_STA, mac); // Use STA MAC for uniqueness - char id[13]; - sprintf(id, "%02X%02X%02X%02X%02X%02X", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - return String(id); + uint8_t mac[6]; + esp_wifi_get_mac(WIFI_IF_STA, mac); // Use STA MAC for uniqueness + char id[13]; + sprintf(id, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], + mac[4], mac[5]); + return String(id); } -String hmacSHA256(const String& key, const String& message) { - byte hmacResult[32]; - mbedtls_md_context_t ctx; - mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; +String hmacSHA256(const String &key, const String &message) { + byte hmacResult[32]; + mbedtls_md_context_t ctx; + mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; - mbedtls_md_init(&ctx); - const mbedtls_md_info_t* md_info = mbedtls_md_info_from_type(md_type); - mbedtls_md_setup(&ctx, md_info, 1); - mbedtls_md_hmac_starts(&ctx, (const unsigned char*)key.c_str(), key.length()); - mbedtls_md_hmac_update(&ctx, (const unsigned char*)message.c_str(), message.length()); - mbedtls_md_hmac_finish(&ctx, hmacResult); - mbedtls_md_free(&ctx); + mbedtls_md_init(&ctx); + const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(md_type); + mbedtls_md_setup(&ctx, md_info, 1); + mbedtls_md_hmac_starts(&ctx, (const unsigned char *)key.c_str(), + key.length()); + mbedtls_md_hmac_update(&ctx, (const unsigned char *)message.c_str(), + message.length()); + mbedtls_md_hmac_finish(&ctx, hmacResult); + mbedtls_md_free(&ctx); - String result = ""; - for (int i = 0; i < 32; i++) { - char buf[3]; - sprintf(buf, "%02X", hmacResult[i]); - result += buf; + String result = ""; + for (int i = 0; i < 32; i++) { + char buf[3]; + sprintf(buf, "%02X", hmacResult[i]); + result += buf; + } + return result; +} + +int getLicenseTier(const String &deviceID, const String &licenseKey) { + for (int tier = 1; tier <= 4; ++tier) { + String data = deviceID + ":" + String(tier); + String expected = hmacSHA256(secret, data); + if (licenseKey.equalsIgnoreCase(expected)) { + return tier; // Found matching tier } - return result; + } + return 0; // No valid tier found } -int getLicenseTier(const String& deviceID, const String& licenseKey) { - for (int tier = 1; tier <= 4; ++tier) { - String data = deviceID + ":" + String(tier); - String expected = hmacSHA256(secret, data); - if (licenseKey.equalsIgnoreCase(expected)) { - return tier; // Found matching tier - } - } - return 0; // No valid tier found -} +void setupLicenceAPI(AsyncWebServer &server) { -void setupLicenceAPI(AsyncWebServer& server) { - - server.on("/api/get-licence", HTTP_GET, [](AsyncWebServerRequest *request){ - Serial.println("Received request to get licence"); - loadLicenceFromPrefs(); - String deviceID = getUniqueDeviceID(); - int tier = getLicenseTier(deviceID, licence); - String json = "{\"licence\":\"" + licence + "\"," - "\"valid\":" + String(tier > 0 ? "true" : "false") + + server.on("/api/get-licence", HTTP_GET, [](AsyncWebServerRequest *request) { + Serial.println("Received request to get licence"); + loadLicenceFromPrefs(); + String deviceID = getUniqueDeviceID(); + int tier = getLicenseTier(deviceID, licence); + String json = "{\"licence\":\"" + licence + + "\"," + "\"valid\":" + + String(tier > 0 ? "true" : "false") + ",\"tier\":" + String(tier) + "}"; request->send(200, "application/json", json); - }); + }); - server.on("/api/set-licence", HTTP_POST, [](AsyncWebServerRequest *request){ - Serial.println("Received request to set licence"); - if (request->hasParam("licence", true)) { - licence = request->getParam("licence", true)->value(); - Serial.println("Received request to set licence " + licence); - saveLicenceToPrefs(); // eigene Funktion - request->send(200, "application/json", "{\"success\":true}"); - } else { - request->send(400, "application/json", "{\"success\":false}"); - } - }); + server.on("/api/set-licence", HTTP_POST, [](AsyncWebServerRequest *request) { + Serial.println("Received request to set licence"); + if (request->hasParam("licence", true)) { + licence = request->getParam("licence", true)->value(); + Serial.println("Received request to set licence " + licence); + saveLicenceToPrefs(); // eigene Funktion + request->send(200, "application/json", "{\"success\":true}"); + } else { + request->send(400, "application/json", "{\"success\":false}"); + } + }); - Serial.println("Licence API setup complete"); + Serial.println("Licence API setup complete"); } void saveLicenceToPrefs() { - preferences.begin("key", false); - preferences.putString("key", licence); - preferences.end(); + preferences.begin("key", false); + preferences.putString("key", licence); + preferences.end(); } void loadLicenceFromPrefs() { diff --git a/src/master.cpp b/src/master.cpp index 77c21a2..bee9b3a 100644 --- a/src/master.cpp +++ b/src/master.cpp @@ -1,28 +1,30 @@ #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#include #include "master.h" +#include + // Aquacross Timer - ESP32 Master (Webserver + ESP-NOW + Anlernmodus) -#include -#include -#include #include +#include +#include #include #include +#include +#include #include -#include -#include -#include -#include -#include -#include + #include #include +#include +#include #include +#include +#include +#include -const char* firmwareversion = "1.0.0"; // Version der Firmware +const char *firmwareversion = "1.0.0"; // Version der Firmware void handleStart1(uint64_t timestamp = 0) { if (!timerData.isRunning1 && timerData.isReady1) { @@ -46,7 +48,8 @@ void handleStop1(uint64_t timestamp = 0) { timerData.bestTime1 = currentTime; saveBestTimes(); } - Serial.println("Bahn 1 gestoppt - Zeit: " + String(currentTime/1000.0) + "s"); + Serial.println("Bahn 1 gestoppt - Zeit: " + String(currentTime / 1000.0) + + "s"); } } @@ -72,66 +75,70 @@ void handleStop2(uint64_t timestamp = 0) { timerData.bestTime2 = currentTime; saveBestTimes(); } - Serial.println("Bahn 2 gestoppt - Zeit: " + String(currentTime/1000.0) + "s"); + Serial.println("Bahn 2 gestoppt - Zeit: " + String(currentTime / 1000.0) + + "s"); } } void checkAutoReset() { unsigned long currentTime = millis(); - - if (timerData.isRunning1 && (currentTime - timerData.localStartTime1 > maxTimeBeforeReset)) { + + if (timerData.isRunning1 && + (currentTime - timerData.localStartTime1 > maxTimeBeforeReset)) { timerData.isRunning1 = false; timerData.startTime1 = 0; Serial.println("Bahn 1 automatisch zurückgesetzt"); } - - if (timerData.isRunning2 && (currentTime - timerData.localStartTime2 > maxTimeBeforeReset)) { + + if (timerData.isRunning2 && + (currentTime - timerData.localStartTime2 > maxTimeBeforeReset)) { timerData.isRunning2 = false; timerData.startTime2 = 0; Serial.println("Bahn 2 automatisch zurückgesetzt"); } // Automatischer Reset nach 10 Sekunden "Beendet" - if (!timerData.isRunning1 && timerData.endTime1 > 0 && timerData.finishedSince1 > 0) { + if (!timerData.isRunning1 && timerData.endTime1 > 0 && + timerData.finishedSince1 > 0) { if (millis() - timerData.finishedSince1 > maxTimeDisplay) { timerData.startTime1 = 0; timerData.endTime1 = 0; timerData.finishedSince1 = 0; timerData.isReady1 = true; // Zurücksetzen auf "Bereit" - - JsonDocument messageDoc; - messageDoc["firstname"] =""; - messageDoc["lastname"] = ""; - messageDoc["lane"] = "start1"; // Add lane information - String message; - serializeJson(messageDoc, message); + JsonDocument messageDoc; + messageDoc["firstname"] = ""; + messageDoc["lastname"] = ""; + messageDoc["lane"] = "start1"; // Add lane information - // Push the message to the frontend - pushUpdateToFrontend(message); + String message; + serializeJson(messageDoc, message); + + // Push the message to the frontend + pushUpdateToFrontend(message); Serial.println("Bahn 1 automatisch auf 'Bereit' zurückgesetzt"); } } - if (!timerData.isRunning2 && timerData.endTime2 > 0 && timerData.finishedSince2 > 0) { + if (!timerData.isRunning2 && timerData.endTime2 > 0 && + timerData.finishedSince2 > 0) { if (currentTime - timerData.finishedSince2 > maxTimeDisplay) { timerData.startTime2 = 0; timerData.endTime2 = 0; timerData.finishedSince2 = 0; timerData.isReady2 = true; // Zurücksetzen auf "Bereit" - - JsonDocument messageDoc; - messageDoc["firstname"] = ""; - messageDoc["lastname"] = ""; - messageDoc["lane"] = "start2"; // Add lane information - String message; - serializeJson(messageDoc, message); + JsonDocument messageDoc; + messageDoc["firstname"] = ""; + messageDoc["lastname"] = ""; + messageDoc["lane"] = "start2"; // Add lane information - // Push the message to the frontend - pushUpdateToFrontend(message); + String message; + serializeJson(messageDoc, message); + // Push the message to the frontend + pushUpdateToFrontend(message); Serial.println("Bahn 2 automatisch auf 'Bereit' zurückgesetzt"); } @@ -186,14 +193,14 @@ void saveWifiSettings() { preferences.putString("ssid", ssidSTA); preferences.putString("password", passwordSTA); preferences.end(); - delay(500); // Warte 2 Sekunden, bevor der Neustart erfolgt + delay(500); // Warte 2 Sekunden, bevor der Neustart erfolgt ESP.restart(); // Neustart des ESP32 } void loadLocationSettings() { - preferences.begin("location", true); - masterlocation = preferences.getString("location", ""); - preferences.end(); + preferences.begin("location", true); + masterlocation = preferences.getString("location", ""); + preferences.end(); } void saveLocationSettings() { @@ -203,18 +210,14 @@ void saveLocationSettings() { } void loadWifiSettings() { - preferences.begin("wifi", true); - String ssid = preferences.getString("ssid", ""); - String password = preferences.getString("password", ""); - ssidSTA = strdup(ssid.c_str()); - passwordSTA = strdup(password.c_str()); - preferences.end(); + preferences.begin("wifi", true); + String ssid = preferences.getString("ssid", ""); + String password = preferences.getString("password", ""); + ssidSTA = strdup(ssid.c_str()); + passwordSTA = strdup(password.c_str()); + preferences.end(); } - - - - int checkLicence() { loadLicenceFromPrefs(); String id = getUniqueDeviceID(); @@ -224,7 +227,7 @@ int checkLicence() { String getTimerDataJSON() { DynamicJsonDocument doc(1024); - + unsigned long currentTime = millis(); // Bahn 1 if (timerData.isRunning1) { @@ -237,7 +240,7 @@ String getTimerDataJSON() { doc["time1"] = 0; doc["status1"] = "ready"; } - + // Bahn 2 if (timerData.isRunning2) { doc["time2"] = (currentTime - timerData.localStartTime2) / 1000.0; @@ -249,40 +252,38 @@ String getTimerDataJSON() { 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"}; + 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); - if (!SPIFFS.begin(true)) { Serial.println("SPIFFS Mount Failed"); return; } - - //setup API libararies + + // setup API libararies setupTimeAPI(server); setupLicenceAPI(server); setupDebugAPI(server); setupBackendRoutes(server); setupRFIDRoute(server); - // Gespeicherte Daten laden loadButtonConfig(); @@ -293,14 +294,12 @@ void setup() { setupWifi(); // WiFi initialisieren setupOTA(&server); - + setupRoutes(); setupWebSocket(); setupLED(); setupMqttServer(); // MQTT Server initialisieren setupRFID(); - - } void loop() { diff --git a/src/master.h b/src/master.h index 234441b..9bf33a9 100644 --- a/src/master.h +++ b/src/master.h @@ -1,15 +1,16 @@ #pragma once #include -#include #include -#include +#include #include +#include -const char* ssidAP; -const char* passwordAP = nullptr; -char* ssidSTA = nullptr; -char* passwordSTA = nullptr; +const char *ssidAP; +const char *passwordAP = nullptr; + +char *ssidSTA = nullptr; +char *passwordSTA = nullptr; // Timer Struktur struct TimerData { @@ -42,7 +43,7 @@ struct ButtonConfigs { ButtonConfig stop2; }; -extern const char* firmwareversion; +extern const char *firmwareversion; // Globale Variablen TimerData timerData; @@ -50,13 +51,13 @@ ButtonConfigs buttonConfigs; bool learningMode = false; int learningStep = 0; // 0=Start1, 1=Stop1, 2=Start2, 3=Stop2 unsigned long maxTimeBeforeReset = 300000; // 5 Minuten default -unsigned long maxTimeDisplay = 20000; // 20 Sekunden Standard (in ms) -bool wifimodeAP = false; // AP-Modus deaktiviert +unsigned long maxTimeDisplay = 20000; // 20 Sekunden Standard (in ms) +bool wifimodeAP = false; // AP-Modus deaktiviert String masterlocation; -//Function Declarations -void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len); -void handleLearningMode(const uint8_t* mac); +// Function Declarations +void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len); +void handleLearningMode(const uint8_t *mac); void handleStartLearning(); void handleStart1(uint64_t timestamp); void handleStop1(uint64_t timestamp); diff --git a/src/rfid.h b/src/rfid.h index d212f83..682b7ab 100644 --- a/src/rfid.h +++ b/src/rfid.h @@ -1,16 +1,17 @@ #pragma once #include #include -#include #include +#include + // RFID Konfiguration #define RST_PIN 21 // Configurable, see typical pin layout above #define SS_PIN 5 // Configurable, see typical pin layout above - MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance -std::map blockedUIDs; // Map to store blocked UIDs and their timestamps +std::map + blockedUIDs; // Map to store blocked UIDs and their timestamps const unsigned long BLOCK_DURATION = 10 * 1000; // 10 Seconds in milliseconds // Neue Variablen für API-basiertes Lesen @@ -18,20 +19,19 @@ bool rfidReadRequested = false; String lastReadUID = ""; bool rfidReadSuccess = false; unsigned long rfidReadStartTime = 0; -const unsigned long RFID_READ_TIMEOUT = 10000; // 10 Sekunden Timeout für API Requests - +const unsigned long RFID_READ_TIMEOUT = + 10000; // 10 Sekunden Timeout für API Requests void setupRFID() { // SPI und RFID initialisieren - SPI.begin(); // Init SPI bus + SPI.begin(); // Init SPI bus mfrc522.PCD_Init(); // Init MFRC522 - delay(4); // Optional delay. Some boards need more time after init to be ready - mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader details - + delay(4); // Optional delay. Some boards need more time after init to be ready + mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card + // Reader details } - void handleAutomaticRFID() { if (!mfrc522.PICC_IsNewCardPresent()) { return; @@ -73,7 +73,7 @@ void handleAutomaticRFID() { // Block the UID for 10 seconds blockedUIDs[uid] = currentTime; - //show the remaining time for the block + // show the remaining time for the block Serial.print(F("UID blocked for 10 seconds. Remaining time: ")); Serial.print((BLOCK_DURATION - (currentTime - blockedUIDs[uid])) / 1000); Serial.println(F(" seconds.")); @@ -85,7 +85,7 @@ void handleAutomaticRFID() { // Neue Funktion für API-basiertes RFID Lesen void handleAPIRFIDRead() { unsigned long currentTime = millis(); - + // Timeout prüfen if (currentTime - rfidReadStartTime > RFID_READ_TIMEOUT) { Serial.println("RFID API Timeout - keine Karte erkannt"); @@ -94,17 +94,17 @@ void handleAPIRFIDRead() { lastReadUID = ""; return; } - + // Prüfen ob neue Karte vorhanden ist if (!mfrc522.PICC_IsNewCardPresent()) { return; } - + // Karte auswählen if (!mfrc522.PICC_ReadCardSerial()) { return; } - + // UID für API lesen (ohne Doppelpunkt-Trenner, Großbuchstaben) String uid = ""; for (byte i = 0; i < mfrc522.uid.size; i++) { @@ -113,17 +113,17 @@ void handleAPIRFIDRead() { } uid += String(mfrc522.uid.uidByte[i], HEX); } - + // UID in Großbuchstaben konvertieren uid.toUpperCase(); - + Serial.println("RFID API UID gelesen: " + uid); - + // Ergebnis speichern lastReadUID = uid; rfidReadSuccess = true; rfidReadRequested = false; - + // Karte "halt" setzen mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); @@ -139,76 +139,79 @@ void startRFIDRead() { } // API Funktion: Prüfen ob Lesevorgang abgeschlossen -bool isRFIDReadComplete() { - return !rfidReadRequested; -} +bool isRFIDReadComplete() { return !rfidReadRequested; } // API Funktion: Ergebnis des Lesevorgangs abrufen -String getRFIDReadResult(bool& success) { +String getRFIDReadResult(bool &success) { success = rfidReadSuccess; return lastReadUID; } -void setupRFIDRoute(AsyncWebServer& server) { - server.on("/api/rfid/read", HTTP_GET, [](AsyncWebServerRequest *request) { - Serial.println("api/rfid/read"); +void setupRFIDRoute(AsyncWebServer &server) { + server.on("/api/rfid/read", HTTP_GET, [](AsyncWebServerRequest *request) { + Serial.println("api/rfid/read"); - // Start RFID-Lesevorgang - startRFIDRead(); - unsigned long startTime = millis(); + // Start RFID-Lesevorgang + startRFIDRead(); + unsigned long startTime = millis(); - // Warten, bis eine UID gelesen wird oder Timeout eintritt - while (!isRFIDReadComplete()) { - if (millis() - startTime > RFID_READ_TIMEOUT) { - break; - } - delay(10); // Kurze Pause, um die CPU nicht zu blockieren - } - - DynamicJsonDocument response(200); - - if (rfidReadSuccess && lastReadUID.length() > 0) { - response["success"] = true; - response["uid"] = lastReadUID; - response["message"] = "UID erfolgreich gelesen"; - } else { - response["success"] = false; - response["error"] = "Keine RFID Karte erkannt oder Timeout"; - response["uid"] = ""; - } - - String jsonString; - serializeJson(response, jsonString); - request->send(200, "application/json", jsonString); - }); - -server.on("/api/users/insert", HTTP_POST, [](AsyncWebServerRequest *request) {}, NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { - Serial.println("/api/users/insert"); - - // Parse the incoming JSON payload - DynamicJsonDocument doc(512); - DeserializationError error = deserializeJson(doc, data, len); + // Warten, bis eine UID gelesen wird oder Timeout eintritt + while (!isRFIDReadComplete()) { + if (millis() - startTime > RFID_READ_TIMEOUT) { + break; + } + delay(10); // Kurze Pause, um die CPU nicht zu blockieren + } DynamicJsonDocument response(200); - if (error) { - Serial.println("Fehler beim Parsen der JSON-Daten"); - response["success"] = false; - response["error"] = "Ungültige JSON-Daten"; + if (rfidReadSuccess && lastReadUID.length() > 0) { + response["success"] = true; + response["uid"] = lastReadUID; + response["message"] = "UID erfolgreich gelesen"; } else { - // Extract user data from the JSON payload - String uid = doc["uid"] | ""; - String vorname = doc["vorname"] | ""; - String nachname = doc["nachname"] | ""; - String geburtsdatum = doc["geburtsdatum"] | ""; - int alter = doc["alter"] | 0; + response["success"] = false; + response["error"] = "Keine RFID Karte erkannt oder Timeout"; + response["uid"] = ""; + } - // Validate the data - if (uid.isEmpty() || vorname.isEmpty() || nachname.isEmpty() || geburtsdatum.isEmpty() || alter <= 0) { + String jsonString; + serializeJson(response, jsonString); + request->send(200, "application/json", jsonString); + }); + + server.on( + "/api/users/insert", HTTP_POST, [](AsyncWebServerRequest *request) {}, + NULL, + [](AsyncWebServerRequest *request, uint8_t *data, size_t len, + size_t index, size_t total) { + Serial.println("/api/users/insert"); + + // Parse the incoming JSON payload + DynamicJsonDocument doc(512); + DeserializationError error = deserializeJson(doc, data, len); + + DynamicJsonDocument response(200); + + if (error) { + Serial.println("Fehler beim Parsen der JSON-Daten"); + response["success"] = false; + response["error"] = "Ungültige JSON-Daten"; + } else { + // Extract user data from the JSON payload + String uid = doc["uid"] | ""; + String vorname = doc["vorname"] | ""; + String nachname = doc["nachname"] | ""; + String geburtsdatum = doc["geburtsdatum"] | ""; + int alter = doc["alter"] | 0; + + // Validate the data + if (uid.isEmpty() || vorname.isEmpty() || nachname.isEmpty() || + geburtsdatum.isEmpty() || alter <= 0) { Serial.println("Ungültige Eingabedaten"); response["success"] = false; response["error"] = "Ungültige Eingabedaten"; - } else { + } else { // Process the data using the enterUserData function Serial.println("Benutzerdaten empfangen:"); Serial.println("UID: " + uid); @@ -216,37 +219,37 @@ server.on("/api/users/insert", HTTP_POST, [](AsyncWebServerRequest *request) {}, Serial.println("Nachname: " + nachname); Serial.println("Alter: " + String(alter)); - bool dbSuccess = enterUserData(uid, vorname, nachname, geburtsdatum, alter); + bool dbSuccess = + enterUserData(uid, vorname, nachname, geburtsdatum, alter); if (dbSuccess) { - response["success"] = true; - response["message"] = "Benutzer erfolgreich gespeichert"; + response["success"] = true; + response["message"] = "Benutzer erfolgreich gespeichert"; } else { - response["success"] = false; - response["error"] = "Fehler beim Speichern in der Datenbank"; + response["success"] = false; + response["error"] = "Fehler beim Speichern in der Datenbank"; } + } } - } - - // Send the response back to the client - String jsonString; - serializeJson(response, jsonString); - request->send(200, "application/json", jsonString); -}); + // Send the response back to the client + String jsonString; + serializeJson(response, jsonString); + request->send(200, "application/json", jsonString); + }); } - // API Funktion: RFID Reader Status prüfen bool checkRFIDReaderStatus() { byte version = mfrc522.PCD_ReadRegister(mfrc522.VersionReg); - + // Bekannte MFRC522 Versionen: 0x91, 0x92 if (version == 0x91 || version == 0x92) { Serial.println("RFID Reader OK (Version: 0x" + String(version, HEX) + ")"); return true; } else { - Serial.println("RFID Reader Fehler (Version: 0x" + String(version, HEX) + ")"); + Serial.println("RFID Reader Fehler (Version: 0x" + String(version, HEX) + + ")"); return false; } } @@ -254,7 +257,7 @@ bool checkRFIDReaderStatus() { // Hilfsfunktion: Blockierte UIDs aufräumen void cleanupBlockedUIDs() { unsigned long currentTime = millis(); - + // Iterator für sicheres Löschen während der Iteration for (auto it = blockedUIDs.begin(); it != blockedUIDs.end();) { if (currentTime - it->second >= BLOCK_DURATION) { @@ -265,16 +268,14 @@ void cleanupBlockedUIDs() { } } -void loopRFID(){ +void loopRFID() { // Originale Funktionalität für automatisches Lesen if (!rfidReadRequested) { handleAutomaticRFID(); } - + // API-basiertes Lesen verarbeiten if (rfidReadRequested) { handleAPIRFIDRead(); } } - - diff --git a/src/statusled.h b/src/statusled.h index 0fdb224..e42bb13 100644 --- a/src/statusled.h +++ b/src/statusled.h @@ -1,7 +1,6 @@ #include - -#define LED_PIN 13 +#define LED_PIN 13 // Status LED unsigned long lastLedBlink = 0; @@ -14,45 +13,44 @@ void setupLED() { void updateStatusLED(int blinkPattern) { unsigned long currentTime = millis(); - - switch (blinkPattern) { - case 0: // Suche Master - Langsames Blinken - if (currentTime - lastLedBlink > 1000) { - ledState = !ledState; - digitalWrite(LED_PIN, ledState); - lastLedBlink = currentTime; - } - break; - - case 1: // Verbunden - Kurzes Blinken alle 3 Sekunden - if (currentTime - lastLedBlink > 3000) { - digitalWrite(LED_PIN, HIGH); - delay(100); - digitalWrite(LED_PIN, LOW); - lastLedBlink = currentTime; - } - break; - - case 2: // Button gedrückt - Schnelles Blinken 3x - static int blinkCount = 0; - if (currentTime - lastLedBlink > 100) { - ledState = !ledState; - digitalWrite(LED_PIN, ledState); - lastLedBlink = currentTime; - blinkCount++; - - if (blinkCount >= 6) { // 3 komplette Blinks - blinkCount = 0; - blinkPattern = 1; // Zurück zu verbunden - } - } - case 3: // Flash bei Empfang - Einmaliges kurzes Blinken - { - digitalWrite(LED_PIN, HIGH); - delay(100); - digitalWrite(LED_PIN, LOW); + switch (blinkPattern) { + case 0: // Suche Master - Langsames Blinken + if (currentTime - lastLedBlink > 1000) { + ledState = !ledState; + digitalWrite(LED_PIN, ledState); + lastLedBlink = currentTime; + } + break; + + case 1: // Verbunden - Kurzes Blinken alle 3 Sekunden + if (currentTime - lastLedBlink > 3000) { + digitalWrite(LED_PIN, HIGH); + delay(100); + digitalWrite(LED_PIN, LOW); + lastLedBlink = currentTime; + } + break; + + case 2: // Button gedrückt - Schnelles Blinken 3x + static int blinkCount = 0; + if (currentTime - lastLedBlink > 100) { + ledState = !ledState; + digitalWrite(LED_PIN, ledState); + lastLedBlink = currentTime; + blinkCount++; + + if (blinkCount >= 6) { // 3 komplette Blinks + blinkCount = 0; + blinkPattern = 1; // Zurück zu verbunden } - break; + } + + case 3: // Flash bei Empfang - Einmaliges kurzes Blinken + { + digitalWrite(LED_PIN, HIGH); + delay(100); + digitalWrite(LED_PIN, LOW); + } break; } } diff --git a/src/timesync.h b/src/timesync.h index 832a47e..32fe768 100644 --- a/src/timesync.h +++ b/src/timesync.h @@ -1,12 +1,13 @@ // Zeit-bezogene Variablen und Includes #pragma once -#include -#include -#include -#include -#include -#include #include "RTClib.h" +#include +#include +#include +#include +#include +#include + RTC_PCF8523 rtc; @@ -16,8 +17,8 @@ struct timezone tz; time_t now; struct tm timeinfo; -//Prototypen für Zeit-Management-Funktionen -void setupTimeAPI(AsyncWebServer& server); +// Prototypen für Zeit-Management-Funktionen +void setupTimeAPI(AsyncWebServer &server); String getCurrentTimeJSON(); bool setSystemTime(long timestamp); @@ -25,11 +26,11 @@ bool setSystemTime(long timestamp); String getCurrentTimeJSON() { gettimeofday(&tv, &tz); now = tv.tv_sec; - + StaticJsonDocument<200> doc; doc["timestamp"] = (long)now; doc["success"] = true; - + // Zusätzliche Zeitinformationen gmtime_r(&now, &timeinfo); char timeStr[64]; @@ -41,7 +42,7 @@ String getCurrentTimeJSON() { doc["hour"] = timeinfo.tm_hour; doc["minute"] = timeinfo.tm_min; doc["second"] = timeinfo.tm_sec; - + String response; serializeJson(doc, response); return response; @@ -51,7 +52,7 @@ bool setSystemTime(long timestamp) { struct timeval tv; tv.tv_sec = timestamp; tv.tv_usec = 0; - + if (settimeofday(&tv, NULL) == 0) { Serial.println("Zeit erfolgreich gesetzt: " + String(timestamp)); return true; @@ -61,151 +62,157 @@ bool setSystemTime(long timestamp) { } } -void setupTimeAPI(AsyncWebServer& server) { +void setupTimeAPI(AsyncWebServer &server) { - //setupRTC(); + // setupRTC(); -// API-Endpunkt: Aktuelle Zeit abrufen -server.on("/api/time", HTTP_GET, [](AsyncWebServerRequest *request){ - String response = getCurrentTimeJSON(); - request->send(200, "application/json", response); -}); + // API-Endpunkt: Aktuelle Zeit abrufen + server.on("/api/time", HTTP_GET, [](AsyncWebServerRequest *request) { + String response = getCurrentTimeJSON(); + request->send(200, "application/json", response); + }); -// API-Endpunkt: Zeit setzen -server.on("/api/set-time", HTTP_POST, [](AsyncWebServerRequest *request){ - StaticJsonDocument<100> doc; - - if (request->hasParam("timestamp", true)) { - String timestampStr = request->getParam("timestamp", true)->value(); - long timestamp = timestampStr.toInt(); - - if (timestamp > 0) { - bool success = setSystemTime(timestamp); - - doc["success"] = success; - if (success) { - doc["message"] = "Zeit erfolgreich gesetzt"; - doc["timestamp"] = timestamp; + // API-Endpunkt: Zeit setzen + server.on("/api/set-time", HTTP_POST, [](AsyncWebServerRequest *request) { + StaticJsonDocument<100> doc; + + if (request->hasParam("timestamp", true)) { + String timestampStr = request->getParam("timestamp", true)->value(); + long timestamp = timestampStr.toInt(); + + if (timestamp > 0) { + bool success = setSystemTime(timestamp); + + doc["success"] = success; + if (success) { + doc["message"] = "Zeit erfolgreich gesetzt"; + doc["timestamp"] = timestamp; + } else { + doc["message"] = "Fehler beim Setzen der Zeit"; + } } else { - doc["message"] = "Fehler beim Setzen der Zeit"; + doc["success"] = false; + doc["message"] = "Ungültiger Timestamp"; } } else { doc["success"] = false; - doc["message"] = "Ungültiger Timestamp"; + doc["message"] = "Timestamp-Parameter fehlt"; } - } else { - doc["success"] = false; - doc["message"] = "Timestamp-Parameter fehlt"; - } - - String response; - serializeJson(doc, response); - request->send(200, "application/json", response); -}); -// Alternative Implementierung für manuelle Datum/Zeit-Eingabe -server.on("/api/set-datetime", HTTP_POST, [](AsyncWebServerRequest *request){ - StaticJsonDocument<150> doc; - - if (request->hasParam("year", true) && - request->hasParam("month", true) && - request->hasParam("day", true) && - request->hasParam("hour", true) && - request->hasParam("minute", true) && - request->hasParam("second", true)) { - - struct tm timeinfo; - timeinfo.tm_year = request->getParam("year", true)->value().toInt() - 1900; - timeinfo.tm_mon = request->getParam("month", true)->value().toInt() - 1; - timeinfo.tm_mday = request->getParam("day", true)->value().toInt(); - timeinfo.tm_hour = request->getParam("hour", true)->value().toInt(); - timeinfo.tm_min = request->getParam("minute", true)->value().toInt(); - timeinfo.tm_sec = request->getParam("second", true)->value().toInt(); - - time_t timestamp = mktime(&timeinfo); - - if (timestamp != -1) { - bool success = setSystemTime(timestamp); - - doc["success"] = success; - if (success) { - doc["message"] = "Zeit erfolgreich gesetzt"; - doc["timestamp"] = (long)timestamp; + String response; + serializeJson(doc, response); + request->send(200, "application/json", response); + }); + + // Alternative Implementierung für manuelle Datum/Zeit-Eingabe + server.on("/api/set-datetime", HTTP_POST, [](AsyncWebServerRequest *request) { + StaticJsonDocument<150> doc; + + if (request->hasParam("year", true) && request->hasParam("month", true) && + request->hasParam("day", true) && request->hasParam("hour", true) && + request->hasParam("minute", true) && + request->hasParam("second", true)) { + + struct tm timeinfo; + timeinfo.tm_year = + request->getParam("year", true)->value().toInt() - 1900; + timeinfo.tm_mon = request->getParam("month", true)->value().toInt() - 1; + timeinfo.tm_mday = request->getParam("day", true)->value().toInt(); + timeinfo.tm_hour = request->getParam("hour", true)->value().toInt(); + timeinfo.tm_min = request->getParam("minute", true)->value().toInt(); + timeinfo.tm_sec = request->getParam("second", true)->value().toInt(); + + time_t timestamp = mktime(&timeinfo); + + if (timestamp != -1) { + bool success = setSystemTime(timestamp); + + doc["success"] = success; + if (success) { + doc["message"] = "Zeit erfolgreich gesetzt"; + doc["timestamp"] = (long)timestamp; + } else { + doc["message"] = "Fehler beim Setzen der Zeit"; + } } else { - doc["message"] = "Fehler beim Setzen der Zeit"; + doc["success"] = false; + doc["message"] = "Ungültiges Datum/Zeit"; } } else { doc["success"] = false; - doc["message"] = "Ungültiges Datum/Zeit"; + doc["message"] = "Datum/Zeit-Parameter fehlen"; } - } else { - doc["success"] = false; - doc["message"] = "Datum/Zeit-Parameter fehlen"; - } - - String response; - serializeJson(doc, response); - request->send(200, "application/json", response); -}); -// Erweiterte Zeit-Informationen (optional) -server.on("/api/time/info", HTTP_GET, [](AsyncWebServerRequest *request){ - gettimeofday(&tv, &tz); - now = tv.tv_sec; - gmtime_r(&now, &timeinfo); - - StaticJsonDocument<400> doc; - doc["timestamp"] = (long)now; - doc["uptime"] = millis(); - - // Formatierte Zeitstrings - char buffer[64]; - strftime(buffer, sizeof(buffer), "%Y-%m-%d", &timeinfo); - doc["date"] = String(buffer); - - strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeinfo); - doc["time"] = String(buffer); - - strftime(buffer, sizeof(buffer), "%A", &timeinfo); - doc["weekday"] = String(buffer); - - strftime(buffer, sizeof(buffer), "%B", &timeinfo); - doc["month_name"] = String(buffer); - - // Zusätzliche Infos - doc["day_of_year"] = timeinfo.tm_yday + 1; - doc["week_of_year"] = (timeinfo.tm_yday + 7 - timeinfo.tm_wday) / 7; - doc["is_dst"] = timeinfo.tm_isdst; - - String response; - serializeJson(doc, response); - request->send(200, "application/json", response); -}); -Serial.println("Zeit-API initialisiert"); + String response; + serializeJson(doc, response); + request->send(200, "application/json", response); + }); + + // Erweiterte Zeit-Informationen (optional) + server.on("/api/time/info", HTTP_GET, [](AsyncWebServerRequest *request) { + gettimeofday(&tv, &tz); + now = tv.tv_sec; + gmtime_r(&now, &timeinfo); + + StaticJsonDocument<400> doc; + doc["timestamp"] = (long)now; + doc["uptime"] = millis(); + + // Formatierte Zeitstrings + char buffer[64]; + strftime(buffer, sizeof(buffer), "%Y-%m-%d", &timeinfo); + doc["date"] = String(buffer); + + strftime(buffer, sizeof(buffer), "%H:%M:%S", &timeinfo); + doc["time"] = String(buffer); + + strftime(buffer, sizeof(buffer), "%A", &timeinfo); + doc["weekday"] = String(buffer); + + strftime(buffer, sizeof(buffer), "%B", &timeinfo); + doc["month_name"] = String(buffer); + + // Zusätzliche Infos + doc["day_of_year"] = timeinfo.tm_yday + 1; + doc["week_of_year"] = (timeinfo.tm_yday + 7 - timeinfo.tm_wday) / 7; + doc["is_dst"] = timeinfo.tm_isdst; + + String response; + serializeJson(doc, response); + request->send(200, "application/json", response); + }); + Serial.println("Zeit-API initialisiert"); } // Hilfsfunktion: Zeit-Validierung -bool isValidDateTime(int year, int month, int day, int hour, int minute, int second) { - if (year < 2020 || year > 2099) return false; - if (month < 1 || month > 12) return false; - if (day < 1 || day > 31) return false; - if (hour < 0 || hour > 23) return false; - if (minute < 0 || minute > 59) return false; - if (second < 0 || second > 59) return false; - +bool isValidDateTime(int year, int month, int day, int hour, int minute, + int second) { + if (year < 2020 || year > 2099) + return false; + if (month < 1 || month > 12) + return false; + if (day < 1 || day > 31) + return false; + if (hour < 0 || hour > 23) + return false; + if (minute < 0 || minute > 59) + return false; + if (second < 0 || second > 59) + return false; + // Erweiterte Validierung für Monatstage int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - + // Schaltjahr-Prüfung if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) { daysInMonth[1] = 29; } - + return day <= daysInMonth[month - 1]; } uint64_t getCurrentTimestampMs() { - struct timeval tv; - gettimeofday(&tv, NULL); - return (uint64_t)tv.tv_sec * 1000LL + (uint64_t)tv.tv_usec / 1000LL; + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000LL + (uint64_t)tv.tv_usec / 1000LL; } diff --git a/src/webserverrouter.h b/src/webserverrouter.h index a586f1d..1132d5f 100644 --- a/src/webserverrouter.h +++ b/src/webserverrouter.h @@ -1,55 +1,57 @@ #pragma once #include -void sendMQTTMessage(const char * topic, const char * message); +void sendMQTTMessage(const char *topic, const char *message); #include "master.h" -#include -#include #include +#include +#include #include #include + +#include "communication.h" #include #include -#include "communication.h" + AsyncWebServer server(80); AsyncWebSocket ws("/ws"); -void setupRoutes(){ - // Web Server Routes +void setupRoutes() { + // Web Server Routes - // Attach WebSocket to the server - server.addHandler(&ws); + // Attach WebSocket to the server + server.addHandler(&ws); - server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/index.html", "text/html"); }); - - server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request){ + + server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(SPIFFS, "/settings.html", "text/html"); }); server.on("/rfid", HTTP_GET, [](AsyncWebServerRequest *request) { - request->send(SPIFFS, "/rfid.html", "text/html"); - }); + request->send(SPIFFS, "/rfid.html", "text/html"); + }); server.on("/firmware.bin", HTTP_GET, [](AsyncWebServerRequest *request) { if (SPIFFS.exists("/firmware.bin")) { - request->send(SPIFFS, "/firmware.bin", "application/octet-stream"); - Serial.println("Firmware file served: /firmware.bin"); + request->send(SPIFFS, "/firmware.bin", "application/octet-stream"); + Serial.println("Firmware file served: /firmware.bin"); } else { - request->send(404, "application/json", "{\"error\":\"File not found\"}"); - Serial.println("Firmware file not found: /firmware.bin"); + request->send(404, "application/json", "{\"error\":\"File not found\"}"); + Serial.println("Firmware file not found: /firmware.bin"); } -}); - - server.on("/api/data", HTTP_GET, [](AsyncWebServerRequest *request){ + }); + + server.on("/api/data", HTTP_GET, [](AsyncWebServerRequest *request) { request->send(200, "application/json", getTimerDataJSON()); }); - - server.on("/api/reset-best", HTTP_POST, [](AsyncWebServerRequest *request){ + + server.on("/api/reset-best", HTTP_POST, [](AsyncWebServerRequest *request) { Serial.println("/api/reset-best called"); timerData.bestTime1 = 0; timerData.bestTime2 = 0; @@ -61,38 +63,39 @@ void setupRoutes(){ request->send(200, "application/json", result); }); - server.on("/api/unlearn-button", HTTP_POST, [](AsyncWebServerRequest *request){ - Serial.println("/api/unlearn-button called"); - unlearnButton(); - request->send(200, "application/json", "{\"success\":true}"); + server.on("/api/unlearn-button", HTTP_POST, + [](AsyncWebServerRequest *request) { + Serial.println("/api/unlearn-button called"); + unlearnButton(); + request->send(200, "application/json", "{\"success\":true}"); + }); -}); - - - server.on("/api/set-max-time", HTTP_POST, [](AsyncWebServerRequest *request){ + server.on("/api/set-max-time", HTTP_POST, [](AsyncWebServerRequest *request) { Serial.println("/api/set-max-time called"); bool changed = false; if (request->hasParam("maxTime", true)) { - maxTimeBeforeReset = request->getParam("maxTime", true)->value().toInt() * 1000; - changed = true; + maxTimeBeforeReset = + request->getParam("maxTime", true)->value().toInt() * 1000; + changed = true; } if (request->hasParam("maxTimeDisplay", true)) { - maxTimeDisplay = request->getParam("maxTimeDisplay", true)->value().toInt() * 1000; - changed = true; + maxTimeDisplay = + request->getParam("maxTimeDisplay", true)->value().toInt() * 1000; + changed = true; } if (changed) { - saveSettings(); - DynamicJsonDocument doc(32); - doc["success"] = true; - String result; - serializeJson(doc, result); - request->send(200, "application/json", result); + saveSettings(); + DynamicJsonDocument doc(32); + doc["success"] = true; + String result; + serializeJson(doc, result); + request->send(200, "application/json", result); } else { - request->send(400, "application/json", "{\"success\":false}"); - } + request->send(400, "application/json", "{\"success\":false}"); + } }); - - server.on("/api/get-settings", HTTP_GET, [](AsyncWebServerRequest *request){ + + server.on("/api/get-settings", HTTP_GET, [](AsyncWebServerRequest *request) { Serial.println("/api/get-settings called"); DynamicJsonDocument doc(256); doc["maxTime"] = maxTimeBeforeReset / 1000; @@ -102,31 +105,33 @@ void setupRoutes(){ request->send(200, "application/json", result); }); - server.on("/api/start-learning", HTTP_POST, [](AsyncWebServerRequest *request){ - Serial.println("/api/start-learning called"); - learningMode = true; - learningStep = 0; - DynamicJsonDocument doc(64); - doc["success"] = true; - String result; - serializeJson(doc, result); - Serial.println("Learning mode started"); - request->send(200, "application/json", result); -}); + server.on("/api/start-learning", HTTP_POST, + [](AsyncWebServerRequest *request) { + Serial.println("/api/start-learning called"); + learningMode = true; + learningStep = 0; + DynamicJsonDocument doc(64); + doc["success"] = true; + String result; + serializeJson(doc, result); + Serial.println("Learning mode started"); + request->send(200, "application/json", result); + }); - server.on("/api/stop-learning", HTTP_POST, [](AsyncWebServerRequest *request){ - Serial.println("/api/stop-learning called"); - learningMode = false; - learningStep = 0; - DynamicJsonDocument doc(64); - doc["success"] = true; - String result; - serializeJson(doc, result); - Serial.println("Learning mode stopped"); - request->send(200, "application/json", result); - }); + server.on("/api/stop-learning", HTTP_POST, + [](AsyncWebServerRequest *request) { + Serial.println("/api/stop-learning called"); + learningMode = false; + learningStep = 0; + DynamicJsonDocument doc(64); + doc["success"] = true; + String result; + serializeJson(doc, result); + Serial.println("Learning mode stopped"); + request->send(200, "application/json", result); + }); - server.on("/api/learn/status", HTTP_GET, [](AsyncWebServerRequest *request){ + server.on("/api/learn/status", HTTP_GET, [](AsyncWebServerRequest *request) { DynamicJsonDocument doc(256); doc["active"] = learningMode; doc["step"] = learningStep; @@ -135,18 +140,19 @@ void setupRoutes(){ request->send(200, "application/json", response); }); - server.on("/api/buttons/status", HTTP_GET, [](AsyncWebServerRequest *request){ - DynamicJsonDocument doc(128); - doc["lane1Start"] = buttonConfigs.start1.isAssigned; - doc["lane1Stop"] = buttonConfigs.stop1.isAssigned; - doc["lane2Start"] = buttonConfigs.start2.isAssigned; - doc["lane2Stop"] = buttonConfigs.stop2.isAssigned; - String result; - serializeJson(doc, result); - request->send(200, "application/json", result); - }); + server.on("/api/buttons/status", HTTP_GET, + [](AsyncWebServerRequest *request) { + DynamicJsonDocument doc(128); + doc["lane1Start"] = buttonConfigs.start1.isAssigned; + doc["lane1Stop"] = buttonConfigs.stop1.isAssigned; + doc["lane2Start"] = buttonConfigs.start2.isAssigned; + doc["lane2Stop"] = buttonConfigs.stop2.isAssigned; + String result; + serializeJson(doc, result); + request->send(200, "application/json", result); + }); - server.on("/api/info", HTTP_GET, [](AsyncWebServerRequest *request){ + server.on("/api/info", HTTP_GET, [](AsyncWebServerRequest *request) { DynamicJsonDocument doc(256); // IP address @@ -158,7 +164,8 @@ void setupRoutes(){ uint8_t mac[6]; esp_wifi_get_mac(WIFI_IF_STA, mac); char macStr[18]; - sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], + mac[3], mac[4], mac[5]); doc["mac"] = macStr; // Free memory @@ -166,55 +173,59 @@ void setupRoutes(){ // Connected buttons (count assigned) int connected = 0; - if (buttonConfigs.start1.isAssigned) connected++; - if (buttonConfigs.stop1.isAssigned) connected++; - if (buttonConfigs.start2.isAssigned) connected++; - if (buttonConfigs.stop2.isAssigned) connected++; + if (buttonConfigs.start1.isAssigned) + connected++; + if (buttonConfigs.stop1.isAssigned) + connected++; + if (buttonConfigs.start2.isAssigned) + connected++; + if (buttonConfigs.stop2.isAssigned) + connected++; doc["connectedButtons"] = connected; doc["valid"] = checkLicence() > 0 ? "Ja" : "Nein"; - doc["tier"] = checkLicence() ; - + doc["tier"] = checkLicence(); String result; serializeJson(doc, result); request->send(200, "application/json", result); -}); + }); // Setze WLAN-Name und Passwort (POST) server.on("/api/set-wifi", HTTP_POST, [](AsyncWebServerRequest *request) { Serial.println("/api/set-wifi called"); String ssid, password; if (request->hasParam("ssid", true)) { - ssid = request->getParam("ssid", true)->value(); + ssid = request->getParam("ssid", true)->value(); } if (request->hasParam("password", true)) { - password = request->getParam("password", true)->value(); + password = request->getParam("password", true)->value(); } if (ssid.length() > 0) { - // Speicher freigeben, bevor neue Werte zugewiesen werden - free(ssidSTA); - free(passwordSTA); + // Speicher freigeben, bevor neue Werte zugewiesen werden + free(ssidSTA); + free(passwordSTA); - // Neue Werte zuweisen - ssidSTA = strdup(ssid.c_str()); - passwordSTA = strdup(password.c_str()); + // Neue Werte zuweisen + ssidSTA = strdup(ssid.c_str()); + passwordSTA = strdup(password.c_str()); - saveWifiSettings(); + saveWifiSettings(); - // Rückmeldung - DynamicJsonDocument doc(64); - doc["success"] = true; - String result; - serializeJson(doc, result); - request->send(200, "application/json", result); + // Rückmeldung + DynamicJsonDocument doc(64); + doc["success"] = true; + String result; + serializeJson(doc, result); + request->send(200, "application/json", result); } else { - request->send(400, "application/json", "{\"success\":false,\"error\":\"SSID fehlt\"}"); + request->send(400, "application/json", + "{\"success\":false,\"error\":\"SSID fehlt\"}"); } -}); + }); // Liefert aktuelle WLAN-Einstellungen (GET) - server.on("/api/get-wifi", HTTP_GET, [](AsyncWebServerRequest *request){ + server.on("/api/get-wifi", HTTP_GET, [](AsyncWebServerRequest *request) { DynamicJsonDocument doc(128); doc["ssid"] = ssidSTA ? ssidSTA : ""; doc["password"] = passwordSTA ? passwordSTA : ""; @@ -223,31 +234,30 @@ void setupRoutes(){ request->send(200, "application/json", result); }); -server.on("/api/set-location", HTTP_POST, [](AsyncWebServerRequest *request) { + server.on("/api/set-location", HTTP_POST, [](AsyncWebServerRequest *request) { Serial.println("/api/set-location called"); String id, name; if (request->hasParam("id", true)) { - id = request->getParam("id", true)->value(); + id = request->getParam("id", true)->value(); } if (request->hasParam("name", true)) { - name = request->getParam("name", true)->value(); + name = request->getParam("name", true)->value(); } - masterlocation = name; - - saveLocationSettings(); + masterlocation = name; - // Rückmeldung - DynamicJsonDocument doc(64); - doc["success"] = true; - String result; - serializeJson(doc, result); - request->send(200, "application/json", result); - -}); + saveLocationSettings(); - server.on("/api/get-location", HTTP_GET, [](AsyncWebServerRequest *request){ + // Rückmeldung + DynamicJsonDocument doc(64); + doc["success"] = true; + String result; + serializeJson(doc, result); + request->send(200, "application/json", result); + }); + + server.on("/api/get-location", HTTP_GET, [](AsyncWebServerRequest *request) { DynamicJsonDocument doc(128); loadLocationSettings(); doc["locationid"] = masterlocation ? masterlocation : ""; @@ -256,41 +266,39 @@ server.on("/api/set-location", HTTP_POST, [](AsyncWebServerRequest *request) { request->send(200, "application/json", result); }); - -server.on("/api/updateButtons", HTTP_GET, [](AsyncWebServerRequest *request){ + server.on("/api/updateButtons", HTTP_GET, [](AsyncWebServerRequest *request) { Serial.println("/api/updateButtons called"); // MQTT publish on aquacross/update/flag with raw payload "1" sendMQTTMessage("aquacross/update/flag", "1"); Serial.println("MQTT published: aquacross/update/flag -> 1"); request->send(200, "application/json", "{\"success\":true}"); -}); + }); - -// Statische Dateien + // Statische Dateien server.serveStatic("/", SPIFFS, "/"); server.begin(); Serial.println("Web Server gestartet"); - } void setupWebSocket() { - ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { - if (type == WS_EVT_CONNECT) { - Serial.printf("WebSocket client connected: %u\n", client->id()); - } else if (type == WS_EVT_DISCONNECT) { - Serial.printf("WebSocket client disconnected: %u\n", client->id()); - } else if (type == WS_EVT_DATA) { - // Handle incoming WebSocket messages if needed - Serial.printf("WebSocket message received: %s\n", (char *)data); - } - }); + ws.onEvent([](AsyncWebSocket *server, AsyncWebSocketClient *client, + AwsEventType type, void *arg, uint8_t *data, size_t len) { + if (type == WS_EVT_CONNECT) { + Serial.printf("WebSocket client connected: %u\n", client->id()); + } else if (type == WS_EVT_DISCONNECT) { + Serial.printf("WebSocket client disconnected: %u\n", client->id()); + } else if (type == WS_EVT_DATA) { + // Handle incoming WebSocket messages if needed + Serial.printf("WebSocket message received: %s\n", (char *)data); + } + }); } void pushUpdateToFrontend(const String &message) { - ws.textAll(message); // Send the message to all connected clients + ws.textAll(message); // Send the message to all connected clients } void loopWebSocket() { - ws.cleanupClients(); // Clean up disconnected clients + ws.cleanupClients(); // Clean up disconnected clients } diff --git a/src/wificlass.h b/src/wificlass.h index 4946a21..36d0b77 100644 --- a/src/wificlass.h +++ b/src/wificlass.h @@ -1,13 +1,15 @@ #pragma once #include -#include -#include -#include -#include #include // <-- mDNS hinzufügen +#include +#include +#include +#include + -#include "master.h" #include "licenceing.h" +#include "master.h" + String uniqueSSID; @@ -17,84 +19,79 @@ String getUniqueSSID(); void setupWifi() { - uniqueSSID = getUniqueSSID(); - ssidAP = uniqueSSID.c_str(); - if (ssidSTA == nullptr || passwordSTA == nullptr || String(ssidSTA).isEmpty() || String(passwordSTA).isEmpty() ) { - Serial.println("Fehler: ssidSTA oder passwordSTA ist null!"); - WiFi.mode(WIFI_MODE_AP); - WiFi.softAP(ssidAP, passwordAP); + uniqueSSID = getUniqueSSID(); + ssidAP = uniqueSSID.c_str(); + if (ssidSTA == nullptr || passwordSTA == nullptr || + String(ssidSTA).isEmpty() || String(passwordSTA).isEmpty()) { + Serial.println("Fehler: ssidSTA oder passwordSTA ist null!"); + WiFi.mode(WIFI_MODE_AP); + WiFi.softAP(ssidAP, passwordAP); + } else { + + WiFi.mode(WIFI_MODE_APSTA); + WiFi.begin(ssidSTA, passwordSTA); + WiFi.softAP(ssidAP, passwordAP); + + // Add timeout for WiFi connection + unsigned long startAttemptTime = millis(); + + while (WiFi.status() != WL_CONNECTED && + millis() - startAttemptTime < 10000) { // 10 seconds timeout + delay(500); + Serial.print("."); } - else - { - - WiFi.mode(WIFI_MODE_APSTA); - WiFi.begin(ssidSTA, passwordSTA); - WiFi.softAP(ssidAP, passwordAP); - - // Add timeout for WiFi connection - unsigned long startAttemptTime = millis(); - - while (WiFi.status() != WL_CONNECTED && - millis() - startAttemptTime < 10000) { // 10 seconds timeout - delay(500); - Serial.print("."); - - } - if (WiFi.status() != WL_CONNECTED) { - Serial.println("Fehler: Verbindung zum WLAN fehlgeschlagen!"); - Serial.println("Starte Access Point..."); - WiFi.mode(WIFI_MODE_AP); - WiFi.softAP(ssidAP, passwordAP); - } - else { - Serial.println("Erfolgreich mit WLAN verbunden!"); - Serial.print("IP Adresse: "); - Serial.println(WiFi.localIP()); - + if (WiFi.status() != WL_CONNECTED) { + Serial.println("Fehler: Verbindung zum WLAN fehlgeschlagen!"); + Serial.println("Starte Access Point..."); + WiFi.mode(WIFI_MODE_AP); + WiFi.softAP(ssidAP, passwordAP); + } else { + Serial.println("Erfolgreich mit WLAN verbunden!"); + Serial.print("IP Adresse: "); + Serial.println(WiFi.localIP()); } - - //Only wait for connection if ssidSTA and passwordSTA are set + // Only wait for connection if ssidSTA and passwordSTA are set Serial.println("WiFi AP gestartet"); Serial.print("SSID: "); Serial.println(WiFi.softAPSSID()); Serial.print("IP Adresse: "); Serial.println(WiFi.softAPIP()); - Serial.println("PrettyOTA can be accessed at: http://" + WiFi.softAPIP().toString() + "/update"); + Serial.println("PrettyOTA can be accessed at: http://" + + WiFi.softAPIP().toString() + "/update"); // mDNS starten if (MDNS.begin("aquacross-timer")) { // z.B. http://aquacross-timer.local/ - Serial.println("mDNS responder gestartet: http://aquacross-timer.local/"); + Serial.println("mDNS responder gestartet: http://aquacross-timer.local/"); } else { - Serial.println("Fehler beim Starten von mDNS!"); - } + Serial.println("Fehler beim Starten von mDNS!"); } + } } void setupOTA(AsyncWebServer *server) { - // Initialize PrettyOTA - OTAUpdates.Begin(server); + // Initialize PrettyOTA + OTAUpdates.Begin(server); - // Set unique Hardware-ID for your hardware/board - OTAUpdates.SetHardwareID("AquaCross-Master"); - - // Set firmware version to 1.0.0 - OTAUpdates.SetAppVersion(firmwareversion); + // Set unique Hardware-ID for your hardware/board + OTAUpdates.SetHardwareID("AquaCross-Master"); - // Set current build time and date - PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE(); + // Set firmware version to 1.0.0 + OTAUpdates.SetAppVersion(firmwareversion); + + // Set current build time and date + PRETTY_OTA_SET_CURRENT_BUILD_TIME_AND_DATE(); } String getUniqueSSID() { - uint8_t mac[6]; - esp_wifi_get_mac(WIFI_IF_STA, mac); - - // Create a 5-character string from last 3 bytes of MAC - char uniqueId[6]; - snprintf(uniqueId, sizeof(uniqueId), "%02X%03X", mac[4], mac[5]); - - return String("AquaCross-") + String(uniqueId); + uint8_t mac[6]; + esp_wifi_get_mac(WIFI_IF_STA, mac); + + // Create a 5-character string from last 3 bytes of MAC + char uniqueId[6]; + snprintf(uniqueId, sizeof(uniqueId), "%02X%03X", mac[4], mac[5]); + + return String("AquaCross-") + String(uniqueId); } - // WiFi als Access Point