Read and write RFID tags. RC522 module for Arduino.

Father

Professional
Messages
2,602
Reaction score
761
Points
113
Today I will tell you about the RC522 RFID module, based on the MFRC522 chip. Power supply 3.3 V, detection range up to 6cm. Designed for reading and writing RFID tags with a frequency of 13.56 MHz. The frequency in this case is very important, since RFID tags exist in three frequency ranges:
  • LF band labels (125-134 kHz)
  • HF Band tags (13.56 MHz)
  • UHF band tags (860-960 MHz)
Specifically, this module works with HF band tags, in particular with the MIFARE protocol.

To work with the module, you can use the standard RFID library included in the Arduino IDE, but there is another library written specifically for this module-MFRC522 (1 MB). Both libraries are quite convenient, but MFRC522 has more special features that allow you to reduce the final program code as much as possible.

Enabling it​

Some may encounter a problem - the name of pins in most lessons and manuals may not match the pinout on your module. If the SS pin is specified in the sketches, but your module does not have it, then it is most likely marked as SDA. Below I will provide a module connection table for the most common boards.

MFRC522Arduino UnoArduino MegaArduino Nano v3Arduino Leonardo/MicroArduino Pro Micro
RST95D9RESET/ICSP-5RST
SDA(SS)1053D101010
MOSI11 (ICSP-4)51D11ICSP-416
MISO12 (ICSP-1)50D12ICSP-114
SCK13 (ICSP-3)52D13ICSP-315
3.3V3.3V3.3V3.3 V Stabilizer3.3 V Stabilizer3.3 V Stabilizer
GNDGNDGNDGNDGNDGND

The SS(SDA) and RST control pins are set in the sketch, so if your board is different from what I will use in my examples, and I use UNO R3, specify the pins from the table at the beginning of the sketch:

#define SS_PIN 10
#define RST_PIN 9

_sOi6H3oMyEHIeRCkQHEd9OSriDWUr64HMMpSHLZ6Q_82ONBexPwfyjpolUogfczPzQlCEkm4MVY-VTdllAljFXSxhkZY-A2OdwadNzBLAHH37VFzAO5Rhe3tLqN1BD3


Example # 1: Reading the card number​

Let's look at an example from the RFID - cardRead library. It doesn't provide data from the card, but only its number, which is usually enough for many tasks.

#include
#include

#define SS_PIN 10
#define RST_PIN 9

RFID rfid(SS_PIN, RST_PIN);

// Data about the card number is stored in 5 variables. We will remember them to check whether we have already read such a card
int serNum0;
int serNum1;
int serNum2;
int serNum3;
int serNum4;

void setup()
{
Serial.begin(9600);
SPI.begin();
rfid.init();

}

