Emulador RFID en Arduino



Muchos han leído mi publicación " RFID Emulator ", donde hablé en detalle sobre el dispositivo EM Marine, cómo enrollar una antena y cómo hacer un emulador RFID a partir de tres partes. Pero, seamos honestos, a pesar de la ingeniosa simplicidad de ese dispositivo, es bastante difícil de repetir. No todo el mundo tiene un osciloscopio en casa para captar la resonancia, y se requiere un programador independiente para el firmware ATtiny85.



Por lo tanto, decidí hacer un emulador que incluso un niño pueda repetir. Todos los componentes se venden en casi todos los pueblos. Además, su funcionalidad incluso se puede ampliar. Por ejemplo, puede guardar varias tarjetas en él, o puede agregar otro lector y guardar todas las tarjetas en un dispositivo, o usarlo para ... Entonces, vamos.



Hardware



Como dije, el emulador debe basarse en componentes disponibles que se puedan obtener fácilmente. Primero, veamos el circuito del emulador.







Tenemos un circuito oscilatorio, que cerraremos en un momento determinado con un transistor, y así cambiará la corriente en el lector, y este recibirá los datos transmitidos.

Lo más difícil para nosotros en este paquete es el circuito oscilatorio sintonizado a una frecuencia de 125 kHz. Y hay una solución muy simple de donde puede obtenerla. Hay un lector de etiquetas RFID para el Arduino RDM6300 a la venta . El lector cuesta solo unos centavos, y ya viene con una antena, y el capacitor resonante ya está soldado en la placa. Así, de hecho, solo necesitamos un lector para dos partes: la bobina y el capacitor resonante.





Lector RDM6300 y ubicación del condensador resonante.



Compré este lector por un centavo, que es inconmensurable con el trabajo de enrollar y ajustar una antena. La operación más difícil para nosotros es desoldar este condensador y soldarlo a la placa de circuito. Creo que incluso un alumno de primaria puede afrontarlo.

Como resultado, recopilamos todo en una placa de pruebas. Tengo dos resistencias en paralelo solo porque no tenía resistencias de 10 kOhm a mano, sino solo 20 kOhm.





Circuito ensamblado.



Bueno, veamos de cerca cómo se ve todo. Asigné especialmente una bufanda separada para el condensador, donde se suelda directamente a las agujas de montaje que se insertan en este colchón.





Para verificar el funcionamiento del emulador, inicialmente pensé en usar el mismo RDM6300 (compré dos de ellos). E incluso al principio lo hizo, pero luego decidió que de alguna manera no era grave, una Arduina depurar la otra, y se arruinó con un lector de fábrica.





Lector de fábrica.



Amartillando el temporizador



En mi artículo anterior conté más detalladamente toda la física del proceso y el principio de funcionamiento , por lo que le recomiendo que se familiarice con él. Sin embargo, para entender lo que estoy haciendo, actualizaré un poco algunos puntos.



Permítame recordarle que el EM4102 usa el esquema de codificación Manchester. Cuando se modula el protocolo EM4102, el tiempo de transmisión de un bit puede ser 64, 32 o 16 períodos de la frecuencia portadora (125 kHz).







En pocas palabras, al transmitir un bit, cambiamos el valor de uno a cero (al transmitir cero) o de cero a uno (al transmitir uno). En consecuencia, si elegimos transmitir un bit de información 64 periodos de la frecuencia portadora, entonces para la transmisión de "medio bit" necesitaremos 32 periodos de la frecuencia portadora. Por lo tanto, cada mordisco debería cambiar a un ritmo:



f=125000/32 = 3906,25 
      
      





El período de este "medio bit" será igual a 256 ms.



Ahora necesitamos calcular el temporizador para que sacuda nuestra pierna con una frecuencia determinada. Pero me volví tan perezoso que cuando abrí la hoja de datos y comencé a bostezar, decidí encontrar una solución lista para usar. Y resultó que hay cálculos de temporizador listos para usar, solo ingrese sus datos. Conoce: calculadora de temporizador para Arduino .



Solo necesitamos martillar la frecuencia del temporizador de 3906 Hz, e inmediatamente generaremos un código listo para usar. Bueno, ¿no es un milagro?





Tenga en cuenta que ingresé la frecuencia en su totalidad, y él la contó como fraccional y exactamente la que necesitamos. Obtuve el siguiente código de inicialización del temporizador:



void setupTimer1() {
  noInterrupts();
  // Clear registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // 3906.25 Hz (16000000/((4095+1)*1))
  OCR1A = 4095;
  // Prescaler 1
  TCCR1B |= (1 << CS10);
  // Output Compare Match A Interrupt Enable
  TIMSK1 |= (1 << OCIE1A);
  interrupts();
}
      
      





