Emulator RFID di Arduino



Banyak yang telah membaca posting saya " RFID Emulator ", di mana saya berbicara secara detail tentang perangkat EM Marine, cara memutar antena, dan cara membuat emulator RFID dari tiga bagian. Tapi, jujur ​​saja, meskipun perangkat itu sangat sederhana, cukup sulit untuk diulang. Tidak semua orang memiliki osiloskop di rumah, untuk menangkap resonansi, dan programmer terpisah diperlukan untuk firmware ATtiny85.



Oleh karena itu, saya memutuskan untuk membuat emulator yang bahkan anak kecil pun dapat mengulang. Semua komponen dijual di hampir setiap desa. Apalagi fungsinya bahkan bisa diperluas. Misalnya, Anda dapat menyimpan beberapa kartu di dalamnya, atau Anda dapat menambahkan pembaca lain dan menyimpan semua kartu di satu perangkat, atau menggunakannya untuk ... Jadi, ayo pergi.



Perangkat keras



Seperti yang saya katakan, emulator harus dibangun di atas komponen yang tersedia yang dapat diperoleh dengan mudah. Pertama, mari kita lihat rangkaian emulator.







Kami memiliki rangkaian osilasi, yang akan kami tutup pada waktu tertentu dengan transistor, dan dengan demikian arus di pembaca akan berubah, dan itu akan menerima data yang ditransmisikan.

Hal tersulit bagi kami dalam bundel ini adalah rangkaian osilasi yang disetel ke frekuensi 125 kHz. Dan ada solusi yang sangat sederhana di mana Anda bisa mendapatkannya. Ada pembaca tag RFID untuk Arduino RDM6300 yang dijual . Pembaca hanya membutuhkan biaya sen, dan sudah dilengkapi dengan antena, dan kapasitor resonan sudah disolder di papan tulis. Jadi, sebenarnya kita hanya membutuhkan pembaca untuk dua bagian: kumparan dan kapasitor resonan.





Pembaca RDM6300 dan lokasi kapasitor resonan.



Saya membeli alat pembaca ini dengan harga beberapa sen, yang tidak sebanding dengan pekerjaan memutar dan menyetel antena. Operasi tersulit bagi kami adalah melepas solder kapasitor ini dan menyoldernya ke papan sirkuit. Saya percaya bahwa bahkan seorang siswa sekolah dasar pun dapat mengatasinya.

Hasilnya, kami mengumpulkan semuanya di papan tempat memotong roti. Saya memiliki dua resistor secara paralel hanya karena saya tidak memiliki resistor 10 kOhm, tetapi hanya 20 kOhm.





Sirkuit rakitan.



Baiklah, mari kita lihat dari dekat bagaimana semuanya terlihat. Saya secara khusus mengalokasikan syal terpisah untuk kapasitor, di mana ia disolder langsung ke jarum pemasangan yang dimasukkan ke dalam kasur ini.





Untuk menguji emulator, awalnya saya berpikir untuk menggunakan RDM6300 yang sama (saya membeli dua di antaranya). Dan bahkan pada awalnya dia melakukannya, tetapi kemudian dia memutuskan bahwa itu tidak serius, untuk men-debug satu Arduina dengan yang lain, dan menjadi bangkrut pada pembaca pabrik.





Pembaca pabrik.



Memiringkan pengatur waktu



Saya memberi tahu sepenuhnya semua fisika proses dan prinsip operasi di artikel saya sebelumnya , jadi saya sangat menyarankan agar Anda membiasakan diri dengannya. Namun, untuk memahami apa yang saya lakukan, saya akan sedikit menyegarkan beberapa poin.



Izinkan saya mengingatkan Anda bahwa EM4102 menggunakan skema pengkodean Manchester. Ketika protokol EM4102 dimodulasi, waktu transmisi satu bit bisa 64, 32 atau 16 periode frekuensi pembawa (125 kHz).







Sederhananya, saat mentransmisikan satu bit, kami mengubah nilai salah satu menjadi nol (saat mentransmisikan nol), atau dari nol ke satu (saat mentransmisikan satu). Dengan demikian, jika kita memilih untuk mengirimkan satu bit informasi 64 periode frekuensi pembawa, maka untuk transmisi "setengah-bit" kita memerlukan 32 periode frekuensi pembawa. Jadi, setiap camilan harus berubah dengan cepat:



f=125000/32 = 3906,25 
      
      





Periode "setengah-bit" ini akan sama dengan 256 ms.



Sekarang kita perlu menghitung pengatur waktu sehingga menyentak kaki kita dengan frekuensi tertentu. Tetapi saya menjadi sangat malas sehingga ketika saya membuka lembar data dan mulai menguap, saya memutuskan untuk mencari solusi yang siap pakai. Dan ternyata ada penghitungan pengatur waktu yang siap pakai, cukup masukkan data Anda. Bertemu: kalkulator pengatur waktu untuk Arduino .



Kita hanya perlu mengatur frekuensi pengatur waktu ke 3906 Hz, dan kita akan segera menghasilkan kode yang siap digunakan. Nah, bukankah itu keajaiban!





Harap dicatat bahwa saya memasukkan frekuensi secara keseluruhan, dan dia menghitungnya sebagai pecahan dan persis seperti yang kami butuhkan. Saya mendapat kode inisialisasi timer berikut:



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





