NaaN日記

やったこと、覚えたことを発信する場

VMを使ってUSB経由でNFCを読み取る話

こんにちは。

現在、私の加入しているサークルでは、チーム開発の一つとして、
PaSoRiを使った出席管理システムを作成中です。

このシステムは、Python3で開発しています。
学生証をPaSoRiにタッチすると、サークルの活動に参加したことになり、その日の議事録に名前が載るシステムです(※開発中)。
NFCタグの読み取りのために、nfcpyのモジュールを使用しています。

現状、開発環境はWindows10となっています。
しかし、Windows10での開発は、正直言って面倒です。

今回、開発環境の構築までの手間を比較するために、
VirtualBox仮想マシンを作成し、PaSoRiを使用するための設定を行いました。

Windowsでの開発

Windowsで開発を行うためには、下の作業が必要です。

  • WinUSB(またはZadig)とlibusbの手動インストール
    • PaSoRiのドライバをWinUSBドライバに置き換える
    • libusbをインストールしたら、DLLファイルを指定されたディレクトリにCopyする
  • nfcpyをpipでインストール

今回、私のチームでは、Zadigとlibusbを手動インストールしました。
ダウンロードしてきたDLLファイルを、手動で C:\Windows\System32 や、C:\Windows\SysWOW64 にCopyすることは、置き間違えなどのリスクがあります。

VirtualBoxでの開発

  • Linuxでは、libusbは通常インストール済み
  • VM上でUSBデバイスを使うための設定を行う必要がある
  • 権限や、ドライバーの設定のために、少しコマンドを叩く必要がある
  • nfcpyをpipでインストール

Windows版と違い、手動でCopyするようなものはありません。
仮想マシンでUSBを認識するための設定や、一般ユーザが使用する際の権限の設定などをする必要があります。

環境

今回、動作確認のために用意したものは以下の通りです。

今回、新しいものが使いたいという理由と、約一年前に友人から布教を受けたこともあり、ArchLinuxを使いましたが、Ubuntuなどでも動くと思います。

設定の流れ

設定の流れは、次のようになります。

  1. 仮想マシンの用意
  2. VirtualBox仮想マシンの設定でUSBの設定
  3. PaSoRiの認識確認
  4. nfcpyのインストール
  5. PaSoRiの接続設定

ここでは、2以降の手順を記述します。

VirtualBox仮想マシンのUSB設定

まず、手元のVirtualBoxに設定済みの仮想マシンを用意します。
そして、仮想マシンの状態が電源オフであることを確認します。次に、PaSoRiをUSBポートに挿します。

仮想マシンのUSBデバイスフィルターの設定
VirtualBox仮想マシンに対するUSBデバイスフィルターの設定

使用する仮想マシンの設定から、USBを選択、USB2.0コントローラーを有効にします。次に、右にある新規のUSBフィルターの追加から、目的の物(今回の場合はSONY RC-S380/P)を選択します。

PaSoRiを認識しているかの確認

次に、仮想マシンを立ち上げ、PaSoRiが認識されているかを確認します。認識を確認する方法はいくつかありますが、ここでは二つ紹介します。

1. lsusbコマンドを使用

$ lsusb
Bus 002 Device 003: ID 054c:06c3 Sony Corp. RC-S380

このように表示されたら認識されています。

2. dmesgコマンドを使用

$ dmesg | grep SONY # または grep NFC

それっぽいものが表示されたら、認識されています。

nfcpyのインストール

venvを使用し、仮想環境内にインストールします。

venvを使用する理由

venvとは、軽量な仮想環境の作成をサポートしてくれるものです。Python3の標準の機能の一つです。

メリット
  • 仮想環境の中に、パッケージ群を独立してインストールできる
    • 仮想環境ごとに、使用するパッケージ群のバージョンを分けられる
  • それぞれの仮想環境は独立しているため、仮想環境間では競合しない

nfcpyは、現在のプロジェクトでのみ使用し、頻繁に使用するものではないため、仮想環境の中で使用します。

nfcpyのインストールの流れ

