El módulo PN532 es uno de los chips NFC más versátiles del mercado, permitiendo la comunicación a través de interfaces UART, I2C y SPI. En entornos de desarrollo con Raspberry Pi, su integración mediante un HAT facilita la lectura de tarjetas Mifare Classic y otros dispositivos compatibles.
Comunicación básica mediante interfaz UART
Para establecer una conexión directa por puerto serie, es fundamental identificar el dispositivo correcto en el sistema. El siguiente script automatiza la búsqueda del puerto activo y extrae el identificador único (UID) de la tarjeta detectada.
import serial
import time
import os
# Configuración de parámetros de comunicación
PUERTOS_SERIALES = ['/dev/ttyS0', '/dev/ttyAMA0', '/dev/ttyUSB0']
VELOCIDAD_BAUDIOS = 115200
def inicializar_conexion_uart():
for ruta in PUERTOS_SERIALES:
if os.path.exists(ruta):
try:
conexion = serial.Serial(ruta, VELOCIDAD_BAUDIOS, timeout=1)
if conexion.is_open:
print(f"Enlace establecido en: {ruta}")
return conexion
except Exception as error:
print(f"Error al abrir {ruta}: {error}")
return None
# Comandos en formato hexadecimal para el PN532
BUFFER_DESPERTAR = bytearray([0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, 0xFD, 0xD4, 0x14, 0x01, 0x17, 0x00])
BUFFER_BUSQUEDA = bytearray([0x00, 0x00, 0xFF, 0x04, 0xFC, 0xD4, 0x4A, 0x01, 0x00, 0xE1, 0x00])
def ejecutar_comando(serial_obj, comando):
serial_obj.write(comando)
time.sleep(0.15)
return serial_obj.read(serial_obj.in_waiting)
def procesar_lectura_nfc(serial_obj):
# Intentar despertar el chip
ejecutar_comando(serial_obj, BUFFER_DESPERTAR)
# Solicitar detección de tarjeta
trama_respuesta = ejecutar_comando(serial_obj, BUFFER_BUSQUEDA)
if trama_respuesta and len(trama_respuesta) > 12:
# El UID suele encontrarse a partir del byte 8 en la trama de respuesta
id_tarjeta = trama_respuesta[8:14]
return id_tarjeta
return None
if __name__ == "__main__":
nfc_serial = inicializar_conexion_uart()
if nfc_serial:
try:
while True:
uid_detectado = procesar_lectura_nfc(nfc_serial)
if uid_detectado:
print(f"Tarjeta detectada. UID: {[hex(b) for b in uid_detectado]}")
else:
print("Buscando tarjeta...")
time.sleep(2)
except KeyboardInterrupt:
nfc_serial.close()
Arquitectura Multi-interfaz con Flask
Para aplicaciones más complejas, es útil disponer de un backend que gestione dinámicamente el protocolo de comunicación (UART, I2C o SPI) y exponga los datos mediante una API web.
from flask import Flask, render_template, jsonify, request
import time
import serial
import smbus
import spidev
import os
app = Flask(__name__)
# Configuración de hardware
BUS_I2C = 1
ADDR_PN532 = 0x24
SPI_CHANNEL = 0
SPI_DEVICE = 0
class GestorNFC:
def __init__(self):
self.driver = None
self.tipo = None
def conectar(self, modo):
self.tipo = modo
try:
if modo == 'uart':
self.driver = serial.Serial('/dev/ttyS0', 115200, timeout=0.5)
elif modo == 'i2c':
self.driver = smbus.SMBus(BUS_I2C)
elif modo == 'spi':
self.driver = spidev.SpiDev()
self.driver.open(SPI_CHANNEL, SPI_DEVICE)
self.driver.max_speed_hz = 1000000
return True
except Exception as e:
print(f"Fallo en conexión {modo}: {e}")
return False
def leer_uid(self):
cmd_scan = bytearray([0x00, 0x00, 0xFF, 0x04, 0xFC, 0xD4, 0x4A, 0x01, 0x00, 0xE1, 0x00])
if self.tipo == 'uart':
self.driver.write(cmd_scan)
time.sleep(0.1)
res = self.driver.read(20)
# Lógica simplificada para demostración de tramas
if res and len(res) > 10:
return [hex(x) for x in res[8:12]]
return None
nfc_manager = GestorNFC()
@app.route('/')
def home():
return render_template('index.html')
@app.route('/api/scan', methods=['GET'])
def api_scan():
protocolo = request.args.get('proto', 'uart')
if nfc_manager.conectar(protocolo):
uid = nfc_manager.leer_uid()
if uid:
return jsonify({"status": "success", "uid": uid})
return jsonify({"status": "no_card"}), 404
return jsonify({"status": "error", "message": "Hardware no disponible"}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
Interfaz de Usuario (index.html)
A continuación se presenta una estructura HTML minimalista para interactuar con el servidor Flask y visualizar los resultados del escaneo en tiempo real.
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Panel de Control NFC</title>
<style>
.contenedor { max-width: 500px; margin: 40px auto; text-align: center; font-family: sans-serif; }
.resultado { margin-top: 20px; padding: 15px; border: 1px solid #ccc; background: #f9f9f9; }
</style>
</head>
<body>
<div class="contenedor">
<h1>Escáner PN532</h1>
<select id="protocolo">
<option value="uart">UART</option>
<option value="i2c">I2C</option>
<option value="spi">SPI</option>
</select>
<button onclick="escanearTarjeta()">Leer UID</button>
<div class="resultado" id="visor-uid">Esperando lectura...</div>
</div>
<script>
function escanearTarjeta() {
const proto = document.getElementById('protocolo').value;
const visor = document.getElementById('visor-uid');
visor.innerText = "Escaneando...";
fetch(`/api/scan?proto=${proto}`)
.then(res => res.json())
.then(data => {
if (data.status === "success") {
visor.innerText = "UID Detectado: " + data.uid.join(' ');
} else {
visor.innerText = "Error: " + (data.message || "No se detectó tarjeta");
}
})
.catch(err => visor.innerText = "Fallo en el servidor");
}
</script>
</body>
</html>