RaspberryPiでNFCタグを使ってみる
久しぶりにいろいろと思うところがあって、RaspberryPiでNFCのタグを使ってみることにしました。
基本的には過去エントリー見てもらえればいいのですが、その頃からRaspbianのバージョンもベースが変わっているのでちょっと変わったようです。 というか楽になっただけなので大したことはないです。
【参考1】 uepon.hatenadiary.com 【参考2】 uepon.hatenadiary.com
使用したタグはサンワサプライさんのシールタイプになります。
一枚あたり100円を切っているので、前よりも安くなってきていますね。
今回のOS情報も念の為。今回はRaspberry Pi3ではなく、使っていなかったRaspberry Pi2を使用してます。
$ lsb_release -a No LSB modules are available. Distributor ID: Raspbian Description: Raspbian GNU/Linux 9.4 (stretch) Release: 9.4 Codename: stretch
最新のイメージを使用していますのでRaspbian GNU/Linux 9.4 (stretch)
となっています。(イメージ名は2018-04-18-raspbian-stretch.img
でした)
RC-S380の認識
基本的には以前のエントリーと同様にUSBを挿すだけで認識は行われます。不確定な情報ではありますが、過去のバージョンのリーダーではどうも認識はされますが、` ``nfcpy```からは認識出来ないようです。自分も何回か試してみましたがだめでした。nfcpy側では対応デバイスになっていますが、情報から察するにその他ライブラリなどの依存関係も疑われるのではないかと思います。ネットの情報を調べるとベースがDebian 6.0 (squeeze)からDebian 7 (wheezy)に変わったあたりか使えなくなっているのではないかと推測しています。
とりあえずUSBケーブルで接続すると以下のようになります。
【リーダーを1つ接続した場合】
$ lsusb Bus 001 Device 006: ID 056e:4008 Elecom Co., Ltd Bus 001 Device 007: ID 054c:06c3 Sony Corp. Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
特に問題ありません。2つ接続しても問題なく認識されています。
【リーダーを2つ接続した場合】
$ lsusb Bus 001 Device 006: ID 056e:4008 Elecom Co., Ltd Bus 001 Device 007: ID 054c:06c3 Sony Corp. Bus 001 Device 008: ID 054c:06c3 Sony Corp. Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. SMC9514 Hub Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
nfcpy
モジュールのインストール
前回とは異なり、pip
に登録されていました。気になる点としてはpython2系にしかまだ対応していない点でしょうか。ドキュメント通りインストールすれば問題はありません。
-Uスイッチ
をつけて置かないとエラーがでるようです。
$ sudo pip install -U nfcpy Collecting nfcpy Downloading https://files.pythonhosted.org/packages/89/2c/3d7378d65c6f21312fae4cc44849606eefa08f5980e06c5bc220c2086808/nfcpy-0.13.5-py2-none-any.whl (214kB) 100% |????????????????????????????????| 215kB 627kB/s Collecting libusb1 (from nfcpy) Downloading https://files.pythonhosted.org/packages/ec/5d/4fdac6c53525786fe35cff035c3345452e24e2bee5627893be65d12555cb/libusb1-1.6.4.tar.gz (55kB) 100% |????????????????????????????????| 61kB 1.0MB/s Collecting pydes (from nfcpy) Downloading https://www.piwheels.org/simple/pydes/pyDes-2.0.1-py2.py3-none-any.whl Collecting pyserial (from nfcpy) Downloading https://files.pythonhosted.org/packages/0d/e4/2a744dd9e3be04a0c0907414e2a01a7c88bb3915cbe3c8cc06e209f59c30/pyserial-3.4-py2.py3-none-any.whl (193kB) 100% |????????????????????????????????| 194kB 680kB/s Collecting ndeflib (from nfcpy) Downloading https://files.pythonhosted.org/packages/01/76/39eb236dc5566618abdb169cb88ba4eabd22245b29cc9e5f8d91d5fcf261/ndeflib-0.3.2-py2.py3-none-any.whl (57kB) 100% |????????????????????????????????| 61kB 1.0MB/s Building wheels for collected packages: libusb1 Running setup.py bdist_wheel for libusb1 ... done Stored in directory: /root/.cache/pip/wheels/98/8d/8b/bac0a20eb9757e7dbf46e8ab1f1695c78ad919f53080a58bc1 Successfully built libusb1 Installing collected packages: libusb1, pydes, pyserial, ndeflib, nfcpy Found existing installation: pyserial 3.2.1 Not uninstalling pyserial at /usr/lib/python2.7/dist-packages, outside environment /usr Successfully installed libusb1-1.6.4 ndeflib-0.3.2 nfcpy-0.13.5 pydes-2.0.1 pyserial-3.4
インストールは終わりましたが、テスト用のコマンドなどはインストールされていませんので、Githubから以下のサイトからcloneします。
$ git clone https://github.com/nfcpy/nfcpy.git $ cd nfcpy/ $ ls HISTORY.rst README.rst requirements-dev.txt setup.py tox.ini LICENSE docs requirements-pypi.txt src MANIFEST.in examples setup.cfg tests
nfcpyの実行を行う
インストール準備がおわったので続いてはnfcpy
モジュールのチェックを行います。
以下のコマンドで行います。ですが、インストール直後はsudoをつけないと以下のようなエラーメッセージが表示されます。(sudo
をつければ実行は問題はありません。)
今回はRaspberryPiに2つのリーダーを接続しているのでエラーも2つ表示されています。
$ python -m nfc No handlers could be found for logger "nfc.llcp.sec" This is the 0.13.5 version of nfcpy run in Python 2.7.13 on Linux-4.14.34-v7+-armv7l-with-debian-9.4 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 ** found usb:054c:06c3 at usb:001:008 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
このメッセージをよくみると
-- 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
このメッセージの内容は、「使用するデバイスをplugdev
グループに入れて続くコマンドをいれるとsudo
なしで実行できるようになります。」とのことでした、ベンダIDなどもセットされた形でメッセージが表示されるので、コピペして以下のように実行します。
$ 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
リーダーが複数ある場合はエラーメッセージにある分だけ実行します。 実行が終わったら、念の為RaspberryPiを再起動し、改めてチェック行ってみます。
$ python -m nfc No handlers could be found for logger "nfc.llcp.sec" This is the 0.13.5 version of nfcpy run in Python 2.7.13 on Linux-4.14.34-v7+-armv7l-with-debian-9.4 I'm now searching your system for contactless devices ** found SONY RC-S380/P NFC Port-100 v1.11 at usb:001:005 ** found SONY RC-S380/P NFC Port-100 v1.11 at usb:001:004 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
をつけなくてもエラーメッセージは表示されないようになります。デバイスも正しく認識されているようです。
続いてはGithubで落としてきたテスト用のコマンドを使用して交通系ICカードを読み込ませてみます。exampleディレクトリ内のtagtool.py
がテスト用のコマンドです。実行すると読み込み待ちになります。
$ git clone https://github.com/nfcpy/nfcpy.git $ cd ~/nfcpy/examples/ $ python tagtool.py No handlers could be found for logger "nfc.llcp.sec" [nfc.clf] searching for reader on path usb [nfc.clf] using SONY RC-S380/P NFC Port-100 v1.11 at usb:001:005 ** waiting for a tag **
ここで交通系ICカードをタッチすると読み込んだデータの一部が表示されます。 (IDの部分は一部伏せています)
$ python tagtool.py No handlers could be found for logger "nfc.llcp.sec" [nfc.clf] searching for reader on path usb [nfc.clf] using SONY RC-S380/P NFC Port-100 v1.11 at usb:001:005 ** waiting for a tag ** Type3Tag 'FeliCa Standard (RC-S915)' ID=**************** PMM=**************** SYS=0003
このコマンドでは使用するリーダーを指定して起動することになります。(指定しない場合には最初に見つかったリーダーが使用される)
では、改めて今回準備したNFCタグを読み込ませてみます。(IDの部分は一部伏せています) うまく読み込めたようです。購入時は何も書き込まれていないので以下のような表示になります。(この商品は144Byteのデータ書き込みができるようですが、実際には137byteの書き込みになってしまうようです。)
$ python tagtool.py No handlers could be found for logger "nfc.llcp.sec" [nfc.clf] searching for reader on path usb [nfc.clf] using SONY RC-S380/P NFC Port-100 v1.11 at usb:001:005 ** waiting for a tag ** Type2Tag 'NXP NTAG213' ID=04D803******** NDEF Capabilities: readable = yes writeable = yes capacity = 137 byte message = 0 byte
pythonからタグを操作する
タグ情報をreadする
on_ほげほげみたいな感じのイベントハンドラーのメソッドを作成して、usb接続のデバイスに登録することで読み込むことができます。今回の例ではon_connectが読み込みに当たりますが、今後の例もこの部分を編集して実装しています。
【readNFC.py】
import nfc def on_startup(targets): print("on_startup()") return targets def on_connect(tag): print("Tag: {}".format(tag)) print("Tag type: {}".format(tag.type)) #print '\n'.join(tag.dump()) if tag.ndef: print tag.ndef.message.pretty() #return True def on_release(tag): print("on_release()") if tag.ndef: print(tag.ndef.message.pretty()) clf = nfc.ContactlessFrontend('usb') if clf: print("Clf: {}".format(clf)) clf.connect(rdwr={ 'on-startup': on_startup, 'on-connect': on_connect, 'on-release': on_release }) clf.close()
このプログラム例ではタグ情報をダンプしていますが、初期状態ではタグの内容は空になってます。
【実行】
$ python readNFC.py No handlers could be found for logger "nfc.llcp.sec" Clf: SONY RC-S380/P on usb:001:005 on_startup() Tag: Type2Tag 'NXP NTAG213' ID=04A903******** Tag type: Type2Tag record 1 type = '' name = '' data = ''
タグをフォーマットする
【formatNFC.py】
import nfc def on_connect(tag): print("format:", tag.format()) clf = nfc.ContactlessFrontend('usb') if clf: print("Clf: {}".format(clf)) clf.connect(rdwr={ 'on-connect': on_connect }) clf.close()
【実行】
$ python formatNFC.py No handlers could be found for logger "nfc.llcp.sec" Clf: SONY RC-S380/P on usb:001:005 ('format:', True)
情報がフォーマットされているかを先程のreadNFC.py
で
もう一度読み込ませてみると
【実行】
$ python readNFC.py No handlers could be found for logger "nfc.llcp.sec" Clf: SONY RC-S380/P on usb:001:005 on_startup() Tag: Type2Tag 'NXP NTAG213' ID=04A903******** Tag type: Type2Tag record 1 type = '' name = '' data = ''
無事にフォーマットされていることがわかります。
タグに情報を書き込む
"Hello World!"という文字列を書き込んでみます。
【writeNFC.py】
import nfc def on_startup(targets): print("on_startup()") return targets def on_connect(tag): print("Tag: {}".format(tag)) print("Tag type: {}".format(tag.type)) #print '\n'.join(tag.dump()) if tag.ndef: record = nfc.ndef.TextRecord("Hello World!") tag.ndef.message = nfc.ndef.Message(record) print tag.ndef.message.pretty() #return True def on_release(tag): print("on_release()") if tag.ndef: print(tag.ndef.message.pretty()) clf = nfc.ContactlessFrontend('usb') if clf: print("Clf: {}".format(clf)) clf.connect(rdwr={ 'on-startup': on_startup, 'on-connect': on_connect, 'on-release': on_release }) clf.close()
【実行】
$ python writeNFC.py No handlers could be found for logger "nfc.llcp.sec" Clf: SONY RC-S380/P on usb:001:005 on_startup() Tag: Type2Tag 'NXP NTAG213' ID=04A903******** Tag type: Type2Tag record 1 type = 'urn:nfc:wkt:T' name = '' data = '\x02enHello World!'
書き込んだタグを先程のreadNFC.py
で読み込ませると…
$ python readNFC.py No handlers could be found for logger "nfc.llcp.sec" Clf: SONY RC-S380/P on usb:001:005 on_startup() Tag: Type2Tag 'NXP NTAG213' ID=04A903******** Tag type: Type2Tag record 1 type = 'urn:nfc:wkt:T' name = '' data = '\x02enHello World!'
無事に書き込まれているようです。また先程のフォーマットをすれば初期化も問題なくできます。
$ python formatNFC.py No handlers could be found for logger "nfc.llcp.sec" Clf: SONY RC-S380/P on usb:001:005 ('format:', True) $ python readNFC.py No handlers could be found for logger "nfc.llcp.sec" Clf: SONY RC-S380/P on usb:001:005 on_startup() Tag: Type2Tag 'NXP NTAG213' ID=04A903******** Tag type: Type2Tag record 1 type = '' name = '' data = ''
複数のリーダーを使用した読み込み(スレッド+interruptシグナル対応)
今回は複数のリーダーを接続してそれぞれでタグを読み込ませたいと思っていたのでそれを実装してみます。各デバイスごとにスレッドを生成し、且つデーモン化してCtrl+Cでの終了にも対応させています。
【multiPollingNFC.py】
#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import signal import nfc import time import threading def on_startup(targets): # print("on_startup()") return targets def on_connect(tag): print("Tag: {}".format(tag)) print("Tag type: {}".format(tag.type)) if tag.ndef: print(tag.ndef.message.pretty()) print("-----------------") def on_release(tag): # print("on_release()") if tag.ndef: print(tag.ndef.message.pretty()) def readNFCThread(device): clf = nfc.ContactlessFrontend(device) # バス番号とデバイス番号を指定 while True: if clf: # print("Clf: {}".format(clf0)) clf.connect(rdwr={ 'on-startup': on_startup, 'on-connect': on_connect, 'on-release': on_release }) clf.close() def main(argv): t1 = threading.Thread(target=readNFCThread,name="readNFC1",args=('usb:001:004',)) t2 = threading.Thread(target=readNFCThread,name="readNFC2",args=('usb:001:005',)) t1.setDaemon(True) t2.setDaemon(True) t1.start() t2.start() while True: pass def handler(signal, frame): print("Process Interrupt!") sys.exit(0) if __name__ == '__main__': signal.signal(signal.SIGINT, handler) main(sys.argv)
ちょっと解説
各デバイスの読み込みをスレッド化しているので以下のようにデバイスの設定をできるようにしています。
clf = nfc.ContactlessFrontend(device) # バス番号とデバイス番号を指定
ここでいうデバイスが接続したリーダーの設定になります。引数として与える情報としてはlsusb
コマンドで認識されたBus
とDeviceID
を与えることになります。
$ lsusb 【略】 Bus 001 Device 005: ID 054c:06c3 Sony Corp. Bus 001 Device 004: ID 054c:06c3 Sony Corp. 【略】
上記のような表示が行われた場合には以下のように引数で与えれることになります。 USBの接続状態によって毎回変わる可能性のある情報なので注意が必要となります。
t1 = threading.Thread(target=readNFCThread,name="readNFC1",args=('usb:001:004',)) t2 = threading.Thread(target=readNFCThread,name="readNFC2",args=('usb:001:005',))
このプログラムではThread化しているのでCtrl+Cを押してinterruptを発生させても、Threadが終了しない状態になります。 そのため、以下のようにしてDeamonの設定をしています。
t1.setDaemon(True) t2.setDaemon(True)
実行してみる
フォーマットされたタグとHello World!
の書き込まれたタグを別のリーダーに読み込ませると以下のような表示になります。
【実行】
$ python multiPollingNFC.py No handlers could be found for logger "nfc.llcp.sec" Tag: Type2Tag 'NXP NTAG213' ID=04D803******** Tag type: Type2Tag record 1 type = '' name = '' data = '' ----------------- Tag: Type2Tag 'NXP NTAG213' ID=04A903******** Tag type: Type2Tag record 1 type = 'urn:nfc:wkt:T' name = '' data = '\x02enHello World!' -----------------
一応はできていますが、なんとなく処理が遅いです。それぞれのリーダーでポーリングをしているということもあるので、少し待ち時間があるようです。(別のターミナルで別々のreadNFC.py
を動作させたほうが早いかもw)このままの仕組みで高速化するのであれば、もっと別の方法での工夫がいるかなと思います。
終わりに
raspbian
などのバージョンも変わると設定方法もいい感じで簡単になってきているのでいいですね。