Sync
This commit is contained in:
548
src/rfid.h
Normal file
548
src/rfid.h
Normal file
@@ -0,0 +1,548 @@
|
||||
#include <WiFi.h>
|
||||
#include <HTTPClient.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <SPI.h>
|
||||
#include <MFRC522.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
// RFID Configuration
|
||||
#define RST_PIN 22 // Reset pin
|
||||
#define SS_PIN 21 // SDA pin
|
||||
#define SCK_PIN 18 // SCK pin
|
||||
#define MOSI_PIN 23 // MOSI pin
|
||||
#define MISO_PIN 19 // MISO pin
|
||||
|
||||
|
||||
// PostgreSQL API Configuration
|
||||
// Replace with your database API endpoint (e.g., Supabase, Railway, Render, etc.)
|
||||
const char* DB_API_BASE_URL = "https://your-database-api.com/api";
|
||||
const char* DB_API_KEY = "YOUR_API_KEY"; // If using Supabase or similar service
|
||||
|
||||
// Server Configuration
|
||||
MFRC522 rfid(SS_PIN, RST_PIN);
|
||||
|
||||
// HTTP clients
|
||||
HTTPClient httpClient;
|
||||
WiFiClientSecure secureClient;
|
||||
|
||||
// Database table structure
|
||||
|
||||
// CORS headers
|
||||
void setCORSHeaders(AsyncWebServerResponse* response) {
|
||||
response->addHeader("Access-Control-Allow-Origin", "*");
|
||||
response->addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
response->addHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, apikey");
|
||||
response->addHeader("Access-Control-Max-Age", "86400");
|
||||
}
|
||||
|
||||
// Initialize RFID
|
||||
void initRFID() {
|
||||
SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, SS_PIN);
|
||||
rfid.PCD_Init();
|
||||
|
||||
// Check if RFID reader is connected
|
||||
byte version = rfid.PCD_ReadRegister(rfid.VersionReg);
|
||||
if (version == 0x00 || version == 0xFF) {
|
||||
Serial.println("WARNING: RFID reader not found!");
|
||||
} else {
|
||||
Serial.print("RFID reader found, version: 0x");
|
||||
Serial.println(version, HEX);
|
||||
}
|
||||
|
||||
Serial.println("RFID Reader initialized");
|
||||
}
|
||||
|
||||
// Initialize WiFi
|
||||
void initWiFi() {
|
||||
WiFi.begin(ssid, password);
|
||||
Serial.print("Connecting to WiFi");
|
||||
|
||||
int attempts = 0;
|
||||
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
|
||||
delay(1000);
|
||||
Serial.print(".");
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
Serial.println();
|
||||
Serial.print("WiFi connected! IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
} else {
|
||||
Serial.println();
|
||||
Serial.println("WiFi connection failed!");
|
||||
}
|
||||
}
|
||||
|
||||
// Test database connection
|
||||
bool testDatabaseConnection() {
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
Serial.println("WiFi not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
HTTPClient http;
|
||||
http.begin(DB_API_BASE_URL);
|
||||
|
||||
// Add API key header if using services like Supabase
|
||||
if (strlen(DB_API_KEY) > 0) {
|
||||
http.addHeader("apikey", DB_API_KEY);
|
||||
http.addHeader("Authorization", String("Bearer ") + DB_API_KEY);
|
||||
}
|
||||
|
||||
int httpResponseCode = http.GET();
|
||||
bool success = (httpResponseCode > 0 && httpResponseCode < 400);
|
||||
|
||||
if (success) {
|
||||
Serial.println("✅ Database connection successful");
|
||||
} else {
|
||||
Serial.print("❌ Database connection failed. HTTP code: ");
|
||||
Serial.println(httpResponseCode);
|
||||
if (httpResponseCode > 0) {
|
||||
Serial.println("Response: " + http.getString());
|
||||
}
|
||||
}
|
||||
|
||||
http.end();
|
||||
return success;
|
||||
}
|
||||
|
||||
// Read RFID UID
|
||||
String readRFIDUID() {
|
||||
// Check for new cards
|
||||
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String uid = "";
|
||||
for (byte i = 0; i < rfid.uid.size; i++) {
|
||||
if (uid.length() > 0) uid += ":";
|
||||
if (rfid.uid.uidByte[i] < 0x10) uid += "0";
|
||||
uid += String(rfid.uid.uidByte[i], HEX);
|
||||
}
|
||||
|
||||
uid.toUpperCase();
|
||||
rfid.PICC_HaltA();
|
||||
rfid.PCD_StopCrypto1();
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
// Make HTTP request to database API
|
||||
DynamicJsonDocument makeDBRequest(const String& method, const String& endpoint, const String& payload = "") {
|
||||
DynamicJsonDocument response(4096);
|
||||
|
||||
if (WiFi.status() != WL_CONNECTED) {
|
||||
response["success"] = false;
|
||||
response["error"] = "WiFi not connected";
|
||||
return response;
|
||||
}
|
||||
|
||||
HTTPClient http;
|
||||
String url = String(DB_API_BASE_URL) + endpoint;
|
||||
http.begin(url);
|
||||
|
||||
// Set headers
|
||||
http.addHeader("Content-Type", "application/json");
|
||||
if (strlen(DB_API_KEY) > 0) {
|
||||
http.addHeader("apikey", DB_API_KEY);
|
||||
http.addHeader("Authorization", String("Bearer ") + DB_API_KEY);
|
||||
}
|
||||
|
||||
int httpResponseCode;
|
||||
|
||||
if (method == "GET") {
|
||||
httpResponseCode = http.GET();
|
||||
} else if (method == "POST") {
|
||||
httpResponseCode = http.POST(payload);
|
||||
} else if (method == "DELETE") {
|
||||
httpResponseCode = http.sendRequest("DELETE", payload);
|
||||
} else {
|
||||
response["success"] = false;
|
||||
response["error"] = "Unsupported HTTP method";
|
||||
http.end();
|
||||
return response;
|
||||
}
|
||||
|
||||
if (httpResponseCode > 0) {
|
||||
String responseString = http.getString();
|
||||
|
||||
if (httpResponseCode >= 200 && httpResponseCode < 300) {
|
||||
// Parse successful response
|
||||
DeserializationError error = deserializeJson(response, responseString);
|
||||
if (error) {
|
||||
response.clear();
|
||||
response["success"] = false;
|
||||
response["error"] = "Failed to parse response JSON";
|
||||
response["raw_response"] = responseString;
|
||||
} else {
|
||||
// Add success flag if not present
|
||||
if (!response.containsKey("success")) {
|
||||
response["success"] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle HTTP errors
|
||||
response["success"] = false;
|
||||
response["error"] = "HTTP Error " + String(httpResponseCode);
|
||||
response["raw_response"] = responseString;
|
||||
}
|
||||
} else {
|
||||
response["success"] = false;
|
||||
response["error"] = "HTTP request failed: " + String(httpResponseCode);
|
||||
}
|
||||
|
||||
http.end();
|
||||
return response;
|
||||
}
|
||||
|
||||
// Get all users from database
|
||||
DynamicJsonDocument getAllUsers(int limit = 50) {
|
||||
String endpoint = "/rfid_users?select=*&order=created_at.desc";
|
||||
if (limit > 0) {
|
||||
endpoint += "&limit=" + String(limit);
|
||||
}
|
||||
return makeDBRequest("GET", endpoint);
|
||||
}
|
||||
|
||||
// Create new user in database
|
||||
DynamicJsonDocument createUser(const String& uid, const String& vorname, const String& nachname, int alter) {
|
||||
DynamicJsonDocument payload(512);
|
||||
payload["uid"] = uid;
|
||||
payload["vorname"] = vorname;
|
||||
payload["nachname"] = nachname;
|
||||
payload["alter"] = alter;
|
||||
|
||||
String payloadString;
|
||||
serializeJson(payload, payloadString);
|
||||
|
||||
return makeDBRequest("POST", "/rfid_users", payloadString);
|
||||
}
|
||||
|
||||
// Delete user from database
|
||||
DynamicJsonDocument deleteUser(const String& uid) {
|
||||
String endpoint = "/rfid_users?uid=eq." + uid;
|
||||
return makeDBRequest("DELETE", endpoint);
|
||||
}
|
||||
|
||||
// Check if user exists
|
||||
bool userExists(const String& uid) {
|
||||
String endpoint = "/rfid_users?uid=eq." + uid + "&select=uid";
|
||||
DynamicJsonDocument result = makeDBRequest("GET", endpoint);
|
||||
|
||||
if (result["success"].as<bool>() && result.containsKey("data")) {
|
||||
JsonArray data = result["data"].as<JsonArray>();
|
||||
return data.size() > 0;
|
||||
} else if (result.containsKey("uid")) {
|
||||
// Direct response format (some APIs return object directly)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setupRFID(AsyncWebServer& server) {
|
||||
Serial.begin(115200);
|
||||
Serial.println("\n=== ESP32 RFID Server with PostgreSQL ===");
|
||||
|
||||
// Initialize components
|
||||
initRFID();
|
||||
|
||||
// Test database connection
|
||||
delay(2000); // Wait for WiFi to stabilize
|
||||
testDatabaseConnection();
|
||||
|
||||
// Configure time for timestamps
|
||||
configTime(0, 0, "pool.ntp.org");
|
||||
|
||||
// CORS preflight handler
|
||||
server.onNotFound([](AsyncWebServerRequest *request) {
|
||||
if (request->method() == HTTP_OPTIONS) {
|
||||
AsyncWebServerResponse *response = request->beginResponse(200);
|
||||
setCORSHeaders(response);
|
||||
request->send(response);
|
||||
} else {
|
||||
request->send(404, "application/json", "{\"error\":\"Not Found\"}");
|
||||
}
|
||||
});
|
||||
|
||||
// Health check endpoint
|
||||
server.on("/api/health", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
DynamicJsonDocument response(512);
|
||||
response["status"] = "OK";
|
||||
response["wifi_connected"] = (WiFi.status() == WL_CONNECTED);
|
||||
response["wifi_ip"] = WiFi.localIP().toString();
|
||||
response["free_heap"] = ESP.getFreeHeap();
|
||||
response["rfid_status"] = "connected";
|
||||
response["database"] = "postgresql";
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(200, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
});
|
||||
|
||||
// Get all users
|
||||
server.on("/api/users", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
Serial.println("GET /api/users - Loading users from database");
|
||||
|
||||
int limit = 50;
|
||||
if (request->hasParam("limit")) {
|
||||
limit = request->getParam("limit")->value().toInt();
|
||||
}
|
||||
|
||||
DynamicJsonDocument dbResponse = getAllUsers(limit);
|
||||
DynamicJsonDocument response(8192);
|
||||
|
||||
if (dbResponse["success"].as<bool>()) {
|
||||
response["success"] = true;
|
||||
|
||||
// Handle different response formats
|
||||
if (dbResponse.containsKey("data")) {
|
||||
response["data"] = dbResponse["data"];
|
||||
response["count"] = dbResponse["data"].as<JsonArray>().size();
|
||||
} else {
|
||||
// Direct array response
|
||||
response["data"] = dbResponse.as<JsonArray>();
|
||||
response["count"] = dbResponse.as<JsonArray>().size();
|
||||
}
|
||||
} else {
|
||||
response["success"] = false;
|
||||
response["error"] = dbResponse["error"];
|
||||
response["data"] = JsonArray();
|
||||
response["count"] = 0;
|
||||
}
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(200, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
});
|
||||
|
||||
// Create new user
|
||||
server.on("/api/users", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
// This will be handled in the body handler
|
||||
}, NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
|
||||
// Parse JSON body
|
||||
DynamicJsonDocument requestDoc(1024);
|
||||
DeserializationError error = deserializeJson(requestDoc, (char*)data, len);
|
||||
|
||||
DynamicJsonDocument response(1024);
|
||||
|
||||
if (error) {
|
||||
response["success"] = false;
|
||||
response["error"] = "Invalid JSON";
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(400, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (!requestDoc.containsKey("uid") || !requestDoc.containsKey("vorname") ||
|
||||
!requestDoc.containsKey("nachname") || !requestDoc.containsKey("alter")) {
|
||||
response["success"] = false;
|
||||
response["error"] = "Missing required fields: uid, vorname, nachname, alter";
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(400, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
String uid = requestDoc["uid"].as<String>();
|
||||
String vorname = requestDoc["vorname"].as<String>();
|
||||
String nachname = requestDoc["nachname"].as<String>();
|
||||
int alter = requestDoc["alter"].as<int>();
|
||||
|
||||
Serial.println("POST /api/users - Creating user: " + uid);
|
||||
|
||||
// Check if user already exists
|
||||
if (userExists(uid)) {
|
||||
response["success"] = false;
|
||||
response["error"] = "User with this UID already exists";
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(409, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create user in database
|
||||
DynamicJsonDocument dbResponse = createUser(uid, vorname, nachname, alter);
|
||||
|
||||
if (dbResponse["success"].as<bool>()) {
|
||||
response["success"] = true;
|
||||
response["message"] = "User created successfully";
|
||||
|
||||
// Include user data in response
|
||||
if (dbResponse.containsKey("data")) {
|
||||
response["data"] = dbResponse["data"];
|
||||
} else {
|
||||
DynamicJsonDocument userData(512);
|
||||
userData["uid"] = uid;
|
||||
userData["vorname"] = vorname;
|
||||
userData["nachname"] = nachname;
|
||||
userData["alter"] = alter;
|
||||
response["data"] = userData;
|
||||
}
|
||||
|
||||
Serial.println("✅ User created successfully: " + uid);
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(201, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
} else {
|
||||
response["success"] = false;
|
||||
response["error"] = dbResponse["error"];
|
||||
|
||||
Serial.println("❌ Failed to create user: " + dbResponse["error"].as<String>());
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(500, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
}
|
||||
});
|
||||
|
||||
// Delete user by UID
|
||||
server.on("/api/users/*", HTTP_DELETE, [](AsyncWebServerRequest *request) {
|
||||
String pathInfo = request->url();
|
||||
String uid = pathInfo.substring(pathInfo.lastIndexOf('/') + 1);
|
||||
|
||||
// URL decode the UID
|
||||
uid.replace("%3A", ":");
|
||||
uid.toUpperCase();
|
||||
|
||||
Serial.println("DELETE /api/users - Deleting user: " + uid);
|
||||
|
||||
DynamicJsonDocument response(512);
|
||||
|
||||
// Check if user exists
|
||||
if (!userExists(uid)) {
|
||||
response["success"] = false;
|
||||
response["error"] = "User not found";
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(404, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete user from database
|
||||
DynamicJsonDocument dbResponse = deleteUser(uid);
|
||||
|
||||
if (dbResponse["success"].as<bool>()) {
|
||||
response["success"] = true;
|
||||
response["message"] = "User deleted successfully";
|
||||
|
||||
Serial.println("✅ User deleted successfully: " + uid);
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(200, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
} else {
|
||||
response["success"] = false;
|
||||
response["error"] = dbResponse["error"];
|
||||
|
||||
Serial.println("❌ Failed to delete user: " + dbResponse["error"].as<String>());
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(500, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
}
|
||||
});
|
||||
|
||||
// Read RFID UID endpoint
|
||||
server.on("/api/rfid/read", HTTP_POST, [](AsyncWebServerRequest *request) {
|
||||
DynamicJsonDocument response(512);
|
||||
|
||||
Serial.println("POST /api/rfid/read - Reading RFID card");
|
||||
|
||||
// Try to read RFID for up to 5 seconds
|
||||
unsigned long startTime = millis();
|
||||
String uid = "";
|
||||
|
||||
while (millis() - startTime < 5000) { // 5 second timeout
|
||||
uid = readRFIDUID();
|
||||
if (uid.length() > 0) {
|
||||
break;
|
||||
}
|
||||
delay(100);
|
||||
}
|
||||
|
||||
if (uid.length() > 0) {
|
||||
response["success"] = true;
|
||||
response["uid"] = uid;
|
||||
response["message"] = "UID read successfully";
|
||||
|
||||
Serial.println("✅ RFID UID read: " + uid);
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(200, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
} else {
|
||||
response["success"] = false;
|
||||
response["error"] = "No RFID card detected";
|
||||
response["message"] = "Please place an RFID card near the reader";
|
||||
|
||||
Serial.println("❌ No RFID card detected");
|
||||
|
||||
String jsonString;
|
||||
serializeJson(response, jsonString);
|
||||
|
||||
AsyncWebServerResponse *resp = request->beginResponse(408, "application/json", jsonString);
|
||||
setCORSHeaders(resp);
|
||||
request->send(resp);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle CORS preflight for all routes
|
||||
server.onNotFound([](AsyncWebServerRequest *request) {
|
||||
if (request->method() == HTTP_OPTIONS) {
|
||||
AsyncWebServerResponse *response = request->beginResponse(200);
|
||||
setCORSHeaders(response);
|
||||
request->send(response);
|
||||
} else {
|
||||
AsyncWebServerResponse *response = request->beginResponse(404, "application/json", "{\"error\":\"Not Found\"}");
|
||||
setCORSHeaders(response);
|
||||
request->send(response);
|
||||
}
|
||||
});
|
||||
|
||||
// Start server
|
||||
Serial.println("Connected to PostgreSQL database");
|
||||
Serial.println("=====================================");
|
||||
}
|
||||
Reference in New Issue
Block a user