RFID message ins backend geht, websocket fürs frontend
This commit is contained in:
@@ -150,6 +150,35 @@ html {
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.swimmer-name {
|
||||||
|
font-size: clamp(1.5rem, 3.5vw, 2.2rem);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: clamp(15px, 2vh, 25px);
|
||||||
|
padding: clamp(8px, 1.5vh, 12px) clamp(12px, 2vw, 18px);
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 15px;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
color: #fff;
|
||||||
|
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.4);
|
||||||
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
animation: fadeIn 0.5s ease-in;
|
||||||
|
text-align: center;
|
||||||
|
word-wrap: break-word;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.time-display {
|
.time-display {
|
||||||
font-size: clamp(3rem, 9vw, 10rem);
|
font-size: clamp(3rem, 9vw, 10rem);
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -280,6 +309,11 @@ html {
|
|||||||
body {
|
body {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.swimmer-name {
|
||||||
|
font-size: clamp(1.2rem, 4vw, 1.8rem);
|
||||||
|
margin-bottom: clamp(10px, 1.5vh, 20px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
@@ -299,4 +333,9 @@ html {
|
|||||||
.timer-container {
|
.timer-container {
|
||||||
padding: 0 2vw;
|
padding: 0 2vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.swimmer-name {
|
||||||
|
font-size: clamp(1rem, 4vw, 1.5rem);
|
||||||
|
padding: clamp(6px, 1vh, 10px) clamp(8px, 1.5vw, 12px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="stylesheet" href="index.css" />
|
<link rel="stylesheet" href="index.css" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
||||||
|
|
||||||
<title>NinjaCross Timer</title>
|
<title>NinjaCross Timer</title>
|
||||||
|
|
||||||
@@ -29,12 +29,14 @@
|
|||||||
|
|
||||||
<div class="timer-container">
|
<div class="timer-container">
|
||||||
<div class="lane">
|
<div class="lane">
|
||||||
|
<div id="name1" class="swimmer-name" style="display: none;"></div>
|
||||||
<h2>🏊♀️ Bahn 1</h2>
|
<h2>🏊♀️ Bahn 1</h2>
|
||||||
<div id="time1" class="time-display">00.00</div>
|
<div id="time1" class="time-display">00.00</div>
|
||||||
<div id="status1" class="status ready">Bereit</div>
|
<div id="status1" class="status ready">Bereit</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="lane">
|
<div class="lane">
|
||||||
|
<div id="name2" class="swimmer-name" style="display: none;"></div>
|
||||||
<h2>🏊♂️ Bahn 2</h2>
|
<h2>🏊♂️ Bahn 2</h2>
|
||||||
<div id="time2" class="time-display">00.00</div>
|
<div id="time2" class="time-display">00.00</div>
|
||||||
<div id="status2" class="status ready">Bereit</div>
|
<div id="status2" class="status ready">Bereit</div>
|
||||||
@@ -64,6 +66,37 @@
|
|||||||
let lastSync = Date.now();
|
let lastSync = Date.now();
|
||||||
let learningMode = false;
|
let learningMode = false;
|
||||||
let learningButton = "";
|
let learningButton = "";
|
||||||
|
let name1 = "";
|
||||||
|
let name2 = "";
|
||||||
|
const ws = new WebSocket(`ws://${window.location.host}/ws`);
|
||||||
|
|
||||||
|
// Handle WebSocket events
|
||||||
|
ws.onopen = () => {
|
||||||
|
console.log("WebSocket connected");
|
||||||
|
};
|
||||||
|
ws.onclose = () => {
|
||||||
|
console.log("WebSocket disconnected");
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onmessage = (event) => {
|
||||||
|
console.log("WebSocket message received:", event.data);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
|
||||||
|
if (data.firstname && data.lastname && data.lane) {
|
||||||
|
if (data.lane === "start1") {
|
||||||
|
name1 = `${data.firstname} ${data.lastname}`;
|
||||||
|
} else if (data.lane === "start2") {
|
||||||
|
name2 = `${data.firstname} ${data.lastname}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDisplay();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error processing WebSocket message:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function formatTime(seconds) {
|
function formatTime(seconds) {
|
||||||
if (seconds === 0) return "00.00";
|
if (seconds === 0) return "00.00";
|
||||||
@@ -83,7 +116,6 @@
|
|||||||
display2 += (now - lastSync) / 1000;
|
display2 += (now - lastSync) / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
document.getElementById("time1").textContent = formatTime(display1);
|
document.getElementById("time1").textContent = formatTime(display1);
|
||||||
const s1 = document.getElementById("status1");
|
const s1 = document.getElementById("status1");
|
||||||
s1.className = `status ${status1}`;
|
s1.className = `status ${status1}`;
|
||||||
@@ -109,6 +141,24 @@
|
|||||||
document.getElementById("best2").textContent =
|
document.getElementById("best2").textContent =
|
||||||
best2 > 0 ? formatTime(best2) + "s" : "--.-";
|
best2 > 0 ? formatTime(best2) + "s" : "--.-";
|
||||||
|
|
||||||
|
// Namen anzeigen/verstecken - verbesserte Logik
|
||||||
|
const name1Element = document.getElementById("name1");
|
||||||
|
const name2Element = document.getElementById("name2");
|
||||||
|
|
||||||
|
if (name1 && name1.trim() !== "") {
|
||||||
|
name1Element.textContent = name1;
|
||||||
|
name1Element.style.display = "block";
|
||||||
|
} else {
|
||||||
|
name1Element.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name2 && name2.trim() !== "") {
|
||||||
|
name2Element.textContent = name2;
|
||||||
|
name2Element.style.display = "block";
|
||||||
|
} else {
|
||||||
|
name2Element.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
// Lernmodus
|
// Lernmodus
|
||||||
const learningDisplay = document.getElementById("learning-display");
|
const learningDisplay = document.getElementById("learning-display");
|
||||||
if (learningMode) {
|
if (learningMode) {
|
||||||
@@ -139,7 +189,7 @@
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync with backend every 2 seconds
|
// Sync with backend every 1 second
|
||||||
setInterval(syncFromBackend, 1000);
|
setInterval(syncFromBackend, 1000);
|
||||||
|
|
||||||
// Smooth update every 50ms
|
// Smooth update every 50ms
|
||||||
|
|||||||
BIN
data/pictures/favicon.ico
Normal file
BIN
data/pictures/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="rfid.css" />
|
<link rel="stylesheet" href="rfid.css" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
||||||
|
|
||||||
<title>RFID Daten Eingabe</title>
|
<title>RFID Daten Eingabe</title>
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<!-- Meta Tags -->
|
<!-- Meta Tags -->
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
||||||
|
|
||||||
<!-- Stylesheets -->
|
<!-- Stylesheets -->
|
||||||
<link rel="stylesheet" href="settings.css" />
|
<link rel="stylesheet" href="settings.css" />
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <databasebackend.h>
|
||||||
|
#include <webserverrouter.h>
|
||||||
|
|
||||||
struct TimestampData {
|
struct TimestampData {
|
||||||
uint64_t lastMessageTimestamp; // Timestamp from the device
|
uint64_t lastMessageTimestamp; // Timestamp from the device
|
||||||
@@ -35,52 +37,6 @@ typedef struct {
|
|||||||
|
|
||||||
PicoMQTT::Server mqtt;
|
PicoMQTT::Server mqtt;
|
||||||
|
|
||||||
void processHeartbeat(const char* topic, const char* payload) {
|
|
||||||
String macAddress = String(topic).substring(15);
|
|
||||||
|
|
||||||
StaticJsonDocument<200> doc;
|
|
||||||
DeserializationError error = deserializeJson(doc, payload);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
Serial.printf("JSON parsing failed for MAC %s: %s\n", macAddress.c_str(), error.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t messageTimestamp = doc["timestamp"] | 0;
|
|
||||||
uint64_t currentLocalTime = getCurrentTimestampMs();
|
|
||||||
|
|
||||||
// Update timestamps for current device
|
|
||||||
if (deviceTimestamps.count(macAddress) > 0) {
|
|
||||||
TimestampData& data = deviceTimestamps[macAddress];
|
|
||||||
uint64_t messageDiff = messageTimestamp - data.lastMessageTimestamp;
|
|
||||||
uint64_t localDiff = currentLocalTime - data.lastLocalTimestamp;
|
|
||||||
data.drift = localDiff - messageDiff;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate drift relative to all other devices
|
|
||||||
Serial.printf("\nDrift analysis for device %s:\n", macAddress.c_str());
|
|
||||||
Serial.println("----------------------------------------");
|
|
||||||
|
|
||||||
for (const auto& device : deviceTimestamps) {
|
|
||||||
if (device.first != macAddress) { // Skip comparing to self
|
|
||||||
int64_t timeDiff = messageTimestamp - device.second.lastMessageTimestamp;
|
|
||||||
int64_t relativeDrift = timeDiff - (currentLocalTime - device.second.lastLocalTimestamp);
|
|
||||||
|
|
||||||
Serial.printf("Relative to %s:\n", device.first.c_str());
|
|
||||||
Serial.printf(" Time difference: %lld ms\n", timeDiff);
|
|
||||||
Serial.printf(" Relative drift: %lld ms\n", relativeDrift);
|
|
||||||
Serial.println("----------------------------------------");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update stored timestamps for current device
|
|
||||||
deviceTimestamps[macAddress] = {
|
|
||||||
messageTimestamp,
|
|
||||||
currentLocalTime,
|
|
||||||
deviceTimestamps[macAddress].drift
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void readButtonJSON(const char * topic, const char * payload) {
|
void readButtonJSON(const char * topic, const char * payload) {
|
||||||
|
|
||||||
if(strcmp(topic, "aquacross/button/press") == 0){
|
if(strcmp(topic, "aquacross/button/press") == 0){
|
||||||
@@ -133,22 +89,99 @@ void readButtonJSON(const char * topic, const char * payload) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setupMqttServer() {
|
void setupMqttServer() {
|
||||||
|
|
||||||
// Set up the MQTT server with the desired port
|
// Set up the MQTT server with the desired port
|
||||||
// Subscribe to a topic pattern and attach a callback
|
// Subscribe to a topic pattern and attach a callback
|
||||||
mqtt.subscribe("#", [](const char * topic, const char * payload) {
|
mqtt.subscribe("#", [](const char * topic, const char * payload) {
|
||||||
if (strncmp(topic, "heartbeat/alive/", 15) == 0) {
|
//Message received callback
|
||||||
processHeartbeat(topic, payload);
|
//Serial.printf("Received message on topic '%s': %s\n", topic, payload);
|
||||||
} else if (strcmp(topic, "aquacross/button/press") == 0) {
|
if (strcmp(topic, "aquacross/button/press") == 0) {
|
||||||
readButtonJSON(topic, payload);
|
readButtonJSON(topic, payload);
|
||||||
|
} else if (strncmp(topic, "aquacross/button/rfid/", 22) == 0) {
|
||||||
|
readRFIDfromButton(topic, payload);
|
||||||
|
// Handle RFID read messages
|
||||||
|
|
||||||
}
|
}
|
||||||
updateStatusLED(3);
|
updateStatusLED(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the button subscription
|
|
||||||
|
|
||||||
// Start the MQTT server
|
// Start the MQTT server
|
||||||
mqtt.begin();
|
mqtt.begin();
|
||||||
|
|
||||||
@@ -168,6 +201,7 @@ void loopMqttServer() {
|
|||||||
mqtt.publish("sync/time", timeStr);
|
mqtt.publish("sync/time", timeStr);
|
||||||
lastPublish = millis();
|
lastPublish = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendMQTTMessage(const char * topic, const char * message) {
|
void sendMQTTMessage(const char * topic, const char * message) {
|
||||||
@@ -187,3 +221,5 @@ void sendMQTTJSONMessage(const char * topic, const JsonDocument & doc) {
|
|||||||
Serial.printf("Published JSON message to topic '%s': %s\n", topic, jsonString.c_str());
|
Serial.printf("Published JSON message to topic '%s': %s\n", topic, jsonString.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
#include "master.h"
|
#include "master.h"
|
||||||
@@ -17,34 +18,76 @@ bool backendOnline() {
|
|||||||
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN);
|
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN);
|
||||||
|
|
||||||
int httpCode = http.GET();
|
int httpCode = http.GET();
|
||||||
|
bool isOnline = (httpCode == HTTP_CODE_OK);
|
||||||
|
|
||||||
if (httpCode == HTTP_CODE_OK) {
|
if (isOnline) {
|
||||||
return true;
|
|
||||||
Serial.println("Database server connection successful");
|
Serial.println("Database server connection successful");
|
||||||
} else {
|
} else {
|
||||||
return false;
|
|
||||||
Serial.printf("Database server connection failed, error: %d\n", httpCode);
|
Serial.printf("Database server connection failed, error: %d\n", httpCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
http.end();
|
http.end();
|
||||||
|
return isOnline;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool userExists(const String& userId) {
|
struct UserData {
|
||||||
|
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 userData = {"", "", "", 0, false};
|
||||||
|
|
||||||
if (!backendOnline()) {
|
if (!backendOnline()) {
|
||||||
Serial.println("No internet connection, cannot check user existence.");
|
Serial.println("No internet connection, cannot check user.");
|
||||||
return false;
|
return userData;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
http.begin(String(BACKEND_SERVER) + "/api/users/" + userId);
|
http.begin(String(BACKEND_SERVER) + "/api/users/find");
|
||||||
|
http.addHeader("Content-Type", "application/json");
|
||||||
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN);
|
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN);
|
||||||
|
|
||||||
//Post request to check if user exists
|
// Create JSON payload
|
||||||
int httpCode = http.POST("");
|
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<String>();
|
||||||
|
userData.firstname = responseDoc["firstname"].as<String>();
|
||||||
|
userData.lastname = responseDoc["lastname"].as<String>();
|
||||||
|
userData.alter = responseDoc["alter"] | 0;
|
||||||
|
userData.exists = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Serial.printf("User check failed, HTTP code: %d\n", httpCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
http.end();
|
||||||
|
return userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep this for backward compatibility
|
||||||
|
bool userExists(const String& uid) {
|
||||||
|
return checkUser(uid).exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupBackendRoutes(AsyncWebServer& server) {
|
void setupBackendRoutes(AsyncWebServer& server) {
|
||||||
|
|
||||||
server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) {
|
server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
|
|
||||||
DynamicJsonDocument doc(64);
|
DynamicJsonDocument doc(64);
|
||||||
|
|||||||
@@ -163,9 +163,25 @@ void saveWifiSettings() {
|
|||||||
|
|
||||||
void loadWifiSettings() {
|
void loadWifiSettings() {
|
||||||
preferences.begin("wifi", true);
|
preferences.begin("wifi", true);
|
||||||
ssidSTA = preferences.getString("ssid", "").c_str();
|
|
||||||
passwordSTA = preferences.getString("password", "").c_str();
|
// Speicher freigeben, falls bereits zugewiesen
|
||||||
|
if (ssidSTA) {
|
||||||
|
free(ssidSTA);
|
||||||
|
}
|
||||||
|
if (passwordSTA) {
|
||||||
|
free(passwordSTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neue Werte laden und dynamisch zuweisen
|
||||||
|
String ssid = preferences.getString("ssid", "");
|
||||||
|
String password = preferences.getString("password", "");
|
||||||
|
ssidSTA = strdup(ssid.c_str());
|
||||||
|
passwordSTA = strdup(password.c_str());
|
||||||
|
|
||||||
preferences.end();
|
preferences.end();
|
||||||
|
|
||||||
|
// Debug-Ausgabe
|
||||||
|
Serial.printf("WLAN-Einstellungen geladen: SSID=%s, Passwort=%s\n", ssidSTA, passwordSTA);
|
||||||
}
|
}
|
||||||
|
|
||||||
int checkLicence() {
|
int checkLicence() {
|
||||||
@@ -179,7 +195,6 @@ String getTimerDataJSON() {
|
|||||||
DynamicJsonDocument doc(1024);
|
DynamicJsonDocument doc(1024);
|
||||||
|
|
||||||
unsigned long currentTime = millis();
|
unsigned long currentTime = millis();
|
||||||
|
|
||||||
// Bahn 1
|
// Bahn 1
|
||||||
if (timerData.isRunning1) {
|
if (timerData.isRunning1) {
|
||||||
doc["time1"] = (currentTime - timerData.startTime1) / 1000.0;
|
doc["time1"] = (currentTime - timerData.startTime1) / 1000.0;
|
||||||
@@ -234,7 +249,7 @@ void setup() {
|
|||||||
setupTimeAPI(server);
|
setupTimeAPI(server);
|
||||||
setupLicenceAPI(server);
|
setupLicenceAPI(server);
|
||||||
setupDebugAPI(server);
|
setupDebugAPI(server);
|
||||||
setupBackendRoutes(server);
|
setupBackendRoutes(server);// Speichere WLAN-Einstellungen, falls noch nicht vorhanden
|
||||||
|
|
||||||
|
|
||||||
// Gespeicherte Daten laden
|
// Gespeicherte Daten laden
|
||||||
@@ -246,6 +261,7 @@ void setup() {
|
|||||||
setupOTA(&server);
|
setupOTA(&server);
|
||||||
|
|
||||||
setupRoutes();
|
setupRoutes();
|
||||||
|
setupWebSocket();
|
||||||
setupLED();
|
setupLED();
|
||||||
setupMqttServer(); // MQTT Server initialisieren
|
setupMqttServer(); // MQTT Server initialisieren
|
||||||
|
|
||||||
@@ -255,4 +271,5 @@ void setup() {
|
|||||||
void loop() {
|
void loop() {
|
||||||
checkAutoReset();
|
checkAutoReset();
|
||||||
loopMqttServer(); // MQTT Server in der Loop aufrufen
|
loopMqttServer(); // MQTT Server in der Loop aufrufen
|
||||||
|
loopWebSocket();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#pragma once
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <WebServer.h>
|
#include <WebServer.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
@@ -21,10 +22,6 @@ struct User {
|
|||||||
unsigned long timestamp;
|
unsigned long timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Array für Benutzer (max 100 Benutzer)
|
|
||||||
User users[100];
|
|
||||||
int userCount = 0;
|
|
||||||
|
|
||||||
void setupRFID() {
|
void setupRFID() {
|
||||||
|
|
||||||
// SPI und RFID initialisieren
|
// SPI und RFID initialisieren
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "master.h"
|
#include "master.h"
|
||||||
#include <ESPAsyncWebServer.h>
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <AsyncWebSocket.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <SPIFFS.h>
|
#include <SPIFFS.h>
|
||||||
#include <esp_wifi.h>
|
#include <esp_wifi.h>
|
||||||
|
|
||||||
|
|
||||||
#include <buttonassigh.h>
|
#include <buttonassigh.h>
|
||||||
|
#include <wificlass.h>
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
|
AsyncWebSocket ws("/ws");
|
||||||
|
|
||||||
void setupRoutes(){
|
void setupRoutes(){
|
||||||
// Web Server Routes
|
// Web Server Routes
|
||||||
|
|
||||||
|
// 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");
|
request->send(SPIFFS, "/index.html", "text/html");
|
||||||
});
|
});
|
||||||
@@ -166,7 +173,7 @@ void setupRoutes(){
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Setze WLAN-Name und Passwort (POST)
|
// Setze WLAN-Name und Passwort (POST)
|
||||||
server.on("/api/set-wifi", HTTP_POST, [](AsyncWebServerRequest *request){
|
server.on("/api/set-wifi", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||||
Serial.println("/api/set-wifi called");
|
Serial.println("/api/set-wifi called");
|
||||||
String ssid, password;
|
String ssid, password;
|
||||||
if (request->hasParam("ssid", true)) {
|
if (request->hasParam("ssid", true)) {
|
||||||
@@ -176,13 +183,14 @@ void setupRoutes(){
|
|||||||
password = request->getParam("password", true)->value();
|
password = request->getParam("password", true)->value();
|
||||||
}
|
}
|
||||||
if (ssid.length() > 0) {
|
if (ssid.length() > 0) {
|
||||||
// Hier speichern wir die neuen Werte (z.B. in Preferences oder global)
|
// Speicher freigeben, bevor neue Werte zugewiesen werden
|
||||||
// Beispiel: strcpy(ssidSTA, ssid.c_str());
|
free(ssidSTA);
|
||||||
// Beispiel: strcpy(passwordSTA, password.c_str());
|
free(passwordSTA);
|
||||||
// In deinem Projekt ggf. persistent speichern!
|
|
||||||
// Hier als global (unsicher, nach Neustart verloren!):
|
// Neue Werte zuweisen
|
||||||
ssidSTA = strdup(ssid.c_str());
|
ssidSTA = strdup(ssid.c_str());
|
||||||
passwordSTA = strdup(password.c_str());
|
passwordSTA = strdup(password.c_str());
|
||||||
|
|
||||||
// Rückmeldung
|
// Rückmeldung
|
||||||
DynamicJsonDocument doc(64);
|
DynamicJsonDocument doc(64);
|
||||||
doc["success"] = true;
|
doc["success"] = true;
|
||||||
@@ -193,7 +201,7 @@ void setupRoutes(){
|
|||||||
} else {
|
} 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)
|
// Liefert aktuelle WLAN-Einstellungen (GET)
|
||||||
server.on("/api/get-wifi", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/api/get-wifi", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
@@ -211,3 +219,24 @@ void setupRoutes(){
|
|||||||
Serial.println("Web Server gestartet");
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void pushUpdateToFrontend(const String &message) {
|
||||||
|
ws.textAll(message); // Send the message to all connected clients
|
||||||
|
}
|
||||||
|
|
||||||
|
void loopWebSocket() {
|
||||||
|
ws.cleanupClients(); // Clean up disconnected clients
|
||||||
|
}
|
||||||
@@ -13,8 +13,8 @@ String uniqueSSID;
|
|||||||
const char* ssidAP;
|
const char* ssidAP;
|
||||||
const char* passwordAP = nullptr;
|
const char* passwordAP = nullptr;
|
||||||
|
|
||||||
const char* ssidSTA = "Obiwlankenobi";
|
char* ssidSTA = strdup("Obiwlankenobi");
|
||||||
const char* passwordSTA = "Delfine1!";
|
char* passwordSTA = strdup("Delfine1!");
|
||||||
|
|
||||||
PrettyOTA OTAUpdates;
|
PrettyOTA OTAUpdates;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user