Un lector NFC conectado a una Raspberry Pi abre la puerta a proyectos bien concretos: registrar quién entra al taller, armar un reloj control para un club o lanzar una acción cuando alguien apoya una tarjeta. El problema es que buena parte de las guías del PN532 que circulan son de antes de la Raspberry Pi 5 y usan librerías que en ella ya no funcionan.
Esta guía usa el camino que sí funciona hoy: el módulo PN532 V3 (la placa roja con interruptores DIP) conectado por I²C con solo cuatro cables, y la librería CircuitPython de Adafruit sobre Blinka. Al final vas a saber detectar el módulo en el bus, leer el UID de tarjetas y llaveros, escribir tus propios datos en una tarjeta MIFARE Classic y copiarlos a otra, y vas a entender por qué esa copia no sirve para clonar una tarjeta de acceso ajena.
Por qué el PN532 y no el RC522
El RC522 es el lector NFC más barato y conocido, pero tiene límites claros. Comparados lado a lado:
| RC522 | PN532 | |
|---|---|---|
| Tarjetas | ISO 14443A (MIFARE, NTAG) | ISO 14443A, otras ISO 14443-4 y FeliCa |
| Emular una tarjeta | No | Sí |
| Interfaz | Solo SPI | I²C, SPI o UART (por interruptor DIP) |
| Cables en la conexión más simple | 7 | 4 (I²C) |
| Librería en la Pi 5 | Una librería aparte | Adafruit CircuitPython PN532 |
Si solo quieres leer el número de una tarjeta MIFARE y ya tienes un RC522, con eso basta. El PN532 conviene cuando quieres ahorrar cables, compartir el bus I²C con otros sensores o trabajar más adelante con otros tipos de tarjeta.
Vale la pena entender qué pasa cuando apoyas una tarjeta: el lector genera un campo magnético de 13,56 MHz y la bobina de la tarjeta, que no tiene batería, toma energía de ese campo para encender su chip. Por eso la distancia de lectura es de pocos centímetros y por eso una tarjeta metálica o un celular entre medio la bloquean. La tarjeta responde modulando el mismo campo, y lo primero que entrega es su UID, un número de serie grabado de fábrica.
Materiales
- Una Raspberry Pi 5 con Raspberry Pi OS al día (con una Pi 4 debería funcionar igual, pero la guía se probó en la Pi 5).
- Un módulo PN532 NFC V3. La mayoría de los kits trae una tarjeta MIFARE blanca y un llavero.
- Cuatro cables hembra hembra.

Hardware: interruptores y cableado
Poner el módulo en modo I²C
Los dos interruptores pequeños al lado del conector de cuatro pines eligen la interfaz. La placa trae impresa la tabla:
| Modo | Interruptor 1 | Interruptor 2 |
|---|---|---|
| HSU (UART) | 0 | 0 |
| I²C | 1 | 0 |
| SPI | 0 | 1 |
La posición hacia "ON" es 1. De fábrica ambos suelen venir en 0, así que para I²C sube el interruptor 1 a ON y deja el 2 donde está. Hazlo con el módulo sin alimentación: el chip lee los interruptores solo al encender, así que si los mueves con el módulo energizado no va a cambiar de modo hasta que le cortes la corriente.

La librería de Adafruit también trae clases para SPI y UART (PN532_SPI y PN532_UART), pero esta guía solo cubre I²C, que es el camino probado en la Pi 5.
Conexión a la Raspberry Pi 5
Usa el conector de cuatro pines rotulado GND, VCC, SDA y SCL. El de ocho pines del lado es para SPI y queda libre. Apaga la Raspberry Pi antes de cablear.
| PN532 | Raspberry Pi 5 |
|---|---|
| VCC | Pin 1 (3,3 V) |
| SDA | Pin 3 (GPIO 2) |
| SCL | Pin 5 (GPIO 3) |
| GND | Pin 6 (GND) |

