ESP32 RFID User Management System with Web Server

In this project, you’ll build an RFID user management system with a web server that runs on an ESP32. The ESP32 board is connected to an MFRC522 RFID reader and a microSD card that will store all the user data. The ESP32 will be programmed using Arduino IDE and with the Arduino_MFRC522v2 library.

ESP32 RFID User Management System with Web Server Arduino IDE

In this tutorial, we’ll cover the following topics:

Project Overview

The following diagram shows a high-level overview of how the project works.

ESP32 RFID Web Server Management System Project Overview

1) When you approximate an RFID tag to the MIFARE reader, it will read its UID and send information about it to the ESP32. When it reads a tag, the buzzer will beep and an LED will turn on.

2) The ESP32 records the time of that interaction and saves the time and UUID on the microSD card on a file called log.txt.

3) The ESP32 also hosts a web server to display and manage the information from the microSD card.

4) The root (/) URL shows the full log (saved on the microSD card module log.txt) with the timestamp and the user UID.

5) There’s another page on /add-user that allows you to add users and their role using a form.

6) The data entered via this form will be saved on the user.txt file saved on the microSD card.

7) There is another page on /manage-users that allows to consult and delete users.

8) Basically, this page allows you to interact with the users.txt file.

Parts Required

Here’s a list of the required components for this project:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

MFRC522 RFID Reader/Writer

In this tutorial, we’ll be using the MFRC522 RFID reader/writer and that’s the one we recommend you getting to interface with the ESP32.

Reader Writer RFID MFRC522 Module

You also need some RFID tags, we have a keychain and an electromagnetic card that come with the MFRC522 RFID Reader/Writer module. Each tag has a unique identification (UID) that will be assigned to each user.

RFID MFRC522 Card Tags Keychain

Recommended reading: ESP32 with MFRC522 RFID Reader/Writer (Arduino IDE) – Getting Started Guide.

Wiring the ESP32 to the MFRC522 RFID Reader

The MFRC522 RFID reader works at 3.3V and we’ll be using SPI communication protocol. We’ll connect the MFRC522 RFID Reader to the ESP32 default SPI pins, use the next table as a reference.

Reader Writer RFID MFRC522 Module ESP32 Board Circuit
MFRC522 RFID ReaderESP32Description
SDAGPIO 5SPI signal input, I2C data line, or UART data input
SCKGPIO 18SPI clock
MOSIGPIO 23SPI data input
MISOGPIO 19SPI master-in-slave-out, I2C serial clock, or UART serial output
IRQDon’t connectInterrupt pin; signals the microcontroller when an RFID tag is nearby
GNDGND
RSTGPIO 21LOW signal to put the module in power-down mode; send a HIGH signal to reset the module
3.3V3.3VPower supply (2.5-3.3V)

MicroSD Card Module

There are different microSD card modules compatible with the ESP32. We’re using the microSD card module sown in the following figure – it communicates using SPI communication protocol. You can use any other microSD card module with an SPI interface.

MicroSD card module for ESP32 ESP8266 Arduino SPI

To learn how to use the microSD card module with the ESP32, you can read the next tutorial:

Wiring the MicroSD Card Module to the ESP32

The microSD card module communicates using SPI communication protocol. You can connect it to the ESP32 using the default SPI pins.

MicroSD card moduleESP32
3V33.3V
CSGPIO 15
MOSIGPIO 23
CLKGPIO 18
MISOGPIO 19
GNDGND

Preparing the microSD Card

Before proceeding with the tutorial, make sure you format your microSD card as FAT32. Follow the next instructions to format your microSD card or use a software tool like SD Card Formater (compatible with Windows and Mac OS).

1. Insert the microSD card into your computer. Go to My Computer and right-click on the SD card. Select Format as shown in the figure below.

MicroSD Card Module format sd card

2. A new window pops up. Select FAT32, press Start to initialize the formatting process and follow the onscreen instructions.

