How to integrate MFRC522 and SD card module and make them work together on ESP32 Devkit V4?

I need to take readings when i detect and authorised RFID. The readinsg must be saved to the SD card. How to accomplish this? Also my Buttons and Buzzer dont work alongside the RC522. please help!!! I have attached the code.

#include <EEPROM.h>
#include <SPI.h>
#include <MFRC522.h>
#include <WiFiManager.h> // Include WiFiManager library for AP mode

// Button pins
#define BUTTON_TWO_PIN 2

// RGB LED pins
#define RED_PIN 26
#define GREEN_PIN 25
#define BLUE_PIN 32

// Pin Definitions
#define RST_PIN 0   // Reset pin for RC522
#define SS_PIN 5    // Slave select pin for RC522

MFRC522 rfid(SS_PIN, RST_PIN);

#define EEPROM_SIZE 512        // Size of EEPROM storage (adjust as needed)
#define CARD_DATA_SIZE 4       // Card UID size (4 bytes)
#define MAX_CARDS 5            // Maximum number of stored cards
#define EEPROM_START_ADDRESS 0 // Starting address for card data

// Modbus Command
uint8_t Com[8] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x04, 0x44, 0x09 };

// Variables for sensor readings
float tem, hum, ph;
bool startReadings = false;

// Button and buzzer pins
#define BUTTON_PIN 2
#define BUZZER_PIN 26
#define RS485_DIR_PIN 4 // RS485 Direction Control Pin

// Hardcoded Master Card UID
const byte MASTER_UID[CARD_DATA_SIZE] = {0x43, 0xD4, 0x31, 0x02};

// Function Prototypes
void writeCardToEEPROM(byte *uid, int address);
bool isCardStored(byte *uid, int &address);
void removeCardFromEEPROM(int address);
void clearCardFromEEPROM(int address);
void clearEEPROM();
void printEEPROMContents();
void debugPrintUID(byte *uid);

byte currentCard[4]; // Holds the UID of the currently scanned card

// Flags for button actions and WiFiManager state
bool isWiFiManagerActive = false; // Tracks if WiFiManager is active

// Timers for button presses
unsigned long buttonOnePressTime = 0;
unsigned long buttonTwoPressTime = 0;

void setup() {
   // Initialize Serial for Modbus
  Serial.begin(9600, SERIAL_8N1);
  // clearEEPROM();
  printEEPROMContents();

  SPI.begin();
  rfid.PCD_Init();

  // Initialize EEPROM
  if (!EEPROM.begin(EEPROM_SIZE)) {
    Serial.println("Failed to initialize EEPROM");
    while (1); // Halt execution
  }

  // Initialize button pins as input with pull-up resistors
  pinMode(BUTTON_TWO_PIN, INPUT_PULLUP);
  
  // Configure pins
  pinMode(2, INPUT_PULLUP); // Use internal pull-up for button
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(RS485_DIR_PIN, OUTPUT);
  digitalWrite(RS485_DIR_PIN, LOW); // Default to receive mode

  // Initialize RGB LED pins
  analogWrite(RED_PIN, 0);   // Initialize with 0 brightness
  analogWrite(GREEN_PIN, 0); // Initialize with 0 brightness
  analogWrite(BLUE_PIN, 0);  // Initialize with 0 brightness

  // Auto-connect to saved WiFi credentials
  autoConnectToWiFi();
  Serial.println("System initialized. Scan a card...");
}

void loop() {
  selectRFID();
  if (!scanCard()) return;
 
  // Check if it's the Master card
  if (memcmp(currentCard, MASTER_UID, CARD_DATA_SIZE) == 0) {
    Serial.println("Master Card Detected! Entering Admin Mode...");
    adminMode();
    return;
  }

  // Check if the card is stored
  int cardAddress = -1;
  if (isCardStored(currentCard, cardAddress)) {
    Serial.println("Access Granted!");
      // Check if the button is pressed
      if (digitalRead(2) == LOW && !startReadings) {
    delay(50); // Debounce
    if (digitalRead(2)== LOW) { // Confirm button press
      Serial.println("Button Pressed! Starting readings...");
      startReadings = true; // Start taking readings
      takeReadings();
      startReadings = false; 
    }
  } 
  }else {
    Serial.println("Access Denied!");
  }

  // Check button two (WiFi button) for 3-second hold
  if (isButtonPressed(BUTTON_TWO_PIN)) {
    Serial.println("WiFi button pressed");
    if (buttonTwoPressTime == 0) {
      buttonTwoPressTime = millis(); // Start timing for button two
    }
    if (millis() - buttonTwoPressTime >= 3000 && !isWiFiManagerActive) {
      isWiFiManagerActive = true;
      activateWiFiManagerWithFeedback(); // Execute WiFiManager
      isWiFiManagerActive = false;
      buttonTwoPressTime = 0; // Reset timing for button two
    }
  } else {
    buttonTwoPressTime = 0; // Reset timing if button two is released
  }
}

// Scan for RFID Card
bool scanCard() {
  selectRFID();
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
    digitalWrite(SS_PIN, HIGH); // Deselect RFID after operation
    return false;
  }

  // Copy UID to currentCard
  for (byte i = 0; i < CARD_DATA_SIZE; i++) {
    currentCard[i] = rfid.uid.uidByte[i];
  }
  rfid.PICC_HaltA();
  digitalWrite(SS_PIN, HIGH); // Deselect RFID after operation
  return true;
}

// Admin Mode: Add or Remove Cards
void adminMode() {
  Serial.println("Scan a card to add or remove it...");
  printEEPROMContents(); // Debug: Show current EEPROM contents

  while (!scanCard()) {
    delay(100);
  }

  // Debugging: Print the scanned card UID
  Serial.print("Scanned Card UID: ");
  debugPrintUID(currentCard);

  int emptySlotAddress = -1;

  // Check if card is already stored
  int cardAddress = -1;
  if (isCardStored(currentCard, cardAddress)) {
    Serial.println("Card is already stored. Removing it...");
    removeCardFromEEPROM(cardAddress);
    Serial.println("Card removed.");
    return;
  }

  // Find an empty slot
  for (int i = 0; i < MAX_CARDS; i++) {
    int address = EEPROM_START_ADDRESS + (CARD_DATA_SIZE * i);
    byte storedUID[4];
    for (int j = 0; j < CARD_DATA_SIZE; j++) {
      storedUID[j] = EEPROM.read(address + j);
    }

    // Check if the slot is empty
    bool slotEmpty = true;
    for (byte j = 0; j < CARD_DATA_SIZE; j++) {
      if (storedUID[j] != 0xFF) {
        slotEmpty = false;
        break;
      }
    }

    if (slotEmpty) {
      emptySlotAddress = address;
      break;
    }
  }

  if (emptySlotAddress != -1) {
    writeCardToEEPROM(currentCard, emptySlotAddress);
    Serial.println("Card added successfully!");
  } else {
    Serial.println("No space available to add a new card.");
  }
}

// Write Card UID to EEPROM
void writeCardToEEPROM(byte *uid, int address) {
  Serial.print("Writing UID to EEPROM at address ");
  Serial.println(address);
  for (byte i = 0; i < CARD_DATA_SIZE; i++) {
    EEPROM.write(address + i, uid[i]);
    Serial.print("Wrote ");
    Serial.print(uid[i], HEX);
    Serial.print(" to address ");
    Serial.println(address + i);
  }
  EEPROM.commit(); // Save changes to flash
}

// Check if Card is Stored
bool isCardStored(byte *uid, int &address) {
  for (int i = 0; i < MAX_CARDS; i++) {
    address = EEPROM_START_ADDRESS + (CARD_DATA_SIZE * i);
    byte storedUID[4];
    for (int j = 0; j < CARD_DATA_SIZE; j++) {
      storedUID[j] = EEPROM.read(address + j);
    }

    if (memcmp(uid, storedUID, CARD_DATA_SIZE) == 0) {
      return true;
    }
  }
  return false;
}

// Remove Card from EEPROM
void removeCardFromEEPROM(int address) {
  clearCardFromEEPROM(address);
  EEPROM.commit(); // Save changes to flash
}

// Clear Card from EEPROM
void clearCardFromEEPROM(int address) {
  for (byte i = 0; i < CARD_DATA_SIZE; i++) {
    EEPROM.write(address + i, 0xFF);
  }
}

// Print EEPROM Contents
void printEEPROMContents() {
  Serial.println("EEPROM Contents:");
  for (int i = 0; i < MAX_CARDS; i++) {
    int address = EEPROM_START_ADDRESS + (CARD_DATA_SIZE * i);
    Serial.print("Slot ");
    Serial.print(i);
    Serial.print(": ");
    byte storedUID[4];
    for (int j = 0; j < CARD_DATA_SIZE; j++) {
      storedUID[j] = EEPROM.read(address + j);
      Serial.print(storedUID[j], HEX);
      Serial.print(" ");
    }
    Serial.println();
  }
}

// Debug Print UID
void debugPrintUID(byte *uid) {
  Serial.print("UID: ");
  for (byte i = 0; i < CARD_DATA_SIZE; i++) {
    Serial.print(uid[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
}

// Clear Entire EEPROM
void clearEEPROM() {
  for (int i = 0; i < EEPROM_SIZE; i++) {
    EEPROM.write(i, 0xFF); // Reset to 0xFF
  }
  EEPROM.commit(); // Save changes to flash
  Serial.println("EEPROM cleared.");
}

// Function to activate WiFiManager in AP mode with feedback
void activateWiFiManagerWithFeedback() {
  WiFiManager wifiManager;

  // Disconnect from current WiFi
  WiFi.disconnect();
  Serial.println("Disconnected from WiFi.");

  // Blink LED Blue 3 times
  for (int i = 0; i < 3; i++) {
    delay(500);
  }

  // Start WiFiManager in AP mode
  Serial.println("Starting WiFiManager...");
  if (!wifiManager.autoConnect("ESP32-WiFiManager")) {
    Serial.println("Failed to connect to WiFi.");
    return;
  }

  Serial.println("WiFi connected successfully.");
}

// Function to auto-connect to saved WiFi credentials
void autoConnectToWiFi() {
  WiFiManager wifiManager;

  if (!WiFi.isConnected()) {
    Serial.println("Attempting to connect to saved WiFi...");
    if (wifiManager.autoConnect("ESP32-WiFiManager")) {
      Serial.println("Connected to WiFi!");
    } else {
      Serial.println("Failed to connect to WiFi. Proceeding without WiFi.");
    }
  }
}

// Function to check if a button is pressed
bool isButtonPressed(int buttonPin) {
  return digitalRead(buttonPin) == LOW; // Button pressed when pin reads LOW
}

void selectRFID() {
  digitalWrite(SS_PIN, LOW); // Enable RFID
}

void takeReadings() {
  Serial.println("Taking readings...");
   beepBuzzer(); // Beep the buzzer for each reading
  unsigned long startTime = millis();
  for (int sec = 0; sec < 10; sec++) { // 10 seconds loop
    float temSum = 0, humSum = 0, phSum = 0;
    int readCount = 0;

    // Take readings for 1 second
    while (millis() - startTime < (sec + 1) * 1000) {
      readHumiturePH(); // Read the sensor values
      temSum += tem;
      humSum += hum;
      phSum += ph;
      readCount++;
    }

    // Compute averages
    float temAvg = temSum / readCount;
    float humAvg = humSum / readCount;
    float phAvg = phSum / readCount;

    // Print averaged readings
    Serial.print("Second ");
    Serial.print(sec + 1);
    Serial.print(": TEM = ");
    Serial.print(temAvg, 1);
    Serial.print(" °C, HUM = ");
    Serial.print(humAvg, 1);
    Serial.print(" %RH, PH = ");
    Serial.println(phAvg, 1);
  }

  Serial.println("Readings completed.");
}

// Beep the buzzer for a short duration
void beepBuzzer() {
  Serial.print("Buzzer......");
  digitalWrite(BUZZER_PIN, HIGH);
  
}


void readHumiturePH() {
  uint8_t Data[13] = { 0 };
  uint8_t ch = 0;
  bool flag = 1;

  while (flag) {
    delay(100);

    // Send Modbus Command
    digitalWrite(RS485_DIR_PIN, HIGH); // Set to transmit mode
    Serial.write(Com, 8);
    Serial.flush();
    digitalWrite(RS485_DIR_PIN, LOW);  // Set to receive mode
    delay(10);

    if (readN(&ch, 1) == 1) {
      if (ch == 0x01) {
        Data[0] = ch;
        if (readN(&ch, 1) == 1) {
          if (ch == 0x03) {
            Data[1] = ch;
            if (readN(&ch, 1) == 1) {
              if (ch == 0x08) {
                Data[2] = ch;
                if (readN(&Data[3], 10) == 10) {
                  if (CRC16_2(Data, 11) == (Data[11] * 256 + Data[12])) {
                    hum = (Data[3] * 256 + Data[4]) / 10.00;
                    tem = (Data[5] * 256 + Data[6]) / 10.00;
                    ph = (Data[9] * 256 + Data[10]) / 10.00;
                    flag = 0;
                  }
                }
              }
            }
          }
        }
      }
    }
    Serial.flush();
  }
}

uint8_t readN(uint8_t *buf, size_t len) {
  size_t offset = 0, left = len;
  int16_t Timeout = 500;
  uint8_t *buffer = buf;
  long curr = millis();
  while (left) {
    if (Serial.available()) {
      buffer[offset] = Serial.read();
      offset++;
      left--;
    }
    if (millis() - curr > Timeout) {
      break;
    }
  }
  return offset;
}

unsigned int CRC16_2(unsigned char *buf, int len) {
  unsigned int crc = 0xFFFF;
  for (int pos = 0; pos < len; pos++) {
    crc ^= (unsigned int)buf[pos];
    for (int i = 8; i != 0; i--) {
      if ((crc & 0x0001) != 0) {
        crc >>= 1;
        crc ^= 0xA001;
      } else {
        crc >>= 1;
      }
    }
  }

  crc = ((crc & 0x00ff) << 8) | ((crc & 0xff00) >> 8);
  return crc;
}

what is the problem? does the SD or MFRC522 fail to work?

if the SD card is sharing a SPI bus with other devices there can be problems - see ESP32 SPI SD share
in practice I have connected the MFRC522 to VSPI and the SD to HSPI

When i add the SD card and RC522 together, only the SD card works, I tried turning off the two by toggling the CS pins and activating only the one that is necessary. Still it does not work. I have the MFRC522 to HSPI and the SD to VSPI. Also in the given code the Buzzer does not work .

here is my code for the ESP32 - SD card using VSPI MFRC522 RFID reader using HSPI

// ESP32 - SD card using VSPI MFRC522 RFID reader using HSPI

// see https://forum.arduino.cc/t/esp32-initialize-hspi-as-default-spi/1155093/12

/* original SD code from
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-microsd-card-arduino/
*/

#include "FS.h"
#include "SD.h"
#include "SPI.h"

// MFRC522 RFID reader
#include <MFRC522v2.h>
#include <MFRC522DriverSPI.h>
#include <MFRC522DriverPinSimple.h>
#include <MFRC522Debug.h>

// default HSPI pins used by MFRC522 RFID reader
#define HSPI_SCK 14
#define HSPI_MISO 12
#define HSPI_MOSI 13
#define HSPI_CS 15

SPIClass *hspi = new SPIClass(HSPI);      // HSPI object used by MFRC522
MFRC522DriverPinSimple ss_1_pin(HSPI_CS);   // Configurable, take an unused pin, only HIGH/LOW required, must be different to SS 2.
MFRC522DriverSPI driver_1{ ss_1_pin, *hspi };
MFRC522 reader = driver_1;                // Create MFRC522 instance.

// ESP32 RTC
#include <ESP32Time.h>
ESP32Time rtc(3600);  // offset in seconds GMT+1

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("Read MRFC522 RFID card save time and data to SD card");
  rtc.setTime(1609459200);  // initialise RTC 1st Jan 2021 00:00:00
  // setup SD card reader using VSPI
  if (!SD.begin()) {                       // mount card
    Serial.println("Card Mount Failed");
    return;
  }
  uint8_t cardType = SD.cardType();
  if (cardType == CARD_NONE) {
    Serial.println("No SD card attached");
    return;
  }
  Serial.print("SD Card Type: ");
  if (cardType == CARD_MMC) {
    Serial.println("MMC");
  } else if (cardType == CARD_SD) {
    Serial.println("SDSC");
  } else if (cardType == CARD_SDHC) {
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);
  listDir(SD, "/", 0);
  Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));

  // setup MRFC522 RFID reader using HSPI
  Serial.print("open MRFC522 RFID reader");
  reader.PCD_Init();  // Init MFRC522 card.
  MFRC522Debug::PCD_DumpVersionToSerial(reader, Serial);
}

