初探 ModBus4j -简单使用指南_JYU_hsy的博客-CSDN博客

目录

前言

开发环境

工具准备

具体实现

下载Modbus4j

解决空指针异常

解决数组越界

测试

测试环境准备

正式测试

    • *

前言

之前提到过 由于项目需求,需要封装 ModBus协议,ModBus协议较早,网上开源开源库也不少,可参见 Modbus 史上最全实例资料汇总。安卓上支持ModBus-RTU的库包较为稀缺,毕竟一般安卓手机不会带个串口。所幸运 Android 是一个大的框架,因而我想到了两种思路:

  • 从底层出发,使用 C/C++ Python 的开源库,通过 JNI 为应用层提供调用。
  • 在应用层移植现有ModBus-java协议库,再通过修改协议的传输层将串行通讯修改为 BLE无线传输。

本文采用的是第二种方法,使用的库是 ModBus4j(点击跳转至下载地址),在 Java 平台调试后再移植到Android,随和修改数据传输方式,在此之前会梳理开源库包的实现,如有必要对自行搭轮子也有不小的帮助。

开发环境

  • VSCode1.39.2
  • JDK1.8.0_221
  • JRE1.8.0_221

工具准备

工欲善其事必先利其器。             ---  不是我说的

使用 Modbus4j 前我们需要准备以下工具以便调试

Modbus Poll(模拟ModBus主站)&& Modbus Slave(模拟ModBus从站)

下载地址

Virtual Serial Port Driver Pro(虚拟串口)

下载地址

安装好工具,我一般会先 玩会 测试一下,使用方法见:

modbus slave 和 modbus poll 使用说明

Modbus 测试工具 ModbusPoll 与 Modbus Slave 使用方法

具体实现

下载Modbus4j

下载 ModBus4j ,并用VSCode 打开

运行 MasterTest.java (这里修改了一下,故而贴出)

