Leer, grabar, o emular tags NFC con Arduino y PN532
El PN532 es un chip NFC que podemos conectar a un
procesador como Arduino para leer y escribir tarjetas NFC, comunicarse
con móviles, o incluso actuar como tag NFC. Es un integrado ampliamente
utilizado en todo tipo de dispositivos comerciales que implementan NDC.
Recordar que el NFC, es un superset del RFID, que es un sistema de
comunicación inalámbrica. El NFC añade funciones de seguridad,
especialmente la limitación del rango de lectura de escritura al rango
de 50-100mm.
El NFC se ha popularizado como sistema de
comunicación debido a su mayor seguridad y a la posibilidad de actuar
como Peer-to-Peer, es decir, que un dispositivo NFC puede actuar tanto
como receptor. Esto ha propiciado que dispositivos como los smartphone
incorporen un chip NFC, tanto para leer tarjetas, como para actuar como
tag en servicios, por ejemplo, de autentificación o pago.
La comunicación es muy sencilla, ya que podemos comunicarnos a través
de SPI, I2C o HSU (High Speed UART). El PN532 opera 3.3V, pero dispone
de conversión de nivel por lo que es posible conectarlo con un
procesador de 5V.
Precio
El PN532 es un poco más caro que el
MIFARE RC522, pero también es un dispositivo muy superior a este. Aun
así, es un dispositivo asequible. Podemos encontrarlo por unos 3.5€ en
vendedores internacionales de AliExpress o Ebay.
¿Cómo funciona el PN532?
El PN532 incluye un
procesador 80C51 con 40 KB ROM y 1 KB RAM, junto con los elementos
necesarios para hacer funcionar correctamente la comunicación NFC. Los
módulos Maker disponibles incluyen una antena en la propia PCB.
Soporta 6 modos de operación:
- ISO/IEC 14443A/MIFARE Lector/Grabador.
- ISO/IEC 14443A/MIFARE Card MIFARE Classic 1K y MIFARE Classic 4K Card.
- ISO/IEC 14443B Lector/Grabador.
- FeliCa Lector/Grabador.
- FeliCa Card emulación.
- ISO/IEC 18092, ECMA 340 Peer-to-Peer
Las distancias típicas de actuación son de 50mm para lectura y
escritura, y 100mm para emulación. La velocidad de lectura es de hasta
212 kbits/s y la de escritura de hasta 424kbts/s. Estos valores
dependen, entre otros, de la antena integrada en el módulo. En los
módulos Maker el alcance típico es 30mm a 50mm.
El
PN532 opera a 3.3V pero admite tensiones de alimentación de 2.7V a
5.4V. Las líneas de comunicación I2C/UART funcionan a 3.3V a 24V TTL.
Sin embargo, el interface SPI funciona a 3.3, pero incorpora
resistencias de 100 Ohm en serie de forma que también puede ser
conectado a 5V.
El consumo es de 100mA en modo
Stand-By y 120mA durante la lectura y escritura. Adicionalmente dispone
de dos modos de baja energía, uno modo Soft-Power-Down con un consumo de
22uA, y un modo Hard-Power-Down con un consumo de 1uA.
El PN532 también puede ser usado con ordenadores y micro ordenadores como Rasperry Pi con la librería libnfc.
Formato NDEF (NFC Data Exchange Format)
El NFC Data Exchange Format (NDEF) es una forma estandarizada de
intercambiar información entre dispositivos NFC compatibles. El formato
NDEF es usado para almacenar información de intercambio, como por
ejemplo URIs, texto plano, etc.
Un mensaje NDEF
está formado por dos componentes, messages y records. Un mensaje está
formado por uno o varios records. Cada uno de los records consta de un
tipo de records, un ID y los propios datos en si (payload), junto con
los campos de longitud de cada uno de los anteriores.
Las tags NFC como las tarjetas Mifare Classic pueden ser configuradas
como tags NDEF. Los mensajes NDEF también pueden ser usados para
intercambiar datos entre dos dispositivos NFC activos (modo
Peer-to-Peer).
El estándar es mantenido por el NFC
Forum, y está disponible para su consulta de forma gratuita, previa
aceptación de las condiciones de licencia.
Esquema de montaje
EL PN532 puede conectarse tanto por I2C, por SPI o UART. El interface
de comunicación se elige mediante unos Jumper ubicados en la placa.
Conexión por I2C
La conexión por I2C sería la siguiente
Que vista desde Arduino quedaría así.
Conexión por SPI
Por su parte, la conexión mediante SPI es la siguiente
Que vista desde Arduino queda así,
Conexión por SPI
Finalmente, la conexión mediante HSU (High Speed UART) es la siguiente
Que vista desde Arduino quedaría así,
Los pines I2C y SPI indicados son válidos para los modelos de Arduino
Uno, Nano y Mini Pro. Para otros modelos de Arduino consultar el esquema patillaje correspondiente.
Ejemplos de código
Para programar el PN532 usaremos la librería desarrollada por Adafruit disponible en este enlace. Esta librería únicamente permite trabajar con comunicación I2C y SPI, pero no UART.
La librería proporciona ejemplos de código, que resulta aconsejable
revisar. Los siguientes ejemplos son modificaciones a partir de los
disponibles en la librería.
Mostrar datos y UID
En este ejemplo mostramos los datos del lector PN532, y de los tag que acerquemos.
#include <Wire.h>#include <SPI.h>#include <Adafruit_PN532.h>#define PN532_IRQ (2)#define PN532_RESET (3) // Not connected by default on the NFC ShieldAdafruit_PN532 nfc(PN532_IRQ, PN532_RESET);void setup(void) {Serial.begin(115200);nfc.begin();uint32_t versiondata = nfc.getFirmwareVersion();if (! versiondata) {Serial.print("PN53x no encontrado");while (1); // halt}// Mostrar datos del sensorSerial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX);Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC);Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);// Configurar para leer etiquetas RFIDnfc.setPassiveActivationRetries(0xFF);nfc.SAMConfig();Serial.println("Esperando tarjeta ISO14443A");}// Funcion auxiliar para mostrar el buffervoid printArray(byte *buffer, byte bufferSize) {for (byte i = 0; i < bufferSize; i++) {Serial.print(buffer[i] < 0x10 ? " 0" : " ");Serial.print(buffer[i], HEX);}}void loop(void) {boolean success;uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };uint8_t uidLength;success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength);if (success) {Serial.println("Tarjeta encontrada");Serial.print("UID Longitud: ");Serial.print(uidLength, DEC);Serial.println(" bytes");Serial.print("UID: "); printArray(uid, uidLength);Serial.println("");delay(1000);}else{Serial.println("Tarjeta no encontrada");}}
Grabar datos
En este ejemplo vemos cómo grabar datos en la memoria de un tag.
#include <Wire.h>#include <SPI.h>#include <Adafruit_PN532.h>#define PN532_IRQ (2)#define PN532_RESET (3)Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);void setup(void){Serial.begin(115200);// Configurar para leer etiquetas RFIDnfc.begin();nfc.setPassiveActivationRetries(0xFF);nfc.SAMConfig();Serial.println("Esperando tarjeta");}void loop(void){uint8_t success;uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };uint8_t uidLength;success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);if (success) {Serial.println("Intentando autentificar bloque 4 con clave KEYA");uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };success = nfc.mifareclassic_AuthenticateBlock(uid, uidLength, 4, 0, keya);if (success){Serial.println("Sector 1 (Bloques 4 a 7) autentificados");uint8_t data[16];memcpy(data, (const uint8_t[]){ 'l', 'u', 'i', 's', 'l', 'l', 'a', 'm', 'a', 's', '.', 'e', 's', 0, 0, 0 }, sizeof validData);success = nfc.mifareclassic_WriteDataBlock (4, data);if (success){Serial.println("Datos escritos en bloque 4");delay(10000);}else{Serial.println("Fallo al escribir tarjeta");delay(1000);}}else{Serial.println("Fallo autentificar tarjeta");delay(1000);}}}
Leer datos
En este ejemplo vemos cómo leer datos previamente grabados en la memoria de un tag.
#include <Wire.h>#include <SPI.h>#include <Adafruit_PN532.h>#define PN532_IRQ (2)#define PN532_RESET (3)Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);void setup(void){Serial.begin(115200);// Configurar para leer etiquetas RFIDnfc.begin();nfc.setPassiveActivationRetries(0xFF);nfc.SAMConfig();Serial.println("Esperando tarjeta");}void loop(void){uint8_t success;uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };uint8_t uidLength;success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);if (success){Serial.println("Intentando autentificar bloque 4 con clave KEYA");uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };success = nfc.mifareclassic_AuthenticateBlock(uid, uidLength, 4, 0, keya);if (success){Serial.println("Sector 1 (Bloques 4 a 7) autentificados");uint8_t data[16];success = nfc.mifareclassic_ReadDataBlock(4, data);if (success){Serial.println("Datos leidos de sector 4:");nfc.PrintHexChar(data, 16);Serial.println("");delay(5000);}else{Serial.println("Fallo al leer tarjeta");}}else{Serial.println("Fallo autentificar tarjeta");}}}
Comprobar UID
En este ejemplo validamos si una tarjeta es válida comprobando su UID.
#include <Wire.h>#include <SPI.h>#include <Adafruit_PN532.h>#define PN532_IRQ (2)#define PN532_RESET (3)Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);void setup(void) {Serial.begin(115200);// Configurar para leer etiquetas RFIDnfc.begin();nfc.setPassiveActivationRetries(0xFF);nfc.SAMConfig();Serial.println("Esperando tarjeta");}const uint8_t validUID[4] = { 0xC8, 0x3E, 0xE7, 0x59 }; // Ejemplo de UID valido//Función para comparar dos vectoresbool isEqualArray(uint8_t* arrayA, uint8_t* arrayB, uint8_t length){for (uint8_t index = 0; index < length; index++){if (arrayA[index] != arrayB[index]) return false;}return true;}void loop(void){uint8_t success;uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };uint8_t uidLength;success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);if (success){uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };if (isEqualArray(uid, validUID, uidLength)){Serial.println("Tarjeta valida");delay(5000);}else{Serial.println("Tarjeta invalida");delay(5000);}}}
Comprobar datos
En este ejemplo validamos si una tarjeta es válida comprobando datos que hemos grabado previamente.
#include <Wire.h>#include <SPI.h>#include <Adafruit_PN532.h>#define PN532_IRQ (2)#define PN532_RESET (3)Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);void setup(void) {Serial.begin(115200);// Configurar para leer etiquetas RFIDnfc.begin();nfc.setPassiveActivationRetries(0xFF);nfc.SAMConfig();Serial.println("Esperando tarjeta");}const uint8_t validData[16] = { 'l', 'u', 'i', 's', 'l', 'l', 'a', 'm', 'a', 's', '.', 'e', 's', 0, 0, 0 }; // Ejemplo de clave valida//Función para comparar dos vectoresbool isEqualArray(uint8_t* arrayA, uint8_t* arrayB, uint8_t length){for (uint8_t index = 0; index < length; index++){if (arrayA[index] != arrayB[index]) return false;}return true;}void loop(void) {uint8_t success;uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };uint8_t uidLength;success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);if (success){uint8_t keya[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };success = nfc.mifareclassic_AuthenticateBlock(uid, uidLength, 4, 0, keya);if (success){uint8_t data[16];success = nfc.mifareclassic_ReadDataBlock(4, data);if (success){if (isEqualArray(data, validData, sizeof validData)){Serial.println("Tarjeta valida");delay(5000);}else{Serial.println("Tarjeta invalida");delay(5000);}}}else{Serial.println("Fallo autentificar tarjeta");delay(5000);}}}
Emular un tag
Para emular un tag (NFC Type 4) podemos usar la librería de Seed-Studio disponible en este enlace. La librería dispone de un ejemplo que muestra la emulación de un tag. No obstante, la funcionalidad real es limitada.
0 comentarios:
Publicar un comentario