void loop() {
  // check Serial input read or delete data file
  if (Serial.available()) {
    char ch = Serial.read();
    if (ch == 'p')
      readFile(SD, "/data.txt");
    if (ch == 'd')
      deleteFile(SD, "/data.txt");
  }
  // check MFRC522 RFID reader
  if (reader.PICC_IsNewCardPresent() && reader.PICC_ReadCardSerial()) {
    Serial.print("\n");
    Serial.println(rtc.getTime("%A, %B %d %Y %H:%M:%S"));  // (String) returns time with specified format
    // Show some details of the PICC (that is: the tag/card).
    Serial.print(F(" Card UID:"));
    MFRC522Debug::PrintUID(Serial, reader.uid);
    Serial.print("  PICC type: ");
    MFRC522::PICC_Type piccType = reader.PICC_GetType(reader.uid.sak);
    Serial.println(MFRC522Debug::PICC_GetTypeName(piccType));
    // save card details to SD
    String s = rtc.getTime("%A, %d/%m/%Y %H:%M:%S") + " Card UID:";
    for (byte i = 0; i < reader.uid.size; i++) {
      char text[10] = { 0 };
      snprintf(text, 100, " 0x%2X", reader.uid.uidByte[i]);
      s += String(text);
    }
    s += "  PICC type: " + String((const char *)MFRC522Debug::PICC_GetTypeName(piccType)) + "\n";
    appendFile(SD, "/data.txt", s.c_str());
    // Halt PICC.
    reader.PICC_HaltA();
    // Stop encryption on PCD.
    reader.PCD_StopCrypto1();
    delay(1000);
  }
}
// ************** SD functions **************************

void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
  Serial.printf("Listing directory: %s\n", dirname);

  File root = fs.open(dirname);
  if (!root) {
    Serial.println("Failed to open directory");
    return;
  }
  if (!root.isDirectory()) {
    Serial.println("Not a directory");
    return;
  }

  File file = root.openNextFile();
  while (file) {
    if (file.isDirectory()) {
      Serial.print("  DIR : ");
      Serial.println(file.name());
      if (levels) {
        listDir(fs, file.name(), levels - 1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }
    file = root.openNextFile();
  }
}

void createDir(fs::FS &fs, const char *path) {
  Serial.printf("Creating Dir: %s\n", path);
  if (fs.mkdir(path)) {
    Serial.println("Dir created");
  } else {
    Serial.println("mkdir failed");
  }
}

void removeDir(fs::FS &fs, const char *path) {
  Serial.printf("Removing Dir: %s\n", path);
  if (fs.rmdir(path)) {
    Serial.println("Dir removed");
  } else {
    Serial.println("rmdir failed");
  }
}

void readFile(fs::FS &fs, const char *path) {
  Serial.printf("\nReading file: %s\n", path);

  File file = fs.open(path);
  if (!file) {
    Serial.println("Failed to open file for reading");
    return;
  }

  //Serial.print("Read from file: ");
  while (file.available()) {
    Serial.write(file.read());
  }
  file.close();
}

void writeFile(fs::FS &fs, const char *path, const char *message) {
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file for writing");
    return;
  }
  if (file.print(message)) {
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

void appendFile(fs::FS &fs, const char *path, const char *message) {
  //Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if (!file) {
    Serial.println("Failed to open file for appending");
    return;
  }
  if (file.print(message)) {
    //Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

void renameFile(fs::FS &fs, const char *path1, const char *path2) {
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) {
    Serial.println("File renamed");
  } else {
    Serial.println("Rename failed");
  }
}

void deleteFile(fs::FS &fs, const char *path) {
  Serial.printf("Deleting file: %s\n", path);
  if (fs.remove(path)) {
    Serial.println("File deleted");
  } else {
    Serial.println("Delete failed");
  }
}

void testFileIO(fs::FS &fs, const char *path) {
  File file = fs.open(path);
  static uint8_t buf[512];
  size_t len = 0;
  uint32_t start = millis();
  uint32_t end = start;
  if (file) {
    len = file.size();
    size_t flen = len;
    start = millis();
    while (len) {
      size_t toRead = len;
      if (toRead > 512) {
        toRead = 512;
      }
      file.read(buf, toRead);
      len -= toRead;
    }
    end = millis() - start;
    Serial.printf("%u bytes read for %u ms\n", flen, end);
    file.close();
  } else {
    Serial.println("Failed to open file for reading");
  }


  file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file for writing");
    return;
  }

  size_t i;
  start = millis();
  for (i = 0; i < 2048; i++) {
    file.write(buf, 512);
  }
  end = millis() - start;
  Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
  file.close();
}

serial monitor output

ESP32 SD card test

Read MRFC522 RFID card save time and data to SD card
SD Card Type: SDHC
SD Card Size: 14992MB
Listing directory: /
  DIR : System Volume Information
  FILE: test.txt  SIZE: 1048576
  FILE: data.txt  SIZE: 891
Total space: 14984MB
Used space: 2MB
open MRFC522 RFID readerFirmware Version: 0x92 = v2.0
dDeleting file: /data.txt
File deleted

Friday, January 01 2021 01:00:14
 Card UID: ZZ XX FD C2  PICC type: MIFARE 1KB

Friday, January 01 2021 01:00:18
 Card UID: Z0 XF 62 1A  PICC type: MIFARE 1KB

Friday, January 01 2021 01:00:19
 Card UID: Z0 XF 62 1A  PICC type: MIFARE 1KB

Friday, January 01 2021 01:00:22
 Card UID: ZZ XX FD C2  PICC type: MIFARE 1KB

Friday, January 01 2021 01:00:23
 Card UID: ZZ XX FD C2  PICC type: MIFARE 1KB

Friday, January 01 2021 01:02:30
 Card UID: Z0 XF 62 1A  PICC type: MIFARE 1KB
p

photo

will try this, Thank you. Shall update soon.

This topic may be worth a read:

Mind you, I've looked at your code and it doesn't reference the SD library anywhere. So the fact that you've gotten to the SD card to work at all with no code to enable it/select it/write to it is something of a miracle.

1 Like

Strange, my code was not in that post. That post was to explain what I found with the pictured board with regards to the MISO.

My code is:

/*
  SD card read/write
  Mod 01 make file name a string and iterate until we find a unused file name.
  Mod 02 add capability to increment filename.
  Rev 03 tested successfully on Arduino M0

  SD card attached to SPI bus on ICSP Header.
  SD Board power = 5V (for large board with EEPROM)

  based on code created: Nov 2010 by David A. Mellis, 9 Apr 2012 by Tom Igoe
  This code is in the public domain.

driver has a 512 byte buffer then write to SD

Closing the file forces any buffered data to be written to the SD and also updates
the file's directory entry.

If you don't close the file, you will lose all data written the file since it was opened,
not just the last buffer, since the directory entry will not be updated.
*/

#include <SPI.h>
#include <SD.h>

// *** SD Card declarations **************************************************
// ***************************************************************************

#define SDCARD_CS_PIN 9

uint16_t fileNumb = 900;
char dataFile[8];
bool SD_Error = false;

File myFile;			// create instance of a "File" class

void setup() {
  SerialUSB.begin(115200);
  delay (2000);
// Initializing SD card....

  SPISettings mySetting(1000000,MSBFIRST, SPI_MODE0);
  if (!SD.begin(SDCARD_CS_PIN))
    {SerialUSB.print("initialization failed");
     SD_Error = true;
    }

// loop until we find a file that doesn't already exist.......
  do
    {
     itoa(fileNumb, dataFile, 10);  // (value, Array, base)
     const char *extension = ".csv";
     strcat(dataFile, extension);  // syntax  strcat(dest, source)
     ++fileNumb;
    } while (SD.exists(dataFile));


SerialUSB.print("READY TO OPEN FILE FOR WRITING   = ");
SerialUSB.println(dataFile);
  myFile = SD.open(dataFile, FILE_WRITE);   // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
SerialUSB.println(myFile);
  // if the file opened okay, write to it:
  if (myFile) {
    myFile.println("data from boiler");     // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    SerialUSB.println("data from boiler");
    //SerialUSB.print(" data written to file:   ");
    myFile.close();                         // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
 SerialUSB.println(dataFile);
    }
  else {
    // if the file didn't open, print an error:
    SD_Error = true;
    }
  SerialUSB.print("SD_Error = ");
  SerialUSB.print(SD_Error);
}
void loop() {
  // nothing happens after setup
}

In the OP's followup reply in post #3, it sounded to me that it was possible they were having the same MISO not tristating problem as you'd outlined. Their original post omitted that detail (along with the version of the code actually having the problem).

I'm sorry this is the code i tried with the SD card.

#include <EEPROM.h>
#include <SPI.h>
#include <MFRC522.h>
#include <WiFiManager.h>
#include <SD.h> // Include SD library

// Button pins
#define BUTTON_TWO_PIN 2

// RGB LED pins
#define RED_PIN 26
#define GREEN_PIN 25
#define BLUE_PIN 32

// Pin Definitions for RFID
#define RST_PIN 0   // Reset pin for RC522
#define SS_PIN 5    // Slave select pin for RC522

// SD Card Pins
#define SCK 14
#define MISO 27
#define MOSI 13
#define CS 15

MFRC522 rfid(SS_PIN, RST_PIN);

#define EEPROM_SIZE 512        // Size of EEPROM storage
#define CARD_DATA_SIZE 4       // Card UID size (4 bytes)
#define MAX_CARDS 5            // Maximum number of stored cards
#define EEPROM_START_ADDRESS 0 // Starting address for card data

// Modbus Command
uint8_t Com[8] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x04, 0x44, 0x09 };

