What is RFID? How It Works? Interface RC522 RFID Module with Arduino

Long gone are the days when people used to stand and wait in long checkout lines at the grocery store. Thanks to the Radio Frequency IDentification (RFID) technology. With this RFID based walk-through automatic checkout solution, you can fill up your cart and walk right out the door. No longer will you have to wait as someone rings up each item in your cart one at a time. Instead, the RFID tags attached to items will communicate with RFID reader that will detect every item in the cart and ring each up almost instantly.

For most of our RFID based Arduino projects, RC522 RFID Reader/Writer module is a great choice. It is low power, low cost, pretty rugged, easy to interface with and insanely popular among hobbyists.

What is RFID technology and how does it work?

RFID or Radio Frequency Identification system consists of two main components, a transponder/tag attached to an object to be identified, and a Transceiver also known as interrogator/Reader.

How RFID Technology Works Reader Writer System Tag Communication

A Reader consists of a Radio Frequency module and an antenna which generates high frequency electromagnetic field. On the other hand, the tag is usually a passive device, meaning it doesn’t contain a battery. Instead it contains a microchip that stores and processes information, and an antenna to receive and transmit a signal.

To read the information encoded on a tag, it is placed in close proximity to the Reader (does not need to be within direct line-of-sight of the reader). A Reader generates an electromagnetic field which causes electrons to move through the tag’s antenna and subsequently power the chip.

The powered chip inside the tag then responds by sending its stored information back to the reader in the form of another radio signal. This is called backscatter. The backscatter, or change in the electromagnetic/RF wave, is detected and interpreted by the reader which then sends the data out to a computer or microcontroller.

Hardware Overview – RC522 RFID Reader/Writer Module

The RC522 RFID module based on MFRC522 IC from NXP is one of the most inexpensive RFID options that you can get online for less than four dollars. It usually comes with a RFID card tag and key fob tag having 1KB memory. And best of all, it can write a tag, so you can store your some sort of secret message in it.

RC522 RFID Reader Writer Module with Tag Card and FOB Key Tag

The RC522 RFID Reader module is designed to create a 13.56MHz electromagnetic field that it uses to communicate with the RFID tags (ISO 14443A standard tags). The reader can communicate with a microcontroller over a 4-pin Serial Peripheral Interface (SPI) with a maximum data rate of 10Mbps. It also supports communication over I2C and UART protocols.

The module comes with an interrupt pin. It is handy because instead of constantly asking the RFID module “is there a card in view yet? “, the module will alert us when a tag comes into its vicinity.

The operating voltage of the module is from 2.5 to 3.3V, but the good news is that the logic pins are 5-volt tolerant, so we can easily connect it to an Arduino or any 5V logic microcontroller without using any logic level converter.

Here are complete specifications:

Frequency Range13.56 MHz ISM Band
Host InterfaceSPI / I2C / UART
Operating Supply Voltage2.5 V to 3.3 V
Max. Operating Current13-26mA
Min. Current(Power down)10µA
Logic Inputs5V Tolerant
Read Range5 cm

RC522 RFID Module Pinout

The RC522 module has total 8 pins that interface it to the outside world. The connections are as follows:

RC522 RFID Reader Writer Module Pinout

VCC supplies power for the module. This can be anywhere from 2.5 to 3.3 volts. You can connect it to 3.3V output from your Arduino. Remember connecting it to 5V pin will likely destroy your module!

RST is an input for Reset and power-down. When this pin goes low, hard power-down is enabled. This turns off all internal current sinks including the oscillator and the input pins are disconnected from the outside world. On the rising edge, the module is reset.

GND is the Ground Pin and needs to be connected to GND pin on the Arduino.

IRQ is an interrupt pin that can alert the microcontroller when RFID tag comes into its vicinity.

MISO / SCL / Tx pin acts as Master-In-Slave-Out when SPI interface is enabled, acts as serial clock when I2C interface is enabled and acts as serial data output when UART interface is enabled.

MOSI (Master Out Slave In) is SPI input to the RC522 module.

SCK (Serial Clock) accepts clock pulses provided by the SPI bus Master i.e. Arduino.

SS / SDA / Rx pin acts as Signal input when SPI interface is enabled, acts as serial data when I2C interface is enabled and acts as serial data input when UART interface is enabled. This pin is usually marked by encasing the pin in a square so it can be used as a reference for identifying the other pins.

Wiring – Connecting RC522 RFID module to Arduino UNO

Now that we know everything about the module, we can begin hooking it up to our Arduino!