package com.serotonin.modbus4j.test; import java.util.Arrays; import com.serotonin.modbus4j.ModbusFactory;import com.serotonin.modbus4j.ModbusMaster;import com.serotonin.modbus4j.code.DataType;import com.serotonin.modbus4j.exception.ModbusTransportException;//import com.serotonin.modbus4j.ip.IpParameters;import com.serotonin.modbus4j.locator.BaseLocator;import com.serotonin.modbus4j.msg.ReadCoilsRequest;import com.serotonin.modbus4j.msg.ReadCoilsResponse;import com.serotonin.modbus4j.msg.ReadDiscreteInputsRequest;import com.serotonin.modbus4j.msg.ReadDiscreteInputsResponse;import com.serotonin.modbus4j.msg.ReadExceptionStatusRequest;import com.serotonin.modbus4j.msg.ReadExceptionStatusResponse;import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;import com.serotonin.modbus4j.msg.ReadInputRegistersRequest;import com.serotonin.modbus4j.msg.ReadInputRegistersResponse;import com.serotonin.modbus4j.msg.ReportSlaveIdRequest;import com.serotonin.modbus4j.msg.ReportSlaveIdResponse;import com.serotonin.modbus4j.msg.WriteCoilRequest;import com.serotonin.modbus4j.msg.WriteCoilResponse;import com.serotonin.modbus4j.msg.WriteCoilsRequest;import com.serotonin.modbus4j.msg.WriteCoilsResponse;import com.serotonin.modbus4j.msg.WriteMaskRegisterRequest;import com.serotonin.modbus4j.msg.WriteMaskRegisterResponse;import com.serotonin.modbus4j.msg.WriteRegisterRequest;import com.serotonin.modbus4j.msg.WriteRegisterResponse;import com.serotonin.modbus4j.msg.WriteRegistersRequest;import com.serotonin.modbus4j.msg.WriteRegistersResponse; public class MasterTest {    public static void main(String[] args) throws Exception {         String commPortId = "COM1";        int baudRate = 9600;        int flowControlIn = 0;        int flowControlOut = 0;         int dataBits = 8;        int stopBits = 1;        int parity = 0;         TestSerialPortWrapper wrapper = new TestSerialPortWrapper(commPortId, baudRate, flowControlIn, flowControlOut, dataBits, stopBits, parity);                //IpParameters ipParameters = new IpParameters();        //ipParameters.setHost("localhost");         ModbusFactory modbusFactory = new ModbusFactory();          ModbusMaster master = modbusFactory.createRtuMaster(wrapper);        // ModbusMaster master = modbusFactory.createAsciiMaster(wrapper);        //ModbusMaster master = modbusFactory.createTcpMaster(ipParameters, false);        // ModbusMaster master = modbusFactory.createUdpMaster(ipParameters);         try {            master.init();            int slaveId = 1;             // readCoilTest(master, slaveId, 0, 10);            // readCoilTest(master, slaveId, 99, 200);            // readDiscreteInputTest(master, slaveId, 1, 10);            // readDiscreteInputTest(master, slaveId, 449, 72);            /*                //This is Success                //读取保持寄存器                readHoldingRegistersTest(master, slaveId, 9, 125);            */            // readHoldingRegistersTest(master, slaveId, 9, 120);            // readInputRegistersTest(master, slaveId, 0, 1);            // readInputRegistersTest(master, slaveId, 14, 8);            // writeCoilTest(master, slaveId, 1, true);            // writeCoilTest(master, slaveId, 110, true);            /*                //This is Success                //写单个寄存器                writeRegisterTest(master, slaveId, 0, 1);            */            // writeRegisterTest(master, slaveId, 14, 12345);            // readExceptionStatusTest(master, slaveId);            // reportSlaveIdTest(master, slaveId);            // writeCoilsTest(master, slaveId, 50, new boolean[] {true, false, false, true, false});            // writeCoilsTest(master, slaveId, 115, new boolean[] {true, false, false, true, false});                            //This is Success                //写多个寄存器                writeRegistersTest(master, slaveId, 300, new short[] {1, 10, 100, 1000, 10000, (short)65535});                        // writeRegistersTest(master, slaveId, 21, new short[] {1, 10, 100, 1000, 10000, (short)65535});                        //This is Success            // writeMaskRegisterTest(master, slaveId, 26, 0xf2, 0x25);             // readCoilTest(master, slaveId, 9, 5);            // readCoilTest(master, slaveId, 10, 5);            // readDiscreteInputTest(master, slaveId, 10, 6);            // readDiscreteInputTest(master, slaveId, 10, 5);            // readHoldingRegistersTest(master, slaveId, 9, 7);            // readHoldingRegistersTest(master, slaveId, 10, 5);            // readInputRegistersTest(master, slaveId, 0, 1);            // readInputRegistersTest(master, slaveId, 10, 5);            // writeCoilTest(master, slaveId, 8, true);            // writeCoilTest(master, slaveId, 11, true);            // writeRegisterTest(master, slaveId, 1, 1);            // writeRegisterTest(master, slaveId, 14, 12345);            // readExceptionStatusTest(master, slaveId);            // reportSlaveIdTest(master, slaveId);            // writeCoilsTest(master, slaveId, 11, new boolean[] {false, true, false, false, true});            // writeCoilsTest(master, slaveId, 10, new boolean[] {false, true, false, false, true});            // writeRegistersTest(master, slaveId, 11, new short[] {(short)65535, 1000, 100, 10, 1});            // writeRegistersTest(master, slaveId, 10, new short[] {(short)65535, 1000, 100, 10, 1});            // writeMaskRegisterTest(master, slaveId, 9, 0xf2, 0x25);            // writeMaskRegisterTest(master, slaveId, 10, 0xf2, 0x25);             // Automatic WriteMaskRegister failover test            // ModbusLocator locator = new ModbusLocator(slaveId, RegisterRange.HOLDING_REGISTER, 15, (byte)2);            // System.out.println(master.getValue(locator));            // master.setValue(locator, true);            // System.out.println(master.getValue(locator));            // master.setValue(locator, false);            // System.out.println(master.getValue(locator));             // BatchRead<String> batch = new BatchRead<String>();            // batch.addLocator("hr1", new ModbusLocator(31, RegisterRange.HOLDING_REGISTER, 80,            // DataType.TWO_BYTE_BCD));            // batch.addLocator("hr2", new ModbusLocator(31, RegisterRange.HOLDING_REGISTER, 81,            // DataType.FOUR_BYTE_BCD));            // BatchResults<String> results = master.send(batch);            // System.out.println(results.getValue("hr1"));            // System.out.println(results.getValue("hr2"));             // This's Successful Way to set Reg Data             // BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, 10, DataType.EIGHT_BYTE_INT_UNSIGNED);            // master.setValue(locator, 10000000);            // System.out.println(master.getValue(locator));        }        finally {            master.destroy();        }    }     public static void readCoilTest(ModbusMaster master, int slaveId, int start, int len) {        try {            ReadCoilsRequest request = new ReadCoilsRequest(slaveId, start, len);            ReadCoilsResponse response = (ReadCoilsResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println(Arrays.toString(response.getBooleanData()));        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }     public static void readDiscreteInputTest(ModbusMaster master, int slaveId, int start, int len) {        try {            ReadDiscreteInputsRequest request = new ReadDiscreteInputsRequest(slaveId, start, len);            ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println(Arrays.toString(response.getBooleanData()));        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }     public static void readHoldingRegistersTest(ModbusMaster master, int slaveId, int start, int len) {        try {            ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, start, len);            ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println(Arrays.toString(response.getShortData()));        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }     public static void readInputRegistersTest(ModbusMaster master, int slaveId, int start, int len) {        try {            ReadInputRegistersRequest request = new ReadInputRegistersRequest(slaveId, start, len);            ReadInputRegistersResponse response = (ReadInputRegistersResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println(Arrays.toString(response.getShortData()));        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }     public static void writeCoilTest(ModbusMaster master, int slaveId, int offset, boolean value) {        try {            WriteCoilRequest request = new WriteCoilRequest(slaveId, offset, value);            WriteCoilResponse response = (WriteCoilResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println("Success");        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }     public static void writeRegisterTest(ModbusMaster master, int slaveId, int offset, int value) {        try {            WriteRegisterRequest request = new WriteRegisterRequest(slaveId, offset, value);            WriteRegisterResponse response = (WriteRegisterResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println("Success");        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }     public static void readExceptionStatusTest(ModbusMaster master, int slaveId) {        try {            ReadExceptionStatusRequest request = new ReadExceptionStatusRequest(slaveId);            ReadExceptionStatusResponse response = (ReadExceptionStatusResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println(response.getExceptionStatus());        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }     public static void reportSlaveIdTest(ModbusMaster master, int slaveId) {        try {            ReportSlaveIdRequest request = new ReportSlaveIdRequest(slaveId);            ReportSlaveIdResponse response = (ReportSlaveIdResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println(Arrays.toString(response.getData()));        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }     public static void writeCoilsTest(ModbusMaster master, int slaveId, int start, boolean[] values) {        try {            WriteCoilsRequest request = new WriteCoilsRequest(slaveId, start, values);            WriteCoilsResponse response = (WriteCoilsResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println("Success");        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }     public static void writeRegistersTest(ModbusMaster master, int slaveId, int start, short[] values) {        try {            WriteRegistersRequest request = new WriteRegistersRequest(slaveId, start, values);            WriteRegistersResponse response = (WriteRegistersResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println("Success");        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }     public static void writeMaskRegisterTest(ModbusMaster master, int slaveId, int offset, int and, int or) {        try {            WriteMaskRegisterRequest request = new WriteMaskRegisterRequest(slaveId, offset, and, or);            WriteMaskRegisterResponse response = (WriteMaskRegisterResponse) master.send(request);             if (response.isException())                System.out.println("Exception response: message=" + response.getExceptionMessage());            else                System.out.println("Success");        }        catch (ModbusTransportException e) {            e.printStackTrace();        }    }}

