Mifare RFID-RC522模組實驗(三):讀取與寫入資料

伴隨Mifare讀寫器模組附贈的RFID卡(或感應扣),都是Mifare Classic 1KB類型,台灣的停車場使用的感應幣,也是Mifare Classic。這種RFID卡內部有1KB的EEPROM記憶體,為了妥善管理並達到一卡多用的功能,這個記憶體空間被劃分成16個區段(sector),每個區段有4個區塊(block)區段0的區塊0包含卡片的唯一識別碼(UID,也稱為「製造商識別碼」,Manufacturer Code)。

Mifare Classic卡片的記憶體劃分方式

程式設計師可根據不同的應用場合,自行規劃儲存內容,例如:區段1可儲存員工編號、區段2儲存部門編號(可限制員工能夠進出的區域)、區段3存放停車時間…等等,一張卡片兼具多重識別用途。

認識Mifare的控制區塊(Trailer Block)

每個區段的區塊3也叫做控制區塊(Sector Trailer, Trailer Block或Security Block),如果把上圖的資料結構想像成16層大樓,控制區塊相當於每一層樓的密碼鎖;進、出該層樓必須先輸入正確的密碼,而且每層樓都有兩組密碼。

控制區塊包含金鑰A和金鑰B兩組密碼(各6位元組),以及存取控制位元(4位元組,但僅使用前3位元組)。

Mifare的控制區塊(Trailer Block)

金鑰B預設是可見的,金鑰A則因為安全考量,在掃描時,全部顯示成00。存取控制位元用於控制區段裡的每個區塊(區塊0到區塊3)是否能被存取、寫入或其他操作,並且決定要透過金鑰A或金鑰B來驗證。0xFF0780是控制區塊的「出廠預設值」,代表:

  • 金鑰A不可見;
  • 若通過金鑰A或金鑰B驗證,即可讀取或寫入該區段的區塊0~2。
  • 若通過金鑰A驗證,可讀取或改寫存取該區段的存取控制位元和金鑰B,也能改寫金鑰A。

傾印Mifare卡片資料,觀察EEPROM記憶體資料結構

練習讀取和寫入區塊之前,先用一張空白卡測試,把它的資料全部傾印出來(dump,亦即,把全部資料顯示在序列監控視窗),藉以觀察EEPROM記憶體的資料結構。

執行Arduino主功能表的「檔案→範例→MFRC522→DumpInfo」,開啟DumpInfo(傾印卡片資料)範例程式,將它上傳到Arduino板之後,請打開序列埠監控視窗。底下是感應一張卡片的結果,在序列埠監控視窗顯示卡片的所有區段數值之前,請勿移開或移動卡片。

傾印Mifare卡片資料

若在讀取資料的過程,把卡片移開感應區,將會出現“Timeout in communication”(通訊超時)錯誤訊息,請重新感應。

MIFARE_Read() failed: Timeout in communication.
14 59 PCD_Authenticate() failed: Timeout in communication.

從卡片資料輸出結果可知:

  • 空白卡片的資料區,除了紀錄識別碼的區塊0之外,預設值都是00。
  • 每個區段最後一個區塊,都是控制區塊(Trailer Block,如:屬於區段15的區塊63)。
  • 控制區塊的前6位元組是金鑰A,預設全是0xFF,但顯示成00。
  • 最右邊的AccessBits(直譯為「存取位元」)代表各個區塊的讀寫權限設定,這些參數經過計算之後,存放在控制區塊的第6, 7, 8位元組。如欲進一步了解「存取位元」設置及其計算方式,可參閱NXP半導體的MIFARE Classic技術文件的Access conditions單元(PDF格式),或者MIFARE Classic 1K Access Bits Calculator(存取位元計算器)

讀取與寫入資料到Mifare卡片

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

  • MFRC522物件.PCD_Authenticate():驗證金鑰,相當於比對輸入密碼和卡片裡的密碼,唯通過驗證才能存取區段資料。
  • MFRC522物件.GetStatusCodeName():取得狀態碼的名稱
  • MFRC522物件.MIFARE_Read():讀取指定區塊的內容
  • MFRC522物件.MIFARE_Write():在指定區塊寫入資料
  • MFRC522物件.PICC_DumpMifareClassicSectorToSerial():在序列埠監控視窗顯示指定的區段內容

