raspberryPi に NFCリーダー/ライター(PaSoRi)を繋いでIDを取得するまで

# 環境
+ raspberryPi 3 Model B
+ Raspbian 10(buster)  
+ Python3

CPU(ラズパイのモデル)、OS、pythonの環境は以下の通り

```bash
$ cat cat /proc/cpuinfo | grep Revision
Revision	: a22082

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Raspbian
Description:	Raspbian GNU/Linux 10 (buster)
Release:	10
Codename:	buster

$ python3 --version
Python 3.7.3

$ pip3 --version
pip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7)
```

# NFCリーダーの接続確認
```bash
$ lsusb | grep Sony
Bus 001 Device 007: ID 054c:06c3 Sony Corp. RC-S380
```
 PaSoRi は Sony 製品なのでSony でgrep 

# nfcpyのインストール
```bash
$ sudo pip3 install -U nfcpy
...
$ pip3 list | grep nfcpy
nfcpy             1.0.3
```
-U オプションは不要かもしれない

# プログラムからのNFCリーダーの認識
```python:index.py
import nfc

clf = nfc.ContactlessFrontend("usb")
print(clf)
```

上記プログラムを試しに実行。

```bash
$ python3 index.py 
Traceback (most recent call last):
  File "index.py", line 3, in <module>
    clf = nfc.ContactlessFrontend("usb")
  File "/usr/local/lib/python3.7/dist-packages/nfc/clf/__init__.py", line 76, in __init__
    raise IOError(errno.ENODEV, os.strerror(errno.ENODEV))
OSError: [Errno 19] No such device

$ sudo python3 index.py 
SONY RC-S380/P on usb:001:007

```

sudo しないとデバイスを見つけられない。
しかし、この方法は非推奨。
解決方法は様々なところで述べられているが、以下の通り。

```bash
$ python3 -m nfc
This is the 1.0.3 version of nfcpy run in Python 3.7.3
on Linux-4.19.57-v7+-armv7l-with-debian-10.0
I'm now searching your system for contactless devices
** found usb:054c:06c3 at usb:001:007 but access is denied
-- the device is owned by 'root' but you are 'pi'
-- also members of the 'root' group would be permitted
-- you could use 'sudo' but this is not recommended
-- better assign the device to the 'plugdev' group
   sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"054c\", ATTRS{idProduct}==\"06c3\", GROUP=\"plugdev\" >> /etc/udev/rules.d/nfcdev.rules'
   sudo udevadm control -R # then re-attach device
I'm not trying serial devices because you haven't told me
-- add the option '--search-tty' to have me looking
-- but beware that this may break other serial devs
Sorry, but I couldn't find any contactless device
```

```python3 -m nfc``` を実行すると、やらなければならないことを教えてくれるので、

```bash
$ sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"054c\", ATTRS{idProduct}==\"06c3\", GROUP=\"plugdev\" >> /etc/udev/rules.d/nfcdev.rules'

$ cat /etc/udev/rules.d/nfcdev.rules 
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="06c3", GROUP="plugdev"

$ sudo udevadm control -R
```

実行して再起動。すると、

```bash
$ python3 index.py 
SONY RC-S380/P on usb:001:006

$ python3 -m nfc
This is the 1.0.3 version of nfcpy run in Python 3.7.3
on Linux-4.19.57-v7+-armv7l-with-debian-10.0
I'm now searching your system for contactless devices
** found SONY RC-S380/P NFC Port-100 v1.11 at usb:001:006
I'm not trying serial devices because you haven't told me
-- add the option '--search-tty' to have me looking
-- but beware that this may break other serial devs
```

sudo が不要になった。

# タグ情報の取得

```python:index.py
import nfc

def on_startup(targets):
    for target in targets:
        # nanaco
        # target.sensf_req = bytearray.fromhex("0004c70000")
        # pasmo
        target.sensf_req = bytearray.fromhex("0000030000")       
    return targets

def on_connect(tag):
    print(tag)
    return True # カードが離れるまでに1回のみ 

while True:
    with nfc.ContactlessFrontend("usb") as clf:
        rdwr = {
            'targets': ['212F'], # Type3Tag
            'on-startup': on_startup,
            'on-connect': on_connect
        }
        clf.connect(rdwr=rdwr)
```

上記プログラムでタグ情報を出力する。

いくつかコメントをつけているが、

+ rdwrのtargets  
  Felicaのみ認識するようにしている。  
  デフォルトだと、on_startupに渡されるtargetsは```['212F','106A','106B']```  
  + 免許証はType4BTag
  + nanaco, pasmoはType3Tag  
  それぞれに対応する値が決まっている(通信速度帯？)
+ on_startupで指定している数値  
  事業者ごとに割り振られているらしい。  
  指定することで、反応するカードを制限できる
  + pasmo(交通系IC)：0003
  + nanaco：04c7
+ on_connectのreturn値  
  falseにすると、連続でイベントを起こし続ける。

実行してみる

```bash
$ python3 index.py 
Type3Tag 'FeliCa Standard (RC-S???)' ID=XXXXXXXX PMM=100B4B428485D0FF SYS=FFFF
```

IDのみ、一応隠した。
PMMは製造コードらしい。手元の2枚のPASMOで同じ値だったのでそのまま。作った場所が同じとか？

なお、on_startup関数においてtarget.sensf_reqを設定すると、
それまで0003と表示されていた```SYS```の値がFFFFになってしまう。
何故かご存じの方居ましたら教えてください...

FeliCaの仕様については[こちらの記事](https://qiita.com/YasuakiNakazawa/items/3109df682af2a7032f8d)に詳しく書いてあった。
ななめ読みしかしていないので後で詳しく読む。

# tag情報からIDmを抜き出す
on_connect関数を下記のように変更

```python:index.py
import binascii

def on_connect(tag):
    print(tag)
    idm = binascii.hexlify(tag.identifier).upper().decode('utf-8')
    print(idm)
    return True
```
tag情報で表示されるIDと同じ値が取得できることを確認。
ID以外のデータの取得方法は[こちら](https://www.slideshare.net/m2wasabi/nfcpy-0100)に書いてあった内容でいけそう。
ななめ読みしか以下略

# コード全体
```python:index.py
import nfc
import binascii

def on_startup(targets):
    for target in targets:
        # nanaco
        # target.sensf_req = bytearray.fromhex("0004c70000")
        # pasmo
        target.sensf_req = bytearray.fromhex("0000030000")
        
    return targets

def on_connect(tag):
    idm = binascii.hexlify(tag.identifier).upper().decode('utf-8')
    print(idm)
    return True

while True:
    with nfc.ContactlessFrontend("usb") as clf:
        rdwr = {
            'targets': ['212F'],
            'on-startup': on_startup,
            'on-connect': on_connect
        }
        clf.connect(rdwr=rdwr)


```

# 参考
[nfcpy ドキュメント](https://nfcpy.readthedocs.io/en/latest/index.html)
[FeliCa library](https://ja.osdn.net/projects/felicalib/)