// Variables for sensor readings
float tem, hum, ph;
bool startReadings = false;

// Button and buzzer pins
#define BUTTON_PIN 2
#define BUZZER_PIN 26
#define RS485_DIR_PIN 4 // RS485 Direction Control Pin

// Hardcoded Master Card UID
const byte MASTER_UID[CARD_DATA_SIZE] = {0x43, 0xD4, 0x31, 0x02};

// Function Prototypes
void writeCardToEEPROM(byte *uid, int address);
bool isCardStored(byte *uid, int &address);
void removeCardFromEEPROM(int address);
void clearCardFromEEPROM(int address);
void clearEEPROM();
void printEEPROMContents();
void debugPrintUID(byte *uid);
void saveReadingsToSD(int second, float tem, float hum, float ph);
void takeReadings();
void selectRFID();
void activateWiFiManagerWithFeedback();
void autoConnectToWiFi();

byte currentCard[4]; // Holds the UID of the currently scanned card

// Flags for button actions and WiFiManager state
bool isWiFiManagerActive = false; // Tracks if WiFiManager is active

// Timers for button presses
unsigned long buttonOnePressTime = 0;
unsigned long buttonTwoPressTime = 0;

File dataFile; // File object for SD card

void setup() {
  Serial.begin(9600, SERIAL_8N1);
  printEEPROMContents();

  // Initialize SD card
  SPI.begin(SCK, MISO, MOSI, CS);
  if (!SD.begin(CS)) {
    Serial.println("SD Card initialization failed!");
    while (1); // Halt if SD card is not detected
  }
  Serial.println("SD Card initialized successfully.");
  
  dataFile = SD.open("readings.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println("Timestamp, TEM (°C), HUM (%RH), PH");
    dataFile.close();
    Serial.println("File created successfully.");
  } else {
    Serial.println("Failed to create file on SD card.");
  }

  SPI.begin();
  rfid.PCD_Init();

  // Initialize EEPROM
  if (!EEPROM.begin(EEPROM_SIZE)) {
    Serial.println("Failed to initialize EEPROM");
    while (1); // Halt execution
  }

  // Initialize button pins as input with pull-up resistors
  pinMode(BUTTON_TWO_PIN, INPUT_PULLUP);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(RS485_DIR_PIN, OUTPUT);
  digitalWrite(RS485_DIR_PIN, LOW); // Default to receive mode

  // Auto-connect to saved WiFi credentials
  autoConnectToWiFi();
  Serial.println("System initialized. Scan a card...");
}

void loop() {
  selectRFID();
  if (!scanCard()) return;

  if (memcmp(currentCard, MASTER_UID, CARD_DATA_SIZE) == 0) {
    Serial.println("Master Card Detected! Entering Admin Mode...");
    adminMode();
    return;
  }

  int cardAddress = -1;
  if (isCardStored(currentCard, cardAddress)) {
    Serial.println("Access Granted!");
    if (digitalRead(BUTTON_PIN) == LOW && !startReadings) {
      delay(50); // Debounce
      if (digitalRead(BUTTON_PIN) == LOW) {
        Serial.println("Button Pressed! Starting readings...");
        startReadings = true;
        takeReadings();
        startReadings = false;
      }
    }
  } else {
    Serial.println("Access Denied!");
  }

  if (isButtonPressed(BUTTON_TWO_PIN)) {
    if (buttonTwoPressTime == 0) {
      buttonTwoPressTime = millis();
    }
    if (millis() - buttonTwoPressTime >= 3000 && !isWiFiManagerActive) {
      isWiFiManagerActive = true;
      activateWiFiManagerWithFeedback();
      isWiFiManagerActive = false;
      buttonTwoPressTime = 0;
    }
  } else {
    buttonTwoPressTime = 0;
  }
}

// Take sensor readings and save to SD card
void takeReadings() {
  Serial.println("Taking readings...");
  unsigned long startTime = millis();

  for (int sec = 0; sec < 10; sec++) {
    float temSum = 0, humSum = 0, phSum = 0;
    int readCount = 0;

    while (millis() - startTime < (sec + 1) * 1000) {
      readHumiturePH();
      beepBuzzer();
      temSum += tem;
      humSum += hum;
      phSum += ph;
      readCount++;
    }

    float temAvg = temSum / readCount;
    float humAvg = humSum / readCount;
    float phAvg = phSum / readCount;

    Serial.print("Second ");
    Serial.print(sec + 1);
    Serial.print(": TEM = ");
    Serial.print(temAvg, 1);
    Serial.print(" °C, HUM = ");
    Serial.print(humAvg, 1);
    Serial.print(" %RH, PH = ");
    Serial.println(phAvg, 1);

    saveReadingsToSD(sec + 1, temAvg, humAvg, phAvg);
  }

  Serial.println("Readings completed.");
}

// Save readings to SD card
void saveReadingsToSD(int second, float tem, float hum, float ph) {
  dataFile = SD.open("readings.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.print(millis() / 1000);
    dataFile.print(", ");
    dataFile.print(tem, 1);
    dataFile.print(", ");
    dataFile.print(hum, 1);
    dataFile.print(", ");
    dataFile.println(ph, 1);
    dataFile.close();
    Serial.println("Readings saved to SD card.");
  } else {
    Serial.println("Failed to open file for writing.");
  }
}

// Beep the buzzer for a short duration
void beepBuzzer() {
  static unsigned long lastBuzzTime = 0;
    static bool isBuzzing = false;

    if (millis() - lastBuzzTime >= 500) { // 500ms beep cycle
        if (isBuzzing) {
            digitalWrite(BUZZER_PIN, LOW); // Turn off buzzer
        } else {
            digitalWrite(BUZZER_PIN, HIGH); // Turn on buzzer
        }
        isBuzzing = !isBuzzing;
        lastBuzzTime = millis();
    }
}


void readHumiturePH() {
  uint8_t Data[13] = { 0 };
  uint8_t ch = 0;
  bool flag = 1;

  while (flag) {
    delay(100);

    // Send Modbus Command
    digitalWrite(RS485_DIR_PIN, HIGH); // Set to transmit mode
    Serial.write(Com, 8);
    Serial.flush();
    digitalWrite(RS485_DIR_PIN, LOW);  // Set to receive mode
    delay(10);

    if (readN(&ch, 1) == 1) {
      if (ch == 0x01) {
        Data[0] = ch;
        if (readN(&ch, 1) == 1) {
          if (ch == 0x03) {
            Data[1] = ch;
            if (readN(&ch, 1) == 1) {
              if (ch == 0x08) {
                Data[2] = ch;
                if (readN(&Data[3], 10) == 10) {
                  if (CRC16_2(Data, 11) == (Data[11] * 256 + Data[12])) {
                    hum = (Data[3] * 256 + Data[4]) / 10.00;
                    tem = (Data[5] * 256 + Data[6]) / 10.00;
                    ph = (Data[9] * 256 + Data[10]) / 10.00;
                    flag = 0;
                  }
                }
              }
            }
          }
        }
      }
    }
    Serial.flush();
  }
}

uint8_t readN(uint8_t *buf, size_t len) {
  size_t offset = 0, left = len;
  int16_t Timeout = 500;
  uint8_t *buffer = buf;
  long curr = millis();
  while (left) {
    if (Serial.available()) {
      buffer[offset] = Serial.read();
      offset++;
      left--;
    }
    if (millis() - curr > Timeout) {
      break;
    }
  }
  return offset;
}

unsigned int CRC16_2(unsigned char *buf, int len) {
  unsigned int crc = 0xFFFF;
  for (int pos = 0; pos < len; pos++) {
    crc ^= (unsigned int)buf[pos];
    for (int i = 8; i != 0; i--) {
      if ((crc & 0x0001) != 0) {
        crc >>= 1;
        crc ^= 0xA001;
      } else {
        crc >>= 1;
      }
    }
  }

  crc = ((crc & 0x00ff) << 8) | ((crc & 0xff00) >> 8);
  return crc;
}


// Scan for RFID Card
bool scanCard() {
  selectRFID();
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
    digitalWrite(SS_PIN, HIGH); // Deselect RFID after operation
    return false;
  }

  // Copy UID to currentCard
  for (byte i = 0; i < CARD_DATA_SIZE; i++) {
    currentCard[i] = rfid.uid.uidByte[i];
  }
  rfid.PICC_HaltA();
  digitalWrite(SS_PIN, HIGH); // Deselect RFID after operation
  return true;
}

Also that is not the SD card module im using. Im using this one :

That's the kind of detail that you ought to have supplied in your original post, don't you agree? It saves all kinds of time and makes people feel that their time isn't being wasted. Good luck with your project.

This is the code that finally sort of works, but the issue is whenever it enters teh takereadings function it executes only once and then stops, can somebody help me?


#include <EEPROM.h>
#include <SPI.h>
#include <MFRC522.h>
#include <WiFiManager.h> // Include WiFiManager library for AP mode
#include <PubSubClient.h>    // TBPubSubClient library is used for MQTT protocol.
#include <WiFiClient.h>
#include <SD.h> // SD card library

// Button pins
#define BUTTON_TWO_PIN 35

// RGB LED pins
#define RED_PIN 25
#define GREEN_PIN 32

// Pin Definitions
#define RST_PIN 0   // Reset pin for RC522
#define SS_PIN 5    // Slave select pin for RC522

// SD Card Pins
#define SD_SCK 14
#define SD_MISO 27
#define SD_MOSI 13
#define SD_CS 15

SPIClass customSPI(VSPI); // Define custom SPI bus using VSPI
File dataFile;

MFRC522 rfid(SS_PIN, RST_PIN);

#define EEPROM_SIZE 512        // Size of EEPROM storage (adjust as needed)
#define CARD_DATA_SIZE 4       // Card UID size (4 bytes)
#define MAX_CARDS 5            // Maximum number of stored cards
#define EEPROM_START_ADDRESS 0 // Starting address for card data

// Modbus Command
uint8_t Com[8] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x04, 0x44, 0x09 };

// Variables for sensor readings
float tem, hum, ph;
bool startReadings = false; // Flag to track if readings are ongoing
bool accessGranted = false; // Flag to track if access is granted

// Button and buzzer pins
#define BUTTON_PIN 2
#define BUZZER_PIN 26
#define RS485_DIR_PIN 4 // RS485 Direction Control Pin

// Hardcoded Master Card UID
const byte MASTER_UID[CARD_DATA_SIZE] = {0x43, 0xD4, 0x31, 0x02};

// Function Prototypes
void writeCardToEEPROM(byte *uid, int address);
bool isCardStored(byte *uid, int &address);
void removeCardFromEEPROM(int address);
void clearCardFromEEPROM(int address);
void clearEEPROM();
void printEEPROMContents();
void debugPrintUID(byte *uid);

byte currentCard[4]; // Holds the UID of the currently scanned card

// Flags for button actions and WiFiManager state
bool isWiFiManagerActive = false; // Tracks if WiFiManager is active

// Timers for button presses
unsigned long buttonOnePressTime = 0;
unsigned long buttonTwoPressTime = 0;

const char* thingsboardServer = "20.90.144.193";  // IP or hostname
const int thingsboardPort = 1883;
const char* Device_ID1 = "fb558a90-d7e3-11ef-9c2b-d177ffb8ce45";
const char* TOKEN1= "t4007buxhigge9ce14zi";
WiFiClient espClient;             // Create object of epswifi client for wifi connection.
PubSubClient client(espClient);   // link the object to MQTT protocol.

void setup() {
   // Initialize Serial for Modbus
  Serial.begin(9600, SERIAL_8N1);
  // clearEEPROM();
  printEEPROMContents();

  client.setServer(thingsboardServer, 1883);
 
  SPI.begin();
  rfid.PCD_Init();

  // Initialize EEPROM
  if (!EEPROM.begin(EEPROM_SIZE)) {
    Serial.println("Failed to initialize EEPROM");
    while (1); // Halt execution
  }

  // Initialize button pins as input with pull-up resistors
  
  // Configure pins
  pinMode(2, INPUT_PULLUP); // Use internal pull-up for button
  pinMode(BUTTON_TWO_PIN, INPUT); // Use internal pull-up for button
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(RS485_DIR_PIN, OUTPUT);
  digitalWrite(RS485_DIR_PIN, LOW); // Default to receive mode

   pinMode(RED_PIN, OUTPUT);    // Set red LED pin as output
   pinMode(GREEN_PIN, OUTPUT); // Set green LED pin as output
   digitalWrite(RED_PIN, LOW);   // Turn red LED off
   digitalWrite(GREEN_PIN, LOW); // Turn green LED off

  // Auto-connect to saved WiFi credentials
  autoConnectToWiFi();
  reconnect(Device_ID1, TOKEN1);
  Serial.println("System initialized. Scan a card...");
}