Conecta VCC a 3,3 V y no a 5 V. El módulo acepta ambos, pero las líneas SDA y SCL quedan al voltaje de alimentación del módulo, y los GPIO de la Raspberry Pi no toleran 5 V. Alimentado a 3,3 V, módulo y Raspberry Pi trabajan al mismo nivel y no necesitas conversor de nivel lógico.
Si ya tienes un HAT puesto
Si tu HAT deja pasar los pines del conector GPIO hacia arriba, puedes conectar el PN532 encima. Revisa tres cosas:
- El bus I²C se comparte: varios dispositivos pueden colgar de las mismas dos líneas mientras cada uno tenga su dirección. El PN532 responde en 0x24; revisa con
i2cdetectque esa dirección esté libre. Si el PN532 se cuelga, bloquea también a los demás dispositivos del bus (la solución está en "Si algo no funciona"). - Nada de SPI si el HAT usa SPI: no afecta al camino I²C de esta guía, pero si conectaras el PN532 por SPI con un HAT que ya ocupa SPI0, la librería manejaría también su línea de selección y lo interferiría.
- Los pines del ejemplo oficial: el ejemplo I²C de la librería de Adafruit usa además GPIO 6 y GPIO 12 para reset y para despertar el módulo. Los scripts de esta guía no los necesitan.

