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

認識C語言的結構(Struct)
C語言的陣列所儲存資料必須是相同的類型。如果要儲存一組包含不同類型的資料,可以使用結構(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 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"}
};
其餘程式碼都一樣。
未完,待續…

老師為什麼我不管掃哪張卡片都會顯示Wrong card!是我的有錯誤嗎
如同「Mifare RFID-RC522模組實驗(一)」這篇貼文提到的,Mifare卡片有不同的系列,有沒有可能你的卡片不是Mifare Classic?如果你有具備NFC功能的Android手機,可以下載NFC reader之類的工具軟體,讀取Mifare卡片的資訊(如:系列類型)確認一下。
thanks,
jeffrey
老師我的用手機掃描出來是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
老師我想請問一下這些代表甚麼意思,我是用一卡通掃描出來的
就是你的卡片的唯一識別碼(ID),十進位(dec)和16進位(hex),以及卡片類型(MiFare Classic)。
thanks,
jeffrey
老師我想請教您
假如我想把唯一辨識碼放到RFID(三)的程式中請問要宣告區段編號是第幾個
謝謝老師
祝老師有個美好的一天
蛤?妳是在問,該怎麼把妳的RFID卡的UID紀錄在程式裡的tags結構嗎?
就是說假如不是上面的那段唯一辨識碼的話就會顯示wrong的意思
謝謝老師
祝老師有個美好的一天
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
但在掃的時候手機顯示不支援此標籤類型
那這個識別碼可以用在上面的範例程式上嗎
NfcA就是MIFARE Classic,我猜想可以。
thanks,
jeffrey
老師你好
我想請問使用NFC reader掃出來之後會出現
ID (hex)
ID (reversed hex)
ID (dec)
ID (reversed dec)
這些資訊,想請問reversed是怎麼去計算的?還是他是原本就包含在裡面的資訊?
你應該是用手機App掃描到這些數據的。reverse代表「反向讀取」,假設NFC標籤的ID值(hex)是AA BB CC DD,反向16進位(reversed hex)值就是DD CC BB AA,它們都代表同一個值。
thanks,
jeffrey
老師您好:
為什麼我明明改成
{{67,72,51,73}, “Arduino”},
{{0xD4,0xD3,0xC0,0x61}, “Raspberry Pi”},
{{0x15,0x8,0xA,0x53}, “Espruino”}
,用Mifare RFID-RC522模組實驗(一)的程式測試過UID了,結果還是
Wrong card!
我不明白byte totalTags = sizeof(tags) / sizeof(RFIDTag);的意思
sizeof()可傳回元素或資料類型佔用的位元組大小。例如,在Uno板子上,「整數」類型佔2位元組,所以底下敘述將傳回2:
sizeof(int)
這個陣列包含3個整數元素:
int data[] = {5, 6, 7};
sizeof()將傳回6:
sizeof(data)
所以經過底下的計算可得知整數類型陣列的元素數量:
sizeof(data)/sizeof(int)
thanks,
jeffrey