本單元的程式將在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