The RC522 is the cheapest way to read RFID cards and key fobs, and it is a 3.3 V part — so on a Pico W every wire goes straight to a GPIO. It talks SPI, here on SPI0 (GP16–GP19) with RST on GP20.
MicroPython has no built-in RC522 driver, so the sketch includes a compact one. Tap a card and the script prints its UID; add your UID to the allowed list to try the access-control demo.
Wiring
| Raspberry Pi Pico W | RC522 RFID reader (MFRC522, 13.56 MHz) | Wire | Notes |
|---|---|---|---|
| GP17 | SDA | orange | Chip select (SPI0 CSn), physical pin 22. “SDA” is just the RC522’s name for SS. |
| GP18 | SCK | yellow | SPI0 SCK, physical pin 24. |
| GP19 | MOSI | green | SPI0 TX, physical pin 25. |
| GP16 | MISO | blue | SPI0 RX, physical pin 21. |
| GND | GND | black | |
| GP20 | RST | purple | Physical pin 26. |
| 3V3 | 3.3V | red | 3V3 is pin 36. Never 5 V. |
3.3 V only part, 3.3 V board. Power the RC522 from 3.3 V — never 5 V. The MFRC522 chip’s pins are not rated for 5 V either: many people wire it straight to an Uno and it works for a while, but for a build that lasts, drop the Uno’s SCK, MOSI, SDA (SS) and RST signals to 3.3 V with a level shifter or dividers. ESP32 and Pico connect directly.
Board quirks
- The RC522 is 3.3 V only — a perfect match for the Pico. Every pin connects directly; leave IRQ unconnected.
- Copy
mfrc522.py(a 120-line driver written for FlashReady from the NXP datasheet, MIT) to the board. It reads the 4-byte UID of MIFARE Classic cards and fobs — what door locks and attendance systems use. - Put your card’s UID (printed when you tap it) into
ALLOWEDto see the “access granted” demo light the on-board LED. - A UID is not a secret: cheap writable cards can copy it. Fine for a hobby attendance register, not for a real door lock on its own.
Code
# RC522 RFID reader on a Raspberry Pi Pico W (MicroPython): print the UID of each card or key fob.
# Wiring (SPI0): 3.3V -> 3V3 (pin 36), GND -> GND, SCK -> GP18 (pin 24), MOSI -> GP19 (pin 25),
# MISO -> GP16 (pin 21), SDA (chip select) -> GP17 (pin 22), RST -> GP20 (pin 26). IRQ unconnected.
# Copy mfrc522.py to the board too.
from machine import Pin, SPI
import time
from mfrc522 import MFRC522, OK
ALLOWED = ["A1B2C3D4"] # put your own card's UID here for the "access granted" demo
spi = SPI(0, baudrate=1_000_000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(19), miso=Pin(16))
reader = MFRC522(spi, cs=Pin(17), rst=Pin(20))
led = Pin("LED", Pin.OUT)
v = reader.version()
if v in (0x00, 0xFF):
raise SystemExit("RC522 not answering (version 0x%02X) - check SPI wiring and 3.3 V" % v)
print("RC522 ready, version 0x%02X - hold a card near the reader" % v)
last_uid = None
last_seen = 0
while True:
if reader.request() == OK:
stat, uid = reader.uid()
if stat == OK:
tag = "".join("{:02X}".format(b) for b in uid)
now = time.ticks_ms()
if tag != last_uid or time.ticks_diff(now, last_seen) > 2000:
ok = tag in ALLOWED
print("Card UID {} -> {}".format(tag, "access granted" if ok else "unknown card"))
led.value(ok)
last_uid = tag
last_seen = now
elif last_uid and time.ticks_diff(time.ticks_ms(), last_seen) > 1000:
led.off()
last_uid = None
time.sleep_ms(50)# RC522 RFID reader on a Raspberry Pi Pico W (MicroPython): print the UID of each card or key fob.
# Wiring (SPI0): 3.3V -> 3V3 (pin 36), GND -> GND, SCK -> GP18 (pin 24), MOSI -> GP19 (pin 25),
# MISO -> GP16 (pin 21), SDA (chip select) -> GP17 (pin 22), RST -> GP20 (pin 26). IRQ unconnected.
# Copy mfrc522.py to the board too.
from machine import Pin, SPI
import time
from mfrc522 import MFRC522, OK
ALLOWED = ["A1B2C3D4"] # put your own card's UID here for the "access granted" demo
spi = SPI(0, baudrate=1_000_000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(19), miso=Pin(16))
reader = MFRC522(spi, cs=Pin(17), rst=Pin(20))
led = Pin("LED", Pin.OUT)
v = reader.version()
if v in (0x00, 0xFF):
raise SystemExit("RC522 not answering (version 0x%02X) - check SPI wiring and 3.3 V" % v)
print("RC522 ready, version 0x%02X - hold a card near the reader" % v)
last_uid = None
last_seen = 0
while True:
if reader.request() == OK:
stat, uid = reader.uid()
if stat == OK:
tag = "".join("{:02X}".format(b) for b in uid)
now = time.ticks_ms()
if tag != last_uid or time.ticks_diff(now, last_seen) > 2000:
ok = tag in ALLOWED
print("Card UID {} -> {}".format(tag, "access granted" if ok else "unknown card"))
led.value(ok)
last_uid = tag
last_seen = now
elif last_uid and time.ticks_diff(time.ticks_ms(), last_seen) > 1000:
led.off()
last_uid = None
time.sleep_ms(50)Expected serial output
RC522 ready, version 0x92 - hold a card near the reader
Card UID 73A1C4E9 -> unknown card
Card UID A1B2C3D4 -> access grantedRC522 ready, version 0x92 - hold a card near the reader Card UID 73A1C4E9 -> unknown card Card UID A1B2C3D4 -> access granted
Common errors
RC522 not answering (version 0x00)
SPI wiring: SCK GP18, MOSI GP19, MISO GP16, SDA GP17. Also check 3.3 V and that RST is on GP20.
Version reads 0x12 or 0x88
A clone chip — usually still works. If cards are never seen, hold them flat and close (under 3 cm).
Phone NFC or metro card not detected
The RC522 reads ISO 14443A (MIFARE). Many transit and bank cards are other types or encrypted; phones emulate cards only in payment apps.