Felica Library「felicalib.dll」

felicalib.dllは、PaSoRi を使って FeliCa のデータを読み書きするためのライブラリです。対応機種は RC-S380 / S370 / S330 / S320になっています。 FeliCa Libraryから felicalib-0.4.2.zip をダウンロードして下さい。解凍して、felicalib.dll を実行ファイル(exe)と同じフォルダに入れれば完了です。

動作環境

  • Windows 7 Professional
  • Microsoft Visual Studio Express 2013 for Windows Desktop
  • PaSoRi RC-S380
  • felicalib-0.4.2.zip

RC-S380 非接触ICカードリーダー/ライター PaSoRi(パソリ)

RC-S380 非接触ICカードリーダー/ライター PaSoRi

Windows® 8搭載パソコンに接続して、NFC対応携帯電話とのデータ送受信やNFC対応周辺機器とのペアリングが簡単に行えるUSB接続の非接触ICカードリーダー/ライターです。

・住民基本台帳カードをかざして確定申告・納税
公的個人認証サービスに対応し、自宅からインターネット経由で、確定申告や納税、申請・届出などができます。
・ネット入金やネットショッピング
Edyチャージ(入金)が簡単に利用でき、「楽天Edy」「SuiCa」カード対応のWebサイトでショッピングができます。
・交通乗車券の利用履歴や残高確認
「Suica」「PASMO」「PiTaPa」などのICカード交通乗車券の利用履歴、残高などが確認できます。

Suicaのフォーマット

Suica/ICOCA/PiTaPa/PASMO/TOICA は基本的に同じフォーマットです。 Suica には入出場記録と履歴の2つのサービスコードがあります。以下の説明は履歴のほうのみで、サービスコードは 0x090fとなります。

各エントリは16バイトで、次にフォーマットを示します。

  • 0: 端末種
  • 1: 処理
  • 2-3: ??
  • 4-5: 日付 (先頭から7ビットが年、4ビットが月、残り4ビットが日)
  • 6 : 入線区
  • 7 : 入駅順
  • 8 : 出線区
  • 9 : 出駅順
  • 10-11: 残高 (little endian)
  • 12-14: 連番
  • 15: リージョン

残高だけ little endian となります。なお、詳細については、FeliCa Libraryを参照してください。

C#によるSuicaの読み出しソフト

次のプログラムは、C#によるSuicaの読み出しソフトです。Suicaの使用履歴は表形式で表示されます(表形式のformは割愛されています)。

// サービスコード
public const int SERVICE_SUICA_INOUT = 0x108f;
public const int SERVICE_SUICA_HISTORY = 0x090f;

private int readSuica(Felica f)
{
    f.Polling((int)SystemCode.Suica);

    for (int i = 0; ; i++)
    {
        data = f.ReadWithoutEncryption(SERVICE_SUICA_HISTORY, i);
        if (data == null) break;
        suica_dump_history(data);
    }

    f.Dispose();
    return 0;
}
private void suica_dump_history(byte[] data)
{
    int ctype, proc, date, time, balance, seq, region;
    int in_line, in_sta, out_line, out_sta;
    int yy, mm, dd;
    DataRow row = dt.NewRow();

    ctype = data[0];            // 端末種
    proc = data[1];             // 処理
    date = (data[4] << 8) + data[5];        // 日付
    balance = (data[10] << 8) + data[11];   // 残高
    balance = ((balance) >> 8) & 0xff | ((balance) << 8) & 0xff00;
    seq = (data[12] << 24) + (data[13] << 16) + (data[14] << 8) + data[15];
    region = seq & 0xff;        // Region
    seq >>= 8;                  // 連番

    out_line = -1;
    out_sta = -1;
    time = -1;

    switch (ctype)
    {
        case 0xC7:  // 物販
        case 0xC8:  // 自販機          
            time = (data[6] << 8) + data[7];
            in_line = data[8];
            in_sta = data[9];
            break;

        case 0x05:  // 車載機
            in_line = (data[6] << 8) + data[7];
            in_sta = (data[8] << 8) + data[9];
            break;

        default:
            in_line = data[6];
            in_sta = data[7];
            out_line = data[8];
            out_sta = data[9];
            break;
    }

    row["Tanmatu"] = consoleType(ctype);
    row["Proc"] = procType(proc);

    // 日付
    yy = date >> 9;
    mm = (date >> 5) & 0xf;
    dd = date & 0x1f;
    row["Date"] = yy.ToString() + "/" + mm.ToString() + "/" + dd.ToString() + " ";

    // 時刻
    if (time > 0)
    {
        int hh = time >> 11;
        int min = (time >> 5) & 0x3f;

        row["Date"] += hh.ToString() + ":" + min.ToString();
    }

    row["Iri"] = in_line.ToString("X") + "/" + in_sta.ToString("X");
    if (out_line != -1)
    {
          row["Iri"] = out_line.ToString("X") + "/" + out_sta.ToString("X");
    }
    row["Zandaka"] = balance.ToString();
    row["Renban"] = seq.ToString();
    dt.Rows.Add(row);

}