运行结果:空指针异常

解决空指针异常

这是由于 ModBus4J 并没有给我们提供底层串口驱动

落后的解决方法:

  • 使用 Sum 基本放弃的 javacomm 

最常见的解决方法:

  • 使用 RXTXcomm.jar

受到前辈启发:

如果你不会导入库包,请前往 Visual Studio Code 手动导入 jar 包

导入库包后,我们去实现 TestSerialPortWrapper.java

/** * Copyright (C) 2015 Infinite Automation Software. All rights reserved. * @author Terry Packer */package com.serotonin.modbus4j.test; import com.serotonin.modbus4j.serial.SerialPortWrapper;import jssc.SerialPort; import java.io.InputStream;import java.io.OutputStream; import jssc.SerialPortException;//The project cannot be built until build path errors are resolved/** *  * This class is not finished *  * @author Terry Packer * */public class TestSerialPortWrapper implements SerialPortWrapper{        private SerialPort port;    private String commPortId;    private int baudRate;    private int flowControlIn;    private int flowControlOut;    private int dataBits;    private int stopBits;    private int parity;        public TestSerialPortWrapper(String commPortId, int baudRate, int flowControlIn,            int flowControlOut, int dataBits, int stopBits, int parity){        this.commPortId = commPortId;        this.baudRate = baudRate;        this.flowControlIn = flowControlIn;        this.flowControlOut = flowControlOut;        this.dataBits = dataBits;        this.stopBits = stopBits;        this.parity = parity;         port = new SerialPort(this.commPortId);            }     /* (non-Javadoc)     * @see com.serotonin.modbus4j.serial.SerialPortWrapper#close()     */    @Override    public void close() throws Exception {        port.closePort();        // TODO Auto-generated method stub            }     /* (non-Javadoc)     * @see com.serotonin.modbus4j.serial.SerialPortWrapper#open()     */    @Override    public void open() throws Exception {        try {            port.openPort();            port.setParams(this.getBaudRate(), this.getDataBits(), this.getStopBits(), this.getParity());            port.setFlowControlMode(this.getFlowControlIn() | this.getFlowControlOut());             //listeners.forEach(PortConnectionListener::opened);            //LOG.debug("Serial port {} opened", port.getPortName());        } catch (SerialPortException ex) {            //LOG.error("Error opening port : {} for {} ", port.getPortName(), ex);        }        // TODO Auto-generated method stub            }       /* (non-Javadoc)     * @see com.serotonin.modbus4j.serial.SerialPortWrapper#getInputStream()     */    @Override    public InputStream getInputStream() {        // TODO Auto-generated method stub        return new SerialInputStream(port);    }     /* (non-Javadoc)     * @see com.serotonin.modbus4j.serial.SerialPortWrapper#getOutputStream()     */    @Override    public OutputStream getOutputStream() {        // TODO Auto-generated method stub        return new SerialOutputStream(port);    }     /* (non-Javadoc)     * @see com.serotonin.modbus4j.serial.SerialPortWrapper#getBaudRate()     */    @Override    public int getBaudRate() {        // TODO Auto-generated method stub        return baudRate;    }      public int getFlowControlIn() {        return flowControlIn;        //return SerialPort.FLOWCONTROL_NONE;    }     public int getFlowControlOut() {        return flowControlOut;        //return SerialPort.FLOWCONTROL_NONE;    }      /* (non-Javadoc)     * @see com.serotonin.modbus4j.serial.SerialPortWrapper#getStopBits()     */    @Override    public int getStopBits() {        // TODO Auto-generated method stub        return stopBits;    }     /* (non-Javadoc)     * @see com.serotonin.modbus4j.serial.SerialPortWrapper#getParity()     */    @Override    public int getParity() {        // TODO Auto-generated method stub        return parity;    }     /* (non-Javadoc)     * @see com.serotonin.modbus4j.serial.SerialPortWrapper#getDataBits()     */    @Override    public int getDataBits() {        // TODO Auto-generated method stub        return dataBits;    } }

