RFID door log with a web page

Scan RFID cards at your door and see every scan, allowed or denied, with the time on a web page from your phone.
  • Level:Intermediate
  • Duration: 1 h 15 min
  • Ages 12+
  • Platform: ESP32
Community project EEMENTOR team
RFID door log with a web page – EEMENTOR STEM™ project

A smart door reader that keeps a diary. Every time someone taps an RFID card, the ESP32 lights a green or red LED and records the card, the result and the time. Open the log on your phone to see the last 20 scans, updated every few seconds.

What you need

  • ESP32 board, expansion board and USB cable
  • RC522 RFID module and S50 card
  • Green LED and red LED
  • 2 resistors, 220 Ω
  • Breadboard and jumper wires (all from the ESP32 IoT Starter Kit)
  • A 2.4 GHz Wi-Fi network

Good to know

Plug the ESP32 into the expansion board from the kit, or place it next to the breadboard, and connect the pins with jumper wires.

The RC522 is a 3.3 V module, which suits the ESP32 perfectly. Connect it to 3V3, never to 5V.

Wiring

Breadboard wiring diagram: RFID door log with a web page
Breadboard view. Check each connection against the wiring table below.
From To
RC522 SDA ESP32 GPIO 5
RC522 SCK ESP32 GPIO 18
RC522 MOSI ESP32 GPIO 23
RC522 MISO ESP32 GPIO 19
RC522 IRQ Not connected
RC522 GND GND
RC522 RST ESP32 GPIO 22
RC522 3.3V ESP32 3V3
ESP32 GPIO 26 220 Ω resistor, then the green LED's long leg
ESP32 GPIO 25 220 Ω resistor, then the red LED's long leg
Both LEDs' short legs GND

Build it step by step

  1. Install the MFRC522 library (by GithubCommunity).
  2. Wire the reader to the ESP32's SPI pins (5, 18, 19, 23) plus GPIO 22, and add the two LEDs.
  3. Enter your Wi-Fi details, upload the code and open the Serial Monitor at 115200 baud.
  4. Tap your card. The Serial Monitor shows its ID, for example {0x3A, 0x7F, 0x12, 0xC4}. Paste it into allowed and upload again.
  5. Open the address shown in the Serial Monitor on your phone and tap a few cards.

The code

Replace YOUR_WIFI_NAME and YOUR_WIFI_PASSWORD with your own network details. The ESP32 only connects to 2.4 GHz Wi-Fi. After uploading, open the Serial Monitor at 115200 baud to see the address to open on your phone. The phone must be on the same Wi-Fi network.

#include <WiFi.h>
#include <WebServer.h>
#include <SPI.h>
#include <MFRC522.h>
#include <time.h>

const char* WIFI_NAME = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* TIME_ZONE = "EET-2EEST,M3.5.0/3,M10.5.0/4"; // Estonia; change for your country

const int SS_PIN = 5;
const int RST_PIN = 22;
const int GREEN_LED = 26;
const int RED_LED = 25;

MFRC522 rfid(SS_PIN, RST_PIN);
WebServer server(80);

// Paste the IDs of allowed cards (shown in the Serial Monitor)
byte allowed[][4] = {
  {0x00, 0x00, 0x00, 0x00}
};
const int ALLOWED_COUNT = sizeof(allowed) / sizeof(allowed[0]);

struct Scan { String id; bool ok; String when; };
const int LOG_SIZE = 20;
Scan scans[LOG_SIZE];
int scanCount = 0;

String cardId() {
  String id = "{";
  for (byte i = 0; i < rfid.uid.size; i++) {
    id += "0x";
    if (rfid.uid.uidByte[i] < 0x10) id += "0";
    id += String(rfid.uid.uidByte[i], HEX);
    if (i < rfid.uid.size - 1) id += ", ";
  }
  id += "}";
  id.toUpperCase();
  id.replace("0X", "0x");
  return id;
}

bool isAllowed() {
  if (rfid.uid.size != 4) return false;
  for (int c = 0; c < ALLOWED_COUNT; c++) {
    if (memcmp(rfid.uid.uidByte, allowed[c], 4) == 0) return true;
  }
  return false;
}

