Android串口使用2之使用Google官方库android-serialport-api

2023-12-15 10:59

本文主要是介绍Android串口使用2之使用Google官方库android-serialport-api,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Google官方库android-serialport-api

GitHub:android-serialport-api
此库版本太旧了,而且还不是AS工程,也不支持奇偶检验、数据位和停止位设定。

想要这个库支持奇偶检验、数据位和停止位设定也很简单

  1. 修改SerialPort.h和SerialPort.c两个文件
/SerialPort.h/
/** Class:     android_serialport_api_SerialPort* Method:    open* Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;*/
JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open(JNIEnv *, jclass, jstring, jint, jint, jint, jint);
/SerialPort.c/
/** Class:     android_serialport_SerialPort* Method:    open* Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;*/
JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open(JNIEnv *env, jclass thiz, jstring path,  jint baudrate, jint parity, jint dataBits, jint stopBits){int fd;int flags;speed_t speed;jobject mFileDescriptor;flags = 0;/* Check arguments */{speed = getBaudrate(baudrate);if (speed == -1) {/* TODO: throw an exception */LOGE("Invalid baudrate");return NULL;}if (parity < 0 || parity > 2) {LOGE("Invalid parity");return NULL;}if (dataBits < 5 || dataBits > 8) {LOGE("Invalid dataBits");return NULL;}if (stopBits < 1 || stopBits > 2) {LOGE("Invalid stopBit");return NULL;}}/* Opening device */{jboolean iscopy;const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);fd = open(path_utf, O_RDWR | flags);LOGD("open() fd = %d", fd);(*env)->ReleaseStringUTFChars(env, path, path_utf);if (fd == -1){/* Throw an exception */LOGE("Cannot open port");/* TODO: throw an exception */return NULL;}}/* Configure device */{struct termios cfg;LOGD("Configuring serial port");if (tcgetattr(fd, &cfg)){LOGE("tcgetattr() failed");close(fd);/* TODO: throw an exception */return NULL;}cfmakeraw(&cfg);cfsetispeed(&cfg, speed);cfsetospeed(&cfg, speed);/* More attribute set */switch (parity) {case 0:cfg.c_cflag &= ~PARENB;    //无奇偶校验break;case 1:cfg.c_cflag |= (PARODD | PARENB);   //奇校验break;case 2:cfg.c_iflag &= ~(IGNPAR | PARMRK); // 偶校验cfg.c_iflag |= INPCK;cfg.c_cflag |= PARENB;cfg.c_cflag &= ~PARODD;break;default:cfg.c_cflag &= ~PARENB;break;}switch (dataBits) {case 5: cfg.c_cflag |= CS5; break;case 6: cfg.c_cflag |= CS6; break;case 7: cfg.c_cflag |= CS7; break;case 8: cfg.c_cflag |= CS8; break;default: cfg.c_cflag |= CS8; break;}switch (stopBits) {case 1: cfg.c_cflag &= ~CSTOPB; break;case 2: cfg.c_cflag |= CSTOPB; break;default:cfg.c_cflag &= ~CSTOPB; break;}if (tcsetattr(fd, TCSANOW, &cfg)){LOGE("tcsetattr() failed");close(fd);/* TODO: throw an exception */return NULL;}}/* Create a corresponding file descriptor */{jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);}return mFileDescriptor;
}
  1. 修改SerialPort.java文件,将原来的构造方法修改成下面的
/*** 串口配置*@param device 串口设备文件*@param baudrate 波特率*@param parity 奇偶校验,0 None, 1 Odd, 2 Even*@param dataBits 数据位,5 - 8*@param stopBits 停止位,1 或 2*/public SerialPort(File device, int baudrate, int parity, int dataBits, int stopBits) throws SecurityException, IOException {/* Check access permission */if (!device.canRead() || !device.canWrite()) {try {/* Missing read/write permission, trying to chmod the file */Process su;su = Runtime.getRuntime().exec("/system/bin/su");String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"+ "exit\n";su.getOutputStream().write(cmd.getBytes());if ((su.waitFor() != 0) || !device.canRead()|| !device.canWrite()) {throw new SecurityException();}} catch (Exception e) {e.printStackTrace();throw new SecurityException();}}mFd = open(device.getAbsolutePath(), baudrate, parity, dataBits, stopBits);if (mFd == null) {Log.e(TAG, "native open returns null");throw new IOException();}mFileInputStream = new FileInputStream(mFd);mFileOutputStream = new FileOutputStream(mFd);}

还有JNI的open方法

private native static FileDescriptor open(String path, int baudrate, int parity, int dataBits, int stopBits);

如何使用呢?

我用的是Android Studio 3.5,在新建项目的时候,是看不到Include C++ Support这个选项的,但也无妨,无论是新建的项目还是已经现有的项目都可以按照下面的步骤完成配置。

  1. 检查ndk配置
    在这里插入图片描述

NDK(Native Develop Kit),Android NDK 是一套允许您使用原生代码语言(例如C和C++) 实现部分应用的工具集。在开发某些类型应用时,这有助于您重复使用C/C++语言编写的代码库。

如果还没有下载Android NDK的可以借助Android Studio下载或者网上找资源下载,比如官方网站https://developer.android.google.cn/ndk/downloads/。哪种方式方便快捷,就可以用哪种。
Android Studio找到Settings,或者使用快捷键Ctrl + Alt + S。搜索Android SDK,找到SDK Tools,最下面就是NDK的版本信息,勾选上之后,点击Apply,最后点OK。
NDK下载完成,按照上面的步骤配置好NDK路径。
在这里插入图片描述

  1. 将整个工程目录切换至Project视图,默认是Android视图。找到src/main,右键main文件夹,选择New,找到Folder,选择JNI Folder
    在这里插入图片描述

JNI(Java Native Interface),即Java本地接口,JNI是Java调用Native 语言的一种特性。通过JNI可以使得Java与C/C++机型交互。即可以在Java代码中调用C/C++等语言的代码或者在C/C++代码中调用Java代码。

将官方库android-serialport-api下载下来,找到jni文件夹,将里面所有的文件复制到上面新建的jni文件夹里。如果想要支持奇偶检验、数据位和停止位设定,按照我上面的代码修改就可以实现。
在这里插入图片描述

  1. 配置app/build.gradle
android {...externalNativeBuild {ndkBuild {path 'src/main/jni/Android.mk'}}
}

4.最后一步,在src/main/java根目录下,新建一个文件夹android_serialport_api,名字千万不要改哦,因为这个名字链接着这个api库,改变之后,java代码无法调用它,会报错的。
新建一个SerialPort类或者将官方原来的SerialPort.java文件复制过去,如果有需要,也可以将SerialPortFinder.java这个文件复制过去,这个是查找串口驱动文件路径。

public class SerialPort {private static final String TAG = "SerialPort";/** Do not remove or rename the field mFd: it is used by native method close();*/private FileDescriptor mFd;private FileInputStream mFileInputStream;private FileOutputStream mFileOutputStream;/*** 串口配置*@param device 串口设备文件*@param baudrate 波特率*@param parity 奇偶校验,0 None, 1 Odd, 2 Even*@param dataBits 数据位,5 - 8*@param stopBits 停止位,1 或 2*/public SerialPort(File device, int baudrate, int parity, int dataBits, int stopBits) throws SecurityException, IOException {/* Check access permission */if (!device.canRead() || !device.canWrite()) {try {/* Missing read/write permission, trying to chmod the file */Process su;su = Runtime.getRuntime().exec("/system/bin/su");String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"+ "exit\n";su.getOutputStream().write(cmd.getBytes());if ((su.waitFor() != 0) || !device.canRead()|| !device.canWrite()) {throw new SecurityException();}} catch (Exception e) {e.printStackTrace();throw new SecurityException();}}mFd = open(device.getAbsolutePath(), baudrate, parity, dataBits, stopBits);if (mFd == null) {Log.e(TAG, "native open returns null");throw new IOException();}mFileInputStream = new FileInputStream(mFd);mFileOutputStream = new FileOutputStream(mFd);}// Getters and setterspublic InputStream getInputStream() {return mFileInputStream;}public OutputStream getOutputStream() {return mFileOutputStream;}// JNIprivate native static FileDescriptor open(String path, int baudrate, int parity, int dataBits, int stopBits);public native void close();static {System.loadLibrary("serial_port");}
}

到这里,Google官方库android-serialport-api基本配置完成了,剩下的就是写工具类进行调用了,实现打开串口、监听数据、发送数据和关闭串口了。

如果不知道如何下手的,我下面写了SerialPortHelper,可以参考参考下。

public abstract class SerialPortHelper
{private SerialPort mSerialPort;private OutputStream mOutputStream;private InputStream mInputStream;private ReadThread mReadThread;private boolean _isOpen = false;//串口配置private String sPort;private int iBaudRate;private int parity = 0; //默认无校验private int dataBits = 8;//默认数据位8位private int stopBits = 1;//默认停止位1位public SerialPortHelper(String sPort, int iBaudRate) {this.sPort = sPort;this.iBaudRate = iBaudRate;}public SerialPortHelper(String sPort, int iBaudRate, int parity, int dataBits, int stopBits) {this.sPort = sPort;this.iBaudRate = iBaudRate;this.parity = parity;this.dataBits = dataBits;this.stopBits = stopBits;}public void open() throws SecurityException, IOException {this.mSerialPort = new SerialPort(new File(sPort), iBaudRate, parity, dataBits, stopBits);this.mOutputStream = this.mSerialPort.getOutputStream();this.mInputStream = this.mSerialPort.getInputStream();this.mReadThread = new ReadThread();this.mReadThread.start();this._isOpen = true;}public void close() {if (this.mReadThread != null) {this.mReadThread.interrupt();}if (this.mSerialPort != null) {this.mSerialPort.close();this.mSerialPort = null;}this._isOpen = false;}public void send(byte[] bOutArray) {try {this.mOutputStream.write(bOutArray);} catch (IOException e) {e.printStackTrace();}}public void sendHex(String sHex) {byte[] bOutArray = ByteUtil.HexToByteArr(sHex);send(bOutArray);}public void sendTxt(String sTxt) {byte[] bOutArray = sTxt.getBytes();send(bOutArray);}private class ReadThread extends Thread {@Overridepublic void run() {super.run();while (!isInterrupted()) {try {if (SerialPortHelper.this.mInputStream == null) {return;}int available = SerialPortHelper.this.mInputStream.available();if (available > 0) {byte[] buffer = new byte[available];int size = SerialPortHelper.this.mInputStream.read(buffer);if (size > 0) {SerialPortHelper.this.onDataReceived(buffer, size);}} else {SystemClock.sleep(50);}} catch (Throwable e) {Log.e("error", e.getMessage());return;}}}}public boolean isOpen() {return this._isOpen;}protected abstract void onDataReceived(byte[] buffer, int size);}

初始化串口


private SerialPortHelper mSerialPortHelper;mSerialPortHelper = new SerialPortHelper("dev/ttyS4", 9600) {@Overrideprotected void onDataReceived(byte[] buffer, int size) {//todo 业务处理,解析接收的数据}
};

打开串口

mSerialPortHelper.open();

发送数据

mSerialPortHelper.send(byte[]/Hex String/Txt String);

关闭串口

mSerialPortHelper.close();

如果觉得上面的步骤比较繁琐,开发效率不高的话,可以试试别人造的轮子。
快速使用Android串口

这篇关于Android串口使用2之使用Google官方库android-serialport-api的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

ModelMapper基本使用和常见场景示例详解

《ModelMapper基本使用和常见场景示例详解》ModelMapper是Java对象映射库,支持自动映射、自定义规则、集合转换及高级配置(如匹配策略、转换器),可集成SpringBoot,减少样板... 目录1. 添加依赖2. 基本用法示例:简单对象映射3. 自定义映射规则4. 集合映射5. 高级配置匹

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是

嵌入式数据库SQLite 3配置使用讲解

《嵌入式数据库SQLite3配置使用讲解》本文强调嵌入式项目中SQLite3数据库的重要性,因其零配置、轻量级、跨平台及事务处理特性,可保障数据溯源与责任明确,详细讲解安装配置、基础语法及SQLit... 目录0、惨痛教训1、SQLite3环境配置(1)、下载安装SQLite库(2)、解压下载的文件(3)、

使用Python绘制3D堆叠条形图全解析

《使用Python绘制3D堆叠条形图全解析》在数据可视化的工具箱里,3D图表总能带来眼前一亮的效果,本文就来和大家聊聊如何使用Python实现绘制3D堆叠条形图,感兴趣的小伙伴可以了解下... 目录为什么选择 3D 堆叠条形图代码实现:从数据到 3D 世界的搭建核心代码逐行解析细节优化应用场景:3D 堆叠图

Springboot如何正确使用AOP问题

《Springboot如何正确使用AOP问题》:本文主要介绍Springboot如何正确使用AOP问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录​一、AOP概念二、切点表达式​execution表达式案例三、AOP通知四、springboot中使用AOP导出

Navicat数据表的数据添加,删除及使用sql完成数据的添加过程

《Navicat数据表的数据添加,删除及使用sql完成数据的添加过程》:本文主要介绍Navicat数据表的数据添加,删除及使用sql完成数据的添加过程,具有很好的参考价值,希望对大家有所帮助,如有... 目录Navicat数据表数据添加,删除及使用sql完成数据添加选中操作的表则出现如下界面,查看左下角从左

python 常见数学公式函数使用详解(最新推荐)

《python常见数学公式函数使用详解(最新推荐)》文章介绍了Python的数学计算工具,涵盖内置函数、math/cmath标准库及numpy/scipy/sympy第三方库,支持从基础算术到复杂数... 目录python 数学公式与函数大全1. 基本数学运算1.1 算术运算1.2 分数与小数2. 数学函数