MENU

【Python】新型PaSoRi(RC-S300)でSuicaの残高を読み取る方法(nfcpy非対応の解決策)

PythonでSuicaや交通系ICカードの残高を読み取ろうとして、最新のPaSoRi「RC-S300」を購入したものの、「nfcpyが動かない!」「残高が読み取れない!」と絶望している方は多いのではないでしょうか。

実は私自身もその一人でした。ネット上の記事は旧型(RC-S380)向けの情報ばかりで、新型のRC-S300ではエラーを吐いて全く動きません。

しかし結論から言うと、Pythonと標準のスマートカードライブラリ(pyscard)だけで、RC-S300でもSuicaの残高は完璧に読み取れます。

この記事では、RC-S300でSuicaの残高を読み取り、ついでにWindowsの音声で「残高は〇〇円です」と喋らせるまでの具体的な手順とPythonコードを公開します。

自己責任でお願いします。Windows11の環境で実行しました。

目次

公式サイトでRC-S300の動作確認

まずは本体の動作チェックをSONYのサイトで確認しましょう。
https://www.sony.co.jp/Products/felica/consumer/guide/transportation.html

なぜRC-S300で「nfcpy」が使えないのか?

最大の原因は、PaSoRiの通信規格が「独自仕様」から「世界標準(PC/SC規格)」へと変わったことです。

  • 旧型(RC-S380): 独自通信だったため、nfcpyがUSBの生データを直接叩くことができました。
  • 新型(RC-S300): WindowsやMac標準の「PC/SC規格(CCID)」に完全移行。ドライバ不要で動くようになった反面、OSのセキュリティが介入するため、従来のnfcpyやエスケープ通信(透過APDU)が軒並み弾かれる仕様になりました。

つまり、「ソニーが世界標準に合わせた結果、これまでの野良ハックツールが全滅した」というのが真相です。

解決策:PC/SC標準コマンドで読み取る

RC-S300で残高を読むには、nfcpyを諦め、スマートカードの標準コマンド(APDU)を直接送信してFeliCaのサービスを選択してあげる必要があります。

今回は、PythonからPC/SC規格を叩けるライブラリ pyscard を使用します。

準備:必要なライブラリのインストール

コマンドプロンプト(またはターミナル)を開き、以下のコマンドを実行します。 ※今回 nfcpy は一切インストール不要です。

Bash

pip install pyscard pywin32
  • pyscard:RC-S300と通信するためのライブラリ
  • pywin32:Windows標準の音声合成(SAPI)を使ってPCに喋らせるためのライブラリ

Pythonコード全文(コピペで動きます)

以下のコードをコピーして、suica_reader.py などの名前で保存してください。
音声が出るのでご注意ください。

Python

import time
import winsound
import win32com.client
from smartcard.System import readers
from smartcard.util import toHexString

# 音声エンジンの初期化(Windows標準SAPI)
speaker = win32com.client.Dispatch("SAPI.SpVoice")

def speak(text):
    """非同期でPCに喋らせる"""
    speaker.Speak(text, 1)

def play_success_sound():
    """改札風のピピッ音"""
    winsound.Beep(2000, 60)
    time.sleep(0.04)
    winsound.Beep(2000, 60)

def read_suica(reader):
    conn = reader.createConnection()
    conn.connect()

    # 1. IDm(カード固有番号)の取得
    res, sw1, sw2 = conn.transmit([0xFF, 0xCA, 0x00, 0x00, 0x00])
    if sw1 != 0x90 or sw2 != 0x00:
        return None, None
    idm = toHexString(res).replace(" ", "")

    # 2. 【最重要】RC-S300向けAPDU:利用履歴サービス(0x090F)を選択
    # ポイント:P2パラメータを 0x01 に指定することでエラーを回避
    res, sw1, sw2 = conn.transmit([0xFF, 0xA4, 0x00, 0x01, 0x02, 0x0F, 0x09])
    if sw1 != 0x90 or sw2 != 0x00:
        return idm, None

    # 3. ブロック0(最新履歴)を読み取り
    res, sw1, sw2 = conn.transmit([0xFF, 0xB0, 0x00, 0x00, 0x00])
    if sw1 == 0x90 and sw2 == 0x00 and len(res) >= 16:
        # サイバネ規格:10バイト目(下位)と11バイト目(上位)を合成して残高を計算
        balance = res[10] + (res[11] << 8)
        return idm, balance

    return idm, None