Brillante, simple, lacónico.



El vector de interrupción para la salida también es muy simple. Permítanme recordarles que necesitamos hacer la transición de uno a cero en el caso de transferir cero, y de cero a uno, en el caso de transferir uno (ver la figura para entenderlo). Por lo tanto, miramos lo que estamos pasando ahora y dónde estamos en el "medio bit", leyendo gradualmente todos los datos de la matriz de datos.



ISR(TIMER1_COMPA_vect) {
        TCNT1=0;
	if (((data[byte_counter] << bit_counter)&0x80)==0x00) {
	    if (half==0) digitalWrite(ANTENNA, LOW);
	    if (half==1) digitalWrite(ANTENNA, HIGH);
	}
	else {
	    if (half==0) digitalWrite(ANTENNA, HIGH);
	    if (half==1) digitalWrite(ANTENNA, LOW);
	}
    
	half++;
	if (half==2) {
	    half=0;
	    bit_counter++;
	    if (bit_counter==8) {
	        bit_counter=0;
	        byte_counter=(byte_counter+1)%8;
		}
	}
}
      
      





Traducir datos para su transmisión



Aquí, también, debe actualizar la memoria de los formatos de datos almacenados en la tarjeta. La forma en que están escritos. Tomemos un ejemplo vivo.



Supongamos que tenemos una tarjeta, pero no un lector. El número 010.48351 está escrito en la tarjeta .





Una tarjeta real con el número 010, 48351.



¿Cómo podemos traducir este número al número de serie que está escrito en la tarjeta? Suficientemente simple. Recuerda la fórmula: traducimos las dos partes del número por separado:



010d = 0xA
48351d = 0xBCDF
      
      





Entonces, obtenemos el número de serie: 0xABCDF. Comprobémoslo, leemos la tarjeta con un lector (lee en formato decimal), obtenemos un número:



0000703711
      
      





Lo traducimos a formato hexadecimal con cualquier calculadora y obtenemos nuevamente: 0xABCDF.

Hasta ahora parece simple, espera, ahora tienes que esforzar tu cerebro. Permítanme recordarles el formato de los datos que se encuentran en la propia tarjeta.





Lo pondré en palabras:



  1. Hay nueve unidades de encabezado al principio.
  2. ID de cliente de medio byte más bajo.
  3. Al final del bit de paridad.
  4. La segunda mitad del byte es la identificación del cliente.
  5. Bit de paridad.
  6. El medio byte menos significativo del byte cero del número de serie.
  7. Bit de paridad
  8. .
  9. ,
  10. . 10 ( ).
  11. , .


En total, obtenemos 64 bits de datos (¡eso es cinco bytes!). Como nota al margen, mi lector no lee el ID del cliente y lo acepto como cero.



¿Qué es un bit de paridad? Este es el número de unos en el paquete: si es par, entonces el bit de paridad es cero, si no, entonces uno. La forma más sencilla de calcularlo es simplemente un XOR normal.



De hecho, pensé durante mucho tiempo cómo hacer más elegante la conversión del número de serie en un paquete, para que ocupe menos espacio en el microcontrolador. Por lo tanto, esbocé un pequeño programa que hace esto. El programa se puede ver debajo del spoiler.



Programa de prueba para traducir serie en datos
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte)  \
  (byte & 0x80 ? '1' : '0'), \
  (byte & 0x40 ? '1' : '0'), \
  (byte & 0x20 ? '1' : '0'), \
  (byte & 0x10 ? '1' : '0'), \
  (byte & 0x08 ? '1' : '0'), \
  (byte & 0x04 ? '1' : '0'), \
  (byte & 0x02 ? '1' : '0'), \
  (byte & 0x01 ? '1' : '0') 

#define NYBBLE_TO_BINARY_PATTERN "%c%c%c%c"
#define NYBBLE_TO_BINARY(byte)  \
	(byte & 0x08 ? '1' : '0'), \
	(byte & 0x04 ? '1' : '0'), \
	(byte & 0x02 ? '1' : '0'), \
	(byte & 0x01 ? '1' : '0') 


