Android串口通讯SerialPort的使用详情

目录
  • 1.什么是串口?
  • 2.添加依赖
  • 3.编写串口处理类
  • 4.使用串口
  • 5.总结

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;//串口读取任务

    /**
     * 添加串口回调
     *
     * @param serialInter
     */
    public void addSerialInter(SerialInter serialInter) {
        this.serialInter = serialInter;
    }

    /**
     * 打开串口
     *
     * @param devicePath 串口地址(根据平板的说明说填写)
     * @param baudrate   波特率(根据对接的硬件填写 - 硬件说明书上"通讯"中会有标注)
     * @param isRead     是否持续监听串口返回的数据
     * @return 是否打开成功
     */
    public boolean open(String devicePath, int baudrate, boolean isRead) {
        return open(devicePath, baudrate, 7, 1, 2, isRead);
    }

    /**
     * 打开串口
     *
     * @param devicePath 串口地址(根据平板的说明说填写)
     * @param baudrate   波特率(根据对接的硬件填写 - 硬件说明书上"通讯"中会有标注)
     * @param dataBits   数据位(根据对接的硬件填写 - 硬件说明书上"通讯"中会有标注)
     * @param stopBits   停止位(根据对接的硬件填写 - 硬件说明书上"通讯"中会有标注)
     * @param parity     校验位(根据对接的硬件填写 - 硬件说明书上"通讯"中会有标注)
     * @param isRead     是否持续监听串口返回的数据
     * @return 是否打开成功
     */
    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();
        }
    }

    /**
     * 把十六进制表示的字节数组字符串,转换成十六进制字节数组
     *
     * @param
     * @return byte[]
     */
    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;
    }

    /**
     * 把16进制字符[0123456789abcde](含大小写)转成字节
     * @param c
     * @return
     */
    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 {
    /**
     * 连接结果回调
     * @param path 串口地址(当有多个串口需要统一处理时,可以用地址来区分)
     * @param isSucc 连接是否成功
     */
    void connectMsg(String path,boolean isSucc);
    /**
     * 读取到的数据回调
     * @param path 串口地址(当有多个串口需要统一处理时,可以用地址来区分)
     * @param bytes 读取到的数据
     * @param size 数据长度
     */
    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;
    }

    /**
     * 获取线程池
     *
     * @return
     */
    public ScheduledExecutorService getScheduledExecutor() {
        return scheduledExecutor;
    }

    /**
     * 串口初始化
     *
     * @param serialInter
     */
    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);//设置地址,波特率,开启读取串口数据
    }

    /**
     * 发送指令
     *
     * @param msg
     */
    public void send(String msg) {
        /*
         此处没有直接使用 serialHandle.send(msg); 方法去发送指令
         因为 某些硬件在极短时间内只能响应一个指令,232通讯一次发送多个指令会有物理干扰,
         让硬件接收到指令不准确;所以 此处将指令添加到队列中,排队执行,确保每个指令一定执行.
         若不相信可以试试用serialHandle.send(msg)方法循环发送10个不同的指令,看看10个指令
         的执行结果。
         */
        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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android USB转串口通信开发实例详解

     Android USB转串口通信开发实例详解 好久没有写文章了,年前公司新开了一个项目,是和usb转串口通信相关的,需求是用安卓平板通过usb转接后与好几个外设进行通信,一直忙到最近,才慢慢闲下来,趁着这个周末不忙,记录下usb转串口通信开发的基本流程. 我们开发使用的是usb主机模式,即:安卓平板作为主机,usb外设作为从机进行数据通信.整个开发流程可以总结为以下几点: 1.发现设备 UsbManager usbManager = (UsbManager) context.getSystem

  • 详解Android USB转串口通信开发基本流程

    好久没有写文章了,年前公司新开了一个项目,是和usb转串口通信相关的,需求是用安卓平板通过usb转接后与好几个外设进行通信,一直忙到最近,才慢慢闲下来,趁着这个周末不忙,记录下usb转串口通信开发的基本流程. 我们开发使用的是usb主机模式,即:安卓平板作为主机,usb外设作为从机进行数据通信.整个开发流程可以总结为以下几点: 1.发现设备 UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERV

  • Android串口开发之使用JNI实现ANDROID和串口通信详解

    一:串口通信简介 前段时间因为工作需要研究了一下android的串口通信,网上有很多讲串口通信的文章,我在做的时候也参考了很多文章,现在就将我学习过程中的一些心得分享给大家,由于串口开发涉及到jni,所以开发环境需要支持ndk开发,如果未配置ndk配置的朋友,或者对jni不熟悉的朋友,请查看上一篇文章,android 串口开发第一篇:搭建ndk开发环境以及第一个jni调用程序 ,串口通信和java操作io类似,先打开串口,然后向串口发送或者读取数据,最后关闭串口,所以基本思路就是: 1.对串口文

  • android串口开发入门之搭建ndk开发环境及第一个jni调用程序

    前言 这几天专门研究了下JNI编程,在网上找了好多资料,不过好多都是以前的,没有更新,而且有的还是错误的,让人不得不吐槽一把.所以觉得自己来一篇,本文将详细介绍关于android搭建ndk开发环境及第一个jni调用程序的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 一:ndk环境搭建 1:开发环境 我使用的是android studio 2.3.3版本,搭建ndk开发环境比较简单,打开File----Settings----Appearance&Behavior--

  • 浅谈Android串口通讯SerialPort原理

    目录 前言 一.名词解释 二.SerialPort的函数分析 三.SerialPort打开串口的流程 四.疑惑 五.总结 前言 通过前面这篇文章Android串口通讯SerialPort的使用详情已经基本掌握了串口的使用,那么不经想问自己,到底什么才是串口通讯呢?串口通讯(Serial Communication),设备与设备之间,通过输入线(RXD),输出线(TXD),地线(GND),按位进行传输数据的一种通讯方式.CPU 和串行设备间的编码转换器(转换器(converter)是指将一种信号转

  • Android串口通信之串口读写实例

    在Android串口通信:基本知识梳理的基础上,我结合我项目中使用串口的实例,进行总结: Android使用jni直接进行串口设备的读写网上已经有开源项目了,本文是基于网上的开源项目在实际项目中的使用做的调整和优化: Google串口开源项目 下面是我项目中的相关代码及介绍: 1.SerialPort.cpp /* * Copyright 2009 Cedric Priscal * * Licensed under the Apache License, Version 2.0 (the "Li

  • Android串口通信apk源码详解(附完整源码)

    1.SerialPortHelper「Android串口通信」介绍 原项目地址 https://github.com/freyskill/SerialPortHelper Android串口通讯助手可以用于需要使用串口通信的Android外设,该库有如下特点: 1.串口通信部分使用C++实现,在笔者接触的部分设备上实测,使用C++实现与Google官方提供的Demo的方式要快: 2.支持且必须设置串口接收最大数据长度,初始化库时填入该参数,这样设置的原因是考虑在实际使用中,规定的串口通信协议格式

  • Android串口通信封装之OkUSB的示例代码

    本文介绍了Android串口通信封装之OkUSB的示例代码,分享给大家.具体如下: Github传送门:OkUSB OkUSB 一个简洁的Android串口通信框架. 功能简介 支持设置波特率 支持设置数据位 支持设置停止位 支持设置校验位 支持DTS和RTS 支持串口连接状态监听 用法简介 Gradle allprojects { repositories { ... maven { url 'https://jitpack.io' } } } dependencies { compile '

  • Android串口通讯SerialPort的使用详情

    目录 1.什么是串口? 2.添加依赖 3.编写串口处理类 4.使用串口 5.总结 1.什么是串口? 在不会使用串口通讯之前,暂且可以把它理解为“一个可通讯的口”:使用篇不深入探讨理论及原理.能理解串口如何使用之后,可以查看浅谈Android串口通讯SerialPort原理 2.添加依赖 1.)在 module 中的 build.gradle 中的 dependencies 中添加以下依赖: dependencies { //串口 implementation 'com.github.lichee

  • C#中的串口通信SerialPort详解

    今天这篇文章带大家学习下C#中的串口通讯.在日常的开发工作中,如果工作内容是CS方向的同学应该很容易接触到串口通讯方面的业务需求.那么也就很容易想到C#中SerialPort类,它就是专门来处理串口通讯相关的. 了解什么是串口通讯 串口通讯是指外设和计算机间通过数据信号线,地线,控制线等,按位进行传输数据的一种通讯方式. 这种通讯方式使用的数据线少,在远距离通信中可以节约成本,但其传输速度比并行通讯慢. 串口是计算机上一种非常通用的设备通讯协议.大多数计算机包含两个基于RS-232的串口. 串口

  • C#基于SerialPort类实现串口通讯详解

    本文实例为大家分享了C#基于SerialPort类实现串口通讯的具体代码,供大家参考,具体内容如下 最终效果 窗体设置: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; usi

  • Android实现蓝牙串口通讯

    本文实例为大家分享了Android实现蓝牙串口通讯的具体代码,供大家参考,具体内容如下 最近在弄蓝牙串口,参考了不少网上的大佬,加上自己早期对C#的学习,写一个给自己的备忘录,如果有大佬看到还请多多指教. 1.简介 Android设备中提供了一整套蓝牙的API,我这边只取了其中需要的部分. 初期权限 <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission andr

  • Android 串口通信编程及串口协议分析

    Android 串口通信编程:嵌入式编程和可穿戴设备及智能设备都会用到串口,这里就带大家分析下, 一,android串口通信 串口通信采用一个第三方开源项目,实现串口数据收发. 1. 使用了http://code.google.com/p/android-serialport-api/的项目的serialport api和jni: 2. 支持4串口同时收发,有定时自动发送功能,收发模式可选Txt或Hex模式: 3.  n,8,1,没得选: 4. 为减轻界面卡顿的情况,接收区的刷新采用单独的线程进

  • C#串口通讯概念及简单的实现方法

    前言 最近在研究串口通讯,其中有几个比较重要的概念,RS-232这种适配于上位机和PC端进行连接,RS-232只限于PC串口和设备间点对点的通信.它很简单的就可以进行连接,由于串口通讯是异步的,也就是说你可以同时向两端或者更多进行数据发送,它们之间的传输数据类型是byte,串口通信最重要的参数是波特率.数据位.停止位和奇偶校验.对于两个进行通信的端口,这些参数必须匹配. 听大佬说的几个关于串口通讯的术语,啥?啥,这是啥? 就让我这个"小白"给你说说:第一个波特率,这个东西在不同领域都有

随机推荐