ESP32 with MFRC522 RFID Reader/Writer (Arduino IDE)

Learn how to interface the MFRC522 RFID reader module with the ESP32 board. You’ll learn how to get raw RFID data, get the RFID card UID, and add personal data to the RFID cards. We’ll use the Arduino_MFRC522v2 library and the ESP32 will be programmed using Arduino IDE.

ESP32 with MFRC522 RFID Reader/Writer (Arduino IDE)

We have a similar guide for the ESP8266 NodeMCU with MFRC522 RFID Reader/Writer (Arduino IDE)

In summary, in this tutorial we’ll cover:

MFRC522 RFID Reader/Writer

In this tutorial, we’ll be using the MFRC522 RFID reader/writer and that’s the one we recommend you getting to interface with the ESP32.

RFID means radio-frequency identification. RFID uses electromagnetic fields to transfer data over short distances and it’s useful to identify people, make transactions, etc. Some stores also use RFID tags on their products’ labels to identify them.

An RFID system needs tags and a reader:

  • Tags (proximity integrated circuit cards—PICC) are attached to the object to be identified, in this example, we have a keychain and an electromagnetic card that come with the MFRC522 RFID Reader/Writer module. Each tag has a unique identification (UID).
RFID MFRC522 Card Tags Keychain
  • Reader/Writer (proximity coupling device—PCD) is a two-way radio transmitter-receiver that sends a signal to the tag and reads its response.
Reader Writer RFID MFRC522 Module

The MFRC522 RFID reader works at 3.3V and supports SPI and I2C communication protocols. The library we’re going to use to control the RFID reader supports both protocols, but we’ll be using the SPI.

Parts Required

Here’s a list of the required components for this project:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

To learn more about the RFID reader with the Arduino read: Security Access using MFRC522 RFID Reader with Arduino

Installing the Arduino_MFRC522v2 Library

For this tutorial, we’ll use the MFRC522v2.h library to control the RFID reader. In the Arduino IDE, go to Sketch > Include Library > Manage Libraries or click on the Libary Manager icon at the left sidebar.

Search for MFRC522v2 and install the library by GithubCommunity.

Installing RFID MFRC522v2 Library Arduino IDE

MFRC522 RFID Reader/Writer Pinout and Wiring to the ESP32

We’ll connect the MFRC522 RFID Reader using the ESP32 default SPI pins. You can use the following table.

Reader Writer RFID MFRC522 Module ESP32 Board Circuit
MFRC522 RFID ReaderESP32Description
SDAGPIO 5SPI signal input, I2C data line, or UART data input
SCKGPIO 18SPI clock
MOSIGPIO 23SPI data input
MISOGPIO 19SPI master-in-slave-out, I2C serial clock, or UART serial output
IRQDon’t connectInterrupt pin; signals the microcontroller when an RFID tag is nearby
GNDGND
RSTGPIO 21LOW signal to put the module in power-down mode; send a HIGH signal to reset the module
3.3V3.3VPower supply (2.5-3.3V)

Note: this library is also compatible with the ESP8266, Arduino, and other boards. If you are using a different board, you can check the recommended library pin assigment.

Read RFID Card Raw Data – Code

The following example reads raw data from the RFID tag. With this example, you’ll better understand how data is stored on the MIFARE 1K tags.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-mfrc522-rfid-reader-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.  
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

#include <MFRC522v2.h>
#include <MFRC522DriverSPI.h>
//#include <MFRC522DriverI2C.h>
#include <MFRC522DriverPinSimple.h>
#include <MFRC522Debug.h>

// Learn more about using SPI/I2C or check the pin assigment for your board: https://github.com/OSSLibraries/Arduino_MFRC522v2#pin-layout
MFRC522DriverPinSimple ss_pin(5);

MFRC522DriverSPI driver{ss_pin}; // Create SPI driver
//MFRC522DriverI2C driver{};     // Create I2C driver
MFRC522 mfrc522{driver};         // Create MFRC522 instance

void setup() {
  Serial.begin(115200);  // Initialize serial communication
  while (!Serial);       // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4).
  
  mfrc522.PCD_Init();    // Init MFRC522 board.
  MFRC522Debug::PCD_DumpVersionToSerial(mfrc522, Serial);	// Show details of PCD - MFRC522 Card Reader details.
  Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks..."));
}

void loop() {
  // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  // Select one of the cards.
  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  // Dump debug info about the card; PICC_HaltA() is automatically called.
  MFRC522Debug::PICC_DumpToSerial(mfrc522, Serial, &(mfrc522.uid));

  delay(2000);
}

