首页 > 其他分享 >Android 串口开发记录

Android 串口开发记录

时间:2023-07-18 22:38:21浏览次数:47  
标签:java 记录 串口 new import Android public android

一、导入

在我们的认识中Android系统是手机系统,它的物理接口一般只有usb host接口(之前的手机还有耳机接口,不过近几年取消了)。但其实安卓支持各种各样的工业接口,如HDMI、usb、网口、串口等等。本文将来说一下在安卓下开发串口通信。

二、串口是什么?

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口 (Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。

三.安卓串口Demo开发

这里使用的是谷歌开源的一个项目https://github.com/cepr/android-serialport-api 我这里以AS项目为例做一个简单的demo。

1、下载该项目,找到android-serialport-api文件夹中project文件夹里的libs文件夹。我们后面需要使用这个libs文件夹里的so库。 如图所示:

Android 串口开发记录_android

2、创建AS工程,在mian目录下创建jniLibs目录,并将刚刚找到的libs文件夹中的三个文件拷贝进去。

Android 串口开发记录_串口_02

3、在app下的build.gradle添加如下配置,用以适配不同架构。

 ndk {
            //选择要添加的对应cpu类型的.so库。
            abiFilters 'armeabi', 'armeabi-v7a', 'x86'
        }

上面我们添加了三中类型的libserial_port.so库,所以build.gradle添加配置也是这三个。

4、创建包名:android_serialport_api,并把SerialPort.java拷贝进去,SerialPort.java已经封装了串口的打开、配置、读写和关闭串口的方法,我们直接调用即可。

以下是串口操作类SerialPort.java

package android_serialport_api;

import android.util.Log;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class SerialPort {

	private static final String TAG = "SerialPort";
	public FileDescriptor mFd;
	private FileInputStream mFileInputStream;
	private FileOutputStream mFileOutputStream;

	public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {

		mFd = open(device.getAbsolutePath(), baudrate, flags);

		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(调用java本地接口,实现串口的打开和关闭)
	private native static FileDescriptor open(String path, int baudrate, int flags);
	public native void close();

	static {//加载jni下的C文件库
		Log.d(TAG, "本地库加载中");
		System.loadLibrary("serial_port");
	}
}

以及使用串口的工具类SerialPortUtils.java

package android_serialport_api;

import android.util.Log;
import android.widget.Toast;


import com.example.androidserialport.DIYToast;
import com.example.androidserialport.Data;
import com.example.androidserialport.MainActivity;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;

/**
 * 串口监听工具
 * Created by sam on 2018/3/31.
 */
public class SerialPortUtils {

    private final String TAG = "SerialPortUtils";
    private String path = "/dev/ttyS1";//串口名,安卓下一般为ttys1,ttys2... 而windows下则为com1,com2,com3... 我们平时用的usb口其实也是一个串口,所以用usb转232转换器就可以在电脑上使用串口了
    private int baudrate = 9600;//波特率
    public boolean serialPortStatus = false; //是否打开串口标志
    public String data_;
    public boolean threadStatus; //线程状态,为了安全终止线程

    public SerialPort serialPort = null;
    public InputStream inputStream = null;
    public OutputStream outputStream = null;


    /**
     * 打开串口
     * @return serialPort串口对象
     */
    public SerialPort openSerialPort(){
        try {
            serialPort = new SerialPort(new File(path),baudrate,0);
            this.serialPortStatus = true;
            threadStatus = false; //线程状态

            //获取打开的串口中的输入输出流,以便于串口数据的收发
            inputStream = serialPort.getInputStream();
            outputStream = serialPort.getOutputStream();

            new ReadThread().start(); //开始线程监控是否有数据要接收
        } catch (Exception e) {
            e.printStackTrace();
            Log.e(TAG, "openSerialPort: 打开串口异常:" + e.toString());
            DIYToast.ShowToast(MainActivity.context,"openSerialPort: 打开串口异常:" + e.toString());
            return serialPort;
        }
        Log.d(TAG, "openSerialPort: 打开串口");
        DIYToast.ShowToast(MainActivity.context,"openSerialPort: 打开串口");
        return serialPort;
    }

    /**
     * 关闭串口
     */
    public void closeSerialPort(){
        try {
            inputStream.close();
            outputStream.close();

            this.serialPortStatus = false;
            this.threadStatus = true; //线程状态
            serialPort.close();
        } catch (Exception e) {
            Log.e(TAG, "closeSerialPort: 关闭串口异常:"+e.toString());
            DIYToast.ShowToast(MainActivity.context,"closeSerialPort: 关闭串口异常:"+e.toString());
            return;
        }
        Log.d(TAG, "closeSerialPort: 关闭串口成功");
        DIYToast.ShowToast(MainActivity.context,"closeSerialPort: 关闭串口成功");
    }

    /**
     * 发送串口指令
     * @param data
     */
    public void sendSerialPort(byte[] data){
        //Log.d(TAG, "sendSerialPort: 发送数据");

        try {
            if (data.length > 0) {
                outputStream.write(data);

                //注意:有些工控机上需要以\n或者\r来表示数据发送完毕,所以在write完毕后要多输出个\r,这个看个人情况
                //outputStream.write('\n');
                //outputStream.write('\r'+'\n');

                outputStream.flush();
                //Log.d(TAG, "sendSerialPort: 串口数据发送成功");
            }
        } catch (IOException e) {
            Log.e(TAG, "sendSerialPort: 串口数据发送失败:"+e.toString());
        }

    }

    /**
     * 因为这里是阻塞的,所以单开一线程来读数据
     */
    private class ReadThread extends Thread {
        @Override
        public void run() {
            super.run();
            //判断进程是否在运行,更安全的结束进程
            while (!threadStatus){
                //Log.d(TAG, "监听打卡...");
                //64   1024
                byte[] buffer = new byte[16];
                int size; //读取数据的大小
                try {
                    size = inputStream.read(buffer);
                    if (size > 0){
                        Log.d(TAG, "接收到数据:" + new String(buffer));
                        Data.test=String.valueOf(new String(buffer));
                        //注意,多数情况下使用串口的单片机都是使用十六井控进行输送,所以这里可能还需要把buffer转换为十六进制
                    }
                } catch (IOException e) {
                    Log.e(TAG, "run: 数据读取异常:" +e.toString());
                }
            }

        }
    }

    /**
     * Utility class to convert a byte array to a hexadecimal string.
     *
     * @param bytes Bytes to convert
     * @return String, containing hexadecimal representation.
     */
    public static String ByteArrayToHexString(byte[] bytes) {
        final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        char[] hexChars = new char[bytes.length * 2];
        int v;
        for ( int j = 0; j < bytes.length; j++ ) {
            v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    /**
     * 将byte[]转为各种进制的字符串
     * @param bytes byte[]
     * @param radix 基数可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制
     * @return 转换后的字符串
     */
    public static String binary(byte[] bytes, int radix){
        return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数
    }

}

我将通过串口的工具类SerialPortUtils来实现安卓串口通信具体功能。以下是我的主界面代码以及布局。

主界面代码

package com.example.androidserialport;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import android_serialport_api.SerialPortUtils;

public class MainActivity extends AppCompatActivity {
    private Button btn_open,btn_close,btn_send;
    private TextView tv1;
    private SerialPortUtils S;
    public static Context context;
    private EditText et_number;
    private Handler handler = new Handler();
    private Runnable runnable;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context=this;
        btn_open = findViewById(R.id.btn_open);
        btn_close = findViewById(R.id.btn_close);
        btn_send = findViewById(R.id.btn_send);
        tv1 = findViewById(R.id.textView);
        et_number = findViewById(R.id.et_number);
        S = new SerialPortUtils();
        //打开串口
        btn_open.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                S.openSerialPort();

            }
        });
        //关闭串口
        btn_close.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                S.closeSerialPort();
            }
        });
        //发送数据
        btn_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int number = Integer.parseInt(et_number.getText().toString());
                byte[] bytes = {(byte) number};
                S.sendSerialPort(bytes);
            }
        });
        //接收数据
        runnable = new Runnable() {
            @Override
            public void run() {
                tv1.setText(Data.test);
                handler.postDelayed(runnable,1000);
            }
        };
        handler.post(runnable);
    }
}

