Mifare RFID-RC522模組實驗(一):讀取Mifare RFID卡的UID識別碼

本文旨在補充《超圖解Arduino互動設計入門》第18章「RFID無線識別裝置與問答遊戲製作」單元,書本採用的RFID讀卡機模組是採用9600bps, TTL序列通訊介面,RFID的通訊頻率為125KHz。這種模組的接線和程式都很簡單,每當感測到RFID卡,讀卡機就把卡片的識別碼送往序列埠。

市售Arduino套件裡面的RFID模組,多半是Mifare(讀音:my-fare)規格,跟書本的不一樣,因此本文將補充說明Mifare的基本原理及其模組的接線,以及讀取卡片識別碼的程式。

Mifare是NXP(恩智普)半導體公司推出的非接觸型IC卡(也就是外表沒有金屬接點的卡片),在市場上獲得廣泛的採用,像是停車場的感應幣(token)、現金卡(如:台灣的悠遊卡)、員工識別證…等等。Mifare卡有不同的系列,如:Mifare Classic, Mifare UltraLight, Mifare Pro…等,主要的差別在於資料安全加密和驗證的等級。

Mifare卡內建EEPROM,應用程式可對它寫入和讀取數據,例如:停車票卡可紀錄車主的停車時間。Mifare還具備「防衝突處理」機制,也就是避免訊號干擾:若多張卡片同時出現在偵測範圍,Mifare讀寫器將能逐一選擇卡片進行處理。例如,假設超級市場裡的每個商品都貼上RFID標籤,結帳櫃台的RFID讀取設備若具備防干擾機制,將能自動掃描堆放在購物推車裡的所有商品,結帳人員不必再手動逐一掃描商品條碼,也不會重複計算。附帶一提,所有符合ISO/IEC 14443-A標準規範的RFID卡片都要有防衝突處理功能,而Mifare是依循ISO/IEC 14443-A規格建立的非接觸式IC卡。

底下是Mifare裝置的概略圖,它的卡片屬於被動式、無內建電源(也稱為「無源」),卡片所需的電力來自讀寫器的電磁場。

Mifare裝置的概略圖