Software: I²C, librería y detección
Activar I²C
Enciende la Raspberry Pi y abre una terminal, directo o por SSH. Activa I²C con:
sudo raspi-config
Entra a 3 Interface Options, luego I5 I2C, confirma con "Yes" y sal con "Finish". Después instala los paquetes que vas a necesitar:
sudo apt update
sudo apt install -y i2c-tools python3-dev python3-lgpio gcc
Encontrar el módulo en el bus
sudo i2cdetect -y 1
En la tabla tiene que aparecer 24. En la prueba del autor aparecen además 3c y 48, que son una pantalla pequeña del gabinete y un chip del HAT de radio; si no tienes nada más conectado, vas a ver solo el 24.
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- 24 -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
Si no aparece el 24, salta directo a "Si algo no funciona".
Instalar la librería en un entorno virtual
La librería CircuitPython PN532 de Adafruit corre sobre Blinka, una capa que permite ejecutar código de CircuitPython en la Raspberry Pi. Raspberry Pi OS ya no deja instalar paquetes de pip directo en el sistema, así que vas a crear un entorno virtual, una carpeta propia con los paquetes de este proyecto. La opción --system-site-packages le permite usar el python3-lgpio que instalaste con apt; sin ella, pip tendría que compilar esa librería de GPIO desde cero.
python3 -m venv --system-site-packages ~/nfc-env
source ~/nfc-env/bin/activate
pip install adafruit-circuitpython-pn532 adafruit-blinka
La instalación se demora un poco porque pip compila dos paquetes pequeños para la Raspberry Pi. Al final aparece una línea como esta (resumida):
Successfully installed ... adafruit-blinka-9.2.0 ... adafruit-circuitpython-pn532-2.4.9 ...
Esas son las versiones de octubre de 2026, con Raspberry Pi OS y Python 3.13. Recuerda que cada vez que abras una terminal nueva tienes que ejecutar source ~/nfc-env/bin/activate antes de correr los scripts.
Leer tarjetas NFC
El primer script le pide al PN532 su versión de firmware al partir: si esa línea aparece, Python ya está conversando con el módulo. Después queda esperando tarjetas y muestra su UID. Crea el archivo:
nano nfc_read.py
Pega este código, guarda con Ctrl + O y sal de nano con Ctrl + X:
import signal
import board
import busio
from adafruit_pn532.i2c import PN532_I2C
running = True
def stop(signum, frame):
# finish the current read first, so the PN532 is not left mid-command
global running
running = False
signal.signal(signal.SIGINT, stop)
i2c = busio.I2C(board.SCL, board.SDA)
pn532 = PN532_I2C(i2c, debug=False)
ic, ver, rev, support = pn532.firmware_version
print(f"Found PN532 with firmware version {ver}.{rev}")
pn532.SAM_configuration()
print("Waiting for a card ... (stop with Ctrl+C)")
last_uid = None
while running:
uid = pn532.read_passive_target(timeout=0.5)
if uid is not None and uid != last_uid:
print(f"UID ({len(uid)} bytes): {uid.hex(':')}")
last_uid = uid
print("Stopped.")
Ejecútalo y apoya la tarjeta y el llavero, uno después del otro:
python nfc_read.py
Found PN532 with firmware version 1.6
Waiting for a card ... (stop with Ctrl+C)
UID (4 bytes): a1:24:04:5e
UID (4 bytes): cd:2d:8c:8e
Stopped.
Para detenerlo usa Ctrl + C. Fíjate en la función stop: en vez de cortar el programa de golpe, cambia la variable running y deja que termine la lectura en curso. Parece un detalle, pero es lo que evita el problema más común del PN532 por I²C: si cortas el script a mitad de una transacción, el módulo queda esperando el resto del mensaje y en el siguiente arranque no responde.
El largo del UID te dice más o menos qué tipo de tarjeta tienes: 4 bytes son MIFARE Classic, como las de los kits; 7 bytes son stickers NTAG y MIFARE Ultralight.
Escribir tus datos en una tarjeta MIFARE
Una tarjeta MIFARE Classic 1K guarda 1 KB repartido en 16 sectores de cuatro bloques de 16 bytes cada uno. Hay dos tipos de bloque que no se tocan:
- El bloque 0 tiene el UID y los datos del fabricante. En tarjetas normales es de solo lectura.
- El último bloque de cada sector (3, 7, 11 y así) guarda las claves y los permisos de acceso. Si escribes cualquier cosa ahí, puedes dejar el sector bloqueado para siempre.
Los demás bloques son para tus datos. Antes de leer o escribir un bloque, el script tiene que autenticarse con una clave. Las tarjetas nuevas traen de fábrica la clave FF FF FF FF FF FF, que fue la que funcionó con las del kit.
¿Por qué se autentica por bloque y no una vez por tarjeta? Porque en MIFARE Classic los permisos son por sector: cada sector puede tener sus propias claves A y B, y eso permite, por ejemplo, que un sector sea público y otro solo lo lea quien conoce la clave B. Ojo con una cosa: el cifrado de MIFARE Classic (Crypto-1) está roto desde 2008, así que estas claves sirven para ordenar tu proyecto, no para proteger algo valioso.
El siguiente script escribe un texto corto en el bloque 4, el primer bloque de datos del sector 1, y lo vuelve a leer. Lo que hubiera antes en el bloque 4 se sobrescribe; en una tarjeta nueva está vacío.
nano nfc_write.py
import board
import busio
from adafruit_pn532.adafruit_pn532 import MIFARE_CMD_AUTH_A
from adafruit_pn532.i2c import PN532_I2C
BLOCK = 4 # first data block of sector 1
KEY = b"\xff\xff\xff\xff\xff\xff" # factory default key A
TEXT = b"raspberry.tips" # max. 16 bytes per block
i2c = busio.I2C(board.SCL, board.SDA)
pn532 = PN532_I2C(i2c, debug=False)
pn532.SAM_configuration()
print("Place the card on the reader ...")
uid = None
while uid is None:
uid = pn532.read_passive_target(timeout=0.5)
print("UID:", uid.hex(":"))
if not pn532.mifare_classic_authenticate_block(uid, BLOCK, MIFARE_CMD_AUTH_A, KEY):
raise SystemExit("Authentication failed - the card does not use the default key")
pn532.mifare_classic_write_block(BLOCK, TEXT.ljust(16, b"\x00"))
data = pn532.mifare_classic_read_block(BLOCK)
print(f"Block {BLOCK}: {data.hex(' ')}")
print("Text:", data.rstrip(b"\x00").decode())
Apoya la tarjeta y ejecuta python nfc_write.py. Déjala apoyada hasta que aparezca el resultado y no cortes el script antes:
Place the card on the reader ...
UID: a1:24:04:5e
Block 4: 72 61 73 70 62 65 72 72 79 2e 74 69 70 73 00 00
Text: raspberry.tips
Puedes cambiar el texto como quieras, mientras no pase de 16 bytes. Las letras con tilde y la ñ ocupan dos bytes cada una en UTF-8, así que "Señal" usa 6 bytes y no 5. Para guardar algo más largo, reparte los datos entre los bloques 5 y 6 o en otros sectores.
Copiar datos entre tarjetas: qué se puede y qué no
Con el PN532 puedes pasar los datos de una tarjeta MIFARE tuya a otra, por ejemplo para tener una de respaldo en tu propio proyecto. Este script lee los tres bloques de datos del sector 1 de la primera tarjeta (bloques 4 a 6) y los escribe en la segunda. Las dos tienen que ser MIFARE Classic con la clave de fábrica, como la tarjeta y el llavero del kit. Para copiar más sectores, agrega sus bloques de datos en BLOCKS y salta siempre los bloques de claves (3, 7, 11, etc.).
nano nfc_copy.py
import board
import busio
from adafruit_pn532.adafruit_pn532 import MIFARE_CMD_AUTH_A
from adafruit_pn532.i2c import PN532_I2C
BLOCKS = (4, 5, 6) # data blocks of sector 1
KEY = b"\xff\xff\xff\xff\xff\xff" # factory default key A
i2c = busio.I2C(board.SCL, board.SDA)
pn532 = PN532_I2C(i2c, debug=False)
pn532.SAM_configuration()
def wait_for_card(other_than=None):
while True:
uid = pn532.read_passive_target(timeout=0.5)
if uid is not None and uid != other_than:
return uid
def authenticate(uid, block):
if not pn532.mifare_classic_authenticate_block(uid, block, MIFARE_CMD_AUTH_A, KEY):
raise SystemExit(f"Authentication failed for block {block}")
def read_blocks(uid):
result = ()
for block in BLOCKS:
authenticate(uid, block)
result += (bytes(pn532.mifare_classic_read_block(block)),)
return result
print("Place the SOURCE card on the reader ...")
source = wait_for_card()
data = read_blocks(source)
print("Source UID:", source.hex(":"))
print("Now swap to the TARGET card ...")
target = wait_for_card(other_than=source)
for block, block_data in zip(BLOCKS, data):
authenticate(target, block)
pn532.mifare_classic_write_block(block, block_data)
print("Target UID:", target.hex(":"))
print("Data identical:", read_blocks(target) == data)
Ejecútalo con python nfc_copy.py, apoya primero la tarjeta con tus datos y, cuando el script lo pida, cámbiala por la segunda. Cuidado: el script sobrescribe los bloques 4 a 6 de la segunda tarjeta. En la prueba se copió la tarjeta del paso anterior a un llavero:
Place the SOURCE card on the reader ...
Source UID: a1:24:04:5e
Now swap to the TARGET card ...
Target UID: 7e:f1:2a:07
Data identical: True
Los datos llegaron al llavero, pero el UID sigue siendo el suyo. Esa es la clave de todo el asunto: el UID vive en el bloque 0 y en tarjetas normales no se puede cambiar. Un lector que reconoce tarjetas por su UID (como la mayoría de los controles de acceso sencillos) ve la copia como una tarjeta distinta.
Lo que no se puede copiar así:
- Tarjetas con clave desconocida: si un sector no usa la clave de fábrica, la autenticación falla y el script no lee nada.
- Tarjetas cifradas: tarjetas bancarias, credenciales modernas de empresas y hoteles, o tarjetas MIFARE DESFire protegen sus datos con criptografía real.
- Tarjetas de 125 kHz: muchas tarjetas de acceso antiguas trabajan en otra frecuencia y el PN532 ni siquiera las detecta.
Y un límite claro: copia solo tarjetas que sean tuyas. Leer o duplicar tarjetas de acceso o de pago ajenas puede ser delito; en Chile, la Ley 21.459 de delitos informáticos sanciona el acceso ilícito a sistemas y la interceptación de datos.
Si algo no funciona
- "Did not receive expected ACK from PN532!" o el 24 desapareció: pasa cuando un script se cortó a mitad de una transmisión. En el peor caso el PN532 deja tomado todo el bus I²C y tampoco responden los demás dispositivos. Reiniciar la Raspberry Pi no sirve, porque el módulo sigue alimentado: desconecta un momento el cable VCC del PN532 o desenchufa la fuente de la Raspberry Pi.
- El 24 no aparece en
i2cdetect: revisa los interruptores (1 en ON, 2 en 0) y quítale la alimentación al módulo después de moverlos. Si el LED rojo PWR no enciende, el problema está en VCC o GND. Si SDA y SCL están invertidos, la tabla sale vacía. - Una tarjeta no se detecta: si tiene impreso un número del tipo
0009042350 137,63918, es una tarjeta de 125 kHz (EM4100). Se ve igual que una NFC, pero trabaja en otra frecuencia y ni el PN532 ni el RC522 la leen. - Faltan módulos o pip compila sin terminar: confirma que creaste el entorno con
--system-site-packagesy que antes instalastepython3-lgpiocon apt. No fuerces a pip con--only-binary: termina instalando una versión muy antigua de Blinka. - Las guías antiguas no funcionan: muchas usan
RPi.GPIO, que no es compatible con la Raspberry Pi 5. La librería de Adafruit corre sobre Blinka y por eso sí funciona. - Advertencias contra I²C: guías viejas desaconsejan I²C por problemas de clock stretching. En la Raspberry Pi 5 no aparecieron: diez minutos de lectura continua con una tarjeta apoyada dieron más de 14.000 lecturas sin un error.
Variantes y mejoras
- Control de acceso al taller con registro en CSV: en el script de lectura, reemplaza el
printpor una función que compare el UID con una lista de tarjetas autorizadas y agregue una línea con fecha, hora, UID y nombre a un archivoaccesos.csvusando el módulocsvde Python. Si además lo arrancas como servicio de systemd, el registro funciona apenas se enciende la Raspberry Pi, sin abrir una terminal. - Cerradura con relé: un módulo de relé en un GPIO libre puede activar una chapa eléctrica de 12 V durante dos o tres segundos cuando el UID está autorizado. Recuerda que el UID se puede falsificar con tarjetas especiales, así que para algo más serio que la puerta del taller conviene guardar además un dato secreto en un sector con clave propia.
- Lectura desde Home Assistant: si prefieres integrar el lector directo en tu casa domótica, el mismo PN532 funciona con una Raspberry Pi Pico W o un ESP32 corriendo ESPHome, que trae soporte nativo para este chip.
Personalización para Chile
En Chile consigues todo lo necesario en MechatronicStore:
- Raspberry Pi 5 8gb Ram (SKU N-351): la computadora que lee el PN532 y corre los scripts de Python.
- Módulo NFC RFID PN532 (SKU GQ2-8): el lector NFC de 13,56 MHz con interruptores para elegir I²C, SPI o UART.
- Tarjeta RFID NFC 13.56MHz para control de acceso (SKU A-310): tarjetas extra para registrar usuarios o como destino de la copia.
- Etiqueta NFC MIFARE 1K (10 unidades) (SKU GV1-4): stickers MIFARE 1K para pegar en herramientas o llaveros.
- Cables hembra hembra 40 piezas 10cm (SKU C-416): los cuatro cables entre el módulo y el conector GPIO.
Si ya tienes un Módulo RFID RC522 para 13.56 MHz (SKU GU2-17) y solo necesitas leer UID, también te sirve, aunque con siete cables por SPI y otra librería.
Recursos
- Tutorial original (alemán): PN532 am Raspberry Pi 5: NFC Karten lesen und beschreiben, de raspberry.tips (también en inglés).
- Repositorio GitHub: Adafruit_CircuitPython_PN532
- Documentación de la librería: CircuitPython PN532
Basado en la guía de raspberry.tips. Versión chilena con componentes en stock local.








