LoginSignup
6
6

More than 5 years have passed since last update.

Android Things 1.0.1搭載のRaspberry Pi 3に、USB接続のFelicaリーダーを繋いで、Felicaのカードを読んでみる

Posted at

経緯

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じゃないので注意ね! )

IMG_9951.jpg

コード

一応、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()
6
6
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6