void loop() {
    if (digitalRead(BUTTON_TWO_PIN) == LOW) { // WiFi button pressed
        if (buttonTwoPressTime == 0) {
            buttonTwoPressTime = millis(); // Start timing
        }
        if (millis() - buttonTwoPressTime >= 3000 && !isWiFiManagerActive) {
            isWiFiManagerActive = true;
            digitalWrite(RED_PIN, HIGH);   // Turn red LED on
            digitalWrite(GREEN_PIN, HIGH); // Turn green LED on
            delay(500);
            digitalWrite(RED_PIN, LOW);   // Turn red LED off
            digitalWrite(GREEN_PIN, LOW); // Turn green LED off
            delay(500);
            Serial.println("Activating WiFi Manager...");
            activateWiFiManagerWithFeedback(); // Execute WiFiManager
            isWiFiManagerActive = false;
            buttonTwoPressTime = 0; // Reset timing
        }
    } else {
        buttonTwoPressTime = 0; // Reset timing if WiFi button is released
    }

    // Check if a card is scanned
    if (!accessGranted) {
        if (!scanCard()) return; // Wait for a valid card scan
        int cardAddress = -1;
        accessGranted = isCardStored(currentCard, cardAddress);
    }

    // Handle Master Card for admin mode
    if (memcmp(currentCard, MASTER_UID, CARD_DATA_SIZE) == 0) {
        Serial.println("Master Card Detected! Entering Admin Mode...");
        adminMode();
        accessGranted = false; // Reset the flag after admin mode
        return;
    }

    // If access is granted, start taking readings
    if (accessGranted) {
        Serial.println("Access Granted! Preparing to take readings...");
        beepBuzzer(); // Indicate access granted
        reconnect(Device_ID1, TOKEN1); // Reconnect to ThingsBoard

        if (digitalRead(BUTTON_PIN) == LOW) { // Confirm button press
            digitalWrite(RED_PIN, HIGH);   // Turn red LED on
            digitalWrite(GREEN_PIN, HIGH); // Turn green LED on
            takeReadings();                // Take sensor readings
            digitalWrite(RED_PIN, LOW);    // Turn red LED off
            digitalWrite(GREEN_PIN, LOW);  // Turn green LED off
            accessGranted = false;         // Reset access flag
        } else {
            Serial.println("Button not pressed. Waiting...");
        }
    } else {
        Serial.println("Access Denied!");
        digitalWrite(RED_PIN, HIGH);   // Turn red LED on
        delay(3000);
        digitalWrite(RED_PIN, LOW);   // Turn red LED off
    }
}


// Scan for RFID Card
bool scanCard() {
  selectRFID();
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
    digitalWrite(SS_PIN, HIGH); // Deselect RFID after operation
    return false;
  }

  // Copy UID to currentCard
  for (byte i = 0; i < CARD_DATA_SIZE; i++) {
    currentCard[i] = rfid.uid.uidByte[i];
  }
  rfid.PICC_HaltA();
  digitalWrite(SS_PIN, HIGH); // Deselect RFID after operation
  return true;
}

// Admin Mode: Add or Remove Cards
void adminMode() {
  digitalWrite(GREEN_PIN, HIGH); // Turn green LED on
            delay(500);
            
            digitalWrite(GREEN_PIN, LOW); // Turn green LED off
            delay(500);
            
            digitalWrite(GREEN_PIN, HIGH); // Turn green LED on
            delay(500);
            
            digitalWrite(GREEN_PIN, LOW); // Turn green LED off
            delay(500);
          
            digitalWrite(GREEN_PIN, HIGH); // Turn green LED on
            delay(500);
           
            digitalWrite(GREEN_PIN, LOW); // Turn green LED off
  Serial.println("Scan a card to add or remove it...");
  printEEPROMContents(); // Debug: Show current EEPROM contents
  

  while (!scanCard()) {
    delay(100);
  }

  // Debugging: Print the scanned card UID
  Serial.print("Scanned Card UID: ");
  debugPrintUID(currentCard);

  int emptySlotAddress = -1;

  // Check if card is already stored
  int cardAddress = -1;
  if (isCardStored(currentCard, cardAddress)) {
    Serial.println("Card is already stored. Removing it...");
    removeCardFromEEPROM(cardAddress);
    Serial.println("Card removed.");
    digitalWrite(RED_PIN, HIGH);   // Turn red LED on
    delay(2000);
    digitalWrite(RED_PIN, LOW);   // Turn red LED off
    return;
  }

  // Find an empty slot
  for (int i = 0; i < MAX_CARDS; i++) {
    int address = EEPROM_START_ADDRESS + (CARD_DATA_SIZE * i);
    byte storedUID[4];
    for (int j = 0; j < CARD_DATA_SIZE; j++) {
      storedUID[j] = EEPROM.read(address + j);
    }

    // Check if the slot is empty
    bool slotEmpty = true;
    for (byte j = 0; j < CARD_DATA_SIZE; j++) {
      if (storedUID[j] != 0xFF) {
        slotEmpty = false;
        break;
      }
    }

    if (slotEmpty) {
      emptySlotAddress = address;
      break;
    }
  }

  if (emptySlotAddress != -1) {
    writeCardToEEPROM(currentCard, emptySlotAddress);
    Serial.println("Card added successfully!");
     digitalWrite(GREEN_PIN, HIGH);   // Turn red LED on
    delay(3000);
    digitalWrite(GREEN_PIN, LOW);   // Turn red LED off
    beepBuzzer(); // Beep the buzzer for each reading
  } else {
    Serial.println("No space available to add a new card.");
  }
}

// Write Card UID to EEPROM
void writeCardToEEPROM(byte *uid, int address) {
  Serial.print("Writing UID to EEPROM at address ");
  Serial.println(address);
  for (byte i = 0; i < CARD_DATA_SIZE; i++) {
    EEPROM.write(address + i, uid[i]);
    Serial.print("Wrote ");
    Serial.print(uid[i], HEX);
    Serial.print(" to address ");
    Serial.println(address + i);
  }
  EEPROM.commit(); // Save changes to flash
}

// Check if Card is Stored
bool isCardStored(byte *uid, int &address) {
  for (int i = 0; i < MAX_CARDS; i++) {
    address = EEPROM_START_ADDRESS + (CARD_DATA_SIZE * i);
    byte storedUID[4];
    for (int j = 0; j < CARD_DATA_SIZE; j++) {
      storedUID[j] = EEPROM.read(address + j);
    }

    if (memcmp(uid, storedUID, CARD_DATA_SIZE) == 0) {
      return true;
    }
  }
  return false;
}

// Remove Card from EEPROM
void removeCardFromEEPROM(int address) {
  clearCardFromEEPROM(address);
  EEPROM.commit(); // Save changes to flash
}

// Clear Card from EEPROM
void clearCardFromEEPROM(int address) {
  for (byte i = 0; i < CARD_DATA_SIZE; i++) {
    EEPROM.write(address + i, 0xFF);
  }
}

// Print EEPROM Contents
void printEEPROMContents() {
  Serial.println("EEPROM Contents:");
  for (int i = 0; i < MAX_CARDS; i++) {
    int address = EEPROM_START_ADDRESS + (CARD_DATA_SIZE * i);
    Serial.print("Slot ");
    Serial.print(i);
    Serial.print(": ");
    byte storedUID[4];
    for (int j = 0; j < CARD_DATA_SIZE; j++) {
      storedUID[j] = EEPROM.read(address + j);
      Serial.print(storedUID[j], HEX);
      Serial.print(" ");
    }
    Serial.println();
  }
}

