安卓开发板之串口通信,通过modbus Rtu协议控制下位机

安卓开发板之串口通信,通过modbus Rtu协议控制下位机

1.环境准备

1).使用该类需要将两个文件夹拷贝到项目目录中,这两个文件夹分别是jni,和jniLibs(jni就是Java本地接口,使用它可以实现java语言与其他语言的交互)jni和jniLibs的压缩包
在这里插入图片描述
2)配置app文件夹下的bulid.gradle文件(配置好后重新编译一次)
在这里插入图片描述
3.配置好调整到安卓特有的目录结构会看到,这说明环境准备完成
在这里插入图片描述

2.编写串口操作核心类

该类的用来控制串口的打开和关闭,并获取到对应串口的输入输出流对象。

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";
    private FileDescriptor mFd;
    private FileInputStream mFileInputStream;
    private FileOutputStream mFileOutputStream;

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

        //检查访问权限,如果没有读写权限,进行文件操作,修改文件访问权限
        if (!device.canRead() || !device.canWrite()) {
            try {
                //通过挂在到linux的方式,修改文件的操作权限
                Process su = Runtime.getRuntime().exec("/system/xbin/su");
                String cmd = "chmod 777 " + 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, 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本地接口,实现串口的打开和关闭)
/**串口有五个重要的参数:串口设备名,波特率,检验位,数据位,停止位
 其中检验位一般默认位NONE,数据位一般默认为8,停止位默认为1*/
    /**
     * @param path     串口设备的据对路径
     * @param baudrate 波特率
     * @param flags    校验位
     */
    private native static FileDescriptor open(String path, int baudrate, int flags);

    public native void close();

    static {//加载jni下的C文件库
        System.loadLibrary("serial_port");
    }
}

3.编写测试类

(注意:红字代码只是本人在MyEclipse中调试所用,无需拷贝)
串口的打开和关闭很好实现,输入输出数据也很好实现,
关键的问题是输入的数据包下位机总是解析不出来,后来摸索了好久
终于发现问题是我发送的数据包的格式有问题。
1.首先我们要知道上位机发送给下位机的到底是什么格式的
(我就以控制从站号为1的从机的第一个线圈的开关命令为例)
开和关的16进制命令分别为:
开启第一个线圈:01 05 00 00 FF 00 8C 3A
关闭第一个线圈:01 05 00 00 00 00 CD CA
上面的命令是平常在主从机调试软件中输入的,但是下位机实际接收到的格式却并非如此)
2.首先我找了一个上位机(这里我用的是有人的网关,可以通过网页界面控制 通过rj485口
向下位机发送数据),配合usb转串口调试工具,以及串口调试软件 SSCOM32.EXE
我终于看到了这个命令的本来面目:“    ?”
what?这是什么?对,我也想问。还好SSCOM32.EXE可以选择以16进制格式显示选项
于是我就看了这个:01 05 00 00 FF 00 8C 3A。对没错,终于看到认识的了。
但是它内部是怎么实现的呢?
于是我用在网上找的字符串转16进制字符串方法
/** 
 * 字符串转换成十六进制字符串
 */  
public static String str2HexStr(String str) {  
    char[] chars = "0123456789ABCDEF".toCharArray();  
    StringBuilder sb = new StringBuilder("");
    byte[] bs = str.getBytes();  
    int bit;  
    for (int i = 0; i < bs.length; i++) {  
        bit = (bs[i] & 0x0f0) >> 4;  
        sb.append(chars[bit]);  
        bit = bs[i] & 0x0f;  
        sb.append(chars[bit]);  
    }  
    return sb.toString();  
} 

调用一下:
@Test
public void run1() {
// TODO Auto-generated method stub
   String string1=Hex2Bytes.str2HexStr("   ?");
   System.out.println(string1);
   }
于是看到了下面的结果

在这里插入图片描述

看起来挺像的不过中间的空格没有了
(为什么和去除空格之后的不完全一样,可能是代码问题,所以上面的代码“字符串
转换成十六进制字符串的”不建议拷贝)
顺着这条线我们继续往下走
于是我将命令:改成:01050000FF008C3A
当然你也可以用:"01 05 00 00 FF 00 8C 3A".trim();去除空格。
然后代码就变成了下面的样子(结果大功告成!!!)

测试类的真正面目(注意每个安卓开发板的串口名不一样,我的用的是百度人脸识别开发板485串口)
在这里插入图片描述

package com.serial_test;

import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

import android_serialport_api.SerialPort;

public class SerialTest {
    protected SerialPort mSerialPort;
    protected OutputStream mOutputStream;
    private static String prot = "ttysWK0";
    private static int baudrate = 9600;

    public void openDoor() {
        new Thread() {
            @Override
            public void run() {
                try {
                    mSerialPort=new SerialPort(new File("/dev/" + prot), baudrate,0);
                    mOutputStream = mSerialPort.getOutputStream();
                    //mInputStream = mSerialPort.getInputStream();
                    //mOutputStream = mSerialPort.getOutputStream();
                    byte [] bytes1=HexStrToBytes.hexToByteArray("01050000FF008C3A");
                    mOutputStream.write(bytes1,0,bytes1.length);
                    Thread.sleep(1000*2);
                    byte [] bytes2=HexStrToBytes.hexToByteArray("010500000000CDCA");
                    mOutputStream.write(bytes2,0,bytes2.length);
                    Log.i("test", "发送成功");
                } catch (SecurityException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    Log.i("test", "打开失败");
                    e.printStackTrace();
                }catch (Exception e){
                    Log.i("test", "发送失败");
                }
                finally {
                    if (mSerialPort != null) {
                        mSerialPort.close();
                    }
                    if (mOutputStream != null) {
                        try {
                            mOutputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }.start();
    }
}

16进制字符串与字节数组的转换工具类

package com.serial_test;

public class HexStrToBytes {
    /**
     * Hex字符串转byte
     * @param inHex 待转换的Hex字符串
     * @return  转换后的byte
     */
    public static byte hexToByte(String inHex){
        return (byte)Integer.parseInt(inHex,16);
    }
    /**
     * 16进制字符串转换为字节数组
     */

    public static byte[] hexToByteArray(String inHex){
        int hexlen = inHex.length();
        byte[] result;
        if (hexlen % 2 == 1){
            //奇数
            hexlen++;
            result = new byte[(hexlen/2)];
            inHex="0"+inHex;
        }else {
            //偶数
            result = new byte[(hexlen/2)];
        }
        int j=0;
        for (int i = 0; i < hexlen; i+=2){
            result[j]=hexToByte(inHex.substring(i,i+2));
            j++;
        }
        return result;
    }

}

特别鸣谢:作者:溪云一片闲舒卷 的Android Studio下的串口程序开发实战

猜你喜欢

转载自blog.csdn.net/qq_32024669/article/details/84500827