En este tutorial te voy a explicar cómo utilizar el lector RFID RC522 con Arduino y, aunque te parezca una tecnología totalmente ajena o desconocida, en realidad es algo que se lleva utilizando hace mucho tiempo.
Hace unos meses, pude comprobar con mis propios ojos cómo esta tecnología se usa en la tienda de deportes Decathlon al lado de mi casa. En las cajas rápidas.
Consiste en una caja donde depositas todos los artículos y automáticamente, como por arte de magia, aparecen todos los productos en la lista. Pero en realidad no es magia.
Detrás está la tecnología RFID que veremos a lo largo de este artículo.
Y qué mejor manera de conocer cómo funciona algo que montarlo por ti mismo. En este tutorial, además de aprender cómo funciona el RFID a grandes rasgos, te enseñaré todos los secretos detrás del modulo RFID RC522.
Es un módulo que permite tanto leer como escribir etiquetas RFID utilizando la tecnología MIFARE. Es uno de los módulos preferidos de los Makers.
Como siempre, si quieres disfrutar del artículo ponte cómodo que empezamos.
Indice de contenidos
¿Qué es y cómo funciona la tecnología RFID?
La tecnología identificación por radiofrecuencia o RFID (del inglés, Radio Frequency Identification) es un sistema capaz de identificar objetos a través de un identificador único (UID) como si fuera un DNI gracias a la radiofrecuencia.
El sistema RFID consta de dos componentes principales: una etiqueta RFID o transpondedor y un lector RFID o transceptor.
La etiqueta RFID tiene diferentes formatos. Puede ser una pegatina como la que colocan en productos de supermercados para evitar su robo, en una tarjeta de plástico, en un llavero en incluso puede ir debajo de la piel como en los animales.
Hay etiquetas RFID de solo lectura. Estas etiquetas son grabadas con el identificador en el proceso de fabricación. Una vez una etiqueta RFID de solo lectura es generada, ya no se puede modificar el UID.
Sin embargo hay otro tipo de etiquetas RFID que son de lectura y escritura. Una etiqueta de este tipo puede cambiar su información gracias a los lectores RFID como RC522 que permiten leer y escribir etiquetas RFID de este tipo.
Por otro lado está el lector RFID o transceptor. En realidad este componente no solo lee información, también es capaz de emitir una señal de radiofrecuencia.
Algo curioso es cómo funciona un sistema de RFID. Sobre todo en el tema de alimentación.
Cómo funciona el RFID
La alimentación del lector RFID y de la etiqueta RFID es diferente. Un módulo como el RC522 debe estar conectado a la red eléctrica para funcionar. Es lo normal.
Pero ¿qué pasa con las etiquetas RFID?¿cómo se alimenta este tipo de tarjetas?
Las típicas pegatinas RFID que van en muchos productos no tienen ninguna batería o fuente de alimentación. Ni tampoco las tarjetas de control de acceso basadas en RFID.
El principio básico de funcionamiento sobre el que se basan es la inducción.
A este tipo de etiquetas RFID se les conoce como pasivas.
Para leer la información codificada en una etiqueta RFID pasiva, se coloca cerca del lector RFID que genera un campo electromagnético que hace que los electrones se muevan a través de la antena de la etiqueta y posteriormente alimenten el chip.
Al contrario de lo que ocurre con los infrarrojos, los dispositivos basados en RFID no tienen que tener visión directa. La radiofrecuencia es capaz de atravesar objetos.
Cuando esto sucede, el chip alimentado es capaz de enviar la información almacenada en la etiqueta RFID a través de la radiofrecuencia. A esto se le llama retrodispersión.
La retrodispersión es detectada e interpretada por el lector RFID que luego envía los datos a un ordenador o a un microcontrolador como el que tiene Arduino o el ESP8266.
Tipos de RFID
Al igual que una radio debe sintonizarse a diferentes frecuencias para escuchar canales diferentes, las etiquetas y los lectores RFID deben estar sintonizados a la misma frecuencia para poder comunicarse.
Un sistema RFID puede utilizar varias frecuencias dentro del espectro de radiofrecuencia. El espectro de radiofrecuencia se encuentra entre la banda de la frecuencia extremadamente baja o ELF (del inglés Extremely Low Frequency) y el infrarrojo.
Hay tres tipos de sistema dependiendo de la frecuencia que utilicen.
- Baja frecuencia o LF (125-134 KHz)
- Alta frecuencia o HF (13,56 MHz)
- Frecuencia ultra alta o UHF (433, 860 y 960 MHz)
Las ondas de radio no se comportan igual a todas las frecuencias del espectro radioeléctrico. Esto nos obliga a elegir una frecuencia dependiendo de la aplicación que queramos construir.
Sistemas RFID de baja frecuencia o LF
Las aplicaciones RFID que utilizan la baja frecuencia o LF (del inglés Low Frequency) tienen una longitud de onda larga y puede penetrar mejor en los objetos metálicos y delgados.
Además, los sistemas RFID que utilizan LF son ideales para leer objetos con alto contenido en agua como frutas y bebidas.
Sin embargo, el rango de alcance de la baja frecuencia está limitado a unos centímetros. La memoria en las etiquetas RFID es muy limitada debido a la baja tasa de transmisión de datos y el alto costo de producción.
Las aplicaciones típicas de RFID que utilizan la baja frecuencia incluyen el control de acceso y el etiquetado de animales.
Frecuencias | Frecuencia primaria | Alcance |
---|---|---|
30 – 300 kHz | 125 – 134 kHz | 10 centímetros |
Sistemas RFID de alta frecuencia o HF
Los sistemas RFID que utilizan la alta frecuencia o HF (del inglés High Frequency) funcionan muy bien en objetos metálicos y con productos con una tasa de agua media y alta.
Por lo general, estos sistemas RFID funcionan en un rango de centímetros aunque la lectura máxima se sitúa entorno al metro.
Utilizan estándares y protocolos globales como el NFC con opciones de memoria mayores aunque el rango de lectura todavía sigue siendo corto y la tasa de transmisión de datos baja.
NFC (del inglés Near-Field Communications) es una tecnología de corto alcance que forma parte de las tecnologías RFID de alta frecuencia. Se trata de una tecnología en constante crecimiento aunque en la actualidad se usa sobre todo para pagos y aplicaciones de marketing.
Frecuencia | Alcance |
---|---|
13,56 MHz | 30 centímetros |
Sistemas RFID de ultra alta frecuencia o UHF
Los sistemas RFID basado en la ultra alta frecuencia o UHF (del inglés Ultra High Frequency) generalmente ofrecen un alcance mayor que los tipos LF y HF. Hablamos de unos pocos centímetros a más de 20 metros.
Otra diferencia es que pueden leer datos más rápido (muchas etiquetas por segundo).
Por contra, debido a que el UHF tiene una longitud de onda más corta, la señal se atenúa más y no pueden atravesar el metal o el agua.
Esta tecnología se utiliza para controlar los artículos de almacenes, para contar a personas y para el control en carreras cuando los corredores cruzan la línea de meta.
También se utiliza para peajes y para control de acceso a estacionamientos.
Frecuencia | Frecuencia primaria | Alcance |
---|---|---|
300 – 3000 MHz | 433 MHz, 860 – 960 MHz | +100 metros |
Lector RFID con Arduino RC522
El módulo lector RFID RC522 está basado en el circuito integrado MFRC522 de la empresa NXP. Se trata de una de las opciones más económicas y fáciles de usar en tus proyectos con Arduino o con ESP8266.
Normalmente viene integrado en un módulo cuyo coste varía dependiendo en la tienda online donde lo adquieras. En Amazon puedes encontrar un módulo lector RFID RC522 desde 6 o 7 euros.
Suele venir acompañado de una etiqueta RFID en formato tarjeta de crédito y una etiqueta RFID en formato llavero.
Estas etiquetas RFID utilizan la tecnología MIFARE de la empresa NXP Semiconductors. Es un estándar muy utilizado en RFID.
Las tarjetas puede tener una memoria de 1K o 4K dividida en sectores y bloques. Más adelante veremos cómo se estructura internamente esta memoria.
El módulo lector RFID RC522 también sirve para escribir etiquetas RFID.
El lector RFID RC522 utiliza la alta frecuencia HF creando un campo electromagnético de 13,56 MHz. En teoría tiene un alcance máximo de 35 centímetros.
El módulo RC522 tiene un alcance máximo de 5 cm. Por encima de este valor puede que el sistema no funcione correctamente.
El lector RFID RC522 puede comunicarse con un microcontrolador a través de un bus de interfaz de periféricos serie o bus SPI (del inglés Serial Peripheral Interface) con una velocidad de datos máxima de 10 Mbps.
También es compatible con la comunicación a través del protocolo I2C y UART.
Algo muy interesante es que este módulo viene con un pin de interrupción muy útil. Sirve para que en vez de preguntar una y otra vez al lector RFID RC522 si hay una etiqueta RFID cerca, el módulo nos avisará a través del pin cuando una etiqueta RFID se acerque y así poder activar el microcontrolador.
El voltaje de operación es de 2,5V a 3,3V. Esto implica que es compatible con la mayoría de microcontroladores del mercado como los que utilizan Arduino y el ESP8266.
La buena noticia es que a pesar de ser alimentado con 3,3V, los niveles lógicos son compatibles con 5V por lo que es compatible con cualquier Arduino o microcontrolador que trabaje a 5V.
Estas sería las especificaciones completas sacadas de la hoja de características técnicas
Rango de frecuencias | Banda ISM de 13,56 MHz |
Interfaz | SPI/I2C/UART |
Voltaje de operación | 2,5V a 3,3V |
Corriente máx. de funcionamiento | 13-26 mA |
Corriente mínima | 10 uA |
Niveles lógicos | 5V y 3V3 |
Alcance | 5 cm |
Pineado lector RFID RC522
El lector RFID RC522 tiene 8 pines para conectarse a un microcontrolador.
- VCC: pin de alimentación del lector RFID RC522. Admite un voltaje de alimentación entre 2,5V y 3,3V.
- RST: es un pin para encender y apagar el módulo. Mientras el pin esté en estado LOW se mantendrá apagado sin apenas consumir. Cuando el estado cambia a HIGH el RC522 se reinicia.
- GND: pin de tierra o GND.
- IRQ: pin de interrupción que alerta al microcontrolador cuando una etiqueta RFID se acerca al lector RFID RC522.
- MISO/SCL/TX: este pin tiene tres funciones. Cuando la interfaz SPI está habilitada funciona como salida de esclavo y entrada de máster. Cuando está activada la interfaz I2C funciona como señal de reloj y como salida serie cuando la interfaz UART está habilitada.
- MOSI: entrada en la interfaz SPI.
- SCK: señal de reloj de la interfaz SPI.
- SS/SDA/RX: el pin actúa como entrada de señal cuando la interfaz SPI está habilitada. Si la interfaz I2C está activa actúa como entrada de datos y como entrada de datos serie cuando la interfaz UART está habilitada.
No conectes el pin VCC al pin de 5V de cualquier placa ¡Esto puede hacer que el módulo se rompa! Comprobar siempre antes de conectar ningún cable que el voltaje es menor de 3,3V.
Conexión lector RFID RC522 con Arduino UNO
Ahora que tenemos claro cuales son las características técnicas del módulo y sus pines, vamos a conectarlo a Arduino UNO.
El esquema más básico de conexión sería el siguiente.
Para conectar el lector RFID RC522 a Arduino utilizamos la interfaz SPI de cuatro pines ya que el módulo RC522 requiere una gran cantidad de transferencia de datos.
La interfaz SPI es más rápida que la interfaz I2C o UART.
Conecta el pin VCC al pin de 3,3V de Arduino y el pin GND al pin GND de Arduino. El pin RST del RC522 se puede conectar a cualquier pin de Arduino en mi caso lo he conectado al pin 9. El pin IRQ se queda desconectado, en este ejemplo no nos va a hacer falta.
La conexión de los pines SPI dependerá de la placa que estés utilizando. En la siguiente tabla tienes un resumen de las placas más importantes.
MOSI | MISO | SCK | CS | |
---|---|---|---|---|
Arduino UNO | 11 | 12 | 13 | 10 |
Arduino Nano | 11 | 12 | 13 | 10 |
Arduino Mega | 51 | 50 | 52 | 53 |
ESP8266 | GPIO16 | GPIO15 | GPIO14 | GPIO17 |
En caso de que estés utilizando una placa diferente de Arduino, te recomiendo que vayas a la documentación oficial.
Ya tenemos listo tanto la placa como el lector RFID RC522 para cargar el primer código.
Lectura de etiqueta RFID con Arduino y RC522
La comunicación entre Arduino y el lector RFID RC522 es bastante compleja a nivel de programación. Afortunadamente, como ocurre en otros muchos casos, hay una librería de Arduino que nos facilita enormemente la tarea.
Esta librería está programada y mantenida por Miguel Balboa y es una de las más populares para programar un lector RFID RC522. Puedes descargarla a través del repositorio de GitHub.
También puedes instalarla a través del gestor de bibliotecas del IDE de Arduino.
Sea como sea una vez instalada en tu ordenador ya tendrás acceso tanto al código como a los ejemplos de la librería.
Vamos a empezar abriendo el ejemplo que se llama DumpInfo.
Este programa no escribe ningún dato en la etiqueta RFID. Simplemente lee si puede la etiqueta RFID y muestra la información por el monitor serie.
Es muy interesante para saber qué información hay almacenada dentro de la etiqueta RFID antes de usarla.
El código que se abre es el siguiente.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
/* * -------------------------------------------------------------------------------------------------------------------- * Example sketch/program showing how to read data from a PICC to serial. * -------------------------------------------------------------------------------------------------------------------- * This is a MFRC522 library example; for further details and other examples see: https://github.com/miguelbalboa/rfid * * Example sketch/program showing how to read data from a PICC (that is: a RFID Tag or Card) using a MFRC522 based RFID * Reader on the Arduino SPI interface. * * When the Arduino and the MFRC522 module are connected (see the pin layout below), load this sketch into Arduino IDE * then verify/compile and upload it. To see the output: use Tools, Serial Monitor of the IDE (hit Ctrl+Shft+M). When * you present a PICC (that is: a RFID Tag or Card) at reading distance of the MFRC522 Reader/PCD, the serial output * will show the ID/UID, type and any data blocks it can read. Note: you may see "Timeout in communication" messages * when removing the PICC from reading distance too early. * * If your reader supports it, this sketch/program will read all the PICCs presented (that is: multiple tag reading). * So if you stack two or more PICCs on top of each other and present them to the reader, it will first output all * details of the first and then the next PICC. Note that this may take some time as all data blocks are dumped, so * keep the PICCs at reading distance until complete. * * @license Released into the public domain. * * Typical pin layout used: * ----------------------------------------------------------------------------------------- * MFRC522 Arduino Arduino Arduino Arduino Arduino * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro * Signal Pin Pin Pin Pin Pin Pin * ----------------------------------------------------------------------------------------- * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST * SPI SS SDA(SS) 10 53 D10 10 10 * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 */ #include <SPI.h> #include <MFRC522.h> #define RST_PIN 9 // Configurable, see typical pin layout above #define SS_PIN 10 // Configurable, see typical pin layout above MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance void setup() { Serial.begin(9600); // Initialize serial communications with the PC while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) SPI.begin(); // Init SPI bus mfrc522.PCD_Init(); // Init MFRC522 delay(4); // Optional delay. Some board do need more time after init to be ready, see Readme mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader details Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks...")); } void loop() { // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. if ( ! mfrc522.PICC_IsNewCardPresent()) { return; } // Select one of the cards if ( ! mfrc522.PICC_ReadCardSerial()) { return; } // Dump debug info about the card; PICC_HaltA() is automatically called mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); } |
Puedes descargar la última versión desde GitHub.
Carga el código a la placa, abre el monitor serie y pasa la tarjeta que viene con el lector RFID RC522. Verás algo parecido a la siguiente imagen.
Tienes que dejar la tarjeta junto al lector RFID RC522 hasta que se muestre toda la información por el monitor serie. Todo eso que aparece son los 1024 bytes (1 Kb) de memoria que tiene la tarjeta.
A continuación vamos a ver su estructura.
Estructura de la memoria MIFARE Classic 1K
Las tarjetas o etiquetas RFID que utiliza el lector RC522 tienen una memoria de 1K organizada en 16 sectores (del 0 al 15). Cada sector se divide en 4 bloques (del 0 al 3). En cada bloque se pueden almacenar 16 bytes de datos (del 0 al 15).
Utilizan la tecnología MIFARE Classics. Esta tecnología utiliza un cifrado propietario de la empresa Philips que se llama CRYPTO1.
El problema que existe actualmente es que la seguridad a través de este sistema de cifrado presenta deficiencias. No hace falta hacer una gran inversión para hackear MIFARE. Es bastante sencillo.
Pero esto se sale totalmente fuera de este tutorial así que volvamos a donde nos habíamos quedado.
Según la estructura de la memoria en sectores y bloques, tenemos que
Donde puedes comprobar que efectivamente esta tarjeta tiene 1 Kb de memoria.
Si miras de nuevo el monitor serie, puedes ver los sectores, bloques y datos.
Como ya he dicho en total hay 16 sectores donde hay 4 bloques y cada bloque tiene 16 bytes.
De estos 4 bloques de cada sector, solo se pueden utilizar 3 bloques para almacenar memoria. Esto equivale a 16 bytes por 3 bloques que hacen un total de 48 bytes.
Los 16 bytes del último bloque se utilizan para guardar las claves de cifrado y los permisos para acceder a ese sector. Este bloque se conoce como trailer y coincide con el último bloque de cada sector.
Así el bloque 64 es el bloque trailer del sector 15, el bloque 59 es el bloque trailer del sector 14, el bloque 55 es el bloque trailer del sector 13 y así sucesivamente con el resto de sectores.
Dentro de este bloque los 16 bytes que pertenecen al trailer se dividen en dos claves para cifrar el contenido (Key A y Key B) que ocupan 6 bytes cada una. 3 bytes para establecer las condiciones y permisos de acceso al sector (Access Conditions). Y por último un byte restante (U) que no se utiliza para nada.
Para cifrar el contenido se puede utilizar o la clave Key A o la clave Key B. Cada sector tiene sus propias claves aunque una práctica común con la tecnología MIFARE es utilizar las mismas claves en todos los sectores.
Por supuesto que para que todo funcione correctamente el lector necesita conocer la clave que se está utilizando. De no ser así no podrá ni leer ni escribir información.
Las etiquetas RFID que vienen con el módulo RFID RC522 vienen de fábrica con la clave por defecto FF FF FF FF FF FF.
Si la clave puede ser leída no es segura y por lo tanto no se utiliza. Por eso en la imagen anterior aparece.
Key A: 00 00 00 00 00 00 (no puede ser leída)
Key B: FF FF FF FF FF FF (puede ser leída y no es segura)
Esta configuración se establece en los bytes de Access Conditions.
Por último quiero hablarte del bloque 0 del sector 0. Este bloque no se utiliza para almacenar datos ya que de fábrica contiene información muy valiosa.
Este bloque se llama el bloque del fabricante (en inglés manufacturer block) y contiene el UID o identificador de la tarjeta y el identificador del fabricante.
El UID es un identificador único que MIFARE asigna a cada etiqueta RFID. Cada tarjeta tiene el suyo propio y consta de 4 bytes de sólo lectura.
Los otros 12 bytes del bloque se utilizan para almacenar información del fabricante.
El bloque del fabricante no se debe modificar ni sobreescribir ya que esto puede bloquear permanentemente la tarjeta o etiqueta RFID.
Si quieres saber más sobre MIFARE consulta la documentación de la memoria MIFARE Classic 1K.
Una vez que tenemos claro cómo funciona y se estructura una tarjeta MIFARE como las que lleva el módulo RFID RC522, vamos a empezar aportando algo de seguridad.
En la siguiente sección vas a ver cómo cambiar la clave Key-A y Key-B de autentificación para que no utilice la que viene por defecto.
Cambiar claves tarjeta RFID con lector RFID RC522
Si quieres utiliza el lector RFID RC522 lo primero que tienes que hacer es cambiar las claves Key-A y Key-B para que tu sistema RFID sea más seguro.
Si utilizas las claves que vienen por defecto, cualquier persona podrá utilizarlas.
Como ya te he comentado, cada sector tiene su propia clave aunque es una práctica común el utilizar la misma clave para todos los sectores. En este ejercicio vamos a cambiar las claves del sector 15.
El siguiente código cambia la clave de este sector por las siguientes:
- Key-A: A0 A1 A2 A3 A4
- Key-B: B0 B1 B2 B3 B4
Puedes utilizar las claves que quieras. Te aconsejo que si lo vas a utilizar con tus propias tarjetas, utilices tus propias claves de cifrado y no las compartas con nadie. Estas claves tienen que estar tanto en la tarjeta con en el lector RFID RC522.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
// Librerías #include <SPI.h> #include <MFRC522.h> // Pines SPI #define RST_PIN 9 #define SS_PIN 10 // Instancia a la clase MFRC522 MFRC522 mfrc522(SS_PIN, RST_PIN); // Claves de cifrado actuales MFRC522::MIFARE_Key keyA = {keyByte: {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; MFRC522::MIFARE_Key keyB = {keyByte: {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; // Nuevas claves de cifrado MFRC522::MIFARE_Key nuevaKeyA = {keyByte: {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5}}; MFRC522::MIFARE_Key nuevaKeyB = {keyByte: {0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5}}; // Datos del sector byte sector = 15; void mostrarByteArray(byte* buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } } void setup() { Serial.begin(9600); while (!Serial); // Bucle que no permite continuar hasta que no se ha abierto el monitor serie SPI.begin(); // Iniciar bus SPI mfrc522.PCD_Init(); // Iniciar lector RFID RC522 Serial.println(F("Acerca la tarjeta al lector para escanear.")); Serial.println(F("Las claves de esta tarjeta deben ser:")); Serial.print("Key-A: "); mostrarByteArray(keyA.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); Serial.print("Key-B: "); mostrarByteArray(keyB.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); Serial.println(F("MUY IMPORTANTE: durante el proceso de actualización de las claves ")); Serial.println(F("no separes la tarjeta del lector hasta que no termine.")); } void loop() { // Si no hay una tarjeta cerca no sigue el programa if (!mfrc522.PICC_IsNewCardPresent()) { return; } // Si hay una tarjeta cerca, que la eleccione // En caso contrario que no continúe if (!mfrc522.PICC_ReadCardSerial()) { return; } // Mostrar información de la tarjeta por el monitor serie Serial.print(F("UID de la tarjeta:")); mostrarByteArray(mfrc522.uid.uidByte, mfrc522.uid.size); // Motrar el UID Serial.println(); Serial.print(F("Tipo de tarjeta: ")); MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); //Motrar el tipo Serial.println(mfrc522.PICC_GetTypeName(piccType)); // Cambiar claves Key-A y Key-B en un sector concreto boolean resultado = cambiarKeys(&keyA, &keyB, &nuevaKeyA, &nuevaKeyB, sector); if (resultado) { Serial.print(F("Claves del sector ")); Serial.println(sector); Serial.print(F("Key-A: ")); mostrarByteArray(nuevaKeyA.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); Serial.print(F("Key-B: ")); mostrarByteArray(nuevaKeyB.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); } else { Serial.print(F("Claves del sector ")); Serial.println(sector); Serial.print(F("Key-A: ")); mostrarByteArray(keyA.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); Serial.print(F("Key-B: ")); mostrarByteArray(keyB.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); } // Detener el lector mfrc522.PICC_HaltA(); // Detener la encriptación Crypto1 mfrc522.PCD_StopCrypto1(); Serial.println(F("Proceso finalizado. Ya puedes retirar la tarjeta del lector RFID")); } boolean cambiarKeys(MFRC522::MIFARE_Key* antiguaKeyA, MFRC522::MIFARE_Key* antiguaKeyB, MFRC522::MIFARE_Key* nuevaKeyA, MFRC522::MIFARE_Key* nuevaKeyB, int sector) { MFRC522::StatusCode estado; byte bloqueTrailer = sector * 4 + 3; // Cálculo del bloque Trailer byte buffer[18]; byte size = sizeof(buffer); Serial.print(F("Modificando sector ")); Serial.println(sector); // Autenticar utilizando la clave Key-A estado = (MFRC522::StatusCode)mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloqueTrailer, antiguaKeyA, &(mfrc522.uid)); // Si no consigue autenticar que no continúe if (estado != MFRC522::STATUS_OK) { Serial.print(F("Fallo en la autenticación Key-A: ")); Serial.println(mfrc522.GetStatusCodeName(estado)); return false; } // Mostrar el sector completo Serial.println(F("Informción en el sector:")); mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), antiguaKeyA, sector); Serial.println(); // Leyendo datos del bloque Serial.print(F("Leyendo datos del bloque ")); Serial.print(bloqueTrailer); Serial.println(F(" ...")); estado = (MFRC522::StatusCode) mfrc522.MIFARE_Read(bloqueTrailer, buffer, &size); if (estado != MFRC522::STATUS_OK) { Serial.print(F("Fallo al leer el bloque: ")); Serial.println(mfrc522.GetStatusCodeName(estado)); return false; } Serial.print(F("Información en el bloque ")); Serial.print(bloqueTrailer); Serial.println(F(":")); mostrarByteArray(buffer, 16); Serial.println(); Serial.println(); // Autenticar utilizando la clave Key-B estado = (MFRC522::StatusCode)mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, bloqueTrailer, antiguaKeyB, &(mfrc522.uid)); // Si no consigue autenticar que no continúe if (estado != MFRC522::STATUS_OK) { Serial.print(F("Fallo en la uatenticación Key-B: ")); Serial.println(mfrc522.GetStatusCodeName(estado)); return false; } // Array con nuevas claves Key-A y Key-B if (nuevaKeyA != nullptr || nuevaKeyB != nullptr) { // Recorrer todos los bytes de la clave for (byte i = 0; i < MFRC522::MF_KEY_SIZE; i++) { if (nuevaKeyA != nullptr) { buffer[i] = nuevaKeyA->keyByte[i]; } if (nuevaKeyB != nullptr) { buffer[i + 10] = nuevaKeyB->keyByte[i]; } } } // Escribir las nuevas claves al bloque Trailer estado = (MFRC522::StatusCode) mfrc522.MIFARE_Write(bloqueTrailer, buffer, 16); if (estado != MFRC522::STATUS_OK) { Serial.print(F("Fallo al escribir el bloque Trailer: ")); Serial.println(mfrc522.GetStatusCodeName(estado)); return false; } return true; } |
Mucho ojo que si cargas este código en la placa y acercas una tarjeta RFID al lector, como tenga los valores de fábrica los cambiará y deberás utilizar las nuevas claves de cifrado.
Voy a analizar las partes más importantes de este código que cambia las claves de cifrado del sector 15 de la tarjeta RFID.
Librerías variables y objetos
Lo primero es incluir las librerías necesarias.
1 2 3 |
// Librerías #include <SPI.h> #include <MFRC522.h> |
Luego se declaran dos constantes para almacenar los pines de la comunicación SPI y crear la instancia a la clase MFRC522.
1 2 3 4 5 6 |
// Pines SPI #define RST_PIN 9 #define SS_PIN 10 // Instancia a la clase MFRC522 MFRC522 mfrc522(SS_PIN, RST_PIN); |
A continuación se almacenan las claves antiguas y nuevas en una estructura de datos MIFARE_Key.
1 2 3 4 5 6 7 |
// Claves de cifrado actuales MFRC522::MIFARE_Key keyA = {keyByte: {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; MFRC522::MIFARE_Key keyB = {keyByte: {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; // Nuevas claves de cifrado MFRC522::MIFARE_Key nuevaKeyA = {keyByte: {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5}}; MFRC522::MIFARE_Key nuevaKeyB = {keyByte: {0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5}}; |
Ojo que una vez actualices las claves de cifrado estos datos se deben modificar. En principio partimos de los datos que vienen cargados por defecto en la etiqueta RFID junto con el módulo lector RFID RC522.
La última variable almacena el número del sector que se va a modificar.
1 2 |
// Datos del sector byte sector = 15; |
Función setup()
La función setup como siempre, dentro van las inicializaciones y configuraciones.
Comunicación serie, iniciar comunicación SPI e iniciar el lector RFID RC522.
1 2 3 4 |
Serial.begin(9600); while (!Serial); // Bucle que no permite continuar hasta que no se ha abierto el monitor serie SPI.begin(); // Iniciar bus SPI mfrc522.PCD_Init(); // Iniciar lector RFID RC522 |
Luego se muestra por el monitor serie las claves actuales.
1 2 3 4 5 6 7 8 9 10 |
Serial.println(F("Acerca la tarjeta al lector para escanear.")); Serial.println(F("Las claves de esta tarjeta deben ser:")); Serial.print("Key-A: "); mostrarByteArray(keyA.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); Serial.print("Key-B: "); mostrarByteArray(keyB.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); Serial.println(F("MUY IMPORTANTE: durante el proceso de actualización de las claves ")); Serial.println(F("no separes la tarjeta del lector hasta que no termine.")); |
Función loop()
Lo primero que hacemos en el bucle loop() es comprobar si han acercado una tarjeta al lector y en caso afirmativo, se selecciona y se lee.
1 2 3 4 5 6 7 8 9 10 |
// Si no hay una tarjeta cerca no sigue el programa if (!mfrc522.PICC_IsNewCardPresent()) { return; } // Si hay una tarjeta cerca, que la eleccione // En caso contrario que no continúe if (!mfrc522.PICC_ReadCardSerial()) { return; } |
Es interesante mostrar la información como el UID y el tipo de tarjeta con el que estamos trabajando. En este caso el lector RFID RC522 viene con una tarjeta MIFARE Classic 1K.
1 2 3 4 5 6 7 |
// Mostrar información de la tarjeta por el monitor serie Serial.print(F("UID de la tarjeta:")); mostrarByteArray(mfrc522.uid.uidByte, mfrc522.uid.size); // Motrar el UID Serial.println(); Serial.print(F("Tipo de tarjeta: ")); MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); //Motrar el tipo Serial.println(mfrc522.PICC_GetTypeName(piccType)); |
Las siguiente parte de código cambia las claves Key-A y Key-B en el sector indicado por la variable sector. En este caso es el sector 15. Todo esto se hace dentro de la función cambiarKeys(…).
Dependiendo del resultado mostrará un mensaje u otro por el monitor serie.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// Cambiar claves Key-A y Key-B en un sector concreto boolean resultado = cambiarKeys(&keyA, &keyB, &nuevaKeyA, &nuevaKeyB, sector); if (resultado) { Serial.print(F("Claves del sector ")); Serial.println(sector); Serial.print(F("Key-A: ")); mostrarByteArray(nuevaKeyA.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); Serial.print(F("Key-B: ")); mostrarByteArray(nuevaKeyB.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); } else { Serial.print(F("Claves del sector ")); Serial.println(sector); Serial.print(F("Key-A: ")); mostrarByteArray(keyA.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); Serial.print(F("Key-B: ")); mostrarByteArray(keyB.keyByte, MFRC522::MF_KEY_SIZE); Serial.println(); } |
Por último hay que detener el lector y la encriptación.
1 2 3 4 5 6 |
// Detener el lector mfrc522.PICC_HaltA(); // Detener la encriptación Crypto1 mfrc522.PCD_StopCrypto1(); Serial.println(F("Proceso finalizado. Ya puedes retirar la tarjeta del lector RFID")); |
Función cambiarKeys()
El proceso de reescribir las claves de cifrado se hace realmente en la función cambiarKeys(). Esta función admite 5 parámetros
- antiguaKeyA: es la clave Key-A actual.
- antiguaKeyB: es la clave Key-B actual.
- nuevaKeyA: es la nueva clave Key-A.
- nuevaKeyB: es la nueva clave Key-B.
- sector: es el sector donde se van a cambiar las claves de cifrado.
Lo primero que hacemos dentro de esta función es calcular el bloque Trailer con una sencilla fórmula. Multiplicas el sector por 4 y sumas 3. Esto te da el último bloque del sector. El bloque Trailer.
1 2 3 4 5 6 7 |
MFRC522::StatusCode estado; byte bloqueTrailer = sector * 4 + 3; // Cálculo del bloque Trailer byte buffer[18]; byte size = sizeof(buffer); Serial.print(F("Modificando sector ")); Serial.println(sector); |
Lo siguiente es comprobar que realmente las claves Key-A y Key-B actuales son válidas. Eso se hace llamando a la función PCD_Authenticate() de la clase MFRC522. Esta función admite 4 parámetros:
1 |
PCD_Authenticate(comando, numbloque, *key, *uid) |
- comando: indica el tipo de clave que se va a utilizar Key-A o KeyB.
- numBloque: número de bloque que se va a utilizar para autenticar.
- key: puntero a la clave de cifrado
- uid: puntero al identificador UID de la etiqueta RFID
Una vez ejecutada la función y si todo ha ido bien, se habrá iniciado la comunicación cifrada entre la etiqueta y el lector RFID.
1 2 3 4 5 6 7 8 9 |
// Autenticar utilizando la clave Key-A estado = (MFRC522::StatusCode)mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloqueTrailer, antiguaKeyA, &(mfrc522.uid)); // Si no consigue autenticar que no continúe if (estado != MFRC522::STATUS_OK) { Serial.print(F("Fallo en la autenticación Key-A: ")); Serial.println(mfrc522.GetStatusCodeName(estado)); return false; } |
Luego muestra el sector completo llamando a la función PICC_DumpMifareClassicSectorToSerial() y muestra la información del bloque Trailer con la función MIFARE_Read().
Esta función es importante ya que la utilizaremos para leer cualquier bloque de la tarjeta. Admite 3 parámetros:
1 |
MIFARE_Read(numBloque, arrayDatos, tam) |
- numBloque: número de bloque que se va a leer.
- arrayDatos: es el array de datos donde se almacena la información.
- tam: tamaño del array de datos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Mostrar el sector completo Serial.println(F("Informción en el sector:")); mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), antiguaKeyA, sector); Serial.println(); // Leyendo datos del bloque Serial.print(F("Leyendo datos del bloque ")); Serial.print(bloqueTrailer); Serial.println(F(" ...")); estado = (MFRC522::StatusCode) mfrc522.MIFARE_Read(bloqueTrailer, buffer, &size); if (estado != MFRC522::STATUS_OK) { Serial.print(F("Fallo al leer el bloque: ")); Serial.println(mfrc522.GetStatusCodeName(estado)); return false; } Serial.print(F("Información en el bloque ")); Serial.print(bloqueTrailer); Serial.println(F(":")); mostrarByteArray(buffer, 16); Serial.println(); Serial.println(); |
Luego se intenta autenticar con la clave de cifrado Key-B como hemos hecho con la clave Key-A.
1 2 3 4 5 6 7 8 9 |
// Autenticar utilizando la clave Key-B estado = (MFRC522::StatusCode)mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, bloqueTrailer, antiguaKeyB, &(mfrc522.uid)); // Si no consigue autenticar que no continúe if (estado != MFRC522::STATUS_OK) { Serial.print(F("Fallo en la uatenticación Key-B: ")); Serial.println(mfrc522.GetStatusCodeName(estado)); return false; } |
El siguiente bloque de código monta un array de bytes, buffer, que contendrá las nuevas claves de cifrado Key-A y Key-B.
1 2 3 4 5 6 7 8 9 10 11 12 |
// Array con nuevas claves Key-A y Key-B if (nuevaKeyA != nullptr || nuevaKeyB != nullptr) { // Recorrer todos los bytes de la clave for (byte i = 0; i < MFRC522::MF_KEY_SIZE; i++) { if (nuevaKeyA != nullptr) { buffer[i] = nuevaKeyA->keyByte[i]; } if (nuevaKeyB != nullptr) { buffer[i + 10] = nuevaKeyB->keyByte[i]; } } } |
Por último hay que escribir las nuevas claves de cifrado en el bloque Trailer del sector correspondiente. Eso se hace con la función MIFARE_Write() que admite 3 parámetros:
1 |
MIFARE_Write(numBloque, arrayDatos, tam) |
- numBloque: número de bloque donde se van a escribir los datos.
- arrayDatos: es el array de datos que se va a escribir en el bloque.
- tam: tamaño del array de datos.
1 2 3 4 5 6 7 |
// Escribir las nuevas claves al bloque Trailer estado = (MFRC522::StatusCode) mfrc522.MIFARE_Write(bloqueTrailer, buffer, 16); if (estado != MFRC522::STATUS_OK) { Serial.print(F("Fallo al escribir el bloque Trailer: ")); Serial.println(mfrc522.GetStatusCodeName(estado)); return false; } |
Si todo ha ido bien la función devolverá true.
Ahora puedes probar el código y cargarlo en tu Arduino.
Cuidado, que cuando ejecutes este código las claves de cifrado ya no serán las mismas y algunos programas de ejemplo de la librería MFRC522 no funcionarán.
Si abres el monitor serie verás algo parecido a lo siguiente.
El programa está esperando a que acerques una tarjeta al lector RFID RC522. Una vez que lo acerques no lo separes del lector hasta que no termine todo el proceso.
Al final, en el monitor serie tiene que aparecer un mensaje como el siguiente.
Eso quiere decir que se han actualizado las claves de cifrado. Aún así te aconsejo que revises todos los sectores por si alguno ha dado algún error.
Y una vez cambiadas las claves ya podemos trabajar con más seguridad. El siguiente código voy a simular cómo funciona una tarjeta de transporte público con una recarga y con la utilización al subir al tranvía por ejemplo.
Escribir etiqueta RFID con Arduino y lector RC522
La idea en esta última parte de este tutorial es simular las típicas tarjetas de transporte público que pueden ser recargadas con un número de viajes para gastarlas en el autobús o en tranvía como es el caso de San Vicente del Raspeig en Alicante. Donde yo vivo.
Esta es la típica tarjeta que te dan. Cuando quieres cargar viajes, vas a una máquina que tiene un lector RFID y cuando acercas la tarjeta y echas dinero, te recarga un número determinado de billetes.
Luego, cuando subes al autobús o al tranvía, lo acercas a un lector de tarjetas y te resta un viaje.
Es un funcionamiento muy sencillo y es el que vamos a simular a continuación.
Recarga de billetes con tarjeta RFID con Arduino
Este sería el código que nos permite recargar los billetes en la tarjeta de transporte.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
// Librerías #include <SPI.h> #include <MFRC522.h> // Pines SPI #define RST_PIN 9 #define SS_PIN 10 // Instancia a la clase MFRC522 MFRC522 mfrc522(SS_PIN, RST_PIN); // Clave de cifrado actuales MFRC522::MIFARE_Key keyA = {keyByte: {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5}}; MFRC522::MIFARE_Key keyB = {keyByte: {0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5}}; // Número de viajes array 16 bytes byte datosBloque[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Datos del sector byte sector = 15; byte numBlque = 60; byte bloqueTrailer = 63; byte numViajes = 10; void mostrarByteArray(byte* buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } } void setup() { Serial.begin(9600); while (!Serial); // Bucle que no permite continuar hasta que no se ha abierto el monitor serie SPI.begin(); // Iniciar bus SPI mfrc522.PCD_Init(); // Iniciar lector RFID RC522 Serial.print(F("Se van a cargar ")); Serial.print(datosBloque[0]); Serial.println(F(" viajes")); Serial.println(F("Acerca la tarjeta al lector para escanear....")); } void loop() { // Si no hay una tarjeta cerca no sigue el programa if (!mfrc522.PICC_IsNewCardPresent()) { return; } // Si hay una tarjeta cerca, que la eleccione // En caso contrario que no continúe if (!mfrc522.PICC_ReadCardSerial()) { return; } // Mostrar información de la tarjeta por el monitor serie Serial.print(F("UID de la tarjeta:")); mostrarByteArray(mfrc522.uid.uidByte, mfrc522.uid.size); // Motrar el UID Serial.println(); Serial.print(F("Tipo de tarjeta: ")); MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); //Motrar el tipo Serial.println(mfrc522.PICC_GetTypeName(piccType)); // Obtener viajes acutales byte viajesActuales = leerViajes(); Serial.println(""); Serial.print(F("Viajes actuales: ")); Serial.println(viajesActuales, DEC); // Añadir viajes a los actuales viajesActuales = viajesActuales + numViajes; datosBloque[0] = viajesActuales; // Escribir la información en el bloque int resultadoEb = escribirBloque(); // Dependiendo del resultado if (resultadoEb == 1) { Serial.println(F("No se puede escribir en un bloque Trailer")); } else if (resultadoEb == 2) { Serial.println(F("No se puede escribir en un bloque del fabricante")); } else if (resultadoEb == 3) { Serial.println(F("Problemas al comunicar con la clave proporcionada")); } else if (resultadoEb == 4) { Serial.println(F("Problemas al escribir en el bloque")); } else { Serial.println(F("Se han guardado los datos correctamente")); byte totalViajes = leerViajes(); Serial.println(""); Serial.print(F("Viajes actualizados: ")); // Leer los datos del bloque Serial.println(totalViajes, DEC); } // Detener el lector mfrc522.PICC_HaltA(); // Detener la encriptación Crypto1 mfrc522.PCD_StopCrypto1(); Serial.println(); Serial.println(F("Proceso finalizado. Ya puedes retirar la tarjeta del lector RFID")); while (true); } byte escribirBloque() { MFRC522::StatusCode estado; // Comenzar comunicación cifrada con Key-A estado = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloqueTrailer, &keyA, &(mfrc522.uid)); if (estado != MFRC522::STATUS_OK) { return 3; } // Escribir en el bloque Serial.print(F("Bloque numeroBloque: ")); Serial.println(numBlque); estado = mfrc522.MIFARE_Write(numBlque, datosBloque, 16); if (estado != MFRC522::STATUS_OK) { Serial.print("MIFARE_Write() fallo: "); Serial.println(mfrc522.GetStatusCodeName(estado)); return 4; } return 0; } byte leerViajes() { MFRC522::StatusCode estado; byte datosLectura[18]; byte tamBuffer = sizeof(tamBuffer); // Comenzar comunicación cifrada con Key-A estado = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloqueTrailer, &keyA, &(mfrc522.uid)); if (estado != MFRC522::STATUS_OK) { return 1; } // Leer bloque estado = mfrc522.MIFARE_Read(numBlque, datosLectura, &tamBuffer); if (estado != MFRC522::STATUS_OK) { Serial.print("MIFARE_Read() fallo: "); Serial.println(mfrc522.GetStatusCodeName(estado)); return 2; } mostrarByteArray(datosLectura, tamBuffer); return datosLectura[0]; } |
En este código hay que tener varias cosas en cuenta.
Lo primero es el array de bytes donde se almacenan los datos. Tiene que contener 16 elementos y se inicia con todos los elementos a cero.
1 2 |
// Número de viajes array 16 bytes byte datosBloque[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
El bloque donde se van a guardar los datos es cualquier bloque del sector 15. En concreto yo he utilizado el 60 pero puedes utilizar cualquier bloque de este sector menos el 63 que es el Trailer.
Dentro de este bloque se utilizará el byte 0 para almacenar el número de viajes que tiene la tarjeta. Lo normal es que empiece por cero pero si en algún momento tiene algún viaje, deberá sumar los nuevos viajes a la cantidad ya existente.
También hay que determinar el número de billetes que se añaden a la tarjeta RFID. Todo esto se configura en las siguientes variables.
1 2 3 4 5 |
// Datos del sector byte sector = 15; byte numBlque = 60; byte bloqueTrailer = 63; byte numViajes = 10; |
El funcionamiento del código es el siguiente.
- Leer los datos y obtener el número de billetes actuales.
- Sumar el número de billetes a los actuales.
- Actualizar la tarjeta a través del lector RFID.
Todo esto es lo que se hace en el resto del código. Utilizo las mismas funciones que hemos visto en la sección anterior.
Carga el código a la placa, abre el monitor serie y acerca la tarjeta al lector. Verás algo parecido a lo siguiente.
Puedes comprobar como tenía 0 viajes y se añaden 10 viajes a la tarjeta RFID. Si reseteas la placa y acercas de nuevo la tarjeta al lector añadirás otros 10 viajes.
Por lo tanto ya tenemos una tarjeta repleta de viajes. Ahora que gastarlos en el autobús. A continuación vamos a ver cómo sería el código.
Sistema de gestión de billetes RFID con Arduino
El código para ir consumiendo los viajes de uno en uno con el lector RFID es muy parecido (por no decir igual) al código de la recarga. Pero en este caso en vez de añadir nuevos viajes a la tarjeta, se resta uno.
La única diferencia es que se comprueba el número de viajes para que en el caso de que no tenga más viajes, no permita continuar si no recarga.
El código sería el siguiente.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
// Librerías #include <SPI.h> #include <MFRC522.h> // Pines SPI #define RST_PIN 9 #define SS_PIN 10 // Instancia a la clase MFRC522 MFRC522 mfrc522(SS_PIN, RST_PIN); // Clave de cifrado actuales MFRC522::MIFARE_Key keyA = {keyByte: {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5}}; MFRC522::MIFARE_Key keyB = {keyByte: {0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5}}; // Número de viajes array 16 bytes byte datosBloque[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Datos del sector byte sector = 15; byte numBlque = 60; byte bloqueTrailer = 63; byte numViajes = 10; void mostrarByteArray(byte* buffer, byte bufferSize) { for (byte i = 0; i < bufferSize; i++) { Serial.print(buffer[i] < 0x10 ? " 0" : " "); Serial.print(buffer[i], HEX); } } void setup() { Serial.begin(9600); while (!Serial); // Bucle que no permite continuar hasta que no se ha abierto el monitor serie SPI.begin(); // Iniciar bus SPI mfrc522.PCD_Init(); // Iniciar lector RFID RC522 Serial.print(F("Se van a cargar ")); Serial.print(datosBloque[0]); Serial.println(F(" viajes")); Serial.println(F("Acerca la tarjeta al lector para escanear....")); } void loop() { // Si no hay una tarjeta cerca no sigue el programa if (!mfrc522.PICC_IsNewCardPresent()) { return; } // Si hay una tarjeta cerca, que la eleccione // En caso contrario que no continúe if (!mfrc522.PICC_ReadCardSerial()) { return; } // Mostrar información de la tarjeta por el monitor serie Serial.print(F("UID de la tarjeta:")); mostrarByteArray(mfrc522.uid.uidByte, mfrc522.uid.size); // Motrar el UID Serial.println(); Serial.print(F("Tipo de tarjeta: ")); MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); //Motrar el tipo Serial.println(mfrc522.PICC_GetTypeName(piccType)); // Obtener viajes acutales byte viajesActuales = leerViajes(); Serial.println(""); Serial.print(F("Viajes actuales: ")); Serial.println(viajesActuales, DEC); // Si no queda más viajes que no deje continuar if (viajesActuales == 0) { Serial.println(F("No te quedan más viajes, debes recargar tu tarjeta")); } else { // Restar un viaje viajesActuales = viajesActuales - 1; datosBloque[0] = viajesActuales; // Escribir la información en el bloque int resultadoEb = escribirBloque(); // Dependiendo del resultado if (resultadoEb == 1) { Serial.println(F("No se puede escribir en un bloque Trailer")); } else if (resultadoEb == 2) { Serial.println(F("No se puede escribir en un bloque del fabricante")); } else if (resultadoEb == 3) { Serial.println(F("Problemas al comunicar con la clave proporcionada")); } else if (resultadoEb == 4) { Serial.println(F("Problemas al escribir en el bloque")); } else { Serial.println(F("Puedes entrar, se ha consumido un viaje.")); byte totalViajes = leerViajes(); Serial.println(""); Serial.print(F("Actualmente te quedan: ")); // Leer los datos del bloque Serial.println(totalViajes, DEC); } } // Detener el lector mfrc522.PICC_HaltA(); // Detener la encriptación Crypto1 mfrc522.PCD_StopCrypto1(); Serial.println(); Serial.println(F("Proceso finalizado. Ya puedes retirar la tarjeta del lector RFID")); while (true); } byte escribirBloque() { MFRC522::StatusCode estado; // Comenzar comunicación cifrada con Key-A estado = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloqueTrailer, &keyA, &(mfrc522.uid)); if (estado != MFRC522::STATUS_OK) { return 3; } // Escribir en el bloque Serial.print(F("Bloque numeroBloque: ")); Serial.println(numBlque); estado = mfrc522.MIFARE_Write(numBlque, datosBloque, 16); if (estado != MFRC522::STATUS_OK) { Serial.print("MIFARE_Write() fallo: "); Serial.println(mfrc522.GetStatusCodeName(estado)); return 4; } return 0; } byte leerViajes() { MFRC522::StatusCode estado; byte datosLectura[18]; byte tamBuffer = sizeof(tamBuffer); // Comenzar comunicación cifrada con Key-A estado = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloqueTrailer, &keyA, &(mfrc522.uid)); if (estado != MFRC522::STATUS_OK) { return 1; } // Leer bloque estado = mfrc522.MIFARE_Read(numBlque, datosLectura, &tamBuffer); if (estado != MFRC522::STATUS_OK) { Serial.print("MIFARE_Read() fallo: "); Serial.println(mfrc522.GetStatusCodeName(estado)); return 2; } mostrarByteArray(datosLectura, tamBuffer); return datosLectura[0]; } |
Si cargas el anterior código a la placa y pasas la tarjeta donde hemos cargado 20 viajes, verás como al pasar se reduce en 1 el número de viajes quedando 19.
Para volver a consumir un viaje tienes que reiniciar Arduino pulsando el botón RESET.
Por último, cuando llegues a cero viajes dentro de la tarjeta, no podrás restar más viajes y aparecerá un mensaje indicando que tienes que recargar la tarjeta.
Y con este último caso práctico damos por finalizado este tutorial que espero te haya servido para conocer mejor el lector RFID RC522 o MFRC522.
Has podido comprobar como con este lector y un poco de programación, se puede simular un sistema de billetes de transporte público que no dista mucho de los que se usan en la realidad.
Y es que hacer proyectos utilizando RFID con Arduino no es difícil. Solo hace falta investigar el componente que vas a utilizar y ponerlo en práctica.
Cualquier duda o sugerencia en los comentarios de aquí abajo.
Gracias a Shutterstock por ceder los derechos de la imágenes de este artículo.