此外我们还需要将下图两个文件添加到我们的项目内

这时再运行程序还会出现错误:数组越界错误

解决数组越界

修改 SerialInputStream.java 中的 read( )

    @Override    public int read(byte[] buf, int offset, int length) throws IOException {         if (buf.length < offset + length) {            length = buf.length - offset;        }         int available = this.available();         if (available > length) {            available = length;        }         try {            byte[] readBuf = serialPort.readBytes(available);            System.arraycopy(readBuf, 0, buf, offset, readBuf.length);            return readBuf.length;        } catch (Exception e) {            throw new IOException(e);        }    }

修改 SerialOutputStream.java 中的 write( )

    @Override    public void write(byte[] b, int off, int len) throws IOException {        byte[] buffer = new byte[len];        System.arraycopy(b, off, buffer, 0, b.length);        try {            serialPort.writeBytes(buffer);        } catch (SerialPortException e) {            throw new IOException(e);        }    }

测试

测试环境准备

虚拟串口:建立 COM1 COM2 虚拟连接

添加完成后可在设备管理器中查看

ModBus Slave

F8 配置

F3配置

正式测试

读多个保持寄存器

写多个保持寄存器

注意到,最后一个寄存器写 65535 却显示 -1

这是由于显示格式导致的,我们可在 Display 中设置为其他显示格式,例如十六进制Hex

若想查看详细的数据,可在Modbus Slave 中点击工具栏的 Display--Communication Traffic 查看详细的读写信息

以文中写多个寄存器为例

000002-Rx(写寄存器请求)

01Addr10Cmd00 00Reg Addr00 06Number0C不知道作用,是 Number*200 01data1:100 0A (data2:1000 64data3:10003 E8data4:100027 10data5:10000FF FFdata6:655353F A8CRC

000003-Tx(应答信号)

01Addr10Cmd00 00Reg Addr00 06Number40 0BCRC

原网址: 访问
创建于: 2023-09-14 18:14:45
目录: default
标签: 无

请先后发表评论
  • 最新评论
  • 总共0条评论