pdf-icon

Arduino入門

2. デバイス&サンプル

6. アプリケーション

Dial RFID 近距離無線通信(NFC)

Dial RFID(NFC)に関する API とサンプルプログラム。

読取位置
RFID タグの外形が Dial の外形より小さい場合、タグを Dial に当てる位置に注意してください。画面中央でベゼルに完全に囲まれる位置は避け、タグの中心は M5 ロゴから離し、反対側の矢印マーク付近に近づけることを推奨します。

サンプルプログラム

ビルド要件

  • M5Stack ボードマネージャ バージョン >= 3.2.2
  • ボードオプション = M5Dial
  • M5Dial ライブラリ バージョン >= 1.0.3

UID 読み取りサンプル

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
#include "M5Dial.h"

void setup() {
  auto cfg = M5.config();
  M5Dial.begin(cfg, false, true);  // encoder, RFID
  Serial.begin(115200);
}

void loop() {
  // PICC: Proximity Integrated Circuit Card
  if (M5Dial.Rfid.PICC_IsNewCardPresent() && M5Dial.Rfid.PICC_ReadCardSerial()) {
    M5Dial.Display.clear();

    uint8_t piccType = M5Dial.Rfid.PICC_GetType(M5Dial.Rfid.uid.sak);
    Serial.print(F("PICC type: "));
    Serial.println(M5Dial.Rfid.PICC_GetTypeName(piccType));

    // Check if the tag / card is of type MIFARE Classic
    if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI && piccType != MFRC522::PICC_TYPE_MIFARE_1K && piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
      Serial.println(F("This tag / card is not of type MIFARE Classic.\n"));
      delay(500);
      return;
    }

    // Output the stored UID data
    for (byte i = 0; i < M5Dial.Rfid.uid.size; i++) {
      Serial.printf("%02X ", M5Dial.Rfid.uid.uidByte[i]);
    }
    Serial.println("\n");
    delay(500);
  }
}

カードの読み書きサンプル

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
#include "M5Dial.h"

MFRC522::MIFARE_Key key;

void setup() {
  auto cfg = M5.config();
  M5Dial.begin(cfg, false, true);  // encoder, RFID
  Serial.begin(115200);

  // Prepare the key (used both as key A and as key B) with FFFFFFFFFFFFh,
  // which is the default at chip delivery from the factory.
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }
}

// Helper routine to dump a byte array as hex values to Serial.
void dump_byte_array(byte *buffer, byte bufferSize) {
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], HEX);
  }
}

