Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

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

動作環境

  • OS: LMDE 2 Betsy
  • FeliCaリーダ: Sony RC-S320
  • Pythonのバージョン: 2.7.9

ライブラリ

  • nfcpyの存在を知ったが,RC-S320には対応していない.
  • libpafeが対応していた.
    • C言語のライブラリとして使える.
    • Ruby上ならlibpafe-rubyが使える.

環境設定

  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を取得する

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
      • 10-11のあたりのデータを取ってくれば良さそう.
  • libpafeのfelica_readという関数で諸々の情報を取ってくることができるので,それを使う.
    参考: 続けるブログ: ラズベリーパイがICカードの残高をしゃべるよ

    • ↑こちらはC言語で書かれているため,ctypesを使ってPython上でfelica_readを呼び出せるように書き換える
    • 引数が構造体のポインタを示していたりするので,あらかじめ定義して調整する
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でもちゃんと使えた.

参考

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away