En este post te voy a mostrar cómo guardar información en las tarjetas o etiquetas RFID usando el lector MFRC522 y un Arduino o NodeMCU ESP8266.
Anteriormente ya te mostré cómo leer el serial de estas etiquetas, pero ahora te voy a enseñar a escribir y leer información dentro de ellas.
Gracias a esto puedes crear infinidad de aplicaciones como una tarjeta de crédito, monedero electrónico, control de entrada para el transporte público, etcétera.
Te voy a mostrar cómo leer y escribir datos usando la NodeMCU ESP8266 pero teóricamente puedes usar cualquier Arduino, ya sea el UNO, Mega, etcétera; lo único que cambia es la conexión, pues el código se queda casi intacto.
Sobre la seguridad
En mi caso siempre utilizo mis proyectos como un pasatiempo, aunque bien esto se puede llevar a la práctica. Solo te recomiendo algo: cambia la clave de las tarjetas, así solo tú la vas a conocer y nadie podrá modificar los datos.
Por otro lado, yo no guardaría datos en la RFID, solo asociaría el serial al servidor, y llevaría el dato (ya sea saldo, crédito restante, etcétera) en el servidor, de modo que no se pueda modificar directamente en la tarjeta.
Al final queda todo en ti, pero te aviso las posibles implicaciones. De igual modo es más simple guardar y escribir datos en la tarjeta, que tener una autenticación con el servidor y cosas de esas.
También te invito a leer lo que dice el autor de la librería que permite usar estas tarjetas.
Almacenar información en tarjetas RFID
Vamos a usar las funciones MIFARE_Write
y MIFARE_Read
para escribir y leer información respectivamente, usando la librería para el lector MMFRC522 también conocido como RFID-RC522.
Esta operación puede ser ejecutada al presionar un botón, recibir una petición HTTP, etcétera. Para ejemplos simples, en los ejemplos voy a escribir y leer tan pronto la tarjeta se acerque al lector. El circuito queda así:
Te repito: puedes usar un Arduino si quieres, solo cambia la conexión y ya.
Por cierto, te invito encarecidamente a que primero leas el tutorial sobre cómo leer el serial de las etiquetas RFID; está más detallado y contiene pasos que me saltaré aquí por simplicidad.
Setup
En el setup del código vamos a iniciar el lector y preparar la clave. Si tú ya cambiaste la clave entonces cámbiala en el código.
void setup()
{
Serial.begin(9600);
while (!Serial)
{
// Esperar serial. Nota: la tarjeta NO HARÁ NADA hasta que haya comunicación Serial (es decir, que el monitor serial sea abierto)
// si tú no quieres esto, simplemente elimina todas las llamadas a Serial
}
// Iniciar lector
SPI.begin();
lector.PCD_Init();
// Preparar la clave para leer las tarjetas RFID
for (byte i = 0; i < 6; i++)
{
clave.keyByte[i] = 0xFF;
}
Serial.println("Iniciado correctamente");
}
Leer datos de RFID
Veamos cómo leer los datos almacenados. La primera vez puede que encuentres bytes raros o cadenas raras. Para que el uso sea fácil, escribí una función que escribe una cadena en el bloque.
bool leer(char mensaje[LONGITUD_BYTES])
{
if (!lector.PICC_IsNewCardPresent())
{
return false;
}
if (!lector.PICC_ReadCardSerial())
{
Serial.println("Error leyendo serial");
return false;
}
byte bloque = 1; // El bloque que leemos
byte longitud = LONGITUD_BYTES;
byte buferLectura[LONGITUD_BYTES];
MFRC522::StatusCode estado;
estado = lector.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloque, &clave, &(lector.uid));
if (estado != MFRC522::STATUS_OK)
{
Serial.println("Error autenticando");
Serial.println(lector.GetStatusCodeName(estado));
return false;
}
estado = lector.MIFARE_Read(bloque, buferLectura, &longitud);
if (estado != MFRC522::STATUS_OK)
{
Serial.println("Error leyendo bloque");
Serial.println(lector.GetStatusCodeName(estado));
return false;
}
for (uint8_t i = 0; i < LONGITUD_BYTES - 2; i++)
{
mensaje[i] = buferLectura[i];
}
// Ya pueden retirar la tarjeta
lector.PICC_HaltA();
lector.PCD_StopCrypto1();
return true;
}
¿Por qué una cadena? fácil, de este modo puedes guardar cualquier tipo de dato. Por ejemplo, si quisieras guardar el saldo o dinero de un usuario simplemente convertirías el número a cadena con snprintf
y luego guardarías esa cadena. Lo mismo para el proceso inverso.
No olvides que las cadenas (al menos en C) usan un byte extra para guardar el byte de terminación \0
, por lo que la cadena “Hola” en realidad pesa 5 bytes. Te digo esto porque el límite de escritura es de 16 bytes, así que solo puedes guardar 15 caracteres por bloque.
La función va a devolver false en caso de que no haya podido leer (esto puede ser porque no hay tarjeta presente, por ejemplo) y true en caso de que todo sea exitoso.
El modo de uso es:
char contenidoRfid[LONGITUD_BYTES] = "";
bool lecturaExitosa = leer(contenidoRfid);
if (lecturaExitosa)
{
Serial.println("Lo que hay escrito es:");
Serial.println(contenidoRfid);
}
else
{
Serial.println("Error leyendo. Tal vez no hay RFID presente");
}
Fíjate en que primero declaro la cadena y luego se la paso a la función, esto es debido a que la función que lee los datos de RFID no devolverá la información que se haya guardado, sino que va a colocar la información dentro de la cadena que le pasemos.
Escribir datos RFID
Ahora veamos cómo escribir. En este caso la función recibe igualmente una cadena para escribirla. Queda así:
bool escribir(char cadena[LONGITUD_BYTES_ESCRITURA])
{
if (!lector.PICC_IsNewCardPresent())
{
return false;
}
if (!lector.PICC_ReadCardSerial())
{
Serial.println("Error leyendo serial");
return false;
}
byte bloque = 1;
byte buferEscritura[LONGITUD_BYTES_ESCRITURA];
// Copiar cadena al búfer
for (uint8_t i = 0; i < LONGITUD_BYTES_ESCRITURA; i++)
{
buferEscritura[i] = cadena[i];
}
MFRC522::StatusCode estado;
estado = lector.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloque, &clave, &(lector.uid));
if (estado != MFRC522::STATUS_OK)
{
Serial.println("Error autenticando");
Serial.println(lector.GetStatusCodeName(estado));
return false;
}
estado = lector.MIFARE_Write(bloque, buferEscritura, LONGITUD_BYTES_ESCRITURA);
if (estado != MFRC522::STATUS_OK)
{
Serial.println("Error escribiendo bloque");
Serial.println(lector.GetStatusCodeName(estado));
return false;
}
// Ya pueden retirar la tarjeta
lector.PICC_HaltA();
lector.PCD_StopCrypto1();
return true;
}
De manera parecida, la función devolverá un booleano. El modo de uso es:
char mensaje[] = "parzibyte";
bool escrituraExitosa = escribir(mensaje);
if (escrituraExitosa)
{
Serial.println("Escrito ok");
}
else
{
Serial.println("Error escribiendo. Tal vez no hay RFID presente");
}
Primero estoy declarando la cadena en el estilo de C (si quieres declarar la cadena vacía hazlo con char mensaje[] = ""
). Luego invoco a la función y dependiendo de ello indico si todo ha ido bien.
Te repito que esta función devolverá false si hay errores de autenticación o si la tarjeta no está presente en el lector.
Poniendo todo junto
El código completo que muestra ejemplos de cómo escribir y leer información en tarjetas RFID usando el MFRC522 y Arduino o ESP8266 lo dejo en GitHub. El loop queda así:
void loop()
{
if (MODO == MODO_LECTURA)
{
char contenidoRfid[LONGITUD_BYTES] = "";
bool lecturaExitosa = leer(contenidoRfid);
if (lecturaExitosa)
{
Serial.println("Lo que hay escrito es:");
Serial.println(contenidoRfid);
}
else
{
Serial.println("Error leyendo. Tal vez no hay RFID presente");
}
}
else if (MODO == MODO_ESCRITURA)
{
char mensaje[] = "parzibyte";
bool escrituraExitosa = escribir(mensaje);
if (escrituraExitosa)
{
Serial.println("Escrito ok");
}
else
{
Serial.println("Error escribiendo. Tal vez no hay RFID presente");
}
}
delay(1000);
}
Estoy comparando los dos modos solo para ejemplificar; obviamente en la vida real tú puedes invocar esas funciones en donde prefieras.
Por cierto, el delay
es para que el mensaje de error no aparezca varias veces en caso de que no haya tarjeta presente.
Para terminar te dejo con más tutoriales de Arduino y Electrónica.