ArduinoのRFIDエミュレーター



多くの人が私の投稿「RFIDエミュレーターを読んでいます 。そこでは、EMマリンデバイス、アンテナの巻き方、3つの部分からRFIDエミュレーターを作成する方法について詳しく説明しました。しかし、正直に言うと、そのデバイスの独創的なシンプルさにもかかわらず、繰り返すのは非常に困難です。共鳴を捕らえるために、誰もが自宅にオシロスコープを持っているわけではなく、ATtiny85ファームウェアには別のプログラマーが必要です。



そこで、子供でも繰り返すことができるようなエミュレーターを作ることにしました。すべてのコンポーネントは、ほぼすべての村で販売されています。さらに、その機能を拡張することもできます。たとえば、複数のカードを保存したり、別のリーダーを追加してすべてのカードを1つのデバイスに保存したり、次の目的で使用したりできます...では、行きましょう。



ハードウェア



私が言ったように、エミュレーターは簡単に入手できる利用可能なコンポーネント上に構築されるべきです。まず、エミュレータ回路を見てみましょう。







ある時点でトランジスタで閉じる発振回路があるので、リーダーの電流が変化し、送信データを受信します。

このバンドルで私たちにとって最も難しいのは、125kHzの周波数に調整された発振回路です。そして、あなたがそれを得ることができる非常に簡単な解決策があります。ArduinoRDM6300用のRFIDタグリーダーが 販売されています。リーダーの価格はわずか1ペニーで、すでにアンテナが付属しており、共振コンデンサーはすでにボードにはんだ付けされています。したがって、実際には、コイルと共振コンデンサの2つの部分のリーダーのみが必要です。





RDM6300リーダーと共振コンデンサの位置。



私はこのリーダーを数ペニーで購入しましたが、これはアンテナの巻き取りと調整の労力に見合わないものです。私たちにとって最も難しい操作は、このコンデンサーのはんだを外して回路基板にはんだ付けすることです。小学生でも対応できると思います。

その結果、私たちはすべてをブレッドボードに集めます。手元に10kOhmの抵抗がなく、20 kOhmしかないため、2つの抵抗を並列に使用しています。





組み立てられた回路。



さて、それがどのように見えるかをクローズアップで見てみましょう。コンデンサー用に別のスカーフを特別に割り当て、このマットレスに挿入されている取り付け針に直接はんだ付けしました。





エミュレータの動作を確認するために、当初は同じRDM6300を使用することを考えていました(2つ購入しました)。そして、最初でもそれをしましたが、それから彼は、あるArduinaを別のArduinaでデバッグすることは、どういうわけか深刻ではないと判断し、ファクトリーリーダーで壊れました。





ファクトリーリーダー。



タイマーをコッキング



前回の記事で、プロセスのすべての物理学と操作の原理を最も完全に説明した ので、よく理解しておくことを強くお勧めします。しかし、私が何をしているのかを理解するために、いくつかの点を少し更新します。



EM4102はマンチェスターエンコーディングスキームを使用していることを思い出させてください。EM4102プロトコルが変調されている場合、1ビットの送信時間はキャリア周波数(125 kHz)の64、32、または16周期になります。







簡単に言うと、1ビットを送信するときは、1の値を0に変更するか(0を送信するとき)、または0から1に変更します(1を送信するとき)。したがって、キャリア周波数の64周期の情報の1ビットを送信することを選択した場合、「ハーフビット」の送信には、キャリア周波数の32周期が必要になります。したがって、各ニブルは次の速度で変化する必要があります。



f=125000/32 = 3906,25 
      
      





この「ハーフビット」の周期は256ミリ秒に等しくなります。



次に、タイマーを計算して、指定された頻度で脚をピクピクさせる必要があります。しかし、私はとても怠惰になり、データシートを開いてあくびを始めたとき、いくつかの既製の解決策を見つけることにしました。そして、既製のタイマー計算があることが判明しました。データをドライブするだけです。会う: Arduinoのタイマー計算機



タイマー周波数を3906Hzに設定するだけで、すぐに使用できるコードが生成されます。まあ、それは奇跡ではありませんか!





私が全体として周波数を入力したことに注意してください、そして彼はそれを部分的で正確に私たちが必要とするものとして数えました。次のタイマー初期化コードを取得しました。



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





華麗で、シンプルで、簡潔です。



