RFID Implementierung

This commit is contained in:
Carsten Graf
2025-09-20 01:04:00 +02:00
parent 1ed3a30340
commit 7e9705902e
10 changed files with 715 additions and 554 deletions

Binary file not shown.

View File

@@ -15,7 +15,8 @@
<div> <div>
<div class="banner-text">⚠️ Niedrige Batterie erkannt!</div> <div class="banner-text">⚠️ Niedrige Batterie erkannt!</div>
<div class="banner-devices" id="battery-devices"> <div class="banner-devices" id="battery-devices">
Deine Geräte mit niedriger Batterie: <span id="low-battery-list"></span> Deine Geräte mit niedriger Batterie:
<span id="low-battery-list"></span>
</div> </div>
</div> </div>
</div> </div>
@@ -47,9 +48,7 @@
<div id="learning-display" class="learning-mode" style="display: none"> <div id="learning-display" class="learning-mode" style="display: none">
<h3>📚 Lernmodus aktiv</h3> <h3>📚 Lernmodus aktiv</h3>
<p> <p>Drücke jetzt den Button für: <span id="learning-button"></span></p>
Drücke jetzt den Button für: <span id="learning-button"></span>
</p>
</div> </div>
<div class="timer-container"> <div class="timer-container">
@@ -193,24 +192,18 @@
} }
// Namen-Handling // Namen-Handling
if ( if ((data.name == "" || !data.name) && data.lane == "start1") {
(data.firstname == "" || data.lastname == "") &&
data.lane == "start1"
) {
name1 = ""; name1 = "";
} }
if ( if ((data.name == "" || !data.name) && data.lane == "start2") {
(data.firstname == "" || data.lastname == "") &&
data.lane == "start2"
) {
name2 = ""; name2 = "";
} }
if (data.firstname && data.lastname && data.lane) { if (data.name && data.lane) {
if (data.lane === "start1") { if (data.lane === "start1") {
name1 = `${data.firstname} ${data.lastname}`; name1 = data.name;
} else if (data.lane === "start2") { } else if (data.lane === "start2") {
name2 = `${data.firstname} ${data.lastname}`; name2 = data.name;
} }
updateDisplay(); updateDisplay();
} }
@@ -482,13 +475,16 @@
updateLaneDisplay(); updateLaneDisplay();
}) })
.catch((error) => .catch((error) =>
console.error("Fehler beim Laden der Lane-Schwierigkeits-Konfiguration:", error) console.error(
"Fehler beim Laden der Lane-Schwierigkeits-Konfiguration:",
error
)
); );
} }
function updateLaneDisplay() { function updateLaneDisplay() {
const lane1Title = document.querySelector('.lane h2'); const lane1Title = document.querySelector(".lane h2");
const lane2Title = document.querySelectorAll('.lane h2')[1]; const lane2Title = document.querySelectorAll(".lane h2")[1];
if (laneConfigType === 0) { if (laneConfigType === 0) {
// Identische Lanes // Identische Lanes
@@ -498,8 +494,10 @@
// Unterschiedliche Lanes // Unterschiedliche Lanes
const lane1Icon = lane1DifficultyType === 0 ? "🟢" : "🔴"; const lane1Icon = lane1DifficultyType === 0 ? "🟢" : "🔴";
const lane2Icon = lane2DifficultyType === 0 ? "🟢" : "🔴"; const lane2Icon = lane2DifficultyType === 0 ? "🟢" : "🔴";
const lane1Difficulty = lane1DifficultyType === 0 ? "Leicht" : "Schwer"; const lane1Difficulty =
const lane2Difficulty = lane2DifficultyType === 0 ? "Leicht" : "Schwer"; lane1DifficultyType === 0 ? "Leicht" : "Schwer";
const lane2Difficulty =
lane2DifficultyType === 0 ? "Leicht" : "Schwer";
lane1Title.textContent = `${lane1Icon} Bahn 1 (${lane1Difficulty})`; lane1Title.textContent = `${lane1Icon} Bahn 1 (${lane1Difficulty})`;
lane2Title.textContent = `${lane2Icon} Bahn 2 (${lane2Difficulty})`; lane2Title.textContent = `${lane2Icon} Bahn 2 (${lane2Difficulty})`;

View File

@@ -62,7 +62,7 @@
type="button" type="button"
id="readUidBtn" id="readUidBtn"
class="read-uid-btn" class="read-uid-btn"
onclick="readRFIDUID()" onclick="toggleRFIDReading()"
> >
📡 Read Chip 📡 Read Chip
</button> </button>
@@ -70,47 +70,16 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="vorname">Vorname <span class="required">*</span></label> <label for="name">Name <span class="required">*</span></label>
<input <input
type="text" type="text"
id="vorname" id="name"
name="vorname" name="name"
placeholder="Vorname eingeben" placeholder="Name eingeben"
required required
/> />
</div> </div>
<div class="form-group">
<label for="nachname">Nachname <span class="required">*</span></label>
<input
type="text"
id="nachname"
name="nachname"
placeholder="Nachname eingeben"
required
/>
</div>
<div class="form-group">
<label for="geburtsdatum"
>Geburtsdatum <span class="required">*</span></label
>
<div class="date-input-group">
<input
type="date"
id="geburtsdatum"
name="geburtsdatum"
required
max=""
/>
<div
id="ageDisplay"
class="age-display"
style="display: none"
></div>
</div>
</div>
<div class="btn-container"> <div class="btn-container">
<button type="submit" class="btn btn-primary">💾 Speichern</button> <button type="submit" class="btn btn-primary">💾 Speichern</button>
<button type="button" class="btn btn-secondary" onclick="clearForm()"> <button type="button" class="btn btn-secondary" onclick="clearForm()">
@@ -124,60 +93,8 @@
// Globale Variablen // Globale Variablen
let rfidData = []; let rfidData = [];
let isLoading = false; let isLoading = false;
let DBUrl = "ninja.reptilfpv.de:3000"; // Lokale Benutzer-Speicherung (geht bei Neustart verloren)
var APIKey; let localUsers = [];
// Maximales Datum auf heute setzen
document.addEventListener("DOMContentLoaded", function () {
const today = new Date().toISOString().split("T")[0];
document.getElementById("geburtsdatum").setAttribute("max", today);
});
// Alter berechnen und anzeigen
function calculateAge(birthDate) {
const today = new Date();
const birth = new Date(birthDate);
let age = today.getFullYear() - birth.getFullYear();
const monthDiff = today.getMonth() - birth.getMonth();
if (
monthDiff < 0 ||
(monthDiff === 0 && today.getDate() < birth.getDate())
) {
age--;
}
return age;
}
// Geburtsdatum Change Event
document
.getElementById("geburtsdatum")
.addEventListener("change", function (e) {
const birthDate = e.target.value;
const ageDisplay = document.getElementById("ageDisplay");
if (birthDate) {
const age = calculateAge(birthDate);
if (age >= 0 && age <= 150) {
ageDisplay.textContent = `${age} Jahre`;
ageDisplay.style.display = "block";
} else {
ageDisplay.style.display = "none";
if (age < 0) {
showErrorMessage(
"Das Geburtsdatum kann nicht in der Zukunft liegen!"
);
e.target.value = "";
} else {
showErrorMessage("Bitte überprüfen Sie das Geburtsdatum!");
e.target.value = "";
}
}
} else {
ageDisplay.style.display = "none";
}
});
// Form Submit Handler // Form Submit Handler
document document
@@ -189,46 +106,40 @@
// Daten aus dem Formular holen // Daten aus dem Formular holen
const uid = document.getElementById("uid").value.trim(); const uid = document.getElementById("uid").value.trim();
const vorname = document.getElementById("vorname").value.trim(); const name = document.getElementById("name").value.trim();
const nachname = document.getElementById("nachname").value.trim();
const geburtsdatum = document.getElementById("geburtsdatum").value;
// Validierung // Validierung
if (!uid || !vorname || !nachname || !geburtsdatum) { if (!uid || !name) {
showErrorMessage("Bitte füllen Sie alle Pflichtfelder aus!"); showErrorMessage("Bitte füllen Sie alle Pflichtfelder aus!");
return; return;
} }
// Alter berechnen
const alter = calculateAge(geburtsdatum);
if (alter < 0) {
showErrorMessage(
"Das Geburtsdatum kann nicht in der Zukunft liegen!"
);
return;
}
// Loading State // Loading State
setLoadingState(true); setLoadingState(true);
try { try {
// API Aufruf zum Erstellen des Benutzers // API Aufruf zum Erstellen des Benutzers (lokal)
const requestData = {
uid: uid,
name: name,
};
console.log("Sende Daten:", requestData);
console.log("JSON String:", JSON.stringify(requestData));
const response = await fetch(`/api/users/insert`, { const response = await fetch(`/api/users/insert`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
...(APIKey && { Authorization: `Bearer ${APIKey}` }),
}, },
body: JSON.stringify({ body: JSON.stringify(requestData),
uid: uid,
vorname: vorname,
nachname: nachname,
geburtsdatum: geburtsdatum,
alter: alter, // Berechnetes Alter wird mit gesendet
}),
}); });
console.log("Response Status:", response.status);
console.log("Response Headers:", response.headers);
const result = await response.json(); const result = await response.json();
console.log("Response Result:", result);
if (result.success) { if (result.success) {
// Erfolg anzeigen // Erfolg anzeigen
@@ -313,7 +224,6 @@
function clearForm() { function clearForm() {
document.getElementById("rfidForm").reset(); document.getElementById("rfidForm").reset();
document.getElementById("ageDisplay").style.display = "none";
document.getElementById("uid").focus(); document.getElementById("uid").focus();
} }
@@ -321,14 +231,13 @@
window.addEventListener("load", function () { window.addEventListener("load", function () {
document.getElementById("uid").focus(); document.getElementById("uid").focus();
checkServerStatus(); checkServerStatus();
loadLicence();
}); });
// Enter-Taste in UID Feld zum nächsten Feld springen // Enter-Taste in UID Feld zum nächsten Feld springen
document.getElementById("uid").addEventListener("keydown", function (e) { document.getElementById("uid").addEventListener("keydown", function (e) {
if (e.key === "Enter") { if (e.key === "Enter") {
e.preventDefault(); e.preventDefault();
document.getElementById("vorname").focus(); document.getElementById("name").focus();
} }
}); });
@@ -340,34 +249,170 @@
e.target.value = value; e.target.value = value;
}); });
// RFID UID lesen let rfidReadingMode = false;
let statusInterval = null;
// Toggle RFID Reading Mode
async function toggleRFIDReading() {
const readBtn = document.getElementById("readUidBtn");
try {
const response = await fetch(`/api/rfid/toggle`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const result = await response.json();
if (result.success) {
rfidReadingMode = result.reading_mode;
if (rfidReadingMode) {
// RFID Reading gestartet
readBtn.innerHTML = "🛑 Stop Reading";
readBtn.className = "read-uid-btn reading";
showSuccessMessage("RFID Lesen gestartet - Karte auflegen!");
// Status Polling starten
startStatusPolling();
} else {
// RFID Reading gestoppt
readBtn.innerHTML = "📡 Read Chip";
readBtn.className = "read-uid-btn";
showSuccessMessage("RFID Lesen gestoppt");
// Status Polling stoppen
stopStatusPolling();
}
} else {
showErrorMessage("Fehler beim Toggle RFID: " + result.message);
}
} catch (error) {
console.error("Toggle RFID Error:", error);
showErrorMessage("Fehler beim Toggle RFID");
}
}
// Status Polling für kontinuierliches Lesen
function startStatusPolling() {
if (statusInterval) {
clearInterval(statusInterval);
}
statusInterval = setInterval(async () => {
try {
const response = await fetch(`/api/rfid/status`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
const result = await response.json();
if (result.success && result.last_uid && result.last_uid !== "") {
// Neue UID gelesen - automatisch stoppen
const uidInput = document.getElementById("uid");
uidInput.value = result.last_uid;
// Visuelles Feedback
uidInput.style.borderColor = "#28a745";
setTimeout(() => {
uidInput.style.borderColor = "#e1e5e9";
}, 2000);
showSuccessMessage("UID gelesen: " + result.last_uid);
// Automatisch zum nächsten Feld springen
setTimeout(() => {
document.getElementById("name").focus();
}, 500);
// RFID Lesen automatisch stoppen
stopRFIDReading();
// UID im Backend zurücksetzen
clearBackendUID();
}
} catch (error) {
console.error("Status Poll Error:", error);
}
}, 500); // Alle 500ms prüfen
}
// Status Polling stoppen
function stopStatusPolling() {
if (statusInterval) {
clearInterval(statusInterval);
statusInterval = null;
}
}
// RFID Reading komplett stoppen (Frontend + Backend)
async function stopRFIDReading() {
// Status Polling stoppen
stopStatusPolling();
// Backend stoppen
try {
const response = await fetch(`/api/rfid/toggle`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const result = await response.json();
if (result.success && !result.reading_mode) {
rfidReadingMode = false;
// Button zurücksetzen
const readBtn = document.getElementById("readUidBtn");
readBtn.innerHTML = "📡 Read Chip";
readBtn.className = "read-uid-btn";
}
} catch (error) {
console.error("Stop RFID Error:", error);
}
}
// UID im Backend zurücksetzen
async function clearBackendUID() {
try {
await fetch(`/api/rfid/clear`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
} catch (error) {
console.error("Clear UID Error:", error);
}
}
// Einzelnes Lesen (für Kompatibilität)
async function readRFIDUID() { async function readRFIDUID() {
const readBtn = document.getElementById("readUidBtn"); const readBtn = document.getElementById("readUidBtn");
const uidInput = document.getElementById("uid"); const uidInput = document.getElementById("uid");
// Button Status ändern
readBtn.disabled = true; readBtn.disabled = true;
readBtn.className = "read-uid-btn reading"; readBtn.innerHTML = "📡 Lese...";
readBtn.innerHTML = "📡 Lese UID...";
try { try {
// API Aufruf zum RFID Reader
const response = await fetch(`/api/rfid/read`, { const response = await fetch(`/api/rfid/read`, {
method: "GET", method: "GET",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
...(APIKey && { Authorization: `Bearer ${APIKey}` }),
}, },
}); });
const result = await response.json(); const result = await response.json();
if (result.success && result.uid) { if (result.success && result.uid) {
// UID in das Eingabefeld setzen uidInput.value = result.uid;
uidInput.value = result.uid
.match(/.{1,2}/g)
.join(":")
.toUpperCase();
uidInput.focus(); uidInput.focus();
// Visuelles Feedback // Visuelles Feedback
@@ -376,38 +421,20 @@
uidInput.style.borderColor = "#e1e5e9"; uidInput.style.borderColor = "#e1e5e9";
}, 2000); }, 2000);
showSuccessMessage("UID erfolgreich gelesen!"); showSuccessMessage("UID gelesen: " + result.uid);
// Automatisch zum nächsten Feld springen // Automatisch zum nächsten Feld springen
setTimeout(() => { setTimeout(() => {
document.getElementById("vorname").focus(); document.getElementById("name").focus();
}, 500); }, 500);
} else { } else {
// Fehler beim Lesen showErrorMessage("Keine Karte erkannt");
const errorMsg = result.error || "Keine UID gefunden";
showErrorMessage(`RFID Fehler: ${errorMsg}`);
// UID Feld rot markieren
uidInput.style.borderColor = "#dc3545";
setTimeout(() => {
uidInput.style.borderColor = "#e1e5e9";
}, 10000);
} }
} catch (error) { } catch (error) {
console.error("Fehler beim Lesen der UID:", error); console.error("RFID Read Error:", error);
showErrorMessage( showErrorMessage("Fehler beim Lesen");
"Verbindungsfehler zum RFID Reader. Bitte prüfen Sie die Verbindung."
);
// UID Feld rot markieren
uidInput.style.borderColor = "#dc3545";
setTimeout(() => {
uidInput.style.borderColor = "#e1e5e9";
}, 3000);
} finally { } finally {
// Button Status zurücksetzen
readBtn.disabled = false; readBtn.disabled = false;
readBtn.className = "read-uid-btn";
readBtn.innerHTML = "📡 Read Chip"; readBtn.innerHTML = "📡 Read Chip";
} }
} }
@@ -415,9 +442,7 @@
async function checkServerStatus() { async function checkServerStatus() {
try { try {
const response = await fetch("/api/health", { const response = await fetch("/api/health", {
headers: { headers: {},
...(APIKey && { Authorization: `Bearer ${APIKey}` }),
},
}); });
const data = await response.json(); const data = await response.json();
@@ -436,16 +461,19 @@
} }
} }
function loadLicence() { // Seite laden - RFID Status initialisieren
fetch("/api/get-licence") document.addEventListener("DOMContentLoaded", function () {
.then((response) => response.json()) // Status Polling stoppen falls aktiv
.then((data) => { stopStatusPolling();
APIKey = data.licence || "";
}) // Server Status prüfen
.catch((error) => checkServerStatus();
showMessage("Fehler beim Laden der Lizenz", "error") });
);
} // Seite verlassen - RFID Reading komplett stoppen
window.addEventListener("beforeunload", function () {
stopRFIDReading();
});
</script> </script>
</body> </body>
</html> </html>

