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

Android串口通讯SerialPort的使用详情

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

北京

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

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

看不清楚,换张图片

免费获取短信验证码

Android串口通讯SerialPort的使用详情

1.什么是串口?

在不会使用串口通讯之前,暂且可以把它理解为“一个可通讯的口”;使用篇不深入探讨理论及原理。能理解串口如何使用之后,可以查看浅谈Android串口通讯SerialPort原理

2.添加依赖

1.)在 module 中的 build.gradle 中的 dependencies 中添加以下依赖:

dependencies {
    //串口
    implementation 'com.github.licheedev:Android-SerialPort-API:2.0.0'
}

2.)低版本的 gradle 在Project 中的 build.gradle 中的 allprojects 中添加以下 maven仓库 (不添加任然无法加载SerialPort);

allprojects {
    repositories {
        maven { url "https://jitpack.io" }//maven仓库
    }
}

高版本的 gradle 已经废弃了 allprojects 在 settings.gradle 中 repositories 添加以下maven仓库(不添加任然无法加载SerialPort);

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        jcenter() // Warning: this repository is going to shut down soon
        maven { url "https://jitpack.io" }//maven仓库
    }
}

3.编写串口处理类

1.)串口处理类:SerialHandle ;简单概括这个类,就是通过串口对象去获取两个流(输入流、输出流),通过者两个流来监听数据或者写入指令,硬件收到后执行。同时注意配置参数(只要支持串口通讯的硬件,一般说明书上都会有写)

package com.chj233.serialmode.serialUtil;
 
import android.serialport.SerialPort;
import android.util.Log;
 
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

public class SerialHandle implements Runnable {
 
    private static final String TAG = "串口处理类";
    private String path = "";//串口地址
    private SerialPort mSerialPort;//串口对象
    private InputStream mInputStream;//串口的输入流对象
    private BufferedInputStream mBuffInputStream;//用于监听硬件返回的信息
    private OutputStream mOutputStream;//串口的输出流对象 用于发送指令
    private SerialInter serialInter;//串口回调接口
    private ScheduledFuture readTask;//串口读取任务
 
    
    public void addSerialInter(SerialInter serialInter) {
        this.serialInter = serialInter;
    }
 
    
    public boolean open(String devicePath, int baudrate, boolean isRead) {
        return open(devicePath, baudrate, 7, 1, 2, isRead);
    }
 
    
    public boolean open(String devicePath, int baudrate, int dataBits, int stopBits, int parity, boolean isRead) {
        boolean isSucc = false;
        try {
            if (mSerialPort != null) close();
            File device = new File(devicePath);
            mSerialPort = SerialPort // 串口对象
                    .newBuilder(device, baudrate) // 串口地址地址,波特率
                    .dataBits(dataBits) // 数据位,默认8;可选值为5~8
                    .stopBits(stopBits) // 停止位,默认1;1:1位停止位;2:2位停止位
                    .parity(parity) // 校验位;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN)
                    .build(); // 打开串口并返回
            mInputStream = mSerialPort.getInputStream();
            mBuffInputStream = new BufferedInputStream(mInputStream);
            mOutputStream = mSerialPort.getOutputStream();
            isSucc = true;
            path = devicePath;
            if (isRead) readData();//开启识别
        } catch (Throwable tr) {
            close();
            isSucc = false;
        } finally {
            return isSucc;
        }
    }
 