// Debug Print UID
void debugPrintUID(byte *uid) {
  Serial.print("UID: ");
  for (byte i = 0; i < CARD_DATA_SIZE; i++) {
    Serial.print(uid[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
}

// Clear Entire EEPROM
void clearEEPROM() {
  for (int i = 0; i < EEPROM_SIZE; i++) {
    EEPROM.write(i, 0xFF); // Reset to 0xFF
  }
  EEPROM.commit(); // Save changes to flash
  Serial.println("EEPROM cleared.");
}

// Function to activate WiFiManager in AP mode with feedback
void activateWiFiManagerWithFeedback() {
  WiFiManager wifiManager;

  // Disconnect from current WiFi
  WiFi.disconnect();
  Serial.println("Disconnected from WiFi.");

  // Blink LED Blue 3 times
  for (int i = 0; i < 3; i++) {
    delay(500);
  }

  // Start WiFiManager in AP mode
  Serial.println("Starting WiFiManager...");
  if (!wifiManager.autoConnect("ESP32-WiFiManager")) {
    Serial.println("Failed to connect to WiFi.");
    digitalWrite(RED_PIN, HIGH);   // Turn red LED on
    //digitalWrite(GREEN_PIN, HIGH); // Turn green LED on
    delay(3000);
    digitalWrite(RED_PIN, LOW);   // Turn red LED off
    return;
  }

  Serial.println("WiFi connected successfully.");
  digitalWrite(GREEN_PIN, HIGH); // Turn green LED on
  delay(3000);
  //digitalWrite(RED_PIN, LOW);   // Turn red LED off
  digitalWrite(GREEN_PIN, LOW); // Turn green LED off
}

// Function to auto-connect to saved WiFi credentials
void autoConnectToWiFi() {
  WiFiManager wifiManager;

  if (!WiFi.isConnected()) {
    Serial.println("Attempting to connect to saved WiFi...");
    if (wifiManager.autoConnect("ESP32-WiFiManager")) {
      Serial.println("Connected to WiFi!");
      //digitalWrite(RED_PIN, HIGH);   // Turn red LED on
      digitalWrite(GREEN_PIN, HIGH); // Turn green LED on
      delay(3000);
      //digitalWrite(RED_PIN, LOW);   // Turn red LED off
      digitalWrite(GREEN_PIN, LOW); // Turn green LED off
    } else {
      Serial.println("Failed to connect to WiFi. Proceeding without WiFi.");
      digitalWrite(RED_PIN, HIGH);   // Turn red LED on
            //digitalWrite(GREEN_PIN, HIGH); // Turn green LED on
      delay(3000);
      digitalWrite(RED_PIN, LOW);   // Turn red LED off
           // digitalWrite(GREEN_PIN, LOW); // Turn green LED off
    }
  }
}

// Function to check if a button is pressed
bool isButtonPressed(int buttonPin) {
  return digitalRead(buttonPin) == LOW; // Button pressed when pin reads LOW
}

void selectRFID() {
  digitalWrite(SS_PIN, LOW); // Enable RFID
}

void takeReadings() {
    customSPI.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);

    // Attempt to initialize the SD card
    if (!SD.begin(SD_CS, customSPI)) {
        Serial.println("SD card initialization failed!");
        digitalWrite(RED_PIN, HIGH); // Indicate error with red LED
        delay(3000);
        digitalWrite(RED_PIN, LOW);
        accessGranted = false; // Go back to waiting for card scan
        return;
    }
    Serial.println("SD card initialized successfully.");

    // Open file on the SD card
    String fileName = "/readings.txt";
    dataFile = SD.open(fileName, FILE_WRITE);
    if (!dataFile) {
        Serial.println("Failed to open file on SD card.");
        SD.end();          // Unmount SD card
        digitalWrite(SD_CS, HIGH); // Disable SD card
        customSPI.end();   // Release custom SPI
        accessGranted = false; // Go back to waiting for card scan
        return;
    }
    Serial.println("File opened for writing.");

    Serial.println("Taking readings...");
    beepBuzzer(); // Beep the buzzer for each reading

    unsigned long startTime = millis();
    unsigned long lastReadingTime = millis();
    int readCount = 0;
    float temSum = 0, humSum = 0, phSum = 0;

    for (int sec = 0; sec < 10; sec++) { // 10 seconds loop
        while (millis() - lastReadingTime < 1000) { // 1-second interval
            if (millis() - startTime > (sec + 1) * 5000) {
                break;
            }
            beepBuzzer();
            readHumiturePH(); // Read the sensor values
            temSum += tem;
            humSum += hum;
            phSum += ph;
            readCount++;
            lastReadingTime = millis(); // Update the last reading time
        }

        // Compute averages
        float temAvg = temSum / readCount;
        float humAvg = humSum / readCount;
        float phAvg = phSum / readCount;

        // Convert UID to a string
        String uidString = "";
        for (byte i = 0; i < CARD_DATA_SIZE; i++) {
            uidString += String(currentCard[i], HEX);
            if (i < CARD_DATA_SIZE - 1) uidString += ":"; // Add colon separator
        }

        // Prepare JSON payload (as per original format)
        String payload = "{";
        payload += "\"Reading\":";
        payload += sec + 1;
        payload += ",\"temperature\":";
        payload += temAvg;
        payload += ",\"moisture\":";
        payload += humAvg;
        payload += ",\"ph\":";
        payload += phAvg;
        payload += ",\"uid\":\"";
        payload += uidString;
        payload += "\"}";

        // Send telemetry data to ThingsBoard
        char attr[300];
        payload.toCharArray(attr, 300);
        client.publish("v1/devices/me/telemetry", attr); // Telemetry data
        client.publish("v1/devices/me/attributes", attr); // Attributes data
        Serial.println(attr); // Debug: Print payload

        // Save the payload to the SD card
        dataFile.println(payload);

        // Reset sums and counts for the next second
        temSum = 0;
        humSum = 0;
        phSum = 0;
        readCount = 0;
    }

    Serial.println("Readings completed.");
    dataFile.close();               // Close the file
    SD.end();                       // Unmount SD card
    digitalWrite(SD_CS, HIGH);  // Disable SD card
    customSPI.end();                // Release custom SPI
    Serial.println("SD card unmounted.");

    // Reset state to wait for a new card scan
    accessGranted = false;
    Serial.println("System ready for a new card scan.");
    ESP.restart();
}



// Beep the buzzer for a short duration
void beepBuzzer() {
  digitalWrite(26, HIGH);
  delay(1000);
  digitalWrite(26,LOW);
  
}


void readHumiturePH() {
  uint8_t Data[13] = { 0 };
  uint8_t ch = 0;
  bool flag = 1;

  while (flag) {
    delay(100);

    // Send Modbus Command
    digitalWrite(RS485_DIR_PIN, HIGH); // Set to transmit mode
    Serial.write(Com, 8);
    Serial.flush();
    digitalWrite(RS485_DIR_PIN, LOW);  // Set to receive mode
    delay(10);

    if (readN(&ch, 1) == 1) {
      if (ch == 0x01) {
        Data[0] = ch;
        if (readN(&ch, 1) == 1) {
          if (ch == 0x03) {
            Data[1] = ch;
            if (readN(&ch, 1) == 1) {
              if (ch == 0x08) {
                Data[2] = ch;
                if (readN(&Data[3], 10) == 10) {
                  if (CRC16_2(Data, 11) == (Data[11] * 256 + Data[12])) {
                    hum = (Data[3] * 256 + Data[4]) / 10.00;
                    tem = (Data[5] * 256 + Data[6]) / 10.00;
                    ph = (Data[9] * 256 + Data[10]) / 10.00;
                    flag = 0;
                  }
                }
              }
            }
          }
        }
      }
    }
    Serial.flush();
  }
}

uint8_t readN(uint8_t *buf, size_t len) {
  size_t offset = 0, left = len;
  int16_t Timeout = 500;
  uint8_t *buffer = buf;
  long curr = millis();
  while (left) {
    if (Serial.available()) {
      buffer[offset] = Serial.read();
      offset++;
      left--;
    }
    if (millis() - curr > Timeout) {
      break;
    }
  }
  return offset;
}

unsigned int CRC16_2(unsigned char *buf, int len) {
  unsigned int crc = 0xFFFF;
  for (int pos = 0; pos < len; pos++) {
    crc ^= (unsigned int)buf[pos];
    for (int i = 8; i != 0; i--) {
      if ((crc & 0x0001) != 0) {
        crc >>= 1;
        crc ^= 0xA001;
      } else {
        crc >>= 1;
      }
    }
  }

  crc = ((crc & 0x00ff) << 8) | ((crc & 0xff00) >> 8);
  return crc;
}

// Dashboard connection
void reconnect(const char *Device_ID, const char *TOKEN)
{
  while (!client.connected())
  {
    Serial.println("Connecting to thingsboard");
    if (client.connect(Device_ID, TOKEN, ""))
    {
      Serial.println("Done");
    }
    else
    {
      Serial.println("failed");
      Serial.println("Not Connected dashboard");
      Serial.println("contact ME");
      ESP.restart();
    }
  }
}

void unmountSDCard() {
    SD.end(); // Unmount the SD card
    digitalWrite(SD_CS, HIGH); // Disable the SD card
    Serial.println("SD card unmounted and disabled.");
}

Hey there could u please take a look at the updated code i have used referring to what u have done? please

Hello, what pin are you using for your sd card module connecting to esp32?

in the code in post 4 the SD card is connected to VSPI

// ESP32 VSPI connections
// ESP32 SCK pin GPIO18  to SD card  SCK
// ESP32 MISO pin GPIO19  to SD card  MISO
// ESP32 MOSI pin GPIO23  to SD card  MOSI
// ESP32 SS  pin GPIO 5   to SD card SS
// ESP32 3.3V to SD VCC and GND to GND

with the ESP32 you can select which SPI pins to use, e.g. using HSPI

// define HSPI pins (default HSPI pins crash processor)
#define HSPI_MISO 16  //12
#define HSPI_MOSI 17  //13
#define HSPI_SCLK 4   // 14
#define HSPI_SS 15
// ESP32 3.3V to SD VCC and GND to GND

SPIClass *hspi = NULL;  // SPI object

void setup() {
  Serial.begin(115200);
  hspi = new SPIClass(HSPI);  // create SPI class

  hspi->begin(HSPI_SCK, HSPI_MISO, HSPI_MOSI, SD_CS); // setup SPI pins
  if (!SD.begin(SD_CS, *hspi)) {                      // mount card
    Serial.println("Card Mount Failed");
    return;
  }

note I had problems using the default HSPI pins with a SD card - pin 12 is a strapping pin see esp32-pinout-reference-gpios

there can be problems if the SD card is sharing a SPI bus with other devices

does appear to depend on specific card PCB being used

How to integrate MFRC522 and SD card module and make them work together on ESP32 S3 N16R8?

While doing my project, I encountered this error where the SPI bus conflicts or interferes between the SD card module and MFRC522 Reader in my ESP32 S3 N16R8. Both Modules work fine if separate sketches code but if together the MFRC522 will stop working. I already tried checking the wirings (pins) from the reader to esp32 but still have the same problem.

this is my code:

#include <Arduino.h>
#include <SPI.h>
#include <MFRC522v2.h>
#include <MFRC522DriverSPI.h>
#include <MFRC522DriverPinSimple.h>
#include <MFRC522Debug.h>
#include <SD.h>

// RFID custom pin configuration for ESP32-S3:
#define MFRC522_SCK   12
#define MFRC522_MISO  13
#define MFRC522_MOSI  11
#define MFRC522_SS    10

// SD card custom pin configuration (using VSPI instance):
#define SD_SCK   36
#define SD_MISO  37
#define SD_MOSI  35
#define SD_CS    39

// RGB LED pin definitions (assuming common cathode)
#define LED_R 2
#define LED_G 3
#define LED_B 4

// Buzzer pin definition
#define BUZZER 5

// Create a simple pin driver for RFID SS:
MFRC522DriverPinSimple ss_pin(MFRC522_SS);

// Create the SPI driver for RFID:
MFRC522DriverSPI rfidDriver(ss_pin);

// Create the MFRC522 instance:
MFRC522 mfrc522(rfidDriver);

// ----------------------
// Helper function to set RGB LED color
// ----------------------
void setRGB(uint8_t r, uint8_t g, uint8_t b) {
  digitalWrite(LED_R, r);
  digitalWrite(LED_G, g);
  digitalWrite(LED_B, b);
}

// ----------------------
// Helper function for buzzer beep
// ----------------------
void beepBuzzer(int frequency = 1000, int duration = 200) {
  tone(BUZZER, frequency, duration);
  delay(duration);
  noTone(BUZZER);
}

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    ; // Wait for serial port to be available
  }
  
  Serial.println("\nInitializing RFID and SD modules...");

  // Initialize RFID SPI on custom pins:
  SPI.begin(MFRC522_SCK, MFRC522_MISO, MFRC522_MOSI, MFRC522_SS);
  mfrc522.PCD_Init();
  MFRC522Debug::PCD_DumpVersionToSerial(mfrc522, Serial);
  Serial.println(F("Place an RFID card near the reader..."));
  
  // Initialize the RGB LED and buzzer pins:
  pinMode(LED_R, OUTPUT);
  pinMode(LED_G, OUTPUT);
  pinMode(LED_B, OUTPUT);
  pinMode(BUZZER, OUTPUT);
  
  // Set idle color to blue:
  setRGB(LOW, LOW, HIGH);

  // Initialize SD card using VSPI instance:
  SPIClass sdSPI(VSPI); // Using VSPI instead of FSPI
  sdSPI.begin(SD_SCK, SD_MISO, SD_MOSI, SD_CS);
  if (!SD.begin(SD_CS, sdSPI)) {
    Serial.println("SD card initialization failed!");
  } else {
    Serial.println("SD card initialized successfully!");
  }
}

void loop() {
  // Poll RFID reader
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }
  
  // Card found - change RGB LED to green and beep buzzer:
  setRGB(LOW, HIGH, LOW);  // Green on
  beepBuzzer();
  
  // Dump card details to Serial:
  MFRC522Debug::PICC_DumpToSerial(mfrc522, Serial, &(mfrc522.uid));
  
  // Optional: Log UID to SD card
  File file = SD.open("/rfid_logs.txt", FILE_APPEND);
  if (file) {
    String uidStr = "";
    for (byte i = 0; i < mfrc522.uid.size; i++) {
      uidStr += String(mfrc522.uid.uidByte[i], HEX);
    }
    uidStr.toUpperCase();
    String logEntry = "UID: " + uidStr + " | Time: " + String(millis()/1000) + "s\n";
    file.print(logEntry);
    file.close();
    Serial.println("Logged to SD: " + logEntry);
  } else {
    Serial.println("Error opening /rfid_logs.txt for writing.");
  }
  
  // Hold the green state for a moment:
  delay(1000);
  
  // Return RGB LED to idle (blue)
  setRGB(LOW, LOW, HIGH);
  
  // Halt card communication:
  mfrc522.PICC_HaltA();
  
  delay(100);
}

your code is unreadable at the moment - edit your post, select all code and click the < CODE/ > button; next save your post. This will make the code easier to read, easier to copy and the forum software will display it correctly.

your original post 14 asked about ESP32 pins - your post 16 mentions a ESP32-S3 a totally different pin layout
with the ESP32-S3-DevKitC-1 for a SD on HSPI I used

// define HSPI pins
#define HSPI_SCK 40
#define HSPI_MISO 42
#define HSPI_MOSI 41
#define SD_CS 39

SPIClass *hspi = NULL;  // SPI object