def main():
    r = readers()
    if not r:
        print("エラー: パソリが見つかりません。USB接続を確認してください。")
        return

    reader = r[0]
    print(f"接続リーダー: {reader}")
    print("Suica / PASMO 等の交通系ICをタッチしてください...")
    print("※終了するには Ctrl + C を押してください\n")

    last_id = None

    while True:
        try:
            idm, balance = read_suica(reader)

            if idm and idm != last_id:
                if balance is not None:
                    print(f"【Suica検知】 IDm: {idm}")
                    print(f"★ 残高: {balance:,} 円\n")
                    play_success_sound()
                    speak(f"残高は、{balance}円です。")
                else:
                    print(f"【一般カード】 ID: {idm} (残高非対応)\n")
                    play_success_sound()

                last_id = idm

        except Exception:
            # カードが離れたらリセット
            last_id = None

        # 0.05秒の超高速ループで一瞬のタッチも逃さない
        time.sleep(0.05)

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        print("\n終了しました。")

実行方法

スクリプトを実行し、RC-S300の上にSuicaやPASMOをポンッと乗せてみてください。 コンソールにリアルタイムで残高が表示され、パソコンから「ピピッ! 残高は、〇〇円です」とクリアな日本語音声が流れるはずです。

このコードのブレイクスルー(技術的な解説)

ネット上の過去のコードをRC-S300で実行すると 0x6a 0x87 や 0x6a 0x81 というエラーで弾かれてしまいます。

今回のコードの最大のキモは、手順2のサービス選択コマンドにある「P2パラメータ(たった1バイトの引数)」です。

  • ❌ 失敗する旧コマンド:[0xFF, 0xA4, 0x00, 0x00, 0x02, 0x0F, 0x09]
  • ⭕ 成功する新コマンド:[0xFF, 0xA4, 0x00, 0x01, 0x02, 0x0F, 0x09]

ここを 0x01(サービスコード指定モード)にしてあげるだけで、RC-S300が正しく解釈し、続くRead Binaryコマンドで残高データを素直に返してくれるようになります。

まとめ

  • 最新のPaSoRi(RC-S300)はPC/SC規格になったため nfcpy は使えない。
  • 代わりに pyscard を使い、正規のAPDUコマンドを送ることで解決。
  • Suicaだけでなく、マイフェア(Mifare)等の社員証・学生証のID読み取りもこの構成で爆速で検知可能。

RC-S300を買ってしまって「失敗したかな…」と落ち込んでいた方は、ぜひこのコードを試して、快適なIoT・NFC開発を楽しんでください!

ブログ主の感想

最初AIに頼った際、動かなくて残念…と思って「なんでできないの?」「Python以外の言語なら?」「業務用のRC-S300/S1なら出来るの?」とAIを責めていたら、出来る答えが出てきました。
別に残高を確認するだけならスマホのアプリで十分でしたが、エンジニア見習いとして探求心が働きました。

今後はマイフェア(MIFARE)規格のカードを購入して、いろいろしてみる予定です。
「タッチだけで注文できたら」「タイムカードではなくタッチで勤怠管理」など、中小企業でも出来る事は沢山。
最近AIでプログラムコードを書いてもらっている私。大崎市の中小企業が発展する事を願って…

この記事が気に入ったら
フォローしてね!

シェアして頂けると嬉しいです。

コメントや質問募集中

コメントする

目次