設定金鑰

讀取卡片內部資料的流程,請參閱「Mifare RFID-RC522模組實驗(一):讀取Mifare RFID卡的UID識別碼」貼文,讀、寫資料之前,都需要通過金鑰驗證。設定儲存金鑰的變數的語法如下:

設定儲存金鑰的變數

金鑰值儲存在key物件裡的keyByte陣列屬性,空白卡的金鑰A和B的出廠預設值都是6組 0xFF

金鑰值儲存在key物件裡的keyByte陣列屬性

驗證金鑰

採用「金鑰A」驗證金鑰的PCD_Authenticate()方法的語法如下,其中的&key參數將指向上一節設定的金鑰值;金鑰值和卡片的唯一識別碼,都必須透過位址引用(亦即,在參數前面加上&符號),而非直接傳入數值。trailerBlock參數存放控制區塊的編號;這個方法將傳回一個包含驗證結果(通過與否)的狀態碼(status code)

驗證金鑰的PCD_Authenticate()方法

控制區塊以及資料區塊的編號值介於0~63。以讀、寫區段1的區塊1為例,此區段的控制區塊編號為7;區塊1的編號為5。因此,驗證區段1時,trailerBlock參數要傳入7

控制區塊以及資料區塊的編號值

程式可透過上圖底下的兩則運算式求得資料區塊和控制區塊的編號值。

儲存狀態碼的status變數,請透過底下的語法宣告。

儲存狀態碼的status變數

底下的條件判斷式將在驗證未通過時,於序列埠監控視窗輸出代表「驗證失敗」的訊息:

序列埠監控視窗輸出代表「驗證失敗」的訊息

讀取區塊的MIFARE_Read()方法

讀取區塊資料的核心敘述如下,請先設定一個儲存讀取值的陣列變數,雖然區塊資料的長度是16位元組,但是MFRC522程式庫規定至少需要準備18位元組大小的陣列來存放讀取值;讀取和寫入區塊資料的方法也會傳回狀態碼。

讀取區塊的MIFARE_read()方法

底下是負責讀取區塊資料的自訂函式readBlock(),它接受三個參數:區段編號區塊編號資料陣列

void readBlock(byte _sector, byte _block, byte _blockData[])  {
  // 判斷區段值是否介於0~15、區塊值介於0~3…
  if (_sector < 0 || _sector > 15 || _block < 0 || _block > 3) {
// 如果不是,則顯示錯誤訊息並結束此函式。
    Serial.println(F("Wrong sector or block number."));
    return;
  }

  byte blockNum = _sector * 4 + _block;  // 計算區塊的實際編號(0~63)
  byte trailerBlock = _sector * 4 + 3;   // 控制區塊編號

  // 驗證金鑰
  status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  // 若未通過驗證…
  if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }
  
  // 讀取指定的區塊
  byte buffersize = 18;
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockNum, _blockData, &buffersize);

  // 若讀取不成功…
  if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息並結束此函式
    Serial.print(F("MIFARE_read() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }

  // 顯示「讀取成功!」
  Serial.println(F("Data was read."));
}

上面的程式可顯示單一區塊的資料,若要傾印Mifare Classic卡片的特定區段(亦即,4個區塊),可透過MFRC522程式庫內建的PICC_DumpMifareClassicSectorToSerial()。執行此方法時,需要傳入卡片識別碼、驗證碼和區段編號,例如,底下的敘述將在序列埠監控視窗輸出整個區段15的資料:

mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, 15);

寫入資料的MIFARE_Write()方法

寫入區塊資料的核心敘述如下,請先宣告一個16位元組的陣列變數(此處命名為blockData),在其中存入即將寫入卡片的資料:

寫入資料的MIFARE_Write()方法

假設要把資料存入區段1的區塊1,區塊編號值請設定成5。

寫入與讀取Mifare卡片資料的程式碼

本實驗的電路和零件,與「Mifare RFID-RC522模組實驗(一):讀取Mifare RFID卡的UID識別碼」貼文相同。底下的程式碼將在掃描到Mifare Classic卡片時,在區段15的區塊1,寫入“Keep Hacking!”字串:

#include <SPI.h>
#include <MFRC522.h>

#define RST_PIN    A0     // Reset腳
#define SS_PIN     10     // 晶片選擇腳

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

MFRC522::MIFARE_Key key;  // 儲存金鑰

byte sector = 15;   // 指定讀寫的「區段」,可能值:0~15
byte block = 1;     // 指定讀寫的「區塊」,可能值:0~3
byte blockData[16] = "Keep Hacking!";   // 最多可存入16個字元
// 若要清除區塊內容,請寫入16個 0
//byte blockData[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

// 暫存讀取區塊內容的陣列,MIFARE_Read()方法要求至少要18位元組空間,來存放16位元組。
byte buffer[18];

MFRC522::StatusCode status;

void writeBlock(byte _sector, byte _block, byte _blockData[]) {
  if (_sector < 0 || _sector > 15 || _block < 0 || _block > 3) {
    // 顯示「區段或區塊碼錯誤」,然後結束函式。
    Serial.println(F("Wrong sector or block number."));
    return;
  }

  if (_sector == 0 && _block == 0) {
    // 顯示「第一個區塊只能讀取」,然後結束函式。
    Serial.println(F("First block is read-only."));
    return;
  }

  byte blockNum = _sector * 4 + _block;  // 計算區塊的實際編號(0~63)
  byte trailerBlock = _sector * 4 + 3;   // 控制區塊編號

  // 驗證金鑰
  status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  // 若未通過驗證…
  if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }

  // 在指定區塊寫入16位元組資料
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockNum, _blockData, 16);
  // 若寫入不成功…
  if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F("MIFARE_Write() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }

  // 顯示「寫入成功!」
  Serial.println(F("Data was written."));
}

void readBlock(byte _sector, byte _block, byte _blockData[])  {
  if (_sector < 0 || _sector > 15 || _block < 0 || _block > 3) {
    // 顯示「區段或區塊碼錯誤」,然後結束函式。
    Serial.println(F("Wrong sector or block number."));
    return;
  }

  byte blockNum = _sector * 4 + _block;  // 計算區塊的實際編號(0~63)
  byte trailerBlock = _sector * 4 + 3;   // 控制區塊編號

  // 驗證金鑰
  status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
  // 若未通過驗證…
  if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }

  byte buffersize = 18;
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockNum, _blockData, &buffersize);

  // 若讀取不成功…
  if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F("MIFARE_read() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
  }

  // 顯示「讀取成功!」
  Serial.println(F("Data was read."));
}

void setup() {
  Serial.begin(9600);
  SPI.begin();               // 初始化SPI介面
  mfrc522.PCD_Init();        // 初始化MFRC522卡片

  Serial.println(F("Please scan MIFARE Classic card..."));

  // 準備金鑰(用於key A和key B),出廠預設為6組 0xFF。
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }

}

void loop() {
  // 查看是否感應到卡片
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return;  // 退回loop迴圈的開頭
  }

  // 選取一張卡片
  if ( ! mfrc522.PICC_ReadCardSerial()) {  // 若傳回1,代表已讀取到卡片的ID
    return;
  }

  writeBlock(sector, block, blockData);  // 區段編號、區塊編號、包含寫入資料的陣列

  readBlock(sector, block, buffer);      // 區段編號、區塊編號、存放讀取資料的陣列

  Serial.print(F("Read block: "));        // 顯示儲存讀取資料的陣列元素值
  for (byte i = 0 ; i < 16 ; i++) {
    Serial.write (buffer[i]);
  }
  Serial.println();

  // 令卡片進入停止狀態
  mfrc522.PICC_HaltA();
}

第128行程式使用Serial.write()而非Serial.print()方法,是因為print()會把字元的ASCII數字值轉換成文字顯示,而write()則直接把字元顯示出來(請參閱這個回應)。

上傳程式之後,請開啟序列埠監控視窗,並掃描Mifare卡片,將能見到下圖的結果:

顯示寫入與讀取卡片資料

延伸閱讀

Posts created 467