int main() {
	//unsigned long long card_id = 0x00000ABCDF;
	//uint64_t card_id = 0x00000ABCDF;
	uint64_t card_id = (uint64_t)3604000;
	uint64_t data_card_ul = 0x1FFF; //first 9 bit as 1
	int32_t i;
	uint8_t tmp_nybble;
	uint8_t column_parity_bits = 0;
	printf("card_id = 0x%lX\n", card_id);
	for (i = 9; i >= 0; i--) { //5 bytes = 10 nybbles
		tmp_nybble = (uint8_t) (0x0f & (card_id >> i*4));
		data_card_ul = (data_card_ul << 4) | tmp_nybble;
		printf("0x%02X", (int) tmp_nybble);
		printf("\t"NYBBLE_TO_BINARY_PATTERN, NYBBLE_TO_BINARY(tmp_nybble));
		printf("\t %d\n", (tmp_nybble >> 3 & 0x01) ^ (tmp_nybble >> 2 & 0x01) ^\
			(tmp_nybble >> 1 & 0x01) ^ (tmp_nybble  & 0x01));
		data_card_ul = (data_card_ul << 1) | ((tmp_nybble >> 3 & 0x01) ^ (tmp_nybble >> 2 & 0x01) ^\
			(tmp_nybble >> 1 & 0x01) ^ (tmp_nybble  & 0x01));
		column_parity_bits ^= tmp_nybble;
	}
	data_card_ul = (data_card_ul << 4) | column_parity_bits;
	data_card_ul = (data_card_ul << 1); //1 stop bit = 0
	printf("\t"NYBBLE_TO_BINARY_PATTERN"\n", NYBBLE_TO_BINARY(column_parity_bits));
	printf("data_card_ul = 0x%lX\n", data_card_ul);
	
	for (i = 7; i >= 0; i--) {
		printf("0x%02X,", (int) (0xFF & (data_card_ul >> i * 8)));
	}
	printf("\n");
	return 0;
}

      
      





Lo más importante para nosotros es cómo se verán los bits de paridad. Por conveniencia, hice la salida a la pantalla exactamente de la misma manera que en esta placa. Como resultado, resultó así.





card_id es el número de serie de la tarjeta (del que hablamos anteriormente).



La primera columna son los nibls, la segunda es su representación de bits, la tercera es el bit de paridad. La tercera línea desde la parte inferior son los bits de paridad de todos los nibls. Como dije, se calculan simplemente mediante XOR.



Habiendo probado los cálculos, habiéndolos verificado visualmente, verifiqué los datos resultantes en el programa en Arduino (la última línea es especialmente para insertar en el código). Todo funcionó bien. Como resultado de dibujar este programa, obtuve una función de recálculo lista para usar. Anteriormente, los cálculos de ritmos eran programas de otra persona en una computadora y no me gustaba su monstruosa implementación. Por lo tanto, la función de convertir el número de serie al formato de transmisión se ve así:




#define CARD_ID 0xABCDF

uint8_t data[8];

void data_card_ul() {
  uint64_t card_id = (uint64_t)CARD_ID;
  uint64_t data_card_ul = (uint64_t)0x1FFF; //first 9 bit as 1
  int32_t i;
  uint8_t tmp_nybble;
  uint8_t column_parity_bits = 0;
  for (i = 9; i >= 0; i--) { //5 bytes = 10 nybbles
    tmp_nybble = (uint8_t) (0x0f & (card_id >> i*4));
    data_card_ul = (data_card_ul << 4) | tmp_nybble;
    data_card_ul = (data_card_ul << 1) | ((tmp_nybble >> 3 & 0x01) ^ (tmp_nybble >> 2 & 0x01) ^\
      (tmp_nybble >> 1 & 0x01) ^ (tmp_nybble  & 0x01));
    column_parity_bits ^= tmp_nybble;
  }
  data_card_ul = (data_card_ul << 4) | column_parity_bits;
  data_card_ul = (data_card_ul << 1); //1 stop bit = 0
  for (i = 0; i < 8; i++) {
    data[i] = (uint8_t)(0xFF & (data_card_ul >> (7 - i) * 8));
  }
}
      
      





Todo, puedes proceder a las pruebas de campo. El código fuente del proyecto vive aquí .



Pruebas



Como dicen, es mejor ver una vez que leer mil veces. Especialmente para ti, grabé una película sobre el trabajo de este emulador. Quería probarlo en hardware real e intentar ingresar a la oficina usando Arduino, pero con la maldita pandemia, no están permitidos allí. Por lo tanto, las pruebas a gran escala deberán examinarse sobre la mesa, en condiciones de laboratorio.





conclusiones



Realmente espero que estos artículos animen a los novatos a aprender programación y electrónica. Y también contribuirán a la retirada del mercado de este tipo de tarjetas, por ser las más desprotegidas e inseguras, ya que ahora hasta un niño puede copiarlas y emularlas.



Expreso mi gratitud a Michal Krumnikl por su paciencia hace muchos, muchos años, cuando me explicó en icq el funcionamiento de dicho emulador, así como su ayuda con el desarrollo del código. En cierto sentido, estas son sus ideas y desarrollos hace 13 años.



Enlaces












All Articles