java实现Modbus通信

参考链接:
https://www.cnblogs.com/ioufe...
https://blog.csdn.net/ioufev/...

Modbus协定

Modbus由MODICON公司于1979年开发,是一种工业现场总线协定规范。1996年施耐德公司推出基于以太网TCP/IP的Modbus协定:ModbusTCP。

Modbus协定是一项应用层报文传输协定,包含ASCII、RTU、TCP三种报文类型。

规范的Modbus协定物理层接口有RS232、RS422、RS485和以太网接口,采纳master/slave形式通信。

Modbus和RS485的关系:Modbus是协定,物理层接口有RS232、RS422、RS485和以太网接口几种

仿真软件和程序下载: 百度网盘提供

应用jlibmodbus

maven配置
<dependency>            <groupId>com.intelligt.modbus</groupId>            <artifactId>jlibmodbus</artifactId>            <version>1.2.9.7</version>        </dependency>
java代码从Modbus读取数据
package com.ioufev;import java.net.InetAddress;import com.intelligt.modbus.jlibmodbus.Modbus;import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException;import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;/** * Hello world! * */public class App {    public static void main(String[] args) {        try {            // 设置主机TCP参数            TcpParameters tcpParameters = new TcpParameters();            // 设置TCP的ip地址            InetAddress adress = InetAddress.getByName("192.168.3.16");            // TCP参数设置ip地址            // tcpParameters.setHost(InetAddress.getLocalHost());            tcpParameters.setHost(adress);            // TCP设置长连贯            tcpParameters.setKeepAlive(true);            // TCP设置端口,这里设置是默认端口502            tcpParameters.setPort(Modbus.TCP_PORT);            // 创立一个主机            ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);            Modbus.setAutoIncrementTransactionId(true);            int slaveId = 1;//从机地址            int offset = 0;//寄存器读取开始地址            int quantity = 10;//读取的寄存器数量            try {                if (!master.isConnected()) {                    master.connect();// 开启连贯                }                // 读取对应从机的数据,readInputRegisters读取的写寄存器,性能码04                int[] registerValues = master.readInputRegisters(slaveId, offset, quantity);                // 控制台输入                for (int value : registerValues) {                    System.out.println("Address: " + offset++ + ", Value: " + value);                }            } catch (ModbusProtocolException e) {                e.printStackTrace();            } catch (ModbusNumberException e) {                e.printStackTrace();            } catch (ModbusIOException e) {                e.printStackTrace();            } finally {                try {                    master.disconnect();                } catch (ModbusIOException e) {                    e.printStackTrace();                }            }        } catch (RuntimeException e) {            throw e;        } catch (Exception e) {            e.printStackTrace();        }    }}

应用modbus4j

maven配置
<dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>4.13-beta-3</version>            <scope>test</scope>        </dependency>        <dependency>            <groupId>com.infiniteautomation</groupId>            <artifactId>modbus4j</artifactId>            <version>3.0.3</version>        </dependency>        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-lang3</artifactId>            <version>3.9</version>        </dependency><!-- 若想援用modbus4j须要引入下列repository id:ias-snapshots id:ias-releases 两个 ,应用默认仓库下载,不要应用阿里云仓库-->    <repositories>        <repository>            <releases>                <enabled>false</enabled>            </releases>            <snapshots>                <enabled>true</enabled>            </snapshots>            <id>ias-snapshots</id>            <name>Infinite Automation Snapshot Repository</name>            <url>https://maven.mangoautomation.net/repository/ias-snapshot/</url>        </repository>        <repository>            <releases>                <enabled>true</enabled>            </releases>            <snapshots>                <enabled>false</enabled>            </snapshots>            <id>ias-releases</id>            <name>Infinite Automation Release Repository</name>            <url>https://maven.mangoautomation.net/repository/ias-release/</url>        </repository>    </repositories>
java应用modbus4j读取数据
package com.ioufev;import com.serotonin.modbus4j.BatchRead;import com.serotonin.modbus4j.BatchResults;import com.serotonin.modbus4j.ModbusFactory;import com.serotonin.modbus4j.ModbusMaster;import com.serotonin.modbus4j.code.DataType;import com.serotonin.modbus4j.exception.ErrorResponseException;import com.serotonin.modbus4j.exception.ModbusInitException;import com.serotonin.modbus4j.exception.ModbusTransportException;import com.serotonin.modbus4j.ip.IpParameters;import com.serotonin.modbus4j.locator.BaseLocator;/** * modbus通信工具类,采纳modbus4j实现 * */public class Modbus4jUtils {    /**     * 工厂。     */    static ModbusFactory modbusFactory;    static {        if (modbusFactory == null) {            modbusFactory = new ModbusFactory();        }    }    /**     * 获取master     *     * @return     * @throws ModbusInitException     */    public static ModbusMaster getMaster() throws ModbusInitException {        IpParameters params = new IpParameters();        params.setHost("10.211.55.4");        params.setPort(502);        //        // modbusFactory.createRtuMaster(wapper); //RTU 协定        // modbusFactory.createUdpMaster(params);//UDP 协定        // modbusFactory.createAsciiMaster(wrapper);//ASCII 协定        ModbusMaster master = modbusFactory.createTcpMaster(params, false);// TCP 协定        master.init();        return master;    }    /**     * 读取[01 Coil Status 0x]类型 开关数据     *     * @param slaveId     *            slaveId     * @param offset     *            地位     * @return 读取值     * @throws ModbusTransportException     *             异样     * @throws ErrorResponseException     *             异样     * @throws ModbusInitException     *             异样     */    public static Boolean readCoilStatus(int slaveId, int offset)            throws ModbusTransportException, ErrorResponseException, ModbusInitException {        // 01 Coil Status        BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);        Boolean value = getMaster().getValue(loc);        return value;    }    /**     * 读取[02 Input Status 1x]类型 开关数据     *     * @param slaveId     * @param offset     * @return     * @throws ModbusTransportException     * @throws ErrorResponseException     * @throws ModbusInitException     */    public static Boolean readInputStatus(int slaveId, int offset)            throws ModbusTransportException, ErrorResponseException, ModbusInitException {        // 02 Input Status        BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);        Boolean value = getMaster().getValue(loc);        return value;    }    /**     * 读取[03 Holding Register类型 2x]模拟量数据     *     * @param slaveId     *            slave Id     * @param offset     *            地位     * @param dataType     *            数据类型,来自com.serotonin.modbus4j.code.DataType     * @return     * @throws ModbusTransportException     *             异样     * @throws ErrorResponseException     *             异样     * @throws ModbusInitException     *             异样     */    public static Number readHoldingRegister(int slaveId, int offset, int dataType)            throws ModbusTransportException, ErrorResponseException, ModbusInitException {        // 03 Holding Register类型数据读取        BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);        Number value = getMaster().getValue(loc);        return value;    }    /**     * 读取[04 Input Registers 3x]类型 模拟量数据     *     * @param slaveId     *            slaveId     * @param offset     *            地位     * @param dataType     *            数据类型,来自com.serotonin.modbus4j.code.DataType     * @return 返回后果     * @throws ModbusTransportException     *             异样     * @throws ErrorResponseException     *             异样     * @throws ModbusInitException     *             异样     */    public static Number readInputRegisters(int slaveId, int offset, int dataType)            throws ModbusTransportException, ErrorResponseException, ModbusInitException {        // 04 Input Registers类型数据读取        BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);        Number value = getMaster().getValue(loc);        return value;    }    /**     * 批量读取应用办法     *     * @throws ModbusTransportException     * @throws ErrorResponseException     * @throws ModbusInitException     */    public static void batchRead() throws ModbusTransportException, ErrorResponseException, ModbusInitException {        BatchRead<Integer> batch = new BatchRead<Integer>();        batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));        batch.addLocator(1, BaseLocator.inputStatus(1, 0));        ModbusMaster master = getMaster();        batch.setContiguousRequests(false);        BatchResults<Integer> results = master.send(batch);        System.out.println(results.getValue(0));        System.out.println(results.getValue(1));    }    /**     * 测试     *     * @param args     */    public static void main(String[] args) {        try {            // 01测试            Boolean v011 = readCoilStatus(1, 0);            Boolean v012 = readCoilStatus(1, 1);            Boolean v013 = readCoilStatus(1, 6);            System.out.println("v011:" + v011);            System.out.println("v012:" + v012);            System.out.println("v013:" + v013);            // 02测试            Boolean v021 = readInputStatus(1, 0);            Boolean v022 = readInputStatus(1, 1);            Boolean v023 = readInputStatus(1, 2);            System.out.println("v021:" + v021);            System.out.println("v022:" + v022);            System.out.println("v023:" + v023);            // 03测试            Number v031 = readHoldingRegister(1, 1, DataType.FOUR_BYTE_FLOAT);// 留神,float            Number v032 = readHoldingRegister(1, 3, DataType.FOUR_BYTE_FLOAT);// 同上            System.out.println("v031:" + v031);            System.out.println("v032:" + v032);            // 04测试            Number v041 = readInputRegisters(1, 0, DataType.FOUR_BYTE_FLOAT);//            Number v042 = readInputRegisters(1, 2, DataType.FOUR_BYTE_FLOAT);//            System.out.println("v041:" + v041);            System.out.println("v042:" + v042);            // 批量读取            batchRead();        } catch (Exception e) {            e.printStackTrace();        }    }}
java通过modbus4j对数据的写入
package com.ioufev;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import com.serotonin.modbus4j.ModbusFactory;import com.serotonin.modbus4j.ModbusMaster;import com.serotonin.modbus4j.code.DataType;import com.serotonin.modbus4j.exception.ErrorResponseException;import com.serotonin.modbus4j.exception.ModbusInitException;import com.serotonin.modbus4j.exception.ModbusTransportException;import com.serotonin.modbus4j.ip.IpParameters;import com.serotonin.modbus4j.locator.BaseLocator;import com.serotonin.modbus4j.msg.ModbusResponse;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.WriteRegisterRequest;import com.serotonin.modbus4j.msg.WriteRegisterResponse;import com.serotonin.modbus4j.msg.WriteRegistersRequest;/** * modbus4j写入数据 * */public class Modbus4jWriteUtils {    static Log log = LogFactory.getLog(Modbus4jWriteUtils.class);    /**     * 工厂。     */    static ModbusFactory modbusFactory;    static {        if (modbusFactory == null) {            modbusFactory = new ModbusFactory();        }    }    /**     * 获取tcpMaster     *     * @return     * @throws ModbusInitException     */    public static ModbusMaster getMaster() throws ModbusInitException {        IpParameters params = new IpParameters();        params.setHost("10.211.55.4");        params.setPort(502);        ModbusMaster tcpMaster = modbusFactory.createTcpMaster(params, false);        tcpMaster.init();        return tcpMaster;    }    /**     * 写 [01 Coil Status(0x)]写一个 function ID = 5     *     * @param slaveId     *            slave的ID     * @param writeOffset     *            地位     * @param writeValue     *            值     * @return 是否写入胜利     * @throws ModbusTransportException     * @throws ModbusInitException     */    public static boolean writeCoil(int slaveId, int writeOffset, boolean writeValue)            throws ModbusTransportException, ModbusInitException {        // 获取master        ModbusMaster tcpMaster = getMaster();        // 创立申请        WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);        // 发送申请并获取响应对象        WriteCoilResponse response = (WriteCoilResponse) tcpMaster.send(request);        if (response.isException()) {            return false;        } else {            return true;        }    }    /**     * 写[01 Coil Status(0x)] 写多个 function ID = 15     *     * @param slaveId     *            slaveId     * @param startOffset     *            开始地位     * @param bdata     *            写入的数据     * @return 是否写入胜利     * @throws ModbusTransportException     * @throws ModbusInitException     */    public static boolean writeCoils(int slaveId, int startOffset, boolean[] bdata)            throws ModbusTransportException, ModbusInitException {        // 获取master        ModbusMaster tcpMaster = getMaster();        // 创立申请        WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);        // 发送申请并获取响应对象        WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request);        if (response.isException()) {            return false;        } else {            return true;        }    }    /***     * 写[03 Holding Register(4x)] 写一个 function ID = 6     *     * @param slaveId     * @param writeOffset     * @param writeValue     * @return     * @throws ModbusTransportException     * @throws ModbusInitException     */    public static boolean writeRegister(int slaveId, int writeOffset, short writeValue)            throws ModbusTransportException, ModbusInitException {        // 获取master        ModbusMaster tcpMaster = getMaster();        // 创立申请对象        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);        WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request);        if (response.isException()) {            log.error(response.getExceptionMessage());            return false;        } else {            return true;        }    }    /**     *     * 写入[03 Holding Register(4x)]写多个 function ID=16     *     * @param slaveId     *            modbus的slaveID     * @param startOffset     *            起始地位偏移量值     * @param sdata     *            写入的数据     * @return 返回是否写入胜利     * @throws ModbusTransportException     * @throws ModbusInitException     */    public static boolean writeRegisters(int slaveId, int startOffset, short[] sdata)            throws ModbusTransportException, ModbusInitException {        // 获取master        ModbusMaster tcpMaster = getMaster();        // 创立申请对象        WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata);        // 发送申请并获取响应对象        ModbusResponse response = tcpMaster.send(request);        if (response.isException()) {            log.error(response.getExceptionMessage());            return false;        } else {            return true;        }    }    /**     * 写入数字类型的模拟量(如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long)     *     * @param slaveId     * @param offset     * @param value     *            写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long     * @param registerCount     *            ,com.serotonin.modbus4j.code.DataType     * @throws ModbusTransportException     * @throws ErrorResponseException     * @throws ModbusInitException     */    public static void writeHoldingRegister(int slaveId, int offset, Number value, int dataType)            throws ModbusTransportException, ErrorResponseException, ModbusInitException {        // 获取master        ModbusMaster tcpMaster = getMaster();        // 类型        BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);        tcpMaster.setValue(locator, value);    }    public static void main(String[] args) {        try {            //@formatter:off            // 测试01//            boolean t01 = writeCoil(1, 0, true);//            System.out.println("T01:" + t01);            // 测试02//            boolean t02 = writeCoils(1, 0, new boolean[] { true, false, true });//            System.out.println("T02:" + t02);            // 测试03//            short v = -3;//            boolean t03 = writeRegister(1, 0, v);//            System.out.println("T03:" + t03);            // 测试04//            boolean t04 = writeRegisters(1, 0, new short[] { -3, 3, 9 });//            System.out.println("t04:" + t04);            //写模拟量            writeHoldingRegister(1,0, 10.1f, DataType.FOUR_BYTE_FLOAT);            //@formatter:on        } catch (Exception e) {            e.printStackTrace();        }    }}

应用modbus-master-tcp

modbus-master-tcp我的项目的底层是基于netty框架开发。人造的反对异步解决。在性能方面有很好的晋升。

maven配置
<dependency>            <groupId>com.digitalpetri.modbus</groupId>            <artifactId>modbus-master-tcp</artifactId>            <version>1.1.0</version>        </dependency>
java应用modbus-master-tcp读取数据
package com.ioufev;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;import com.digitalpetri.modbus.codec.Modbus;import com.digitalpetri.modbus.master.ModbusTcpMaster;import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;import com.digitalpetri.modbus.requests.ReadCoilsRequest;import com.digitalpetri.modbus.requests.ReadDiscreteInputsRequest;import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;import com.digitalpetri.modbus.requests.ReadInputRegistersRequest;import com.digitalpetri.modbus.responses.ReadCoilsResponse;import com.digitalpetri.modbus.responses.ReadDiscreteInputsResponse;import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;import com.digitalpetri.modbus.responses.ReadInputRegistersResponse;import io.netty.buffer.ByteBuf;import io.netty.util.ReferenceCountUtil;/*** * modbus TCP协定Java通信读取例子 * * */public class ModbusMasterTCPDemo {    static ModbusTcpMaster master;    /**     * 获取TCP协定的Master     *     * @return     */    public static void initModbusTcpMaster() {        if (master == null) {            // 创立配置            ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("10.211.55.4").setPort(502).build();            master = new ModbusTcpMaster(config);        }    }    /***     * 开释资源     */    public static void release() {        if (master != null) {            master.disconnect();        }        Modbus.releaseSharedResources();    }    /**     * 读取Coils开关量     *     * @param address     *            寄存器开始地址     * @param quantity     *            数量     * @param unitId     *            ID     * @return 读取值     * @throws InterruptedException     *             异样     * @throws ExecutionException     *             异样     */    public static Boolean readCoils(int address, int quantity, int unitId)            throws InterruptedException, ExecutionException {        Boolean result = null;        CompletableFuture<ReadCoilsResponse> future = master.sendRequest(new ReadCoilsRequest(address, quantity),                unitId);        ReadCoilsResponse readCoilsResponse = future.get();// 工具类做的同步返回.理论应用举荐联合业务进行异步解决        if (readCoilsResponse != null) {            ByteBuf buf = readCoilsResponse.getCoilStatus();            result = buf.readBoolean();            ReferenceCountUtil.release(readCoilsResponse);        }        return result;    }    /**     * 读取readDiscreteInputs开关量     *     * @param address     *            寄存器开始地址     * @param quantity     *            数量     * @param unitId     *            ID     * @return 读取值     * @throws InterruptedException     *             异样     * @throws ExecutionException     *             异样     */    public static Boolean readDiscreteInputs(int address, int quantity, int unitId)            throws InterruptedException, ExecutionException {        Boolean result = null;        CompletableFuture<ReadDiscreteInputsResponse> future = master                .sendRequest(new ReadDiscreteInputsRequest(address, quantity), unitId);        ReadDiscreteInputsResponse discreteInputsResponse = future.get();// 工具类做的同步返回.理论应用举荐联合业务进行异步解决        if (discreteInputsResponse != null) {            ByteBuf buf = discreteInputsResponse.getInputStatus();            result = buf.readBoolean();            ReferenceCountUtil.release(discreteInputsResponse);        }        return result;    }    /**     * 读取HoldingRegister数据     *     * @param address     *            寄存器地址     * @param quantity     *            寄存器数量     * @param unitId     *            id     * @return 读取后果     * @throws InterruptedException     *             异样     * @throws ExecutionException     *             异样     */    public static Number readHoldingRegisters(int address, int quantity, int unitId)            throws InterruptedException, ExecutionException {        Number result = null;        CompletableFuture<ReadHoldingRegistersResponse> future = master                .sendRequest(new ReadHoldingRegistersRequest(address, quantity), unitId);        ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get();// 工具类做的同步返回.理论应用举荐联合业务进行异步解决        if (readHoldingRegistersResponse != null) {            ByteBuf buf = readHoldingRegistersResponse.getRegisters();            result = buf.readFloat();            ReferenceCountUtil.release(readHoldingRegistersResponse);        }        return result;    }    /**     * 读取InputRegisters模拟量数据     *     * @param address     *            寄存器开始地址     * @param quantity     *            数量     * @param unitId     *            ID     * @return 读取值     * @throws InterruptedException     *             异样     * @throws ExecutionException     *             异样     */    public static Number readInputRegisters(int address, int quantity, int unitId)            throws InterruptedException, ExecutionException {        Number result = null;        CompletableFuture<ReadInputRegistersResponse> future = master                .sendRequest(new ReadInputRegistersRequest(address, quantity), unitId);        ReadInputRegistersResponse readInputRegistersResponse = future.get();// 工具类做的同步返回.理论应用举荐联合业务进行异步解决        if (readInputRegistersResponse != null) {            ByteBuf buf = readInputRegistersResponse.getRegisters();            result = buf.readDouble();            ReferenceCountUtil.release(readInputRegistersResponse);        }        return result;    }    public static void main(String[] args) {        try {            // 初始化资源            initModbusTcpMaster();            // 执行操作            // 读取开关量            System.out.println(readCoils(0, 1, 1));            System.out.println(readDiscreteInputs(0, 1, 1));            System.out.println(readDiscreteInputs(1, 1, 1));            // 读取模拟量            System.out.println(readHoldingRegisters(0, 2, 1));            System.out.println(readHoldingRegisters(2, 2, 1));            System.out.println(readHoldingRegisters(4, 2, 1));            System.out.println(readInputRegisters(2, 4, 1));            System.out.println(readInputRegisters(6, 4, 1));            // 开释资源            release();        } catch (Exception e) {            e.printStackTrace();        }    }}

总结

  • jlibmodbus:集成多个串口通信开源库,有意思
  • modbus4j:很有名
  • modbus-master-tcp:底层netty,反对异步
  • Jamod:Github上安卓开发modbus通信用的多