Interfacing a raspberry pi with rfid technology is one of the most reliable ways to build physical access control, attendance trackers, or secure tool-lockout systems. The direct answer for 90% of hobbyist and light-commercial builds is to use the MFRC522 reader module communicating over the SPI bus. The Pi reads the 4-byte or 7-byte UID of 13.56 MHz MIFARE tags without needing external power supplies or complex level-shifting, provided you wire it correctly.
This guide targets the Raspberry Pi 4 Model B (4GB) running Raspberry Pi OS (Bookworm or Bullseye), though the exact same SPI pinout and Python code apply to the Pi 3B+ and Pi 5. We will cover the exact hardware variants, the physical wiring, a production-ready Python script with error handling, and the specific debugging steps for when the SPI bus inevitably throws a fit.
Hardware Spec Sheet & Parts List
Before you start stripping wires, verify your modules. The most common mistake in RFID builds is buying a 125 kHz module expecting it to read 13.56 MHz fobs, or buying a 5V Arduino-tolerant RFID board and frying the Pi's 3.3V GPIO pins.
| Component | Exact Variant Required | Est. Price (2026) | Critical Notes |
|---|---|---|---|
| Microcontroller | Raspberry Pi 4 Model B (4GB) | $55.00 | Target board for this guide. Pi 5 works but requires active cooling. |
| RFID Reader | MFRC522 SPI Module (3.3V) | $4.50 | Must be the SPI version with 8 pins. Do NOT buy the I2C or UART variants. |
| RFID Tags | MIFARE Classic 1K (13.56 MHz) | $0.50 / ea | Standard blue fobs or white cards. Will NOT work with 125 kHz EM4100 tags. |
| Wiring | Female-to-Female Dupont Jumpers | $3.00 | Use short runs (under 10cm) to prevent SPI clock signal degradation. |
| Resistor | 10k Ohm (0.25W) | $0.10 | Optional but recommended pull-up for the SPI CE0 line if using long wires. |
Wiring the Raspberry Pi with RFID (SPI Pinout)
The MFRC522 uses the Serial Peripheral Interface (SPI) protocol. Unlike I2C, SPI requires separate lines for data in (MOSI) and data out (MISO), plus a clock (SCK) and a chip select (CE0). For a deep dive into the physical pin layout, always cross-reference Pinout.xyz to avoid counting pins backward from the wrong corner.
Step 1: Enable the SPI Interface
- Open the terminal on your Pi and type:
sudo raspi-config - Navigate to Interface Options > SPI.
- Select Yes to enable the SPI kernel module.
- Reboot the Pi:
sudo reboot
Step 2: Physical Pin Mapping
Wire the MFRC522 module to the Pi's GPIO header exactly as shown below. The RC522 pin labels vary slightly by manufacturer (sometimes SDA is labeled SS or SDA), but the 8-pin layout is standard.
| MFRC522 Pin | Pi GPIO (BCM) | Physical Pin | Function |
|---|---|---|---|
| SDA (SS) | GPIO 8 | Pin 24 | SPI Chip Enable 0 (CE0) |
| SCK | GPIO 11 | Pin 23 | SPI Clock |
| MOSI | GPIO 10 | Pin 19 | Master Out Slave In |
| MISO | GPIO 9 | Pin 21 | Master In Slave Out |
| IRQ | None | None | Interrupt (Leave disconnected for polling) |
| GND | Ground | Pin 6 | Common Ground |
| RST | GPIO 25 | Pin 22 | Reset / Power Down |
| 3.3V | 3V3 Power | Pin 1 | 3.3V Power Supply |
Python Code: Reading MIFARE Tags
We will use the pi-rc522 Python library, which is actively maintained for Python 3 and handles the low-level SPI register writes natively. Install it via pip:
pip install pi-rc522
The following script initializes the SPI bus, polls for a tag, extracts the UID, and formats it as a hexadecimal string. It includes a try/finally block to ensure the SPI bus is cleanly released when you press Ctrl+C, preventing the 'device busy' lock on subsequent runs.
import time
import sys
from pirc522 import RFID
# Initialize the RFID reader
# The library defaults to spidev0.0 (CE0) and GPIO 25 for RST
rdr = RFID()
util = rdr.util()
util.debug = False
print('Raspberry Pi RFID Scanner Active. Press Ctrl+C to exit.')
try:
while True:
# Wait for a tag to be presented to the antenna
rdr.wait_for_tag()
# Attempt to request the tag
(error, data) = rdr.request()
if not error:
# Anti-collision to get the UID
(error, uid) = rdr.anticoll()
if not error:
# UID is returned as a list of integers (e.g., [192, 45, 12, 88, 55])
# We slice the first 4 bytes for standard MIFARE Classic tags
uid_hex = ':'.join(f'{x:02X}' for x in uid[:4])
print(f'[{time.strftime("%H:%M:%S")}] Tag Detected | UID: {uid_hex}')
# Brief debounce delay to prevent reading the same tag 10x a second
time.sleep(1.5)
except KeyboardInterrupt:
print('\nKeyboard interrupt received. Cleaning up GPIO and SPI bus...')
finally:
# Crucial: This resets the RC522 and releases the SPI file handle
rdr.cleanup()
sys.exit(0)
Debugging: 'Can't read tag' and SPI Errors
Embedded hardware rarely works on the first compile. If your script fails, here are the first three things to check before rewriting code: 1) Verify SPI is enabled in raspi-config and ls /dev/spi* shows spidev0.0. 2) Check that MISO and MOSI are not swapped at the breadboard. 3) Ensure your tag is actually 13.56 MHz (hold it to a smartphone; if the phone's NFC wakes up, it's 13.56 MHz).
Error 1: The SPI Device Missing Error
Exact Error String: OSError: [Errno 2] No such file or directory: '/dev/spidev0.0'
Ranked Causes & Fixes:
- SPI is disabled in the OS. Run
sudo raspi-config, enable SPI, and reboot. The kernel modulespi-bcm2835must be loaded. - SPI is blacklisted. Check
/etc/modprobe.d/raspi-blacklist.conf. Ifblacklist spi-bcm2835is present, comment it out with a#.
Error 2: The Permission Denied Error
Exact Error String: PermissionError: [Errno 13] Permission denied: '/dev/spidev0.0'
Ranked Causes & Fixes:
- Running without elevated privileges. Run the script with
sudo python3 rfid_scan.py. - User not in the SPI group. If you want to run without sudo, add your user to the spi group:
sudo usermod -a -G spi $USER, then log out and log back in.
Error 3: Tag Reads as '00:00:00:00' or Fails to Detect
Exact Error String: Script runs, but outputs Tag Detected | UID: 00:00:00:00 or hangs on wait_for_tag().
Ranked Causes & Fixes:
- Frequency Mismatch: You are using a 125 kHz RFID fob (like an HID or EM4100) instead of a 13.56 MHz MIFARE tag. The RC522 physically cannot generate the 125 kHz magnetic field. Buy MIFARE Classic 1K tags.
- MISO Line Floating: The Pi is reading garbage data. Check the physical connection on Pin 21 (MISO). Use a multimeter to verify continuity from the Pi header to the RC522 module pin.
- RST Pin Not Toggling: The
pi-rc522library uses GPIO 25 to hard-reset the chip on initialization. If Pin 22 (RST) is loose, the chip remains in power-down mode. Solder the header pins properly; breadboard Dupont wires often fail on the RST pin due to insertion angle.
Extending and Simplifying the Build
How to Simplify: If you are already using the SPI bus for an ILI9341 TFT display or an MCP3008 ADC, you will run into CE0/CE1 pin conflicts or bus speed mismatches. To simplify the wiring, switch to an PN532 NFC module configured for I2C. The PN532 uses only two data pins (SDA/SCL), shares the bus safely with other I2C devices, and supports a wider range of NFC tags, including NTAG215 (used for Amiibos).
How to Extend: To turn this into a physical access controller, add a 5V relay module to trigger an electric strike. Wire the relay IN pin to GPIO 23. When the Python script matches a known UID against a local SQLite database or an MQTT broker, pulse GPIO 23 HIGH for 3 seconds. For enterprise-grade logging, integrate the paho-mqtt library to publish the UID and timestamp to a Home Assistant MQTT broker, allowing you to track entry events via a centralized dashboard.
Frequently Asked Questions
Can I use a 125 kHz EM4100 fob with the Raspberry Pi with RFID RC522?
No. The MFRC522 chip is an NFC transponder specifically tuned to the 13.56 MHz ISM band. 125 kHz tags (often used in older apartment fobs and HID proximity cards) rely on a completely different magnetic induction frequency and protocol. If your existing building uses 125 kHz fobs, you must buy a separate RDM6300 or Seeed Studio 125kHz RFID module, which connects via UART (TX/RX pins) rather than SPI.
How do I read the actual data blocks on a MIFARE Classic tag instead of just the UID?
Reading the UID only requires the tag to be in the RF field. Reading the memory blocks requires authentication. The pi-rc522 library includes a util helper for this. You must select the tag, authenticate to the specific sector using a 6-byte key (factory default is usually FF FF FF FF FF FF), and then call rdr.read(block_address). Be extremely careful when writing to Sector 0, Block 0, as this contains the UID and manufacturer data; corrupting it can permanently brick the tag.
Is the Raspberry Pi with RFID setup secure enough for a front door access control system?
For a shed, a garage, or a maker-space tool lockout, yes. For a high-security front door or server room, no. The MIFARE Classic 1K encryption (Crypto-1) was cryptographically broken in 2008. Attackers can use a Proxmark3 device to clone your fob's UID and memory blocks in under 60 seconds. For high-security applications, the NXP MIFARE DESFire EV3 standard is required, which uses AES-128 encryption. If you must use the RC522 for security, configure your system to rely purely on the UID and pair it with a secondary factor, like a PIN code entered on a matrix keypad.






