最近一个项目是android和MCU之间的串口指令数据通信,捣鼓了很久,也找了很多网上的资料。最后将实现的总结记录下来。

使用的是GitHub中的一个项目,下载地址:https://github.com/cepr/android-serialport-api

下载解压后会出现两个文件夹,我们使用的是这个,如图:

复制整个文件夹到项目的java目录下,不要变更名字,名字是跟里面的东西相关的,变更后会报错,如图:

再将android_serialport_api下的除了drawable下的其他资源文件复制粘贴到项目中对应的文件下,如图:

再将jni和lib下的文件也都复制到项目中,如图:

接下来配置model的build.gradle

在android下的defaultConfig 添加ndk,如图:

 

ndk{    abiFilters "armeabi-v7a"}ndk{    moduleName "serial_port"    abiFilters "armeabi", "armeabi-v7a", "x86"}ndk{    ldLibs "log"}

在android下添加,引用jni:

sourceSets{    main{        jni.srcDirs = []    }}注:targetSdkVersion版本要22以下,22以上会报错奔溃

至此,配置完成。接下来就是打开和使用串口通信了

串口使用到的数据定义:

/** * 标记当前串口状态(true:打开,false:关闭) **/public static boolean isFlagSerial = false;public static SerialPort serialPort = null;public static InputStream inputStream = null;public static OutputStream outputStream = null;public static Thread receiveThread = null;public static String strData = "";

打开串口,在oncreate()中调用open(),可根据返回值来确认串口是否打开成功,需要注意的是

serialPort = new SerialPort(new File("/dev/ttyS2"), 9600, 0);

/dev/ttyS2 : 表示串口号,根据自身设备更换,一般为ttyS2,ttyS3

9600 : 波特率,根据模块设定而更换

0 : 校验位,固定为0即可

/** * 打开串口 */public boolean open() {    boolean isopen = false;    if (isFlagSerial) {        Log.i("1111111", "串口已经打开,打开失败");        return false;    }    try {        serialPort = new SerialPort(new File("/dev/ttyS2"), 9600, 0);        inputStream = serialPort.getInputStream();        outputStream = serialPort.getOutputStream();        receive();        isopen = true;        isFlagSerial = true;    } catch (IOException e) {        e.printStackTrace();        isopen = false;    }    return isopen;}

关闭串口没什么好说明的,顾名思义,在退出应用时调用