    // 读取数据
    private void readData() {
        if (readTask != null) {
            readTask.cancel(true);
            try {
                Thread.sleep(160);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //此处睡眠:当取消任务时 线程池已经执行任务,无法取消,所以等待线程池的任务执行完毕
            readTask = null;
        }
        readTask = SerialManage
                .getInstance()
                .getScheduledExecutor()//获取线程池
                .scheduleAtFixedRate(this, 0, 150, TimeUnit.MILLISECONDS);//执行一个循环任务
    }
 
    @Override//每隔 150 毫秒会触发一次run
    public void run() {
        if (Thread.currentThread().isInterrupted()) return;
        try {
            int available = mBuffInputStream.available();
            if (available == 0) return;
            byte[] received = new byte[1024];
            int size = mBuffInputStream.read(received);//读取以下串口是否有新的数据
            if (size > 0 && serialInter != null) serialInter.readData(path, received, size);
        } catch (IOException e) {
            Log.e(TAG, "串口读取数据异常:" + e.toString());
        }
    }
 
    
    public void close(){
        try{
            if (mInputStream != null) mInputStream.close();
        }catch (Exception e){
            Log.e(TAG,"串口输入流对象关闭异常:" +e.toString());
        }
        try{
            if (mOutputStream != null) mOutputStream.close();
        }catch (Exception e){
            Log.e(TAG,"串口输出流对象关闭异常:" +e.toString());
        }
        try{
            if (mSerialPort != null) mSerialPort.close();
            mSerialPort = null;
        }catch (Exception e){
            Log.e(TAG,"串口对象关闭异常:" +e.toString());
        }
    }
 
    
    public void send(final String msg) {
        byte[] bytes = hexStr2bytes(msg);//字符转成byte数组
        try {
            mOutputStream.write(bytes);//通过输出流写入数据
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    
    private byte[] hexStr2bytes(String hex) {
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] achar = hex.toUpperCase().toCharArray();
        for (int i = 0; i < len; i++) {
            int pos = i * 2;
            result[i] = (byte) (hexChar2byte(achar[pos]) << 4 | hexChar2byte(achar[pos + 1]));
        }
        return result;
    }
 
    
    private static int hexChar2byte(char c) {
        switch (c) {
            case '0':
                return 0;
            case '1':
                return 1;
            case '2':
                return 2;
            case '3':
                return 3;
            case '4':
                return 4;
            case '5':
                return 5;
            case '6':
                return 6;
            case '7':
                return 7;
            case '8':
                return 8;
            case '9':
                return 9;
            case 'a':
            case 'A':
                return 10;
            case 'b':
            case 'B':
                return 11;
            case 'c':
            case 'C':
                return 12;
            case 'd':
            case 'D':
                return 13;
            case 'e':
            case 'E':
                return 14;
            case 'f':
            case 'F':
                return 15;
            default:
                return -1;
        }
    }

2.)串口回调SerialInter;简单概括一下这个类,就是将SerialHandle类中产生的结果,返回给上一层的业务代码,解偶合

package com.chj233.serialmode.serialUtil;

public interface SerialInter {
    
    void connectMsg(String path,boolean isSucc);
    
    void readData(String path,byte[] bytes,int size);
 
}

 3.)串口统一管理SerialManage;简单概括一下这个类,用于管理串口的连接以及发送等功能,尤其是发送指令,极短时间内发送多个指令(例如:1毫秒内发送10个指令),多个指令之间会相互干扰。可能执行了第一个指令,可能一个都没执行。这个类不是必须的,如果有更好的方法可以自己定义。

package com.chj233.serialmode.serialUtil;
 
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
 

public class SerialManage {
 
    private static SerialManage instance;
    private ScheduledExecutorService scheduledExecutor;//线程池 同一管理保证只有一个
    private SerialHandle serialHandle;//串口连接 发送 读取处理对象
    private Queue<String> queueMsg = new ConcurrentLinkedQueue<String>();//线程安全到队列
    private ScheduledFuture sendStrTask;//循环发送任务
    private boolean isConnect = false;//串口是否连接
 
    private SerialManage() {
        scheduledExecutor = Executors.newScheduledThreadPool(8);//初始化8个线程
    }
 
    public static SerialManage getInstance() {
        if (instance == null) {
            synchronized (SerialManage.class) {
                if (instance == null) {
                    instance = new SerialManage();
                }
            }
        }
        return instance;
    }
 
    
    public ScheduledExecutorService getScheduledExecutor() {
        return scheduledExecutor;
    }
 
    
    public void init(SerialInter serialInter) {
        if (serialHandle == null) {
            serialHandle = new SerialHandle();
            startSendTask();
        }
        serialHandle.addSerialInter(serialInter);
 
    }
 
    
    public void open() {
        isConnect = serialHandle.open("/dev/ttyS1", 9600, true);//设置地址,波特率,开启读取串口数据
    }
 
    
    public void send(String msg) {
        
        queueMsg.offer(msg);//向队列添加指令
    }
 
    
    public void colse() {
        serialHandle.close();//关闭串口
    }
 
