Mifare RFID-RC522模組實驗(二):C語言的結構(struct)與類型定義(typedef)說明

本單元的程式將在Arduino中紀錄一些Mifare標籤的識別碼,並且替它們標示名稱。每當掃描到這些標籤,Arduino就在序列埠監控視窗顯示它的名稱。識別碼可以用16進位、10進位…等格式表示。

Mifare標籤

認識C語言的結構(Struct)

C語言的陣列所儲存資料必須是相同的類型。如果要儲存一組包含不同類型的資料,可以使用結構(struct)

比較陣列(array)與結構(struct)

定義結構相當於規劃容器的「藍圖」。下圖右是定義儲存一個位元組陣列和一個字串的結構,結構裡的每一個資料欄位,稱為一個「成員」。

結構定義的語法

結構定義完畢後,就可以填入資料,這個步驟稱為「初始化結構資料」。下圖右的程式將利用RFIDTag這個「藍圖」,打造一個叫做“tag”的容器,並在其中填入UID識別碼和自訂的名稱。

初始化結構資料

存取結構裡的成員的語法如下,底下的敘述將取出tag裡的name資料:

取出結構資料

若要儲存一組結構資料,請使用陣列,底下的敘述將在tags陣列中儲存三組標籤的識別碼和名稱:

struct RFIDTag tags[] = {
   {{60,209,110,133}, "Arduino"},
   {{0xD4,0xD3,0xC0,0x61}, "Raspberry Pi"},
   {{0x15,0x8,0xA,0x53}, "Espruino"}
};

顯示Mifare標籤名稱的Arduino程式

本實驗單元的材料和電路,與前一篇貼文相同。完整的程式碼如下:

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

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

struct RFIDTag {    // 定義結構
   byte uid[4];
   char *name;
};

struct RFIDTag tags[] = {  // 初始化結構資料
   {{60,209,110,133}, "Arduino"},
   {{0xD4,0xD3,0xC0,0x61}, "Raspberry Pi"},
   {{0x15,0x8,0xA,0x53}, "Espruino"}
};

byte totalTags = sizeof(tags) / sizeof(RFIDTag);

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

void setup() {
  Serial.begin(9600);
  Serial.println();
  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的長度
      bool foundTag = false;            // 是否找到紀錄中的標籤,預設為「否」。
      
      for (byte i=0; i<totalTags; i++) {
        if (memcmp(tags[i].uid, id, idSize) == 0) {
          Serial.println(tags[i].name);  // 顯示標籤的名稱
          foundTag = true;  // 設定成「找到標籤了!」
          break;            // 退出for迴圈
        }
      }

      if (!foundTag) {    // 若掃描到紀錄之外的標籤,則顯示"Wrong card!"。
        Serial.println("Wrong card!");
      }

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

第18行的sizeof()將傳回結構和陣列資料佔用的位元組大小,此程式的陣列佔用18位元組,結構佔用6位元組,兩者相除之後存入totalTags,因此其值為3,代表一共有3個標籤元素。

第39行透過memcmp()函式比對陣列,相關語法和範例說明,請參閱《超圖解Arduino互動設計入門》18-16頁。

上傳程式碼之後,掃描Mifare標籤,Arduino的序列埠監控視窗將顯示類似下圖的結果:

序列埠監控視窗

使用typedef指令自訂資料類型

typedef指令能將現有的資料類型改成自訂的名稱,通常用於簡化類型名稱,語法如下:

我們可以把結構定義帶入typedef指令,如此,初始化結構資料時,就不用加上struct關鍵字:

使用typedef自訂結構資料類型

因此,上一節程式當中的結構定義和初始化語法,可改寫成:

typedef struct {   // 宣告自訂的結構資料類型
   byte uid[4];
   char *name;
} RFIDTag;

RFIDTag tags[] = {  // 初始化結構資料
   {{60,209,110,133}, "Arduino"},
   {{0xD4,0xD3,0xC0,0x61}, "Raspberry Pi"},
   {{0x15,0x8,0xA,0x53}, "Espruino"}
};

其餘程式碼都一樣。

未完,待續…

延伸閱讀

Posts created 467

14 thoughts on “Mifare RFID-RC522模組實驗(二):C語言的結構(struct)與類型定義(typedef)說明

    1. 如同「Mifare RFID-RC522模組實驗(一)」這篇貼文提到的,Mifare卡片有不同的系列,有沒有可能你的卡片不是Mifare Classic?如果你有具備NFC功能的Android手機,可以下載NFC reader之類的工具軟體,讀取Mifare卡片的資訊(如:系列類型)確認一下。

      thanks,
      jeffrey

  1. 老師我的用手機掃描出來是classic
    ID(hex):72 bb 3a dc
    ID(reversed hex):dc 3a bb 72
    ID(dec):1924872924
    ID(reversed dec):3694836594
    Mifare Classic type:classic
    老師我想請問一下這些代表甚麼意思,我是用一卡通掃描出來的

    1. 就是你的卡片的唯一識別碼(ID),十進位(dec)和16進位(hex),以及卡片類型(MiFare Classic)。

      thanks,
      jeffrey

  2. 老師我想請教您
    假如我想把唯一辨識碼放到RFID(三)的程式中請問要宣告區段編號是第幾個
    謝謝老師
    祝老師有個美好的一天

    1. 就是說假如不是上面的那段唯一辨識碼的話就會顯示wrong的意思
      謝謝老師
      祝老師有個美好的一天

  3. ID (hex): 29 e1 82 3b
    ID (reversed hex): 3b 82 e1 29
    ID (dec): 702644795
    ID (reversed dec): 998433065
    Technologies: NfcA
    我用手機的NFC reader的APP掃了一遍
    顯示這個,他沒有說是classic
    但在掃的時候手機顯示不支援此標籤類型
    那這個識別碼可以用在上面的範例程式上嗎

  4. 老師你好
    我想請問使用NFC reader掃出來之後會出現
    ID (hex)
    ID (reversed hex)
    ID (dec)
    ID (reversed dec)
    這些資訊,想請問reversed是怎麼去計算的?還是他是原本就包含在裡面的資訊?

    1. 你應該是用手機App掃描到這些數據的。reverse代表「反向讀取」,假設NFC標籤的ID值(hex)是AA BB CC DD,反向16進位(reversed hex)值就是DD CC BB AA,它們都代表同一個值。

      thanks,
      jeffrey

  5. 老師您好:
    為什麼我明明改成
    {{67,72,51,73}, “Arduino”},
    {{0xD4,0xD3,0xC0,0x61}, “Raspberry Pi”},
    {{0x15,0x8,0xA,0x53}, “Espruino”}
    ,用Mifare RFID-RC522模組實驗(一)的程式測試過UID了,結果還是
    Wrong card!

    1. sizeof()可傳回元素或資料類型佔用的位元組大小。例如,在Uno板子上,「整數」類型佔2位元組,所以底下敘述將傳回2:

      sizeof(int)

      這個陣列包含3個整數元素:

      int data[] = {5, 6, 7};

      sizeof()將傳回6:

      sizeof(data)

      所以經過底下的計算可得知整數類型陣列的元素數量:

      sizeof(data)/sizeof(int)

      thanks,
      jeffrey

發佈留言

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

Related Posts

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

Back To Top