Gone are the days when people waited in long checkout lines at the grocery store, thanks to RFID technology. With an RFID based walk-through automatic checkout solution, you can fill up your cart and walk right out the door. You no longer need to wait for someone to ring each item in your cart one by one; Now with the help of RFID tags attached to the items, every item in the cart will be detected and ringed almost instantly.

For most of our RFID based Arduino projects, the RC522 RFID reader/writer module is a great choice. It is low power, low cost, very rugged, easy to interface and extremely popular among hobbyists.

What is RFID technology and how does it work?

An RFID or radio frequency identification system consists of two main components, a tag attached to the object to be identified, and a reader that reads the tag.

A reader consists of a radio frequency module and an antenna that generates a high frequency electromagnetic field. Whereas the tag is usually a passive device (it does not have a battery). It consists of a microchip that stores and processes information, and an antenna for receiving and transmitting a signal.

How RFID Technology Works Reader Writer System Tag Communication

When the tag is brought close to the reader, the reader generates an electromagnetic field. This causes electrons to move through the tag’s antenna and subsequently powers the chip.

The chip then responds by sending its stored information back to the reader in the form of another radio signal. This is called a backscatter. The reader detects and interprets this backscatter and sends the data to a computer or microcontroller.

Hardware Overview

The RC522 RFID module based on the MFRC522 IC from NXP is one of the cheapest RFID options you can get online for less than four dollars. It usually comes with an RFID card tag and a key fob tag with 1KB of memory. And the best part is that it can write a tag that means you can store any 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 and communicate with RFID tags (ISO 14443A standard tags).

The reader can communicate with a microcontroller over a 4-pin SPI with a maximum data rate of 10 Mbps. It also supports communication over I2C and UART protocols.

The RC522 RFID module can be programmed to generate an interrupt, allowing the module to alert us when a tag approaches it, instead of constantly asking the module “Is there a card nearby?”.

The module’s operating voltage ranges 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 a logic level converter.

Technical Specifications

Here are the 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

For more details, please refer below datasheet.

RC522 RFID Module Pinout

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

RC522 RFID Reader Writer Module Pinout

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

RST is an input for reset and power-down. When this pin goes low the module enters power-down mode. In which the oscillator is turned off and the input pins are disconnected from the outside world. Whereas the module is reset on the rising edge of the signal.

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

IRQ is an interrupt pin that alerts the microcontroller when an RFID tag is in the vicinity.

MISO / SCL / Tx pin acts as master-in-slave-out when SPI interface is enabled, as serial clock when I2C interface is enabled and as serial data output when the UART interface is enabled.

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

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

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

Wiring an RC522 RFID Module to an Arduino

Now that we know everything about the module, let’s start connecting it to our Arduino!

First connect the VCC pin on the module to 3.3V and the GND pin to ground on the Arduino. Pin RST can be connected to any digital pin on the Arduino. In our case, it is connected to digital pin #5. The IRQ pin is left unconnected because the Arduino library we are going to use does not support it.

Now we are left with the pins that are used for SPI communication. Since RC522 modules require a lot of data transfer, they will give the best performance when connected to the hardware SPI pins on the microcontroller. For Arduino boards such as the UNO/Nano V3.0, these pins are digital 13 (SCK), 12 (MISO), 11 (MOSI) and 10 (SS).

If you are using a different Arduino than the boards mentioned above, please check the Arduino’s official documentation before proceeding.

The following table lists the pin connections:

RC522 ModuleArduino
VCC3.3V
GNDGND
RST5
MISO / SCL / Tx12
MOSI11
SCK13
SS / SDA / Rx10

The image below shows how to connect the RC522 module to the Arduino.

Arduino Wiring Fritzing Connections with RC522 RFID Reader Writer Module

Once you have connected everything you are ready to go!

Library Installation

Communicating with an RC522 RFID module is a lot of work, but luckily for us there is a library called the MFRC522 library that makes reading and writing RFID tags simple.

This library is not included in the Arduino IDE, so you will need to install it first.

To install the library navigate to Sketch > Include Libraries > Manage Libraries… Wait for Library Manager to download the library index and update the list of installed libraries.

manage libraries

Filter your search by typing ‘mfrc522‘. Look for the library by GithubCommunity. Click on that entry, and then select Install.

mfrc522 library installation

Arduino Code – Reading an RFID Tag

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

MFRC522 Library Sketch DumpInfo

This sketch just reads the tag and displays the information stored in it. This sketch can be very handy before trying out any new tags!

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