void loop() {
  M5Dial.update();

  // PICC: Proximity Integrated Circuit Card
  if (M5Dial.Rfid.PICC_IsNewCardPresent() && M5Dial.Rfid.PICC_ReadCardSerial()) {
    M5Dial.Display.clear();

    uint8_t piccType = M5Dial.Rfid.PICC_GetType(M5Dial.Rfid.uid.sak);
    Serial.print(F("PICC type: "));
    Serial.println(M5Dial.Rfid.PICC_GetTypeName(piccType));

    // Check if the tag / card is of type MIFARE Classic
    if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI && piccType != MFRC522::PICC_TYPE_MIFARE_1K && piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
      Serial.println(F("This tag / card is not of type MIFARE Classic.\n"));
      delay(500);
      return;
    }

    // Output the stored UID data
    String uid = "";
    for (byte i = 0; i < M5Dial.Rfid.uid.size; i++) {
      Serial.printf("%02X ", M5Dial.Rfid.uid.uidByte[i]);
      uid += String(M5Dial.Rfid.uid.uidByte[i], HEX);
    }
    Serial.println("\n");

    // M5Dial.Rfid.PICC_DumpToSerial(&(M5Dial.Rfid.uid));
    // Serial.println("\n");

    // In this example, we use the second sector (sector #1) including blocks #4, #5, #6, #7
    byte sector = 1;
    byte blockAddr = 4;
    byte trailerBlock = 7;
    byte dataBlock[] = {
      0x01, 0x02, 0x03, 0x04,  //  1,  2,  3,   4,
      0x05, 0x06, 0x07, 0x08,  //  5,  6,  7,   8,
      0x09, 0x0a, 0x0b, 0x0c,  //  9, 10, 11,  12,
      0x0d, 0x0e, 0x0f, 0xff   // 13, 14, 15, 255
    };
    MFRC522::StatusCode status;
    byte buffer[18];
    byte size = sizeof(buffer);

    // Authenticate using key A
    Serial.println(F("Authenticating using key A..."));
    status = (MFRC522::StatusCode)M5Dial.Rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(M5Dial.Rfid.uid));
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("PCD_Authenticate() failed: "));
      Serial.println(M5Dial.Rfid.GetStatusCodeName(status));
      return;
    }

    // Show the whole sector as it currently is
    Serial.println(F("Current data in sector: "));
    M5Dial.Rfid.PICC_DumpMifareClassicSectorToSerial(&(M5Dial.Rfid.uid), &key, sector);
    Serial.println("");

    // Read data from the block
    Serial.print(F("Reading data from block #"));
    Serial.print(blockAddr);
    Serial.println(F(" ..."));
    status = (MFRC522::StatusCode)M5Dial.Rfid.MIFARE_Read(blockAddr, buffer, &size);
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("MIFARE_Read() failed: "));
      Serial.println(M5Dial.Rfid.GetStatusCodeName(status));
    }
    Serial.print(F("Data in block #"));
    Serial.print(blockAddr);
    Serial.println(F(": "));
    dump_byte_array(buffer, 16);
    Serial.println("\n");

    // Authenticate using key B
    Serial.println(F("Authenticating again using key B..."));
    status = (MFRC522::StatusCode)M5Dial.Rfid.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(M5Dial.Rfid.uid));
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("PCD_Authenticate() failed: "));
      Serial.println(M5Dial.Rfid.GetStatusCodeName(status));
      return;
    }

    // Write data to the block
    Serial.print(F("Writing data into block #"));
    Serial.print(blockAddr);
    Serial.println(F(" ..."));
    dump_byte_array(dataBlock, 16);
    Serial.println();
    status = (MFRC522::StatusCode)M5Dial.Rfid.MIFARE_Write(blockAddr, dataBlock, 16);
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("MIFARE_Write() failed: "));
      Serial.println(M5Dial.Rfid.GetStatusCodeName(status));
    }
    Serial.println("");

    // Read data from the block again, now it should be what we have written
    Serial.print(F("Reading data from block #"));
    Serial.print(blockAddr);
    Serial.println(F(" ..."));
    status = (MFRC522::StatusCode)M5Dial.Rfid.MIFARE_Read(blockAddr, buffer, &size);
    if (status != MFRC522::STATUS_OK) {
      Serial.print(F("MIFARE_Read() failed: "));
      Serial.println(M5Dial.Rfid.GetStatusCodeName(status));
    }
    Serial.print(F("Data in block #"));
    Serial.print(blockAddr);
    Serial.println(F(": "));
    dump_byte_array(buffer, 16);
    Serial.println("\n");

    // Check if the data in block is what we have written, by counting the number of bytes that are equal
    Serial.println(F("Checking result..."));
    byte count = 0;
    for (byte i = 0; i < 16; i++) {
      // Compare buffer (what we've read) with dataBlock (what we've written)
      if (buffer[i] == dataBlock[i]) {
        count++;
      }
    }
    Serial.print(F("Number of bytes that match = "));
    Serial.println(count);
    if (count == 16) {
      Serial.println(F("Success :-)"));
    } else {
      Serial.println(F("Failure, no match :-("));
      Serial.println(F("  perhaps the write didn't work properly..."));
    }
    Serial.println();

    // Dump the sector data
    Serial.println(F("Current data in sector: "));
    M5Dial.Rfid.PICC_DumpMifareClassicSectorToSerial(&(M5Dial.Rfid.uid), &key, sector);
    Serial.println("");

    // Halt PICC
    M5Dial.Rfid.PICC_HaltA();
    // Stop encryption on PCD
    M5Dial.Rfid.PCD_StopCrypto1();

    Serial.println("====================");
  }
}

このプログラムは、Key A 認証完了後に セクタ 1 のブロック 4 のデータを読み取り、その後 Key B 認証完了後に セクタ 1 のブロック 4 のデータを更新します。処理中はタグ / カードを常に Dial に近づけた状態に保ってください。プログラム出力は次のとおりです:

Init API

以下は一般的な API の使用説明です。MIFARE カードの読取り/書込みの標準フローは次のとおりです:

  1. RFID を初期化する
  2. 新しいカードの存在を検出し、カードの UID (Unique Identifier) を取得する
  3. UID でカードを選択し、active 状態にする
  4. Key A または Key B を用いて対象 Block をアンロックする
  5. データを読み取り / 書き込み (Read / Write) する
  6. カードを sleep 状態にする

操作の戻り値ステータスコード

cpp
1 2 3 4 5 6 7 8 9 10 11
enum StatusCode {
    STATUS_OK             = 1,  // Success
    STATUS_ERROR          = 2,  // Error in communication
    STATUS_COLLISION      = 3,  // Collision detected
    STATUS_TIMEOUT        = 4,  // Timeout in communication
    STATUS_NO_ROOM        = 5,  // The buffer is not big enough
    STATUS_INTERNAL_ERROR = 6,  // Internal error in the code. Should not happen ;-)
    STATUS_INVALID        = 7,  // Invalid argument
    STATUS_CRC_WRONG      = 8,  // The CRC_A does not match
    STATUS_MIFARE_NACK    = 9   // The MIFARE PICC responded with NAK
};

begin

関数プロトタイプ:

void begin();

機能説明:

  • RFID を初期化します。

M5Dial.begin() を呼び出す際に、パラメータ enableRFIDtrue に設定すると同時に初期化できます。

M5Dial.begin(m5::M5Unified::config_t cfg, bool enableEncoder, bool enableRFID);

入力パラメータ:

  • null

戻り値:

  • null

PICC_IsNewCardPresent

関数プロトタイプ:

bool PICC_IsNewCardPresent();

機能説明:

  • まだ検出されていない IDLE 状態のカードが存在するかどうかをスキャンします。HALT 状態のカードは無視されます。

