PythonでSuicaの残高を取得する(libpafe使用)

# PythonでSuicaの残高を取得する(libpafe使用)

* 手元に中古で買ったFelicaリーダのRC-S320があったので，Pythonを使って動かせないか試してみた．
* ちょっとした備忘録的なメモです．
* そのうちRaspberry piとかで何かと組み合わせてみる予定．

## 動作環境
* OS: LMDE 2 Betsy
* FeliCaリーダ: Sony RC-S320
* Pythonのバージョン: 2.7.9

## ライブラリ
* [nfcpy](http://nfcpy.readthedocs.io/en/latest/index.html)の存在を知ったが，RC-S320には対応していない．
* [libpafe](https://github.com/rfujita/libpafe)が対応していた．
    * C言語のライブラリとして使える．
    * Ruby上なら[libpafe-ruby](http://hito.music.coocan.jp/pasori/libpafe-ruby.html)が使える．

## 環境設定
1. libusbのインストール
    * `$ sudo apt-get install libusb-dev`
2. GitHubからlibpafeをcloneする
    * `$ git clone https://github.com/rfujita/libpafe.git`
3. コンパイル＆インストール

    ```
    $ cd libpafe
    $ ./configure
    $ make
    $ sudo make install
    ```
4. 動作確認

    ```
    $ cd libpafe
    $ ./tests/pasori_test 
    PaSoRi (RC-S320)
     firmware version 1.40
    Echo test... success
    EPROM test... success
    RAM test... success
    CPU test... success
    Polling test... success
    ```
    * この時点でudevの設定をしていないので，多分sudoがいるかもしれない (2017/8/14 追記)

5. udevの設定
    * 60-libpafe.rulesを作成
        * `$ sudo vi /lib/udev/rules.d/60-libpafe.rules`
    * 以下の内容を記述
    
    ```60-libpafe.rules
    ACTION!="add", GOTO="pasori_rules_end"
    SUBSYSTEM=="usb_device", GOTO="pasori_rules_start"
    SUBSYSTEM!="usb", GOTO="pasori_rules_end"
    LABEL="pasori_rules_start"

    ATTRS{idVendor}=="054c", ATTRS{idProduct}=="01bb", MODE="0664", GROUP="plugdev"
    ATTRS{idVendor}=="054c", ATTRS{idProduct}=="02e1", MODE="0664", GROUP="plugdev"

    LABEL="pasori_rules_end"
    ```
    * `$ sudo udevadm control --reload-rules`を実行
    * `$ sudo reboot`で再起動する．
    

## ICカードのIDmを取得する
* ひとまずICカードのIDmを取得してみる．
    [raspberrypiでFeliCaのIDmを読み取る（pasori RC-S320版）](http://qiita.com/t114/items/819a9cdfe90ea98dd4d3)

    [Raspberry Pi + libpafe + Python + ctypesで、FeliCaのIDmを読む](http://thinkami.hatenablog.com/entry/2015/06/16/212600)   
(ほぼこちらに載せられていたものを参考にしています)

```python:read_idm.py
# -*- coding: utf-8 -*-

from __future__ import print_function
from ctypes import *

# libpafe.hの77行目で定義
FELICA_POLLING_ANY = 0xffff

if __name__ == '__main__':

	libpafe = cdll.LoadLibrary("/usr/local/lib/libpafe.so")

	libpafe.pasori_open.restype = c_void_p
	pasori = libpafe.pasori_open()

	libpafe.pasori_init(pasori)

	libpafe.felica_polling.restype = c_void_p
	felica = libpafe.felica_polling(pasori, FELICA_POLLING_ANY, 0, 0)

	idm = c_ulonglong()
	libpafe.felica_get_idm.restype = c_void_p
	libpafe.felica_get_idm(felica, byref(idm))

	# IDmは16進表記
	print("IDm:", "%016X" % idm.value)

	# READMEより、felica_polling()使用後はfree()を使う
	# なお、freeは自動的にライブラリに入っているもよう
	libpafe.free(felica)

	libpafe.pasori_close(pasori)
```

* libpafeはC言語用のライブラリだが，Pythonのctypesを使うことでC言語の関数がPython上で使えるようになる．
    * felica_get_idmを使う．
        * `int felica_get_idm(felica *f, uint8 *idm);`
            * f : felica_pollingで取得したfelica型のポインタ
            * idm : IDmを格納するためのポインタ

## Suicaの残高を取得する
* Suicaの残高を表示するのはどうやるか？
    * ICカード内の情報を取ってきて，その中から残高の情報を拾ってくる必要がある
    * Suicaのデータ構造まとめ: [suica - FeliCa Library Wiki - FeliCa Library - OSDN](https://ja.osdn.net/projects/felicalib/wiki/suica)
        * 10-11のあたりのデータを取ってくれば良さそう．

* libpafeのfelica_readという関数で諸々の情報を取ってくることができるので，それを使う．
参考: [続けるブログ:  ラズベリーパイがICカードの残高をしゃべるよ](http://continue-to-challenge.blogspot.jp/2014/11/ic.html)
    * ↑こちらはC言語で書かれているため，ctypesを使ってPython上でfelica_readを呼び出せるように書き換える
    * 引数が構造体のポインタを示していたりするので，あらかじめ定義して調整する

```python:read_balance.py
# -*- coding: utf-8 -*-

from __future__ import print_function
from ctypes import *

# libpafe.hの77行目で定義
FELICA_POLLING_ANY = 0xffff

# 構造体の代わりとなるクラスの定義
class felica_block_info(Structure):
	_fields_ = [
		("service", c_uint16),
		("mode", c_uint8),
		("block", c_uint16)
	]

if __name__ == '__main__':

	libpafe = cdll.LoadLibrary("/usr/local/lib/libpafe.so")

	libpafe.pasori_open.restype = c_void_p
	pasori = libpafe.pasori_open()

	libpafe.pasori_init(pasori)

	libpafe.felica_polling.restype = c_void_p
	felica = libpafe.felica_polling(pasori, FELICA_POLLING_ANY, 0, 0)

	# Cのint型配列の定義(長さ16)
	int_array16 = c_uint8 * 16

	# 応答データ
	data = int_array16()
	# サービスコードのリスト
	info = felica_block_info(c_uint16(0x090f), c_uint8(0), c_uint16(0))
	for i in range(0, 32):
		c_i = c_int(i)
		libpafe.felica_read(felica, byref(c_i), byref(info), byref(data))
		if (data[1] > 0) or (data[2] > 0):
			print("残高:", data[11] * 256 + data[10], "円")
			break

	libpafe.free(felica)

	libpafe.pasori_close(pasori)

```

* 同じ構造になっているからか，ICOCAとtoicaでもちゃんと使えた．

## 参考
* [raspberrypiでFeliCaのIDmを読み取る（pasori RC-S320版）](http://qiita.com/t114/items/819a9cdfe90ea98dd4d3)
* [Raspberry Pi + libpafe + Python + ctypesで、FeliCaのIDmを読む](http://thinkami.hatenablog.com/entry/2015/06/16/212600)
* [suica - FeliCa Library Wiki - FeliCa Library - OSDN](https://ja.osdn.net/projects/felicalib/wiki/suica)
* [ラズベリーパイがICカードの残高をしゃべるよ](http://continue-to-challenge.blogspot.jp/2014/11/ic.html)
* [ctypes 〜pythonでCをイジる(Macで)〜 - カールヌードル](http://curlnoodle.hatenablog.com/entry/2013/12/30/221858)