Office doors open with a card, and the manager can add new cards and unlock the door from a computer. In this final lesson you build the same system. The RC522 reader checks each card, the two-colour LED shows green for "open" and red for "denied", and the Blynk app shows who came in. From your phone you can unlock the door, switch on a learning mode to add new cards, and get a notification when someone tries an unknown card.
What you'll learn
- How to read RFID card IDs with the RC522 over SPI
- How to keep a list of allowed cards and add to it remotely
- How to combine local decisions with remote control
- How to log access events with the time
What you need
- ESP32 board, expansion board and USB cable (ESP32 IoT Starter Kit)
- RC522 RFID reader and S50 card (ESP32 IoT Starter Kit)
- Dual-colour LED module (45-in-1 Sensor Kit)
- 2 resistors, 220 Ω (ESP32 IoT Starter Kit)
- Breadboard and jumper wires (ESP32 IoT Starter Kit)
- A free Blynk account and the Blynk IoT app on a phone
- A 2.4 GHz Wi-Fi network
Wiring

| 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 | 3V3 (never 5V) |
| ESP32 GPIO 25 | 220 Ω resistor, then the LED module's red pin |
| ESP32 GPIO 26 | 220 Ω resistor, then the LED module's S (green) pin |
| LED module − | GND |
Sensor modules from different makers can label and order their pins differently. Always follow the labels printed on your module.
On many dual-colour LED modules the middle pin is red and S is green, but some are the other way round. If the colours are swapped, swap the two pin numbers in the sketch.
Set up Blynk
In the Blynk console, create a new template called RFID door (hardware ESP32, connection type Wi-Fi) and add these datastreams. Lesson 1 shows each step in detail.
| Virtual pin | Name | Data type | Range | Phone widget |
|---|---|---|---|---|
| V0 | Last visitor | String | n/a | Value Display |
| V1 | Unlock | Integer | 0 – 1 | Button (push mode) |
| V2 | Learn mode | Integer | 0 – 1 | Button (switch mode) |
| V3 | Door | Integer | 0 – 1 | LED |
Then open the template's Events & Notifications tab, add an event with the code access_denied (name: Unknown card) and turn on push notifications for it. The event code must match the sketch exactly.
Save the template, create a new device from it and copy its three BLYNK_… lines. In the phone app, open the device, tap the wrench icon and add a Value Display (V0), a Button in push mode (V1), a Button in switch mode (V2) and an LED widget (V3). Link each widget to its datastream.

