80
70

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Raspberry PiとPaSoRiでICカードのIDを取得するnpmパッケージを作ってみた

Last updated at Posted at 2017-06-11

以前に書いた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を受け取るなど)する必要があります。

Python-to-Nodejs.png

PaSoRi(RC-S380)でも、HDMIとnode-cecのようにケーブルを接続してnpmパッケージをインストールするだけでICカードのIDを取得できればいいのにな... と思い、nfcpyを取り扱うためのPythonスクリプトを内包する node-nfcpy-id というnpmパッケージを作ってみました。

npm-node-nfcpy-id.png

動作環境

Raspberry Pi側

ICカードリーダー

  • SONY PaSoRi RC-S380
    • 2012年10月発売
    • 実売価格2500円前後
P6110607.jpg

カード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-usbpython-pip をインストールします

Raspbian
pi@raspberrypi:~ $ sudo apt-get install python-usb python-pip -y

pipで nfcpy-id-reader (node-nfcpy-id と連携するPythonスクリプト)をインストールします。
nfcpy-id-reader をインストール際に nfcpy もインストールされます。

Raspbian
pi@raspberrypi:~ $ sudo pip install -U nfcpy-id-reader

ユーザー権限でもPaSoRiを利用可能にするために、 /etc/udev/rules.d/nfcdev.rules というファイルを作成します

Raspbian
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

一度再起動をします

Raspbian
pi@raspberrypi:~ $ sudo reboot

node-nfcpy-id を適当なディレクトリ上でインストールしてください

Raspbian
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の方もプロセスを終了します。

各モード・メソッド・イベントの図解

node-nfcpy-id-modes.gif

サンプル

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');
});

実行例

Raspbian
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」(スライド)というタイトルで発表いたしました
wejs-slide-1.png

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が残念なことになった)
KP-NFLEW2

参考サイト

Raspberry piで、PaSoRi USB Felica Reader(RC-S380)を使用する
3分でできるnpmモジュール

80
70
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
80
70

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?