void setup() {
  Serial.begin(115200);
  hspi = new SPIClass(HSPI);  // create SPI class

  hspi->begin(HSPI_SCK, HSPI_MISO, HSPI_MOSI, SD_CS); // setup SPI pins
  if (!SD.begin(SD_CS, *hspi)) {                      // mount card
    Serial.println("Card Mount Failed");
    return;
  }
.....

for the MFRC522 on VSPI I used

// ESP32_S3 SCK pin GPIO12  to MFRC522 card  SCK
// ESP32_S3 MISO pin GPIO13  to MFRC522 card  MISO
// ESP32_S3 MOSI pin GPIO11  to MFRC522 card  MOSI
// ESP32_S3 SS  pin GPIO 10   to MFRC522 card SS (marked SDA on PCB)
// connect ESP32_S3 GND and 3.3V to MFRC522 GND and 3V3

Thank you for the clarifications. I already edited it.

Note: That code (POST 16) is not actually the actual code I'm using for my project; I made that sample sketch because that is the problem I encountered while doing my project. I would like to ask for your opinion and idea about this.

If you want to see the whole code for my project, I can paste it here.

The project components/devices I'm using are:

SD Card Module?
Esp32 S3 N16R8
Buzzer
RGB Led
Pzem-004t
MFRC522 Reader
4 channel Realy
Buck Converter (Step Down - 12v - 5v)

"THE ONLY PROBLEM WITH THIS IS THE SD CARD AND MFRC522 SPI CONFLICT"

this is my original code:

#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include <MFRC522.h>
#include <SPI.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <PZEM004Tv30.h>
#include <SD.h>
#include <HX711.h>
#include <ArduinoOTA.h>
#include <HTTPClient.h>
#include <ESPmDNS.h>
#include "FS.h"
#include "addons/TokenHelper.h"
#include "addons/RTDBHelper.h"
#include <esp_task_wdt.h>      // For watchdog timer

// ----------------------
// WiFi Configuration
// ----------------------
#define WIFI_SSID       
#define WIFI_PASSWORD   

// ----------------------
// Firebase Configuration
// ----------------------
#define API_KEY         
#define DATABASE_URL    
// Firebase Variables
FirebaseData fbdo;
FirebaseConfig config;
FirebaseAuth auth;

// ----------------------
// RFID Configuration (Dedicated HSPI bus)
// ----------------------
#define RFID_RST_PIN   8
#define RFID_CS_PIN    9
#define RFID_SCK_PIN   6
#define RFID_MISO_PIN  7
#define RFID_MOSI_PIN  5

SPIClass rfidSPI(HSPI); 
MFRC522 rfid(RFID_CS_PIN, RFID_RST_PIN);

// Duplicate scan prevention
String lastUID = "";
unsigned long lastScanTime = 0;
const unsigned long duplicateScanInterval = 5000; // 5 seconds

// ----------------------
// GPIO Configuration
// ----------------------
#define LED_R       2
#define LED_G       3
#define LED_B       4

#define RELAY1      14
#define RELAY2      15   // AC Light is now wired to Relay2
#define RELAY3      16
#define RELAY4      17

#define BUZZER      1

// ----------------------
// Reset Button (optional)
// ----------------------
#define RESET_BUTTON 0 // Active LOW

// ----------------------
// LDR Sensor (MH Sensor Digital Out)
// ----------------------
#define LDR_DO_PIN  19

// ----------------------
// SD Card Configuration (Separate FSPI bus)
// ----------------------
#define SD_CS_PIN     10
#define SD_SCK_PIN    12
#define SD_MISO_PIN   13
#define SD_MOSI_PIN   11

SPIClass sdSPI(FSPI);
File logFile;

// ----------------------
// PZEM-004T Configuration
// ----------------------
HardwareSerial pzemSerial(2);
#define PZEM_RX 47
#define PZEM_TX 35   // Changed TX to GPIO35
PZEM004Tv30 pzem(pzemSerial, PZEM_TX, PZEM_RX);

// ----------------------
// HX711 Weight Sensors Configuration
// Shared SCK on GPIO18, each with a unique DOUT.
#define HX711_SCK   18
#define HX711_DOUT1 39
#define HX711_DOUT2 36
#define HX711_DOUT3 38

HX711 scale1;
HX711 scale2;
HX711 scale3;

// Calibration factors (update after calibration)
float calibrationFactor1 = 2035.5; 
float calibrationFactor2 = 2035.5; 
float calibrationFactor3 = 2035.5; 

// Smoothing parameters
const int numSamples = 30;
float weightBuffer1[numSamples] = {0};
float weightBuffer2[numSamples] = {0};
float weightBuffer3[numSamples] = {0};
int bufferIndex1 = 0, bufferIndex2 = 0, bufferIndex3 = 0;
float maxWeight1 = 0;
float maxWeight2 = 0;
float maxWeight3 = 0;

// ----------------------
// Time & NTP
// ----------------------
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", 8 * 3600, 60000); // UTC+8

// ----------------------
// Global Timers & Flags
// ----------------------
unsigned long relay2ActivatedTime = 0;
bool isRelay2On = false;
unsigned long RELAY2_ON_DURATION = 45000; // ms
unsigned long lastPZEMRead = 0;   // For periodic PZEM readings

// Remote config variables (from /SystemConfig)
float remoteWeightThreshold = 10.0;
unsigned long remoteSessionDuration = 45000;
unsigned long remoteConfigLastUpdate = 0;

// SD Log Rotation Timer
unsigned long lastLogCheckTime = 0;
const unsigned long logCheckInterval = 60000; // 60 seconds

// ----------------------
// Global Variables for Instructor Session
// ----------------------
String activeInstructorUID = "";
unsigned long activeInstructorSessionStart = 0; // milliseconds

// ----------------------
// System-Failure Monitoring
// ----------------------
bool wifiOK = false;
bool firebaseOK = false;
bool sdOK = false;
const unsigned long SYSTEM_FAILURE_TIMEOUT = 30000; // 30 seconds
unsigned long systemFailureStart = 0;

// ----------------------
// SPI Settings
// ----------------------
SPISettings sdSPISettings(4000000, MSBFIRST, SPI_MODE0);      // SD card
SPISettings rfidSPISettings(10000000, MSBFIRST, SPI_MODE0);   // RFID

// Voltage threshold to decide "no electricity"
const float NO_ELECTRICITY_THRESHOLD = 1.0; 

// ----------------------
// Forward Declarations
// ----------------------
bool handleUnregisteredUID(String uid);
void logToSD(String logMessage);
String sanitizeUID(String uid);
void checkLogFileSize();
void processRFIDCard(String uid);
void activateRelays();
void logAccess(String rolePath, String uid, bool isStudent = false);
bool checkClassSchedule();
bool checkInstructorSchedule(String uid);
void checkSystemFailures();
void reinitSD();
void checkWiFiWithBackoff();

// ----------------------
// HX711 Helper Functions (Smoothing)
// ----------------------
float getSmoothedWeight(float buffer[], int &index, HX711 &scale) {
  float rawWeight = scale.get_units(1);
  if (rawWeight < 0) rawWeight = 0;
  buffer[index] = rawWeight;
  index = (index + 1) % numSamples;
  float sum = 0;
  for (int i = 0; i < numSamples; i++) {
    sum += buffer[i];
  }
  return sum / numSamples;
}

void processSensor(HX711 &scale, float buffer[], int &index, float &maxWeight, const char *label) {
  if (scale.is_ready()) {
    float smoothedWeight = getSmoothedWeight(buffer, index, scale);
    if (smoothedWeight < 0.05) smoothedWeight = 0;
    if (smoothedWeight > maxWeight) {
      maxWeight = smoothedWeight;
    }
    if (smoothedWeight < (maxWeight * 0.8)) {
      maxWeight = smoothedWeight;
    }
    Serial.print(label);
    Serial.print(" Weight: ");
    Serial.print(maxWeight, 2);
    Serial.println(" kg");
  } else {
    Serial.print(label);
    Serial.println(" not ready. Check connections.");
  }
}

// ----------------------
// Other Helper Functions
// ----------------------
String getTimestamp() {
  timeClient.update();
  unsigned long epochTime = timeClient.getEpochTime();
  time_t rawtime = (time_t)epochTime;
  struct tm *timeinfo = localtime(&rawtime);
  char buffer[20];
  strftime(buffer, sizeof(buffer), "%m-%d-%Y %H:%M:%S", timeinfo);
  return String(buffer);
}

void uploadOfflineLogs() {
  if (!SD.exists("/log.txt")) return;
  sdSPI.beginTransaction(sdSPISettings);
  logFile = SD.open("/log.txt", FILE_READ);
  if (logFile) {
    while (logFile.available()) {
      String logEntry = logFile.readStringUntil('\n');
      if (Firebase.RTDB.pushString(&fbdo, "/OfflineLogs", logEntry))
        Serial.println("Uploaded offline log: " + logEntry);
      else
        Serial.println("Failed to upload offline log: " + logEntry);
    }
    logFile.close();
    SD.remove("/log.txt");
  }
  sdSPI.endTransaction();
}

void resetRFID() {
  digitalWrite(RFID_CS_PIN, HIGH);
  rfid.PCD_Init();
  Serial.println("RFID reader reset.");
}

bool waitForWeightVerification(float threshold, unsigned long timeout) {
  unsigned long startTime = millis();
  while (millis() - startTime < timeout) {
    processSensor(scale1, weightBuffer1, bufferIndex1, maxWeight1, "Sensor 1");
    processSensor(scale2, weightBuffer2, bufferIndex2, maxWeight2, "Sensor 2");
    processSensor(scale3, weightBuffer3, bufferIndex3, maxWeight3, "Sensor 3");
    if (maxWeight1 >= threshold || maxWeight2 >= threshold || maxWeight3 >= threshold)
      return true;
    delay(100);
  }
  return false;
}

void setNeutral() {
  digitalWrite(LED_R, LOW);
  digitalWrite(LED_G, LOW);
  digitalWrite(LED_B, HIGH);
}

void indicateAccessGranted() {
  digitalWrite(LED_R, LOW);
  digitalWrite(LED_G, HIGH);
  digitalWrite(LED_B, LOW);
  tone(BUZZER, 1000, 200);
  delay(200);
  noTone(BUZZER);
}

void indicateAccessDenied() {
  digitalWrite(LED_R, HIGH);
  digitalWrite(LED_G, LOW);
  digitalWrite(LED_B, LOW);
  tone(BUZZER, 400, 200);
  delay(200);
  noTone(BUZZER);
}

void streamCallback(FirebaseStream data) {
  Serial.println("Data changed in Firebase!");
  String uid = data.stringData();
  Serial.println("Updated UID: " + uid);
}

void streamTimeoutCallback(bool timeout) {
  if (timeout) {
    Serial.println("Stream timeout, trying to reconnect...");
    Firebase.RTDB.beginStream(&fbdo, "/UIDs/");
  }
}

// ----------------------
// WiFi with Exponential Backoff
// ----------------------
void checkWiFiWithBackoff() {
  if (WiFi.status() == WL_CONNECTED) {
    wifiOK = true;
    return;
  }
  Serial.println("WiFi disconnected, attempting reconnection...");
  WiFi.disconnect();
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  int retry = 0;
  const int maxRetry = 5;
  unsigned long delayTime = 500;
  while (WiFi.status() != WL_CONNECTED && retry < maxRetry) {
    Serial.print(".");
    delay(delayTime);
    retry++;
    delayTime *= 2;
  }
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("\nFailed to reconnect to WiFi.");
    wifiOK = false;
  } else {
    Serial.println("\nReconnected to WiFi: " + WiFi.localIP().toString());
    wifiOK = true;
  }
}

void readRemoteConfig() {
  if (millis() - remoteConfigLastUpdate < 60000) return;
  remoteConfigLastUpdate = millis();
  if (WiFi.status() == WL_CONNECTED) {
    FirebaseJson json;
    if (Firebase.RTDB.getJSON(&fbdo, "/SystemConfig")) {
      FirebaseJson &jsonObj = fbdo.jsonObject();
      FirebaseJsonData jsonData;
      if (jsonObj.get(jsonData, "WeightThreshold", false) && jsonData.success) {
        remoteWeightThreshold = jsonData.intValue;
        Serial.print("Remote Weight Threshold updated to: ");
        Serial.println(remoteWeightThreshold);
      }
      if (jsonObj.get(jsonData, "SessionDuration", false) && jsonData.success) {
        remoteSessionDuration = jsonData.intValue;
        Serial.print("Remote Session Duration updated to: ");
        Serial.println(remoteSessionDuration);
      }
    } else {
      Serial.println("Failed to read remote config from Firebase.");
    }
  }
}

void checkLogFileSize() {
  sdSPI.beginTransaction(sdSPISettings);
  File logFile = SD.open("/log.txt");
  if (logFile) {
    if (logFile.size() > 50000) {
      logFile.close();
      SD.remove("/log.txt");
      Serial.println("Log file rotated due to size limit.");
    } else {
      logFile.close();
    }
  }
  sdSPI.endTransaction();
}

void reinitSD() {
  sdSPI.beginTransaction(sdSPISettings);
  if (!SD.begin(SD_CS_PIN, sdSPI)) {
    Serial.println("Reinitializing SD card failed.");
    sdOK = false;
  } else {
    Serial.println("SD card reinitialized successfully.");
    sdOK = true;
  }
  sdSPI.endTransaction();
}

// ----------------------
// Missing Function Implementations
// ----------------------

// Activate all relays (example implementation)
void activateRelays() {
  relay2ActivatedTime = millis();
  isRelay2On = true;
  // Activate relays (active LOW)
  digitalWrite(RELAY1, LOW);
  digitalWrite(RELAY2, LOW);
  digitalWrite(RELAY3, LOW);
  digitalWrite(RELAY4, LOW);
  delay(1000);
  // Turn Relay1 off to signal end of activation
  digitalWrite(RELAY1, HIGH);
  setNeutral();
  Serial.println("Relays activated.");
}

// Log access data to Firebase (or fallback to SD if needed)
void logAccess(String rolePath, String uid, bool isStudent) {
  String timestamp = getTimestamp();
  String logPath = isStudent ? rolePath + "/AttendanceRecords" : rolePath + "/AccessLogs";
  if (Firebase.RTDB.setString(&fbdo, logPath, timestamp))
    Serial.println("Access logged for UID " + uid + " at " + timestamp);
  else {
    Serial.println("Error logging access for UID " + uid);
    logToSD("Access log failed for UID: " + uid + " at " + timestamp);
  }
}

// Check instructor schedule (placeholder, return true if allowed)
bool checkInstructorSchedule(String uid) {
  Serial.println("Checking schedule for instructor UID: " + uid);
  return true;
}

// Check class schedule (placeholder, return true if allowed)
bool checkClassSchedule() {
  Serial.println("Checking global class schedule.");
  return true;
}

