RFID-Emulator auf Arduino



Viele haben meinen Beitrag " RFID-Emulator " gelesen , in dem ich ausführlich über das EM Marine-Gerät sprach, wie man eine Antenne aufwickelt und wie man einen RFID-Emulator aus drei Teilen herstellt. Aber seien wir ehrlich, trotz der genialen Einfachheit dieses Geräts ist es schwierig genug, es zu wiederholen. Nicht jeder hat zu Hause ein Oszilloskop, um die Resonanz zu erfassen, und für die ATtiny85-Firmware ist ein separater Programmierer erforderlich.



Deshalb habe ich mich für einen solchen Emulator entschieden, den auch ein Kind wiederholen kann. Alle Komponenten werden in fast jedem Dorf verkauft. Darüber hinaus kann seine Funktionalität sogar erweitert werden. Sie können beispielsweise mehrere Karten darin speichern oder einen weiteren Leser hinzufügen und alle Karten auf einem Gerät speichern oder für ... Also, los geht's.



Hardware



Wie gesagt, der Emulator sollte auf verfügbaren Komponenten basieren, die leicht erhältlich sind. Schauen wir uns zunächst die Emulatorschaltung an.







Wir haben einen Schwingkreis, den wir zu einem bestimmten Zeitpunkt mit einem Transistor schließen, und somit ändert sich der Strom im Lesegerät und er empfängt die übertragenen Daten.

Das Schwierigste für uns in diesem Bündel ist der auf eine Frequenz von 125 kHz abgestimmte Schwingkreis. Und es gibt eine sehr einfache Lösung, von der Sie sie erhalten können. Für den Arduino RDM6300 steht ein RFID-Tag-Reader zum Verkauf . Der Leser kostet nur ein paar Cent, und er wird bereits mit einer Antenne geliefert, und der Resonanzkondensator ist bereits auf die Platine gelötet. Wir brauchen also nur einen Leser für zwei Teile: die Spule und den Resonanzkondensator.





Position des RDM6300-Lesegeräts und des Resonanzkondensators.



Ich habe diesen Reader für einen Cent gekauft, was nicht mit dem Aufziehen und Einstellen einer Antenne vereinbar ist. Für uns ist es am schwierigsten, diesen Kondensator zu löten und auf die Leiterplatte zu löten. Ich glaube, dass sogar ein Grundschüler damit umgehen kann.

Als Ergebnis sammeln wir alles auf einem Steckbrett. Ich habe nur zwei Widerstände parallel, weil ich keine 10 kOhm Widerstände zur Hand hatte, sondern nur 20 kOhm.





Zusammengebaute Schaltung.



Nun, lassen Sie uns in Nahaufnahme sehen, wie alles aussieht. Ich habe speziell einen separaten Schal für den Kondensator zugewiesen, der direkt auf die Befestigungsnadeln gelötet wird, die in diese Matratze eingesetzt werden.





Um den Emulator zu überprüfen, dachte ich zunächst daran, dasselbe RDM6300 zu verwenden (ich habe zwei davon gekauft). Und selbst zuerst tat er es, aber dann entschied er, dass es irgendwie nicht ernst war, eine Arduina, um die andere zu debuggen, und ging auf einen Fabrikleser los.





Werksleser.



Den Timer spannen



In meinem vorherigen Artikel habe ich die gesamte Physik des Prozesses und das Funktionsprinzip am ausführlichsten beschrieben . Ich empfehle daher dringend, dass Sie sich damit vertraut machen. Um jedoch zu verstehen, was ich tue, werde ich einige Punkte ein wenig auffrischen.



Ich möchte Sie daran erinnern, dass der EM4102 das Manchester-Codierungsschema verwendet. Wenn das EM4102-Protokoll moduliert wird, kann die Übertragungszeit eines Bits 64, 32 oder 16 Perioden der Trägerfrequenz (125 kHz) betragen.







Einfach ausgedrückt, ändern wir beim Senden eines Bits den Wert von Eins auf Null (beim Senden von Null) oder von Null auf Eins (beim Senden von Eins). Wenn wir uns dafür entscheiden, ein Informationsbit mit 64 Perioden der Trägerfrequenz zu übertragen, benötigen wir für die Übertragung von "Halbbit" 32 Perioden der Trägerfrequenz. Daher sollte sich jedes Knabbern mit einer Geschwindigkeit ändern:



f=125000/32 = 3906,25 
      
      





Die Periode dieses "Halbbits" beträgt 256 ms.



Jetzt müssen wir den Timer so berechnen, dass er unser Bein mit einer bestimmten Frequenz ruckelt. Aber ich wurde so faul, dass ich mich entschied, eine vorgefertigte Lösung zu finden, als ich das Datenblatt öffnete und anfing zu gähnen. Und es stellte sich heraus, dass es vorgefertigte Timer-Berechnungen gibt. Geben Sie einfach Ihre Daten ein. Treffen: Timer-Rechner für Arduino .



Wir müssen nur die Timerfrequenz von 3906 Hz hämmern und generieren sofort einen gebrauchsfertigen Code. Ist das nicht ein Wunder?





Bitte beachten Sie, dass ich die Frequenz als Ganzes eingegeben habe und er sie als Bruch und genau diejenige gezählt hat, die wir brauchen. Ich habe den folgenden Timer-Initialisierungscode erhalten:



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();
}
      
      





Genial, einfach, prägnant.