View raw code

After wiring the circuit, upload the previous code to your board.

After uploading, open the Serial Monitor at a baud rate of 115200. Approximate your tag to the reader and see the tag info being displayed.

Reader Writer RFID MFRC522 Module ESP32 Card Reader Keychain

It displays the card UID, the tag type, and the memory blocks. The tag is of type MIFARE with 1KB of memory.

RFID MFRC522v2 Library Read Sectors Blocks UID Dumpinfo Card Arduino IDE Demonstration

Here are some essential aspects you need to take into account about how data is stored on the MIFARE 1K tag.

Memory Layout

The memory is organized into 16 sectors (0 to 15), and each sector is divided into 4 blocks (0 to 3), which gives a total of 64 blocks. Each block can store 16 bytes of data (0 to 15).

Manufacturer Block

Sector 0, block 0 stores the UID (first 4 bytes), a CRC check byte, and manufacturer data. This is read-only and cannot be changed (except on UID-changeable cards). As you can see from the results in the Serial Monitor, the UUID of our tag is 82 72 9F 0B.

Memory Storage and the Sector Trailer

The first three blocks of each sector can be used for storing data. The last block of each sector is a sector trailer. This sector trailer stores the 2 keys or passwords and controls access to the rest of the blocks in the sector. For example, block number 7 controls block 4, 5 and 6). You should not write on these sectors, otherwise, you’ll probably brick your card and it will become unusable.

In summary, the Mifare 1K cards have a net storage capacity of:

(16 sectors/card x 3 data blocks/sector X 16 bytes/block) - 16 bytes (first block) = 752 bytes/card

There are 16 sectors with 4 blocks each (64 blocks total). Each block is 16 bytes, giving 1024 bytes in total (1 KB). However, 16 blocks (one in each sector) are sector trailers that store access keys and permissions, not user data. This leaves 48 blocks available for user data. Subtracting 16 bytes for the read-only manufacturer block (Sector 0, Block 0), the effective user storage is 752 bytes.

Read the RFID Card UID – Code

The following example only reads the UID of the RFID tag. This can be useful if you just want to identify the card without caring about the rest of the stored data.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-mfrc522-rfid-reader-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.  
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

#include <MFRC522v2.h>
#include <MFRC522DriverSPI.h>
//#include <MFRC522DriverI2C.h>
#include <MFRC522DriverPinSimple.h>
#include <MFRC522Debug.h>

// Learn more about using SPI/I2C or check the pin assigment for your board: https://github.com/OSSLibraries/Arduino_MFRC522v2#pin-layout
MFRC522DriverPinSimple ss_pin(5);

MFRC522DriverSPI driver{ss_pin}; // Create SPI driver
//MFRC522DriverI2C driver{};     // Create I2C driver
MFRC522 mfrc522{driver};         // Create MFRC522 instance

void setup() {
  Serial.begin(115200);  // Initialize serial communication
  while (!Serial);       // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4).
  
  mfrc522.PCD_Init();    // Init MFRC522 board.
  MFRC522Debug::PCD_DumpVersionToSerial(mfrc522, Serial);	// Show details of PCD - MFRC522 Card Reader details.
	Serial.println(F("Scan PICC to see UID"));
}

void loop() {
	// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
	if (!mfrc522.PICC_IsNewCardPresent()) {
		return;
	}

	// Select one of the cards.
	if (!mfrc522.PICC_ReadCardSerial()) {
		return;
	}

  Serial.print("Card UID: ");
  MFRC522Debug::PrintUID(Serial, (mfrc522.uid));
  Serial.println();

  // Save the UID on a String variable
  String uidString = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    if (mfrc522.uid.uidByte[i] < 0x10) {
      uidString += "0"; 
    }
    uidString += String(mfrc522.uid.uidByte[i], HEX);
  }
  Serial.println(uidString);
}

View raw code

This code is quite simple to understand. First, you initialize the reader on the setup().

mfrc522.PCD_Init();    // Init MFRC522 board.
MFRC522Debug::PCD_DumpVersionToSerial(mfrc522, Serial);	// Show details of PCD - MFRC522 Card Reader details.
Serial.println(F("Scan PICC to see UID"));

Then, in the loop(), check if there is a card present.

// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
if (!mfrc522.PICC_IsNewCardPresent()) {
  return;
}

If so, read the card.

// Select one of the cards.
if (!mfrc522.PICC_ReadCardSerial()) {
  return;
}