MicroSD Card Module format sd card

ESP32 Project Circuit – Wiring to All the Components

Besides the RFID reader/writer and the microSD card, we’ll also connect a piezzo buzzer and an LED to give us some feedback to let us know that the MFRC522 RFID Reader read a new tag.

  • LED — connected to GPIO 22 (via a 220 Ohm resistor)
  • Buzzer — connected to GPIO 4 (via a 1k Ohm resistor

Here’s how the final circuit looks like:

ESP32 RFID User Management System Wiring Circuit Diagram

Preparing the Arduino IDE

We’ll program the ESP32 board using Arduino IDE. So, make sure you have the ESP32 add-on installed:

To upload the HTML and CSS files needed to build this project to the ESP32 flash memory filesystem (LittleFS), we’ll use a plugin for Arduino IDE: LittleFS Filesystem uploader. Follow the next tutorial to install the filesystem uploader plugin in your Arduino IDE 2 if you haven’t already:

Installing the Arduino_MFRC522v2 Library

For this tutorial, we’ll use the MFRC522v2.h library to control the RFID reader. In the Arduino IDE, go to Sketch > Include Library > Manage Libraries or click on the Libary Manager icon at the left sidebar.

Search for MFRC522v2 and install the library by GithubCommunity.

Installing RFID MFRC522v2 Library Arduino IDE

Installing the ESPAsyncWebServer and AsyncTCP Libraries

To guarantee that you’re using the latest version of the ESPAsyncWebServer and AsyncTCP libraries, please install them via .ZIP folder (instead of the Library Manager). Click the following links to download the library files.

  1. Click here to download the ESPAsyncWebServer library.
  2. Click here to download the Async TCP library.
  3. In your Arduino IDE, go to Sketch > Include Library > Add .zip Library and select the libraries you’ve just downloaded.

Important: These libraries are a forked version of the ones we used to use in our projects. This new libraries are well maintained and updated and compatible with the latest core of the ESP32. Make sure to uninstall any previous versions or forks of the library and install the new versions we recommend here. Are you having issues with the libraries? Check how to solve the problem here.

Organizing Your Files

To keep the project organized and make it easier to understand, we’ll create 6 files to build the web server:

  • Arduino sketch: to handle the web server, RFID reader and MicroSD card;
  • full-log.html: loads all the log of every RFID card that has been scanned and its user data;
  • manage-users.html: web page that allows you to view and delete users;
  • add-user.html: web page that allows you to add new users with a unique UID;
  • get.html: handles all the HTTP GET requests;
  • style.css: to style the web page.
Organizing your Files arduino sketch index html style css

You should save the HTML and CSS files inside a folder called data inside the Arduino sketch folder, as shown in the previous diagram. We’ll upload these files to the ESP32 filesystem (LittleFS).

You can download all project files:

HTML Files

Copy the following to the full-log.html file.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Manage Users</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <nav>
        <div class="nav-container">
            <a href="/" class="brand">User Management</a>
            <ul class="nav-menu">
                <li><a href="/">📄 Full Log</a></li>
                <li><a href="add-user">➕ Add User</a></li>
                <li><a href="manage-users">👤 Manage Users</a></li>
            </ul>
        </div>
    </nav>
    <div class="main-container">
        <section class="main-section">
            <h2>📄 Full Access Log</h2>
            <table id="tableData">
                <thead>
                    <tr>
                        <th>Date</th>
                        <th>Time</th>
                        <th>UID</th>
                        <th>Role</th>
                    </tr>
                </thead>
                <tbody>
                    <!-- Data from log.txt will be loaded here -->
                </tbody>
            </table>
        </section>
    </div>
    <div class="main-container">
        <a href="get?delete=log"><button class="button button-delete">🗑️ Delete log.txt File</button></a>
    </div>
    <script>
        // JavaScript to load and parse log.txt
        async function loadTableData() {
            try {
                const response = await fetch('view-log');
                const data = await response.text();
                const rows = data.trim().split('\n').slice(1); // Skip the header line

                const tableBody = document.querySelector('#tableData tbody');
                rows.forEach(row => {
                    const columns = row.split(',');
                    const tr = document.createElement('tr');
                    columns.forEach(column => {
                        const td = document.createElement('td');
                        td.textContent = column;
                        tr.appendChild(td);
                    });
                    tableBody.appendChild(tr);
                });
            } catch (error) {
                console.error('Error loading log data:', error);
            }
        }
        // Call the function to load log data
        loadTableData();
    </script>
</body>
</html>

View raw code

Copy the following to the manage-users.html file.

View raw code

Copy the following to the add-user.html file.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Add User</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <nav>
        <div class="nav-container">
            <a href="/" class="brand">User Management</a>
            <ul class="nav-menu">
                <li><a href="/">📄 Full Log</a></li>
                <li><a href="add-user">➕ Add User</a></li>
                <li><a href="manage-users">👤 Manage Users</a></li>
            </ul>
        </div>
    </nav>
    <div class="main-container">
        <section class="main-section">
            <h2>➕ Add User</h2>
            <p>Enter the UID in lower case letters and no spaces.</p><br>
            <form action="get" class="user-form">
                <label for="uid">UID</label>
                <input type="text" id="uid" name="uid" required>
                <label for="role">Role</label>
                <select id="role" name="role">
                    <option value="admin">Admin</option>
                    <option value="user">User</option>
                </select>
                <button type="submit">✅ Save</button>
            </form>
        </section>
    </div>
</body>
</html>

View raw code

Copy the following to the get.html file.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Add User</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <nav>
        <div class="nav-container">
            <a href="/" class="brand">User Management</a>
            <ul class="nav-menu">
                <li><a href="/">📄 Full Log</a></li>
                <li><a href="add-user">➕ Add User</a></li>
                <li><a href="manage-users">👤 Manage Users</a></li>
            </ul>
        </div>
    </nav>
    <div class="main-container">
        <section class="main-section">
            <p>%inputmessage%</p>
        </section>
    </div>
</body>
</html>

View raw code

CSS File

Copy the following to the style.css file. Feel free to change it to make the web page look as you wish.

/* General Styles */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f9;
    color: #333;
    display: flex;
    flex-direction: column;
    align-items: center;
    height: 100vh;
    margin: 0;
}