private string consoleType(int ctype)
{
    switch (ctype)
    {
        case 0x03: return "清算機";
        case 0x05: return "車載端末";
        case 0x08: return "券売機";
        case 0x12: return "券売機";
        case 0x16: return "改札機";
        case 0x17: return "簡易改札機";
        case 0x18: return "窓口端末";
        case 0x1a: return "改札端末";
        case 0x1b: return "携帯電話";
        case 0x1c: return "乗継清算機";
        case 0x1d: return "連絡改札機";
        case 0xc7: return "物販";
        case 0xc8: return "自販機";
    }
    return "???";
}

private string procType(int proc)
{
    switch (proc)
    {
        case 0x01: return "運賃支払";
        case 0x02: return "チャージ";
        case 0x03: return "券購";
        case 0x04: return "清算";
        case 0x07: return "新規";
        case 0x0d: return "バス";
        case 0x0f: return "バス";
        case 0x14: return "オートチャージ";
        case 0x46: return "物販";
        case 0x49: return "入金";
        case 0xc6: return "物販(現金併用)";
    }
    return "???";
}

RC-S380 非接触ICカードリーダーでSuicaカードを読み込んだ結果は、次のようになります。

Suicaカードを読み込んだ結果

.NetFramework 4.0で使用する場合に必要なFelicaLib.csの変更

C# の関数呼び出し時に、次のエラーが出てしまいます。これは、.NetFramework 3.5 の環境から 4.0 に変更した事により発生するエラーで、提供されているFelicaLib.cs内のDllImportを使用したfelicalib.dllの呼び出しパラメータを変更する必要があります。

マネージ デバッグ アシスタント ‘PInvokeStackImbalance’ が ‘C:\Users\ne\Documents\Visual Studio 2013\Projects\FelicaTool\FelicaTool\bin\Debug\FelicaTool.vshost.exe’ で問題を検出しました。
追加情報:PInvoke 関数 ‘FelicaTool!FelicaLib.Felica::felica_polling’ がスタックを不安定にしています。・・・・

提供されているFelicaLib.csのパラメータの変更方法の詳細を次に示します。
[DllImport(“felicalib.dll”)]
private extern static IntPtr pasori_open(String dummy);
           ↓
           ↓(次のように変更)
           ↓
[DllImport(“felicalib.dll”, CallingConvention = CallingConvention.Cdecl)]
private extern static IntPtr pasori_open(String dummy);

SonyのSDK for NFC Starter KitとPaSoRi RC-S380

SonyのSDK for NFC Starter Kitを使用しても、PaSoRiにはアクセスできます。しかし、新型RC-S380からは同もアクセスできないことが話題になっています。NFCタグへのアクセスは、今後はデスクトップUIではPC/SC経由、メトロUIではNFP経由に集約されます。FeliCa Lib.は残るようです。
RC-S330/370はNFC対応ではあるのですが、NFC Forum対応ではないためこの中間解のようなAPIが定義されていたということになり、業界標準であるPC/SCにも完全対応が終わったRC-S380からは、役目を終えたことになります。
実際に、SDK for NFC Starter KitのC#のサンプル((ルート)→ sample → NFC_and_PCSC → ReadingMifareCard → src → Csharp → nfc_sample_01フォルダ内)を、felica_nfc_library.dllを使用して、PaSoRiにアクセスすると次のようなエラーがでます。

PaSoRiのアクセスエラー