Finally, print the UID and save it on a variable.

  Serial.print("Card UID: ");
  MFRC522Debug::PrintUID(Serial, (mfrc522.uid));
  Serial.println();

  // Save the UID on a String variable
  String uidString = "";
  for (byte i = 0; i < mfrc522.uid.size; i++) {
    if (mfrc522.uid.uidByte[i] < 0x10) {
      uidString += "0"; 
    }
    uidString += String(mfrc522.uid.uidByte[i], HEX);
  }
  Serial.println(uidString);
}

Upload the code to your board. Open the Serial Monitor at a baud rate of 115200.

Reader Writer RFID MFRC522 Module ESP32 Reading Card

Swipe different tags on the reader and see their unique UID being printed.

Read RFID Card UID MFRC522 Arduino IDE Demonstration Example

Write/Read Personal Data to and from the RFID Card – Code

In this example, we’ll show you how to write custom data on specific blocks. Then, we’ll also show you how to read the data from those same blocks.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-mfrc522-rfid-reader-arduino/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.  
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

#include <MFRC522v2.h>
#include <MFRC522DriverSPI.h>
//#include <MFRC522DriverI2C.h>
#include <MFRC522DriverPinSimple.h>
#include <MFRC522Debug.h>

// Learn more about using SPI/I2C or check the pin assigment for your board: https://github.com/OSSLibraries/Arduino_MFRC522v2#pin-layout
MFRC522DriverPinSimple ss_pin(5);

MFRC522DriverSPI driver{ss_pin}; // Create SPI driver
//MFRC522DriverI2C driver{};     // Create I2C driver
MFRC522 mfrc522{driver};         // Create MFRC522 instance

MFRC522::MIFARE_Key key;

byte blockAddress = 2;
byte newBlockData[17] = {"Rui Santos - RNT"};
//byte newBlockData[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};   // CLEAR DATA
byte bufferblocksize = 18;
byte blockDataRead[18];

void setup() {
  Serial.begin(115200);  // Initialize serial communication
  while (!Serial);       // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4).
  
  mfrc522.PCD_Init();    // Init MFRC522 board.
  Serial.println(F("Warning: this example overwrites a block in your card, use with care!"));
 
  // Prepare key - all keys are set to FFFFFFFFFFFF at chip delivery from the factory.
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }
}

void loop() {
  // Check if a new card is present
  if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
    delay(500);
    return;
  }

  // Display card UID
  Serial.print("----------------\nCard UID: ");
  MFRC522Debug::PrintUID(Serial, (mfrc522.uid));
  Serial.println();

  // Authenticate the specified block using KEY_A = 0x60
  if (mfrc522.PCD_Authenticate(0x60, blockAddress, &key, &(mfrc522.uid)) != 0) {
    Serial.println("Authentication failed.");
    return;
  }
  
  // Write data to the specified block
  if (mfrc522.MIFARE_Write(blockAddress, newBlockData, 16) != 0) {
    Serial.println("Write failed.");
  } else {
    Serial.print("Data written successfully in block: ");
    Serial.println(blockAddress);
  }

  // Authenticate the specified block using KEY_A = 0x60
  if (mfrc522.PCD_Authenticate(0x60, blockAddress, &key, &(mfrc522.uid)) != 0) {
    Serial.println("Authentication failed.");
    return;
  }

  // Read data from the specified block
  if (mfrc522.MIFARE_Read(blockAddress, blockDataRead, &bufferblocksize) != 0) {
    Serial.println("Read failed.");
  } else {
    Serial.println("Read successfully!");
    Serial.print("Data in block ");
    Serial.print(blockAddress);
    Serial.print(": ");
    for (byte i = 0; i < 16; i++) {
      Serial.print((char)blockDataRead[i]);  // Print as character
    }
    Serial.println();
  }
  
  // Halt communication with the card
  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();

  delay(2000);  // Delay for readability
}

View raw code

How Does the Code Work?

We start by including the necessary libraries to set up the RFID reader.

#include <MFRC522v2.h>
#include <MFRC522DriverSPI.h>
//#include <MFRC522DriverI2C.h>
#include <MFRC522DriverPinSimple.h>
#include <MFRC522Debug.h>

It defines the CS (Chip Select) pin as pin 5, which will be used for SPI communication with the reader.

// Learn more about using SPI/I2C or check the pin assigment for your board: https://github.com/OSSLibraries/Arduino_MFRC522v2#pin-layout
MFRC522DriverPinSimple ss_pin(5);