void loop()
{
if (rfid.isCard ()) {
if (rfid. readCardSerial ()) {//Comparing the card number with the previous card
number if (rfid.serNum[0] != serNum0
&& rfid.serNum[1] != serNum1
&& rfid.serNum[2] != serNum2
&& rfid.serNum[3] != serNum3
&& rfid.serNum[4] != serNum4
) {
/* If the map is new, then read* /
Serial. println(" ");
Serial.println("Card found");
serNum0 = rfid.serNum[0];
serNum1 = rfid.serNum[1];
serNum2 = rfid.serNum[2];
serNum3 = rfid.serNum[3];
serNum4 = rfid.serNum[4];

//Displaying the card number
Serial.println("Cardnumber:");
Serial.print("Dec: ");
Serial.print(rfid.serNum[0],DEC);
Serial.print(", ");
Serial.print(rfid.serNum[1],DEC);
Serial.print(", ");
Serial.print(rfid.serNum[2],DEC);
Serial.print(", ");
Serial.print(rfid.serNum[3],DEC);
Serial.print(", ");
Serial.print(rfid.serNum[4],DEC);
Serial.println(" ");

Serial.print("Hex: ");
Serial.print(rfid.serNum[0],HEX);
Serial.print(", ");
Serial.print(rfid.serNum[1],HEX);
Serial.print(", ");
Serial.print(rfid.serNum[2],HEX);
Serial.print(", ");
Serial.print(rfid.serNum[3],HEX);
Serial.print(", ");
Serial.print(rfid.serNum[4],HEX);
Serial.println(" ");
} else {
/* If it's already a read map, just print a dot * /
Serial. print(".");
}
}
}

rfid.halt();
}

The sketch lights up, the power LED on the module lights up, but the module doesn't respond to the card? Do not panic, or run to find the "right" examples of work. Most likely, there is simply no contact on one of the pins - the holes on the board are slightly larger than the thickness of the jumper, so it's worth trying to rearrange them. Is the LED off on the board? Try rearranging the jumper that operates in 3.3 V, and make sure that it is connected to 3.3 V on the board, since a 5V power supply can easily kill your board.

Let's say everything worked for you. Then, reading the RFID tags with the module, we will see the following in the serial port monitor::

_sOi6H3oMyEHIeRCkQHEd9OSriDWUr64HMMpSHLZ6Q_82ONBexPwfyjpolUogfczPzQlCEkm4MVY-VTdllAljIPcqNerfOGsM-h63zPOx60tS3kZiDWwfacI-Oq3yvv-


Here I read 3 different labels, and as you can see, it successfully counted all 3.

Example # 2: Reading data from a bank card​

Let's consider a more detailed version - it will read not only the card number, but also all the data available for reading. This time, let's take an example from the MFRC522-DumpInfo library.

#include
#include

#define RST_PIN 9 //
#define SS_PIN 10 //

MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance

void setup() {
Serial. begin(9600); / / Initializing the serial port monitor
while (!Serial); / / Do nothing until it is open (for Arduino on an ATMEGA32U4 chip)
SPI. begin (); / / Initialize the SPI bus
mfrc522. PCD_Init (); / / Initializing the RFID module
ShowReaderDetails(); // Displaying data about the MFRC522 module
Serial.println(F("Scan PICC to see UID, type, and data blocks..."));
}

void loop () {
//Looking for a new map
if (! mfrc522. PICC_IsNewCardPresent ()) {
return;
}

// Selecting one of the maps
if ( ! mfrc522. PICC_ReadCardSerial ()) {
return;
}

// Displaying data from the map
mfrc522. PICC_DumpToSerial(&(mfrc522. uid));
}

void ShowReaderDetails() {
// Getting the module version number
byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
Serial.print(F("MFRC522 Software Version: 0x"));
Serial.print(v, HEX);
if (v == 0x91)
Serial.print(F(" = v1.0"));
else if (v == 0x92)
Serial.print(F(" = v2.0"));
else
Serial.print(F(" (unknown)"));
Serial.println("");
// When we receive 0x00 or 0xFF, data transmission is broken
if ((v == 0x00) || (v == 0xFF)) {
Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?"));
}
}

If the previous example worked without errors, then there should be no problems with this one either. Although the metro pass that gave out the card number in the previous example without any problems turned out to have an undetectable data type, and the module couldn't read anything other than the card number.

As a result, after reading data from the card, we get its type, ID, and data from 16 memory sectors. It should be noted that MIFARE 1K cards consist of 16 sectors, each sector consists of 4 blocks, and each block contains 16 bytes of data.

_sOi6H3oMyEHIeRCkQHEd9OSriDWUr64HMMpSHLZ6Q_82ONBexPwfyjpolUogfczPzQlCEkm4MVY-VTdllAljA0q-xuiUN26b8l34dXrwIPSzG6NWr6ROdtQZ1jxrzxs


_sOi6H3oMyEHIeRCkQHEd9OSriDWUr64HMMpSHLZ6Q_82ONBexPwfyjpolUogfczPzQlCEkm4MVY-VTdllAljAqoMhqedF67gkY0hnBg5GbtYSTn79bPPMDGSQmCPp0e


Example # 3: Writing a new ID to the map​

In this example, we will look at changing the map ID (UID). It is important to know that not all maps support changing the ID. The map can be rewritable, but this only means that the data is rewritable. Unfortunately, the cards I had in my hands didn't support overwriting the UID, but I'll give you the sketch code here just in case.

#include
#include

/* Setting a new UID here */
#define NEW_UID {0xDE, 0xAD, 0xBE, 0xEF}
#define SS_PIN 10
#define RST_PIN 9

MFRC522 mfrc522(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;

void setup() {
Serial.begin(9600);
while (!Serial);
SPI.begin();
mfrc522.PCD_Init();
Serial.println(F("Warning: this example overwrites the UID of your UID changeable card, use with care!"));
for (byte i = 0; i < 6; i++) {
key.keyByte = 0xFF;
}
}
void loop() {
if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) {
delay(50);
return;
}
/ / Reading the current UID
Serial. print(F ("Card UID:"));
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte < 0x10 ? " 0" : " ");
Serial.print(mfrc522.uid.uidByte, HEX);
}
Serial. println();
// Writing a new UID
byte newUid[] = NEW_UID;
if ( mfrc522.MIFARE_SetUid(newUid, (byte)4, true) ) {
Serial.println(F("Wrote new UID to card."));
}

// Halt PICC and re-select it so DumpToSerial doesn't get confused
mfrc522.PICC_HaltA();
if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522. PICC_ReadCardSerial() ) {
return;
}

// Reading data from the map
Serial.println(F("New UID and contents:"));
mfrc522.PICC_DumpToSerial(&(mfrc522.uid));

delay(2000);
}

Example # 4: Writing data to the card​

Finally, what we've been working on for so long is writing data to the map. The sweetest part of working with the module is the ability to make a copy of an existing map, add or change something, which is much more interesting than just reading it.

Changing one of the data blocks on the map:

#include
#include

#define RST_PIN 9
#define SS_PIN 10

MFRC522 mfrc522(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key key;

void setup() {
Serial.begin(9600);
while (!Serial);
SPI.begin();
mfrc522.PCD_Init();

// Prepare the key
/ / use the FFFFFFFFFFFFh key, which is the standard for empty maps
for (byte i = 0; i < 6; i++) {
key.keyByte = 0xFF;
}

Serial.println(F("Scan a MIFARE Classic PICC to demonstrate read and write."));
Serial.print(F("Using key (for A and B):"));
dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE);
Serial.println();

Serial.println(F("BEWARE: Data will be written to the PICC, in sector #1"));
}

void loop () {
//Waiting for a new map
if (! mfrc522. PICC_IsNewCardPresent())
return;

// Selecting one of the maps
if (! mfrc522. PICC_ReadCardSerial ())
return;

// Showing map details
Serial.print(F("Card UID:"));
dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);
Serial.println();
Serial.print(F("PICC type: "));
byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
Serial.println(mfrc522.PICC_GetTypeName(piccType));

// Checking compatibility
if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI
&& piccType != MFRC522::PICC_TYPE_MIFARE_1K
&& piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
Serial.println(F("This sample only works with MIFARE Classic cards."));
return;
}