To start with, connect VCC pin on the module to 3.3V on the Arduino and GND pin to ground. The pin RST can be connected to any digital pin on the Arduino. In our case, it’s connected to digital pin#5. The IRQ pin is left unconnected as the Arduino library we are going to use doesn’t support it.

Now we are remaining with the pins that are used for SPI communication. As RC522 module require a lot of data transfer, they will give the best performance when connected up to the hardware SPI pins on a microcontroller. The hardware SPI pins are much faster than ‘bit-banging’ the interface code using another set of pins.

Note that each Arduino Board has different SPI pins which should be connected accordingly. For Arduino boards such as the UNO/Nano V3.0 those pins are digital 13 (SCK), 12 (MISO), 11 (MOSI) and 10 (SS).

If you have a Mega, the pins are different! You’ll want to use digital 50 (MISO), 51 (MOSI), 52 (SCK), and 53 (SS). Refer below table for quick understanding.

MOSIMISOSCKCS
Arduino Uno11121310
Arduino Nano11121310
Arduino Mega51505253

In case you’re using different Arduino board than mentioned above, it is advisable to check the Arduino official documentation before proceeding.

Arduino Wiring Fritzing Connections with RC522 RFID Reader Writer Module
Wiring RC522 RFID Reader Writer Module with Arduino UNO

Once you have everything hooked up you are ready to go!

Arduino Code – Reading RFID Tag

Communicating with RC522 RFID module is a bunch of work, but luckily for us, there’s a library called MFRC522 library which simplifies reading from and writing to RFID tags. Thanks to Miguel Balboa. Download the library first, by visiting the GitHub repo or, just click this button to download the zip:

To install it, open the Arduino IDE, go to Sketch > Include Library > Add .ZIP Library, and then select the rfid-master.zip file that you just downloaded. If you need more details on installing a library, visit this Installing an Arduino Library tutorial.

Once you have the library installed, open Examples submenu and select MFRC522 > DumpInfo example sketch.

MFRC522 Library Sketch DumpInfo

This sketch will not write any data to the tag. It just tells you if it managed to read the tag, and displays some information about it. This can be very useful before trying out any new tag!

Go to the beginning of the sketch and make sure that the RST_PIN is correctly initialized, in our case we’re using digital pin #5 so change it to 5!

MFRC522 Library Sketch DumpInfo RST pin

OK, now upload the sketch and open the Serial Monitor. As soon as you bring the tag closer to the module, you’ll probably get something like the following. Do not move the tag until all the information is displayed.

MFRC522 Library DumpInfo Output

It displays all the useful information about the tag including tag’s Unique ID (UID), the memory size and the whole 1K memory.

MIFARE Classic 1K Memory Layout

The 1K memory of the Tag is organized in 16 sectors (from 0 to 15)Each sector is further devided in to 4 blocks (block 0 to 3).Each block can store 16 bytes of data (from 0 to 15).

That surely tells us we have

16 sectors x 4 blocks x 16 bytes of data = 1024 bytes = 1K memory

The whole 1K memory with sectors, blocks and data is highlighted below.

MFRC522 Library DumpInfo Memory Layout
3D Representation MIFARE Classic 1K Memory Map Layout
3D Representation of MIFARE Classic 1K Memory Map Layout

The Block 3 of each sector is called Sector Trailer and contains information called Access Bits to grant read and write access to remaining blocks in a sector. That means only the bottom 3 blocks (block 0, 1 & 2) of each sector are actually available for data storage, meaning we have 48 bytes per 64 byte sector available for our own use.

Also The Block 0 of sector 0 is known as Manufacturer Block/Manufacturer Data contains the IC manufacturer data, and the Unique IDentifier (UID). The Manufacturer Block is highlighted in red below.

MFRC522 Library DumpInfo Manufacturer Block

Warning:

It is very risky to overwrite the Manufacturer Block and it may permanently lock the card.

Arduino Code – Writing RFID Tag

Considering you have successfully read the RFID tag, we’ll move on to our next experiment. The following sketch will do a basic demonstration of writing custom data to RFID tag. Try the sketch out, before we begin its detailed breakdown.

#include <SPI.h>      //include the SPI bus library
#include <MFRC522.h>  //include the RFID reader library

#define SS_PIN 10  //slave select pin
#define RST_PIN 5  //reset pin

MFRC522 mfrc522(SS_PIN, RST_PIN);  // instatiate a MFRC522 reader object.
MFRC522::MIFARE_Key key;          //create a MIFARE_Key struct named 'key', which will hold the card information

//this is the block number we will write into and then read.
int block=2;  