MFRC522 Library Sketch DumpInfo RST pin

Now upload the sketch and open Serial Monitor. As you bring the tag closer to the module, you’ll 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 the tag’s Unique ID (UID), memory size, and the entire 1K memory.

MIFARE Classic 1K Memory Layout

The tag’s 1K memory is organized into 16 sectors (from 0 to 15). Each sector is further divided into 4 blocks (blocks 0 to 3). And each block can store 16 bytes of data (from 0 to 15).

This of course tells us that we have :

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

The entire 1K of memory, along with sectors, blocks, and data, is highlighted below.

MFRC522 Library DumpInfo Memory Layout

Here is the 3D representation of the MIFARE Classic 1K memory map layout.

3D Representation MIFARE Classic 1K Memory Map Layout
3D Representation of MIFARE Classic 1K Memory Map Layout

The last block of each sector is called a Sector Trailer. It contains information called Access Bits that provide read and write access to the remaining blocks in the sector. This means that only 3 blocks of each sector (Blocks #0, #1 and #2) are actually writable, in other words only 48 bytes per sector are available for use.

Also Block #0 of Sector #0 is called Manufacturer Block which contains IC Manufacturer data and Unique Identifier (UID). The manufacturer block is highlighted in red.

MFRC522 Library DumpInfo Manufacturer Block

Warning:

Remember that overwriting the manufacturer block is very risky and can permanently lock the card.

Arduino Code – Writing an RFID Tag

Assuming that you have successfully read an RFID tag, we will move on to our next experiment. The following sketch demonstrates writing custom data to an RFID tag. Before we start a detailed analysis of it, give Sketch a try.

#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 the serial monitor will look something like this.

RC522 RFID Writing Tag Code Sketch Output

Code Explanation:

The sketch begins by including the MFRC522 and SPI libraries, defining the Arduino pins to which the RC522 is connected, and instantiating the 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

After this we define a block in which we are going to store our data. Here Sector #0 Block #2 is selected. Remember to never select block #3 of any sector. Writing in a ‘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 called blockcontent, which holds the message we want to write to the block. When you want to delete a block you can set blockcontent to 0.

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 define an array of 18 bytes called readbackblock. We will use it to read back written content. Wait… 18 bytes? Shouldn’t it be 16 bytes? The answer is No. The MIFARE_Read method in the MFRC522 library requires a buffer at least 18 bytes long that holds 16 bytes of a block.

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

In setup, we initialize serial communication, the SPI library, and the MFRC522 object. We also prepare a security key for read and write operations. Here all six bytes of key are set to 0xFF.

Remember that newer cards have all six bytes of the key set to 0xFF. If you have a card that has been programmed by someone else, you will need to know the key in order to access the card. Store that key in a key variable then.

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 the loop, we look for the new card and select it for writing and reading purposes.

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

Writing blocks is much easier now. We just call a custom function called writeBlock() which takes two parameters – a 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 whether the write operation was successful, we read back the block contents. For this we use a custom function called readBlock() which again takes two parameters – the block number and the array to store the block content. If you want to see entire 1k memory you can call PICC_DumpToSerial() function.

//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 the readbackblock array on the serial monitor using a for loop.

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

Arduino Project – RFID Based Door Access Control System

Let’s quickly create an Arduino project to demonstrate how a simple RC522 RFID reader module can be used to build a door access control system.

The program below scans the unique ID of each RFID tag. If the tag’s UID matches a predefined value (master tag) that is stored in the Arduino memory, access is granted. For unknown tags, access is denied. Awesome! Right?

This is how the output looks.

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

Of course this project can be expanded to open doors, switch relays, light LEDs, or anything else you can think of.

If you’re not familiar with 16×2 character LCDs, consider reading the tutorial below.

Wiring

Before we upload the code and start scanning the tags, let’s look at the wiring 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

Let’s try the sketch below.

#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;
}

Code Explanation:

The program is quite simple. In the beginning we include the required libraries, define the Arduino pins, create instances of LCD and MFRC522 objects and define the 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 the setup, we initialize the SPI interface, the MFRC522 object, and the LCD. After that we print the 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 the loop, we wait until the new tag is scanned. After scanning we compare the unknown tag ID with the master ID. If both match, access is granted else access is 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 most important code here is the getID() custom function. Once it scans the new card, inside the for loop it converts the 4 bytes of the uid into a string and concatenates to form 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;
}

Login
ADS CODE