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

この記事は最終更新日から1年以上が経過しています。

環境

  • raspberryPi 3 Model B
  • Raspbian 10(buster)
  • Python3

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

$ 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リーダーの接続確認

$ lsusb | grep Sony
Bus 001 Device 007: ID 054c:06c3 Sony Corp. RC-S380

PaSoRi は Sony 製品なのでSony でgrep

nfcpyのインストール

$ sudo pip3 install -U nfcpy
...
$ pip3 list | grep nfcpy
nfcpy             1.0.3

-U オプションは不要かもしれない

プログラムからのNFCリーダーの認識

index.py
import nfc

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

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

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

$ 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 を実行すると、やらなければならないことを教えてくれるので、

$ 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

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

$ 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 が不要になった。

タグ情報の取得

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にすると、連続でイベントを起こし続ける。

実行してみる

$ 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の仕様についてはこちらの記事に詳しく書いてあった。
ななめ読みしかしていないので後で詳しく読む。

tag情報からIDmを抜き出す

on_connect関数を下記のように変更

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以外のデータの取得方法はこちらに書いてあった内容でいけそう。
ななめ読みしか以下略

コード全体

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 ドキュメント
FeliCa library

irutack
つよつよえんじにゃーになりたい
ユーザー登録して、Qiitaをもっと便利に使ってみませんか。
  1. あなたにマッチした記事をお届けします
    ユーザーやタグをフォローすることで、あなたが興味を持つ技術分野の情報をまとめてキャッチアップできます
  2. 便利な情報をあとで効率的に読み返せます
    気に入った記事を「ストック」することで、あとからすぐに検索できます
コメント

sensf_req の設定方法がわからず探していました、参考になりました。

sensf_reqで指定している、Pollingコマンドのリクエストコードが00h要求なし
だからだと思います。
システムコード要求01hにする必要がある?
"0000030100"
00:コマンド
0003:システムコード
01:リクエストコード
00:タイムスロット

あなたもコメントしてみませんか :)
すでにアカウントを持っている方は
ユーザーは見つかりませんでした