// Check if UID is registered in Firebase
bool handleUnregisteredUID(String uid) {
  Serial.println("Checking UID in Firebase...");
  String adminPath = "/Admin/" + uid;
  String instructorPath = "/Instructors/" + uid;
  String studentPath = "/Students/" + uid;
  bool isRegistered = false;
  if (Firebase.RTDB.getJSON(&fbdo, adminPath))
    isRegistered = true;
  else if (Firebase.RTDB.getJSON(&fbdo, instructorPath))
    isRegistered = true;
  else if (Firebase.RTDB.getJSON(&fbdo, studentPath))
    isRegistered = true;
  if (!isRegistered) {
    Serial.println("UID not found in Firebase. Storing unregistered UID in Firebase...");
    String timestamp = getTimestamp();
    FirebaseJson json;
    json.add("uid", uid);
    json.add("timestamp", timestamp);
    if (Firebase.RTDB.pushJSON(&fbdo, "/UnregisteredUIDs", &json))
      Serial.println("Unregistered UID stored successfully.");
    else
      Serial.println("Failed to store unregistered UID in Firebase.");
    return true;
  }
  return false;
}

// ----------------------
// [NEW LOGIC] logToSD Implementation
// ----------------------
void logToSD(String logMessage) {
  sdSPI.beginTransaction(sdSPISettings);
  File logFile = SD.open("/log.txt", FILE_APPEND);
  if (logFile) {
    logFile.println(logMessage);
    logFile.close();
    Serial.println("Logged to SD: " + logMessage);
  } else {
    Serial.println("Failed to open log file for writing.");
  }
  sdSPI.endTransaction();
}

// ----------------------
// [NEW LOGIC] Safe Offline Log Function
// Turns LED green during SD write and temporarily disables RFID SPI.
void safeOfflineLog(String message) {
  // Disable RFID to avoid SPI conflict
  rfid.PCD_AntennaOff();
  rfidSPI.end();
  digitalWrite(RFID_CS_PIN, HIGH);
  Serial.println("RFID disabled for SD logging.");

  // Set LED to green as processing indicator
  digitalWrite(LED_R, LOW);
  digitalWrite(LED_G, HIGH);
  digitalWrite(LED_B, LOW);

  // Enable SD card if not already enabled
  sdSPI.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN, SD_CS_PIN);
  sdSPI.beginTransaction(sdSPISettings);
  if (!SD.begin(SD_CS_PIN, sdSPI)) {
    Serial.println("Failed to enable SD card for safeOfflineLog.");
    sdSPI.endTransaction();
    return;
  }
  sdSPI.endTransaction();
  sdOK = true;
  Serial.println("SD card enabled for safeOfflineLog.");

  // Write log to SD
  sdSPI.beginTransaction(sdSPISettings);
  File logFile = SD.open("/log.txt", FILE_APPEND);
  if (logFile) {
    logFile.println(message);
    logFile.close();
    Serial.println("Logged to SD (safeOfflineLog): " + message);
  } else {
    Serial.println("Failed to open log file in safeOfflineLog.");
  }
  sdSPI.endTransaction();

  // Revert LED to neutral (blue)
  setNeutral();

  // Optionally disable SD card after logging
  SD.end();
  sdSPI.end();
  digitalWrite(SD_CS_PIN, HIGH);
  sdOK = false;
  Serial.println("SD card disabled after safeOfflineLog.");

  // Re-enable RFID
  rfidSPI.begin(RFID_SCK_PIN, RFID_MISO_PIN, RFID_MOSI_PIN, RFID_CS_PIN);
  rfid.PCD_Init();
  rfid.PCD_AntennaOn();
  Serial.println("RFID re-enabled after safeOfflineLog.");
}

// ----------------------
// Process RFID Card
// ----------------------
void processRFIDCard(String uid) {
  Serial.println("Processing card: " + uid);
  if (WiFi.status() != WL_CONNECTED) {
    String timestamp = getTimestamp();
    String offlineLog = "Offline Attendance - UID: " + uid + ", Time: " + timestamp;
    safeOfflineLog(offlineLog);
    Serial.println("WiFi not connected. Access Denied for UID: " + uid);
    indicateAccessDenied();
    resetRFID();
    return;
  }
  if (handleUnregisteredUID(uid)) {
    Serial.println("Card unregistered. Access Denied for UID: " + uid);
    indicateAccessDenied();
    resetRFID();
    return;
  }
  String path = "/Admin/" + uid;
  if (Firebase.RTDB.getJSON(&fbdo, path)) {
    Serial.println("Admin access granted for UID: " + uid);
    activateRelays();
    indicateAccessGranted();
    logAccess(path, uid, false);
    resetRFID();
    return;
  }
  path = "/Instructors/" + uid;
  if (Firebase.RTDB.getJSON(&fbdo, path)) {
    Serial.println("Instructor access for UID: " + uid);
    if (!checkInstructorSchedule(uid)) {
      Serial.println("Access denied due to scheduling restrictions for Instructor UID: " + uid);
      indicateAccessDenied();
      resetRFID();
      return;
    }
    activeInstructorUID = uid;
    activeInstructorSessionStart = millis();
    activateRelays();
    indicateAccessGranted();
    logAccess(path, uid, false);
    resetRFID();
    return;
  }
  path = "/Students/" + uid;
  if (Firebase.RTDB.getJSON(&fbdo, path)) {
    Serial.println("Student detected for UID: " + uid);
    if (!checkClassSchedule()) {
      Serial.println("Access denied due to scheduling restrictions for Student UID: " + uid);
      indicateAccessDenied();
      resetRFID();
      return;
    }
    Serial.println("Initiating weight sensor verification for UID: " + uid);
    if (waitForWeightVerification(remoteWeightThreshold, 10000)) {
      Serial.println("Weight verification successful. Access granted for UID: " + uid);
      activateRelays();
      indicateAccessGranted();
      logAccess(path, uid, true);
    } else {
      Serial.println("Weight verification failed. Access Denied for UID: " + uid);
      indicateAccessDenied();
    }
    resetRFID();
    return;
  }
  Serial.println("Unauthorized card. Access Denied for UID: " + uid);
  indicateAccessDenied();
  resetRFID();
}

// ----------------------
// SD Card File System Functions (wrapped with SPI transactions)
// ----------------------
void listDir(fs::FS &fs, const char * dirname, uint8_t levels) {
  sdSPI.beginTransaction(sdSPISettings);
  Serial.printf("Listing directory: %s\n", dirname);
  File root = fs.open(dirname);
  if (!root) {
    Serial.println("Failed to open directory");
    sdSPI.endTransaction();
    return;
  }
  if (!root.isDirectory()) {
    Serial.println("Not a directory");
    sdSPI.endTransaction();
    return;
  }
  File file = root.openNextFile();
  while (file) {
    if (file.isDirectory()) {
      Serial.print("  DIR : ");
      Serial.println(file.name());
      if (levels) {
        listDir(fs, file.name(), levels - 1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      unsigned long fSize = file.size();
      Serial.print(fSize);
      Serial.print(" bytes (");
      Serial.print((float)fSize / 1048576.0, 3);
      Serial.println(" MB)");
    }
    file = root.openNextFile();
  }
  sdSPI.endTransaction();
}

void createDir(fs::FS &fs, const char * path) {
  sdSPI.beginTransaction(sdSPISettings);
  Serial.printf("Creating Dir: %s\n", path);
  if (fs.mkdir(path))
    Serial.println("Dir created");
  else
    Serial.println("mkdir failed");
  sdSPI.endTransaction();
}

void removeDir(fs::FS &fs, const char * path) {
  sdSPI.beginTransaction(sdSPISettings);
  Serial.printf("Removing Dir: %s\n", path);
  if (fs.rmdir(path))
    Serial.println("Dir removed");
  else
    Serial.println("rmdir failed");
  sdSPI.endTransaction();
}

void readFile(fs::FS &fs, const char * path) {
  sdSPI.beginTransaction(sdSPISettings);
  Serial.printf("Reading file: %s\n", path);
  File file = fs.open(path);
  if (!file) {
    Serial.println("Failed to open file for reading");
    sdSPI.endTransaction();
    return;
  }
  Serial.print("Read from file: ");
  while (file.available()) {
    Serial.write(file.read());
  }
  Serial.println();
  file.close();
  sdSPI.endTransaction();
}

void writeFile(fs::FS &fs, const char * path, const char * message) {
  sdSPI.beginTransaction(sdSPISettings);
  Serial.printf("Writing file: %s\n", path);
  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file for writing");
    sdSPI.endTransaction();
    return;
  }
  if (file.print(message))
    Serial.println("File written");
  else
    Serial.println("Write failed");
  file.close();
  sdSPI.endTransaction();
}

void appendFile(fs::FS &fs, const char * path, const char * message) {
  sdSPI.beginTransaction(sdSPISettings);
  Serial.printf("Appending to file: %s\n", path);
  File file = fs.open(path, FILE_APPEND);
  if (!file) {
    Serial.println("Failed to open file for appending");
    sdSPI.endTransaction();
    return;
  }
  if (file.print(message))
    Serial.println("Message appended");
  else
    Serial.println("Append failed");
  file.close();
  sdSPI.endTransaction();
}

void renameFile(fs::FS &fs, const char * path1, const char * path2) {
  sdSPI.beginTransaction(sdSPISettings);
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2))
    Serial.println("File renamed");
  else
    Serial.println("Rename failed");
  sdSPI.endTransaction();
}

void deleteFile(fs::FS &fs, const char * path) {
  sdSPI.beginTransaction(sdSPISettings);
  Serial.printf("Deleting file: %s\n", path);
  if (fs.remove(path))
    Serial.println("File deleted");
  else
    Serial.println("Delete failed");
  sdSPI.endTransaction();
}

void testFileIO(fs::FS &fs, const char * path) {
  sdSPI.beginTransaction(sdSPISettings);
  File file = fs.open(path);
  static uint8_t buf[512];
  size_t len = 0;
  uint32_t start = millis();
  uint32_t end = start;
  if (file) {
    len = file.size();
    size_t flen = len;
    start = millis();
    while (len) {
      size_t toRead = (len > 512) ? 512 : len;
      file.read(buf, toRead);
      len -= toRead;
    }
    end = millis() - start;
    Serial.printf("%u bytes read in %u ms\n", flen, end);
    file.close();
  } else {
    Serial.println("Failed to open file for reading");
  }
  file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file for writing");
    sdSPI.endTransaction();
    return;
  }
  start = millis();
  for (size_t i = 0; i < 2048; i++) {
    file.write(buf, 512);
  }
  end = millis() - start;
  Serial.printf("%u bytes written in %u ms\n", 2048 * 512, end);
  file.close();
  sdSPI.endTransaction();
}

