ESP32 and RC522 RFID module wired on a breadboard with RGB LED — Codezlab build

Build an ESP32 RFID Attendance System with RC522, Buzzer & RGB LED


Attendance tracking is one of the most common and practical problems that IoT can solve beautifully. In this tutorial, you will build a fully working RFID-based attendance system using an ESP32 microcontroller, an RC522 RFID reader, an RGB LED for visual feedback, and a buzzer for audio feedback.

When a registered card or tag is scanned, the system lights up green and beeps once — access granted. An unregistered card triggers a red light and a different tone — access denied. The ESP32 logs each scan, making it straightforward to extend into a full network-connected attendance tracker.

This project is great for students learning embedded systems, hobbyists exploring IoT, and developers looking for a practical hardware-software integration example.

What You Need

Component Quantity Purpose
ESP32 Development Board 1 Main microcontroller with Wi-Fi & Bluetooth
RC522 RFID Module 1 Reads RFID cards and key fobs (13.56 MHz)
RFID Cards / Tags 2+ One registered, one unregistered for testing
RGB LED (Common Cathode) 1 Visual feedback — green (access) / red (denied)
Active Buzzer 1 Audio feedback on each scan
Resistors (220Ω) 3 Current limiting for RGB LED pins
Breadboard 1 Prototyping connections
Jumper Wires Several Connecting components
USB Cable (Micro or USB-C) 1 Power and serial programming
💡
Library needed: Install the MFRC522 library in Arduino IDE via Sketch → Include Library → Manage Libraries. Search for "MFRC522" by GithubCommunity.

Video Tutorial

Watch the full build walkthrough below. The video covers hardware setup, wiring, programming the ESP32, and a live demonstration of the attendance system in action.

Full build walkthrough by Codezlab on YouTube

How It Works

The RC522 communicates with the ESP32 over SPI (Serial Peripheral Interface) — a fast, short-distance protocol that uses four main lines: MOSI, MISO, SCK, and SS (SDA).

Here is the basic flow of the system:

  1. Card detected — The RC522 constantly checks for a card in its RF field. When one is found, it reads the unique UID (4–7 bytes).
  2. UID check — The ESP32 compares the scanned UID against a list of authorised UIDs stored in the code.
  3. Feedback — If the card matches, the RGB LED turns green and the buzzer beeps once (short). If no match, the LED turns red and the buzzer beeps twice (long).
  4. Serial log — Each scan is printed to the Serial Monitor with the UID and result, which you can extend to Wi-Fi logging or an LCD screen.
📡
Extend this project: Because the ESP32 has built-in Wi-Fi, you can easily send attendance data to a Google Sheet, a Firebase database, or a local web server — turning this hardware prototype into a real networked system.

Wiring Diagram

Connect your components according to the diagram below. Double-check all connections before powering on — incorrect wiring of the RC522 can damage the module.

ESP32 38-pin wiring diagram showing RC522 RFID, RGB LED, and buzzer connections with wire colour legend
ESP32 38-pin · RC522 · RGB LED · Buzzer — full wiring diagram with wire colour legend & pin mapping

RC522 → ESP32 Pin Map

RC522 Pin ESP32 GPIO Description
SDA (SS)GPIO 5Chip select
SCKGPIO 18SPI clock
MOSIGPIO 23Master out
MISOGPIO 19Master in
RSTGPIO 22Reset
3.3V3.3VPower — do NOT use 5V
GNDGNDGround

RGB LED & Buzzer → ESP32

Component Pin ESP32 GPIO Notes
Red (R)GPIO 26Via 220Ω resistor
Green (G)GPIO 25Via 220Ω resistor
Blue (B)GPIO 32Via 220Ω resistor
Cathode (GND)GNDCommon ground
Buzzer (+)GPIO 27Active buzzer signal
Buzzer (–)GNDGround

The Code

Upload the following sketch to your ESP32 using Arduino IDE. Make sure you have selected the correct board (ESP32 Dev Module) and COM port before uploading.

Arduino / C++
#include <SPI.h>
#include <MFRC522.h>

#define SS_PIN     5
#define RST_PIN    22
#define BUZZER_PIN 27

#define RED_LED_PIN   26
#define GREEN_LED_PIN 25
#define BLUE_LED_PIN  32

MFRC522 rfid(SS_PIN, RST_PIN);

// Add your RFID card UIDs here
String allowedUIDs[] = {
  "17 C4 7F 05",
  "C1 82 74 A1",
};

// Student/User names according to UID order
String userNames[] = {
  "Person 1",
  "Person 2",
};

