MFRC522
by Miguel Balboa (GithubCommunity)
This library drives the MFRC522 chip found on the cheap RC522 RFID reader module (13.56 MHz). It can detect a card, read its unique ID (UID), and read and write data on Mifare Classic cards and on Mifare Ultralight and NTAG tags. The two calls behind almost every "scan a card" project are `PICC_IsNewCardPresent()` and `PICC_ReadCardSerial()`. The kit that usually comes with the module contains a white card and a blue keyfob, both Mifare Classic 1K. Know what it can and cannot read. The RC522 works at 13.56 MHz, so it will not read the 125 kHz keyfobs and cards (EM4100) that look similar, and it cannot read most phones, because it cannot act as the reader for card emulation. The reading range is only about 3 cm. For projects, the UID alone is fine for simple access control, but a UID can be copied onto "magic" cards, so do not use it alone to protect anything valuable. Power and wiring cause most problems. The MFRC522 runs at 2.5–3.3 V, so power the module from 3.3 V and never from 5 V. The SPI signal pins are also 3.3 V devices, and an Uno drives them at 5 V, which is outside the specification. Many people run it directly without trouble, but the safe way is a resistor divider or a level shifter on SCK, MOSI and SDA (SS). The Uno wiring is SDA(SS) to pin 10, SCK to 13, MOSI to 11, MISO to 12 and RST to 9. On an ESP32 using the default SPI port, it is SDA(SS) to GPIO5, SCK to GPIO18, MOSI to GPIO23, MISO to GPIO19 and RST to GPIO22. The IRQ pin can stay unconnected. Before debugging anything else, call `PCD_DumpVersionToSerial()` in `setup()`. A reading of 0x91 or 0x92 means the chip is responding. A reading of 0x00 or 0xFF means no communication, which is a wiring or power problem. Cheap clone chips may report another value and still work. A "Timeout in communication" message later on usually points to the same causes, or to a card held too far from the antenna. If you write to cards, understand how a Mifare Classic 1K is organised: 16 sectors of 4 blocks each, and each block holds 16 bytes. The last block of every sector (3, 7, 11 and so on) is the sector trailer, which holds the keys and access bits. Writing a wrong value there can lock that sector permanently, and block 0 holds the manufacturer data and is normally read-only. Only write to the data blocks, authenticate with the default key `FFFFFFFFFFFF` on a new card, and finish with `PICC_HaltA()` and `PCD_StopCrypto1()` so the card can be read again.
Installation
Arduino IDE — Library Manager
Sketch → Include Library → Manage Libraries → search MFRC522
PlatformIO
lib_deps = miguelbalboa/MFRC522Supported boards
Examples
Read a card UID (Arduino Uno)
Prints the firmware version first, so you know the module is wired correctly, then the UID and card type of every card you scan.
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 10
#define RST_PIN 9
MFRC522 mfrc522(SS_PIN, RST_PIN);
void setup() {
Serial.begin(115200);
SPI.begin();
mfrc522.PCD_Init();
delay(4);
mfrc522.PCD_DumpVersionToSerial(); // 0x91 or 0x92 = module is responding
Serial.println(F("Scan a card..."));
}
void loop() {
if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) return;
Serial.print(F("UID:"));
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
Serial.print(mfrc522.uid.uidByte[i], HEX);
}
Serial.println();
Serial.println(mfrc522.PICC_GetTypeName(mfrc522.PICC_GetType(mfrc522.uid.sak)));
mfrc522.PICC_HaltA();
}ESP32 wiring and setup
Only the pin definitions and SPI.begin() change. This uses the default ESP32 SPI pins. Power the module from 3.3 V.
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 5
#define RST_PIN 22
// SCK = 18, MISO = 19, MOSI = 23
MFRC522 mfrc522(SS_PIN, RST_PIN);
void setup() {
Serial.begin(115200);
SPI.begin(18, 19, 23, SS_PIN); // sck, miso, mosi, ss
mfrc522.PCD_Init();
mfrc522.PCD_DumpVersionToSerial();
}Simple access control with an allowed UID
Compares the scanned UID with a stored one and drives an output. Replace the bytes with the UID printed by the first example. A UID can be copied, so this is for hobby use rather than real security.
byte allowedUID[4] = {0xDE, 0xAD, 0xBE, 0xEF}; // replace with your card's UID
bool uidMatches() {
if (mfrc522.uid.size != 4) return false;
for (byte i = 0; i < 4; i++) {
if (mfrc522.uid.uidByte[i] != allowedUID[i]) return false;
}
return true;
}
// inside loop(), after a card is read:
// if (uidMatches()) {
// Serial.println(F("Access granted"));
// digitalWrite(RELAY_PIN, HIGH);
// } else {
// Serial.println(F("Access denied"));
// }
// mfrc522.PICC_HaltA();Write and read a data block on a Mifare Classic card
Authenticates with the factory key and writes 16 bytes to block 4 (a data block in sector 1). Never write to blocks 3, 7, 11 and so on, because those are sector trailers that hold the keys.
MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; // factory default key
byte blockAddr = 4; // data block in sector 1
byte trailerBlock = 7; // sector trailer of that sector - do NOT write to it
byte dataBlock[16] = {'S','o','l','d','e','r','H','u','b',' ',' ',' ',' ',' ',' ',' '};
// after a card has been detected and read:
MFRC522::StatusCode status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(
MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Auth failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16);
if (status != MFRC522::STATUS_OK) {
Serial.println(mfrc522.GetStatusCodeName(status));
}
byte buffer[18]; // 16 data bytes + 2 CRC bytes
byte size = sizeof(buffer);
status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
if (status == MFRC522::STATUS_OK) {
for (byte i = 0; i < 16; i++) Serial.write(buffer[i]);
Serial.println();
}
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1(); // required after authenticating