The sketch
You also need the MFRC522 library (by GithubCommunity) from the RFID projects.
Replace the three BLYNK_… lines at the top with the ones from your device's Device info page, and add your Wi-Fi name and password (2.4 GHz only). The BLYNK_… lines must stay above #include <BlynkSimpleEsp32.h>.
#define BLYNK_TEMPLATE_ID "YOUR_TEMPLATE_ID"
#define BLYNK_TEMPLATE_NAME "RFID door"
#define BLYNK_AUTH_TOKEN "YOUR_AUTH_TOKEN"
#define BLYNK_PRINT Serial // show connection messages in the Serial Monitor
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>
#include <SPI.h>
#include <MFRC522.h>
#include <time.h>
char ssid[] = "YOUR_WIFI_NAME";
char pass[] = "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 RED_PIN = 25;
const int GREEN_PIN = 26;
MFRC522 rfid(SS_PIN, RST_PIN);
BlynkTimer timer;
const int MAX_CARDS = 10;
String allowed[MAX_CARDS] = { "3A 7F 12 C4" }; // paste your card's ID here
int cardCount = 1;
bool learnMode = false;
bool doorOpen = false;
unsigned long openedAt = 0;
unsigned long redUntil = 0;
String timeNow() {
struct tm t;
if (!getLocalTime(&t, 10)) return "time not set yet";
char text[20];
strftime(text, sizeof(text), "%d.%m %H:%M:%S", &t);
return String(text);
}
String readId() {
String id = "";
for (byte i = 0; i < rfid.uid.size; i++) {
if (rfid.uid.uidByte[i] < 0x10) id += "0";
id += String(rfid.uid.uidByte[i], HEX);
if (i < rfid.uid.size - 1) id += " ";
}
id.toUpperCase();
return id;
}
bool isAllowed(String id) {
for (int i = 0; i < cardCount; i++) if (allowed[i] == id) return true;
return false;
}
void openDoor(String who) {
doorOpen = true;
openedAt = millis();
digitalWrite(GREEN_PIN, HIGH);
Blynk.virtualWrite(V3, 1);
Blynk.virtualWrite(V0, who + ", " + timeNow());
Serial.println("Open: " + who);
}
BLYNK_WRITE(V1) { if (param.asInt() == 1) openDoor("Unlocked from the app"); }
BLYNK_WRITE(V2) {
learnMode = param.asInt();
Serial.println(learnMode ? "Learn mode: tap a new card" : "Learn mode off");
}
BLYNK_CONNECTED() {
Blynk.virtualWrite(V2, 0); // always start with learn mode off
Blynk.virtualWrite(V3, 0);
}
void checkCard() {
if (doorOpen && millis() - openedAt > 3000) { // close after 3 seconds
doorOpen = false;
digitalWrite(GREEN_PIN, LOW);
Blynk.virtualWrite(V3, 0);
}
digitalWrite(RED_PIN, millis() < redUntil);
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;
String id = readId();
Serial.println("Card: " + id);
if (learnMode) {
if (!isAllowed(id) && cardCount < MAX_CARDS) allowed[cardCount++] = id;
learnMode = false;
Blynk.virtualWrite(V2, 0);
Blynk.virtualWrite(V0, "Added card " + id);
} else if (isAllowed(id)) {
openDoor("Card " + id);
} else {
redUntil = millis() + 1500;
Blynk.virtualWrite(V0, "Denied: " + id + ", " + timeNow());
Blynk.logEvent("access_denied", "Unknown card " + id);
}
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
void setup() {
Serial.begin(115200);
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
SPI.begin();
rfid.PCD_Init();
Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
configTzTime(TIME_ZONE, "pool.ntp.org");
timer.setInterval(100L, checkCard);
}
void loop() {
Blynk.run();
timer.run();
}
Upload the sketch and tap your card: it's unknown, so the LED flashes red, the Serial Monitor shows its ID and your phone gets a notification. Now switch on Learn mode in the app and tap the card again. It's added, and the next tap turns the LED green. To keep a card after a restart, paste its ID into the allowed list and upload again.
How it works
Every RFID card has an ID number (UID). The RC522 talks to the ESP32 over SPI, a fast connection that uses four wires: SCK for the clock, MOSI and MISO for data in each direction, and SDA to select the reader. The sketch turns the UID into text like 3A 7F 12 C4 and compares it with the list of allowed cards. The decision happens on the ESP32 itself, so the door still works with known cards if the internet goes down. The app adds remote features on top: unlocking, learning new cards and notifications. Cards added in learn mode live in the ESP32's memory and are forgotten after a restart.
Try this next
- Save learned cards with the Preferences library so they survive a restart.
- Add the servo from the pet feeder lesson as a real latch.
- Add a "Remove card" mode that deletes the next card you tap.
Troubleshooting
-
The device shows "Offline" in the app: check the Serial Monitor at 115200 baud. "Invalid auth token" means the
BLYNK_AUTH_TOKENline is wrong; if it never connects, check the Wi-Fi name and password, and that your network is 2.4 GHz. - Cards aren't read: check the SPI wires (5, 18, 19, 23) and RST on 22, and that the reader gets 3.3 V.
- Your own card is denied: copy the ID exactly as the Serial Monitor shows it, with spaces and capital letters.
-
The app reacts slowly or the device keeps going offline: keep
loop()free of longdelay()calls. Blynk needsBlynk.run()to run many times per second.