Android 中的串口开发

一:背景

本文着重讲安卓下的串口。

由于开源的Android在各种智能设备上的使用越来越多,如车载系统等。在我们的认识中,Android OS的物理接口一般只有usb host接口和耳机接口,但其实安卓支持各种各样的工业接口,如HDMI、usb、网口、串口等等。

下图就是一块Android工业板,标圈的DB9(也叫RS232串口)就是串口中的一种形态。

 

二、什么是串口?

串行端口 ,即:SerialPort,简称串口,主要用于数据被逐位按顺序传送的通讯方式称为串口通讯(简单来讲就是按顺序一位一位地传输数据)。

三、串口的一般形态

串口一般有RS232和RS485之分,485串口可以使用RS-232RS-485串口的转换器转换。

RS232:

232协议的串口是全双工 的,它允许数据同时接收和发送,但RS232的理论传输距离只有10米。

RS-485:

485是半双工的,半双工意味着同一时间只能收/发,就像是独木桥,同时只能有一个方向的人流通过,如果对向有来人则会造成数据丢失,RS485的理论距离是1200峭,通常如果要远距离使用的话会使用485串口,短距离则可以使用232。

四、串口的使用

无论是Android、windows还是Linux,串口的使用都要以下几步:

  1. 打开串口

  2. 串口配置(一般为:波特率、数据位、停止位和奇偶校验

  3. 串口操作(读/写,无非就是输入输出流的操作罢了

  4. 关闭串口

五、代码实践

package com.xz.andfasterserialport;

import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import tp.xmaihh.serialport.SerialHelper;
import tp.xmaihh.serialport.bean.ComBean;
import tp.xmaihh.serialport.stick.AbsStickPackageHelper;
import tp.xmaihh.serialport.utils.ByteUtil;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{


    private Button leftReduce;
    private Button leftAdd;
    private Button buyWine;
    private TextView wine_number;
    private String yellowWineType = "01";

    @BindView(R.id.rg_type)
    RadioGroup mRgType;

    private SerialHelper serialHelper;
    private boolean isHexType = false;
    private String text = "";

    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            ComBean comBean = (ComBean) msg.obj;
            String time = comBean.sRecTime;
            String rxText;
            rxText = new String(comBean.bRec);
            if (isHexType) {
                //转成十六进制数据
                rxText = ByteUtil.ByteArrToHex(comBean.bRec);
            }
            text += "Rx-> " + time + ": " + rxText + "\r" + "\n";
            //mEtReadContent.setText(text);
            return false;
        }
    });
    private RadioButton radioButton1;
    private RadioButton radioButton2;
    private RadioButton radioButton3;
    private RadioButton radioButton4;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);


        initSerialConfig();


        //下面的两行设置全屏,隐藏系统状态栏
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

        leftReduce = findViewById(R.id.left_reduce);
        leftAdd = findViewById(R.id.left_add);
        buyWine = findViewById(R.id.buy_wine);
        wine_number = findViewById(R.id.wine_number);
        radioButton1 = findViewById(R.id.rb_mk);
        radioButton2 = findViewById(R.id.rb_ls);
        radioButton3 = findViewById(R.id.rb_lx);
        radioButton4 = findViewById(R.id.rb_fzz);

        leftReduce.setOnClickListener(this);
        leftAdd.setOnClickListener(this);
        buyWine.setOnClickListener(this);
        radioButton1.setOnClickListener(this);
        radioButton2.setOnClickListener(this);
        radioButton3.setOnClickListener(this);
        radioButton4.setOnClickListener(this);

    }

    private void initSerialConfig() {
        //初始化SerialHelper对象,设定串口名称和波特率
        serialHelper = new SerialHelper(Const.SPORT_NAME, Const.BAUD_RATE) {
            @Override
            protected void onDataReceived(ComBean paramComBean) {
                Message message = mHandler.obtainMessage();
                message.obj = paramComBean;
                mHandler.sendMessage(message);
            }
        };

        /*
         * 默认的BaseStickPackageHelper将接收的数据扩展成64位,一般用不到这么多位
         * 我这里重新设定一个自适应数据位数的
         */

        serialHelper.setStickPackageHelper(new AbsStickPackageHelper() {
            @Override
            public byte[] execute(InputStream is) {
                try {
                    int available = is.available();
                    if (available > 0) {
                        byte[] buffer = new byte[available];
                        int size = is.read(buffer);
                        if (size > 0) {
                            return buffer;
                        }
                    } else {
                        SystemClock.sleep(50);
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    //设置数据格式为十六进制还是其他,我们希望isHexType = true

    @Override
    protected void onDestroy() {
        super.onDestroy();
        serialHelper.close();
        serialHelper = null;
    }


    //打开串口方法
    public void openSerialPort() {
        if (serialHelper.isOpen()) {
            Toast.makeText(this, Const.SPORT_NAME + "串口已经打开", Toast.LENGTH_SHORT).show();
            return;
        }
        try {
            serialHelper.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
        Toast.makeText(this, Const.SPORT_NAME + "串口打开成功", Toast.LENGTH_SHORT).show();

    }


    //关闭串口方法
    public void closeSerialPort() {
        if (serialHelper.isOpen()) {
            serialHelper.close();
            Toast.makeText(this, Const.SPORT_NAME + "串口已经关闭", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.left_reduce:
                String stringValue1 = wine_number.getText().toString();
                //Log.d("XTQXTQXTQ",stringValue1);
                int value1 = Integer.parseInt(stringValue1);
                //Log.d("XTQXTQXTQ",value1+"");
                if ((value1 > 50)) {
                    int newValue1 = value1 - 10;
                    wine_number.setText(newValue1 + "");
                }
                break;
            case R.id.left_add:
                String stringValue2 = wine_number.getText().toString();
                //Log.d("XTQXTQXTQ",stringValue2);
                int value2 = Integer.parseInt(stringValue2);
                //Log.d("XTQXTQXTQ",value2+"");
                if ((value2 < 150)) {
                    int newValue2 = value2 + 10;
                    wine_number.setText(newValue2 + "");
                }
                break;

            case R.id.buy_wine:
                Toast.makeText(this,"开始发送串口数据",Toast.LENGTH_LONG).show();
                openSerialPort();


                if (!serialHelper.isOpen()) {
                    Toast.makeText(this, Const.SPORT_NAME + "串口没打开 发送失败", Toast.LENGTH_SHORT).show();
                    return;
                }
                int sendContentInt = Integer.parseInt(wine_number.getText().toString());
                //Log.d("100转十六进制" ,String.format("%02x", 100));
                String sendContent = "FB01"+yellowWineType+"00"+String.format("%x", sendContentInt)+"00"+"00"+"FE";

                Log.d("100转十六进制内容" ,sendContent);
                isHexType = true;
                //String sendContent = "FB010200960000FE";
                if (isHexType) {
                    serialHelper.sendHex(sendContent);
                } else {
                    serialHelper.sendTxt(sendContent);
                }
            case R.id.rb_mk:
                yellowWineType = "01";
                break;
            case R.id.rb_ls:
                yellowWineType = "02";
                break;
            case R.id.rb_lx:
                yellowWineType = "03";
                break;
            case R.id.rb_fzz:
                yellowWineType = "04";
                break;
            default:
                break;

        }
    }

    //十六进制校验位checkSum
}
package com.xz.andfasterserialport;


public class Const {

    //串口 波特率
    public static final String SPORT_NAME = "/dev/ttyS1";
    public static final int BAUD_RATE = 9600;

    public static final String TXT_TYPE_SEND = "hello";
    public static final String HEX_TYPE_SEND = "123ABC";
}

主要思路:

添加依赖

首先,你需要在你的Android项目中添加依赖。确保项目build.gradle(Module级别)文件中有以下配置:

allprojects {
    repositories {
        maven { url 'https://jitpack.io' } // 或者使用作者推荐的仓库地址
    }
}
 
dependencies {
    implementation 'com.github.xiaozhuang799:AndFasterSerialPort:latest.version'
}

初始化并使用串口

接下来,在你的Activity或Service中,初始化SerialHelper对象,并配置必要的串口参数:

import com.example.andfasterserialport.SerialHelper;
 
// 假设已经定义了串口名和波特率
final String SERIAL_PORT_NAME = "/dev/ttyS0"; // 根据实际情况更改
final int BAUD_RATE = 9600; // 设置波特率
 
SerialHelper serialHelper = new SerialHelper(SERIAL_PORT_NAME, BAUD_RATE) {
    @Override
    protected void onDataReceived(ComBean paramComBean) {
        // 处理接收到的数据
        byte[] data = paramComBean.getData();
        // 你的逻辑代码
    }
};
 
// 打开串口
serialHelper.open();
 
// 发送数据示例
byte[] sendBuffer = "Hello, Serial Port!".getBytes();
serialHelper.send(sendBuffer);
 
// 不要忘记在适当的时候关闭串口
serialHelper.close();

 确保在使用串口前,已添加必要的权限到AndroidManifest.xml:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 对于Android 6.0及以上,还需要在运行时请求这些权限 -->

参考项目地址:
GitCode - 全球开发者的开源社区,开源代码托管平台

上一篇:零售升级新引擎!云里物里电子价签助力连锁便利店提质增效


下一篇:webpack面试笔记(一)