Android - 串口通讯(SerialPort)

最早的博客Android 模拟串口通信过程_launch virtual serial port driver pro-CSDN博客里就是用过 Google 提供的 demo,最近想再写个其他的demo发现用起来有点麻烦,还需要导入其他 module,因此在网上找到了Android-SerialPort-API: https://github.com/licheedev/Android-SerialPort-API.git 也是Fork自Google开源的Android串口通信Demo。

话不多说,直接开搞。

目录

一、简单说明

1、添加依赖

[2、创建串口通讯工具类 SerialPortUtil](#2、创建串口通讯工具类 SerialPortUtil)

[3、示例 MainActivity](#3、示例 MainActivity)

4、通过logcat验证app

二、注意

三、总结

四、demo地址


一、简单说明

1、添加依赖
复制代码
dependencies {

...
    //添加依赖
    implementation ("com.licheedev:android-serialport:2.1.3")

}
2、创建串口通讯工具类 SerialPortUtil
Kotlin 复制代码
class SerialPortUtil {

    companion object {

        private const val TAG = "SerialPortUtil"

        val sInstances by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
            SerialPortUtil()
        }
    }

    private lateinit var mIOpenSerialPortListener: IOpenSerialPortListener
    private lateinit var mISerialPortDataListener: ISerialPortDataListener

    private lateinit var mSendingHandlerThread: HandlerThread
    private lateinit var mSendingHandler: Handler
    private lateinit var mSerialPortReceivedThread: SerialPortReceivedThread

    private lateinit var mFileInputStream: FileInputStream
    private lateinit var mFileOutputStream: FileOutputStream
    private lateinit var serialPort: SerialPort

    /**
     * 打开串口方法
     */
    fun open(path: File) {
        try {
            serialPort = SerialPort
                .newBuilder(path, 9600) // 校验位;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)
                .build()
            mFileInputStream = serialPort.inputStream as FileInputStream
            mFileOutputStream = serialPort.outputStream as FileOutputStream
        } catch (e: SecurityException) {
            mIOpenSerialPortListener.onFail(path, Status.NO_READ_WRITE_PERMISSION)
            return
        } catch (e: Exception) {
            mIOpenSerialPortListener.onFail(path, Status.OPEN_FAIL)
            return
        }
        mIOpenSerialPortListener.onSuccess(path)
        startSendThread()
        startReceivedThread()
    }

    /**
     * 发送数据
     */
    fun sendBytes(bytes: ByteArray?): Boolean {
        try {
            Runnable {
                val message = Message.obtain()
                message.obj = bytes
                mSendingHandler.sendMessage(message)
                Thread.sleep(100)
            }.run()
        } catch (e: Exception) {
            Log.e(TAG, "sendBytes: 发送数据失败 " + e.message)
            return false
        }
        return true
    }

    /**
     * 开启发送消息线程
     */
    private fun startSendThread() {
        Log.d(TAG, "startSendThread: 开启发送消息线程")
        mSendingHandlerThread = HandlerThread("mSendingHandlerThread")
        mSendingHandlerThread.start()
        mSendingHandler = object : Handler(mSendingHandlerThread.looper) {

            override fun handleMessage(msg: Message) {
                val sendBytes: ByteArray? = msg.obj as ByteArray?
                if ((null != sendBytes) && (sendBytes.isNotEmpty())) {
                    try {
                        mFileOutputStream.write(sendBytes)
                        mISerialPortDataListener.onDataSend(sendBytes)
                    } catch (e: java.io.IOException) {
                        e.printStackTrace()
                    }
                }
            }
        }
    }

    /**
     * 停止发送消息线程
     */
    fun stopSendThread() {
        Log.d(TAG, "stopSendThread: 停止发送消息线程")
        mSendingHandlerThread.interrupt()
        mSendingHandlerThread.quit()
    }

    /**
     * 开启接收消息的线程
     */
    private fun startReceivedThread() {
        Log.d(TAG, "startReceivedThread: 开启接受消息线程")
        mSerialPortReceivedThread = object : SerialPortReceivedThread(mFileInputStream) {
            override fun onDataReceived(bytes: ByteArray?) {
                mISerialPortDataListener.onDataReceived(bytes)
            }
        }
        mSerialPortReceivedThread.start()
    }

    /**
     * 停止接收消息的线程
     */
    fun stopReceivedThread() {
        Log.d(TAG, "stopReceivedThread: 停止接收消息的线程")
        mSerialPortReceivedThread.release()
        serialPort.tryClose()
    }

    /**
     * 设置串口打开的监听
     */
    fun setIOpenSerialPortListener(iOpenSerialPortListener: IOpenSerialPortListener) {
        mIOpenSerialPortListener = iOpenSerialPortListener
    }

    /**
     * 设置串口数据收发的监听
     */
    fun setISerialPortDataListener(iSerialPortDataListener: ISerialPortDataListener) {
        mISerialPortDataListener = iSerialPortDataListener
    }

}

在这里简单介绍些,在开启串口通讯后开启两个线程,分别处理send及received步骤。

3、示例 MainActivity
Kotlin 复制代码
class MainActivity : Activity(), IOpenSerialPortListener, ISerialPortDataListener {

    companion object {
        private const val TAG = "MainActivity"
        private val FIND_CARD =
            byteArrayOf(0x20, 0x00, 0x80.toByte(), 0x04, 0x03, 0x03, 0x01, 0x00, 0x7a, 0x03)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        CrashHandler.sInstance.init(this)

        //0、设置su位置
        SerialPort.setSuPath("/system/xbin/su")
        //1、首先设置open监听
        SerialPortUtil.sInstances.setIOpenSerialPortListener(this)
        //2、设置串口监听
        SerialPortUtil.sInstances.setISerialPortDataListener(this)
        //3、open串口
        SerialPortUtil.sInstances.open(File("/dev/ttyS3"))
        //4、模拟发送命令
        SerialPortUtil.sInstances.sendBytes(FIND_CARD)
    }

    override fun onDestroy() {
        super.onDestroy()
        SerialPortUtil.sInstances.stopSendThread()
        SerialPortUtil.sInstances.stopReceivedThread()
    }

    override fun onSuccess(device: File?) {
        Log.d(TAG, "onSuccess: open成功")
    }

    override fun onFail(device: File?, status: Status?) {
        Log.d(TAG, "onFail: open失败,原因: $status")
    }

    override fun onDataReceived(bytes: ByteArray?) {
        Log.d(TAG, "onDataReceived: 接受数据: " + Arrays.toString(bytes))
    }

    override fun onDataSend(bytes: ByteArray?) {
        Log.d(TAG, "onDataSend: 发送数据: " + Arrays.toString(bytes))
    }

}
4、通过logcat验证app
Kotlin 复制代码
09:52:48.349 serial_port               D  Opening serial port /dev/ttyS3 with flags 0x2
09:52:48.349 serial_port               D  open() fd = 45
09:52:48.349 serial_port               D  Configuring serial port
09:52:48.359 MainActivity              D  onSuccess: open成功
09:52:48.359 SerialPortUtil            D  startSendThread: 开启发送消息线程
09:52:48.359 SerialPortUtil            D  startReceivedThread: 开启接受消息线程
09:52:48.359 SerialPortReceivedThread  I  run: available =  0
09:52:48.359 MainActivity              D  onDataSend: 发送数据: [32, 0, -128, 4, 3, 3, 1, 0, 122, 3]
09:52:48.529 BufferQueue               E  [com.lichang.source/com.lichang.source.MainActivity] connect: already connected (cur=1, req=1)
09:52:48.529 mali_winsys               D  EGLint new_window_surface(egl_winsys_display*, void*, EGLSurface, EGLConfig, egl_winsys_surface**, egl_color_buffer_format*, EGLBoolean) returns 0x3000
09:52:48.529 OpenGLRenderer            D  Enabling debug mode 0
09:52:51.609 SerialPortReceivedThread  I  run: size = 14
09:52:51.609 SerialPortReceivedThread  I  run: bytes = [32, 0, 0, 8, 4, 0, 8, 4, 28, 37, -117, -5, -74, 3]
09:52:51.609 MainActivity              D  onDataReceived: 接受数据: [32, 0, 0, 8, 4, 0, 8, 4, 28, 37, -117, -5, -74, 3]
09:52:51.609 SerialPortReceivedThread  I  run: available =  0
09:52:53.729 SerialPortReceivedThread  I  run: size = 22
09:52:53.729 SerialPortReceivedThread  I  run: bytes = [32, 0, 0, 16, 32, 17, 35, 69, 103, -113, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 80, 3]
09:52:53.729 MainActivity              D  onDataReceived: 接受数据: [32, 0, 0, 16, 32, 17, 35, 69, 103, -113, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 80, 3]
09:52:53.729 SerialPortReceivedThread  I  run: available =  0
09:53:03.239 dalvikvm                  D  Debugger has detached; object registry had 1 entries
09:53:03.249 dalvikvm                  D  GC_CONCURRENT freed 222K, 15% free 2334K/2744K, paused 1ms+1ms, total 12ms
09:53:12.419 SerialPortUtil            D  stopSendThread: 停止发送消息线程
09:53:12.419 SerialPortUtil            D  stopReceivedThread: 停止接收消息的线程
09:53:12.419 serial_port               D  close(fd = 45)

二、注意

为了在Android上读/写串行端口,你需要在设备上安装su binary(这可以通过root设备来完成)。通常,具有串口通信能力的Android设备已将su安装在默认路径下。

默认的 su 路径使用的是 "/system/bin/su"

三、总结

在新项目中快速应用,可以先导入依赖,然后copy示例中的 com/lichang/source/serialport文件夹,即可按照 3、示例 MainActivity 钓箱串口工具类。

四、demo地址

serialport-demo_kt: 使用 implementation ("com.licheedev:android-serialport:2.1.3")

相关推荐
雨白8 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹9 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空11 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭11 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日12 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安12 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑12 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟17 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡18 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0018 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体