RFID message ins backend geht, websocket fürs frontend

This commit is contained in:
Carsten Graf
2025-06-08 00:38:00 +02:00
parent e5c4094cfa
commit c35e857904
11 changed files with 311 additions and 98 deletions

View File

@@ -11,6 +11,8 @@
#include "helper.h"
#include <debug.h>
#include <map>
#include <databasebackend.h>
#include <webserverrouter.h>
struct TimestampData {
uint64_t lastMessageTimestamp; // Timestamp from the device
@@ -35,52 +37,6 @@ typedef struct {
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) {
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() {
// 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) {
if (strncmp(topic, "heartbeat/alive/", 15) == 0) {
processHeartbeat(topic, payload);
} else if (strcmp(topic, "aquacross/button/press") == 0) {
//Message received callback
//Serial.printf("Received message on topic '%s': %s\n", topic, payload);
if (strcmp(topic, "aquacross/button/press") == 0) {
readButtonJSON(topic, payload);
} else if (strncmp(topic, "aquacross/button/rfid/", 22) == 0) {
readRFIDfromButton(topic, payload);
// Handle RFID read messages
}
updateStatusLED(3);
});
// Add the button subscription
// Start the MQTT server
mqtt.begin();
@@ -168,6 +201,7 @@ void loopMqttServer() {
mqtt.publish("sync/time", timeStr);
lastPublish = millis();
}
}
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());
}

View File

@@ -1,3 +1,4 @@
#pragma once
#include <Arduino.h>
#include <HTTPClient.h>
#include "master.h"
@@ -17,34 +18,76 @@ bool backendOnline() {
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN);
int httpCode = http.GET();
bool isOnline = (httpCode == HTTP_CODE_OK);
if (httpCode == HTTP_CODE_OK) {
return true;
if (isOnline) {
Serial.println("Database server connection successful");
} else {
return false;
Serial.printf("Database server connection failed, error: %d\n", httpCode);
}
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()) {
Serial.println("No internet connection, cannot check user existence.");
return false;
Serial.println("No internet connection, cannot check user.");
return userData;
}
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);
//Post request to check if user exists
int httpCode = http.POST("");
// 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<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) {
server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) {
DynamicJsonDocument doc(64);

View File

@@ -162,10 +162,26 @@ void saveWifiSettings() {
}
void loadWifiSettings() {
preferences.begin("wifi", true);
ssidSTA = preferences.getString("ssid", "").c_str();
passwordSTA = preferences.getString("password", "").c_str();
preferences.end();
preferences.begin("wifi", true);
// 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();
// Debug-Ausgabe
Serial.printf("WLAN-Einstellungen geladen: SSID=%s, Passwort=%s\n", ssidSTA, passwordSTA);
}
int checkLicence() {
@@ -179,7 +195,6 @@ String getTimerDataJSON() {
DynamicJsonDocument doc(1024);
unsigned long currentTime = millis();
// Bahn 1
if (timerData.isRunning1) {
doc["time1"] = (currentTime - timerData.startTime1) / 1000.0;
@@ -234,7 +249,7 @@ void setup() {
setupTimeAPI(server);
setupLicenceAPI(server);
setupDebugAPI(server);
setupBackendRoutes(server);
setupBackendRoutes(server);// Speichere WLAN-Einstellungen, falls noch nicht vorhanden
// Gespeicherte Daten laden
@@ -246,6 +261,7 @@ void setup() {
setupOTA(&server);
setupRoutes();
setupWebSocket();
setupLED();
setupMqttServer(); // MQTT Server initialisieren
@@ -255,4 +271,5 @@ void setup() {
void loop() {
checkAutoReset();
loopMqttServer(); // MQTT Server in der Loop aufrufen
loopWebSocket();
}

View File

@@ -1,3 +1,4 @@
#pragma once
#include <Arduino.h>
#include <WebServer.h>
#include <ArduinoJson.h>
@@ -21,10 +22,6 @@ struct User {
unsigned long timestamp;
};
// Array für Benutzer (max 100 Benutzer)
User users[100];
int userCount = 0;
void setupRFID() {
// SPI und RFID initialisieren

View File

@@ -1,17 +1,24 @@
#pragma once
#include <Arduino.h>
#include "master.h"
#include <ESPAsyncWebServer.h>
#include <AsyncWebSocket.h>
#include <ArduinoJson.h>
#include <SPIFFS.h>
#include <esp_wifi.h>
#include <buttonassigh.h>
#include <wificlass.h>
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
void setupRoutes(){
// Web Server Routes
// Web Server Routes
// Attach WebSocket to the server
server.addHandler(&ws);
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html", "text/html");
@@ -166,34 +173,35 @@ void setupRoutes(){
});
// 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");
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) {
// Hier speichern wir die neuen Werte (z.B. in Preferences oder global)
// Beispiel: strcpy(ssidSTA, ssid.c_str());
// Beispiel: strcpy(passwordSTA, password.c_str());
// In deinem Projekt ggf. persistent speichern!
// Hier als global (unsicher, nach Neustart verloren!):
ssidSTA = strdup(ssid.c_str());
passwordSTA = strdup(password.c_str());
// Rückmeldung
DynamicJsonDocument doc(64);
doc["success"] = true;
String result;
serializeJson(doc, result);
request->send(200, "application/json", result);
Serial.println("WiFi-Settings updated (nur bis zum Neustart aktiv!)");
// Speicher freigeben, bevor neue Werte zugewiesen werden
free(ssidSTA);
free(passwordSTA);
// Neue Werte zuweisen
ssidSTA = strdup(ssid.c_str());
passwordSTA = strdup(password.c_str());
// Rückmeldung
DynamicJsonDocument doc(64);
doc["success"] = true;
String result;
serializeJson(doc, result);
request->send(200, "application/json", result);
Serial.println("WiFi-Settings updated (nur bis zum Neustart aktiv!)");
} 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){
@@ -210,4 +218,25 @@ void setupRoutes(){
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);
}
});
}
void pushUpdateToFrontend(const String &message) {
ws.textAll(message); // Send the message to all connected clients
}
void loopWebSocket() {
ws.cleanupClients(); // Clean up disconnected clients
}

View File

@@ -13,8 +13,8 @@ String uniqueSSID;
const char* ssidAP;
const char* passwordAP = nullptr;
const char* ssidSTA = "Obiwlankenobi";
const char* passwordSTA = "Delfine1!";
char* ssidSTA = strdup("Obiwlankenobi");
char* passwordSTA = strdup("Delfine1!");
PrettyOTA OTAUpdates;