int totalUsers = 2;

void setup() {
  Serial.begin(115200);

  SPI.begin(18, 19, 23, SS_PIN);
  rfid.PCD_Init();

  pinMode(BUZZER_PIN, OUTPUT);
  digitalWrite(BUZZER_PIN, LOW);

  pinMode(RED_LED_PIN, OUTPUT);
  pinMode(GREEN_LED_PIN, OUTPUT);
  pinMode(BLUE_LED_PIN, OUTPUT);

  Serial.println("=================================");
  Serial.println(" ESP32 RC522 RFID Attendance System");
  Serial.println("=================================");
  Serial.println("Scan your RFID card...");
}

void loop() {
  if (!rfid.PICC_IsNewCardPresent()) {
    return;
  }

  if (!rfid.PICC_ReadCardSerial()) {
    return;
  }

  String scannedUID = getUID();

  Serial.println("---------------------------------");
  Serial.print("Card UID: ");
  Serial.println(scannedUID);

  int userIndex = findUser(scannedUID);

  if (userIndex >= 0) {
    Serial.print("Attendance Marked: ");
    Serial.println(userNames[userIndex]);
    Serial.println("Status: PRESENT");

    successGlow();
    successTone();
  } else {
    Serial.println("Unknown Card");
    Serial.println("Status: ACCESS DENIED");

    errorGlow();
    errorTone();
  }

  Serial.println("---------------------------------");

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

  delay(1500);
  ledOff();
}

String getUID() {
  String uid = "";

  for (byte i = 0; i < rfid.uid.size; i++) {
    if (rfid.uid.uidByte[i] < 0x10) {
      uid += "0";
    }
    uid += String(rfid.uid.uidByte[i], HEX);
    if (i < rfid.uid.size - 1) {
      uid += " ";
    }
  }

  uid.toUpperCase();
  return uid;
}

int findUser(String uid) {
  for (int i = 0; i < totalUsers; i++) {
    if (uid == allowedUIDs[i]) {
      return i;
    }
  }
  return -1;
}

// ── LED functions ────────────────────────────────────────

void ledOff() {
  digitalWrite(RED_LED_PIN, LOW);
  digitalWrite(GREEN_LED_PIN, LOW);
  digitalWrite(BLUE_LED_PIN, LOW);
}

void successGlow() {
  digitalWrite(RED_LED_PIN, LOW);
  digitalWrite(GREEN_LED_PIN, HIGH);
  digitalWrite(BLUE_LED_PIN, LOW);
}

void errorGlow() {
  digitalWrite(RED_LED_PIN, HIGH);
  digitalWrite(GREEN_LED_PIN, LOW);
  digitalWrite(BLUE_LED_PIN, LOW);
}

// ── Buzzer functions ─────────────────────────────────────

void successTone() {
  tone(BUZZER_PIN, 1000, 150);
  delay(180);
  tone(BUZZER_PIN, 1500, 150);
  delay(180);
  tone(BUZZER_PIN, 2000, 200);
  delay(220);
  noTone(BUZZER_PIN);
}

void errorTone() {
  tone(BUZZER_PIN, 400, 250);
  delay(300);
  tone(BUZZER_PIN, 300, 250);
  delay(300);
  noTone(BUZZER_PIN);
}
⚠️
Find your card UID first: Upload a simple sketch that prints the UID of any scanned card to Serial Monitor. Copy that UID into the authorised list in the main sketch.

Testing Your Build

  1. Open Serial Monitor at 115200 baud after uploading.
  2. Scan your registered card — you should see a green flash and one short beep.
  3. Scan an unregistered card — you should see a red flash and two long beeps.
  4. Check the Serial Monitor output for the scanned UID and access result on each scan.

If the RC522 is not detected, power-cycle the ESP32, confirm your wiring matches the pin table above, and ensure the library is correctly installed.

Next Steps

Once the basic system is working, here are some practical extensions:

  • Wi-Fi logging — Send scan data to a Google Sheet via Google Apps Script or to a Firebase Realtime Database.
  • LCD display — Add a 16×2 I2C LCD to show the cardholder's name and timestamp locally.
  • EEPROM storage — Store registered UIDs in EEPROM so they persist across power cycles without re-uploading the sketch.
  • Relay control — Connect a relay to a door lock or turnstile to make the system physically control access.
  • Web dashboard — Build a small Express.js or Flask server that receives attendance pings and shows a live dashboard.

Want to learn IoT and embedded systems hands-on? We run practical classes.

Ask About IoT Courses