NFC with Arduino and Raspberry Pi
Embedded NFC opens up projects that smartphones cannot: fixed-installation readers, kiosk systems, industrial automation, and custom hardware. The PN532 and RC522 are the two most common breakout modules for hobbyist and prototyping use.
Hardware Overview
| Module | Chip | Protocol | Interface | Price | Supported Tag Types |
|---|---|---|---|---|---|
| PN532 | NXP PN532 | iso-14443 A/B, FeliCa, ISO 18092 | I²C, SPI, UART | $5–$12 | Types 1–4, MIFARE |
| RC522 | NXP MFRC522 | ISO 14443ISO 14443Standard for contactless smart cards at 13.56 MHz (Types A and B)View full →-A only | SPI | $2–$5 | Type 2 (NTAG/MIFARE) |
| ST25R3911B | STMicro | ISO 14443, 15693, 18000-3 | SPI | $15–$30 | Types 1–5 |
The PN532 is the recommended choice: it supports all four major nfc-a and nfc-b tag types, has mature libraries for both Arduino and Raspberry Pi, and handles the full iso-14443 T=CL protocol needed for DESFire communication. The RC522 is cheaper but limited to ISO 14443-A and lacks ISO 14443-4 support.
Arduino: PN532 Wiring (I²C)
PN532 VCC → Arduino 5V (or 3.3V — check your module)
PN532 GND → Arduino GND
PN532 SDA → Arduino SDA (A4 on Uno, 20 on Mega)
PN532 SCL → Arduino SCL (A5 on Uno, 21 on Mega)
PN532 IRQ → Arduino D2 (interrupt pin)
PN532 RST → Arduino D3 (optional reset)
Set the DIP switches on the PN532 breakout to 1=ON, 2=OFF for I²C mode.
Arduino: Reading a Tag UID
#include <Wire.h>
#include <Adafruit_PN532.h>
#define PN532_IRQ 2
#define PN532_RST 3
Adafruit_PN532 nfc(PN532_IRQ, PN532_RST);
void setup() {
Serial.begin(115200);
nfc.begin();
uint32_t versiondata = nfc.getFirmwareVersion();
if (!versiondata) { Serial.println("PN532 not found"); while(1); }
nfc.SAMConfig(); // configure the SAM (Security Access Module)
}
void loop() {
uint8_t uid[7];
uint8_t uidLength;
if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 500)) {
Serial.print("UID: ");
for (uint8_t i = 0; i < uidLength; i++) {
Serial.print(uid[i], HEX); Serial.print(" ");
}
Serial.println();
}
}
The uid is printed as hex. For NTAG213 tags, uidLength is 7 bytes. For older MIFARE Classic 1K, it is 4 bytes.
Raspberry Pi: PN532 via SPI (Python)
import board
import busio
import adafruit_pn532.spi as pn532_spi
import digitalio
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
cs = digitalio.DigitalInOut(board.D4)
pn532 = pn532_spi.PN532_SPI(spi, cs, debug=False)
pn532.SAM_configuration()
print("Waiting for NFC card...")
while True:
uid = pn532.read_passive_target(timeout=0.5)
if uid:
print("Found card with UID:", [hex(i) for i in uid])
Install dependencies: pip install adafruit-circuitpython-pn532
RC522 with Arduino
The RC522 uses SPI only and works exclusively with ISO 14443-A (nfc-a) tags. The MFRC522 library is the standard:
#include <SPI.h>
#include <MFRC522.h>
#define RST_PIN 9
#define SS_PIN 10
MFRC522 mfrc522(SS_PIN, RST_PIN);
void setup() {
SPI.begin();
mfrc522.PCD_Init();
}
void loop() {
if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i], HEX);
}
Serial.println();
mfrc522.PICC_HaltA();
}
}
Choosing Between PN532 and RC522
| Need | Recommendation |
|---|---|
| Read NTAG tags only | RC522 (cheaper) |
| Read + write NDEF | PN532 (better library support) |
| DESFire / ISO 14443-4 | PN532 only |
| Raspberry Pi | PN532 (SPI or I²C) |
| Battery-powered / low cost | RC522 |
The nfc-controller chip in both modules handles the RF protocol layer — analog RF generation, anti-collision, and framing — so your microcontroller only needs to exchange commands over SPI/I²C.
Use the NFC Compatibility Checker to verify that your chosen module supports the tag type you plan to read.
See also: NFC Chip Comparison Guide and How to Read and Write NFC Tags.