Reading RFID Cards with an RC522

Draft. This tutorial has not been checked against the physical product yet. Confirm wiring, pin assignments and voltage limits against the product documentation before building it.
DifficultyAdvanced
CategoryCommunication
Supported boardsArduino Uno R3 and compatible ATmega328P boards
Time needed60 minutes
PrerequisitesMixing 3.3 V and 5 V Safely
Tested boardNot yet verified
Tested onNot yet verified
Arduino IDE versionNot yet verified
LibraryNot yet verified

What it does

The spiral on the board is an antenna. It broadcasts a short-range field at 13.56 MHz, and a card held near it harvests enough energy from that field to power its own chip and answer back. The card has no battery; it is woken by the reader.

What comes back includes a UID, a number unique to that card, and access to a small amount of memory on it. That is enough for an attendance log, a door that opens for known cards, or a machine that knows who is using it.

Before you begin

Required parts and tools

  • An Arduino Uno or compatible board.
  • One RC522 reader kit. This is Siratek product 2609, which the photographs show includes a white card and a blue keyfob.
  • A soldering iron. Two eight-pin headers are supplied loose, one straight and one right-angled; fit whichever suits your layout.
  • Seven jumper wires.

3.3 volts, and this one really does matter

The RC522 is a 3.3 V device. Its 3.3V pin is a supply input, and 5 V there will destroy it, often immediately.

This is the most common way these boards die, because the pin sits next to GND and RST in a row that looks like any other module’s.

The SPI inputs are a softer question. In practice an Uno’s 5 V MOSI, SCK and SS work with these readers and the parts generally survive, which is why so many wiring diagrams show them connected directly. In specification terms it is out of bounds, exactly like the I2C case in Mixing 3.3 V and 5 V Safely. For something you intend to keep, use a level converter on those three lines; MISO runs the other way and needs nothing.

The simplest answer remains an ESP32, which is 3.3 V throughout and connects to this board directly with nothing in between.

Eight pins

SDA, SCK, MOSI, MISO, IRQ, GND, RST, 3.3V.

SDA here is not I2C. On this board it is the SPI chip select, which other boards call SS or CS, and the silkscreen is simply misleading. The RC522 chip can speak I2C, but these breakouts are wired for SPI.

IRQ signals that a card has arrived, and is left unconnected in the examples below.

The card and the fob

Both supplied items are MIFARE Classic 1K cards, the common type these readers are sold with.

Each holds 1 KB of memory divided into sixteen sectors of four blocks. Most blocks hold data, and the last block in each sector holds the keys that control access to it. The library ships with the factory default key, which is what makes the reading example work out of the box.

A UID is not a password

This is the part to be honest about before anyone builds a door lock.

A card’s UID is readable by anybody with a reader, and cards that let you write any UID you like are cheap and easy to buy. So a lock that opens for a known UID can be defeated by anyone who can stand near your card for a second.

MIFARE Classic’s own encryption has been thoroughly broken in published research and should not be relied on either.

What that means in practice:

  • A cabinet, a toolbox, a workshop machine, an attendance log: fine. The convenience is real and the threat is low.
  • A front door, or anything protecting money or safety: not with this. Use a proper access control product.

Build the project. Enjoy it. Just do not let it be the only thing between someone and your house, and say the same to anyone you build one for.

Pin reference

Module pin Connect to Purpose Notes
3.3V Uno 3.3V Power Never 5V
GND Uno GND Ground
RST Uno D9 Reset
SDA Uno D10 SPI chip select Not I2C, despite the name
MOSI Uno D11 Data to the reader Converter for permanent builds
MISO Uno D12 Data from the reader No converter needed
SCK Uno D13 Clock Converter for permanent builds
IRQ Not connected Card-present signal
An RC522 reader wired to an Uno over SPI, with its supply pin on the Arduino's 3.3 volt pin and the card and keyfob that come in the kit
The supply pin is 3.3 V. Five volts there destroys the reader, and the pin sits in a row that looks ordinary.

Wiring steps

  1. Unplug the USB cable.
  2. Solder one of the supplied headers to the board.
  3. GND to GND.
  4. 3.3V to the Uno’s 3.3V pin. Check this one twice.
  5. RST to D9, SDA to D10, MOSI to D11, MISO to D12, SCK to D13.
  6. Leave IRQ unconnected.
  7. Plug the USB cable back in.

Keep the leads short. SPI over long loose jumpers is a known source of readers that work intermittently.

Software and libraries

Install MFRC522 by GithubCommunity through Library Manager. Record the version.

Minimal working example

// Siratek: read the UID of any card presented to the reader.

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

const int ssPin = 10;
const int rstPin = 9;

MFRC522 reader(ssPin, rstPin);

void setup() {
  Serial.begin(9600);
  SPI.begin();
  reader.PCD_Init();

  delay(100);
  byte version = reader.PCD_ReadRegister(MFRC522::VersionReg);
  Serial.print("Siratek: reader version 0x");
  Serial.println(version, HEX);

  if (version == 0x00 || version == 0xFF) {
    Serial.println("  Reader not responding. Check wiring, and check 3.3V not 5V.");
  } else {
    Serial.println("  Present a card.");
  }
}

void loop() {
  if (!reader.PICC_IsNewCardPresent() || !reader.PICC_ReadCardSerial()) {
    return;
  }

  Serial.print("card UID: ");
  for (byte i = 0; i < reader.uid.size; i++) {
    if (reader.uid.uidByte[i] < 0x10) {
      Serial.print("0");
    }
    Serial.print(reader.uid.uidByte[i], HEX);
    Serial.print(" ");
  }
  Serial.println();

  reader.PICC_HaltA();
}

