RC522 RFID Lock: Open a Door with a Card or Key Fob
An RC522 reader recognises 13.56 MHz cards and fobs, the ESP32 checks the UID against a list and opens a solenoid lock for 3 seconds. With audible feedback.
What we're building
Access control for a cabinet, garage or workshop: tap a card — the lock opens; unknown card — a long beep and a log entry. The RC522 module costs next to nothing and usually comes with a card and a key fob.
Parts
- ESP32 DevKit
- RC522 module (13.56 MHz, MIFARE) + cards/fobs
- 12 V solenoid lock
- Logic-level MOSFET module (IRLZ44N) or a relay
- 1N4007 diode across the lock (absorbs the voltage spike)
- 3.3–5 V active buzzer
- 12 V power supply + a 5 V buck converter for the ESP32 (e.g. MP1584)
Wiring
The RC522 talks SPI and must be powered from 3.3 V only.
| RC522 | ESP32 |
|---|---|
| SDA (SS) | GPIO 5 |
| SCK | GPIO 18 |
| MOSI | GPIO 23 |
| MISO | GPIO 19 |
| RST | GPIO 22 |
| 3.3V / GND | 3V3 / GND |
| IRQ | not connected |
| Other | ESP32 |
|---|---|
| MOSFET SIG (lock) | GPIO 26 |
| Buzzer + | GPIO 25 |
Lock: +12V → lock → MOSFET (DRAIN), with the 1N4007 across the lock, stripe towards +12V. The 12 V and ESP32 grounds are shared.
Library
MFRC522 (GithubCommunity / miguelbalboa) — from the Library Manager.
Code
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 5
#define RST_PIN 22
#define LOCK_PIN 26
#define BUZZER_PIN 25
const unsigned long OPEN_MS = 3000;
// UIDs of allowed cards — add yours (the Serial Monitor prints them)
const char* ALLOWED[] = {
"A1B2C3D4",
"04A23B1A6F5E80",
};
MFRC522 rfid(SS_PIN, RST_PIN);
String uidToString(const MFRC522::Uid &uid) {
String s;
for (byte i = 0; i < uid.size; i++) {
if (uid.uidByte[i] < 0x10) s += '0';
s += String(uid.uidByte[i], HEX);
}
s.toUpperCase();
return s;
}
bool isAllowed(const String &uid) {
for (const char* allowed : ALLOWED) {
if (uid == allowed) return true;
}
return false;
}
void beep(int ms) {
digitalWrite(BUZZER_PIN, HIGH);
delay(ms);
digitalWrite(BUZZER_PIN, LOW);
}
void setup() {
Serial.begin(115200);
pinMode(LOCK_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(LOCK_PIN, LOW);
SPI.begin(); // SCK 18, MISO 19, MOSI 23
rfid.PCD_Init();
rfid.PCD_DumpVersionToSerial(); // should report version 0x91 or 0x92
Serial.println("Tap a card...");
}
void loop() {
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;
String uid = uidToString(rfid.uid);
if (isAllowed(uid)) {
Serial.println("Access granted: " + uid);
beep(80);
digitalWrite(LOCK_PIN, HIGH);
delay(OPEN_MS);
digitalWrite(LOCK_PIN, LOW);
} else {
Serial.println("Unknown card: " + uid);
beep(600);
}
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
Enrolling cards
- Flash the sketch as-is and open the Serial Monitor.
- Tap your cards — you'll see
Unknown card: 04A23B1A6F5E80. - Copy the UID into the
ALLOWEDarray and flash again.
Troubleshooting
Firmware Version: 0x0 = (unknown)— check the SPI wires and the 3.3 V supply (5 V can kill the module).- The lock doesn't click — the MOSFET must be logic-level; a regular IRF540 won't turn on at 3.3 V.
- The ESP32 resets when the lock fires — missing diode or a weak power supply.
⚠️ About security
Checking only the UID is convenience, not security. MIFARE Classic UIDs are easy to clone onto "magic" cards with a writable UID. Serious access control uses MIFARE DESFire cards with cryptographic authentication. For a tool cabinet or a kids' "secret entrance" it's perfectly fine.
Also provide a mechanical way to open the door in case of a power cut.
Ideas
- Store the card list in NVS and enrol new cards with a "master card" — no reflashing.
- Send an entry log to Telegram (see the "Telegram Bot" project).
- A web page to manage the card list.