// In this example, we use the first sector of map data, block 4
byte sector = 1;
byte blockAddr = 4;
byte dataBlock[] = { // Data that we will write to the map
0x01, 0x02, 0x03, 0x04, // 1, 2, 3, 4,
0x05, 0x06, 0x07, 0x08, // 5, 6, 7, 8,
0x08, 0x09, 0xff, 0x0b, // 9, 10, 255, 12,
0x0c, 0x0d, 0x0e, 0x0f // 13, 14, 15, 16
};
byte trailerBlock = 7;
byte status;
byte buffer[18];
byte size = sizeof(buffer);

// Authentication
Serial.println(F("Authenticating using key A..."));
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}

// Showing current sector data
Serial.println(F("Current data in sector:"));
mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
Serial.println();

// Reading data from the block
Serial.print(F("Reading data from block ")); Serial.print(blockAddr);
Serial.println(F(" ..."));
status = mfrc522.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Read() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
}
Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));
dump_byte_array(buffer, 16); Serial.println();
Serial.println();

// Authentication
Serial.println(F("Authenticating again using key B..."));
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}

// Writing data to the block
Serial.print(F("Writing data into block ")); Serial.print(blockAddr);
Serial.println(F(" ..."));
dump_byte_array(dataBlock, 16); Serial.println();
status = mfrc522.MIFARE_Write(blockAddr, dataBlock, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
}
Serial.println();

// Reading the data again to verify that the write was successful
Serial.print(F("Reading data from block ")); Serial.print(blockAddr);
Serial.println(F(" ..."));
status = mfrc522.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Read() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
}
Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":"));
dump_byte_array(buffer, 16); Serial.println();

Serial.println(F("Checking result..."));
byte count = 0;
for (byte i = 0; i < 16; i++) {
if (buffer == dataBlock)
count++;
}
Serial.print(F("Number of bytes that match = ")); Serial.println(count);
if (count == 16) {
Serial.println(F("Success :-)"));
} else {
Serial.println(F("Failure, no match :-("));
Serial.println(F(" perhaps the write didn't work properly..."));
}
Serial.println();

// Outputting data
Serial.println(F("Current data in sector:"));
mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
Serial.println();

mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
}

void dump_byte_array(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer < 0x10 ? " 0" : " ");
Serial.print(buffer, HEX);
}
}

And as a result, we get a map with a modified data block:

_sOi6H3oMyEHIeRCkQHEd9OSriDWUr64HMMpSHLZ6Q_82ONBexPwfyjpolUogfczPzQlCEkm4MVY-VTdllAljEcbJ8FwX91t0F7-t6-9q66JlrBvjlBQ8oRlk_SVcTzK


Now that you've learned how to read and write map data blocks, you can experiment with placemarks that you most likely have - passes, public transport passes. Try reading and writing data from these cards, a couple of duplicate passes never hurt, right?)

That's all, subscribe and follow the posts. Next time, I'll explain and show you how to add custom characters to a standard 1602 character display, actually adding graphics to the display.
 
Top