The version check in setup() is worth the five lines. 0x00 or 0xFF means the reader is not talking at all, which tells you to look at the wiring rather than at the card. A working board reports something like 0x92 or 0x91.

That one check turns the commonest failure from a mystery into a sentence.

PICC_HaltA() tells the card to stop responding, so a card left on the reader is reported once rather than continuously.

Run this and write down the UIDs of your card and your fob. You need them for what follows.

A door that opens for known cards

// Siratek: act on recognised cards. See the honest note above about what this is for.

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

MFRC522 reader(10, 9);

const int greenPin = 6;
const int redPin = 7;

// From the sketch above. Put your own UIDs here.
const byte allowed[][4] = {
  {0xDE, 0xAD, 0xBE, 0xEF},
  {0x12, 0x34, 0x56, 0x78}
};
const int allowedCount = sizeof(allowed) / sizeof(allowed[0]);

bool isAllowed(MFRC522::Uid& uid) {
  if (uid.size != 4) {
    return false;              // 7-byte UIDs exist; this list holds 4-byte ones
  }

  for (int card = 0; card < allowedCount; card++) {
    bool match = true;
    for (byte i = 0; i < 4; i++) {
      if (uid.uidByte[i] != allowed[card][i]) {
        match = false;
        break;
      }
    }
    if (match) {
      return true;
    }
  }
  return false;
}

void setup() {
  pinMode(greenPin, OUTPUT);
  pinMode(redPin, OUTPUT);

  Serial.begin(9600);
  SPI.begin();
  reader.PCD_Init();
  Serial.println("Siratek: present a card.");
}

void loop() {
  if (!reader.PICC_IsNewCardPresent() || !reader.PICC_ReadCardSerial()) {
    return;
  }

  bool ok = isAllowed(reader.uid);

  digitalWrite(ok ? greenPin : redPin, HIGH);
  Serial.println(ok ? "accepted" : "refused");
  delay(1200);
  digitalWrite(greenPin, LOW);
  digitalWrite(redPin, LOW);

  reader.PICC_HaltA();
  reader.PCD_StopCrypto1();
}

Two details.

The UID length is checked. Not every card has a four-byte UID; seven-byte ones are common on newer cards and phones. Comparing only the first four bytes of a seven-byte UID would accept cards it should not.

PCD_StopCrypto1() after each card. Without it the reader can be left in a state where the next card is not read, which looks like an intermittent hardware fault.

Reading what is on the card

The UID is not the only thing available. Each card has 1 KB of memory, and with the default key you can read and write it.

// Siratek: read one block of card memory, using the factory default key.

MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) {
  key.keyByte[i] = 0xFF;             // the factory default
}

byte block = 4;                      // block 0 holds the UID; do not write there
byte buffer[18];
byte size = sizeof(buffer);

if (reader.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A,
                            block, &key, &(reader.uid)) == MFRC522::STATUS_OK) {
  if (reader.MIFARE_Read(block, buffer, &size) == MFRC522::STATUS_OK) {
    for (byte i = 0; i < 16; i++) {
      Serial.print(buffer[i], HEX);
      Serial.print(" ");
    }
    Serial.println();
  }
}

Writing is possible too, which is how you store a name or a balance on the card rather than in the Arduino. Two cautions: block 0 holds the UID and the manufacturer data, and writing there bricks the card on most types; and every fourth block is a sector trailer holding the access keys, so writing the wrong value there locks that sector permanently. Stick to blocks that are neither, such as 4, 5, 6, 8, 9, 10.

Expected result

A version byte other than 0x00 or 0xFF at startup, then a UID printed each time the card or fob is brought within a centimetre or two.

Range is short by design, a few centimetres at most. A card held flat against the antenna reads best; held at an angle or edge-on it may not read at all, because the antenna has to couple to the coil inside the card.

Troubleshooting

Symptom Likely cause Check or fix
Version reads 0x00 or 0xFF Wiring, or reader not powered Check every SPI line and the supply
Reader dead after one use 5 V on the 3.3V pin That pin is 3.3 V only
Intermittent reads Long or loose jumper leads Shorten and reseat
First card reads, later ones do not Crypto state left open Call PCD_StopCrypto1()
Same card reported repeatedly No halt Call PICC_HaltA()
Card only reads at one angle Antenna coupling Hold it flat against the board
Very short range Normal, and metal nearby makes it worse Keep the reader clear of metal
Looking for I2C pins SDA here is SPI chip select Wire it as SS
Works on an ESP32, not an Uno Logic levels on the SPI lines Use a converter
Some cards have longer UIDs Four and seven byte UIDs both exist Check the length before comparing
Card stopped working after a write Wrote to block 0 or a sector trailer Not recoverable; avoid those blocks
Planning to use this on a front door A UID is not a password See the note above

Next steps

  • Easier first: Mixing 3.3 V and 5 V Safely, which this board is a main reason for.
  • Related products: a relay or a servo to act on a card, and the relay tutorial’s safety section before you do.
  • Next project: an attendance logger, writing a UID and a timestamp to an SD card with the DS3231 supplying the time.

Sources and attribution

All text, wiring guidance and code in this tutorial are original Siratek work.

  • MFRC522 datasheet for the 13.56 MHz operation, the supply voltage and the version register.
  • MFRC522 library documentation for the calls used.
  • The RC522 board marking, the eight-pin header, the two loose headers and the included card and keyfob were read from the Siratek product photographs for item 2609.

The statement that MIFARE Classic’s encryption is broken refers to published academic research into that card family, and the advice that a UID is not an access credential follows from UIDs being readable and writable cards being freely available. No third-party tutorial text, diagrams, photographs or code has been reproduced here.

Product used in this tutorial: MFRC-522 RC522 RFID NFC Reader
Shop
Sidebar
0 Wishlist
0 Cart