byte blockcontent[16] = {"Last-Minute-Engg"};  //an array with 16 bytes to be written into one of the 64 card blocks is defined
//byte blockcontent[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};  //all zeros. This can be used to delete a block.

//This array is used for reading out a block.
byte readbackblock[18];

void setup() 
{
    Serial.begin(9600);        // Initialize serial communications with the PC
    SPI.begin();               // Init SPI bus
    mfrc522.PCD_Init();        // Init MFRC522 card (in case you wonder what PCD means: proximity coupling device)
    Serial.println("Scan a MIFARE Classic card");
  
  // Prepare the security key for the read and write functions.
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;  //keyByte is defined in the "MIFARE_Key" 'struct' definition in the .h file of the library
  }
}

void loop()
{  
  // Look for new cards
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  
  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial()) 
  {
    return;
  }
    Serial.println("card selected");
         
   //the blockcontent array is written into the card block
   writeBlock(block, blockcontent);
   
   //read the block back
   readBlock(block, readbackblock);
   //uncomment below line if you want to see the entire 1k memory with the block written into it.
   //mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
   
   //print the block contents
   Serial.print("read block: ");
   for (int j=0 ; j<16 ; j++)
   {
     Serial.write (readbackblock[j]);
   }
   Serial.println("");
}



//Write specific block    
int writeBlock(int blockNumber, byte arrayAddress[]) 
{
  //this makes sure that we only write into data blocks. Every 4th block is a trailer block for the access/security info.
  int largestModulo4Number=blockNumber/4*4;
  int trailerBlock=largestModulo4Number+3;//determine trailer block for the sector
  if (blockNumber > 2 && (blockNumber+1)%4 == 0){Serial.print(blockNumber);Serial.println(" is a trailer block:");return 2;}
  Serial.print(blockNumber);
  Serial.println(" is a data block:");
  
  //authentication of the desired block for access
  byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
         Serial.print("PCD_Authenticate() failed: ");
         Serial.println(mfrc522.GetStatusCodeName(status));
         return 3;//return "3" as error message
  }
  
  //writing the block 
  status = mfrc522.MIFARE_Write(blockNumber, arrayAddress, 16);
  //status = mfrc522.MIFARE_Write(9, value1Block, 16);
  if (status != MFRC522::STATUS_OK) {
           Serial.print("MIFARE_Write() failed: ");
           Serial.println(mfrc522.GetStatusCodeName(status));
           return 4;//return "4" as error message
  }
  Serial.println("block was written");
}



//Read specific block
int readBlock(int blockNumber, byte arrayAddress[]) 
{
  int largestModulo4Number=blockNumber/4*4;
  int trailerBlock=largestModulo4Number+3;//determine trailer block for the sector

  //authentication of the desired block for access
  byte status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));

  if (status != MFRC522::STATUS_OK) {
         Serial.print("PCD_Authenticate() failed (read): ");
         Serial.println(mfrc522.GetStatusCodeName(status));
         return 3;//return "3" as error message
  }

//reading a block
byte buffersize = 18;//we need to define a variable with the read buffer size, since the MIFARE_Read method below needs a pointer to the variable that contains the size... 
status = mfrc522.MIFARE_Read(blockNumber, arrayAddress, &buffersize);//&buffersize is a pointer to the buffersize variable; MIFARE_Read requires a pointer instead of just a number
  if (status != MFRC522::STATUS_OK) {
          Serial.print("MIFARE_read() failed: ");
          Serial.println(mfrc522.GetStatusCodeName(status));
          return 4;//return "4" as error message
  }
  Serial.println("block was read");
}

The output on serial monitor will look like this.

RC522 RFID Writing Tag Code Sketch Output

Code Explanation:

The sketch starts with including the MFRC522 and SPI library, defining Arduino pins to which RC522 is connected and instantiating MFRC522 reader object.

#include <SPI.h>//include the SPI bus library
#include <MFRC522.h>//include the RFID reader library

#define SS_PIN 10  //slave select pin
#define RST_PIN 5  //reset pin
MFRC522 mfrc522(SS_PIN, RST_PIN);        // instatiate a MFRC522 reader object.
MFRC522::MIFARE_Key key;//create a MIFARE_Key struct named 'key', which will hold the card information

Next, we need to define a block in which we are going to store our data. Here sector 0 block 2 is selected. Remember never select block 3 of any sector. Writing into ‘sector trailer’ block can make the block unusable.

//this is the block number we will write into and then read.
int block=2;