View File

@@ -38,6 +38,7 @@
<!-- Navigation Buttons --> <!-- Navigation Buttons -->
<div class="nav-buttons"> <div class="nav-buttons">
<a href="/" class="nav-button">🏠 Hauptseite</a> <a href="/" class="nav-button">🏠 Hauptseite</a>
<a href="/rfid.html" class="nav-button">🏷️ RFID</a>
</div> </div>
<!-- Date & Time Section --> <!-- Date & Time Section -->

View File

@@ -29,8 +29,7 @@ lib_deps =
esp32async/ESPAsyncWebServer@^3.7.7 esp32async/ESPAsyncWebServer@^3.7.7
esp32async/AsyncTCP@^3.4.2 esp32async/AsyncTCP@^3.4.2
mlesniew/PicoMQTT@^1.3.0 mlesniew/PicoMQTT@^1.3.0
miguelbalboa/MFRC522@^1.4.12 adafruit/Adafruit PN532@^1.3.4
adafruit/RTClib@^2.1.4
[env:esp32thing_OTA] [env:esp32thing_OTA]
board = esp32thing board = esp32thing
@@ -50,8 +49,8 @@ lib_deps =
esp32async/ESPAsyncWebServer@^3.7.7 esp32async/ESPAsyncWebServer@^3.7.7
esp32async/AsyncTCP@^3.4.2 esp32async/AsyncTCP@^3.4.2
mlesniew/PicoMQTT@^1.3.0 mlesniew/PicoMQTT@^1.3.0
miguelbalboa/MFRC522@^1.4.12 adafruit/Adafruit PN532@^1.3.4
adafruit/RTClib@^2.1.4
[env:esp32thing] [env:esp32thing]
board = esp32thing_plus board = esp32thing_plus
@@ -69,8 +68,7 @@ lib_deps =
esp32async/ESPAsyncWebServer@^3.7.7 esp32async/ESPAsyncWebServer@^3.7.7
esp32async/AsyncTCP@^3.4.2 esp32async/AsyncTCP@^3.4.2
mlesniew/PicoMQTT@^1.3.0 mlesniew/PicoMQTT@^1.3.0
miguelbalboa/MFRC522@^1.4.12 adafruit/Adafruit PN532@^1.3.4
adafruit/RTClib@^2.1.4
[env:esp32thing_CI] [env:esp32thing_CI]
platform = espressif32 platform = espressif32
@@ -87,8 +85,7 @@ lib_deps =
esp32async/ESPAsyncWebServer@^3.7.7 esp32async/ESPAsyncWebServer@^3.7.7
esp32async/AsyncTCP@^3.4.2 esp32async/AsyncTCP@^3.4.2
mlesniew/PicoMQTT@^1.3.0 mlesniew/PicoMQTT@^1.3.0
miguelbalboa/MFRC522@^1.4.12 adafruit/Adafruit PN532@^1.3.4
adafruit/RTClib@^2.1.4
[env:esp32-s3-devkitc-1] [env:esp32-s3-devkitc-1]
board = esp32-s3-devkitc-1 board = esp32-s3-devkitc-1
@@ -103,5 +100,5 @@ lib_deps =
esp32async/ESPAsyncWebServer@^3.7.7 esp32async/ESPAsyncWebServer@^3.7.7
esp32async/AsyncTCP@^3.4.2 esp32async/AsyncTCP@^3.4.2
mlesniew/PicoMQTT@^1.3.0 mlesniew/PicoMQTT@^1.3.0
miguelbalboa/MFRC522@^1.4.12 adafruit/Adafruit PN532@^1.3.4
adafruit/RTClib@^2.1.4