/* Navigation Bar Styles */
nav {
    width: 100%;
    background-color: #333;
    padding: 1rem 0;
}

.nav-container {
    max-width: 1200px;
    margin: 0 auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 1rem;
}

.brand {
    color: #fff;
    text-decoration: none;
    font-size: 1.5rem;
    font-weight: bold;
}

.nav-menu {
    list-style-type: none;
    display: flex;
}

.nav-menu li {
    margin-left: 1.5rem;
}

.nav-menu a {
    color: #fff;
    text-decoration: none;
    font-size: 1rem;
    transition: color 0.3s;
}

.nav-menu a:hover, .nav-menu a.active {
    color: #f4f4f9;
}

.main-container {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-grow: 1;
    width: 100%;
}

.main-section {
    max-width: 500px;
    padding: 2rem;
    background-color: #fff;
    border-radius: 5px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    text-align: center;
}

.main-section h2 {
    margin-bottom: 1rem;
    color: #333;
}

.user-form label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: bold;
    color: #333;
}

.user-form input, .user-form select {
    width: 100%;
    padding: 0.5rem;
    margin-bottom: 1rem;
    border: 1px solid #ddd;
    border-radius: 4px;
}

.user-form button {
    width: 100%;
    padding: 0.7rem;
    background-color: #333;
    color: #fff;
    border: none;
    border-radius: 4px;
    font-size: 1rem;
    cursor: pointer;
    transition: background-color 0.3s;
}

.user-form button:hover {
    background-color: #555;
}


