四.5 RFID/Mifare卡片UID號碼拷貝機設計
■ 功能與動作:
本實習在設計一RFID/Mifare卡UID號碼拷貝/複製機,當程式啟動時,使用者先將欲拷貝或複製的原始卡片放置在RFID讀寫器/RC522上,等系統成功讀取原來卡片的UID號碼並顯示在LCD上後,接著移開舊卡,並將新的可複寫的空白卡放在RC522的感應區上,就可完成拷貝複製的動作,整個拷貝複製的流程簡單易用!
在程式啟動之後系統的LCD會先顯示圖四.15的【等待欲拷貝卡片到來】畫面,即在LCD顯示器的第一行第二格開始顯示" RFID UID Copyer"訊息,第二行則先顯示"UID:"提示訊息,之後依RC522實際讀到的結果,將4個位元組的卡片UID序號顯示於後。此時使用者可將欲複製拷貝的RFID/Mifare卡放置在RC522上,如果RC522讀取發生錯誤,LCD上會顯示圖四.16【卡片UID讀取錯誤】畫面,如果正常讀取則顯示圖四.17【卡號讀取成功等待原卡片移開】畫面,等待使用者將原始卡片拿開;當卡片離開RC522後會進入圖四.18【提示放上拷貝用的新卡】畫面,開始等待空白卡片到來,當使用者將可拷貝的空白卡放置在RC522上時,系統會將之前讀到的卡片號碼複寫在空白卡的前面4個位元組上,如果複製成功LCD會顯示圖四.19【卡片UID號碼複製成功】畫面,反之則出現圖四.20【卡片UID號碼複製失敗】畫面,這樣便完成一次拷貝複製的動作。然後再次回到圖四.15的【等待欲拷貝卡片到來】畫面,等待下一次的拷貝複製動作。
圖四.15 等待欲拷貝卡片到來畫面
圖四.16 卡片UID讀取錯誤畫面
圖四.17 卡號讀取成功等待原卡片移開畫面
圖四.18 提示放上拷貝用的新卡畫面
圖四.19 卡片UID號碼複製成功畫面
圖四.20 卡片UID號碼複製失敗畫面
■電路圖:
■ 程式名稱與內容列表:RFID_CopyUID_LCD.ino
#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define I2C_Addr 0x27 // I2C LCD的位址
LiquidCrystal_I2C lcd(I2C_Addr,16,2); // set LCD address to 0x27 as 16 chars with 2 line display
#define RST_PIN 9 // Configurable, see typical pin layout above
#define SS_PIN 10 // Configurable, see typical pin layout above
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
/* Set your new UID here! */
#define NEW_UID {0xDE, 0xAD, 0xBE, 0xEF}
MFRC522::MIFARE_Key key;
// setup()部分程式開始
void setup() {
lcd.init(); // initialize the lcd
lcd.backlight();
Serial.begin(115200); // Initialize serial communications with the PC
while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
Serial.println(F("Warning: this example overwrites the UID of your UID changeable card, use with care!"));
// Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;
}
} // setup()部分程式結束
// loop()主程式部分開始
void loop() {
Serial.println("Please place the old card...");
lcd.clear();
lcd.setCursor(1,0);
lcd.print("RFID UID Copyer");
lcd.setCursor(0,1);
lcd.print("Place old card..");
// Look for new cards, and select one if present
while ( ! mfrc522.PICC_IsNewCardPresent())
{}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial())
{
Serial.println("Read UID Error!!");
lcd.setCursor(0,1);
lcd.print("Read UID Error!!");
delay(1000);
return;
}
// Now a card is selected. The UID and SAK is in mfrc522.uid.
// Set new UID
byte newUid[] = NEW_UID;
// Dump UID
lcd.clear();
lcd.setCursor(1,0);
lcd.print("CardUID:");
Serial.print(F("Card UID:"));
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
Serial.print(mfrc522.uid.uidByte[i], HEX);
newUid[i]=mfrc522.uid.uidByte[i];
lcd.print(newUid[i] < 0x10 ? "0" : "");
lcd.print(newUid[i],HEX);
}
Serial.println();
Serial.println("Please remove old card!");
Serial.println();
lcd.setCursor(0,1);
lcd.print("Remove old card!");
WaitCardLeft();
delay(1000);
Serial.println("Now place the new card!");
lcd.setCursor(0,1);
lcd.print("Now the new card");
while(! mfrc522.PICC_IsNewCardPresent()){
delay(50);
}
lcd.setCursor(0,1);
lcd.print("New card Detect!");
while(! mfrc522.PICC_ReadCardSerial()){
delay(50);
}
// delay(1000);
if ( mfrc522.MIFARE_SetUid(newUid, (byte)4, true) ) {
Serial.println(F("Wrote new UID to card."));
lcd.setCursor(0,1);
lcd.print(" Copy Success! ");
}
else{
Serial.println("Wrote new UID failed!!");
lcd.setCursor(0,1);
lcd.print("Wrote UID failed");
}
delay(2000);
WaitCardLeft();
// Halt PICC and re-select it so DumpToSerial doesn't get confused
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD
delay(1000);
} //
//
void WaitCardLeft()
{
byte count=0;
while(count<5)
{
if( ! mfrc522.PICC_IsNewCardPresent())
count++;
else
count=0;
delay(50);
} // while迴圈結束
} // WaitCardLeft()副程式結束
■ 執行結果:
照片四.5 【圖四.15】等待欲拷貝卡片到來畫面之實現
照片四.6 【圖四.17】卡號讀取成功等待原卡片移開畫面之實現
照片四.7 【圖四.18】提示放上拷貝用的新卡畫面之實現
照片四.8 【圖四.19】卡片UID號碼複製成功畫面之實現
照片四.9 【圖四.20】卡片UID號碼複製失敗畫面之實現
程式說明:
本實習可說是前面實習四的擴充應用,因為所使用的零件和電路是完全一模一樣,只是內部的程式稍加修改而已,可是兩者的價值感可說是差別很大;所以同樣的硬體裝置,只要配上不同的軟體,就可以大大的提升它的附加價值。
在第一章的[一、7 Mifare卡相關知識介紹]節中的[一、7-1 廠商識別區塊(Manufacturer Code)]小節曾介紹過,每一張RFID/Mifare卡的第一個資料區塊(編號為Block 00),是一個只能讀取的唯讀區塊,在這個區塊中記錄了每一張卡片的識別碼,和一些特定的資料,而這些特定的識別碼和資料是由發卡廠商在卡片製造完成時就已經寫入,一般的RFID讀寫器(RFID Reader/Writer)只能讀取這個區塊,是無法寫入或改變其中任何內容的。這些識別碼資料包括了該卡片的序號、卡片類型(即Mifare-S20~Mifare-S70)、製造日期及發卡廠商等相關資料;所謂的卡片序號,長度為4 Bytes,位於該張卡片的最前面,也就是Block 00這個區塊的最前面四個位元組,也等於這張卡片的身分證字號一樣,也就是說全世界每一張Mifare卡都有它唯一且不會重複的序號碼,這個序號就是這張卡片的識別碼(又稱為唯一識別碼UID: Unique Identification Code),而這個卡片序號碼在出廠之後是不能再更改的。
讀者們在進行這個實習時要注意一點,就是前面【動作與功能】部分裡提到的”空白卡”,其實並不包括在本次專題所使用到的零件之中,必須另外準備。所以會提到這個問題,要就是一般我們在市面上買到的所謂的”白卡”,不管它是以甚麼樣的形式出現(如卡片、鑰匙圈或是腕帶等),只要是號稱相容於Mifare卡的,基本上它的UID號碼就是固定不能更改的。可是由於目前這類的RFID/Mifare卡被廣泛用在門禁管制或辨識系統上,數量非常龐大,所以有很大的複製/拷貝的需求及商機,因此便有廠商開發出可以重新抹除與燒錄的卡片;例如目前市面上很常看到由大陸復旦微電子所開發的復旦白卡,它和一般的Mifare卡結構上是完全相容,可是它的Block 00這個區塊卻是可以覆寫與燒錄的!也就是說透過特殊的RFID讀寫器就可達成拷貝/複製的功能。而本次製作所使用的MFRC522讀寫器模組就是具有這種功能的硬體裝置,只要再使用特定的軟體程式,就可以輕易地設計出一個RFID/Mifare卡拷貝機,而這也是本實習想要教導各位的,讓各位讀者了解如何利用一個軟體程式,讓一個硬體可以大大的提高它的附加價值。這種可覆寫的空白卡在網路上一些拍賣或購物網站很容易就可以找得到,而且價錢也和標準Mifare卡的差不多,讀者們不妨去買幾片來練習實作本實習。
接下來就讓我們研究一下這個實習的程式內容,在開始的1~11行其實就是上面[實習四]程式的14~23行,接著第14行定義一個4位元組的陣列變數” NEW_UID”,這是用來儲存要拷貝卡片的UID號碼;而第16行則是宣告一個型態為”MFRC522::MIFARE_Key”的變數”key”,好用來存放讀/寫Mifare卡時必須用到的6個位元組的金鑰匙密碼值。而整個”setup()”程式部分(19~31行)和[實習四]程式的主要差別,就是多了28~30行這個迴圈次數為6的”for”迴圈,這個迴圈主要是把預設值”ff”填入金鑰匙密碼陣列變數”key”的6個位元組內。
後面的33~104行是主體程式”void loop()”的部分,其中36~40行是在實作出【圖四.15 等待欲拷貝卡片到來】的畫面,然後42、43行呼叫” mfrc522.PICC_IsNewCardPresent()”這個副程式,等待欲複製/拷貝的原始卡片的到來;如果測試到有卡片出現在RC522的感應範圍內,便會呼叫副程式”mfrc522.PICC_ReadCardSerial()”去讀取卡片的UID序號,假如讀取失敗,則在LCD上顯示【圖四.16 卡片UID讀取錯誤】畫面,並結束讀卡動作,重新開始(45~52行)。
如果成功讀取卡片的UID序號,程式55~72行會實作出【圖四.17 卡號讀取成功等待原卡片移開】畫面,也就是將卡片的UID序號顯示在LCD螢幕的第一行上,並呼叫自建的副程式”WaitCardLeft()”以等待使用者將舊卡片拿開;在舊卡片移開之後,由74~81行的程式實作出【圖四.18 提示放上拷貝用的新卡】畫面,並開始等待可複寫的白卡到來(82~87行)。當RC522偵測到新的卡片之後,便呼叫”mfrc522.MIFARE_SetUid(newUid, (byte)4, true)”這個卡片序號的複寫副程式(88行),將之前讀取到舊卡片的UID序號(存放在” NEW_UID”這個陣列變數內)寫入空白卡,如果複寫成功則顯示【圖四.19卡片UID號碼複製成功】畫面(程式89~92行),反之則顯示【圖四.20卡片UID號碼複製失敗】畫面(93~97行)。
剩下的98~103行程式會再次的呼叫自建”WaitCardLeft()”副程式(99行)以等待使用者將複寫成功的新卡片拿開,如果沒有這個等待的動作,我們的系統就會連續的重複進行:[讀取原來舊卡UID]→[複寫入新卡]→[讀取原來舊卡UID]→[複寫入新卡]→ ………的動作,這樣系統就會變得很不穩定,以至於不容易拷貝成功!至於剩下的幾行是在停止卡片動作,而且結束RC522的讀寫功能,好讓程式重新開始新的拷貝迴圈。
後面的107~118行程式碼是”WaitCardLeft()”這個副程式的主體部分,在前面我們使用”mfrc522.PICC_IsNewCardPresent()”這個型態為[boolean]的副程式去偵測是否有卡片出現在RC522的感應範圍內,如果有,會回傳”true”的結果,反之則為”false”;照理來說當然也是可以用來偵測卡片是否已經離開RC522的感應範圍,可是根據網路上一些高手的實際測試,當卡片持續出現在RC522的感應範圍內時,這個副程式並不是連續的回傳”true”的結果,反而是會回傳:”true”→”false”→”true”→”false”……這種交互的結果,而筆者實際去測試後發現的確是這樣的反應,因此如果使用這個副程式去測試卡片是否離開,假如只測一次”false”就認為成立的話,很顯然會出現錯誤;所以為了確認卡片確實是已經離開RC522的感應範圍,我們必須至少連需三次以上都是偵測到”false”才算數;因此我們用"while(count<5){}"這個迴圈指令去監測是否已經連續5次都測到卡片已經不存在了,如果是,才會離這個無窮的迴圈(程式110~117行),如此就可以確保不會誤動作。
作者已經移除這則留言。
回覆刪除你好 我想問如果寫入失敗的話會有甚麼原因 感謝
回覆刪除