/** * 关闭串口 */public boolean close() {    if (isFlagSerial) {        Log.i("1111111", "串口关闭失败");        return false;    }    boolean isClose = false;    Log.i("1111111", "关闭串口");    try {        if (inputStream != null) {            inputStream.close();        }        if (outputStream != null) {            outputStream.close();        }        isClose = true;        isFlagSerial = false;//关闭串口时,连接状态标记为false    } catch (IOException e) {        e.printStackTrace();        isClose = false;    }    return isClose;}

发送串口指令,这个是android下发数据到mcu时调用,一般用于mcu上发数据给android端,android发特定指令回应指令发送给mcu。

data : 为string类型,定义下发指令时为byte[]类型,需要转换成string

十进制指令:byte data[] = new byte[]{104, 81, 03, 00, 00, 25, 85};

十六进制指令:byte data[] = new byte[]{0x68, 0x51, 0x03, 0x00, 0x00, 0x19, 0x55};

sendString(ByteUtil.getSum16(ms, ms.length));    //发送指令

我一般使用的是十进制指令,因为十六进制指令有时是0xBA这种两个字母时,会出错。使用十进制指令要将mcu给的指令转成

十进制,因为mcu给的都会是十六进制的数值。byte[]转string的工具类会在文章末贴出,网上也很容易找到

/** * 发送串口指令 */public void sendString(String data) {    Toast.makeText(MainActivity.this, "发送串口指令 : " + data, Toast.LENGTH_SHORT).show();    if (!isFlagSerial) {        Log.i("1111111", "串口未打开,发送失败" + data);        return;    }    try {        outputStream.write(ByteUtil.hex2byte(data));        outputStream.flush();        Log.i("1111111", "sendSerialData:" + data);    } catch (IOException e) {        e.printStackTrace();        Log.i("1111111", "发送指令出现异常");    }}

接收串口数据,此方法为接收mcu上发给android的指令数据方法。

strData  : 上发的数据,根据数据进行处理,实现各需功能

/** * 接收串口数据的方法 */public void receive() {    if (receiveThread != null && !isFlagSerial) {        return;    }    receiveThread = new Thread() {        @Override        public void run() {            while (isFlagSerial) {                try {                    byte[] readData = new byte[32];                    if (inputStream == null) {                        return;                    }                    int size = inputStream.read(readData);                    if (size > 0 && isFlagSerial) {                        strData = ByteUtil.byteToStr(readData, size);                        Message msg = new Message();                        msg.what = 0x10;                        msg.obj = strData;                        handler.sendMessage(msg);                        Log.i("1111111", "readSerialData:" + strData);                    }                } catch (IOException e) {                    e.printStackTrace();                }            }        }    };    receiveThread.start();}

串口通信类,复制粘贴即可使用,我这里是将这些方法写在MainActivity下,也可以写成一个工具类调用,由你们决定。下面将byte[]和string的转换工具类贴一下出来

public class ByteUtil {    /**     * 字符串转化成为16进制字符串     *     * @param s     * @return     */    public static String strTo16(String s) {        String str = "";        for (int i = 0; i < s.length(); i++) {            int ch = (int) s.charAt(i);            String s4 = Integer.toHexString(ch);            str = str + s4;        }        return str;    }    /**     * 16进制转换成为string类型字符串     *     * @param s     * @return     */    public static String hexStringToString(String s) {        if (s == null || s.equals("")) {            return null;        }        s = s.replace(" ", "");        byte[] baKeyword = new byte[s.length() / 2];        for (int i = 0; i < baKeyword.length; i++) {            try {                baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));            } catch (Exception e) {                e.printStackTrace();            }        }        try {            s = new String(baKeyword, "UTF-8");            new String();        } catch (Exception e1) {            e1.printStackTrace();        }        return s;    }    /**     * 向串口发送数据转为字节数组     */    public static byte[] hex2byte(String hex) {        String digital = "0123456789ABCDEF";        String hex1 = hex.replace(" ", "");        char[] hex2char = hex1.toCharArray();        byte[] bytes = new byte[hex1.length() / 2];        byte temp;        for (int p = 0; p < bytes.length; p++) {            temp = (byte) (digital.indexOf(hex2char[2 * p]) * 16);            temp += digital.indexOf(hex2char[2 * p + 1]);            bytes[p] = (byte) (temp & 0xff);        }        return bytes;    }    /**     * 接收到的字节数组转换16进制字符串     */    public static String bytes2HexString(byte[] b, int size) {        String ret = "";        for (int i = 0; i < size; i++) {            String hex = Integer.toHexString(b[i] & 0xFF);            if (hex.length() == 1) {                hex = '0' + hex;            }            ret += hex.toUpperCase();        }        return ret;    }    public static String bytesToHexString(byte[] src) {        StringBuilder stringBuilder = new StringBuilder("");        if (src == null || src.length <= 0) {            return null;        }        for (int i = 0; i < src.length; i++) {            int v = src[i] & 0xFF;            String hv = Integer.toHexString(v);            if (hv.length() < 2) {                stringBuilder.append(0);            }            stringBuilder.append(hv);        }        return stringBuilder.toString();    }    /**     * 接收到的字节数组转换16进制字符串     */    public static String byteToStr(byte[] b, int size) {        String ret = "";        for (int i = 0; i < size; i++) {            String hex = Integer.toHexString(b[i] & 0xFF);            if (hex.length() == 1) {                hex = '0' + hex;            }            ret += hex.toUpperCase();        }        return ret;    }    /**     * 计算CRC16校验码     * 逐个求和     *     * @param bytes 字节数组     * @return {@link String} 校验码     * @since 1.0     */    public static String getCRC_16(byte[] bytes) {        int CRC = 0x0000ffff;        int POLYNOMIAL = 0x0000a001;        int i, j;        for (i = 0; i < bytes.length; i++) {            CRC ^= ((int) bytes[i] & 0x000000ff);            for (j = 0; j < 8; j++) {                if ((CRC & 0x00000001) != 0) {                    CRC >>= 1;                    CRC ^= POLYNOMIAL;                } else {                    CRC >>= 1;                }            }        }        if (Integer.toHexString(CRC).toUpperCase().length() == 2) {            return byteToStr(bytes, bytes.length) + "00" + Integer.toHexString(CRC).toUpperCase();        } else if (Integer.toHexString(CRC).toUpperCase().length() == 3) {            return byteToStr(bytes, bytes.length) + "0" + Integer.toHexString(CRC).toUpperCase();        }        return byteToStr(bytes, bytes.length) + Integer.toHexString(CRC).toUpperCase();    }    /**     * 指令校验和,并取出后两位字节     * */    public static String getSum16(byte[] msg, int length) {        long mSum = 0;        byte[] mByte = new byte[length];        /** 逐Byte添加位数和 */        for (byte byteMsg : msg) {            long mNum = ((long) byteMsg >= 0) ? (long) byteMsg : ((long) byteMsg + 256);            mSum += mNum;        } /** end of for (byte byteMsg : msg) */        /** 位数和转化为Byte数组 */        for (int liv_Count = 0; liv_Count < length; liv_Count++) {            mByte[length - liv_Count - 1] = (byte) (mSum >> (liv_Count * 8) & 0xff);        } /** end of for (int liv_Count = 0; liv_Count < length; liv_Count++) */        return byteToStr(msg, length) + byteToStr(mByte, mByte.length).substring(byteToStr(mByte,       mByte.length).length() - 4, byteToStr(mByte, mByte.length).length());    }}

至此,END。

 

 

 

更多相关文章

  1. “罗永浩抖音首秀”销售数据的可视化大屏是怎么做出来的呢?
  2. Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
  3. 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
  4. 初涉Android之数据库数据列表显示
  5. Android(安卓)NDK学习笔记6-JNI对引用数据类型的操作
  6. Android键值上报流程
  7. 玩转Android---组件篇---数据存储之SQLite
  8. android中adb shell常用命令及查询数据库的方法
  9. Android关于SQLite的日期时间的存储

随机推荐

  1. Android中动态设置TextView的drawableLef
  2. QuickContactBadge如何实现
  3. android 有效加载大型Bitmap - 开发文档
  4. android: Looper 源码浅析 [Handler,Loop
  5. [Android(安卓)GMS 认证] CTS 测试准备
  6. 如何控制android中ImageView的位置
  7. Android(安卓)标题栏和状态栏随ScrollVie
  8. ListView 和 Edittext 使用时焦点和数据
  9. android六大布局和UI组件
  10. Android——一分钟学会使用实时模板Live