PN532 NFC RFID module + Arduino で FeliCa と Mifare を判別する

(Felica/Mifare/NFC チャレンジシリーズ) その他の記事はこちら 「Felica/Mifare/NFC でいろいろ実験」
https://qiita.com/nanbuwks/items/1f416d6e45a87250ee0a

-----

PN532 NFC RFID module は Felica に対応していますが、これを使って FeliCa と Mifare 両方に対応した処理をしたい。



# 環境
- PN532 NFC RFID module
  - 電解コンデンサ追加加工済
  - cf., 「NFC モジュールの動作安定性を改善する」https://qiita.com/nanbuwks/items/a9217ba2e8f206b899b1
- Arduino 1.8.10 Linux版 (ポータブル化済)
- Arduino UNO
- elechouse/PN532 ライブラリ

# 準備

ライブラリのインストールや設定、Arduino への接続方法などはこちらの通りに行いました。
「PN532 NFC RFID module を Arduino UNO で読む」
https://qiita.com/nanbuwks/items/42f639e7eec928181298



# 調査

まずはライブラリとして、Felica/Mifare 両方に対応している elechouse/PN532 ライブラリを元に考えてみます。
作戦として、カード検出時に Felica/Mifare どちらかを検出し、それによって処理を分けるようにすれば良さそう。しかしながら elechouse/PN532 ライブラリ内に、 FeliCa/Mifare 両方に対応したカード検出は入っていません。
Felica と Mifare だと搬送波周波数こそ同じだけど、通信速度も変調方式も違うのだけどできるかな?




https://www.nxp.com/docs/en/nxp/data-sheets/PN532_C1.pdf
を見ると、


> 8.6.11 Data mode detector
The data mode detector is able to detect received signals according to the ISO/IEC 14443A/MIFARE, FeliCa or NFCIP-1 schemes and the standard baud rates for 106 kbit/s, 212 kbit/s and 424 kbit/s in order to prepare the internal receiver in a fast and convenient way for further data processing.
The data mode detector can only be activated by the AutoColl command (see Section 8.6.20.12 “AutoColl command” on page 137). The mode detector is reset, when no external RF field is detected by the RF level detector.
The data mode detector could be switched off during the Autocoll command by setting the bit ModeDetOff in the register Mode to logic 1 (see Table 207 on page 157).


という記述がありました。AutoCollという機能が使えそうで、もう少し調べてみます。

> 8.6.14.2 Polling sequence functionality for target
1. The 80C51 has to configure the CIU with the correct polling response parameters for the Polling command.
2. To activate the automatic polling in target mode, the AutoColl Command has to be activated.
3. The CIU receives the polling command send out by an initiator and answers with the polling response. The timeslot is selected automatically (The timeslot itself is randomly generated, but in the range 0 to TSN, which is defined by the polling command). The CIU compares the system code, stored in byte 17 and 18 of the Config Command with the system code received with the polling command by an initiator. If the system code is equal, the CIU answers according to the configured polling response. The system code FF(hex) acts as a wildcard for the system code bytes (i.e. a target of a system code 1234(hex) answers to the polling command with one of the following system codes 1234(hex), 12FF(hex), FF34 (hex) or FFFF(hex)). If the system code does not match no answer is sent back by the PN532. If a valid command, which is not a Polling command, is received by the CIU, no answer is sent back and the command AutoColl is stopped. The received frame is stored in the FIFO.


> 8.6.20.12 AutoColl command
This command automatically handles the MIFARE activation and the FeliCa polling in the Card Operation mode. The bit Initiator in the CIU_Control register has to be set to logic 0 for correct operation. During this command, Mode Detector is active if not deactivated by setting the bit ModeDetOff in the CIU_Mode register. After Mode Detector detects a mode, the mode dependent registers are set according to the received data. In case of no external RF field this command resets the internal state machine and returns to the initial state but it will not be terminated.
When the Autocoll command terminates the Transceive command gets active.
During Autocoll command:
• The CIU interrupt bits, except RfOnIRq, RfOffIRq and SIGINActIRq (see Table 187 on
page 149), are not supported. Only the last received frame will serve the CIU
interrupts.
• During ISO/IEC 14443A activation, TxCRCEn and RxCRCEn bits are defined by the
AutoColl command. The changes cannot be observed at the CIU_TxMode and
CIU_RxMode registers. When the Transceive command is active, the value of the bits
is relevant.
• During Felica activation (polling), TxCRCEn and RxCRCEn bits are always relevant
and are not overruled by the Autocoll command. Their value must be set to logic 1
according the FeliCa protocol.
Note: Pay attention, that the FIFO will also receive the two CRC check bytes of the last
command, even if they are already checked and correct, and if the state machine
(Anticollision and Select routine) has not been executed, and 106 kbit is detected.
This command can be cleared by firmware by writing any other command to the
CIU_Command register, e.g. the Idle command. 
Writing the same content again to the CIU_Command register resets the state machine.


コマンドの詳細はユーザマニュアル
https://www.nxp.com/docs/en/user-guide/141520.pdf

に示されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/d0fff43c-3e15-89e9-bc2e-0526d89ff3d5.png)

あれれ？ ここでは AutoColl ではなく AutoPoll になってますね。これは別物ではなく、表記ゆれのようです。

# ライブラリを変更