$ python -V
Python 3.8.0
$ cd [WORKDIR] # 作業ディレクトリへ移動
$ python -m venv [ENV_DIR] # 環境の作成 (ENV_DIRをvenvとする例が多いらしい)
$ source [ENV_DIR]/bin/activate
(venv)$ pip install nfcpy
(venv)$ pip freeze # インストールしたパッケージの確認

PaSoRiの接続設定

接続確認のために、下のコマンドを実行します。

(venv)$ python -m nfc

いくつかエラーが出ることがあるので、確認したものについて順に対処していきます。

This is the 1.0.3 version of nfcpy run in Python 3.8.0
on Linux-5.4.2-arch1-1-x86_64-with-glibc2.2.5
I'm now searching your system for contactless devices
** found usb:054c:06c3 at usb:002:004 but access is denied
-- the device is owned by 'root' but you are 'amakuchi'
-- also members of the 'root' group would be permitted
-- you could use 'sudo' but this is not recommended
-- it's better to adjust the device permissions
   sudo sh -c 'echo SUBSYSTEM==\"usb\", ACTION==\"add\", ATTRS{idVendor}==\"054c\", ATTRS{idProduct}==\"06c3\", MODE=\"0666\" >> /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

このように出ることがあります。
要するに、デバイスを "plugdev" というグループへ追加することを推奨しています。
言われた通りに作業を行います。

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

$ sudo udevadm control -R

PaSoRiの再接続を行い、もう一度、実行します。

(venv)$ python -m nfc
This is the 1.0.3 version of nfcpy run in Python 3.8.0
on Linux-5.4.2-arch1-1-x86_64-with-glibc2.2.5
I'm now searching your system for contactless devices
** found usb:054c:06c3 at usb:002:005 but it's already used
-- scan sysfs entry at '/sys/bus/usb/devices/2-2:1.0/'
-- the device is used by the 'port100' kernel driver
-- this kernel driver belongs to the linux nfc subsystem
-- you can remove it to free the device for this session
   sudo modprobe -r port100
-- and blacklist the driver to prevent loading next time
   sudo sh -c 'echo blacklist port100 >> /etc/modprobe.d/blacklist-nfc.conf'
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

今度は、デバイスのport100は、既に使われているというメッセージが出ました。

そのため、port100のカーネルドライバを取り外します。

$ sudo modprobe -r port100

また、今後も停止させ続けておきたい場合は、ブラックリストに入れます。

$ sudo sh -c 'echo blacklist port100 >> /etc/modprobe.d/blacklist-nfc.conf'

もう一度、実行します。

(venv)$ python -m nfc
This is the 1.0.3 version of nfcpy run in Python 3.8.0
on Linux-4.19.88-1-lts-x86_64-with-glibc2.2.5
I'm now searching your system for contactless devices
** found SONY RC-S380/P NFC Port-100 v1.11 at usb:002:003
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

このように出たら、成功です!最後に、きちんと終了させます。

(venv)$ deactivate # 仮想環境から出る

おわり!

仮想マシンのUSBデバイスフィルターの設定は、GUIでしましたが、操作ミスの起こりにくい場所であり、ファイルの手動コピーよりは心理的安全性がありました。また、仮想マシン内での操作は、コマンドをぽちぽち貼り付けるだけなので、個人的には、Windows版でやるより、仮想マシンの中でやるほうが楽だと感じました。
それから、やはりWindowsの方のドライバはあまり触りたくないなーという気持ちがあります。libusbの導入と、Zadigのインストールをしなくて良いというだけで、仮想マシンでの開発を選ぶメリットがある気がします。

なお、今回一番時間がかかったのは、ArchLinuxの設定です……。しばらく環境を壊さないように気を付けたいです……。また、ArchLinuxのデスクトップ環境は作っていません。Webアプリケーション以外のGUIアプリケーションを作る予定がない間は、デスクトップ環境は作らないと思います。作ったらまたブログに書くと思います。
それから、昨年の誕生日プレゼントにPaSoRiをくださった先輩、ありがとうございました☺