Android 使用modbus协议与可能遇到的问题解决一览

这篇具有很好参考价值的文章主要介绍了Android 使用modbus协议与可能遇到的问题解决一览。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

本篇文章主要演示android的串口通讯功能,其中需要使用serialport模块(下载链接),注意: 串口通讯需要root权限,需要将应用设置成‘android:sharedUserId=“android.uid.system”’即可,如果出现串口通讯无法访问设备,首先看串口名称与波特率是否一致,如果都一致看看是否是打开串口就失败了,如果出现无权限的情况,可能是Android开发板不支持与该设备通讯,可以考虑让嵌入式工程师使用单片机提供通讯访问能力,本篇文章只是演示android使用modbus协议,具体协议理论可以参考其它文章,这里不在讲解。


一、导入模块

implementation project(path: ':serialport')

二、协议相关

1. CRC16

package com.jujiang.fyc.myttftwo.utils.modbus;

import java.util.Arrays;

public class CRC16 {
    private static final byte[] crc16_tab_h = { (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40,
            (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
            (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0,
            (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00,
            (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41,
            (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
            (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0,
            (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01,
            (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41,
            (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
            (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1,
            (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00,
            (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41,
            (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
            (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0,
            (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00,
            (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41,
            (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
            (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0,
            (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00,
            (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41,
            (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
            (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1,
            (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01,
            (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41,
            (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80,
            (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1,
            (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00,
            (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41,
            (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
            (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1,
            (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x01,
            (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41,
            (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40, (byte) 0x00, (byte) 0xC1, (byte) 0x81,
            (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1,
            (byte) 0x81, (byte) 0x40, (byte) 0x01, (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x01,
            (byte) 0xC0, (byte) 0x80, (byte) 0x41, (byte) 0x00, (byte) 0xC1, (byte) 0x81, (byte) 0x40 };

    private static final byte[] crc16_tab_l = { (byte) 0x00, (byte) 0xC0, (byte) 0xC1, (byte) 0x01,
            (byte) 0xC3, (byte) 0x03, (byte) 0x02, (byte) 0xC2, (byte) 0xC6, (byte) 0x06, (byte) 0x07,
            (byte) 0xC7, (byte) 0x05, (byte) 0xC5, (byte) 0xC4, (byte) 0x04, (byte) 0xCC, (byte) 0x0C,
            (byte) 0x0D, (byte) 0xCD, (byte) 0x0F, (byte) 0xCF, (byte) 0xCE, (byte) 0x0E, (byte) 0x0A,
            (byte) 0xCA, (byte) 0xCB, (byte) 0x0B, (byte) 0xC9, (byte) 0x09, (byte) 0x08, (byte) 0xC8,
            (byte) 0xD8, (byte) 0x18, (byte) 0x19, (byte) 0xD9, (byte) 0x1B, (byte) 0xDB, (byte) 0xDA,
            (byte) 0x1A, (byte) 0x1E, (byte) 0xDE, (byte) 0xDF, (byte) 0x1F, (byte) 0xDD, (byte) 0x1D,
            (byte) 0x1C, (byte) 0xDC, (byte) 0x14, (byte) 0xD4, (byte) 0xD5, (byte) 0x15, (byte) 0xD7,
            (byte) 0x17, (byte) 0x16, (byte) 0xD6, (byte) 0xD2, (byte) 0x12, (byte) 0x13, (byte) 0xD3,
            (byte) 0x11, (byte) 0xD1, (byte) 0xD0, (byte) 0x10, (byte) 0xF0, (byte) 0x30, (byte) 0x31,
            (byte) 0xF1, (byte) 0x33, (byte) 0xF3, (byte) 0xF2, (byte) 0x32, (byte) 0x36, (byte) 0xF6,
            (byte) 0xF7, (byte) 0x37, (byte) 0xF5, (byte) 0x35, (byte) 0x34, (byte) 0xF4, (byte) 0x3C,
            (byte) 0xFC, (byte) 0xFD, (byte) 0x3D, (byte) 0xFF, (byte) 0x3F, (byte) 0x3E, (byte) 0xFE,
            (byte) 0xFA, (byte) 0x3A, (byte) 0x3B, (byte) 0xFB, (byte) 0x39, (byte) 0xF9, (byte) 0xF8,
            (byte) 0x38, (byte) 0x28, (byte) 0xE8, (byte) 0xE9, (byte) 0x29, (byte) 0xEB, (byte) 0x2B,
            (byte) 0x2A, (byte) 0xEA, (byte) 0xEE, (byte) 0x2E, (byte) 0x2F, (byte) 0xEF, (byte) 0x2D,
            (byte) 0xED, (byte) 0xEC, (byte) 0x2C, (byte) 0xE4, (byte) 0x24, (byte) 0x25, (byte) 0xE5,
            (byte) 0x27, (byte) 0xE7, (byte) 0xE6, (byte) 0x26, (byte) 0x22, (byte) 0xE2, (byte) 0xE3,
            (byte) 0x23, (byte) 0xE1, (byte) 0x21, (byte) 0x20, (byte) 0xE0, (byte) 0xA0, (byte) 0x60,
            (byte) 0x61, (byte) 0xA1, (byte) 0x63, (byte) 0xA3, (byte) 0xA2, (byte) 0x62, (byte) 0x66,
            (byte) 0xA6, (byte) 0xA7, (byte) 0x67, (byte) 0xA5, (byte) 0x65, (byte) 0x64, (byte) 0xA4,
            (byte) 0x6C, (byte) 0xAC, (byte) 0xAD, (byte) 0x6D, (byte) 0xAF, (byte) 0x6F, (byte) 0x6E,
            (byte) 0xAE, (byte) 0xAA, (byte) 0x6A, (byte) 0x6B, (byte) 0xAB, (byte) 0x69, (byte) 0xA9,
            (byte) 0xA8, (byte) 0x68, (byte) 0x78, (byte) 0xB8, (byte) 0xB9, (byte) 0x79, (byte) 0xBB,
            (byte) 0x7B, (byte) 0x7A, (byte) 0xBA, (byte) 0xBE, (byte) 0x7E, (byte) 0x7F, (byte) 0xBF,
            (byte) 0x7D, (byte) 0xBD, (byte) 0xBC, (byte) 0x7C, (byte) 0xB4, (byte) 0x74, (byte) 0x75,
            (byte) 0xB5, (byte) 0x77, (byte) 0xB7, (byte) 0xB6, (byte) 0x76, (byte) 0x72, (byte) 0xB2,
            (byte) 0xB3, (byte) 0x73, (byte) 0xB1, (byte) 0x71, (byte) 0x70, (byte) 0xB0, (byte) 0x50,
            (byte) 0x90, (byte) 0x91, (byte) 0x51, (byte) 0x93, (byte) 0x53, (byte) 0x52, (byte) 0x92,
            (byte) 0x96, (byte) 0x56, (byte) 0x57, (byte) 0x97, (byte) 0x55, (byte) 0x95, (byte) 0x94,
            (byte) 0x54, (byte) 0x9C, (byte) 0x5C, (byte) 0x5D, (byte) 0x9D, (byte) 0x5F, (byte) 0x9F,
            (byte) 0x9E, (byte) 0x5E, (byte) 0x5A, (byte) 0x9A, (byte) 0x9B, (byte) 0x5B, (byte) 0x99,
            (byte) 0x59, (byte) 0x58, (byte) 0x98, (byte) 0x88, (byte) 0x48, (byte) 0x49, (byte) 0x89,
            (byte) 0x4B, (byte) 0x8B, (byte) 0x8A, (byte) 0x4A, (byte) 0x4E, (byte) 0x8E, (byte) 0x8F,
            (byte) 0x4F, (byte) 0x8D, (byte) 0x4D, (byte) 0x4C, (byte) 0x8C, (byte) 0x44, (byte) 0x84,
            (byte) 0x85, (byte) 0x45, (byte) 0x87, (byte) 0x47, (byte) 0x46, (byte) 0x86, (byte) 0x82,
            (byte) 0x42, (byte) 0x43, (byte) 0x83, (byte) 0x41, (byte) 0x81, (byte) 0x80, (byte) 0x40 };

    /**
     * 计算CRC16校验
     *
     * @param data
     *            需要计算的数组
     * @return CRC16校验值
     */
    public static int compute(byte[] data) {
        return compute(data, 0, data.length);
    }

    /**
     * 校验byte数据是否是CRC16数据
     * @return 是否成功
     */
    public static boolean checkCRC16(byte[] data) {
        int size = data.length;
        if (size <= 2) {
            return false;
        }
        byte[] oldCheckArray = new byte[]{ data[size - 2], data[size - 1]};
        // 将数据拆分成三分
        byte[] bytes = new byte[size - 2];
        System.arraycopy(data, 0, bytes, 0, size - 2);
        // 计算CRC校验码
        int crc = compute(bytes);
        ByteArrayWriter request = new ByteArrayWriter();
        request.writeInt16Reversal(crc);
        byte[] checkArray = request.toByteArray();
        return Arrays.equals(oldCheckArray, checkArray);
    }

    /**
     * 计算CRC16校验
     *
     * @param data
     *            需要计算的数组
     * @param offset
     *            起始位置
     * @param len
     *            长度
     * @return CRC16校验值
     */
    public static int compute(byte[] data, int offset, int len) {
        return compute(data, offset, len, 0xffff);
    }

    /**
     * 计算CRC16校验
     *
     * @param data
     *            需要计算的数组
     * @param offset
     *            起始位置
     * @param len
     *            长度
     * @param preval
     *            之前的校验值
     * @return CRC16校验值
     */
    public static int compute(byte[] data, int offset, int len, int preval) {
        int ucCRCHi = (preval & 0xff00) >> 8;
        int ucCRCLo = preval & 0x00ff;
        int iIndex;
        for (int i = 0; i < len; ++i) {
            iIndex = (ucCRCLo ^ data[offset + i]) & 0x00ff;
            ucCRCLo = ucCRCHi ^ crc16_tab_h[iIndex];
            ucCRCHi = crc16_tab_l[iIndex];
        }
        int result=((ucCRCHi & 0x00ff) << 8) | (ucCRCLo & 0x00ff) & 0xffff;
        return result;
    }
}

2. ByteUtil

package com.jujiang.fyc.myttftwo.utils.modbus;

public class ByteUtil {
    public static String toHexString(byte[] input, String separator) {
        if (input==null) return null;

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < input.length; i++) {
            if (separator != null && sb.length() > 0) {
                sb.append(separator);
            }
            String str = Integer.toHexString(input[i] & 0xff);
            if (str.length() == 1) str = "0" + str;
            sb.append(str);
        }
        return sb.toString();
    }

    public static String toHexString(byte[] input) {
        return toHexString(input, " ");
    }

    public static byte[] fromInt32(int input){
        byte[] result=new byte[4];
        result[3]=(byte)(input >> 24 & 0xFF);
        result[2]=(byte)(input >> 16 & 0xFF);
        result[1]=(byte)(input >> 8 & 0xFF);
        result[0]=(byte)(input & 0xFF);
        return result;
    }

    public static byte[] fromInt16(int input){
        byte[] result=new byte[2];
        result[0]=(byte)(input >> 8 & 0xFF);
        result[1]=(byte)(input & 0xFF);
        return result;
    }

    public static byte[] fromInt16Reversal(int input){
        byte[] result=new byte[2];
        result[1]=(byte)(input>>8&0xFF);
        result[0]=(byte)(input&0xFF);
        return result;
    }
}

3. ModbusError

package com.jujiang.fyc.myttftwo.utils.modbus;

import android.text.TextUtils;

public class ModbusError extends Exception {
    private int code;

    public ModbusError(int code, String message) {
        super(!TextUtils.isEmpty(message) ? message : "Modbus Error: Exception code = " + code);
        this.code = code;
    }

    public ModbusError(int code) {
        this(code, null);
    }

    public ModbusError(ModbusErrorType type, String message) {
        super(type.name() + ": " + message);
    }

    public ModbusError(String message) {
        super(message);
    }

    public int getCode() {
        return this.code;
    }
}

4. ModbusErrorType

package com.jujiang.fyc.myttftwo.utils.modbus;

/**
 * 常见的Modbus通讯错误
 */
public enum ModbusErrorType {
    ModbusError,
    ModbusFunctionNotSupportedError,
    ModbusDuplicatedKeyError,
    ModbusMissingKeyError,
    ModbusInvalidBlockError,
    ModbusInvalidArgumentError,
    ModbusOverlapBlockError,
    ModbusOutOfBlockError,
    ModbusInvalidResponseError,
    ModbusInvalidRequestError,
    ModbusTimeoutError
}

5. ModbusFunction

package com.jujiang.fyc.myttftwo.utils.modbus;

/**
 * 功能码(十进制显示)
 */
public class ModbusFunction {

    //读线圈寄存器
    public static final int READ_COILS = 1;

    //读离散输入寄存器
    public static final int READ_DISCRETE_INPUTS = 2;

    //读保持寄存器
    public static final int READ_HOLDING_REGISTERS = 3;

    //读输入寄存器
    public static final int READ_INPUT_REGISTERS = 4;

    //写单个线圈寄存器
    public static final int WRITE_SINGLE_COIL = 5;

    //写单个保持寄存器
    public static final int WRITE_SINGLE_REGISTER = 6;

    //写入多个线圈寄存器
    public static final int WRITE_COILS = 15;

    //写入多个保持寄存器
    public static final int WRITE_HOLDING_REGISTERS = 16;
}

6. ModbusRtuMaster

package com.jujiang.fyc.myttftwo.utils.modbus;

// 提供协议部分功能
public class ModbusRtuMaster {
    /**
     * 组装Modbus RTU消息帧
     *
     * @param slave            从站地址号
     * @param function_code    功能码
     * @param starting_address 读取寄存器起始地址 / 写入寄存器地址 / 写入寄存器起始地址
     * @param quantity_of_x    读取寄存器个数 / 写入寄存器个数
     * @param output_value     需要写入单个寄存器的数值
     * @param output_values    需要写入多个寄存器的数组
     * @return 将整个消息帧转成byte[]
     * @throws ModbusError Modbus错误
     */
    synchronized byte[] execute(int slave, int function_code, int starting_address, int quantity_of_x,
                                        int output_value, int[] output_values) throws ModbusError {
        //检查参数是否符合协议规定
        if (slave < 0 || slave > 0xff) {
            throw new ModbusError(ModbusErrorType.ModbusInvalidArgumentError, "Invalid slave " + slave);
        }
        if (starting_address < 0 || starting_address > 0xffff) {
            throw new ModbusError(ModbusErrorType.ModbusInvalidArgumentError, "Invalid starting_address " + starting_address);
        }
        if (quantity_of_x < 1 || quantity_of_x > 0xff) {
            throw new ModbusError(ModbusErrorType.ModbusInvalidArgumentError, "Invalid quantity_of_x " + quantity_of_x);
        }

        // 构造request
        ByteArrayWriter request = new ByteArrayWriter();
        //写入从站地址号
        request.writeInt8(slave);
        //根据功能码组装数据区
        //如果为读取寄存器指令
        if (function_code == ModbusFunction.READ_COILS || function_code == ModbusFunction.READ_DISCRETE_INPUTS
                || function_code == ModbusFunction.READ_INPUT_REGISTERS || function_code == ModbusFunction.READ_HOLDING_REGISTERS) {
            request.writeInt8(function_code);
            request.writeInt16(starting_address);
            request.writeInt16(quantity_of_x);

        } else if (function_code == ModbusFunction.WRITE_SINGLE_COIL || function_code == ModbusFunction.WRITE_SINGLE_REGISTER) {//写单个寄存器指令
            if (function_code == ModbusFunction.WRITE_SINGLE_COIL)
                if (output_value != 0) output_value = 0xff00;//如果为线圈寄存器(写1时为 FF 00,写0时为00 00)
            request.writeInt8(function_code);
            request.writeInt16(starting_address);
            request.writeInt16(output_value);

        } else if (function_code == ModbusFunction.WRITE_COILS) {//写多个线圈寄存器
            request.writeInt8(function_code);
            request.writeInt16(starting_address);
            request.writeInt16(quantity_of_x);

            //计算写入字节数
            int writeByteCount = (quantity_of_x / 8) + 1;/// 满足关系-> (w /8) + 1
            //写入数量 == 8 ,则写入字节数为1
            if (quantity_of_x % 8 == 0) {
                writeByteCount -= 1;
            }
            request.writeInt8(writeByteCount);

            int index = 0;
            //如果写入数据数量 > 8 ,则需要拆分开来
            int start = 0;//数组开始位置
            int end = 7;//数组结束位置
            int[] splitData = new int[8];
            //循环写入拆分数组,直到剩下最后一组 元素个数 <= 8 的数据
            while (writeByteCount > 1) {
                writeByteCount--;
                int sIndex = 0;
                for (index = start; index <= end; index++) {
                    splitData[sIndex++] = output_values[index];
                }
                //数据反转 对于是否要反转要看你传过来的数据,如果高低位顺序正确则不用反转
                splitData = reverseArr(splitData);
                //写入拆分数组
                request.writeInt8(toDecimal(splitData));
                start = index;
                end += 8;
            }
            //写入最后剩下的数据
            int last = quantity_of_x - index;
            int[] tData = new int[last];
            System.arraycopy(output_values, index, tData, 0, last);
            //数据反转 对于是否要反转要看你传过来的数据,如果高低位顺序正确则不用反转
            tData = reverseArr(tData);
            request.writeInt8(toDecimal(tData));
        } else if (function_code == ModbusFunction.WRITE_HOLDING_REGISTERS) {//写多个保持寄存器
            request.writeInt8(function_code);
            request.writeInt16(starting_address);
            request.writeInt16(quantity_of_x);
            request.writeInt8(2 * quantity_of_x);
            //写入数据
            for (int v : output_values) {
                request.writeInt16(v);
            }
        } else {
            throw new ModbusError(ModbusErrorType.ModbusFunctionNotSupportedError, "Not support function " + function_code);
        }
        byte[] bytes = request.toByteArray();
        //计算CRC校验码
        int crc = CRC16.compute(bytes);
        request.writeInt16Reversal(crc);
        bytes = request.toByteArray();
        return bytes;
    }

    //将数组反转
    private int[] reverseArr(int[] arr) {
        int[] tem = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            tem[i] = arr[arr.length - 1 - i];
        }
        return tem;
    }

    //将int[1,0,0,1,1,0]数组转换为十进制数据
    private int toDecimal(int[] data) {
        int result = 0;
        if (data != null) {
            StringBuilder sData = new StringBuilder();
            for (int d : data) {
                sData.append(d);
            }
            try {
                result = Integer.parseInt(sData.toString(), 2);
            } catch (NumberFormatException e) {
                result = -1;
            }

        }
        return result;
    }
}

7. ByteArrayWriter

package com.jujiang.fyc.myttftwo.utils.modbus;

import java.io.ByteArrayOutputStream;

public class ByteArrayWriter extends ByteArrayOutputStream {
    public ByteArrayWriter() {
        super();
    }

    public void writeInt8(byte b)
    {
        this.write(b);
    }

    public void writeInt8(int b)
    {
        this.write((byte)b);
    }

    public void writeInt16(int n) {
        byte[] bytes = ByteUtil.fromInt16(n);
        this.write(bytes, 0, bytes.length);
    }

    public void writeInt16Reversal(int n){
        byte[] bytes=ByteUtil.fromInt16Reversal(n);
        this.write(bytes,0,bytes.length);
    }

    public void writeInt32(int n) {
        byte[] bytes = ByteUtil.fromInt32(n);
        this.write(bytes, 0, bytes.length);
    }

    public void writeBytes(byte[] bs,int len){
        this.write(bs,0,len);
    }
}

8. ModbusRtuSerialPortUtil

package com.jujiang.fyc.myttftwo.utils.modbus

import android_serialport_api.SerialPort
import com.android.jws.JwsManager
import com.android.jws.JwsSerialPort
import com.jujiang.fyc.myttftwo.utils.LogUtils
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream

/**
 * 帮助Modbus 发送或接受数据
 */
class ModbusRtuSerialPortUtil {

    private var jwp: SerialPort? = null
    private var inputStream: InputStream? = null
    private var outputStream: OutputStream? = null

    fun open(device: String, baud: Int, dataBits: Int, parity: Int, stopBit: Int) {
        try {
            // 普通串口通讯
//            jwp = SerialPort(File(device), rate, 0)
            // 设置验证位等参数
            jwp = SerialPort(File(device), baud, parity, dataBits, stopBit)
            inputStream = jwp!!.inputStream
            outputStream = jwp!!.outputStream
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    // 发送数据
    fun send(bytes: ByteArray, millisecond: Long, block: (ByteArray, ByteArray) -> Unit) {
        try {
            LogUtils.e("bytes = ${bytes.map { String.format("%02x", *arrayOf<Any>(it)) }}")
            outputStream?.apply {
                write(bytes)
                flush()
            }
            Thread.sleep(millisecond)
            // 写入数据
            val size = inputStream!!.available()
            val byteArray = ByteArray(size)
            inputStream!!.read(byteArray)
            block(bytes, byteArray)
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
}

9. ModbusRtuMasterHelp

package com.jujiang.fyc.myttftwo.utils.modbus

import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume

/**
 * 实际调用类
 */
class ModbusRtuMasterHelp(private val serialHelper: ModbusRtuSerialPortUtil) {
    private val modbusRtu: ModbusRtuMaster = ModbusRtuMaster()

    /**
     * 读多个线圈寄存器
     * @param slave 从站地址
     * @param startAddress 起始地址
     * @param numberOfPoints 读取线圈寄存器个数
     * @throws ModbusError Modbus错误
     */
    fun readCoils(
        slave: Int,
        startAddress: Int,
        numberOfPoints: Int,
        millisecond: Long = 50,
        block: (ByteArray, ByteArray) -> Unit
    ) {
        val sendBytes = modbusRtu.execute(
            slave,
            ModbusFunction.READ_COILS,
            startAddress,
            numberOfPoints,
            0,
            null
        )
        this.serialHelper.send(sendBytes, millisecond, block)
    }

    //读单个线圈寄存器
    fun readCoils(
        slave: Int,
        startAddress: Int,
        millisecond: Long = 50,
        block: (ByteArray, ByteArray) -> Unit
    ) {
        readCoils(slave, startAddress, 1, millisecond, block)
    }

    // 异步读取并返回结果
    suspend fun readCoilsAsync(slave: Int,
                               startAddress: Int,
                               millisecond: Long = 50): Pair<ByteArray, ByteArray> {
        return suspendCancellableCoroutine { continuation ->
            readCoils(slave, startAddress, millisecond) { old, new ->
                continuation.resume(Pair(old, new))
            }
        }
    }

    /**
     * 写单个线圈寄存器
     * @param slave 从站地址
     * @param address 写入寄存器地址
     * @param value 写入值(true/false)
     * @throws ModbusError Modbus错误
     */
    fun writeSingleCoil(
        slave: Int, address: Int, value: Boolean, millisecond: Long = 50,
        block: (ByteArray, ByteArray) -> Unit
    ) {
        val sendBytes = modbusRtu.execute(
            slave,
            ModbusFunction.WRITE_SINGLE_COIL,
            address,
            1,
            if (value) 1 else 0,
            null
        )
        this.serialHelper.send(sendBytes, millisecond, block)
    }

    // 异步写并返回结果
    suspend fun writeSingleCoilAsync(slave: Int, address: Int, value: Boolean, millisecond: Long = 50): Pair<ByteArray, ByteArray> {
        return suspendCancellableCoroutine { continuation ->
            writeSingleCoil(slave, address, value, millisecond) { old, new ->
                continuation.resume(Pair(old, new))
            }
        }
    }
}

三、使用

class MainActivity : BaseBindingActivity<ActivityMainBinding>({
    ActivityMainBinding.inflate(it)
}) {
    private val modbusRtuSerialPortUtil by lazy { ModbusRtuSerialPortUtil() }
    private val modbusRtuSerialPort by lazy { ModbusRtuMasterHelp(modbusRtuSerialPortUtil) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 连接串口, 一定要保证串口是能正常的读写, 否则会导致后续读写操作失效, 如果提示无权限, 只能让嵌入式工程师
        // 使用单片机来做通讯, Android以普通串口读取方式来使用, 如果能正常读取成功, 可以采用实例方法
        modbusRtuSerialPortUtil.open("/dev/ttyS3", 19200, 8, 2, 1)
        // 同步读取线圈, 参数1: 地址; 参数2: 线圈地址; 返回参数1: 表示发送时的byte数组; 参数2: 表示设备回发的数据数组
        modbusRtuSerialPort.readCoils(0x01, 0x0A) { oldAray, byteArray -> }
        // 异步读取在协程中操作即可
        lifecycleScope.launch(Dispatchers.IO) {
            // 在协程中可以读取的操作方式有很多种, 这里只演示一种, 如果只是需要读一个那可以不需要使用 async 包裹
            async {
                val (oldArray, byteArray) = modbusRtuSerialPort.readCoilsAsync(0x01, 0x0A)
                // CRC16.checkCRC16 用于校验返回数据的后两位是否符合协议验证, 如果不符合则需要联系设备商修改
                if (CRC16.checkCRC16(byteArray)) {
                }
            }.await()
        }
        // 同步写数据, 参数1: 地址; 参数2: 线圈地址; 参数3: 需要写入的内容,线圈操作类似于按钮,所以只需要true/false即可;
        // 返回参数1: 表示发送时的byte数组; 参数2: 表示设备回发的数据数组
        // 异步写入这里就不在演示了, 目前没涉及到需要异步写入的需求
        modbusRtuSerialPort.writeSingleCoil(0x01, 0x00, true) { oldAray, byteArray ->
            // 校验两次数据是否相等,不相等即表示操作失败
            if (oldAray.contentEquals(byteArray)) {
            }
        }
    }
}

总结

本篇文章主要记录Android使用modbus协议, 其中遇到了按照本篇文章中的方式去读取后会提示无权限的情况或写入数据无返回, 对该情况的推测是串口的打开是成功的但是没有写入数据到设备中, 因为如果串口没打开或打开失败会抛出异常, 通过协调之后得到的结论是设备并不支持Android主板, 解决方案是让嵌入式工程师使用单片机做一个中间层,单片机只负责与设备连接与数据写入操作, Android这边按照modbus协议发送数据给单片机, 单片机将数据发送给设备, 设备回发数据后单片机读取数据后写入数据, Android读取数据拿到实际结果, 该方案可以解决实际问题。文章来源地址https://www.toymoban.com/news/detail-621648.html

到了这里,关于Android 使用modbus协议与可能遇到的问题解决一览的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity连接数据库mysql全过程+可能遇到的问题与解决方案

    目录 一、具备条件 二、unity连接mysql 三、问题总结 1. Mysql安装完成         安装完成后需要查看mysql的版本,打开终端(管理者身份运行),输入以下语句登录mysql,查看MySQL版本; 可以看到我下载的版本是 5.7.38; 2. MySQL Connector/NET下载 目的:为了搭建unity连接mysql的环境

    2024年02月03日
    浏览(65)
  • Ubuntu20.04安装sql server(内含多个可能遇到的问题以及解决方法)

    这学期的课程有数据库,因此今天(2022.3.6)在ubuntu上安装了sql server2019 即使根据官方指引进行安装也会遇到不少问题,因此为在这里总结了我自己遇到的所有问题,以及给出了相应的解决方案,希望能为后来的人解决一些困惑和麻烦 根据指引,在终端输入第一条语句:(导入

    2024年02月05日
    浏览(63)
  • 使用 Ant Design Vue 你可能会遇到的14个问题

    公司有一个新需求,在原来项目基础上开发,项目中使用 Ant Design Vue,版本是 1.X ,在此记录下遇到的问题;对于没有使用过或者使用程度不深的同学来说,希望可以帮助你在开发中遇到问题时有个参考。对于已经熟练使用的同学,可能这些问题都遇到过,欢迎大家在评论区补

    2024年02月08日
    浏览(33)
  • Android 使用外置USB麦克风MIC录音遇到问题并解决(含录音播放源码)

    使用RK3399的开发板,跑Android8.1系统 一开始插上外置的USB麦克风的时候,无法使用 查看USB麦克风拔插过程的debug打印日志 插入USB麦克风后,adb 查询当前声卡信息 cat cards 通过分析发现,USB麦克风设备没有枚举出来,节点都没挂载上去 这时候就怀疑是硬件问题, USB麦克风设备

    2024年02月09日
    浏览(84)
  • 关于Qt适配不同分辨率和缩放率时可能遇到的问题和解决方案

    如果没有特殊的处理,Qt的UI窗口在不同的分辨率和缩放率下,其显示效果可能会出现问题,常见的有: 子控件堆叠,无法显示完整 窗口尺寸变大,超出屏幕的显示范围 控件变形,长宽比不合理 界面模糊 字体变大,控件尺寸却没有变化 有两种方式可以对UI界面进行良好的缩

    2024年02月05日
    浏览(54)
  • Android 老项目导入可能遇到的问题 Unsupported Java. Your build is currently configured to use Java 17.0.6 and Gr

    Unsupported Java. Your build is currently configured to use Java 17.0.6 and Gradle 5.4.1. Possible solution: Upgrade Gradle wrapper to 7.2 version and re-import the project 意思是当前的Java17.0.6,需要升级到7.2. 原本这个项目执行不是Java17,本地使用了更高的Java版本,因此同步需要更新gradle。 当然,可以更新gradl

    2024年02月07日
    浏览(39)
  • 神器抓包工具 HTTP Analyzer v7.5 的下载,安装,使用,破解说明以及可能遇到的问题

    A1: HTTP Analyzer 是一款抓取网络数据包的软件,能够对抓取的数据包进行分析。 A2:下载链接:http://pan.baidu.com/s/1bG7KU6。 A3:解压刚刚下载好的软件,选择如下图所示的setup.exe点击安装,一路next即可完成。 完成后桌面会生成一个相应的图标: A4:(1)双击刚刚的桌面图标,

    2024年02月08日
    浏览(64)
  • Android 打包可能遇到的报错

    问题一:com.android.ide.common.signing.KeytoolException: Failed to read key testxlk from store “E:AndroidAndroid Keystestkey.jks”: Invalid keystore format 升高gradle JDK版本,我这里用了JDK11 问题二:Integrity check failed: java.security.NoSuchAlgorithmException: Algorithm HmacPBESHA256 not available 在解决问题一时升级到JDK1

    2024年04月11日
    浏览(56)
  • 可能会解决你Android studio中gradle安装的问题

    我的电脑让我不小心弄出了很多小毛病,我忍不了了,重装了系统 然后在我配置as时发现gradle竟然出现了好多问题,看了好多的帖子发现都对我没用!!! 概括 问题是下载gradle超时 检查是否有.gradle这个文件,有则改名,没有的话就先下载,看报不报错,报错了就看下一步(

    2024年02月03日
    浏览(45)
  • Windows 11 安装 pytorch3d可能遇到的问题,以及最终成功安装使用的版本分享(使用RTX3070)

    Win10下pytorch3D安装方法   由于pytorch3d对于pytorch、CUDA、CUB的版本对应实在是过于严苛,所以我的建议是直接找别人成功安装的软件版本列表对着抄,pytorch啥的该重装重装,反正可以用虚拟环境,CUDA也是,一台电脑共存两个版本的CUDA也不是不行。唯一麻烦一点的可能就是G

    2024年02月20日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包