/LSerialPort

C++实现多线程串口读写管理的Android库

Primary LanguageC++

LSerialPort 一个C++实现多线程管理读写串口Android库

该库采用C++实现异步线程读写串口。上层使用无需过多关心异步线程读写问题。

12e2d4e30b495362d7ffaf3a1b63719



arm64-v8a、armeabi-v7a

接口说明

函数名称 说明 返回值 备注
LSerialPort.openSerialPort 开启串口 0:成功
1:失败
对串口读写前必须先使用该函数打开串口,
LSerialPort.sendMsg 发送二进制数据 0:成功
1:失败
异步发送消息给串口。底层使用queue+mutex维护实现一个线程安全消息队列。上层可在任意线程内调用该函数。
LSerialPort.setOnLSerialPortListener 设置串口接收监听器 0:成功
1:失败
当接收到串口数据时会调用该回调

注意!!!该回调线程为底层读线程,不推荐在该回调线程做任何耗时操作,以免影响线程消息读取。
LSerialPort.closeSerialPort 关闭串口 0:成功
1:失败
停止底层线程并关闭串口,该函数会阻塞调用线程直到资源释放完毕。
LSerialPort.hasOpen 检查串口是否打开 true:已打开false:未打开

Native接口说明

以下是jni函数,不推荐直接使用。

函数名称 说明 返回值
LSerialPort.native_openSerialPort 开启串口 0:成功
1:失败
LSerialPort.native_sendMsg 发送二进制数据 0:成功
1:失败
LSerialPort.native_setLSerialPortDataListener 设置串口接收监听器
LSerialPort.native_closeSerialPort 关闭串口 0:成功
1:失败
LSerialPort.native_hasOpen 检查串口是否打开 true:成功
false:失败

引入AAR

  1. 下载 AAR包放入工程内libs目录下

d1bd64379531d24b56db04b24a30bc9

  1. 在build.gradle中dependencies内添加引用声明
implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])

12fde479a9d566889521909bd8f4d10


开始使用(Kotlin)参考代码

打开串口

//打开串口/dev/ttysWK0,波特率为9600
val result = LSerialPort.openSerialPort(path = "/dev/ttysWK0",baudrate =  BaudRate.B_9600)

//当然也可以分别定制数据位,校验位,停止位。
//默认数据位:8,校验位:无,停止位:1
val result = LSerialPort.openSerialPort(
    path = "/dev/ttysWK0", //串口地址
    baudrate = BaudRate.B_9600,//波特率
    dataBits = DataBits.EIGHT,//数据位
    parity = Parity.NONE,//校验位
    stopBits = StopBits.ONE//停止位
)


//如果需要一次返回尽量多一些数据。可以设置checkIntervalWaitMills参数。
//该函数是线程循环检查串口是否有数据并通知回传的等待时间,设置等待时间越长,数据返回量越大,当然数据回调的间隔也会越久,酌情配置。
//默认等待时间:0 单位:毫秒
val result = LSerialPort.openSerialPort(
    path = "/dev/ttysWK0", 
    baudrate = BaudRate.B_9600,
    checkIntervalWaitMills = 100//设置等待时间100ms 
)

发送一条数据

//打开串口后发送数据
val msg = byteArrayOf(0xFF.toByte(),0x01.toByte(),0x02.toByte(),0x03.toByte(),0x04.toByte(),0x05.toByte(),0xFE.toByte())
val result = LSerialPort.sendMsg("/dev/ttysWK0",msg)

//可以在子线程内发送数据,发送线程以及队列由C++部分维护,无需关心线程同步问题
Thread{
    val msg = byteArrayOf(0xFF.toByte(),0x01.toByte(),0x02.toByte(),0x03.toByte(),0xFE.toByte())
    val result = LSerialPort.sendMsg("/dev/ttysWK0",msg)
}.start()

设置监听器

//打开串口后设置监听器 返回数据为byteArray
//注意! 如果进行多次设置,每次会覆盖掉前一个监听器。
val result = LSerialPort.setOnLSerialPortListener("/dev/ttysWK0") { msg ->
  Log.d("LSerialPort","接收到数据长度:${msg.size}")
}

关闭串口

//调用时该函数会阻塞等待C++线程退出,需要一定耗时
val result = LSerialPort.closeSerialPort("/dev/ttysWK0")

判断串口是否已经打开

//使用该函数判断串口是否已经打开
val isOpened:Boolean = LSerialPort.hasOpen("/dev/ttysWK0")
Log.d("LSerialPort","串口/dev/ttysWK0是否已打开:$isOpened}")

工程开发环境信息

NDK :23.1.7779620
C++ :17
Android Gradle Plugin :7.4.1
Gradle :7.5
Android Studio :Android Studio Electric Eel | 2022.1.1 Patch 1

编译工程生成AAR

  1. 导入工程配置后选择Android Studio 中的build -> Refresh Linked C++ Projects 等待Gradle build完成。

63f048d3450c8e4f3a9e3f12ffdf325

  1. Gradle build完成后选择Rebuild Project 等待Gradle build完成。

323f9c45804d06a432885f14fbbfb9c

  1. 完成后在LSerialPort/build/outputs/aar/目录下会看到LSerialPort-debug.aar文件

f8eb60ecd662347259874a816ec7a11



可能会遇到的问题

1. 调用函数是返回-1失败,我该如何查看是什么问题

返回错误码-1时,logcat中会打印错误信息,可以通过Error等级寻找"LSerialPortLog"关键字查看。

package:mine level:error LSerialPortLog

2a4f6285874dfb001cb817e97924afc

2. 新版Studio怎么下载某个版本的NDK or 新版的SDK Manager找不到想要的NDK版本

首先,打开Studio的Tools -> SDK Manage

a9b279b844028fea09238cca48c5a37

然后,打开页面后先选择 SDK Tools选项

d1fc5f8b0dfeb715c1390a92b00ba4c

最后,勾选Show Package Details选项即可下载想要版本的NDK

2f577bfdcdda4fd5b73c2d3eb7f6613

3. 想要打包运行其他架构的包。如x86平台

打开LSerialPort库的Modele Gradle,在NDK内填写需要的架构,如下图内的x86

07c2b43ad86f7ff01ed7dcfbeecd138

然后删除app以及LSerialPort中的build目录

cbf363f3ca6e83f9805f5d05fa1b52a

最后重新编译生成AAR即可

cbf1a9173a7a205ca019501facc90ae

4. 想要打包release版本的AAR

打开studio的Build Variants。app与LSerialPort都改为release后,创建release需要的签名文件并重新打包
da0c9bb910a2cb306095cfdd40447ee

5. 为什么有时候Refresh Linked C++ Projects以及Rebuild Project后没有AAR文件

先把app以及LSerialPort目录下的build删除,然后重新Refresh Linked C++ Projects以及Rebuild Project,如果还是没有生成多rebuild几次
cbf363f3ca6e83f9805f5d05fa1b52a

结语

最近学了点C++,总感觉需要上手撸点东西。正好公司项目有使用串口的场景,但是发现大多数串口库一般都需要使用者自己在上层实现对串口多线程管理读写,有些不方便。在网上发现个优秀的C++底层实现多线程串口读写管理的库[MserialPort] https://github.com/flykule/MserialPort) ,遂按照该库的代码思路写了LSerialPort。一方面巩固刚学习学习的C++知识,另一方面是重拾很久没用过的JNI、NDK。

特别感谢