.button {
    display: inline-block;
    padding: 10px 20px;
    margin: 10px;
    font-size: 16px;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition-duration: 0.4s;
}

.button-delete {
    background-color: #780320;
    color: #fff;
}

.button-home {
    background-color: #333;
    color: #fff;
}

#tableData {
    font-family: Arial, Helvetica, sans-serif;
    border-collapse: collapse;
    width: 100%;
  }
  
#tableData td, #tableData th {
    border: 1px solid #ddd;
    padding: 8px;
}

#tableData tr:nth-child(even) {
    background-color: #f2f2f2;
}

#tableData tr:hover {
    background-color: #ddd;
}

#tableData th {
    padding-top: 12px;
    padding-bottom: 12px;
    text-align: left;
    background-color: #1f1f1f;
    color: white;
}

View raw code

Code – ESP32 RFID User Management System Web Server

The following code handles the web server to handle the RFID user management system as explained in the project overview.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete instructions at https://RandomNerdTutorials.com/esp32-rfid-user-management-web-server/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*********/
#include <Arduino.h>
#include <MFRC522v2.h>
#include <MFRC522DriverSPI.h>
//#include <MFRC522DriverI2C.h>
#include <MFRC522DriverPinSimple.h>
#include <MFRC522Debug.h>

#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <LittleFS.h>

#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <time.h>
#include <WiFi.h>

// Learn more about using SPI/I2C or check the pin assigment for your board: https://github.com/OSSLibraries/Arduino_MFRC522v2#pin-layout
MFRC522DriverPinSimple ss_pin(5);

MFRC522DriverSPI driver{ss_pin}; // Create SPI driver
//MFRC522DriverI2C driver{};     // Create I2C driver
MFRC522 mfrc522{driver};         // Create MFRC522 instance

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

long timezone = 0;
byte daysavetime = 1;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

const char* PARAM_INPUT_1 = "uid";
const char* PARAM_INPUT_2 = "role";
const char* PARAM_INPUT_3 = "delete";
const char* PARAM_INPUT_4 = "delete-user";

String inputMessage;
String inputParam;

const int ledPin = 22;
const int buzzerPin = 4;

// Write to the SD card
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();
}

// Append data to the SD card
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;
  }

  time_t t = file.getLastWrite();
  struct tm *tmstruct = localtime(&t);

  char bufferDate[50]; // Adjust buffer size as needed
  snprintf(bufferDate, sizeof(bufferDate), "%d-%02d-%02d", 
          (tmstruct->tm_year) + 1900, 
          (tmstruct->tm_mon) + 1, 
          tmstruct->tm_mday);
  char bufferTime[50]; // Adjust buffer size as needed
  snprintf(bufferTime, sizeof(bufferTime), "%02d:%02d:%02d", 
          tmstruct->tm_hour, 
          tmstruct->tm_min, 
          tmstruct->tm_sec);
          
  String lastWriteTime = bufferDate;
  String finalString = String(bufferDate) + "," + String(bufferTime) + "," + String(message) + "\n";
  Serial.println(lastWriteTime);
  if(file.print(finalString.c_str())) {
    Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

// Append data to the SD card
void appendUserFile(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;
  }

  String finalString = String(message) + "\n";

  if(file.print(finalString.c_str())) {
    Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

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");
  }
}

String processor(const String& var){
  return String("HTTP GET request sent to your ESP on input field (" 
                + inputParam + ") with value: " + inputMessage +
                "<br><a href=\"/\"><button class=\"button button-home\">Return to Home Page</button></a>");
}

void deleteLineFromFile(const char* filename, int lineNumber) {
  File file = SD.open(filename);
  if (!file) {
    Serial.println("Failed to open file for reading.");
    return;
  }

  // Read all lines except the one to delete
  String lines = "";
  int currentLine = 0;
  while (file.available()) {
    String line = file.readStringUntil('\n');
    if (currentLine != lineNumber) {
      lines += line + "\n";
    }
    currentLine++;
  }
  file.close();

  // Write back all lines except the deleted one
  file = SD.open(filename, FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file for writing.");
    return;
  }

  file.print(lines);
  file.close();
  Serial.println("Line deleted successfully.");
}

