Hi, I have a project that involves a React Web App and my ESP32. I am having a difficult understanding and whether to have my whole vite build into 1 gzip or for each file (html and js) to have the gzip copies each. Alongside implementing LittleFS and websocket to my Arduino code. The only goal of my web socket is to trigger something like a scan mode once my add rfid dialog in my react app is shown. Otherwise, it just uses the door logic with my react app not allowing the ESP32 to add any new RFID UIDs. And also to change the WIFI STA SSID and Password from my React App as well.
Here is my code:
#include <WiFi.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Firebase_ESP_Client.h>
// ---------- WiFi ----------
const char* WIFI_SSID =
const char* WIFI_PASS =
// ---------- RFID / hardware ----------
#define NR_OF_READERS 2
#define SS_1 5
#define SS_2 17
#define RST_1 21
#define RST_2 22
#define RELAY_PIN 32 // solenoid relay pin
byte ssPins [] = {SS_1, SS_2};
byte rstPins [] = {RST_1, RST_2};
unsigned long doorUnlockTime = 0;
bool isDoorUnlocked = false;
const long doorOpenDuration = 6000;
MFRC522 mfrc522[NR_OF_READERS];
//FIREBASE INSTANCES
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
#define API_KEY
#define DATABASE_URL
#define ADMIN_EMAIL
#define ADMIN_PASSWORD
String uidToString(byte *buffer, byte bufferSize) {
String localUID = "";
for (byte i = 0; i < bufferSize; i++) {
if (buffer[i] < 0x10) localUID += "0";
localUID += String(buffer[i], HEX);
if (i < bufferSize - 1) localUID += ":";
}
localUID.toUpperCase();
return localUID;
}
void setup(){
Serial.begin(115200);
while (!Serial);
SPI.begin();
initReader();
connectWiFi();
connectFirebase();
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
}
void loop() {
checkRFID();
manageDoorLock();
}
void connectFirebase() {
Serial.print("Connecting to Firebase... ");
// Assign the project credentials
config.api_key = API_KEY;
config.database_url = DATABASE_URL;
auth.user.email = ADMIN_EMAIL;
auth.user.password = ADMIN_PASSWORD;
// Begin Firebase connection
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
// Check for connection status
if (Firebase.ready()){
Serial.println("Connected and authenticated.");
} else {
Serial.println("Authentication failed.");
Serial.println("REASON: " + fbdo.errorReason());
}
}
void addOrUpdateRfidTag(String uid) {
// Construct the specific path for this UID.
String path = "/rfid_tags/" + uid;
Serial.println("Processing RFID tag: " + uid);
Serial.println();
String statusPath = path + "/status";
if (Firebase.RTDB.getString(&fbdo, statusPath)) {
// Tag exists - just update the lastSeen timestamp
Serial.println("Known RFID tag. New timestamp for currently scanned UID.");
} else {
// Check if the error is because the path doesn't exist
if (fbdo.errorCode() == FIREBASE_ERROR_PATH_NOT_EXIST || fbdo.dataType() == "null") {
// New tag - create it
Serial.println("New RFID tag detected. Adding to database...");
FirebaseJson json;
json.set("status", "inactive"); //boolean in mob/web app 1 = active | 0 = inactive, changes in realtime db
json.set("createdAt/.sv", "timestamp");
if (Firebase.RTDB.setJSON(&fbdo, path, &json)) {
Serial.println("New tag added successfully!");
} else {
Serial.println("ERROR: Failed to add new tag: " + fbdo.errorReason());
}
} else {
// Some other error occurred
Serial.println("ERROR: Could not check for tag. Reason: " + fbdo.errorReason());
Serial.println("Error code: " + String(fbdo.errorCode()));
}
}
}
void logAccessAttempt(String uid, String result, uint8_t reader) {
String path = "/access_logs";
String readerRole = "";
FirebaseJson json;
if(reader == 0){
readerRole = "Entry";
}else if (reader == 1){
readerRole = "Exit";
}else{
Serial.println("Reader detection failed");
}
json.set("uid", uid);
json.set("result", result);
json.set("readerRole", readerRole);
json.set("timestamp/.sv", "timestamp");
Firebase.RTDB.pushJSON(&fbdo, path, &json);
}
void manageDoorLock() {
// If the door is unlocked and 6 seconds have passed...
if (isDoorUnlocked && (millis() - doorUnlockTime >= doorOpenDuration)) {
Serial.println("6 seconds have passed. Locking the door.");
digitalWrite(RELAY_PIN, LOW); // Lock the door
isDoorUnlocked = false; // Update the state
}
}
void doorLogic(String uid, uint8_t reader) {
String path = "/rfid_tags/" + uid;
bool accessGranted = false;
String reason = "Not Found";
Serial.println("Checking database for UID: " + uid);
// Query Firebase for the status of this specific UID
if (Firebase.RTDB.getString(&fbdo, path + "/status")) {
if (fbdo.dataType() == "string") {
String status = fbdo.stringData();
Serial.println("Database status: " + status);
if (status == "active") {
accessGranted = true;
reason = "Granted";
} else {
reason = "Denied (" + status + ")";
}
}
} else {
Serial.println("Error fetching status: " + fbdo.errorReason());
}
// --- Perform Action Based on Query Result ---
if (accessGranted) {
Serial.println("ACCESS GRANTED");
digitalWrite(RELAY_PIN, HIGH);
isDoorUnlocked = true;
doorUnlockTime = millis();
//digitalWrite(RELAY_PIN, LOW);
} else {
Serial.println("ACCESS DENIED");
}
// Log the final result of the attempt
logAccessAttempt(uid, reason, reader);
}
void checkRFID() {
for (uint8_t reader = 0; reader < NR_OF_READERS; reader++) {
// Look for new cards and read their serial number
if (mfrc522[reader].PICC_IsNewCardPresent() && mfrc522[reader].PICC_ReadCardSerial()) {
byte uidSize = mfrc522[reader].uid.size;
if (uidSize == 4 || uidSize == 7) {
String uid = uidToString(mfrc522[reader].uid.uidByte, uidSize);
Serial.print("\nReader " + String(reader) + ": Valid Card Scanned. UID: " + uid);
Serial.println();
addOrUpdateRfidTag(uid);
doorLogic(uid, reader);
} else {
Serial.print("\nReader " + String(reader) + ": Invalid UID size detected (" + String(uidSize) + " bytes). Ignoring scan.");
}
mfrc522[reader].PICC_HaltA();
mfrc522[reader].PCD_StopCrypto1();
delay(500);
}
}
}
void dump_byte_array(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], HEX);
}
}
void connectWiFi() {
WiFi.mode(WIFI_AP_STA);
WiFi.begin();
Serial.print("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED) {
Serial.print('.');
delay(1000);
}
Serial.println();
Serial.println(WiFi.localIP());
}
void initReader() {
for (uint8_t reader = 0; reader < NR_OF_READERS; reader++) {
mfrc522[reader].PCD_Init(ssPins[reader], rstPins[reader]);
Serial.print(F("Reader "));
Serial.print(reader);
Serial.print(F(": "));
mfrc522[reader].PCD_DumpVersionToSerial();
}
}
My JS code:
// WebSocket service for ESP32 communication
export class ESPWebSocketService {
constructor() {
this.ws = null;
this.isConnected = false;
this.scanModeEnabled = false;
this.messageHandlers = new Set();
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
// Connect to ESP32 WebSocket
connect(espIP = '', port = 81) {
try {
this.ws = new WebSocket(`ws://${espIP}:${port}`);
this.ws.onopen = () => {
console.log('Connected to ESP32 WebSocket');
this.isConnected = true;
this.reconnectAttempts = 0;
};
this.ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
this.handleMessage(message);
} catch (error) {
console.error('Error parsing WebSocket message:', error);
}
};
this.ws.onclose = () => {
console.log('ESP32 WebSocket connection closed');
this.isConnected = false;
this.attemptReconnect(espIP, port);
};
this.ws.onerror = (error) => {
console.error('ESP32 WebSocket error:', error);
};
} catch (error) {
console.error('Error connecting to ESP32:', error);
}
}
// Attempt to reconnect
attemptReconnect(espIP, port) {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
console.log(`Attempting to reconnect... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
setTimeout(() => {
this.connect(espIP, port);
}, 2000 * this.reconnectAttempts); // Exponential backoff
}
}
// Handle incoming messages from ESP32
handleMessage(message) {
// Notify all message handlers
this.messageHandlers.forEach(handler => handler(message));
// Handle specific message types
switch (message.type) {
case 'rfid_scanned':
if (this.scanModeEnabled) {
console.log('New RFID scanned in scan mode:', message.uid);
// The ESP32 will automatically add to Firebase when scan mode is enabled
}
break;
case 'access_attempt':
console.log('Access attempt logged:', message);
break;
default:
console.log('Unknown message type:', message);
}
}
// Enable/disable scan mode
setScanMode(enabled) {
if (this.isConnected && this.ws) {
const command = {
type: 'scan_mode',
enabled: enabled
};
this.ws.send(JSON.stringify(command));
this.scanModeEnabled = enabled;
console.log(`Scan mode ${enabled ? 'enabled' : 'disabled'}`);
} else {
console.warn('WebSocket not connected. Cannot set scan mode.');
}
}
// Add message handler
addMessageHandler(handler) {
this.messageHandlers.add(handler);
}
// Remove message handler
removeMessageHandler(handler) {
this.messageHandlers.delete(handler);
}
// Send WiFi configuration
sendWiFiConfig(ssid, password) {
if (this.isConnected && this.ws) {
const command = {
type: 'wifi_config',
ssid: ssid,
password: password
};
this.ws.send(JSON.stringify(command));
console.log('WiFi configuration sent to ESP32');
} else {
console.warn('WebSocket not connected. Cannot send WiFi config.');
}
}
// Disconnect
disconnect() {
if (this.ws) {
this.ws.close();
this.ws = null;
this.isConnected = false;
this.scanModeEnabled = false;
}
}
// Get connection status
getConnectionStatus() {
return {
connected: this.isConnected,
scanMode: this.scanModeEnabled,
reconnectAttempts: this.reconnectAttempts
};
}
}
// Create singleton instances
export const rfidService = new RFIDService();
export const espWebSocket = new ESPWebSocketService();