简单的布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_open"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="打开串口"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.179"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.704" />

    <Button
        android:id="@+id/btn_close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="关闭串口"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.803"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.704" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:textSize="55dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btn_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发送"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.367"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.23" />

    <EditText
        android:id="@+id/et_number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="text"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.529"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.232" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="接收数据"
        android:textSize="50dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.389" />

</androidx.constraintlayout.widget.ConstraintLayout>

到这里基本就结束了,成功实现了安卓下的串口通信。贴张图吧。

Android 串口开发记录_java_03

这里使用的是虚拟串口,具体方法将在下一篇文章中讲解。

最后直接附上所需so库。

so库下载

标签:java,记录,串口,new,import,Android,public,android
From: https://blog.51cto.com/u_16192235/6768581

相关文章

  • Android平台GB28181设备接入端语音广播技术探究和填坑指南
    技术背景GB/T28181-2016官方规范和交互流程,我们不再赘述。SIP服务器发起广播流程示意图如下:需要注意的是:语音广播通知、语音广播应答命令消息头Content-type字段为Content-type:Application/MANSCDP+xml。语音广播通知、语音广播应答命令采用MANSCDP协议格式定义。消息示例如下:......
  • 记录--卸下if-else 侠的皮衣!- 策略模式
    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助......
  • 倒数日 版本更新记录
    点击下载最新版本WindDeskv1.1.62023/07/18增加模板创建事件自定义创建事件增加是否重复选项,e.g.每天、每月等增加编辑菜单选项,实现修改事件功能修改主界面面板字体以及样式修复已知bugWindDeskv1.0.62023/07/15主面板目标日修改为节假日,e.g.元旦、春节、国庆节......
  • Android平台GB28181设备接入侧音频采集推送示例
    技术背景GB/T28181是广泛应用于视频监控行业的标准协议规范,可以在不同设备之间实现互联互通。今天我们主要探讨Android平台的Audio采集部分。先说如何拿到数据源,在Android平台上采集音频,常用的方式如下:使用MediaRecorder类:MediaRecorder类提供了一组API,可以用于录制音频。您可以使......
  • Android使用Dagger注入的方式初始化对象的简单使用
    一.Dagger简介Dagger2是Google开源的一款依靠注入结构,它的前身是square的Dagger1,Dagger2在Android中有着较为广泛的运用。Dagger2根据Java注解,采用annotationProcessor(注解处理器)在项目编译时动态生成依靠注入需求的Java代码,然后咱们在合适的位置手动完结......
  • Android之adb安装busybox使用wget、telnet等服务
    二、通过busybox安装使用wgetbusyboxwget1也可以直接输入wget,不用加busybox了三、通过busybox使用telnet服务(1)进入root权限su1(2)每次开启adbshell后都需要设置环境变量才能重启busybox服务(没有安装busybox可以看DHCPv6之GitHub项目Android侧验证)exportPATH=/data/busybox:......
  • python日志记录代码
    Python日志记录的实现步骤为了帮助这位刚入行的小白实现Python日志记录代码,我们将按照以下步骤进行操作。这些步骤将指导他从头开始创建一个基本的日志记录系统。我们首先给出这些步骤的概述,然后逐一介绍每一个步骤的细节以及相应的代码。步骤概述步骤描述1导入loggin......
  • 新手学c#常用到的语法记录
    ​C#是一种面向对象的编程语言。在面向对象的程序设计方法中,程序由各种相互交互的对象组成。相同种类的对象通常具有相同的类型,或者说,是在相同的class中。例如,以Rectangle(矩形)对象为例。它具有length和width属性。根据设计,它可能需要接受这些属性值、计算面积和显示细节......
  • Hadoop的hdfs云服务器配置踩坑记录
    本章更多的是通过hdfs的API接口问题角度记录坑点坑点记录一、能够远程访问和通过web端访问hdfs在java代码中添加或更改如下:Configurationconf=newConfiguration();conf.set("dfs.client.use.datanode.hostname","true");//添加此配置信息即可FileSystemfs=FileSys......
  • 《python从入门到实践》第二章习题记录
    '''动手试一试请尝试编写一些简短的程序来完成下面的练习,以获得一些使用Python列表的第一手经验。你可能需要为每章的练习创建一个文件夹,以整洁有序的方式存储为完成各章的练习而编写的程序。'''#3-1姓名:将一些朋友的姓名存储在一个列表中,并将其命名为names。依次访问该列表......