Next, we define an array of 16 bytes named blockcontent[16] which holds the message we want to write into the block. You can delete any block by writing zeros.

byte blockcontent[16] = {"Last-Minute-Engg"};  //an array with 16 bytes to be written into one of the 64 card blocks is defined
//byte blockcontent[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};  //all zeros. This can be used to delete a block.

Next, we need to define an array of 18 bytes named readbackblock[18]. This can be used to read the written contents back. Wait…18 bytes? Shouldn’t this be 16 bytes? The answer is No. The MIFARE_Read method in MFRC522 library requires a buffer that is at least 18 bytes to hold the 16 bytes of a block.

//This array is used for reading out a block.
byte readbackblock[18];

In Setup function: we initialize the serial communications with the PC, SPI library and MFRC522 object. We also need to prepare the security key for the read and write functions. Here all six key bytes are set to 0xFF. Since the cards in the kit are new and the keys were never defined, they are 0xFF. If we had a card that was programmed by someone else, we would need to know the key to be able to access it. This key would then need to be stored in ‘key’ instead.

Serial.begin(9600);        // Initialize serial communications with the PC
SPI.begin();               // Init SPI bus
mfrc522.PCD_Init();        // Init MFRC522 card (in case you wonder what PCD means: proximity coupling device)
Serial.println("Scan a MIFARE Classic card");
  
// Prepare the security key for the read and write functions.
for (byte i = 0; i < 6; i++) {
   key.keyByte[i] = 0xFF;  //keyByte is defined in the "MIFARE_Key" 'struct' definition in the .h file of the library
}

In loop function: we first scan if there is a card in view, if yes, that card is selected for writing and reading purpose.

// Look for new cards
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;
  }
  
  // Select one of the cards
  if ( ! mfrc522.PICC_ReadCardSerial()) 
  {
    return;
  }
  Serial.println("card selected");

Wring the block is now very easy, we just need to call a custom function called writeBlock() which takes two parameter – one is block number to which we are interested in writing the data and the data itself.

//the blockcontent array is written into the card block
writeBlock(block, blockcontent);

To check if the write operation was successful, we need to read the block contents back. This can be done using custom function called readBlock() which again takes two parameters – one is block number and other is array to store block contents. You can use the PICC_DumpToSerial() function if you want to see the entire 1k memory with the block written into it.

//read the block back
readBlock(block, readbackblock);
//uncomment below line if you want to see the entire 1k memory with the block written into it.
//mfrc522.PICC_DumpToSerial(&(mfrc522.uid));

Finally, we print the contents of readbackblock array using a for loop and display contents on serial monitor.

//print the block contents
 Serial.print("read block: ");
 for (int j=0 ; j<16 ; j++)
 {
   Serial.write (readbackblock[j]);
 }
 Serial.println("");

Arduino Project

RFID Door Lock Access Control System

Let’s create a quick Arduino project to demonstrate how a simple RC522 RFID reader module can be used to make a RFID Door Lock Access Control System. Our program will scan unique ID of each RFID tag when it’s close enough to be energized by the RC522 reader. If the UID of the tag matches a predefined value (Master tag) that is stored in Arduino memory, the access will be granted. And if we scan any unknown tag the access will be denied. Great! Right?

This is how the output looks like.

RC522 RFID Reader Writer Door Lock Access Control Arduino Project
Door Lock Access Control Arduino Project Output

Of course this project could be interfaced to open doors, switch on a relay, light up an LED, or anything else you can think of.

In case you are not familiar with 16×2 character LCDs, consider reading (at least skimming) below tutorial.

SUGGESTED READING

Tutorial Interfacing 16x2 character LCD with Arduino Uno
Interfacing 16×2 Character LCD Module with Arduino
Want your Arduino projects to display status messages or sensor readings? Then these LCD displays might be the perfect fit. They are extremely common and...

Before we get to uploading code and scanning tags, let’s look at the circuit schematic for the project.

Arduino Wiring Fritzing Connections with RC522 RFID Reader & LCD
Wiring RC522 RFID Reader Writer Module with Arduino UNO & 16×2 Character LCD

That’s it! Now, try the below sketch out.

#include <SPI.h>
#include <MFRC522.h>
#include <LiquidCrystal.h>

#define RST_PIN 9
#define SS_PIN 10

byte readCard[4];
String MasterTag = "20C3935E";	// REPLACE this Tag ID with your Tag ID!!!
String tagID = "";

// Create instances
MFRC522 mfrc522(SS_PIN, RST_PIN);
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); //Parameters: (rs, enable, d4, d5, d6, d7) 

