以前に書いたRaspberry PiとテレビをHDMI接続してテレビのON/OFFを確認するという記事では、テレビのON/OFF(STANDBY)状況をRaspberry Piから確認することができ、これを活用すれば電子工作不要で高齢者見守りデバイスを製作できることをお伝えしました。
具体的には、npmにnode-cec
というパッケージがあり、これをインストールするとNode.jsでHDMI-CECの信号を送受信できるというものでした。
ところが、SONYのICカードリーダー PaSoRi(RC-S380)を用いてRaspberry PiでカードIDを取得するには、nfcpy
というPythonのライブラリを使うしかありません。
Node.jsからPaSoRi(RC-S380)を使うには、PythonとNode.jsを連携(標準入出力を用いてカードIDを含むJSONを受け取るなど)する必要があります。
PaSoRi(RC-S380)でも、HDMIとnode-cec
のようにケーブルを接続してnpmパッケージをインストールするだけでICカードのIDを取得できればいいのにな... と思い、nfcpy
を取り扱うためのPythonスクリプトを内包する node-nfcpy-id
というnpmパッケージを作ってみました。
動作環境
Raspberry Pi側
- Raspberry Pi 3 Model B
- Raspbian Jessie Lite 2017-04-10
- イメージをMicroSDに書き込んだ後、MicroSDのルートディレクトリに
ssh
という名前で空のファイルを作成
- イメージをMicroSDに書き込んだ後、MicroSDのルートディレクトリに
- Raspbian Jessie Lite 2017-04-10
ICカードリーダー
- SONY PaSoRi RC-S380
- 2012年10月発売
- 実売価格2500円前後
カードIDとカードタイプについて
PaSoRi(RC-S380)で読み取れるNFCカードは、主にFeliCa規格のものとMIFARE規格のものがあります。
FeliCa規格ではカードIDのことをIDm、MIFARE規格ではカードIDのことをUIDといいます。
node-nfcpy-id
では、カードIDを文字列(アルファベット小文字の16進数)で取得できます。
カード規格は nfcpy
に準じた以下の数値(2
, 3
, 4
)で取得できます。
- Type 2
- MIFARE規格(DES Fire以外)のカード
- ゲームセンターのカード(Aime, NESiCA)、国立国会図書館利用者カードなど
- Type 3
- FeliCa規格のカード
- Suicaなどの交通系、楽天Edy、nanaco、WAONなど
- Type 4
- MIFARE DESFireのカード
- au WALLETプリペイドカードなど
インストール
Node.jsのインストール
Node.jsとnpmの最新版をRaspberry PiにNode.jsとnpmの最新版をインストールするを参照してインストールします
nfcpy
とPaSoRiを利用するための設定
Raspbianには標準でPython 2系がインストールされていますので、Python自体のインストールは不要です
apt-getで python-usb
と python-pip
をインストールします
pi@raspberrypi:~ $ sudo apt-get install python-usb python-pip -y
pipで nfcpy-id-reader
(node-nfcpy-id
と連携するPythonスクリプト)をインストールします。
nfcpy-id-reader
をインストール際に nfcpy
もインストールされます。
pi@raspberrypi:~ $ sudo pip install -U nfcpy-id-reader
ユーザー権限でもPaSoRiを利用可能にするために、 /etc/udev/rules.d/nfcdev.rules
というファイルを作成します
pi@raspberrypi:~ $ cat << EOF | sudo tee /etc/udev/rules.d/nfcdev.rules
SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="06c3", GROUP="plugdev"
EOF
一度再起動をします
pi@raspberrypi:~ $ sudo reboot
node-nfcpy-id
を適当なディレクトリ上でインストールしてください
pi@raspberrypi:~ $ npm install node-nfcpy-id --save
npm install
時に、 npm ERR! 404 'types/node' is not in the npm registry.
のようなエラーが出る場合は、npmのバージョンが古い可能性があります。
npmの最新版をRaspberry PiにNode.jsとnpmの最新版をインストールするを参照してインストールしてください。
使用方法
機能説明
インポート
version 0.1.0より、CommonJSでのimport(require
-ing)は下記のように default
が必要になりました
const NfcpyId = require('node-nfcpy-id').default;
TypeScript, ECMAScript 2015(Babel利用)で import
文を利用される方はこちら
import NfcpyId from 'node-nfcpy-id';
初期化(モードの指定)
初期化時に動作モードを指定できます
loop
モード
複数枚のカードを連続で読み込めるよう、カードリーダーをカード1枚ごとに停止させずに動作させます。
カードがカードリーダーから離れた時のイベントを取得できます。
コンストラクタの引数に何も指定しなかった場合のデフォルトのモードです。
// const nfc = new NfcpyId({mode: 'loop'});
const nfc = new NfcpyId(); // loop mode
non-loop
モード
カードを一度カードリーダーの上に置いて離すと、カードリーダーを停止させます。
カードがカードリーダーから離れた時のイベントを取得できます。
const nfc = new NfcpyId({mode: 'non-loop'});
non-touchend
モード
カードを一度カードリーダーの上に置くと、カードリーダーを停止させます。
カードがカードリーダーから離れた時のイベントを取得できません。
const nfc = new NfcpyId({mode: 'non-touchend'});
メソッド
start()
メソッド
カードリーダーによるカードの読み取りを開始します
nfc.start();
初期化と同時にstart()
メソッドを使用することもできます
const nfc = new NfcpyId().start();
pause()
メソッド
カードリーダーによるカードの読み取りを停止します
nfc.pause();
イベント
EventEmitterの形式でイベントを登録できます。
touchstart
イベント
カードがカードリーダーの上に置かれた時に発生します。
コールバック関数の引数にカードIDとカードタイプが格納されます。
nfc.on('touchstart', (card) => {
console.log('touchstart', 'id:', card.id, 'type:', card.type);
});
touchend
イベント
カードがカードリーダーから離れた時に発生します(non-touchend
モードでは利用できません)
ゲッター
isRunning
プロパティ
カードリーダーが動作中の場合は ture
を返し、そうでない場合は false
を返します。
console.log(nfc.isRunning); // true or false
Node.jsの終了時
Node.jsのプロセスを終了(control+Cなども含む)する場合に、同時にPaSoRiから電波を発しない状態にしてからPythonの方もプロセスを終了します。
各モード・メソッド・イベントの図解
サンプル
loop
モードの例
カードをカードリーダーの上に置くと、カードIDとカードタイプを出力し、カードがカードリーダーから離れた時に touchend
を出力します
const NfcpyId = require('node-nfcpy-id').default;
const nfc = new NfcpyId().start();
nfc.on('touchstart', (card) => {
console.log('touchstart', 'id:', card.id, 'type:', card.type);
});
nfc.on('touchend', () => {
console.log('touchend');
});
nfc.on('error', (err) => {
// standard error output (color is red)
console.error('\u001b[31m', err, '\u001b[0m');
});
実行例
pi@raspberrypi:~ $ node app
touchstart id: aa7dxxxx type: 2
touchend
touchstart id: 0114b3fxxxxxxxxx type: 3
touchend
touchstart id: 01103f0xxxxxxxxx type: 3
touchend
touchstart id: 0114b3fxxxxxxxxx type: 3
touchend
touchstart id: 04192xxxxxxxxx type: 4
touchend
non-touchend
モードの例
カードをカードリーダーの上に置くと、カードIDとカードタイプを出力し、5秒後にカードリーダーによる読み込みを再開します
const NfcpyId = require('node-nfcpy-id').default;
const nfc = new NfcpyId({mode: 'non-touchend'}).start();
nfc.on('touchstart', (card) => {
console.log('touchstart:', card.id, 'type:', card.type);
console.log('5秒後に読み込みを再開します');
setTimeout(() => {
nfc.start();
}, 5000);
});
nfc.on('error', (err) => {
// standard error output (color is red)
console.error('\u001b[31m', err, '\u001b[0m');
});
既知の問題
loop
モードまたはnon-loop
モードの場合、Aimeなど一部MIFARE系カードをタッチするとtouchstart
イベント発生後、すぐにtouchend
イベントが発生し、loop
モードの場合これを繰り返すチャタリングのような現象が発生します。
nfcpy
をPythonだけで利用した場合も同様の現象が発生します。
LT発表
このnpmパッケージについて、2日間連続!!(1日目) We Are JavaScripters! @8th【初心者歓迎LT大会】で「Node.js + Raspberry Piで お手軽IoT」(スライド)というタイトルで発表いたしました
npmパッケージを作ってみて
- npmパッケージとして公開して使っていただくことを考えて、control+CでNode.jsを終了した場合などに同時にPythonの方も終了するような処理を書いてみました
- 誘導電流でLEDが発光するテスターのようなもの(共立プロダクツ KP-NFLEW2)を買ってみたところ、Pythonをうまく終了しないとPaSoRiが電波を発したままとなる(電源がオフにならない)ことがわかり、電源をオフにするために
nfcpy
のドキュメントやサンプルコードを見てうまくシグナルを送る必要がありました- シグナルを用いたためWin32に非対応となってしまった
- Python単体で書くよりもPaSoRiをうまくコントロールできる気がします
- Pythonと連携する必要があったので、標準入出力の他にシグナルとコマンドライン引数を使ってしまい、コードが複雑になってしまいました
- PythonとNode.jsを連携するという面倒な部分を吸収するという意味では、仕方なかったのかもしれない
- npmで公開することを意識して、ドキュメントやコミットログを(文法的に合っているのか別として)英語で書いてみました
- ドキュメントを英語で作る難しさを改めて感じた
- このQiitaの記事が日本語のドキュメントという感じになった
- We Are JavaScripters!でコーディングルールについてお話しされていた方がいらっしゃったのをきっかけに、ESLintを導入してみたところ、最初のインデントだけ半角スペース4個にしてしまうクセを見つけた(
git diff
が残念なことになった)
参考サイト
Raspberry piで、PaSoRi USB Felica Reader(RC-S380)を使用する
3分でできるnpmモジュール