出力の割り込みベクトルも非常に単純です。ゼロを転送する場合は1からゼロに、1を転送する場合はゼロから1に移行する必要があることを思い出してください(理解のために図を参照してください)。したがって、現在通過しているものと「ハーフビット」のどこにあるかを確認し、データ配列からすべてのデータを徐々に読み取ります。



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





送信用のデータの翻訳



ここでも、カードに保存されているデータ形式のメモリを更新する必要があります。それらが書かれている方法。実例を見て​​みましょう。



カードはあるが、リーダーがないとします。カードには番号010.48351が書かれてい ます





番号010、48351の実際のカード。



この番号をカードに記載されているシリアル番号に変換するにはどうすればよいですか。十分に単純です。次の式を覚えておいてください。数値の2つの部分を別々に変換します。



010d = 0xA
48351d = 0xBCDF
      
      





したがって、シリアル番号は0xABCDFになります。それを確認して、リーダーでカードを読んでみましょう(10進形式で読み取ります)。数字が表示されます。



0000703711
      
      





任意の計算機を使用してそれを16進形式に変換し、0xABCDFを再度取得します。

これまでのところ単純なようです、待ってください、今あなたはあなたの脳を緊張させなければなりません。カード自体にあるデータの形式を思い出させてください。





私はそれを言葉で言います:



  1. 最初は9つの見出しユニットがあります。
  2. 最下位のハーフバイトクライアントID。
  3. パリティビットの終わり。
  4. バイトの後半はクライアントIDです。
  5. パリティビット。
  6. シリアル番号のゼロバイトの最下位ハーフバイト。
  7. パリティビット
  8. .
  9. ,
  10. . 10 ( ).
  11. , .


合計で64ビットのデータを取得します(これは5バイトです!)。ちなみに、私の読者はクライアントIDを読み取らないので、ゼロとして受け入れます。



パリティビットとは何ですか?これはパッケージ内の1の数です。偶数の場合、パリティビットは0で、そうでない場合は1です。それを計算する最も簡単な方法は、通常のXORです。



実際、私は長い間、シリアル番号をパッケージに変換する方法をよりエレガントにして、マイクロコンピューターのスペースを節約する方法を考えていました。したがって、私はこれを行う小さなプログラムをスケッチしました。プログラムはスポイラーの下で見ることができます。



シリアルをデータに変換するためのテストプログラム
#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;
}

      
      





私たちにとって最も重要なことは、パリティビットがどのようになるかです。便宜上、このプレートとまったく同じ方法で画面に出力しました。その結果、このようになりました。





card_id は、カードのシリアル番号です(上記で説明しました)。



最初の列はnibls、2番目はそれらのビット表現、3番目はパリティビットです。下から3行目は、すべてのniblのパリティビットです。私が言ったように、それらは単にXORによって計算されます。



計算をテストし、視覚的に確認した後、Arduinoのプログラムで結果のデータを確認しました(最後の行は特にコードに挿入するためのものです)。すべてが完璧に機能しました。このプログラムをスケッチした結果、既製の再計算機能が得られました。以前は、ビート計算は他の誰かのコンピュータープログラムであり、私はそれらの巨大な実装が好きではありませんでした。したがって、シリアル番号を送信形式に変換する機能は次のようになります。




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





すべて、フィールドテストに進むことができます。プロジェクトのソースコードは ここにあります



テスト



彼らが言うように、千回読むよりも一度見る方が良いです。特にあなたのために、私はこのエミュレーターの仕事についての映画を録画しました。実際のハードウェアでテストして、Arduinoを使用してオフィスに入ろうとしたかったのですが、ひどいパンデミックのため、そこでは許可されていません。したがって、実物大のテストは、実験室の条件で、テーブル上で見る必要があります。





結論



そのような記事が初心者にプログラミングとエレクトロニクスを学ぶきっかけになることを本当に望んでいます。また、子供でもコピーしてエミュレートできるようになったため、最も保護されておらず安全ではないため、このタイプのカードの市場からの撤退にも貢献します。



私は私に感謝の意表明 ミハルKrumniklを彼はICQに私に、このようなエミュレータの操作だけでなく、コードを開発してヘルプを説明したときに、何年も前に、彼の忍耐の多くのために。ある意味で、これらは13年前の彼のアイデアと発展です。



リンク












All Articles