86 thoughts on “Mifare RFID-RC522模組實驗(三):讀取與寫入資料

  1. 您好,可以請問一下,如果Key A被我改成不是預設值的0xFF,要怎麼變回預設值。或是在哪裡可以輸入驗證的密碼。

    謝謝

    1. 「輸入驗證密碼」的程式部份,請參閱上文「設定金鑰」一節。

      thanks,
      jeffrey

  2. 我把
    for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
    }
    換成(aaaaa)
    key.keyByte[0] = 0x61;
    key.keyByte[1] = 0x61;
    key.keyByte[2] = 0x61;
    key.keyByte[3] = 0x61;
    key.keyByte[4] = 0x61;
    key.keyByte[5] = 0xFF;
    仍然會出現PCD_Authenticate() failed: Timeout in communication.

  3. 你好,我想要請教一下
    我現在程式打完有一塊是
    把第5腳設置為輸出,第六腳為輸入
    請問為何我第六腳要直接讀取第五腳的準位(1or0)
    不管怎樣設定(強制為0),他只會讀到1
    是硬體本身不能嗎?

  4. 您好,我想請問 我照用了一次code 發現可以正常work,第一次有正常讀寫
    但卻無法再一次寫入或讀取,請問我是哪裡錯了嗎??

    1. 您好
      已自己找到解答
      只要修正最後的
      // Halt PICC
      mfrc522.PICC_HaltA();
      // Stop encryption on PCD
      mfrc522.PCD_StopCrypto1();
      即可~ 感謝

    2. 您好:
      我也遇到此問題,然後依照您說的修改後面程式
      // Halt PICC
      mfrc522.PICC_HaltA();
      // Stop encryption on PCD
      mfrc522.PCD_StopCrypto1();
      還是沒有辦法再次寫入或讀取
      請問我是哪裡錯錯嗎??
      (程式碼是依照上面)

    3. 好的,我明天測試看看。

      [4/9更新] 剛剛再度測試上文的程式,第一次寫入”Happy Hacking!”,第二次在同一個區塊寫入”Cool Hacking!”,全都讀寫正常。

      thanks,
      jeffrey

    4. 您好:
      如果我想要一次寫入兩筆資料可以怎麼做呢?
      我找了很多資料都只有一次寫入一筆。
      謝謝您
      祝您有個愉快的一天

    5. 「很多資料都只有一次寫入一筆」 –> 因為寫入多筆,就是重複寫入一筆的步驟。

      假設你要寫入的區段的金鑰A都是預設的6組 0xFF,那麼,你只需要執行兩次writeBlock()自訂函式:

      writeBlock(sector, block, blockData); // 第一組資料
      writeBlock(sector2, block2, blockData2); // 第二組資料

      如果金鑰不同,程式就要個別設定金鑰,例如,在writeBlock()自訂函式的定義當中,加入key參數:

      void writeBlock(byte _sector, byte _block, byte _blockData[], MFRC522::MIFARE_Key key) {
      :
      }

      並且分別設置金鑰:

      MFRC522::MIFARE_Key k1; // 儲存金鑰
      MFRC522::MIFARE_Key k2; // 儲存金鑰

      寫入兩個不同的區段:

      writeBlock(sector, block, blockData, k1);
      writeBlock(sector1, block1, blockData1, k2);

      以此類推。

      thanks,
      jeffrey

  5. 你好,
    我有重新下載過軟體、板子也都有設定好
    上傳後出現 開發板 Arduino/Genuino Uno 編譯錯誤 的問題
    請問該怎麼解決?

    1. 編譯錯誤通常是缺少安裝引用的程式庫,或者程式語法錯誤,實際要看錯誤訊息。

      thanks,
      jeffrey

  6. 老師您好:
    我已執行兩次
    writeBlock(sector, block, blockData); // 第一組資料
    writeBlock(sector2, block2, blockData2); // 第二組資料
    也在前面指定讀寫的區塊和區段還有要存入的字元
    但出現 sketch_apr11a:127: error: ‘blockData2’ was not declared in this scope
    這樣的錯誤
    我應該如何解決?

    想問老師
    關於 Mifare RFID 卡 的教學,是只有出現在”超圖解3″中嗎?
    還是您有其他書籍有更詳細的解說呢?
    謝謝老師

    1. ‘blockData2’ was not declared in this scope
      代表程式沒有定義變數,抱歉,我目前沒有其他RFID的文章,而且你的問題主因也不是RFID。

      thanks,
      jeffrey

  7. 老師你好:
    我想要連續讀取不同張卡片,讀取第一次時正常,但讀取第二次時監控視窗出現空白,請問是什麼原因?
    thanks

  8. 老師你好:
    第一次感測完卡片,我必須要把板子從電腦移除再重新插入才可以做第二次的讀取跟寫入的動作。
    這是正常的嗎?
    Thanks

    1. 嗯,不正常,執行上文的程式碼也不行嗎?讀取卡片之後有沒有執行 mfrc522.PICC_HaltA(); 這個敘述?

      thanks,
      jeffrey

  9. 老師你好:
    用上面的程式碼做執行,依然遇到相同問題。
    在執行第一次時,監控視窗可以正常顯示,在沒有移除板子和重新上傳程式下,直接進行第二次讀寫,監控視窗沒有任何反應!
    重新上傳程式但沒有移除板子執行後,監控視窗只會出現Please scan MIFARE Classic card…。
    只有在移除板子後才又可以正常執行!
    Thanks

    1. 剛剛再次接線、驗證程式碼,在我的板子上測試無誤,可以多次連續感應卡片。

      thanks,
      jeffrey

  10. 老師
    我想問一下,我想寫一套關於RFID密碼鎖的設計
    而流程是如果錯誤前兩次會顯示錯誤第三次會發出警報並且只能透過一張卡片讀寫這個密碼鎖其餘的卡片都會顯示錯誤
    請問該如何寫,謝謝老師

  11. 老師我想問一下,如果卡片讀寫成功不會復歸是正常的嗎?
    如果要讓它復歸原始狀態要怎麼做呢?謝謝老師

    1. 請問「復歸原始狀態」是什麼意思?讀卡機準備寫入下一張卡片的意思嗎?

      thanks,
      jeffrey

  12. 老師我想問你一下
    可以把這組程式改寫成只有讀取和驗證就好而不要寫入卡片請問該如何去修改程式碼?

    1. 請閱讀並練習本系列文章一、二以及本文,妳就知道怎麼修改了。

      thanks,
      jeffrey

  13. 老師對不起,我想特地再問一下
    如果RFID跟4X4第三篇文章結合可以防衝突Void loop()的那一個部分嗎?
    比如說卡片感應過驗證後,跳到密碼部分再次感應卡片就不會再跑回感應卡片的程式碼部分。
    因為是專題需要接觸到所以想特地問一下老師如何防衝突這一個區塊
    祝老師有個美好的一天!

    1. 妳的意思是卡片感應過後,讓用戶輸入密碼嗎?
      這兩個流程並不衝突,可以個別處理,
      妳可以用一個變數紀錄卡片是否通過感應,
      如果通過,就顯示輸入密碼的畫面,
      否則顯示WRONG…之類的。

      thanks,
      jeffrey

    2. 老師我還發現到因為RFID也有KEY這個字元存在導致系統會有所衝突請問有什麼可以解決這個問題?
      謝謝老師

    3. 我不懂妳的問題,妳是說,程式已經定義了名叫KEY的變數,另一個變數不能叫KEY,該怎麼辦……嗎?

  14. 老師我今天有在試過兩個放在一起
    現在我想問說,假如我想把void loop()的這段放進去副程式裡面是否有辦法
    if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return; // 退回loop迴圈的開頭
    }
    // 選取一張卡片
    if ( ! mfrc522.PICC_ReadCardSerial()) { // 若傳回1,代表已讀取到卡片的ID
    return;
    }
    readBlock(sector, block, buffer);

    1. 就比如我這段自行宣告的副程式是否可以,還是說感應卡片的一定要放在VOID loop迴圈裏頭
      void readcard(){
      if ( ! mfrc522.PICC_IsNewCardPresent()) {
      return; // 退回loop迴圈的開頭
      }
      // 選取一張卡片
      if ( ! mfrc522.PICC_ReadCardSerial()) { // 若傳回1,代表已讀取到卡片的ID
      return;
      }
      readBlock(sector, block, buffer); // 區段編號、區塊編號、存放讀取資料的陣列
      Serial.println();
      mfrc522.PICC_HaltA();
      }

  15. 老師目前上面問題排除了,可是我想請教老師一件事
    假如我密碼鎖顯示解鎖之後要怎麼重新跑回先掃描卡片的地方
    老師我可以給你看我的程式嗎?
    謝謝老師
    have a wonderful day

  16. 老師上述的問題我都解決了,可是我想再請教老師一個問題
    就是我密碼鎖的程式跑過一次之後第二次會自動跳過掃描卡片的那段文字
    可是我有指令最後一行如果輸入正確會跑回void setup();
    我試過很多辦法去排除跳過該段程式碼的這個問題可是最後都還是失敗
    我想請教老師請問有甚麼程式可以阻擋跳過程式碼的辦法
    最後謝謝老師!!

    1. 老師還是說有什麼樣的程式碼可以讓程式一定強制執行這一段?

    2. 老師我找到竅門了以上問題我都迎刃而解了
      謝謝老師的耐心指導

  17. MFRC522::STATUS_OK老師我被專題老師考倒這一行了
    我想請問雙冒號代表什麼
    而STATUS_OK是甚麼意思
    謝謝老師

  18. 老師我還想問一個問題
    今天老師也跟我說動作要掃描卡片後,如果10秒內沒輸入密碼要跳回初始介面
    那我想請問老師要如何達成10秒內跳出該段副程式?用中斷的方式嗎

  19. 老師我最後想再問最後一個問題了。就是如果想要讓薄膜鍵盤按下去時可以發出聲音請問可以怎麼做?

    1. 在處理按鍵的程式當中加入tone()函式發聲即可,相關說明請參閱13-5頁。

      thanks,
      jeffrey

  20. 老師您好:
    我想請問一下如果不小心將Sector15的第3個blcok(全部的第63個)的資料都清除了
    還有辦法還原回來嗎?

    因為現在不管是讀取或修改資料都無法操作了,難道這樣一張白卡就報銷了嗎? XD

    感謝

    1. 老師您好:
      奇怪的地方就在這裡,我也是覺得可以讀寫其他Sector,但用同一個程式(dumpinfo)後面的Sector也全部看不到了
      我在想會不會是程式固定從sector15讀到sector0這個順序導致第一個sector15無效之後後面也都無效了?

      另外我還是有嘗試用您範例中的寫入範例將block 63的資料改回來,但目前尚未成功,會持續嘗試

      感謝老師

  21. 赵老师,你好
    请问,
    我的一张空白卡最开始模组读可以取,之后呢,使用手机一个nfc软件向里边写入了数据,用手机可以读取,可是想再用模组读取却只能读到uid,剩下所有区段的显示都为读取超时出错。
    后来在用手机的nfc软件擦除掉原来写入的数据,用手机再次读取,发现没有数据,但模组却还是显示超时。
    这是什么情况呢?有什么办法可以解决呢?
    谢谢
    祝老师新春快乐!

  22. 老師 您好,
    我想要請教一個問題。

    一般我們所使用RFID,若是用在金錢交易或是一般的門禁上。
    每張RFID的金鑰都是不一樣的嗎?

    謝謝!

    1. 老師,不好意思 int要轉回byte時 之後讀取int的數值都不一樣是寫入錯誤嗎

    2. 在寫入資料之前,用print確認轉換內容,看看是轉換錯誤還是寫入錯誤。

      thanks,
      jeffrey

  23. 老師你好請問
    Serial.println(“請輸入加值金額:”);
    if(Serial.available()>0){
    byte a=Serial.read();
    money[16]+= a ;
    }
    這樣出來結果是寫入失敗

    1. 你應該把收到的字元轉成數字,再寫入RFID,請參閱10-9頁的「動手做10-3」。

      thanks,
      jeffrey

    2. 用String出來的字串是ASCII碼那要分開轉換嗎? 那要用char 還是long來轉換會比較好

  24. 老師不好意思 我int轉換成money[16] 的數值跟原本int 的數值不一樣,可是把money[16]的”[16]陣列”拿掉數值就一樣了可是這樣一來就寫不進去RFID卡片裡了 該怎麼辦

    1. 不太了解你的意思,不過,整數佔兩個位元組,存取整數之前你有先將它轉換成兩個位元組分別存放嗎?相關範例請參閱11-17頁的「在I2C介面上傳送整數資料」。

      thanks,
      jeffrey

    2. 老師新年快樂
      分別存放後 直接寫入可以成功,可是下一次讀取卡片時資料是0

  25. 老師好,我在測試寫入資料的程式時發現,用NFC Tools寫過資料的白卡去讀取就會不斷跳出PCD_Authenticate() failed: Timeout in communication. ,即使把白卡寫的資料全部清除還是一樣,想請問是什麼問題? 另外老師有把寫入和讀取縮成一個程式的嗎?

    1. 感謝告知…我沒有試過用NFC Tools寫入資料,都是直接在Arduino上操作。

      thanks,
      jeffrey

  26. 您好,請問一下,上面設定金鑰的程式是有指定哪個區塊還是這塊卡的每個區塊都改成那個金鑰?

  27. 請問如何自訂金鑰??
    不太常用arduino不太明白
    這段是用循環的金鑰都一樣還行,但我的金鑰是A0 A1 A2 A3 A4 A5 該怎麼定義金鑰??

    // 準備金鑰(用於key A和key B),出廠預設為6組 0xFF。
    for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
    }

  28. 遇到不知道金鑰的卡片,是否可以暴力破解出來?
    M1卡是否就可以成為UID卡。

  29. 請問在”寫入與讀取Mifare卡片資料的程式碼” 那邊第83行
    _blockData前面的底線代表什麼意思

  30. 老師您好,我有兩個問題想問
    第一:
    我想要紀錄偵測不同卡片的數量:
    A刷一次跳1,再刷B跳2,A或B再刷一樣是2

    結果不是同一張重複刷跳1、2、3…
    就是用不同張去讀取都只跳1、1、1…
    第二:我想要讓同一張有類似以下的功能:
    If第一次偵測到,LED亮
    If第二次偵測到,LED熄…以此類推
    拜託老師指導

  31. 請問老師寫入卡片裡的陣列數值要怎麼把讀出來的數值變成可以作加減的動作,請老師指導,謝謝老師
    for (byte i = 0 ; i < 16 ; i++) {
    Serial.write( buffer[i] );
    }

  32. 請問老師,這個是RFID寫入的陣列,要怎麼利用變數代替數字來寫入這個陣列。
    byte blockData[16] = “100”;
    我有試過
    A=100;
    byte blockData[16] = A;
    但是行不通
    或是老師您有另一種寫法,拜託幫幫我。

    1. 重新檢視了你的問題,出現在資料類型錯誤,dataBlock是陣列,所以要像這樣寫入資料:

      byte blockData[16];
      blockData[0] = 100;
      blockData[1] = 24;

      thanks,
      jeffrey

  33. 我嘗試了老師您的方式,但是出現這句話,我查了翻譯還是不太懂
    ‘blockData’ does not name a type
    是要額外宣告嗎?
    還是我做錯方法
    請老師指導

    1. 宣告之後,在setup()或者你的程式裡面設定值:

      blockData[0] = 100;

      thanks,
      jeffrey

  34. 老師您好 我想請問為什麼
    第二標題寫 “每個區段的區塊3也叫做控制區塊” ,
    然後第三標題又寫 “每個區段最後一個區塊,都是控制區塊(Trailer Block,如:屬於區段15的區塊63)。” ?

    是我哪裡理解錯誤了嗎?

  35. Dear 老師:
    我遇到一個問題, 想請問老師是否能給一些解決方向.
    問題說明:
    – 環境: arduino uno + RC522(card reader),
    – 程式碼: (https://hackmd.io/aVGVVGkASWKdGSup7YlJCg?view)
    – setup區: 有加入 pinMode(5, OUTPUT); // 用pin5去控制繼電器
    – loop區: 與您的程式碼相同; 有額外判斷若UID若符合即令pin 5 的RELAY=HIGH
    – 電路: 有接RELAY 與 電磁閥開關
    – 問題:
    – 當不接上電磁閥, 所有動作執行皆正常(含: 燈泡、繼電器)。 電磁閥也都可以正常動作。
    當接上電磁閥後, 讀取一次後, 便無法進行第二次的讀卡。
    請問是什麼原因呢? 謝謝老師

發佈留言

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

Related Posts

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

Back To Top