Cemerlang, sederhana, singkat.



Vektor interupsi untuk keluaran juga sangat sederhana. Izinkan saya mengingatkan Anda bahwa kita perlu melakukan transisi dari satu ke nol dalam kasus mentransfer nol, dan dari nol ke satu, dalam kasus mentransfer satu (lihat gambar untuk pemahaman). Oleh karena itu, kami melihat apa yang kami lewati sekarang dan di mana kami berada di "setengah-bit", secara bertahap membaca semua data dari larik data.



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





Menerjemahkan data untuk transmisi



Di sini, Anda juga harus menyegarkan memori dari format data yang disimpan di kartu. Cara penulisannya. Mari kita ambil contoh langsung.



Misalkan kita punya kartu, tapi tidak ada pembaca. Nomor 010.48351 tertulis di kartu .





Kartu asli dengan nomor 010, 48351.



Bagaimana kita bisa menerjemahkan nomor ini menjadi nomor seri yang tertulis di kartu? Cukup sederhana. Ingat rumusnya: kami menerjemahkan dua bagian angka secara terpisah:



010d = 0xA
48351d = 0xBCDF
      
      





Jadi, kami mendapatkan nomor seri: 0xABCDF. Mari kita periksa, baca kartu dengan pembaca (terbaca dalam format desimal), kami mendapatkan nomor:



0000703711
      
      





Kami menerjemahkannya ke dalam format hex dengan kalkulator apa pun dan mendapatkan lagi: 0xABCDF.

Tampaknya sejauh ini sederhana, tunggu, sekarang Anda harus membebani otak Anda. Izinkan saya mengingatkan Anda tentang format data yang ada di kartu itu sendiri.





Saya akan menjelaskannya dengan kata-kata:



  1. Ada sembilan unit heading di awal.
  2. ID klien setengah bita terendah.
  3. Di akhir bit paritas.
  4. Paruh kedua byte tersebut adalah ID klien.
  5. Bit paritas.
  6. Setengah byte paling tidak signifikan dari nol byte nomor seri.
  7. Bit paritas
  8. .
  9. ,
  10. . 10 ( ).
  11. , .


Secara total, kami mendapatkan 64 bit data (itu lima byte!). Sebagai catatan tambahan, pembaca saya tidak membaca ID klien, dan saya menerimanya sebagai nol.



Apa itu bit paritas? Ini adalah jumlah satuan dalam paket: jika genap, maka bit paritasnya adalah nol, jika tidak, maka satu. Cara termudah untuk menghitungnya hanyalah XOR biasa.



Malah sudah lama terpikir bagaimana cara mengonversi serial number menjadi paket lebih elegan, sehingga memakan sedikit space di mikrokontroler. Oleh karena itu, saya membuat sketsa program kecil yang melakukan ini. Program tersebut dapat dilihat di bawah spoiler.



Program uji untuk menerjemahkan serial menjadi data
#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;
}

      
      





Yang terpenting bagi kami adalah seperti apa bit paritas itu. Untuk kenyamanan, saya membuat keluaran ke layar dengan cara yang persis sama seperti di pelat ini. Hasilnya seperti ini.





card_id adalah nomor seri kartu (yang kita bicarakan di atas).



Kolom pertama adalah nibl, yang kedua adalah representasi bitnya, yang ketiga adalah bit paritas. Baris ketiga dari bawah adalah bit paritas dari semua nibl. Seperti yang saya katakan, mereka dihitung hanya dengan XOR.



Setelah menguji perhitungan, setelah memeriksanya secara visual, saya memeriksa data yang dihasilkan dalam program di Arduino (baris terakhir khusus untuk dimasukkan ke dalam kode). Semuanya bekerja dengan baik. Sebagai hasil dari membuat sketsa program ini, saya mendapatkan fungsi penghitungan ulang yang sudah jadi. Sebelumnya, perhitungan ketukan adalah program orang lain di komputer dan saya tidak suka penerapannya yang mengerikan. Jadi, fungsi pengubahan serial number menjadi format transmisi terlihat seperti ini:




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





Semuanya, Anda bisa melanjutkan ke uji lapangan. Kode sumber proyek ada di sini .



Tes



Seperti yang mereka katakan, lebih baik melihat sekali daripada membaca seribu kali. Khusus untuk Anda, saya merekam film tentang kerja emulator ini. Saya ingin mengujinya pada perangkat keras asli, dan mencoba masuk ke kantor menggunakan Arduino, tetapi dengan pandemi terkutuk, mereka tidak diizinkan di sana. Oleh karena itu, pengujian skala penuh harus dilihat di atas meja, dalam kondisi laboratorium.





kesimpulan



Saya sangat berharap artikel seperti ini akan memacu para pemula untuk belajar pemrograman dan elektronika. Dan mereka juga akan berkontribusi pada keluarnya pasar dari jenis kartu ini, sebagai yang paling tidak terlindungi dan tidak aman, karena sekarang bahkan seorang anak dapat menyalin dan meniru mereka.



Saya mengucapkan terima kasih kepada Michal Krumnikl atas kesabarannya bertahun-tahun yang lalu, ketika dia menjelaskan kepada saya tentang pengoperasian emulator semacam itu di icq, serta membantu mengembangkan kodenya. Sedikit banyak, ini adalah gagasan dan perkembangannya 13 tahun yang lalu.



Tautan












All Articles