String getRoleFromFile(const char* filename, String uid) {
  File file = SD.open(filename);
  if (!file) {
    Serial.println("Failed to open file for reading.");
    return "";
  }

  // Skip the header line
  file.readStringUntil('\n');
  
  // Read each line and check for UID
  while (file.available()) {
    String line = file.readStringUntil('\n');
    
    int commaIndex = line.indexOf(',');
    if (commaIndex > 0) {
      String fileUID = line.substring(0, commaIndex);
      String role = line.substring(commaIndex + 1);

      // Compare UID
      if (fileUID == uid) {
        file.close();
        role.trim();  // Remove any extra spaces or newline characters
        return role;
      }
    }
  }
  file.close();
  return "";  // Return empty string if UID not found
}

void initRFIDReader() {
  mfrc522.PCD_Init();    // Init MFRC522 board.
  MFRC522Debug::PCD_DumpVersionToSerial(mfrc522, Serial);	// Show details of PCD - MFRC522 Card Reader details.
	Serial.println(F("Scan PICC to see UID"));
}

void initLittleFS() {
  if(!LittleFS.begin()){
    Serial.println("An Error has occurred while mounting LittleFS");
        return;
  }
}
void initWifi() {
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  // Print ESP32 Local IP Address
  Serial.print("ESP IP Address: ");
  Serial.println(WiFi.localIP());
}

void initTime() {
  Serial.println("CInitializing Time");
  struct tm tmstruct;
  delay(2000);
  tmstruct.tm_year = 0;
  getLocalTime(&tmstruct, 5000);
  Serial.printf(
    "Time and Date right now is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min,
    tmstruct.tm_sec
  );
}

void initSDCard() {
  // CS pin = 15
  if (!SD.begin(15)) {
    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);

  // If the log.txt file doesn't exist, create a file on the SD card and write the header
  File file = SD.open("/log.txt");
  if(!file) {
    Serial.println("log.txt file doesn't exist");
    Serial.println("Creating file...");
    writeFile(SD, "/log.txt", "Date,Time,UID,Role\r\n");
  }
  else {
    Serial.println("log.txt file already exists");  
  }
  file.close();

  // If the users.txt file doesn't exist, create a file on the SD card and write the header
  file = SD.open("/users.txt");
  if(!file) {
    Serial.println("users.txt file doesn't exist");
    Serial.println("Creating file...");
    writeFile(SD, "/users.txt", "UID,Role\r\n");
  }
  else {
    Serial.println("users.txt file already exists");  
  }
  file.close();
}