Der Interruptvektor für die Ausgabe ist ebenfalls sehr einfach. Ich möchte Sie daran erinnern, dass wir bei der Übertragung von Null den Übergang von Eins zu Null und bei der Übertragung von Eins von Null auf Eins vornehmen müssen (siehe Abbildung zum Verständnis). Daher schauen wir uns an, was wir gerade übergeben und wo wir uns im „Halbbit“ befinden, und lesen nach und nach alle Daten aus dem Datenarray.



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;
		}
	}
}
      
      





Daten für die Übertragung übersetzen



Auch hier sollten Sie den Speicher der auf der Karte gespeicherten Datenformate aktualisieren. Die Art, wie sie geschrieben sind. Nehmen wir ein Live-Beispiel.



Angenommen, wir haben eine Karte, aber keinen Leser. Die Nummer 010.48351 steht auf der Karte .





Eine echte Karte mit der Nummer 010, 48351.



Wie können wir diese Nummer in die Seriennummer übersetzen, die auf der Karte steht? Einfach genug. Denken Sie an die Formel: Wir übersetzen die beiden Teile der Zahl getrennt:



010d = 0xA
48351d = 0xBCDF
      
      





Wir erhalten also die Seriennummer: 0xABCDF. Lassen Sie es uns überprüfen, die Karte mit einem Lesegerät lesen (es liest im Dezimalformat), wir erhalten eine Zahl:



0000703711
      
      





Wir übersetzen es mit jedem Taschenrechner ins Hex-Format und erhalten erneut: 0xABCDF.

Es scheint so einfach zu sein, warte, jetzt musst du dein Gehirn belasten. Ich möchte Sie an das Format der Daten erinnern, die sich auf der Karte selbst befinden.





Ich werde es in Worte fassen:



  1. Am Anfang stehen neun Überschrifteneinheiten.
  2. Niedrigste Halbbyte-Client-ID.
  3. Am Ende des Paritätsbits.
  4. Die zweite Hälfte des Bytes ist die Client-ID.
  5. Paritätsbit.
  6. Das niedrigstwertige halbe Byte des Nullbytes der Seriennummer.
  7. Paritätsbit
  8. .
  9. ,
  10. . 10 ( ).
  11. , .


Insgesamt erhalten wir 64 Datenbits (das sind fünf Bytes!). Als Randnotiz liest mein Leser die Client-ID nicht und ich akzeptiere sie als Null.



Was ist ein Paritätsbit? Dies ist die Anzahl der Einsen im Paket: Wenn es gerade ist, ist das Paritätsbit Null, wenn nicht, dann Eins. Der einfachste Weg, dies zu berechnen, ist nur ein regulärer XOR.



Tatsächlich habe ich lange darüber nachgedacht, wie die Umwandlung der Seriennummer in ein Paket eleganter gestaltet werden kann, damit weniger Platz im Mikrocontroller benötigt wird. Deshalb habe ich ein kleines Programm entworfen, das dies tut. Das Programm kann unter dem Spoiler angezeigt werden.



Testprogramm zur Übersetzung von Serien in Daten
#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;
}

      
      





Das Wichtigste für uns ist, wie die Paritätsbits aussehen werden. Der Einfachheit halber habe ich die Ausgabe auf dem Bildschirm genauso wie auf dieser Platte vorgenommen. Infolgedessen stellte sich heraus, dass.





card_id ist die Seriennummer der Karte (über die wir oben gesprochen haben).



Die erste Spalte sind die Nibls, die zweite ist ihre Bitdarstellung, die dritte ist das Paritätsbit. Die dritte Zeile von unten sind die Paritätsbits aller Nibls. Wie gesagt, sie werden einfach von XOR berechnet.



Nachdem ich die Berechnungen getestet und visuell überprüft hatte, überprüfte ich die resultierenden Daten im Programm auf Arduino (die letzte Zeile dient speziell zum Einfügen in den Code). Alles hat perfekt funktioniert. Als Ergebnis des Skizzierens dieses Programms erhielt ich eine vorgefertigte Neuberechnungsfunktion. Früher waren Beat-Berechnungen die Computerprogramme anderer, und ich mochte ihre monströse Implementierung nicht. Die Funktion zum Konvertieren der Seriennummer in das Übertragungsformat sieht also folgendermaßen aus:




#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));
  }
}
      
      





Alles, Sie können mit Feldtests fortfahren. Der Quellcode des Projekts lebt hier .



Tests



Wie sie sagen, ist es besser, einmal zu sehen, als tausendmal zu lesen. Speziell für Sie habe ich einen Film über die Arbeit dieses Emulators aufgenommen. Ich wollte es auf echter Hardware testen und versuchen, mit Arduino ins Büro zu kommen, aber mit der verdammten Pandemie sind sie dort nicht erlaubt. Daher müssen Tests in vollem Umfang unter Laborbedingungen auf dem Tisch betrachtet werden.





Schlussfolgerungen



Ich hoffe wirklich, dass solche Artikel Neulinge zum Erlernen von Programmierung und Elektronik anregen. Und sie werden auch dazu beitragen, dass diese Art von Karten als die ungeschütztesten und unsichersten vom Markt genommen werden, da jetzt sogar ein Kind sie kopieren und emulieren kann.



Ich bedanke mich bei Michal Krumnikl für seine Geduld vor vielen, vielen Jahren, als er mir auf icq die Funktionsweise eines solchen Emulators erklärte und bei der Entwicklung des Codes half. In gewisser Weise sind dies seine Ideen und Entwicklungen vor 13 Jahren.



Links












All Articles