質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.41%
Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

777閲覧

SONY Pasori RC-S380を接続するAndroidアプリケーション

Harunnko

総合スコア1

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2023/11/23 11:40

実現したいこと

Androidデバイスにて、SONY Pasori RC-S380を接続し、IDmの情報をディスプレイに表示するアプリケーションを作成したいです。

前提

質問させていただきます。
NFC非対応端末にて、NFCカードリーダーから情報を読み書きするアプリケーションを開発したいと考えております。
すでに実在するアプリもありますが、今後構想中のアプリに実装したいと考えているためテストや勉強のため、まずは情報を読み込む簡易的なアプリを作っています。

完全な初心者のため、インターネットで調べて情報を集めながら作成し、S380が接続されたら左上にONと表示するところまで実装しましたが、肝心のIDmを表示するということがうまく実現できておりません。

初歩的で申し訳ございませんが、ご教授いただけますと幸いです。

挑戦したフロー

1.webを参考にしながら作成したが、IDmが表示されない。
2.S380を認識していないなど、手前のエラーを確認するため、接続されたらON 未接続でOFFという表示を追加し、表示に成功。
3.テキスト表示ではなく、toastやsnackbarでの表示を試みるが失敗。

該当のソースコード

kotlin

1package com.example.myapplication 2 3import android.app.Activity 4import android.app.PendingIntent 5import android.content.BroadcastReceiver 6import android.content.Context 7import android.content.Intent 8import android.content.IntentFilter 9import android.hardware.usb.* 10import android.os.Bundle 11import android.os.Handler 12import android.os.Looper 13import android.util.Log 14import android.widget.TextView 15import android.widget.Toast 16import com.google.android.material.snackbar.Snackbar 17 18class MainActivity : Activity() { 19 private val TAG = "debug" 20 private val ACTION_USB_PERMISSION = "com.example.myapplication.USB_PERMISSION" 21 22 private var manager: UsbManager? = null 23 private var rcs380: UsbDevice? = null 24 private var ep_in: UsbEndpoint? = null 25 private var ep_out: UsbEndpoint? = null 26 private var con: UsbDeviceConnection? = null 27 private lateinit var textView: TextView 28 private lateinit var usbPermissionIntent: PendingIntent 29 private lateinit var usbPermissionReceiver: BroadcastReceiver 30 private lateinit var deviceStatusTextView: TextView 31 32 override fun onCreate(savedInstanceState: Bundle?) { 33 super.onCreate(savedInstanceState) 34 setContentView(R.layout.activity_main) 35 36 textView = findViewById(R.id.text_view) 37 manager = getSystemService(Context.USB_SERVICE) as UsbManager 38 39 usbPermissionIntent = PendingIntent.getBroadcast( 40 this, 0, Intent(ACTION_USB_PERMISSION), 41 PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT 42 ) 43 44 usbPermissionReceiver = object : BroadcastReceiver() { 45 override fun onReceive(context: Context, intent: Intent) { 46 if (ACTION_USB_PERMISSION == intent.action) { 47 synchronized(this) { 48 val device: UsbDevice? = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE) 49 50 if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { 51 device?.apply { 52 connectDevice(this) 53 } 54 } else { 55 Log.d(TAG, "Permission denied for device $device") 56 } 57 } 58 } 59 } 60 } 61 62 val filter = IntentFilter(ACTION_USB_PERMISSION) 63 registerReceiver(usbPermissionReceiver, filter) 64 65 discoverDevices() 66 67 // デバイスの接続状態を表示するTextViewを初期化 68 deviceStatusTextView = findViewById(R.id.deviceStatusTextView) 69 70 // USBデバイスの接続・切断を監視するBroadcastReceiverを設定 71 setupUsbDeviceBroadcastReceiver() 72 } 73 74 //ここから追加 75 private fun setupUsbDeviceBroadcastReceiver() { 76 val usbDeviceFilter = IntentFilter().apply { 77 addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED) 78 addAction(UsbManager.ACTION_USB_DEVICE_DETACHED) 79 } 80 registerReceiver(usbDeviceBroadcastReceiver, usbDeviceFilter) 81 } 82 83 private val usbDeviceBroadcastReceiver = object : BroadcastReceiver() { 84 override fun onReceive(context: Context, intent: Intent) { 85 when (intent.action) { 86 UsbManager.ACTION_USB_DEVICE_ATTACHED -> checkDeviceAndDisplayStatus() 87 UsbManager.ACTION_USB_DEVICE_DETACHED -> checkDeviceAndDisplayStatus() 88 } 89 } 90 } 91 92 private fun checkDeviceAndDisplayStatus() { 93 val isRcs380Connected = manager?.deviceList?.values?.any { it.vendorId == 0x054C && it.productId == 0x06C3 } == true 94 deviceStatusTextView.text = if (isRcs380Connected) "ON" else "OFF" 95 } 96 //ここまで 97 98 private fun discoverDevices() { 99 val deviceList = manager?.deviceList 100 deviceList?.values?.forEach { device -> 101 if (device.productId == 0x06C3 && device.vendorId == 0x054C) { 102 rcs380 = device 103 if (!manager!!.hasPermission(device)) { 104 manager?.requestPermission(device, usbPermissionIntent) 105 } else { 106 connectDevice(device) 107 } 108 } 109 } 110 } 111 112 private fun connectDevice(device: UsbDevice) { 113 con = manager?.openDevice(device) 114 if (con == null) { 115 Log.d(TAG, "Connection is null") 116 return 117 } 118 119 con?.apply { 120 if (!claimInterface(device.getInterface(0), true)) { 121 Log.d(TAG, "Cannot claim interface") 122 close() 123 return 124 } 125 126 device.getInterface(0)?.apply { 127 for (i in 0 until endpointCount) { 128 val ep = getEndpoint(i) 129 if (ep.direction == UsbConstants.USB_DIR_IN && ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK) { 130 ep_in = ep 131 } else if (ep.direction == UsbConstants.USB_DIR_OUT && ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK) { 132 ep_out = ep 133 } 134 } 135 } 136 } 137 138 readFelica() 139 } 140 141 // IDmを読み取り、結果をToastで表示するメソッド 142 private fun readFelica() { 143 // InListPassiveTarget Felica 144 val cmd = byteArrayOf(0xd4.toByte(), 0x4a, 0x1, 0x1, 0x0, 0xff.toByte(), 0xff.toByte(), 0x1, 0x0) 145 146 // rwCommandメソッドを呼び出してコマンドを送信し、成功したかどうかをチェック 147 val isSuccess = rwCommand(cmd, cmd.size) 148 149 // 実行結果に基づいてToastメッセージを表示 150 // 成功した場合はrwCommandメソッド内でIDmが表示されるので、ここでは何もしない 151 // 失敗した場合はToastメッセージで通知 152 if (!isSuccess) { 153 showToast("IDmの読み取りに失敗しました") 154 } 155 156 // 定期的に読み取りを繰り返す場合 157 Handler(Looper.getMainLooper()).postDelayed({ readFelica() }, 250) 158 } 159 160 private fun showSnackbar(message: String) { 161 runOnUiThread { 162 Snackbar.make(findViewById(android.R.id.content), message, Snackbar.LENGTH_LONG).show() 163 } 164 } 165 166 private fun rwCommand(cmd: ByteArray, cmd_len: Int): Boolean { 167 // 応答を受け取るためのバッファ 168 val tmp = ByteArray(64) 169 170 var buf = ByteArray(cmd_len + 7) 171 val dcs = calcDCS(cmd, cmd_len) 172 173 buf[0] = 0x0 174 buf[1] = 0x0 175 buf[2] = 0xff.toByte() 176 buf[3] = cmd_len.and(0xff).toByte() 177 buf[4] = (0x100 - buf[3]).and(0xff).toByte() 178 179 var co = 5 180 for (i in 0 until cmd_len) { 181 buf[co] = cmd[i] 182 co++ 183 } 184 185 buf[co] = dcs 186 co++ 187 buf[co] = 0x0 188 189 if (con?.bulkTransfer(ep_out, buf, buf.size, 100) != buf.size) { 190 Log.d(TAG, "Error USB Send") 191 return false 192 } 193 194 // USBデバイスから応答を受け取る 195 val responseLength = con?.bulkTransfer(ep_in, tmp, tmp.size, 100) 196 197 if (responseLength == null || responseLength < 0) { 198 Log.d(TAG, "応答に失敗しました") 199 return false 200 } 201 202 // 応答からIDmを取得する処理 203 if (tmp[5] == 0xd5.toByte() && tmp[6] == 0x4b.toByte() && tmp[7] == 0x1.toByte()) { 204 if (tmp[8] == 0x1.toByte()) { 205 var idm = "" 206 for (i in 11..18) { 207 idm += "%02x".format(tmp[i]) 208 } 209 showToast("IDm: $idm") 210 return true 211 } 212 } 213 214 showToast("IDmの読み取りに失敗しました") 215 return false 216 } 217 218 private fun showToast(message: String) { 219 runOnUiThread { 220 Toast.makeText(this, message, Toast.LENGTH_SHORT).show() 221 } 222 } 223 224 225 226 227 // calcDCSメソッドをここに定義 228 private fun calcDCS(datas: ByteArray, size: Int): Byte { 229 var sum = 0 230 for (i in 0 until size) { 231 sum += datas[i].toInt() and 0xFF 232 } 233 return ((0x100 - sum) and 0xFF).toByte() 234 } 235 override fun onDestroy() { 236 super.onDestroy() 237 // BroadcastReceiverの登録を解除 238 unregisterReceiver(usbDeviceBroadcastReceiver) 239 } 240 241 242 243} 244

試したこと

・toastやsnackbarでの表示
・カードリーダーの抜き差し、カードリーダーの交換
・すでに実装されたandroidアプリケーションでS380を接続、認識と動作をすることを確認

補足情報(FW/ツールのバージョンなど)

Android端末: FireHD 10 Plus 第11世代(このデバイスを使う必要があり、このデバイス以外で動作させることはありません)
FW:Fire OS 7.3.2.9

参考にさせていただいたURL
https://github.com/nagai-takayuki/Android/tree/master/FCFReaderForPasori
https://qiita.com/KazuyukiEguchi/items/8ed62198561449e4d64a
http://www.cc.kumamoto-u.ac.jp/nagai/apps/FCFReaderForPaSoRi
http://www.cc.kumamoto-u.ac.jp/sites/default/files/H3-2.pdf

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2023/11/23 14:20 編集

discoverDevices() は onCreate() でしか呼んでいませんが、接続した状態でアプリを起動するのではなく、接続していない状態から、接続したら通信という流れにしたいということでしょうか。
Harunnko

2023/11/23 14:19

コメントいただきありがとうございます。 ご指摘いただいた部分ですが、接続していない状態で起動する事があります(usbハブを持っておらず、テストで起動する際にPCと接続しているため)。 あまり良い書き方ではないでしょうか?
jimbe

2023/11/23 14:24

androidmanifest に intentfilter で usb_device_attached を書いていれば アプリが動いていない時に接続したらアプリが起動するでしょうけれど、アプリを起動中に usb を抜き差しした時はどうするのかなという感じでした。(ON/OFFは表示できているとのことで。) それと、書かれているコマンドは S380 のでは無いのではないでしょうか。 S370 のコマンドでは動かないと思います。
Harunnko

2023/11/23 15:00

そうなのですね…常に起動している想定だったのですが、起動中に抜き差ししたらどうするかも設定しないとなのですね。勉強不足です。 なるほど…ベンダーID プロダクトIDを変えれば対応のデバイスに変更すれば使えると安易に考えておりました。 無知すぎて恥ずかしい限りです。 改めてS380用のコマンドを勉強します。最悪おとなしくS370を購入しようと思います。
jimbe

2023/11/23 16:43

探したところでは以下の記事とリンクで色々情報が取れると思います。 今更ですが、SONY RC-S380 で Suica の IDm を読み込んでみた https://qiita.com/ysomei/items/32f366b61a7b631c4750 私も手元に RC-S380 がありますので興味出てやってみてます^^;
Harunnko

2023/11/24 03:06

リンクまで教えていただきありがとうございます! 大変助かります。 手元にあり、さらにトライまでしていただけるとは...恐縮です 教えていただいたサイトを参考に、トライしてみます!
guest

回答1

0

ベストアンサー

以下記事内でリンクされている Python プログラムを java 化して動作させてみました。
NFC の用語すらよく分からないまま手持ち PASMO でソレっぽいのが読めたとしか確認していませんので、いろいろご注意ください。

今更ですが、SONY RC-S380 で Suica の IDm を読み込んでみた

nfcpy(rcs380.py)
https://github.com/nfcpy/nfcpy/blob/master/src/nfc/clf/rcs380.py

teratail に書ききれないので GitHub です。
https://github.com/Jimbe-github/com.teratail.q_bqafusg2g5at35

投稿2023/11/25 11:12

編集2023/11/25 11:17
jimbe

総合スコア12951

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Harunnko

2023/11/26 06:15

jimbeさん お忙しい中、調整までしてくださったのですか…!本当にありがとうございます。 数日色々試しましたがことごとく失敗し、chaquopyを使いpythonを使おうと試行錯誤している最中でした。 とても助かります…。帰宅後、早速試させていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.41%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問