void setup() 
{
  // Initiating
  SPI.begin(); // SPI bus
  mfrc522.PCD_Init(); // MFRC522
  lcd.begin(16, 2); // LCD screen

  lcd.clear();
  lcd.print(" Access Control ");
  lcd.setCursor(0, 1);
  lcd.print("Scan Your Card>>");
}

void loop() 
{
  
  //Wait until new tag is available
  while (getID()) 
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    
    if (tagID == MasterTag) 
    {
      
      lcd.print(" Access Granted!");
      // You can write any code here like opening doors, switching on a relay, lighting up an LED, or anything else you can think of.
    }
    else
    {
      lcd.print(" Access Denied!");
    }
    
      lcd.setCursor(0, 1);
      lcd.print(" ID : ");
      lcd.print(tagID);
      
    delay(2000);

    lcd.clear();
    lcd.print(" Access Control ");
    lcd.setCursor(0, 1);
    lcd.print("Scan Your Card>>");
  }
}

//Read new tag if available
boolean getID() 
{
  // Getting ready for Reading PICCs
  if ( ! mfrc522.PICC_IsNewCardPresent()) { //If a new PICC placed to RFID reader continue
  return false;
  }
  if ( ! mfrc522.PICC_ReadCardSerial()) { //Since a PICC placed get Serial and continue
  return false;
  }
  tagID = "";
  for ( uint8_t i = 0; i < 4; i++) { // The MIFARE PICCs that we use have 4 byte UID
  //readCard[i] = mfrc522.uid.uidByte[i];
  tagID.concat(String(mfrc522.uid.uidByte[i], HEX)); // Adds the 4 bytes in a single String variable
  }
  tagID.toUpperCase();
  mfrc522.PICC_HaltA(); // Stop reading
  return true;
}

The program is quite simple. At the start we include the necessary libraries, define Arduino pins, create instances of LCD & MFRC522 objects and define master tag.

#include <SPI.h>
#include <MFRC522.h>
#include <LiquidCrystal.h>

#define RST_PIN 9
#define SS_PIN 10

byte readCard[4];
String MasterTag = "20C3935E";	// REPLACE this Tag ID with your Tag ID!!!
String tagID = "";

// Create instances
MFRC522 mfrc522(SS_PIN, RST_PIN);
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); //Parameters: (rs, enable, d4, d5, d6, d7)

In setup function, we initialize SPI interface, MFRC522 object and LCD. Following that we print welcome message on the LCD.

void setup() 
{
  // Initiating
  SPI.begin(); // SPI bus
  mfrc522.PCD_Init(); // MFRC522
  lcd.begin(16, 2); // LCD screen

  lcd.clear();
  lcd.print(" Access Control ");
  lcd.setCursor(0, 1);
  lcd.print("Scan Your Card>>");
}

In loop function, we wait until the new tag is scanned. Once it’s done, we compare the unknown tag with the master tag defined prior setup function. That’s it! If its ID matches master ID, access is granted else denied.

void loop() 
{
  
  //Wait until new tag is available
  while (getID()) 
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    
    if (tagID == MasterTag) 
    {
      
      lcd.print(" Access Granted!");
      // You can write any code here like opening doors, switching on a relay, lighting up an LED, or anything else you can think of.
    }
    else
    {
      lcd.print(" Access Denied!");
    }
    
      lcd.setCursor(0, 1);
      lcd.print(" ID : ");
      lcd.print(tagID);
      
    delay(2000);

    lcd.clear();
    lcd.print(" Access Control ");
    lcd.setCursor(0, 1);
    lcd.print("Scan Your Card>>");
  }
}

The key thing in the project is a custom function called getID(). Once it scans for the new card, inside a for loopit converts 4 byte of UID into string and concatenates to create a single string.

boolean getID() 
{
  // Getting ready for Reading PICCs
  if ( ! mfrc522.PICC_IsNewCardPresent()) { //If a new PICC placed to RFID reader continue
  return false;
  }
  if ( ! mfrc522.PICC_ReadCardSerial()) { //Since a PICC placed get Serial and continue
  return false;
  }
  tagID = "";
  for ( uint8_t i = 0; i < 4; i++) { // The MIFARE PICCs that we use have 4 byte UID
  //readCard[i] = mfrc522.uid.uidByte[i];
  tagID.concat(String(mfrc522.uid.uidByte[i], HEX)); // Adds the 4 bytes in a single String variable
  }
  tagID.toUpperCase();
  mfrc522.PICC_HaltA(); // Stop reading
  return true;
}