経緯
Android Things 1.0.1で遊んでいて、HIDのキーボードもマウスも繋がるなあと、USB結構使えるんじゃないか?と、USB接続のバーコードリーダーでキーイベント拾えるなあとやっていたら、そういえば、Androidには、USB HOSTのAPIあったよなあと思い出し、それなら、Felicaリーダー(RC-S370)を繋いでやってみようと!
コードは、kotlinで書いています。
環境
Android Studio 3.1.3
Android Things 1.0.1 for Raspberry Pi 3
SONY RC-S370 ( RC-S380じゃないので注意ね! )
コード
一応、USB Hostを指定しておいた。パーミッションは特に付けていない
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.eguchi.android.androidthingstest5">
<uses-feature android:name="android.hardware.usb.host"/>
<application>
<uses-library android:name="com.google.android.things" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.IOT_LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.kt
package jp.eguchi.android.androidthingstest5
// Android Things 1.0.1 で Raspberry Pi3 のUSBポートに、RC-S370を繋いで、FelicaのIDm読んでみよう!
// Progmramed by Kazuyuki Eguchi
import android.app.Activity
import android.content.Context
import android.hardware.usb.*
import android.os.Bundle
import android.os.Handler
import android.util.Log
class MainActivity : Activity() {
private val TAG = "debug"
private var manager : UsbManager? = null
private var rcs370: UsbDevice? = null
private var ep_in : UsbEndpoint? = null
private var ep_out : UsbEndpoint? = null
private var con: UsbDeviceConnection? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG,"onCraete()")
setContentView(R.layout.activity_main)
manager = getSystemService(Context.USB_SERVICE) as UsbManager
val devices = manager?.deviceList
for(device in devices!!){
val value = device.value
// RC-S370
if((value.productId == 0x2e1) && (value.vendorId == 0x54c)) {
Log.d(TAG,"manufacturerName=" + value.manufacturerName)
Log.d(TAG,"productName=" + value.productName)
rcs370 = value;
con = manager?.openDevice(rcs370)
if(con == null)
{
Log.d(TAG,"con is null");
return;
}
Log.d(TAG,"Interface count=" + rcs370?.interfaceCount)
if(!con!!.claimInterface(rcs370?.getInterface(0),true)){
Log.d(TAG,"Error getInterface")
con?.close()
return
}
for(i in 0..rcs370?.getInterface(0)!!.endpointCount-1){
val ep = rcs370?.getInterface(0)!!.getEndpoint(i)
if(ep.direction == UsbConstants.USB_DIR_IN && ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK){
ep_in = ep;
}
if(ep.direction == UsbConstants.USB_DIR_OUT && ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK)
{
ep_out = ep;
}
}
// GetFirmwareVersion
val cmd1 = byteArrayOf(0xd4.toByte(),0x02)
rwCommand(cmd1,cmd1.size)
// RFConfiguration RF OFF
val cmd2 = byteArrayOf(0xd4.toByte(),0x32,0x1,0x0)
rwCommand(cmd2,cmd2.size)
// RFConfiguration Max Retry
val cmd3 = byteArrayOf(0xd4.toByte(),0x32,0x5,0x0,0x0,0x0)
rwCommand(cmd3,cmd3.size)
// RFConfiguration Max Retry
val cmd4 = byteArrayOf(0xd4.toByte(),0x32,0x81.toByte(),0xb7.toByte())
rwCommand(cmd4,cmd4.size)
read_felica()
}
}
}
private fun read_felica(){
Log.d(TAG,"read_felica()")
// InListPassiveTarget Felica
val cmd5 = byteArrayOf(0xd4.toByte(),0x4a,0x1,0x1,0x0,0xff.toByte(),0xff.toByte(),0x1,0x0)
rwCommand(cmd5,cmd5.size)
// RFConfiguration RF OFF
val cmd2 = byteArrayOf(0xd4.toByte(),0x32,0x1,0x0)
rwCommand(cmd2,cmd2.size)
Handler().postDelayed(Runnable {
read_felica()
},250)
}
private fun print_hex (mes: ByteArray, size:Int) : String {
var res = ""
for(i in 0..size-1){
res = res + "%02x".format(mes.get(i))
}
return res;
}
private fun calcDCS(datas: ByteArray,size: Int) : Byte {
var sum = 0
for(i in 0..size-1){
sum += datas.get(i)
}
return(((0x100)-(sum.and(0xff))).and(0xff).toByte())
}
private fun rwCommand(cmd: ByteArray,cmd_len: Int) : Boolean {
var tmp = ByteArray(64)
var buf = ByteArray(cmd_len + 7)
val dcs = calcDCS(cmd,cmd_len)
buf[0] = 0x0
buf[1] = 0x0
buf[2] = 0xff.toByte()
buf[3] = cmd_len.and(0xff).toByte()
buf[4] = (0x100 - buf[3]).and(0xff).toByte()
var co = 5
for(i in 0..cmd_len-1){
buf[co] = cmd[i]
co++
}
buf[co] = dcs;
co++
buf[co] = 0x0
co++
if(con?.bulkTransfer(ep_out,buf,buf.size,100) != buf.size){
Log.d(TAG,"Error USB Send")
return false
}
var nRet = con?.bulkTransfer(ep_in,tmp,tmp.size,100)
if(nRet != 6){
Log.d(TAG,"Error USB Recv")
return false
}
if(tmp.get(0) != 0x0.toByte() || tmp.get(1) != 0x0.toByte() || tmp.get(2) != 0xff.toByte() || tmp.get(3) != 0x0.toByte() || tmp.get(4) != 0xff.toByte() || tmp.get(5) != 0x0.toByte()){
Log.d(TAG,"Error Not ACK")
return false
}
nRet = con?.bulkTransfer(ep_in,tmp,tmp.size,100)
if(tmp.get(5) == 0xd5.toByte() && tmp.get(6) == 0x4b.toByte() && tmp.get(7) == 0x1.toByte())
{
if(tmp.get(8) == 0x1.toByte()){
var idm = ""
for(i in 11..18){
idm += "%02x".format(tmp.get(i))
}
Log.d(TAG,"IDm=" + idm)
}
}
return true
}
}
実行結果
logcat
06-17 13:50:15.378 10069-10069/? I/zygote: Late-enabling -Xcheck:jni
06-17 13:50:16.047 10069-10069/jp.eguchi.android.androidthingstest5 I/InstantRun: starting instant run server: is main process
06-17 13:50:16.148 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: onCraete()
06-17 13:50:16.551 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: manufacturerName=Sony
06-17 13:50:16.552 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: productName=RC-S370/P
06-17 13:50:16.555 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: Interface count=1
06-17 13:50:16.574 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: read_felica()
06-17 13:50:16.871 10069-10069/jp.eguchi.android.androidthingstest5 D/vndksupport: Loading /vendor/lib/hw/android.hardware.graphics.mapper@2.0-impl.so from current namespace instead of sphal namespace.
06-17 13:50:16.948 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: read_felica()
06-17 13:50:17.237 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: read_felica()
06-17 13:50:19.256 10069-10069/jp.eguchi.android.androidthingstest5 I/chatty: uid=10033(jp.eguchi.android.androidthingstest5) identical 7 lines
06-17 13:50:19.545 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: read_felica()
06-17 13:50:19.593 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: IDm=01105000e10ddc04
06-17 13:50:19.849 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: read_felica()
06-17 13:50:19.897 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: IDm=01105000e10ddc04
06-17 13:50:20.152 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: read_felica()
06-17 13:50:20.201 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: IDm=01105000e10ddc04
06-17 13:50:20.457 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: read_felica()
06-17 13:50:20.745 10069-10069/jp.eguchi.android.androidthingstest5 I/chatty: uid=10033(jp.eguchi.android.androidthingstest5) identical 1 line
06-17 13:50:21.034 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: read_felica()
06-17 13:50:21.323 10069-10069/jp.eguchi.android.androidthingstest5 D/debug: read_felica()