啟動RFID讀寫器之後,讀寫器的天線將不停地發送電磁波,每當卡片進入此電磁場,卡片內部的線圈和電路將與此電磁場產生共振,從而獲得電能。無線充電器也是透過電磁感應、非直接連線方式獲取能量,RFID讀寫器和卡片兩端的線圈(天線)相當於變壓器裡的主線圈和副線圈(請參閱「測試Palm Touchstone(點金石)無線充電模組」貼文。

依照感應距離,非接觸型IC卡分成緊耦合型(close-coupled,需要緊貼感測器)、接近式(proximity,10cm以內)和鄰近式(vicinity,50cm以內),Mifare屬於接近式,這種類型的卡片簡稱為PICC(Proximity IC Card,接近式IC卡),讀寫器則簡稱PCD(Proximity Coupling Device,接近型耦合器)。

另一方面,某些RFID的應用場合,標籤的尺寸大小有固定的標準,像信用卡和金融卡,這一類的卡片長、寬和厚度,都要遵循國際ID-1標準(85.6 × 53.98 × 0.76mm),目前的技術沒有辦法把電池塞入這麼薄的卡片,所以非接觸型卡片只能採用無線供電方案。

當卡片被電磁能啟動(activate)之後,將等待接收與回應來自讀寫器的命令。微控器和Mifare讀寫器之間採用TTL數位訊號傳遞資料,為了用電波傳送數位訊號,必須將數位訊號加上載波調變,Mifare的載波頻率是13.56MHz。上圖的射頻訊號波形只是描述概念,數位訊號在調變成電波之前,從讀寫器發送的訊號會採用米勒(Miller)編碼,而從卡片發出訊息則經過曼徹斯特(Manchester)編碼。讀寫器在操作卡片時,都會經過三次雙向認證,互相驗證使用的合法性,而且通訊過程中的所有數據都經過加密,以確保安全。

Mifare讀寫器模組與Arduino接線示範

本單元使用的Mifare RFID-RC522讀寫器模組的外觀與接腳定義如下,模組採用的MFRC522晶片本身有支援UART, I2C和SPI介面,但是本文採用的程式庫僅支援SPI介面。

Mifare RFID-RC522讀寫器模組

Arduino Uno板的接線示範如下,SPI介面的晶片線選擇通常接在Arduino數位10腳,但這不是強制性的,模組的Reset腳也可以接在其他腳位:

Mifare讀寫器模組與Arduino接線示範

操控Mifare模組的MFRC522程式庫

本單元程式採用Miki Balboa開發的這個MFRC522程式庫來操控Mifare模組,若不使用程式庫,我們需要詳閱MFRC522晶片的規格書,了解讀寫器、卡片和微控制器之間的數據通訊流程,以及晶片內部的暫存器的指令位址,才能動手撰寫程式。

下載程式庫之後,請將它解壓縮到Arduino的libraries資料夾,再開啟Arduino IDE,即可從主功能表的「檔案→範例→MFRC522」指令底下找到一些範例程式。

儲存MFRC522程式庫

底下列舉本單元使用到的MFRC522程式物件的方法和屬性:

  • MFRC522物件.PCD_Init():初始化MFRC522讀卡機模組
  • MFRC522物件.PICC_IsNewCardPresent():是否感應到新的卡片
  • MFRC522物件.PICC_ReadCardSerial():讀取卡片的資料
  • MFRC522物件.PICC_GetType():取得卡片類型
  • MFRC522物件.PICC_GetTypeName():取得卡片類型名稱

每張Mifare卡片都有個唯一的ID(unique identifier,簡稱UID),當讀寫機讀取到卡片的資料之後,UID的長度和內容,可從底下兩個屬性值取得:

  • MFRC522物件.uid.size:包含UID的長度
  • MFRC522物件.uid.uidByte:包含UID碼的陣列

讀取Mifare卡片的UID碼

讀取Mifare卡片的流程如下,我們的程式不需要理會其中的「防衝突處理」和「選卡」部份,讀寫器會幫我們搞定,但是在讀取資料之後,我們的程式要發出命令讓卡片進入停止(halt)狀態,避免讀寫器重複讀取同一張卡片:

讀取Mifare卡片與衝突處理流程

SAK代表select acknowledge,直譯為「選擇應答」,是由卡片發給讀寫器,對於選擇卡片命令的回應,不同類型的Mifare卡片的SAK值不一樣(例如,Mifare Classic的SAK值為0x18),程式可藉此判別感應到的卡片類型。詳細的防衝突處理與SAK值判斷流程,請參閱NXP公司的“MIFARE ISO/IEC 14443 PICC Selection”技術文件(PDF格式)。

讀取Mifare卡片類型及其UID碼的程式如下:

#include <SPI.h>
#include <MFRC522.h>     // 引用程式庫

#define RST_PIN      A0        // 讀卡機的重置腳位
#define SS_PIN       10        // 晶片選擇腳位

MFRC522 mfrc522(SS_PIN, RST_PIN);  // 建立MFRC522物件

void setup() {
  Serial.begin(9600);
  Serial.println("RFID reader is ready!");

  SPI.begin();
  mfrc522.PCD_Init();   // 初始化MFRC522讀卡機模組
}

void loop() {
    // 確認是否有新卡片
    if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {
      byte *id = mfrc522.uid.uidByte;   // 取得卡片的UID
      byte idSize = mfrc522.uid.size;   // 取得UID的長度

      Serial.print("PICC type: ");      // 顯示卡片類型
      // 根據卡片回應的SAK值(mfrc522.uid.sak)判斷卡片類型
      MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
      Serial.println(mfrc522.PICC_GetTypeName(piccType));

      Serial.print("UID Size: ");       // 顯示卡片的UID長度值
      Serial.println(idSize);
 
      for (byte i = 0; i < idSize; i++) {  // 逐一顯示UID碼
        Serial.print("id[");
        Serial.print(i);
        Serial.print("]: ");
        Serial.println(id[i], HEX);       // 以16進位顯示UID值
      }
      Serial.println();

      mfrc522.PICC_HaltA();  // 讓卡片進入停止模式
    } 
}

程式第20行宣告一個指向儲存UID值的指標變數(假設UID碼的長度為4):

指向儲存UID值的指標變數

第25行的“MFRC522::PICC_Type”代表引用在MFRC522類別(程式庫)裡面定義的PICC_Type這個資料類型,其中的雙冒號(::)代表範圍解析運算子(scope-resolution operator),用來表示“PICC_Type”定義在MFRC522程式庫裡面。如果不用雙冒號指出“PICC_Type”資料類型的來源,程式編譯器會產生未定義之類的錯誤。

上傳程式碼之後,開啟序列埠監控視窗,你可以嘗試一次讓Mifare模組感應多個卡片(筆者同時用3個),它將能逐一顯示每個卡片的類型和UID:

序列埠監控視窗

除非這些卡片離開、再次進入感應區,否則它們不會被重複讀取。

延伸閱讀

Posts created 483

84 thoughts on “Mifare RFID-RC522模組實驗(一):讀取Mifare RFID卡的UID識別碼

  1. 第一段的RFID通訊頻率好像寫錯了
    如果是用RC522這一顆IC來讀取MIFARE
    125K應該要寫成13.56M

    1. hi scott:

      沒有錯喔~第一段指的是書本範例所採用的RFID模組(非Mifare),載波頻率是125KHz;本文採用的Mifare是13.56MHz。

      thanks,
      jeffrey

    2. 因為你第二段講的是HF,我以為你第一段打的頻率LF是打錯的,SORRY
      原來第一段是補充書的內容
      MIFARE是另外提出的
      看太快了,抱歉

    3. 請問為什麼我讀取我家的磁扣都不會有反應
      而麥當勞點點卡和悠遊卡就可以
      是不是因為我家的磁扣的類別不是Mifare

  2. 趙老師我想請問一下~
    Mifare RFID-RC522線路圖接好了,程式庫也下載了
    程式驗證,上傳沒有題,
    序列視窗有出現
    RFID reader is ready
    可是卡片靠上去,序列視窗也都沒有反應!!!
    在想說是不是買到地雷模組了…

    老師你的模組都是在網路上買,還是在實體店面買的呢?

    1. hi johnny:

      有可能…假若你曾把它的電源誤接到5V,那它八成是故障了。
      模組大多是在網路上購買的。

      thanks,
      jeffrey

  3. 趙老師 請問一下 RFID-RC522這個模組到底該接3.3V,還是5V? 有些網站建議使用5V 那一陣子用了好像也沒問題
    最近要再使用的時候發現完全都無法讀卡片,我該怎麼確認RC522模組是正常的?再買一組來測試嗎?

    1. NXP公司的MFRC522技術文件指出,該晶片的工作電壓為2.5V~3.6V。除非你的RFID模組上面有直流電源轉換晶片(我的沒有),那麼…接5V電壓,肯定故障了。

      thanks,
      jeffrey

  4. 你好
    請問可以借用網頁的第二張圖片來使用嗎?(MFRC522晶片那張)
    因為圖片畫得很精美
    想放進課程期末報告裡面使用

    謝謝~ 🙂

  5. 老師,請問一下
    為什麼我程式碼在驗證就出錯了
    MFRC522的程式庫我有丟進去了也更新到最新了
    目前發現兩個問題是
    1.’id’ was not declared in this scope
    byte *id = mfrc522.uid.uidByte; 的宣告好像就出問題了這邊有需要注意的嗎

    2.’class MFRC522′ has no member named ‘PICC_HaLtA’
    這邊的讓程式停止的程序不在他的城市庫裏面的意思嗎

    1. 如果連程式庫的範例都無法順利編譯,那應該是你選用了程式庫不支援的控制板。

      thanks,
      jeffrey

  6. 老師你好
    前面的問題我解決好了
    現在是能順利執行的
    但是我發現如果卡片uid有”0″的話
    會顯示空白

  7. 趙老師請問一下~
    Mifare RFID-RC522線路圖接好了,程式庫也下載了
    程式驗證,上傳沒有題,
    序列視窗有出現
    RFID reader is ready
    可是卡片靠上去,序列視窗也都沒有反應!!!

  8. 老師請問一下~
    我照著您書上寫的,可是卡片靠上去,都沒有反應!!!真是@#$#*%
    是因為新卡還沒資料所以沒反應嗎???

  9. 趙老師請問一下~

    顯示出來的識別碼要如何存取?
    我要在感應到正確卡片時點亮LED燈,但是我使用了其他不同的晶片它也會點亮

    我只接讀取HEX的值,不知道是不是我的判斷是錯誤,以下是我判斷的程式。
    謝謝指教。
    if (*id = HEX)
    {
    digitalWrite(led, HIGH);
    }
    else
    {
    digitalWrite(led, LOW);
    }

    1. Mifare RFID-RC522讀寫器模組採用SPI介面,所以理論上你可以同時連接多組,只要每個模組的SDA分別接到Arduino的不同接腳即可。可是,硬體上可行,軟體操作上應該會遇到資料衝突的問題,因為程式每一次只能選取一個SPI裝置,若在讀取A裝置資料的同時,B裝置有資料進來,就會漏接。

      所以,不要想太多,一個Arduino板只接一個Mifare RFID-RC522讀寫器模組,然後你把這些Arduino板當成I2C slave,其中一個當作I2C master,串接起來,這樣應該可以解決你的問題。

      thanks,
      jeffrey

  10. 老師,你好

    多謝你無私分享
    已試過讀取白卡是OK的!!
    想問一下家中想做個門禁系統的話
    可以讀取悠遊卡,(香港八達通)數據嗎?
    (或者直接問,ARDUINO可以讀取悠遊卡數據嗎?)
    謝謝

    1. 非常感謝!我試過感應具備悠遊卡的信用卡,可以讀取到UID,卡片類型顯示Unknown type(未知類型)。寫入資料的話,沒試過,應該不行,因為不知道它的區段密碼。

      thanks,
      jeffrey

  11. 為了答謝老師,已買了一本無線版<>
    人在香港,很難買呀,要空運過來
    期待收貨那一刻

  12. 您好 實測是成功的 不知道能否轉仔您的圖片到自己的部落格 會保留浮水印跟標明圖片來源

    另外想問一下整塊晶片的腳位是除了A0和D10外都是綁死的嗎?

    謝謝您

  13. 請問 有沒有單純用SPI寫RC522的範例

    看arduino的Library不是很明白

    想要用其他單晶片寫SPI跟他溝通

    但網路上的教學看不太懂

  14. 有試著讀過

    可以讀取他的暫存器

    但是看不太懂 要如何去讀取RFID 的 UID

    我也試著看Arduino RC522的Library 去照著他去一點一點地改寫

    但還是無法讀取到UID 只能讀寫暫存器

    1. 我相信可以,Mega 2560板子的預設SPI接腳編號:
      MOSI: 51
      MISO: 50
      SCK: 52
      SS: 53

      thanks,
      jeffrey

  15. 老師您好
    感謝您的分享
    我也是在嘗試做門禁系統
    發現白卡跟部份信用卡(有或無悠遊卡功能)可以讀取
    但部份信用卡(有悠遊卡功能)無法讀取
    想請教您可能是什麼問題呢?
    謝謝您

    1. 建議使用NFC Tools之類的工具,在手機上讀取這些卡片的類型和規範等格式,應該可以比較出它們的差異。

      thanks,
      jeffrey

  16. 請問如果我想同時使用RFID跟SD讀卡機的時候他們有兩個腳位重複到…因為腳位不知道該如何設定到我想設定的腳位,請問這有解嗎?

    1. 剛剛才了解到他們重複的地方是匯流排
      如果匯流排共用應該不會怎樣吧?
      畢竟程式是一行一行在跑 是這樣嗎?

    2. 如8-12頁的說明,每個SPI週邊都有專屬的SS線,你要在程式中設定。

      thanks,
      jeffrey

  17. 老師好,請問如果要用不同的RFID感應卡分別做不同的事情,例如:感應到A卡做一件事,感應到B卡做另一件事,可以請老師教我如何打這類的程式嗎,麻煩老師了

  18. 老師你好

    我在網路上, 看到 RC522 的rfid 好像很多人說都不能同時讀取超過一個tag , 你的例子好像可以同讀取3 個tag, 不知道如果再多的tag 例如30 個 會可行嗎?

    1. 可以讀取多個標籤,它會輪流探尋偵測到的晶片,主要的限制是距離。以卡片式標籤為例,疊兩、三張就超過感測距離了,所以30張辦不到。

      thanks,
      jeffrey

  19. 為甚麼程式燒好了,線路已沒問題,監控視窗顯示”is ready”
    但掃了很次也沒有反應

  20. 老師您好,想請問連接到R522的 SDA 以及 SCK腳位原本是13 以及10 ,如果換成I2C腳位也可以嗎?

  21. 你好~~~我想問要如何用同一張RFID tag來開啟和關閉servo motor???我現在只能做到開啟,然後用delay來讓servo motor關閉,可是我想用同一張卡來開啟,然後再scan一次才關閉…

  22. 請問老師:
    如果要感應卡片後 數量+1,宣告變數count=0然後在if裡輸入count++;及Serial.println(count);
    這樣是否能實現?

    1. 這個程式本身就能感應多張卡片,同時感應卡片的數量主要和距離有關。

      thanks,
      jeffrey

    2. 謝謝老師的回答,不過老師誤會我的意思了
      我想要作類似計數器(統計卡片總共感應多少次)
      結合老師的範例,加上宣告變數count=0然後在if裡輸入count++;及Serial.println(count);
      這樣是否能正確顯示數量在ARDUINO的視窗?
      目前手邊沒有材料沒辦法測試,先謝謝老師!!!

  23. 老師您好,請問老師有Arduino透過RFID讀取Mifare白卡UID並同時傳UID資料給App inventor端的練習嗎? 或者能提供相關資料? 萬分感謝!!

  24. 老师您好,我的板子 if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) {}函数进不去,是RC522模块本身问题吗?

  25. 老師您好,如果想把ID[0]~ID[4]的值合併,該如何合併呢?
    我有參考老師寫的”建立可傳回多個數值(陣列)的Arduino(C語言)自訂函式”,但我還是嘗試不出來,
    可以請老師協助我嗎?

  26. 老師,我剛剛有提出問題,我自己想了一下找到我自己問題點在於”println”!
    Serial.print(id[i], HEX); // 以16進位顯示UID值
    Serial.print(” “);

  27. 您好,想請教一下,我照書上操作,可是我的uid視別出來是 DF G4 32 F7 這種格式~~把書上的{{60,209,110,133}, “Arduino”}這一句換成{{DF,G4,32,F7}, “Arduino”} 會無法寫入~~顯示這四碼 was not declared in this scope~~把四碼各加上雙引號也不行~~我在想會不會是要改結構這一句,struct RFIDTag{byte uid[4]; char *name;}; 但是我不知要如何修改, 能否指導下呢~tks

  28. 老師請問要怎麼做出資料庫能夠像外面一樣讀取卡片能知道他的資料謝謝

    1. 建立一個RFID資料表,RFID的key值欄位與使用者資料表關聯,即可用RFID檢索到用戶資料。

      thanks,
      jeffrey

  29. 老師您好:
    如果使用UNO執行本程式時可順利取得RFID的資料.
    但當我改用Mega2560時,就無法順利取回.
    試了二片MEGA2560不知為何就是不行.

    1. 更換不同控制板,程式無法運作的主因是處理器不一樣,
      請先確認程式庫是否支援MEGA2560,其次是接腳是否正確。

      thanks,
      jeffrey

    2. 碰到同樣的問題,看到留言,跑去找Mega直2560的定義檔,
      的確是腳位設定的不同,Mega2560定義腳位如下:

      PIN_SPI_SS (53)
      PIN_SPI_MOSI (51)
      PIN_SPI_MISO (50)
      PIN_SPI_SCK (52)

  30. Arduino RFID 門禁系統 會當機不動作,必須重置開機才能恢復正常,想問問有何對策?

    1. Arduino會當機通常是記憶體不足導致,若無法改善程式,請嘗試改用SRAM大一點的控制板。

  31. 請問若使用ArdulnoSensorShleldv5.0,腳位接法一樣? 請問3.3V腳位可以接哪?
    謝謝

    1. 就是接SPI介面,我手邊沒有ArdulnoSensorShleldv5.0,請先按照上文的接線實驗。

      thanks,
      jeffrey

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top