入力パラメータ:

  • null

戻り値:

  • bool
    • true: 新しいカードが検出された
    • false: 新しいカードは検出されなかった

PICC_ReadCardSerial

関数プロトタイプ:

bool PICC_ReadCardSerial();

機能説明:

  • カード UID を読み取ります。読み取りに成功すると、UID はクラスメンバー Uid uid; から取得できます。読み取り操作の前に PICC_IsNewCardPresent()PICC_RequestA()、または PICC_WakeupA() を実行して、カードが検出されていることを確認する必要があります。
cpp
1 2 3
for (byte i = 0; i < M5Dial.Rfid.uid.size; i++) {
  Serial.printf("%02X ", M5Dial.Rfid.uid.uidByte[i]);
}

入力パラメータ:

  • null

戻り値:

  • bool
    • true: 読み取り成功
    • false: 読み取り失敗

PICC_RequestA

関数プロトタイプ:

uint8_t PICC_RequestA(uint8_t *bufferATQA, uint8_t *bufferSize);

機能説明:

  • 読み取り範囲内の Type A 規格カードをスキャンして検出します。

入力パラメータ:

  • uint8_t *bufferATQA
    • リクエスト応答 ATQA (Answer To Request) を格納するバッファ。
  • uint8_t *bufferSize
    • バッファの長さ (>2 byte)。

戻り値:

  • uint8_t
    • StatusCode

PICC_WakeupA

関数プロトタイプ:

uint8_t PICC_WakeupA(uint8_t *bufferATQA, uint8_t *bufferSize);

機能説明:

  • 範囲内の Type A 規格カードをウェイクアップします。

入力パラメータ:

  • uint8_t *bufferATQA
    • リクエスト応答 ATQA (Answer To Request) を格納するバッファ。
  • uint8_t *bufferSize
    • バッファの長さ (>2 byte)。

戻り値:

  • uint8_t
    • StatusCode

PICC_Select

関数プロトタイプ:

uint8_t PICC_Select(Uid *uid, uint8_t validBits = 0);

機能説明:

  • UID でカードを選択し、active 状態にします。

入力パラメータ:

  • Uid *uid
    • スキャンで取得したカード UID 構造体へのポインタ。
  • uint8_t validBits
    • 最後の uint8_t における有効ビット数。0 は 8 ビットすべてが有効であることを示します。

戻り値:

  • uint8_t
    • StatusCode

PICC_HaltA

関数プロトタイプ:

uint8_t PICC_HaltA();

機能説明:

  • 選択中のカードをスリープ状態にします。

入力パラメータ:

  • null

戻り値:

  • uint8_t
    • StatusCode

MIFARE API

PCD_Authenticate

関数プロトタイプ:

uint8_t PCD_Authenticate(uint8_t command, uint8_t blockAddr, MIFARE_Key *key, Uid *uid);

機能説明:

  • MIFARE 認証を実行します。この関数を呼び出す前に、カードを選択してアクティブ状態にする必要があります。認証済み PICC との通信が終了した後は必ず PCD_StopCrypto1() を呼び出してください。そうしないと、新しい通信を開始できません。

入力パラメータ:

  • uint8_t command
    • PICC_CMD_MF_AUTH_KEY_A
    • PICC_CMD_MF_AUTH_KEY_B
  • uint8_t blockAddr
    • Block アドレス
  • MIFARE_Key *key
    • デフォルトでは Key A、Key B はどちらも FFFFFFFFFFFF に設定されています
  • Uid *uid

戻り値:

  • uint8_t
    • StatusCode

PCD_StopCrypto1

関数プロトタイプ:

void PCD_StopCrypto1();

機能説明:

  • PCD の認証状態を解除します。認証済み PICC との通信が終了した後は、必ず PCD_StopCrypto1() を呼び出してください。そうしないと、新しい通信を開始できません。

入力パラメータ:

  • null

戻り値:

  • null

MIFARE_Read

関数プロトタイプ:

uint8_t MIFARE_Read(uint8_t blockAddr, uint8_t *buffer, uint8_t *bufferSize);

機能説明:

  • 指定した blockAddr からデータを読み取ります。

入力パラメータ:

  • uint8_t blockAddr
    • 実際のカードセクタ内の Block アドレス。
  • uint8_t *buffer
    • データを受け取る buffer へのポインタ。
  • uint8_t *bufferSize
    • データを受け取る buffer の長さ (>= 18 byte)。

戻り値:

  • uint8_t
    • StatusCode

MIFARE_Write

関数プロトタイプ:

uint8_t MIFARE_Write(uint8_t blockAddr, uint8_t *buffer, uint8_t bufferSize);

機能説明:

  • 指定した blockAddr にデータを書き込みます。

入力パラメータ:

  • uint8_t blockAddr
    • 実際のカードセクタ内の Block アドレス。
  • uint8_t *buffer
    • 書き込むデータを保持する buffer へのポインタ。
  • uint8_t *bufferSize
    • 書き込みデータの buffer 長 (16 byte)。

戻り値:

  • uint8_t
    • StatusCode

参考リンク

On This Page