我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

Google官方库android-serialport-api

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

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

修改SerialPort.h和SerialPort.c两个文件
/////////////////////////SerialPort.h/////////////////////////

JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open
  (JNIEnv *, jclass, jstring, jint, jint, jint, jint);
/////////////////////////SerialPort.c/////////////////////////

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;
    
    {
        speed = getBaudrate(baudrate);
        if (speed == -1) {
            
            LOGE("Invalid baudrate");
            return NULL;
        }
        if (parity  2) {
            LOGE("Invalid parity");
            return NULL;
        }
        if (dataBits  8) {
            LOGE("Invalid dataBits");
            return NULL;
        }
        if (stopBits  2) {
            LOGE("Invalid stopBit");
            return NULL;
        }
    }
	
	{
        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)
        {
            
            LOGE("Cannot open port");
            
            return NULL;
        }
	}
	
	{
        struct termios cfg;
        LOGD("Configuring serial port");
        if (tcgetattr(fd, &cfg))
        {
            LOGE("tcgetattr() failed");
            close(fd);
            
            return NULL;
        }
        cfmakeraw(&cfg);
        cfsetispeed(&cfg, speed);
        cfsetospeed(&cfg, speed);
        
        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);
            
            return NULL;
        }
	}
	
	{
		jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
		jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V");
		jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
		mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
		(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
	}
	return mFileDescriptor;
}
修改SerialPort.java文件,将原来的构造方法修改成下面的

    public SerialPort(File device, int baudrate, int parity, int dataBits, int stopBits) throws SecurityException, IOException {
        
        if (!device.canRead() || !device.canWrite()) {
            try {
                
                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这个选项的,但也无妨,无论是新建的项目还是已经现有的项目都可以按照下面的步骤完成配置。

检查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路径。
在这里插入图片描述

将整个工程目录切换至Project视图,默认是Android视图。找到class="lazy" data-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文件夹里。如果想要支持奇偶检验、数据位和停止位设定,按照我上面的代码修改就可以实现。
在这里插入图片描述

配置app/build.gradle
android {
    ...
    externalNativeBuild {
        ndkBuild {
            path 'class="lazy" data-src/main/jni/Android.mk'
        }
    }
}

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

public class SerialPort {
	private static final String TAG = "SerialPort";
	
	private FileDescriptor mFd;
	private FileInputStream mFileInputStream;
	private FileOutputStream mFileOutputStream;
    
    public SerialPort(File device, int baudrate, int parity, int dataBits, int stopBits) throws SecurityException, IOException {
        
        if (!device.canRead() || !device.canWrite()) {
            try {
                
                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 setters
	public InputStream getInputStream() {
		return mFileInputStream;
	}
	public OutputStream getOutputStream() {
		return mFileOutputStream;
	}
	// JNI
	private 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 {
        @Override
        public 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) {
            @Override
            protected void onDataReceived(byte[] buffer, int size) {
                 //todo 业务处理,解析接收的数据
            }
};

打开串口

mSerialPortHelper.open();

发送数据

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

关闭串口

mSerialPortHelper.close();

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


作者:Steven Jon


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

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

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

Android串口使用3之使用CMake工具完成android-serialport-api库的移植

君问归期未有期,巴山夜雨涨秋池。 对于Android串口的使用,基本已经被写烂了,网上一搜一大堆教程,还有很多大佬也已经封装成库了,可以在项目中直接添加依赖进行使用。用别人造的轮子不好吗?非要自己动手再造轮子?这是在弄啥嘞? emm。。。。
2022-06-06

Android官推kotlin-first图片加载库Coil的使用方法

这篇文章给大家分享的是有关Android官推kotlin-first图片加载库Coil的使用方法的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Coil 是一个非常年轻的图片加载库,在 2020 年 10 月 22
2023-06-14

android开发教程之ubuntu使用adb连接小米2的步骤和adb调试方法

步骤:分两步 一、usb连接: 在Ubuntu挂载使用MTP设备步骤如下: 1.将MTP设备连接至PC机 2.如果是第一次使用MTP设备需要安装以下软件,否则可以跳过此步骤:代码如下:$ sudo apt-get install mtpfs
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录