void setup() {
  Serial.begin(115200);  // Initialize serial communication
  while (!Serial);       // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4).
  
  initRFIDReader();
  initLittleFS();
  initWifi();
  configTime(3600 * timezone, daysavetime * 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
  initTime();
  initSDCard();

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  pinMode(buzzerPin, OUTPUT);
  digitalWrite(buzzerPin, LOW);

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/full-log.html");
  });
  // Route for root /add-user web page
  server.on("/add-user", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/add-user.html");
  });
  // Route for root /manage-users web page
  server.on("/manage-users", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/manage-users.html");
  });

  // Serve Static files
  server.serveStatic("/", LittleFS, "/");

  // Loads the log.txt file
  server.on("/view-log", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SD, "/log.txt", "text/plain", false);
  });
  // Loads the users.txt file
  server.on("/view-users", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SD, "/users.txt", "text/plain", false);
  });
  
  // Receive HTTP GET requests on <ESP_IP>/get?input=<inputMessage>
  server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
    // GET input1 and input2 value on <ESP_IP>/get?input1=<inputMessage1>&input2=<inputMessage2>
    if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
      inputMessage = request->getParam(PARAM_INPUT_1)->value();
      inputParam = String(PARAM_INPUT_1);
      inputMessage += " " + request->getParam(PARAM_INPUT_2)->value();
      inputParam += " " + String(PARAM_INPUT_2);

      String finalMessageInput = String(request->getParam(PARAM_INPUT_1)->value()) + "," + String(request->getParam(PARAM_INPUT_2)->value());
      appendUserFile(SD, "/users.txt", finalMessageInput.c_str());
    }
    else if (request->hasParam(PARAM_INPUT_3)) {
      inputMessage = request->getParam(PARAM_INPUT_3)->value();
      inputParam = String(PARAM_INPUT_3);
      if(request->getParam(PARAM_INPUT_3)->value()=="users") {
        deleteFile(SD, "/users.txt");
      }
      else if(request->getParam(PARAM_INPUT_3)->value()=="log") {
        deleteFile(SD, "/log.txt");
      }
    }
    else if (request->hasParam(PARAM_INPUT_4)) {
      inputMessage = request->getParam(PARAM_INPUT_4)->value();
      inputParam = String(PARAM_INPUT_4);
      deleteLineFromFile("/users.txt", inputMessage.toInt());
    }
    else {
      inputMessage = "No message sent";
      inputParam = "none";
    }
    request->send(LittleFS, "/get.html", "text/html", false, processor);
  });

  // Start server
  server.begin();
}

void loop() {
	// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
	if (!mfrc522.PICC_IsNewCardPresent()) {
		return;
	}

	// Select one of the cards.
	if (!mfrc522.PICC_ReadCardSerial()) {
		return;
	}

  // Save the UID on a String variable
  String uidString = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    if (mfrc522.uid.uidByte[i] < 0x10) {
      uidString += "0"; 
    }
    uidString += String(mfrc522.uid.uidByte[i], HEX);
  }
  Serial.print("Card UID: ");
  Serial.println(uidString);

  String role = getRoleFromFile("/users.txt", uidString);
  if (role != "") {
    Serial.print("Role for UID: ");
    Serial.print(uidString);
    Serial.print(" is ");
    Serial.println(role);
  } else {
    role = "unknown";
    Serial.print("UID: ");
    Serial.print(uidString);
    Serial.println(" not found, set user role to unknown");
  }
  String sdMessage = uidString + "," + role;
  appendFile(SD, "/log.txt", sdMessage.c_str());

  digitalWrite(buzzerPin, HIGH);
  digitalWrite(ledPin, HIGH);
  delay(500);
  digitalWrite(buzzerPin, LOW);
  delay(2500);
  digitalWrite(ledPin, LOW);
}

View raw code

Before uploading the code, you need to insert your network credentials in the following lines so that the ESP32 can establish a Wi-Fi connection.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_SSID";

Resources to Help You Understand the Code

This code is quite long and we won’t explain in detail how it works because all subjects covered here were already explained in previous projects.

Here’s a list of the tutorials you should look at to learn about the subjects covered here:

1) Reading the UID of an RFID tag with the ESP32: ESP32: Getting Started with MFRC522 RFID Reader/Writer (Arduino IDE).

2) Getting a timestamp using an NTP server: ESP32 NTP Client-Server: Get Date and Time (Arduino IDE).

3) Reading and writing from/to the microSD card: ESP32: Guide for MicroSD Card Module using Arduino IDE.

4) Datalogging with the ESP32: ESP32: How to Log Data (10 Different Ways)

5) Creating a web server with the ESP32: a list of all our web server projects.

6) Sending data from a web page to the ESP32 via HTML form: Input Data on HTML Form ESP32/ESP8266 Web Server using Arduino IDE.

7) Everything you need to know about creating a web server with HTML and CSS files with the ESP32: our Build Web Server with ESP32 eBook covers a lot of subjects in great detail.