View File

@@ -2,6 +2,8 @@
#include "master.h" #include "master.h"
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <HTTPClient.h>
#include <WiFi.h>
#include <PicoMQTT.h> #include <PicoMQTT.h>
@@ -249,34 +251,105 @@ void publishLaneStatus(int lane, String status) {
* sendet diese ggf. als JSON an das Frontend. * sendet diese ggf. als JSON an das Frontend.
*/ */
void readRFIDfromButton(const char *topic, const char *payload) { void readRFIDfromButton(const char *topic, const char *payload) {
loadLicenceFromPrefs();
String topicStr(topic);
int lastSlash = topicStr.lastIndexOf('/');
if (lastSlash < 0)
return;
String macStr = topicStr.substring(lastSlash + 1);
// Create a JSON document to hold the button press data // Create a JSON document to hold the button press data
StaticJsonDocument<256> doc; StaticJsonDocument<256> doc;
DeserializationError error = deserializeJson(doc, payload); DeserializationError error = deserializeJson(doc, payload);
if (!error) { if (!error) {
const char *mac = doc["buttonmac"] | "unknown";
const char *uid = doc["uid"] | "unknown"; const char *uid = doc["uid"] | "unknown";
Serial.printf("RFID Read from Button:\n"); Serial.printf("RFID Read from Button:\n");
Serial.printf(" Button MAC: %s\n", mac); Serial.printf(" Button MAC: %s\n", macStr.c_str());
Serial.printf(" UID: %s\n", uid); Serial.printf(" UID: %s\n", uid);
String debugUpperUid = String(uid);
debugUpperUid.toUpperCase();
Serial.printf(" UID (Upper): %s\n", debugUpperUid.c_str());
// Convert buttonmac to byte array for comparison // Convert buttonmac to byte array for comparison
auto macBytes = macStringToBytes(mac); auto macBytes = macStringToBytes(macStr.c_str());
// Check if the buttonmac matches buttonConfigs.start1.mac // Check if the buttonmac matches buttonConfigs.start1.mac
if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0) { if (memcmp(macBytes.data(), buttonConfigs.start1.mac, 6) == 0) {
// Fetch user data // Zuerst lokal suchen (UID in Großbuchstaben konvertieren)
UserData userData = checkUser(uid); String upperUid = String(uid);
upperUid.toUpperCase();
UserData userData = checkUser(upperUid);
if (!userData.exists) {
// Nicht lokal gefunden - Online-Server fragen
Serial.println("User nicht lokal gefunden, suche online...");
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(String(BACKEND_SERVER) + "/api/v1/private/users/find");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", String("Bearer ") + licence);
Serial.println("Online-Suche mit Token: " + licence);
StaticJsonDocument<200> requestDoc;
String upperUidForRequest = String(uid);
upperUidForRequest.toUpperCase();
requestDoc["uid"] =
upperUidForRequest; // UID in Großbuchstaben konvertieren
String requestBody;
serializeJson(requestDoc, requestBody);
Serial.println("Request Body: " + requestBody);
int httpCode = http.POST(requestBody);
if (httpCode == HTTP_CODE_OK) {
String response = http.getString();
Serial.println("Response: " + response);
StaticJsonDocument<512> responseDoc;
DeserializationError parseError =
deserializeJson(responseDoc, response);
if (!parseError && responseDoc["success"].as<bool>() &&
responseDoc["data"]["exists"].as<bool>()) {
// Online gefundenen Benutzer verwenden (nicht lokal speichern)
String firstName = responseDoc["data"]["firstname"].as<String>();
String lastName = responseDoc["data"]["lastname"].as<String>();
String fullName = firstName + " " + lastName;
// UserData für Frontend erstellen
userData.uid = upperUid;
userData.firstname = firstName;
userData.lastname = "";
userData.alter = 0;
userData.exists = true;
Serial.println("User online gefunden: " + fullName);
} else {
Serial.println("User auch online nicht gefunden für UID: " +
upperUid);
}
} else {
Serial.printf("Online-Suche fehlgeschlagen: HTTP %d\n", httpCode);
}
http.end();
} else {
Serial.println("Keine Internetverbindung für Online-Suche");
}
}
// Wenn Benutzer gefunden wurde (lokal oder online)
if (userData.exists) { if (userData.exists) {
// Log user data // Log user data
Serial.printf("User found for start1: %s %s, Alter: %d\n", Serial.printf("User found for start1: %s\n",
userData.firstname.c_str(), userData.lastname.c_str(), userData.firstname.c_str());
userData.alter);
// Create JSON message to send to the frontend // Create JSON message to send to the frontend
StaticJsonDocument<128> messageDoc; StaticJsonDocument<128> messageDoc;
messageDoc["firstname"] = userData.firstname; messageDoc["name"] =
messageDoc["lastname"] = userData.lastname; userData.firstname; // Verwende name statt firstname/lastname
messageDoc["lane"] = "start1"; // Add lane information messageDoc["lane"] = "start1"; // Add lane information
String message; String message;
@@ -287,23 +360,86 @@ void readRFIDfromButton(const char *topic, const char *payload) {
Serial.printf("Pushed user data for start1 to frontend: %s\n", Serial.printf("Pushed user data for start1 to frontend: %s\n",
message.c_str()); message.c_str());
} else { } else {
Serial.println("User not found for UID: " + String(uid)); Serial.println("User nicht gefunden für UID: " + upperUid);
} }
} }
// Check if the buttonmac matches buttonConfigs.start2.mac // Check if the buttonmac matches buttonConfigs.start2.mac
else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0) { else if (memcmp(macBytes.data(), buttonConfigs.start2.mac, 6) == 0) {
// Fetch user data // Zuerst lokal suchen (UID in Großbuchstaben konvertieren)
UserData userData = checkUser(uid); String upperUid = String(uid);
upperUid.toUpperCase();
UserData userData = checkUser(upperUid);
if (!userData.exists) {
// Nicht lokal gefunden - Online-Server fragen
Serial.println("User nicht lokal gefunden, suche online...");
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(String(BACKEND_SERVER) + "/api/v1/private/users/find");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", String("Bearer ") + licence);
Serial.println("Online-Suche mit Token: " + licence);
StaticJsonDocument<200> requestDoc;
String upperUidForRequest2 = String(uid);
upperUidForRequest2.toUpperCase();
requestDoc["uid"] =
upperUidForRequest2; // UID in Großbuchstaben konvertieren
String requestBody;
serializeJson(requestDoc, requestBody);
Serial.println("Request Body: " + requestBody);
int httpCode = http.POST(requestBody);
if (httpCode == HTTP_CODE_OK) {
String response = http.getString();
Serial.println("Response: " + response);
StaticJsonDocument<512> responseDoc;
DeserializationError parseError =
deserializeJson(responseDoc, response);
if (!parseError && responseDoc["success"].as<bool>() &&
responseDoc["data"]["exists"].as<bool>()) {
// Online gefundenen Benutzer verwenden (nicht lokal speichern)
String firstName = responseDoc["data"]["firstname"].as<String>();
String lastName = responseDoc["data"]["lastname"].as<String>();
String fullName = firstName + " " + lastName;
// UserData für Frontend erstellen
userData.uid = upperUid;
userData.firstname = firstName;
userData.lastname = "";
userData.alter = 0;
userData.exists = true;
Serial.println("User online gefunden: " + fullName);
} else {
Serial.println("User auch online nicht gefunden für UID: " +
upperUid);
}
} else {
Serial.printf("Online-Suche fehlgeschlagen: HTTP %d\n", httpCode);
}
http.end();
} else {
Serial.println("Keine Internetverbindung für Online-Suche");
}
}
// Wenn Benutzer gefunden wurde (lokal oder online)
if (userData.exists) { if (userData.exists) {
// Log user data // Log user data
Serial.printf("User found for start2: %s %s, Alter: %d\n", Serial.printf("User found for start2: %s\n",
userData.firstname.c_str(), userData.lastname.c_str(), userData.firstname.c_str());
userData.alter);
// Create JSON message to send to the frontend // Create JSON message to send to the frontend
StaticJsonDocument<128> messageDoc; StaticJsonDocument<128> messageDoc;
messageDoc["firstname"] = userData.firstname; messageDoc["name"] =
messageDoc["lastname"] = userData.lastname; userData.firstname; // Verwende name statt firstname/lastname
messageDoc["lane"] = "start2"; // Add lane information messageDoc["lane"] = "start2"; // Add lane information
String message; String message;
@@ -314,7 +450,7 @@ void readRFIDfromButton(const char *topic, const char *payload) {
Serial.printf("Pushed user data for start2 to frontend: %s\n", Serial.printf("Pushed user data for start2 to frontend: %s\n",
message.c_str()); message.c_str());
} else { } else {
Serial.println("User not found for UID: " + String(uid)); Serial.println("User nicht gefunden für UID: " + upperUid);
} }
} else { } else {
Serial.println("Button MAC does not match start1.mac or start2.mac"); Serial.println("Button MAC does not match start1.mac or start2.mac");
@@ -335,11 +471,11 @@ void setupMqttServer() {
mqtt.subscribe("#", [](const char *topic, const char *payload) { mqtt.subscribe("#", [](const char *topic, const char *payload) {
// Message received callback // Message received callback
// Serial.printf("Received message on topic '%s': %s\n", topic, payload); // Serial.printf("Received message on topic '%s': %s\n", topic, payload);
if (strncmp(topic, "aquacross/button/", 17) == 0) { if (strncmp(topic, "aquacross/button/rfid/", 22) == 0) {
readButtonJSON(topic, payload);
} else if (strncmp(topic, "aquacross/button/rfid/", 22) == 0) {
readRFIDfromButton(topic, payload); readRFIDfromButton(topic, payload);
// Handle RFID read messages // Handle RFID read messages
} else if (strncmp(topic, "aquacross/button/", 17) == 0) {
readButtonJSON(topic, payload);
} else if (strncmp(topic, "aquacross/battery/", 17) == 0) { } else if (strncmp(topic, "aquacross/battery/", 17) == 0) {
handleBatteryTopic(topic, payload); handleBatteryTopic(topic, payload);
} else if (strncmp(topic, "heartbeat/alive/", 16) == 0) { } else if (strncmp(topic, "heartbeat/alive/", 16) == 0) {

View File

@@ -4,12 +4,21 @@
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <HTTPClient.h> #include <HTTPClient.h>
#include <preferencemanager.h> #include <preferencemanager.h>
#include <vector>
const char *BACKEND_SERVER = "https://ninja.reptilfpv.de"; const char *BACKEND_SERVER = "https://ninja.reptilfpv.de";
extern String extern String
licence; // Declare licence as an external variable defined elsewhere licence; // Declare licence as an external variable defined elsewhere
String BACKEND_TOKEN =
licence; // Use the licence as the token for authentication // Lokale Benutzer-Struktur
struct LocalUser {
String uid;
String name;
unsigned long timestamp; // Zeitstempel der Erstellung
};
// Lokale Benutzer-Speicherung (geht bei Neustart verloren)
std::vector<LocalUser> localUsers;
bool backendOnline() { bool backendOnline() {
@@ -21,8 +30,8 @@ bool backendOnline() {
} }
HTTPClient http; HTTPClient http;
http.begin(String(BACKEND_SERVER) + "/v1/private/health"); http.begin(String(BACKEND_SERVER) + "/api/v1/private/health");
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN); http.addHeader("Authorization", String("Bearer ") + licence);
int httpCode = http.GET(); int httpCode = http.GET();
bool isOnline = (httpCode == HTTP_CODE_OK); bool isOnline = (httpCode == HTTP_CODE_OK);
@@ -50,42 +59,27 @@ struct UserData {
UserData checkUser(const String &uid) { UserData checkUser(const String &uid) {
UserData userData = {"", "", "", 0, false}; UserData userData = {"", "", "", 0, false};
String upperUid = uid;
upperUid.toUpperCase(); // UID in Großbuchstaben konvertieren
if (!backendOnline()) { // Lokale Benutzer durchsuchen
Serial.println("No internet connection, cannot check user."); for (const auto &user : localUsers) {
String userUpperUid = user.uid;
userUpperUid.toUpperCase();
if (userUpperUid == upperUid) {
userData.uid = user.uid;
userData.firstname = user.name;
userData.lastname = ""; // Nicht mehr verwendet
userData.alter = 0; // Nicht mehr verwendet
userData.exists = true;
Serial.println("Lokaler Benutzer gefunden: " + user.name);
return userData; return userData;
} }
HTTPClient http;
http.begin(String(BACKEND_SERVER) + "/v1/private/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<String>();
userData.firstname = responseDoc["firstname"].as<String>();
userData.lastname = responseDoc["lastname"].as<String>();
userData.alter = responseDoc["alter"] | 0;
userData.exists = responseDoc["exists"] | false;
}
} else {
Serial.printf("User check failed, HTTP code: %d\n", httpCode);
} }
http.end(); Serial.println("Benutzer mit UID " + uid +
" nicht in lokaler Datenbank gefunden");
return userData; return userData;
} }
@@ -100,7 +94,7 @@ JsonDocument getAllLocations() {
HTTPClient http; HTTPClient http;
http.begin(String(BACKEND_SERVER) + "/v1/private/locations"); http.begin(String(BACKEND_SERVER) + "/v1/private/locations");
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN); http.addHeader("Authorization", String("Bearer ") + licence);
int httpCode = http.GET(); int httpCode = http.GET();
@@ -124,42 +118,47 @@ JsonDocument getAllLocations() {
bool userExists(const String &uid) { return checkUser(uid).exists; } bool userExists(const String &uid) { return checkUser(uid).exists; }
// Fügt einen neuen Benutzer in die Datenbank ein // Fügt einen neuen Benutzer in die Datenbank ein
bool enterUserData(const String &uid, const String &vorname, bool enterUserData(const String &uid, const String &name) {
const String &nachname, const String &geburtsdatum, String upperUid = uid;
int alter) { upperUid.toUpperCase(); // UID in Großbuchstaben konvertieren
if (!backendOnline()) {
Serial.println("No internet connection, cannot enter user data."); // Prüfen ob Benutzer bereits existiert
for (const auto &user : localUsers) {
String userUpperUid = user.uid;
userUpperUid.toUpperCase();
if (userUpperUid == upperUid) {
Serial.println("Benutzer mit UID " + upperUid + " existiert bereits!");
return false; return false;
} }
HTTPClient http;
http.begin(String(BACKEND_SERVER) + "/v1/private/users/insert");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", String("Bearer ") + BACKEND_TOKEN);
// Create JSON payload
StaticJsonDocument<512> requestDoc;
requestDoc["uid"] = uid;
requestDoc["firstname"] = vorname;
requestDoc["lastname"] = nachname;
requestDoc["geburtsdatum"] = geburtsdatum;
requestDoc["alter"] = alter;
String requestBody;
serializeJson(requestDoc, requestBody);
int httpCode = http.POST(requestBody);
bool success = (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_CREATED);
if (success) {
Serial.println("User data successfully entered into database");
} else {
Serial.printf("Failed to enter user data, HTTP code: %d\n", httpCode);
} }
http.end(); // Neuen Benutzer erstellen
return success; LocalUser newUser;
newUser.uid = upperUid; // UID in Großbuchstaben speichern
newUser.name = name;
newUser.timestamp = millis();
// Benutzer zum lokalen Array hinzufügen
localUsers.push_back(newUser);
Serial.println("Benutzer lokal gespeichert:");
Serial.println("UID: " + upperUid);
Serial.println("Name: " + name);
Serial.println("Gespeicherte Benutzer: " + String(localUsers.size()));
return true;
}
// Gibt alle lokalen Benutzer zurück (für Debugging)
String getLocalUsersList() {
String result = "Lokale Benutzer (" + String(localUsers.size()) + "):\n";
for (const auto &user : localUsers) {
result += "- UID: " + user.uid + ", Name: " + user.name +
", Erstellt: " + String(user.timestamp) + "\n";
}
return result;
} }
// Richtet die HTTP-Routen für die Backend-API ein (z.B. Health-Check, User- und // Richtet die HTTP-Routen für die Backend-API ein (z.B. Health-Check, User- und
@@ -175,14 +174,143 @@ void setupBackendRoutes(AsyncWebServer &server) {
}); });
server.on("/api/users", HTTP_GET, [](AsyncWebServerRequest *request) { server.on("/api/users", HTTP_GET, [](AsyncWebServerRequest *request) {
if (!backendOnline()) { // Lokale Benutzer als JSON zurückgeben
request->send(503, "application/json", DynamicJsonDocument doc(2048);
"{\"error\":\"Database not connected\"}"); JsonArray usersArray = doc.createNestedArray("users");
for (const auto &user : localUsers) {
JsonObject userObj = usersArray.createNestedObject();
userObj["uid"] = user.uid;
userObj["name"] = user.name;
userObj["timestamp"] = user.timestamp;
}
doc["count"] = localUsers.size();
String response;
serializeJson(doc, response);
request->send(200, "application/json", response);
});
// Route zum Erstellen eines neuen Benutzers
server.on(
"/api/users/insert", HTTP_POST,
[](AsyncWebServerRequest *request) {
Serial.println("API: /api/users/insert aufgerufen");
},
NULL,
[](AsyncWebServerRequest *request, uint8_t *data, size_t len,
size_t index, size_t total) {
// Diese Funktion wird für den Body aufgerufen
static String bodyBuffer = "";
// Daten anhängen
for (size_t i = 0; i < len; i++) {
bodyBuffer += (char)data[i];
}
// Wenn alle Daten empfangen wurden
if (index + len == total) {
Serial.println("Request Body empfangen: '" + bodyBuffer + "'");
if (bodyBuffer.length() == 0) {
Serial.println("FEHLER: Request Body ist leer!");
DynamicJsonDocument response(200);
response["success"] = false;
response["error"] = "Request Body ist leer";
String jsonString;
serializeJson(response, jsonString);
request->send(400, "application/json", jsonString);
bodyBuffer = "";
return; return;
} }
// Handle user retrieval logic here DynamicJsonDocument doc(512);
DeserializationError error = deserializeJson(doc, bodyBuffer);
if (error) {
Serial.println("JSON Parse Error: " + String(error.c_str()));
DynamicJsonDocument response(200);
response["success"] = false;
response["error"] = "Invalid JSON: " + String(error.c_str());
String jsonString;
serializeJson(response, jsonString);
request->send(400, "application/json", jsonString);
bodyBuffer = "";
return;
}
String uid = doc["uid"].as<String>();
String name = doc["name"].as<String>();
Serial.println("Extrahierte UID: " + uid);
Serial.println("Extrahierter Name: " + name);
if (uid.length() == 0 || name.length() == 0) {
DynamicJsonDocument response(200);
response["success"] = false;
response["error"] = "UID und Name sind erforderlich";
String jsonString;
serializeJson(response, jsonString);
request->send(400, "application/json", jsonString);
bodyBuffer = "";
return;
}
// Prüfen ob Benutzer bereits existiert
bool userExists = false;
for (const auto &user : localUsers) {
if (user.uid == uid) {
userExists = true;
break;
}
}
if (userExists) {
DynamicJsonDocument response(200);
response["success"] = false;
response["error"] = "Benutzer bereits vorhanden";
String jsonString;
serializeJson(response, jsonString);
request->send(409, "application/json", jsonString);
bodyBuffer = "";
return;
}
// Neuen Benutzer direkt in das Array einfügen
LocalUser newUser;
newUser.uid = uid;
newUser.name = name;
newUser.timestamp = millis();
localUsers.push_back(newUser);
Serial.println("Benutzer über API eingefügt:");
Serial.println("UID: " + uid);
Serial.println("Name: " + name);
Serial.println("Gespeicherte Benutzer: " + String(localUsers.size()));
DynamicJsonDocument response(200);
response["success"] = true;
response["message"] = "Benutzer erfolgreich erstellt";
response["uid"] = uid;
response["name"] = name;
String jsonString;
serializeJson(response, jsonString);
request->send(200, "application/json", jsonString);
// Buffer zurücksetzen
bodyBuffer = "";
}
}); });
// Debug-Route für lokale Benutzer
server.on("/api/debug/users", HTTP_GET, [](AsyncWebServerRequest *request) {
String userList = getLocalUsersList();
request->send(200, "text/plain", userList);
});
// Location routes /api/location/ // Location routes /api/location/
server.on("/api/location/", HTTP_GET, [](AsyncWebServerRequest *request) { server.on("/api/location/", HTTP_GET, [](AsyncWebServerRequest *request) {
String result; String result;

View File

@@ -18,18 +18,16 @@
#include <debug.h> #include <debug.h>
#include <gamemodes.h> #include <gamemodes.h>
#include <licenceing.h> #include <licenceing.h>
#include <preferencemanager.h>
#include <rfid.h> #include <rfid.h>
#include <timesync.h> #include <timesync.h>
#include <webserverrouter.h> #include <webserverrouter.h>
#include <wificlass.h> #include <wificlass.h>
#include <preferencemanager.h>
const char *firmwareversion = "1.0.0"; // Version der Firmware const char *firmwareversion = "1.0.0"; // Version der Firmware
// moved to preferencemanager.h // moved to preferencemanager.h
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
@@ -52,7 +50,6 @@ void setup() {
loadWifiSettings(); loadWifiSettings();
loadLocationSettings(); loadLocationSettings();
setupWifi(); // WiFi initialisieren setupWifi(); // WiFi initialisieren
setupOTA(&server); setupOTA(&server);
@@ -61,7 +58,8 @@ void setup() {
setupLED(); setupLED();
setupMqttServer(); // MQTT Server initialisieren setupMqttServer(); // MQTT Server initialisieren
// setupBattery(); // setupBattery();
// setupRFID();
setupRFID(); // RFID initialisieren (ganz einfach)
} }
void loop() { void loop() {
@@ -69,5 +67,5 @@ void loop() {
loopMqttServer(); // MQTT Server in der Loop aufrufen loopMqttServer(); // MQTT Server in der Loop aufrufen
loopWebSocket(); loopWebSocket();
// loopBattery(); // Batterie-Loop aufrufen // loopBattery(); // Batterie-Loop aufrufen
// loopRFID(); // RFID Loop aufrufen loopRFID(); // RFID Loop aufrufen
} }

View File

@@ -1,189 +1,145 @@
#pragma once #pragma once
#include "databasebackend.h" #include <Adafruit_PN532.h>
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <MFRC522.h> #include <Wire.h>
#include <SPI.h>
// RFID Konfiguration - KORREKTE ESP32 Thing Plus Pins
#define SDA_PIN 23 // ESP32 Thing Plus SDA
#define SCL_PIN 22 // ESP32 Thing Plus SCL
#define IRQ_PIN 14
#define RST_PIN 15
// RFID Konfiguration // PN532 RFID Reader (mit IRQ und Reset-Pin)
#define RST_PIN 21 // Configurable, see typical pin layout above Adafruit_PN532 nfc(IRQ_PIN, RST_PIN);
#define SS_PIN 5 // Configurable, see typical pin layout above
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance // RFID Variablen
std::map<String, unsigned long> bool rfidInitialized = false;
blockedUIDs; // Map to store blocked UIDs and their timestamps bool readingMode = false;
const unsigned long BLOCK_DURATION = 10 * 1000; // 10 Seconds in milliseconds
// Neue Variablen für API-basiertes Lesen
bool rfidReadRequested = false;
String lastReadUID = ""; String lastReadUID = "";
bool rfidReadSuccess = false; unsigned long lastReadTime = 0;
unsigned long rfidReadStartTime = 0;
const unsigned long RFID_READ_TIMEOUT =
10000; // 10 Sekunden Timeout für API Requests
// Initialisiert den RFID-Reader und das SPI-Interface. // Initialisiert den RFID-Reader
void setupRFID() { void setupRFID() {
// I2C starten mit korrekten Pins
Wire.begin(SDA_PIN, SCL_PIN, 100000);
delay(100);
// SPI und RFID initialisieren // PN532 initialisieren
SPI.begin(); // Init SPI bus if (!nfc.begin()) {
mfrc522.PCD_Init(); // Init MFRC522 Serial.println("RFID: PN532 nicht gefunden!");
delay(4); // Optional delay. Some boards need more time after init to be ready return;
mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card }
// Reader details
// Firmware prüfen
uint32_t versiondata = nfc.getFirmwareVersion();
if (!versiondata) {
Serial.println("RFID: Firmware nicht lesbar!");
return;
}
// SAM Config
nfc.SAMConfig();
rfidInitialized = true;
Serial.println("RFID: Setup erfolgreich!");
} }
// Liest automatisch eine RFID-Karte ein und blockiert die UID für eine // Prüft ob RFID funktioniert
// bestimmte Zeit. bool checkRFID() {
void handleAutomaticRFID() { if (!rfidInitialized) {
if (!mfrc522.PICC_IsNewCardPresent()) { return false;
return; }
uint32_t versiondata = nfc.getFirmwareVersion();
return (versiondata != 0);
}
// Liest RFID-Karte - GANZ EINFACH
String readRFIDCard() {
if (!checkRFID()) {
return "";
} }
// Select one of the cards uint8_t uid[] = {0, 0, 0, 0, 0, 0, 0};
if (!mfrc522.PICC_ReadCardSerial()) { uint8_t uidLength;
return;
uint8_t success =
nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
if (!success) {
return ""; // Keine Karte
} }
// Read the UID // UID zu String
String uid = ""; String uidString = "";
for (byte i = 0; i < mfrc522.uid.size; i++) { for (uint8_t i = 0; i < uidLength; i++) {
if (i > 0) if (i > 0)
uid += ":"; uidString += ":";
if (mfrc522.uid.uidByte[i] < 0x10) if (uid[i] < 0x10)
uid += "0"; uidString += "0";
uid += String(mfrc522.uid.uidByte[i], HEX); uidString += String(uid[i], HEX);
} }
uidString.toUpperCase();
// Check if the UID is blocked Serial.println("RFID: " + uidString);
unsigned long currentTime = millis(); return uidString;
if (blockedUIDs.find(uid) != blockedUIDs.end()) {
if (currentTime - blockedUIDs[uid] < BLOCK_DURATION) {
Serial.print(F("UID blocked for 10 seconds. Remaining time: "));
Serial.print((BLOCK_DURATION - (currentTime - blockedUIDs[uid])) / 1000);
Serial.println(F(" seconds."));
Serial.println(uid);
return;
} else {
// Remove the UID from the blocked list if the block duration has passed
blockedUIDs.erase(uid);
}
}
// Process the UID
Serial.print(F("UID: "));
Serial.println(uid);
// Block the UID for 10 seconds
blockedUIDs[uid] = currentTime;
// 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."));
// Halt the card
mfrc522.PICC_HaltA();
} }
// Neue Funktion für API-basiertes RFID Lesen // RFID Loop - kontinuierliches Lesen wenn aktiviert
void loopRFID() {
if (!readingMode) {
return; // Lesen nicht aktiviert
}
// Liest eine RFID-Karte im API-Modus ein (für Web-Requests). static unsigned long lastCheck = 0;
void handleAPIRFIDRead() {
unsigned long currentTime = millis();
// Timeout prüfen // Nur alle 200ms prüfen (schneller für bessere Responsivität)
if (currentTime - rfidReadStartTime > RFID_READ_TIMEOUT) { if (millis() - lastCheck < 200) {
Serial.println("RFID API Timeout - keine Karte erkannt");
rfidReadRequested = false;
rfidReadSuccess = false;
lastReadUID = "";
return; return;
} }
lastCheck = millis();
// Prüfen ob neue Karte vorhanden ist // Versuchen zu lesen
if (!mfrc522.PICC_IsNewCardPresent()) { String uid = readRFIDCard();
return; if (uid.length() > 0) {
} // Nur neue UIDs oder nach 2 Sekunden Pause
if (uid != lastReadUID || millis() - lastReadTime > 2000) {
// 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++) {
if (mfrc522.uid.uidByte[i] < 0x10) {
uid += "0"; // Leading Zero für einstellige Hex-Werte
}
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; lastReadUID = uid;
rfidReadSuccess = true; lastReadTime = millis();
rfidReadRequested = false; Serial.println("RFID gelesen: " + uid);
}
// Karte "halt" setzen }
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
} }
// API Funktion: RFID Lesevorgang starten // API Routes
// Startet einen neuen RFID-Lesevorgang über die API.
void startRFIDRead() {
Serial.println("RFID API Lesevorgang gestartet...");
rfidReadRequested = true;
rfidReadSuccess = false;
lastReadUID = "";
rfidReadStartTime = millis();
}
// API Funktion: Prüfen ob Lesevorgang abgeschlossen
// Prüft, ob der aktuelle RFID-Lesevorgang abgeschlossen ist.
bool isRFIDReadComplete() { return !rfidReadRequested; }
// API Funktion: Ergebnis des Lesevorgangs abrufen
// Gibt das Ergebnis des letzten RFID-Lesevorgangs zurück.
String getRFIDReadResult(bool &success) {
success = rfidReadSuccess;
return lastReadUID;
}
// Richtet die HTTP-API-Routen für RFID-Operationen ein.
void setupRFIDRoute(AsyncWebServer &server) { void setupRFIDRoute(AsyncWebServer &server) {
server.on("/api/rfid/read", HTTP_GET, [](AsyncWebServerRequest *request) { // Toggle RFID Reading Mode
Serial.println("api/rfid/read"); server.on("/api/rfid/toggle", HTTP_POST, [](AsyncWebServerRequest *request) {
readingMode = !readingMode;
// 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); DynamicJsonDocument response(200);
if (rfidReadSuccess && lastReadUID.length() > 0) {
response["success"] = true; response["success"] = true;
response["uid"] = lastReadUID; response["reading_mode"] = readingMode;
response["message"] = "UID erfolgreich gelesen"; response["message"] =
readingMode ? "RFID Lesen gestartet" : "RFID Lesen gestoppt";
String jsonString;
serializeJson(response, jsonString);
request->send(200, "application/json", jsonString);
});
// Einzelnes Lesen (wie vorher)
server.on("/api/rfid/read", HTTP_GET, [](AsyncWebServerRequest *request) {
String uid = readRFIDCard();
DynamicJsonDocument response(200);
if (uid.length() > 0) {
response["success"] = true;
response["uid"] = uid;
response["message"] = "Karte gelesen";
} else { } else {
response["success"] = false; response["success"] = false;
response["error"] = "Keine RFID Karte erkannt oder Timeout"; response["error"] = "Keine Karte gefunden";
response["uid"] = ""; response["uid"] = "";
} }
@@ -192,107 +148,32 @@ void setupRFIDRoute(AsyncWebServer &server) {
request->send(200, "application/json", jsonString); request->send(200, "application/json", jsonString);
}); });
server.on( // Status und letzte gelesene UID
"/api/users/insert", HTTP_POST, [](AsyncWebServerRequest *request) {}, server.on("/api/rfid/status", HTTP_GET, [](AsyncWebServerRequest *request) {
NULL, DynamicJsonDocument response(300);
[](AsyncWebServerRequest *request, uint8_t *data, size_t len, response["success"] = true;
size_t index, size_t total) { response["rfid_initialized"] = rfidInitialized;
Serial.println("/api/users/insert"); response["reading_mode"] = readingMode;
response["last_uid"] = lastReadUID;
response["message"] =
readingMode ? "RFID Lesen aktiv" : "RFID Lesen inaktiv";
// Parse the incoming JSON payload String jsonString;
DynamicJsonDocument doc(512); serializeJson(response, jsonString);
DeserializationError error = deserializeJson(doc, data, len); request->send(200, "application/json", jsonString);
});
// UID zurücksetzen (nach erfolgreichem Lesen)
server.on("/api/rfid/clear", HTTP_POST, [](AsyncWebServerRequest *request) {
lastReadUID = ""; // UID zurücksetzen
lastReadTime = 0; // Zeit auch zurücksetzen
DynamicJsonDocument response(200); 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 {
// Process the data using the enterUserData function
Serial.println("Benutzerdaten empfangen:");
Serial.println("UID: " + uid);
Serial.println("Vorname: " + vorname);
Serial.println("Nachname: " + nachname);
Serial.println("Alter: " + String(alter));
bool dbSuccess =
enterUserData(uid, vorname, nachname, geburtsdatum, alter);
if (dbSuccess) {
response["success"] = true; response["success"] = true;
response["message"] = "Benutzer erfolgreich gespeichert"; response["message"] = "UID zurückgesetzt";
} else {
response["success"] = false;
response["error"] = "Fehler beim Speichern in der Datenbank";
}
}
}
// Send the response back to the client
String jsonString; String jsonString;
serializeJson(response, jsonString); serializeJson(response, jsonString);
request->send(200, "application/json", jsonString); request->send(200, "application/json", jsonString);
}); });
} }
// API Funktion: RFID Reader Status prüfen
// Prüft, ob der RFID-Reader korrekt funktioniert und gibt den Status zurück.
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) +
")");
return false;
}
}
// Hilfsfunktion: Blockierte UIDs aufräumen
// Entfernt UIDs aus der Blockliste, deren Blockdauer abgelaufen ist.
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) {
it = blockedUIDs.erase(it);
} else {
++it;
}
}
}
// Hauptschleife für das RFID-Handling (automatisch und API-basiert).
void loopRFID() {
// Originale Funktionalität für automatisches Lesen
if (!rfidReadRequested) {
handleAutomaticRFID();
}
// API-basiertes Lesen verarbeiten
if (rfidReadRequested) {
handleAPIRFIDRead();
}
}

View File

@@ -1,15 +1,11 @@
// Zeit-bezogene Variablen und Includes // Zeit-bezogene Variablen und Includes
#pragma once #pragma once
#include "RTClib.h"
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <Wire.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
RTC_PCF8523 rtc;
// Globale Zeitvariablen // Globale Zeitvariablen
struct timeval tv; struct timeval tv;
struct timezone tz; struct timezone tz;
@@ -90,8 +86,6 @@ bool setSystemTime(long timestamp) {
// Initialisiert die Zeit-API und richtet die HTTP-Endpunkte ein. // Initialisiert die Zeit-API und richtet die HTTP-Endpunkte ein.
void setupTimeAPI(AsyncWebServer &server) { void setupTimeAPI(AsyncWebServer &server) {
// setupRTC();
// API-Endpunkt: Aktuelle Zeit abrufen // API-Endpunkt: Aktuelle Zeit abrufen
server.on("/api/time", HTTP_GET, [](AsyncWebServerRequest *request) { server.on("/api/time", HTTP_GET, [](AsyncWebServerRequest *request) {
String response = getCurrentTimeJSON(); String response = getCurrentTimeJSON();