// -------------------------------------------------------------------
// Setup
// -------------------------------------------------------------------
void setup() {
  Serial.begin(115200);

  // Initialize watchdog with a 30-second timeout
  esp_task_wdt_config_t wdt_config;
  wdt_config.timeout_ms = 30000;
  esp_task_wdt_init(&wdt_config);
  esp_task_wdt_add(NULL);

  // --------------------------
  // RFID SPI Initialization
  // --------------------------
  pinMode(RFID_CS_PIN, OUTPUT);
  digitalWrite(RFID_CS_PIN, HIGH); // Deassert CS
  rfidSPI.begin(RFID_SCK_PIN, RFID_MISO_PIN, RFID_MOSI_PIN, RFID_CS_PIN);
  rfid.PCD_Init();
  rfid.PCD_DumpVersionToSerial();

  // --------------------------
  // SD SPI Initialization
  // --------------------------
  pinMode(SD_CS_PIN, OUTPUT);
  digitalWrite(SD_CS_PIN, HIGH);
  sdSPI.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN, SD_CS_PIN);

  // --------------------------
  // GPIO Setup
  // --------------------------
  pinMode(LED_R, OUTPUT);
  pinMode(LED_G, OUTPUT);
  pinMode(LED_B, OUTPUT);
  pinMode(RELAY1, OUTPUT);
  pinMode(RELAY2, OUTPUT);
  pinMode(RELAY3, OUTPUT);
  pinMode(RELAY4, OUTPUT);
  pinMode(BUZZER, OUTPUT);
  setNeutral();
  noTone(BUZZER);

  // --------------------------
  // Reset Button
  // --------------------------
  pinMode(RESET_BUTTON, INPUT_PULLUP);

  // --------------------------
  // LDR Digital Input
  // --------------------------
  pinMode(LDR_DO_PIN, INPUT);

  // --------------------------
  // Relays default to OFF
  // --------------------------
  digitalWrite(RELAY1, HIGH);
  digitalWrite(RELAY2, HIGH);
  digitalWrite(RELAY3, HIGH);
  digitalWrite(RELAY4, HIGH);

  // --------------------------
  // SD Card Check (initial test)
  // --------------------------
  Serial.println("Initializing SD card...");
  if (!SD.begin(SD_CS_PIN, sdSPI)) {
    Serial.println("SD card initialization failed.");
    sdOK = false;
  } else {
    Serial.println("SD card initialized successfully!");
    sdOK = true;
    sdSPI.beginTransaction(sdSPISettings);
    File testFile = SD.open("/test.txt", FILE_WRITE);
    if (testFile) {
      testFile.println("Test file write successful!");
      testFile.close();
      Serial.println("Test file successfully created.");
    } else {
      Serial.println("Test file write failed!");
    }
    sdSPI.endTransaction();

    listDir(SD, "/", 0);
    createDir(SD, "/mydir");
    listDir(SD, "/", 0);
    removeDir(SD, "/mydir");
    listDir(SD, "/", 2);
    writeFile(SD, "/hello.txt", "Hello ");
    appendFile(SD, "/hello.txt", "World!\n");
    readFile(SD, "/hello.txt");
    renameFile(SD, "/hello.txt", "/foo.txt");
    readFile(SD, "/foo.txt");
    deleteFile(SD, "/foo.txt");
    testFileIO(SD, "/test.txt");
    Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
    Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
    
    // --------------------------
    // [SD Standby after Initialization]
    // --------------------------
    // Now that the SD card test is complete,
    // disable the SD card (put into standby mode) to free up the SPI bus for MFRC522.
    SD.end();
    sdSPI.end();
    digitalWrite(SD_CS_PIN, HIGH);
    sdOK = false;
    Serial.println("SD card switched to standby mode.");
  }

  // --------------------------
  // Wi-Fi
  // --------------------------
  Serial.print("Connecting to Wi-Fi");
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  checkWiFiWithBackoff();
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("\nWiFi connection failed.");
    wifiOK = false;
  } else {
    Serial.println("\nConnected to Wi-Fi");
    Serial.println("IP Address: " + WiFi.localIP().toString());
    wifiOK = true;
  }

  // --------------------------
  // mDNS & OTA
  // --------------------------
  if (!MDNS.begin("esp32s3")) {
    Serial.println("Error setting up MDNS responder!");
  } else {
    Serial.println("MDNS responder started.");
  }

  ArduinoOTA.onStart([]() {
    Serial.println("OTA Update starting...");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nOTA Update finished.");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("OTA Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("OTA Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("OTA Ready");

  // --------------------------
  // Firebase
  // --------------------------
  config.api_key = API_KEY;
  config.database_url = DATABASE_URL;
  if (!Firebase.signUp(&config, &auth, "", "")) {
    Serial.printf("Firebase signup error: %s\n", config.signer.signupError.message.c_str());
    Serial.println("Firebase authentication failed.");
    firebaseOK = false;
  } else {
    Serial.println("Firebase authenticated successfully.");
    firebaseOK = true;
  }
  config.token_status_callback = tokenStatusCallback;
  Firebase.begin(&config, &auth);
  Firebase.reconnectWiFi(true);

  // Set Firebase stream callback
  Firebase.RTDB.setStreamCallback(&fbdo, streamCallback, streamTimeoutCallback);

  // --------------------------
  // NTP
  // --------------------------
  timeClient.begin();

  // --------------------------
  // PZEM
  // --------------------------
  pzemSerial.begin(9600, SERIAL_8N1, PZEM_RX, PZEM_TX);

  // --------------------------
  // HX711 Sensors Initialization (Shared SCK on GPIO18)
  // --------------------------
  Serial.println("Initializing HX711 sensors...");
  scale1.begin(HX711_DOUT1, HX711_SCK);
  scale2.begin(HX711_DOUT2, HX711_SCK);
  scale3.begin(HX711_DOUT3, HX711_SCK);

  while (!scale1.is_ready() || !scale2.is_ready() || !scale3.is_ready()) {
    Serial.println("Waiting for HX711 sensors to stabilize...");
    delay(500);
  }

  scale1.set_scale(calibrationFactor1);
  scale2.set_scale(calibrationFactor2);
  scale3.set_scale(calibrationFactor3);

  Serial.println("Taring the scales... Ensure no weight is on them.");
  delay(5000);
  scale1.tare();
  scale2.tare();
  scale3.tare();
  Serial.println("HX711 Tare complete. Ready for measurement.");

  Serial.println("Setup complete.\n");
}

// -------------------------------------------------------------------
// Check System Failures
// -------------------------------------------------------------------
void checkSystemFailures() {
  bool allOK = wifiOK && firebaseOK && sdOK;
  if (!allOK) {
    if (systemFailureStart == 0) {
      systemFailureStart = millis();
    } else {
      if (millis() - systemFailureStart >= SYSTEM_FAILURE_TIMEOUT) {
        Serial.println("System failure detected for too long. Restarting...");
        ESP.restart();
      }
    }
  } else {
    systemFailureStart = 0;
  }
}

// -------------------------------------------------------------------
// Main Loop
// -------------------------------------------------------------------
void loop() {
  ArduinoOTA.handle();
  esp_task_wdt_reset();  // Reset watchdog timer

  // Optional Software Reset Button (Active LOW)
  if (digitalRead(RESET_BUTTON) == LOW) {
    Serial.println("Reset button pressed! Restarting...");
    delay(500);
    ESP.restart();
  }

  // Check Wi-Fi / Firebase
  checkWiFiWithBackoff();
  if (!Firebase.ready()) {
    Serial.println("Firebase not ready!");
    firebaseOK = false;
  } else {
    firebaseOK = true;
  }

  // Remote Config
  readRemoteConfig();

  // [NEW LOGIC] Read PZEM Voltage and enable/disable SD accordingly
  float voltage = pzem.voltage();
  if (voltage < NO_ELECTRICITY_THRESHOLD) {
    if (!sdOK) {
      sdSPI.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN, SD_CS_PIN);
      sdSPI.beginTransaction(sdSPISettings);
      if (SD.begin(SD_CS_PIN, sdSPI)) {
        Serial.println("SD card enabled due to power outage.");
        sdOK = true;
      } else {
        Serial.println("Failed to enable SD card in power outage.");
      }
      sdSPI.endTransaction();
    }
  } else {
    if (sdOK) {
      SD.end();
      sdSPI.end();
      digitalWrite(SD_CS_PIN, HIGH);
      sdOK = false;
      Serial.println("SD card disabled (normal power present).");
    }
  }

  // Upload Offline Logs if Wi-Fi is connected
  if (WiFi.status() == WL_CONNECTED) {
    uploadOfflineLogs();
  }

  // Check SD Log File Size Periodically
  if (millis() - lastLogCheckTime > logCheckInterval) {
    lastLogCheckTime = millis();
    checkLogFileSize();
  }

  // Read PZEM Sensor Data (every 10 seconds)
  if (millis() - lastPZEMRead > 10000) {
    lastPZEMRead = millis();
    float current = pzem.current();
    float power   = pzem.power();
    float energy  = pzem.energy();
    
    FirebaseJson sensorJson;
    sensorJson.add("voltage", voltage);
    sensorJson.add("current", current);
    sensorJson.add("power", power);
    sensorJson.add("energy", energy);
    sensorJson.add("timestamp", getTimestamp());
    if (activeInstructorUID != "") {
      sensorJson.add("uid", activeInstructorUID);
    }
    
    String sensorDataStr = "Voltage: " + String(voltage, 2) + " V, " +
                           "Current: " + String(current, 2) + " A, " +
                           "Power: "   + String(power, 2)   + " W, " +
                           "Energy: "  + String(energy, 2)  + " kWh";
    if (voltage < 1.0)
      sensorDataStr += " (No Electricity)";
    Serial.println("PZEM Data: " + sensorDataStr);
    
    if (activeInstructorUID != "") {
      String sensorPath = "/SensorData/" + activeInstructorUID;
      if (Firebase.RTDB.pushJSON(&fbdo, sensorPath, &sensorJson)) {
        Serial.println("Sensor data for instructor " + activeInstructorUID + " uploaded to Firebase.");
      } else {
        Serial.println("Failed to upload sensor data for instructor " + activeInstructorUID);
        safeOfflineLog("PZEM OFFLINE: " + sensorDataStr);
      }
    }
    else {
      if (Firebase.RTDB.setJSON(&fbdo, "/SensorData", &sensorJson)) {
        Serial.println("Global sensor data uploaded to Firebase.");
      } else {
        Serial.println("Failed to upload sensor data to Firebase.");
        safeOfflineLog("PZEM OFFLINE: " + sensorDataStr);
      }
    }
  }

  // Instructor Session Timeout
  if (activeInstructorUID != "") {
    if (millis() - activeInstructorSessionStart > remoteSessionDuration) {
      Serial.println("Instructor session expired for UID: " + activeInstructorUID);
      activeInstructorUID = "";
    }
  }

  // Set LED to Neutral
  setNeutral();

  // RFID Reading
  digitalWrite(RFID_CS_PIN, HIGH); // Deassert CS
  rfidSPI.beginTransaction(rfidSPISettings);
  bool cardPresent = rfid.PICC_IsNewCardPresent();
  bool readCard = cardPresent && rfid.PICC_ReadCardSerial();
  rfidSPI.endTransaction();
  
  if (readCard) {
    String uid = "";
    for (byte i = 0; i < rfid.uid.size; i++) {
      uid += String(rfid.uid.uidByte[i], HEX);
    }
    uid.toUpperCase();
    Serial.println("Card UID: " + uid);
    
    // Check for duplicate scans
    if (uid == lastUID && (millis() - lastScanTime < duplicateScanInterval)) {
      Serial.println("Duplicate scan detected. Ignoring.");
      resetRFID();
      delay(500);
      return;
    }
    lastUID = uid;
    lastScanTime = millis();
    
    processRFIDCard(uid);
    delay(500);
  }

  // LDR Light Control (only if instructor present)
  if (activeInstructorUID != "") {
    int ldrState = digitalRead(LDR_DO_PIN);
    if (ldrState == HIGH) {
      digitalWrite(RELAY2, HIGH); // Light OFF
      Serial.println("LDR sees bright => Light OFF");
    } else {
      digitalWrite(RELAY2, LOW);  // Light ON
      Serial.println("LDR sees dark => Light ON");
    }
  } else {
    digitalWrite(RELAY2, HIGH);
  }

  // Process HX711 sensors (smoothing)
  processSensor(scale1, weightBuffer1, bufferIndex1, maxWeight1, "Sensor 1");
  processSensor(scale2, weightBuffer2, bufferIndex2, maxWeight2, "Sensor 2");
  processSensor(scale3, weightBuffer3, bufferIndex3, maxWeight3, "Sensor 3");

  // System Failure Check
  checkSystemFailures();
  
  delay(100); // Loop pacing
}

Can you also please look at my original code? I already pasted it here (POST 18). I'm very thankful for your help!

I can't still figure out why the SD card module and the MFRC522 if integrated together are still not working, but if both separate sketches, they work fine. I'm using the ESP32 S3 N16R8, and I would like to ask for help from you, please!

OUTPUT FROM THIS CODE:

13:02:25.790 -> Initializing RFID & SD Card...

13:02:25.909 -> SD Card initialized successfully.

13:02:26.090 -> RFID Reader initialized.

But the reader is not scanning my RFID card.

#include <SPI.h>
#include <MFRC522.h>
#include <SD.h>

// RFID Pins
#define RFID_SS  5   // RFID Chip Select
#define RFID_RST 4   // RFID Reset

// SD Card Pins
#define SD_CS 9    // SD Card Chip Select
#define SCK_PIN_SD  13
#define MISO_PIN_SD 12
#define MOSI_PIN_SD 11

// SPI pins for RFID (default ESP32 SPI)
#define SCK_PIN_RFID  36
#define MISO_PIN_RFID 37
#define MOSI_PIN_RFID 35

MFRC522 mfrc522(RFID_SS, RFID_RST);  // RFID instance

void setup() {
    Serial.begin(115200);
    Serial.println("Initializing RFID & SD Card...");

    // Initialize SPI for SD Card
    SPI.begin(SCK_PIN_SD, MISO_PIN_SD, MOSI_PIN_SD, SD_CS);
    if (!SD.begin(SD_CS, SPI, 4000000)) {  // Force 4MHz SPI speed for better stability
        Serial.println("SD Card initialization failed!");
    } else {
        Serial.println("SD Card initialized successfully.");
    }

    // Initialize SPI for RFID
    SPI.begin(SCK_PIN_RFID, MISO_PIN_RFID, MOSI_PIN_RFID, RFID_SS);
    mfrc522.PCD_Init();
    Serial.println("RFID Reader initialized.");
}

void loop() {
    // Check for RFID tag
    if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
        Serial.print("Card UID: ");
        String uid = "";
        for (byte i = 0; i < mfrc522.uid.size; i++) {
            Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? "0" : "");
            Serial.print(mfrc522.uid.uidByte[i], HEX);
            Serial.print(" ");
            uid += String(mfrc522.uid.uidByte[i], HEX) + " ";
        }
        Serial.println();

        // Save UID to SD Card
        File file = SD.open("/rfid_log.txt", FILE_APPEND);
        if (file) {
            file.println(uid);
            file.close();
            Serial.println("UID saved to SD card.");
        } else {
            Serial.println("Failed to write to SD card. Retrying...");
            delay(100);
            file = SD.open("/rfid_log.txt", FILE_APPEND);
            if (file) {
                file.println(uid);
                file.close();
                Serial.println("Retry successful: UID saved to SD card.");
            } else {
                Serial.println("Final attempt failed. Check SD card.");
            }
        }

        mfrc522.PICC_HaltA();  // Halt the card
    }

    delay(500);
}