Android之USB转串口通信-基本流程

/ Android / 没有评论 / 2415浏览

Android之USB转串口通信-基本流程

背景:近期公司一个项目对接第三方支付设备(类似平板的设备外接usb转串口设备),需要使用usb转串口,实现通信和交互,今天记下过程。有引用这个库 https://github.com/mik3y/usb-serial-for-android,感谢开源的大佬。这个库已经集合了一般的芯片协议,就不需要自己再配置了。我们设备是用的ProlificSerialDriver.唯一要注意的是配置设备的参数:波特率,数据位,体制位,奇偶校验等。其中我就在奇偶校验的参数配置上坑了一把。

只需要按照流程,注意些细节就能跑。贴下核心的几段代码:

import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbManager
import com.hoho.android.usbserial.driver.UsbSerialPort
import com.hoho.android.usbserial.driver.UsbSerialProber

class ZYDataSource(){
    var mUsbSeriaPortManager: USBSerialPortManager? = null
    private var usbDeviceConnection: UsbDeviceConnection? = null
    private var mDevice: UsbDevice? = null
    private var mUsbSerialPort: UsbSerialPort? = null
    private var usbPermissionReceiver: UsbPermissionReceiver? = null

    private var mUsbManager: UsbManager? = null

    /**
    * 初始化
    */
    override fun initCompletable(context: Context): Completable {
        return Completable.create() {
            mUsbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
            //查找所有设备
            val driversList = UsbSerialProber.getDefaultProber().findAllDrivers(mUsbManager)
            if (driversList == null || driversList.size == 0) {
                throw Exception("未找到设备")
            }
            //直接取第一个.
            mDevice = driversList.first().device

            if (!mUsbManager?.hasPermission(mDevice)!!) {
                usbPermissionReceiver = UsbPermissionReceiver()
                //申请权限
                val intent = Intent(ACTION_DEVICE_PERMISSION)
                val mPermissionIntent = PendingIntent.getBroadcast(context, 0, intent, 0)
                val permissionFilter = IntentFilter(ACTION_DEVICE_PERMISSION)
                context.registerReceiver(usbPermissionReceiver, permissionFilter)
                mUsbManager?.requestPermission(mDevice, mPermissionIntent)
            } else {
                this.openDeviceConnection(device = mDevice!!)
            }

            it.onComplete()
        }
    }

    inner class UsbPermissionReceiver : BroadcastReceiver() {
        override fun onReceive(p0: Context?, intent: Intent?) {
            val action = intent?.getAction()
            if (ACTION_DEVICE_PERMISSION.equals(action)) {
                synchronized(this) {
                    val device = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE)
                    openDeviceConnection(device)
                }
            }
        }

    }

    /**
    * 打开连接
    */
    fun openDeviceConnection(device: UsbDevice) {
        synchronized(this) {
            //授权成功,在这里进行打开设备操作
            val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(mUsbManager)
            if (availableDrivers.isEmpty()) {
                return
            }

            // Open a connection to the first available driver.
            val driver = availableDrivers[0]
            // Read some data! Most have just one port (port 0).
            mUsbSerialPort = driver.ports[0]
            try {
                mUsbSeriaPortManager = USBSerialPortManager(mUsbSerialPort!!)
                usbDeviceConnection = mUsbManager?.openDevice(device)
                mUsbSerialPort?.open(usbDeviceConnection)
                mUsbSerialPort?.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_EVEN)
            } catch (e: Exception) {
                XLog.tag(TAG).e(e.message)
                throw Exception("设备初始化失败")
            }

        }
    }

    /**
    * 实际通信调用方法-找卡
    */
    override fun searchCardSingle(requestData: ByteArray): Single<ByteArray> {
        return Single.just(requestData)
        .flatMap { data ->
                  val command = USBSerialPortManager.Command(data, data.size)
                  //通信
                  mUsbSeriaPortManager!!.execute(command)
                 }
        .flatMap {
            Single.just(it.rxBytes)
        }
    }
}

下面这个是USBSerialPortManager工具类

import com.hoho.android.usbserial.driver.UsbSerialPort
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers


class USBSerialPortManager(private val usbSerialPort: UsbSerialPort,
                           private val writeTimeout: Int = 100,
                           private val readTimeout: Int = 50) {
    open class Command(val hex: String, var rxLength: Int = 0, var rx: String = "") {
        val bytes: ByteArray
        get() = hex.hexStringToByteArray()
        val rxBytes: ByteArray
        get() = rx.hexStringToByteArray()

        constructor(bytes: ByteArray, rxLength: Int) : this(bytes.toHex(), rxLength, "")
    }

    private fun runOnceSingle(command: Command, timeoutInMillis: Int = 2000): Single<Command> {
        return Single.create {
            XLog.tag(TAG).i("send data=${command.hex} to $usbSerialPort")
            val txBytes = command.hex.hexStringToByteArray()
            usbSerialPort.write(txBytes, writeTimeout)

            if (command.rxLength <= 0) {
                command.rx = ""
                it.onSuccess(command)
                return@create
            }

            var times = 0L
            val interval = 2L
            val bytes = ByteArray(command.rxLength)
            var totalLength = 0
            while (totalLength < command.rxLength) {
                val bytesToRead = ByteArray(command.rxLength - totalLength)
                val n = usbSerialPort.read(bytesToRead, readTimeout)
                if (n > 0) {
                    for (i in 0 until n) {
                        bytes[totalLength + i] = bytesToRead[i]
                    }
                    totalLength += n
                }
                Thread.sleep(interval)
                times += interval
                if (times > timeoutInMillis) {
                    XLog.tag(TAG).e("read data from $usbSerialPort timeout, expect ${command.rxLength} bytes")
                    it.onError(Exception("read data from $usbSerialPort timeout, expect ${command.rxLength} bytes"))
                    return@create
                }
            }
            command.rx = bytes.toHex()
            XLog.tag(TAG).i("recv data=${command.rx} from $usbSerialPort")
            it.onSuccess(command)
        }
    }

    fun execute(command: Command, retryTimes: Long = 0L): Single<Command> {
        return runOnceSingle(command).retry(retryTimes).subscribeOn(Schedulers.single())
    }

    companion object {
        const val TAG = "USBSerialPortManager"
    }
}

代码是kotlin语言,没有写注释,目前项目已上线。工具类基本可以通用。