Android - 串口通讯(SerialPort)

2024-01-11 18:36

本文主要是介绍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

3、示例 MainActivity

4、通过logcat验证app

二、注意

三、总结

四、demo地址


一、简单说明

1、添加依赖
dependencies {...//添加依赖implementation ("com.licheedev:android-serialport:2.1.3")}
2、创建串口通讯工具类 SerialPortUtil
class SerialPortUtil {companion object {private const val TAG = "SerialPortUtil"val sInstances by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {SerialPortUtil()}}private lateinit var mIOpenSerialPortListener: IOpenSerialPortListenerprivate lateinit var mISerialPortDataListener: ISerialPortDataListenerprivate lateinit var mSendingHandlerThread: HandlerThreadprivate lateinit var mSendingHandler: Handlerprivate lateinit var mSerialPortReceivedThread: SerialPortReceivedThreadprivate lateinit var mFileInputStream: FileInputStreamprivate lateinit var mFileOutputStream: FileOutputStreamprivate 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 FileInputStreammFileOutputStream = 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 = bytesmSendingHandler.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
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
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")

这篇关于Android - 串口通讯(SerialPort)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/595327

相关文章

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目

Android fill_parent、match_parent、wrap_content三者的作用及区别

这三个属性都是用来适应视图的水平或者垂直大小,以视图的内容或尺寸为基础的布局,比精确的指定视图的范围更加方便。 1、fill_parent 设置一个视图的布局为fill_parent将强制性的使视图扩展至它父元素的大小 2、match_parent 和fill_parent一样,从字面上的意思match_parent更贴切一些,于是从2.2开始,两个属性都可以使用,但2.3版本以后的建议使

Android Environment 获取的路径问题

1. 以获取 /System 路径为例 /*** Return root of the "system" partition holding the core Android OS.* Always present and mounted read-only.*/public static @NonNull File getRootDirectory() {return DIR_ANDR