MFRC522DriverSPI driver{ss_pin}; // Create SPI driver
//MFRC522DriverI2C driver{};     // Create I2C driver
MFRC522 mfrc522{driver};         // Create MFRC522 instance

MFRC522::MIFARE_Key key;

Then, we define a variable called blockAddress. This specifies the block on the RFID tag where data will be written or read. In this example, blockAddress is set to 2, which means the code will interact with block 2 of the card’s memory. You can change this value if you want to write to a different block.

byte blockAddress = 2;

Important: the last block of each sector is a sector trailer. You should not write on that sector, otherwise, you’ll probably brick your card.

Next, newBlockData[17] holds the data you want to write to the card. Right now, it’s set to Rui Santos – RNT, but you can change it. Make sure it is no longer than 16 bytes.

byte newBlockData[17] = {"Rui Santos - RNT"};

If you want to clear the block data, you can pass the following bytes array:

byte newBlockData[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

In the setup(), initialize the MFRC522 module.

mfrc522.PCD_Init();    // Init MFRC522 board.

The default key for the RFID card is also set on the following lines. By default, all bytes are 0xFF for the factory key. This key allows access to the card’s data blocks.

// Prepare key - all keys are set to FFFFFFFFFFFF at chip delivery from the factory.
for (byte i = 0; i < 6; i++) {
  key.keyByte[i] = 0xFF;
}

The loop() checks if a new RFID card is detected. If a card is present, it reads and displays the card’s UID.

if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
  delay(500);
  return;
}

// Display card UID
Serial.print("----------------\nCard UID: ");
MFRC522Debug::PrintUID(Serial, (mfrc522.uid));
Serial.println();

Next, the code tries to authenticate a specific block on the card using the default key (in this case, block 2).

// Authenticate the specified block using KEY_A = 0x60
if (mfrc522.PCD_Authenticate(0x60, blockAddress, &key, &(mfrc522.uid)) != 0) {
  Serial.println("Authentication failed.");
  return;
}

Note: 0x60 is a command that specifies the use of KEY_A to authenticate. KEY_A is one of two keys (KEY_A and KEY_B) available on RFID cards, each offering different permissions. Using 0x60 means the code is attempting to authenticate with KEY_A, which by default is 0xFF 0xFF 0xFF 0xFF 0xFF on MIFARE RFID cards.

If authentication is successful, it writes some data (saved on the newBlockData) to that block.

// Write data to the specified block
if (mfrc522.MIFARE_Write(blockAddress, newBlockData, 16) != 0) {
  Serial.println("Write failed.");
} else {
  Serial.print("Data written successfully in block: ");
  Serial.println(blockAddress);
}

After writing, it reads the data back from the block and prints it to the Serial Monitor.

// Read data from the specified block
if (mfrc522.MIFARE_Read(blockAddress, blockDataRead, &bufferblocksize) != 0) {
  Serial.println("Read failed.");
} else {
  Serial.println("Read successfully!");
  Serial.print("Data in block ");
  Serial.print(blockAddress);
  Serial.print(": ");
  for (byte i = 0; i < 16; i++) {
    Serial.print((char)blockDataRead[i]);  // Print as character
  }
  Serial.println();
}

Finally, the code halts communication with the card and stops encryption.

// Halt communication with the card
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              

Testing the Example

In this example, we stored the following personal data “Rui Santos – RNT” in block number 2 on two different RFID cards.

RFID MFRC522v2 Library Read Personal Data Sectors Blocks Arduino IDE Demonstration

Other Useful Examples

There are few examples that come with the Arduino_MFRC522v2 library that might be helpful. In the Hack submenu, you can find the ChangeUID and FixBrickedUID examples.

Important: most RFID cards don’t allow you to modify the UID. This code only works on specific MIFARE Classic cards that have a writable UID block. Most RFID cards have the UID set as read-only, so if you try to change the UID it won’t work.

RFID MFRC522v2 Library Examples Arduino IDE

Wrapping Up

In this guide, you learned how to interface the MFRC522 RFID Reader/Writer module with the ESP32. We’ve taken a look at how storage on Mifare 1K cards works and how to dump the information from the cards. We also learned how to get the card UID, write new data on a specific block and how to read data from a specific block.

We hope you found this guide for the ESP32 with MFRC522 RFID Reader/Writer useful. We have a similar guide for the Arduino board:

If you would like to learn more about the ESP32, and for inspiration for new projects, make sure to take a look at our resources:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.