Upload Code and Data Folder

After inserting your network credentials, save the code. Go to Sketch > Show Sketch Folder, and create a folder called data.

Arduino IDE Open Sketch Folder to create data folder

Inside that folder, you should place the HTML, and CSS files provided previously.

Uploading the Filesystem Image

Upload those files to the filesystem: press [Ctrl] + [Shift] + [P] on Windows or [] + [Shift] + [P] on MacOS to open the command palette. Search for the Upload LittleFS to Pico/ESP8266/ESP32 command and click on it.

If you don’t have this option is because you didn’t install the filesystem uploader plugin. Check this tutorial.

ESP32 Sketch Data Upload LittleFS Arduino IDE

Important: make sure the Serial Monitor is closed before uploading to the filesystem. Otherwise, the upload will fail.

Uploading the Code

Then, upload the code to your ESP32 board. Make sure you inserted your network credentials in the code.

Arduino IDE 2 Upload Button

When everything is successfully uploaded, open the Serial Monitor at a baud rate of 115200. Press the ESP32 EN/RST button, and it should print the ESP32 IP address.

ESP32 RFID User Management System Arduino IDE Serial Monitor Priting IP Address

Troubleshooting

Are you getting this error? assert failed: tcp_alloc /IDF/components/lwip/lwip/src/core/tcp.c:1851 (Required to lock TCPIP core functionality!)

It means you’re not using the recommended libraries. Check this explanation on how to solve the issue.

Demonstration

Open a browser on your local network and type the ESP32 IP address. You should get access to the web server page that looks like this, it should have a blank table by default.

ESP32 RFID User Management System Web Server Testing IP Address

Grab an RFID tag, and scan it in the RFID reader. Every time you scan an RFID tag, the LED should light up and the piezo buzzer will beep briefly. In your Arduino IDE Serial Monitor, the UID of your RFID card will be printed:

ESP32 RFID User Management System Arduino IDE Serial Monitor Check RFID Tags UID

For testing purposes, I recommend scanning multiple RFID tags, so that you have more data displayed in your web server. Now open your web server Full Log page, your table should look similar:

ESP32 RFID User Management System Web Server Check Full Log Table

Copy the UID of one of your RFID cards, please note that it should be in lower-case letters and without any spaces (for example: bd31152b). Then, open the Add User tab.

ESP32 RFID User Management System Open Add User web page

Type the UID and select the role (user or admin). Finally, click the “Save” button. I’ll be repeating this process for other RFID tags for demonstration purposes.

ESP32 RFID User Management System Web Server Add New User with Role

Now, if you browse to the Manager Users web page:

ESP32 RFID User Management System Open Manage Users Tab

It loads a table with all the UIDs and their corresponding user roles, you can click the “X” to delete a user.

ESP32 RFID User Management System Web Server User Log Web Page Delete Users

Scan the RFID tags a few more times, then open the web server home page. The log table should have all the entries with a timestamp, UID and their corresponding user roles.

ESP32 RFID User Management System Web Server Check Full Log Table Users with Roles

At the bottom of the Full Log and Manager Users web pages, you have the option to delete the log.txt and users.txt files from the microSD card at any time.

ESP32 RFID User Management System Web Server Delete log or users txt files

You can also access the web server page on your smartphone.

ESP32 RFID User Management System Project Demonstration

Wrapping Up

In this tutorial, you combined different subjects to build an RFID Management System and Logger with the ESP32. Here’s a list of the subjects we covered: different web server features, getting a timestamp, datalogging, interfacing an RFID reader, reading and writing files to a microSD card, and much more.

We hope you found this project useful, if you want to learn more about RFID reader/writer module with the ESP32 or about the microSD card module you can read the following guides:

To learn more about building web servers with the ESP32, check out our ebook:

If you would like to learn more about the ESP32, and for inspiration for new projects, make sure to take a look at our resources:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.