String timeNow() {
  struct tm t;
  if (getLocalTime(&t, 10)) {
    char text[20];
    strftime(text, sizeof(text), "%d.%m %H:%M:%S", &t);
    return String(text);
  }
  return String(millis() / 1000) + " s after start";
}

void addScan(String id, bool ok) {
  for (int i = LOG_SIZE - 1; i > 0; i--) scans[i] = scans[i - 1]; // newest first
  scans[0] = {id, ok, timeNow()};
  if (scanCount < LOG_SIZE) scanCount++;
}

String page() {
  String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>";
  html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<meta http-equiv='refresh' content='5'><title>Door log</title></head>";
  html += "<body style='font-family:sans-serif;padding:16px'><h1>Door log</h1>";
  if (scanCount == 0) html += "<p>No scans yet.</p>";
  else {
    html += "<table style='border-collapse:collapse;width:100%'>";
    html += "<tr><th align='left'>Time</th><th align='left'>Card</th><th align='left'>Result</th></tr>";
    for (int i = 0; i < scanCount; i++) {
      html += "<tr style='border-top:1px solid #ddd'><td>" + scans[i].when + "</td><td>" + scans[i].id + "</td><td>";
      html += scans[i].ok ? "<b style='color:green'>Allowed</b>" : "<b style='color:#c00'>Denied</b>";
      html += "</td></tr>";
    }
    html += "</table>";
  }
  html += "</body></html>";
  return html;
}

void setup() {
  Serial.begin(115200);
  pinMode(GREEN_LED, OUTPUT);
  pinMode(RED_LED, OUTPUT);
  SPI.begin();          // SCK 18, MISO 19, MOSI 23, SS 5
  rfid.PCD_Init();

  WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.print("Open http://");
  Serial.println(WiFi.localIP());

  configTzTime(TIME_ZONE, "pool.ntp.org"); // get the time from the internet
  server.on("/", []() { server.send(200, "text/html", page()); });
  server.begin();
}

void loop() {
  server.handleClient();
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;

  String id = cardId();
  bool ok = isAllowed();
  Serial.print("Card ID: ");
  Serial.print(id);
  Serial.println(ok ? "  allowed" : "  denied");
  addScan(id, ok);

  digitalWrite(ok ? GREEN_LED : RED_LED, HIGH);
  delay(1000);
  digitalWrite(ok ? GREEN_LED : RED_LED, LOW);

  rfid.PICC_HaltA();
  rfid.PCD_StopCrypto1();
}

How it works

The RC522 talks to the ESP32 over SPI, on the board's standard SPI pins. Each scan is compared with the allowed list and added to the start of a list of the last 20 scans. The ESP32 gets the real time from an internet time server (NTP) and uses your time zone. The web page is rebuilt from that list every time your phone reloads it, which it does every 5 seconds.

Make it your own

  • Give each card a name and show it in the log instead of the ID.
  • Add the servo from the kit to open a latch for allowed cards.
  • Count how many times each card was scanned today.

Troubleshooting

  • The time shows "s after start": the ESP32 couldn't reach the time server yet. Check the internet connection, and wait a minute.
  • No card is detected: check the SPI wires and that the reader is on 3V3.
  • The page doesn't open: make sure your phone is on the same Wi-Fi network.

Learn the basics first

New to these parts? These lessons explain them step by step: Set up the ESP32 in the Arduino IDE and Control a relay from your phone over Wi-Fi.

0 comments

Leave a comment

Please note, comments need to be approved before they are published.

Build it yourself

Get the parts used in this project, tested by our engineers.

ESP32 IoT Starter Kit | Arduino IDE Compatible | Free Tutorials $57.00

Shop the parts

More projects

  • Pulse visualiser

    • Level:Intermediate
    • Duration: 45 min
    • Platform: Arduino

    by EEMENTOR team

  • Flame alarm

    • Level:Beginner
    • Duration: 40 min
    • Platform: Arduino

    by EEMENTOR team

Community projects

Built something awesome? Share it.

Send us your project and we'll feature it in the hub, credited to you, so other makers can learn from it.

  1. Sign in and tell us about your build
  2. Our team reviews your submission
  3. We publish it in the hub, credited to you