初探 ModBus4j -简单使用指南

目录

前言

开发环境

工具准备

具体实现

下载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 batch = new BatchRead();
            // 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 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 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 -简单使用指南_第1张图片

解决空指针异常

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

落后的解决方法:

  • 使用 Sum 基本放弃的 javacomm 

最常见的解决方法:

  • 使用 RXTXcomm.jar

受到前辈启发:

  • modbus4j初次使用总结(该解决方案主要参照了 Freedomotic Open IoT Framework 开源框架

  • 使用 Jssc.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;
	}

}

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

初探 ModBus4j -简单使用指南_第2张图片

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

初探 ModBus4j -简单使用指南_第3张图片

解决数组越界

修改 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 虚拟连接

初探 ModBus4j -简单使用指南_第4张图片

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

初探 ModBus4j -简单使用指南_第5张图片

ModBus Slave

F8 配置

初探 ModBus4j -简单使用指南_第6张图片

F3配置

初探 ModBus4j -简单使用指南_第7张图片

正式测试

读多个保持寄存器

初探 ModBus4j -简单使用指南_第8张图片

写多个保持寄存器

初探 ModBus4j -简单使用指南_第9张图片

初探 ModBus4j -简单使用指南_第10张图片

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

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

初探 ModBus4j -简单使用指南_第11张图片

初探 ModBus4j -简单使用指南_第12张图片

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

初探 ModBus4j -简单使用指南_第13张图片

以文中写多个寄存器为例

000002-Rx(写寄存器请求)

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

000003-Tx(应答信号)

01Addr10Cmd00 00Reg Addr00 06Number40 0BCRC

你可能感兴趣的:(Java)