    //启动发送发送任务
    private void startSendTask() {
        cancelSendTask();//先检查是否已经启动了任务 ? 若有则取消
        //每隔100毫秒检查一次 队列中是否有新的指令需要执行
        sendStrTask = scheduledExecutor.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                if (!isConnect) return;//串口未连接 退出
                if (serialHandle == null) return;//串口未初始化 退出
                String msg = queueMsg.poll();//取出指令
                if (msg == null || "".equals(msg)) return;//无效指令 退出
                serialHandle.send(msg);//发送指令
            }
        }, 0, 100, TimeUnit.MILLISECONDS);
    }
    //取消发送任务
    private void cancelSendTask() {
        if (sendStrTask == null) return;
        sendStrTask.cancel(true);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        sendStrTask = null;
    }
}

4.使用串口

package com.chj233.serialmode;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.chj233.serialmode.serialUtil.SerialInter;
import com.chj233.serialmode.serialUtil.SerialManage;
public class MainActivity extends AppCompatActivity implements SerialInter {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        SerialManage.getInstance().init(this);//串口初始化
        SerialManage.getInstance().open();//打开串口
        findViewById(R.id.send_but).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SerialManage.getInstance().send("Z");//发送指令 Z 
            }
        });
    }
    @Override
    public void connectMsg(String path, boolean isSucc) {
        String msg = isSucc ? "成功" : "失败";
        Log.e("串口连接回调", "串口 "+ path + " -连接" + msg);
    }
    @Override//若在串口开启的方法中 传入false 此处不会返回数据
    public void readData(String path, byte[] bytes, int size) {
//        Log.e("串口数据回调","串口 "+ path + " -获取数据" + bytes);
    }
}

5.总结

串口通讯对于Android开发者来说,仅需关注如何连接、操作(发送指令)、读取数据;无论是232、485还是422,对于开发者来说连接、操作、读取代码都是一样的

到此这篇关于Android串口通讯SerialPort的使用详情的文章就介绍到这了,更多相关Android SerialPort内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

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

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

Android串口通讯SerialPort的使用详情

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

下载Word文档

猜你喜欢

怎么​使用Java实现串口SerialPort通讯

要使用Java实现串口通讯,你可以使用Java的RXTX库。下面是一个简单的示例代码:首先,你需要下载并安装RXTX库。你可以从以下网址下载合适的版本:http://rxtx.qbang.org/wiki/index.php/Downloa
2023-10-23

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中使用OkUSB封装一个串口通信

这篇文章给大家介绍怎么在Android中使用OkUSB封装一个串口通信,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。OkUSB一个简洁的Android串口通信框架。功能简介支持设置波特率支持设置数据位支持设置停止位支持
2023-05-30

Android使用ContentProvider实现跨进程通讯示例详解

这篇文章主要为大家介绍了Android使用ContentProvider实现跨进程通讯示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-08

.NET Core跨平台串口通讯使用SerialPortStream基础类库问题的解决方法

这篇文章跟大家分析一下“.NET Core跨平台串口通讯使用SerialPortStream基础类库问题的解决方法”。内容详细易懂,对“.NET Core跨平台串口通讯使用SerialPortStream基础类库问题的解决方法”感兴趣的朋友
2023-06-26

Android使用ContentResolver搜索手机通讯录的方法

本文实例讲述了Android使用ContentResolver搜索手机通讯录的方法。分享给大家供大家参考,具体如下: 在这个程序中使用ContentResolver来访问通讯录里联系人的关键字,并将所有找到的联系人存入CursorAdapt
2022-06-06

Ubuntu虚拟机下使用cutecom进行串口通信的方法

Ubuntu虚拟机下使用cutecom进行串口通信 1. 安装cutecom 使用以下命令安装cutecomsudo apt-get install cutecom调用以下命令打开cutecomsudo su cutecom其中,Devi
2022-06-04

实例讲解Android中的AIDL内部进程通信接口使用

首先描述下我们想要实现的内容,我们希望在一个应用中通过点击按钮,去操作另一个进程中应用的音乐播放功能。如图,我们点击“播放”时,系统就会去远程调用我们提供的一个service(与当前service不是同一个应用哦),然后操作service中
2022-06-06

Vue3父子通讯方式及Vue3插槽的使用方法详解

这篇文章主要介绍了Vue3父子通讯方式及Vue3插槽的使用方法详解,需要的朋友可以参考下
2023-01-28

编程热搜

  • 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第一次实验

目录