An Arduino RFID attendance system is one of the most practical projects a maker can build — replacing paper registers with instant, tamper-proof card scans that log names, times, and dates automatically. The two most common RFID modules for Arduino projects are the EM-18 (125 kHz, serial UART) and the MFRC522 (13.56 MHz, SPI). They look similar from a distance but differ fundamentally in frequency, communication protocol, card compatibility, and security capability. This guide compares both in depth and walks you through building a complete attendance system from wiring to SD card logging.
Table of Contents
- 1. RFID Basics: 125 kHz vs 13.56 MHz
- 2. EM-18 Module: Serial UART Reader
- 3. MFRC522 Module: SPI with MIFARE Support
- 4. EM-18 vs MFRC522: Side-by-Side Comparison
- 5. EM-18 Wiring and Arduino Code
- 6. MFRC522 Wiring and Arduino Code
- 7. Complete Attendance System with SD Card Logging
- FAQ
1. RFID Basics: 125 kHz vs 13.56 MHz
Radio Frequency Identification uses electromagnetic fields to identify tags wirelessly. The reader generates an RF field; when a passive tag enters the field, it harvests energy from it and transmits its stored ID back to the reader — no battery required in the tag.
125 kHz (LF — Low Frequency): Used by EM4100/EM4102 tags, HID ProxCard, and similar legacy systems. Lower frequency means longer wavelength, which penetrates materials (cards, wallets, mild water) better than HF. Read range: 3–15 cm depending on antenna size. Data capacity is minimal — typically just a 10-digit ID code. No encryption, no read/write capability on most 125 kHz tags. Inexpensive and extremely common in older access control systems.
13.56 MHz (HF — High Frequency): Used by MIFARE Classic, MIFARE Ultralight, NTAG203, ICODE SLI, and similar cards. ISO/IEC 14443 and 15693 standards. Short read range (typically 3–10 cm at module level, up to 70 cm with large antennas). MIFARE Classic 1K cards have 1KB of data storage divided into 16 sectors, each protected by two 48-bit keys. Supports read/write, authentication, and anticollision (multiple cards detected simultaneously).
For an attendance system: if you just need to read fixed IDs (like office ID cards), 125 kHz (EM-18) is simpler. If you need to write attendance data to the card, use encrypted sectors, or work with modern smart cards, the 13.56 MHz (MFRC522) is the right choice.
2. EM-18 Module: Serial UART Reader
The EM-18 is a self-contained 125 kHz RFID reader module in a compact PCB form. It communicates via simple TTL serial UART — no library needed, just read bytes from a serial port. When a compatible tag (EM4100/EM4102 protocol) comes within range, the module automatically outputs the tag ID as 12 ASCII bytes followed by a carriage return:
- Output format: 10 hex digits of tag data + 2 hex digits checksum + CR (0x0D)
- Example output:
170042C25A05
- Baud rate: 9600 by default (some variants: 2400)
- Supply voltage: 5V, typically 100mA
- Read range: 8–12 cm with standard ISO card
- LED indicator output pin (active LOW when tag detected)
Compatible tags: EM4100/EM4102 key fobs and cards, T5577 cards programmed in EM4100 mode. NOT compatible with MIFARE or HID cards. Cheap EM cards and fobs are widely available and cost ₹10–30 each in bulk.
Advantages: No library required, works with any Arduino (just need one hardware or software UART), low power, very fast read time, very robust — just reads and outputs, nothing to go wrong.
Limitations: Read-only (cannot write to tags), no anticollision (only reads one tag at a time), no encryption, fixed 10-digit ID only — cannot store additional data.
3. MFRC522 Module: SPI with MIFARE Support
The MFRC522 is NXP’s 13.56 MHz RFID reader/writer IC mounted on a breakout module with onboard antenna. It communicates via SPI (up to 10 MHz), I2C, or UART (SPI is standard for Arduino use). The MFRC522 library for Arduino is well-maintained and handles all low-level protocol complexity.
- Frequency: 13.56 MHz, ISO/IEC 14443 Type A
- Compatible cards: MIFARE Classic 1K/4K, MIFARE Mini, MIFARE Ultralight, NTAG203/213/215/216
- Communication: SPI (default), I2C, UART
- Supply voltage: 3.3V (the module has no onboard regulator — do NOT connect to 5V directly)
- Logic levels: 3.3V — use level shifter with 5V Arduinos, or connect SPI pins directly (most 5V Arduino SPI outputs are tolerated by the MFRC522 in practice, but not officially guaranteed)
- Read range: 3–5 cm typically (module antenna size limits range)
- Read/write: Full read and write access to all MIFARE sectors
Advantages: Read/write capability, MIFARE security (sector authentication), anticollision, can detect multiple tags, rich Arduino library (Miguel Balboa’s MFRC522 library), works with modern NFC-compatible cards.
Limitations: Requires SPI wiring (5+ wires), 3.3V logic (level shifting may be needed), library required, more complex code than EM-18.
4. EM-18 vs MFRC522: Side-by-Side Comparison
| Feature | EM-18 | MFRC522 |
|---|---|---|
| Frequency | 125 kHz | 13.56 MHz |
| Protocol | EM4100/EM4102 | ISO 14443-A, MIFARE |
| Arduino interface | UART (2 pins) | SPI (5 pins) |
| Library needed | No | Yes (MFRC522) |
| Read/Write | Read only | Read and Write |
| Encryption | None | MIFARE Crypto1 |
| Supply voltage | 5V | 3.3V |
| Read range | 8–12 cm | 3–5 cm |
| Cost (approx) | ₹200–400 | ₹100–200 |
| Best for | Simple ID logging | Full R/W, secure systems |
5. EM-18 Wiring and Arduino Code
EM-18 Wiring (Uno):
- EM-18 VCC → 5V
- EM-18 GND → GND
- EM-18 TX → Arduino Pin 2 (SoftwareSerial RX)
- EM-18 LED → Arduino Pin 3 (optional, for LED feedback)
The EM-18 TX is 5V TTL — connect directly to Arduino without level shifting.
#include <SoftwareSerial.h>
SoftwareSerial rfidSerial(2, 3); // RX=2 (from EM-18 TX), TX=3 (unused)
String cardID = "";
void setup() {
Serial.begin(9600);
rfidSerial.begin(9600);
Serial.println("EM-18 RFID Attendance System");
Serial.println("Waiting for card...");
}
void loop() {
if (rfidSerial.available()) {
char c = rfidSerial.read();
if (c == 0x0D) { // Carriage return = end of ID
if (cardID.length() == 12) {
String tagID = cardID.substring(0, 10); // First 10 chars = ID
String checksum = cardID.substring(10); // Last 2 chars = checksum
Serial.print("Card ID: ");
Serial.print(tagID);
Serial.print(" | Checksum: ");
Serial.println(checksum);
// Check against known IDs
if (tagID == "170042C25A") {
Serial.println("Access GRANTED — Jayesh");
} else {
Serial.println("Unknown card");
}
}
cardID = ""; // Reset buffer
} else {
cardID += c; // Accumulate bytes
}
}
}
6. MFRC522 Wiring and Arduino Code
MFRC522 Wiring (Uno):
- MFRC522 VCC → 3.3V (Arduino 3.3V pin)
- MFRC522 GND → GND
- MFRC522 RST → Arduino Pin 9
- MFRC522 SDA (SS) → Arduino Pin 10
- MFRC522 MOSI → Arduino Pin 11
- MFRC522 MISO → Arduino Pin 12
- MFRC522 SCK → Arduino Pin 13
Install MFRC522 library: Library Manager → search MFRC522 → install by Miguel Balboa.
#include <SPI.h>
#include <MFRC522.h>
#define RST_PIN 9
#define SS_PIN 10
MFRC522 rfid(SS_PIN, RST_PIN);
void setup() {
Serial.begin(9600);
SPI.begin();
rfid.PCD_Init();
Serial.println("MFRC522 RFID Attendance System");
Serial.println("Tap card to register attendance...");
}
void loop() {
// Look for new card
if (!rfid.PICC_IsNewCardPresent()) return;
if (!rfid.PICC_ReadCardSerial()) return;
// Build UID string
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();
Serial.print("Card UID: ");
Serial.println(uid);
// Compare with known UIDs
if (uid == "A3:B2:4F:11") {
Serial.println("Attendance marked — Rahul Sharma");
} else if (uid == "C1:D4:8E:22") {
Serial.println("Attendance marked — Priya Singh");
} else {
Serial.println("Unregistered card");
}
rfid.PICC_HaltA(); // Stop reading card
rfid.PCD_StopCrypto1(); // Stop encryption
delay(1000); // Prevent double-read
}
7. Complete Attendance System with SD Card Logging
A real attendance system needs persistent storage. An SD card module (SPI-based) records entries with timestamps when combined with an RTC module (DS3231 over I2C). Here is the architecture:
- MFRC522 RFID reader: Detects cards (SPI)
- DS3231 RTC module: Provides accurate date/time (I2C)
- SD card module: Logs attendance to CSV file (SPI, different CS pin)
- 16×2 LCD (I2C): Shows feedback (I2C, shares bus with RTC)
- Buzzer: Audio confirmation (any digital pin)
Key code structure for the complete system:
#include <SPI.h>
#include <MFRC522.h>
#include <SD.h>
#include <Wire.h>
#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
#define RFID_SS 10
#define RFID_RST 9
#define SD_SS 8 // SD CS on pin 8
#define BUZZER 7
MFRC522 rfid(RFID_SS, RFID_RST);
RTC_DS3231 rtc;
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Known users: UID → Name mapping
struct User { String uid; String name; };
User users[] = {
{"A3:B2:4F:11", "Rahul Sharma"},
{"C1:D4:8E:22", "Priya Singh"},
{"F5:A1:33:44", "Amit Patel"}
};
const int USER_COUNT = 3;
void logAttendance(String name, String uid) {
DateTime now = rtc.now();
File log = SD.open("attend.csv", FILE_WRITE);
if (log) {
// CSV format: Date,Time,Name,UID
log.print(now.year()); log.print("-");
log.print(now.month()); log.print("-");
log.print(now.day()); log.print(",");
log.print(now.hour()); log.print(":");
log.print(now.minute()); log.print(",");
log.print(name); log.print(",");
log.println(uid);
log.close();
}
}
void setup() {
SPI.begin();
rfid.PCD_Init();
SD.begin(SD_SS);
rtc.begin();
Wire.begin();
lcd.init();
lcd.backlight();
pinMode(BUZZER, OUTPUT);
// Write CSV header if file is new
if (!SD.exists("attend.csv")) {
File f = SD.open("attend.csv", FILE_WRITE);
f.println("Date,Time,Name,UID");
f.close();
}
lcd.print("Attendance System");
lcd.setCursor(0,1);
lcd.print("Tap card...");
}
void loop() {
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;
String uid = "";
for (byte i = 0; i < rfid.uid.size; i++) {
if (i) uid += ":";
if (rfid.uid.uidByte[i] < 0x10) uid += "0";
uid += String(rfid.uid.uidByte[i], HEX);
}
uid.toUpperCase();
String foundName = "";
for (int i = 0; i < USER_COUNT; i++) {
if (users[i].uid == uid) { foundName = users[i].name; break; }
}
lcd.clear();
if (foundName != "") {
logAttendance(foundName, uid);
lcd.print(foundName.substring(0, 16));
lcd.setCursor(0, 1);
lcd.print("Marked!");
tone(BUZZER, 1000, 200); // Short beep
} else {
lcd.print("Unknown Card");
lcd.setCursor(0, 1);
lcd.print(uid.substring(0, 11));
tone(BUZZER, 300, 600); // Low long beep
}
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
delay(1500);
lcd.clear();
lcd.print("Tap card...");
}
The CSV file on the SD card opens in Excel or Google Sheets directly. For a school or office with 50+ students, extend the users[] array or read registrations from the SD card itself at startup.
Frequently Asked Questions
Can I use an EM-18 reader with MIFARE cards?
No. EM-18 operates at 125 kHz and is only compatible with EM4100/EM4102 protocol tags. MIFARE cards operate at 13.56 MHz on a completely different protocol. They are physically incompatible — you need an MFRC522 or similar 13.56 MHz reader for MIFARE cards.
How do I find a card’s UID with the MFRC522?
Flash the DumpInfo example from the MFRC522 library (File → Examples → MFRC522 → DumpInfo). Open Serial Monitor at 9600 baud and tap your card. It prints the complete UID, card type, and all readable data sectors. Note the UID for your lookup table.
My MFRC522 is not detecting cards. What should I check?
First confirm VCC is 3.3V (NOT 5V — this damages the module). Check SPI wiring (MOSI/MISO/SCK/SS). Ensure the MFRC522 library version matches your code. Try reducing distance (place card directly on the module antenna). Also check that SPI.begin() is called before rfid.PCD_Init() in setup().
Can I clone a card’s UID with MFRC522?
The MFRC522 can write to UID-writable (“magic”) cards like the UID changeable MIFARE Classic blanks. Standard MIFARE Classic cards have read-only UIDs assigned by the manufacturer. UID cloning is useful for testing but should not be relied on for security — use sector data authentication instead.
How many cards can the attendance system support?
The Arduino Uno has 2KB SRAM and 32KB flash. A users[] array stored in flash (using PROGMEM) can hold hundreds of entries. For thousands of users, store the UID-to-name database on the SD card as a CSV file and search it when a card is scanned — slower but unlimited capacity.
Build your RFID project today. Find all Arduino boards, RFID modules, and project accessories in our Arduino & Microcontrollers store at Zbotic — with fast delivery across India.
Add comment