elechouse/PN532 ライブラリに手を加える方法で AutoColl/AutoPoll を実装してみます。

以下を PN532.h に加えます。


```

    int autoPoll(uint8_t cardtype1,  uint8_t cardtype2 , uint16_t pollnumber,  uint16_t timeout);
```
以下を PN532.cpp に加えます。

```
int PN532::autoPoll(uint8_t cardtype1,  uint8_t cardtype2 , uint16_t pollnumber,  uint16_t timeout)
{

    pn532_packetbuffer[0] = 0x60;
    pn532_packetbuffer[1] = pollnumber;  // 1 times polling
    pn532_packetbuffer[2] = timeout; // 150ms x N times
    pn532_packetbuffer[3] = cardtype1; // cardtype1
    pn532_packetbuffer[4] = cardtype2; // cardtype2

    if (HAL(writeCommand)(pn532_packetbuffer, 5)) {
        DMSG("write fail");
        return 0xFF;  // command failed
    }

    // read data packet
    if (HAL(readResponse)(pn532_packetbuffer, sizeof(pn532_packetbuffer), 1000) < 0) {
        DMSG("read fail");
        return 0xFF;
    }


    if (pn532_packetbuffer[0] != 1){
        DMSG("card not detect");
        return 0xFF;
    }

    return     pn532_packetbuffer[1];
}
```

# サンプルスケッチ

```

  #include <SPI.h>
  #include <PN532_SPI.h>
  #include "PN532.h"

  PN532_SPI pn532spi(SPI, 10);
  PN532 nfc(pn532spi);

void setup(void) {
  Serial.begin(115200);
  Serial.println("Hello!");
  nfc.begin();
  uint32_t versiondata = nfc.getFirmwareVersion();
  if (! versiondata) {
    Serial.print("Didn't find PN53x board");
    while (1); // halt
  }
  Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 
  Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 
  Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
  nfc.SAMConfig();
  Serial.println("Waiting for NFC card ...");
}


void loop(void) {
  uint8_t detectCard;
  
  detectCard = nfc.autoPoll(0x11,0x10,1,1);
  if ( 0x10 == detectCard ) Serial.println("Card is Mifare");
  if ( 0x11 == detectCard ) Serial.println("Card is FeliCa");
  delay(100);
    
}
```

```
    int autoPoll(uint8_t cardtype1,  uint8_t cardtype2 , uint16_t pollnumber,  uint16_t timeout);
```

cardtype1 と cardtype2 で指定された2つの形式のカードを検出し、検出したカードタイプを返します。
検出しなかった場合は0xFFが返ります。

カードタイプなどのパラメータは以下のとおりです。

```

/*
cardtype:
0x00 : Generic passive 106 kbps (ISO/IEC14443-4A, Mifare and DEP),
0x01 : Generic passive 212 kbps (FeliCa and DEP)
0x02 : Generic passive 424 kbps (FeliCa and DEP),
0x03 : Passive 106 kbps ISO/IEC14443-4B,
0x04 : Innovision Jewel tag,
0x10 : Mifare card,
0x11 : FeliCa 212 kbps card,
0x12 : FeliCa 424 kbps card,
0x20 : Passive 106 kbps ISO/IEC14443-4A,
0x23 : Passive 106 kbps ISO/IEC14443-4B,
0x40 : DEP passive 106 kbps,
0x41 : DEP passive 212 kbps,
0x42 : DEP passive 424 kbps,
0x80 : DEP active 106 kbps,
0x81 : DEP active 212 kbps,
0x82 : DEP active 424 kbps.

pollnumber:
number of polling
one oilling is a polling for each 2 cardtype
if 0xFF means endless polling

timeout:
unit of 150ms 
*/


```

詳細は、NXP PN532 ユーザマニュアルを参照。
https://www.nxp.com/docs/en/user-guide/141520.pdf


# 実行結果

```

Hello!
Found chip PN532
Firmware ver. 1.6
Waiting for NFC card ...
Card is Mifare
Card is Mifare
Card is Mifare
Card is Mifare
Card is Mifare
Card is FeliCa
Card is FeliCa
```


うまくいきました


# その後

  nfc.autoPoll(0x11,0x10,1,1);

で FeliCa を検出

↓

FeliCa 操作

↓
 nfc.autoPoll(0x11,0x10,1,1);

で Mifare を検出

↓
    success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);

が失敗

という事例が発生することがわかりました。また、nfc.autoPollを繰り返していても 
    success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
が失敗するようです。

いろいろ調べると、FeliCa 操作のあと、一度ダミーで
   success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
を行うとうまくいくようです。


なので FeliCa Mifare の判定は 以下のようにすることにしました。

```
void loop(void)
{
  uint8_t detectCard;
  uint8_t success,success2;
  uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };  // Buffer to store the returned UID
  uint8_t uidLength;     
  
  Serial.print("MifareCard Checking...");
  success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
  Serial.println(success);
  if ( success ){
    Serial.println("Card is Mifare");
    readMifare();
  }
  Serial.print("detectCard Checking...");
  detectCard = nfc.autoPoll(0x11,0x10,1,1);
  Serial.println(detectCard);
  if ( 0x11 == detectCard ){
    Serial.println("Card is FeliCa");
    readFeliCa();
    success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
  }

}

void readMifare(){
 ...
}
void readFeliCa(){
 ...
}

```