Java语言实现Modbus协议通过用串口读取数据(以RTU为例)

和豆包,deepSeek友好的聊了一天,也是终于实现了这一个功能。。。

首先下载依赖

XML 复制代码
<dependency>
            <groupId>com.infiniteautomation</groupId>
            <artifactId>modbus4j</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.fazecast</groupId>
            <artifactId>jserialcomm</artifactId>
            <version>2.10.4</version>
        </dependency>

完整的POM.xml文件如下

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>projectDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <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>
    <dependencies>
        <!--串口 jSerialComm 正常可用-->
        <dependency>
            <groupId>com.fazecast</groupId>
            <artifactId>jSerialComm</artifactId>
            <version>2.10.4</version>
        </dependency>

        <!-- 稳定版modbus4j -->
        <dependency>
            <groupId>com.infiniteautomation</groupId>
            <artifactId>modbus4j</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.fazecast</groupId>
            <artifactId>jserialcomm</artifactId>
            <version>2.10.4</version>
        </dependency>

    </dependencies>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
</project>

然后,创建两个工具类

java 复制代码
package org.modbus.util;

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;
import com.serotonin.modbus4j.serial.SerialPortWrapper;

import java.util.ArrayList;
import java.util.List;

public class Modbus4jUtils {
    /**
     * 工厂。
     */
    static ModbusFactory modbusFactory;

    static {
        if (modbusFactory == null) {
            modbusFactory = new ModbusFactory();
        }
    }

    /**
     * 获取master
     *
     * @return
     * @throws ModbusInitException
     */
    public static ModbusMaster getMaster(SerialPortWrapper wrapper, int type) {

        if (type == 1) {  //RTU
            return modbusFactory.createRtuMaster(wrapper);
        } else if (type == 2) {  //ASC
            return modbusFactory.createAsciiMaster(wrapper);
        } else {
            throw new RuntimeException("参数有误");
        }
    }

    /**
     * 获取master
     *
     * @return
     * @throws ModbusInitException
     */
    public static ModbusMaster getMaster(IpParameters params, Boolean keepAlive) {

        if (keepAlive != null) {   //TCP
            return modbusFactory.createTcpMaster(params, keepAlive);
        } else return modbusFactory.createUdpMaster(params);    //UDP

    }

    /**
     * 读取[01 Coil Status 0x]类型 开关数据
     *
     * @param slaveId slaveId
     * @param startAddress   起始寄存器位置
     * @param numberOfPoints   读取寄存器数量
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     * @throws ModbusInitException      异常
     */
    public static BatchResults<Integer>  readCoilStatus(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints)
            throws ModbusTransportException, ErrorResponseException {
        BatchRead<Integer> batch = new BatchRead<Integer>();
        for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){
            batch.addLocator(j, BaseLocator.coilStatus(slaveId, i));
        }
        batch.setContiguousRequests(false);
        BatchResults<Integer> results = master.send(batch);
        return results;

    }

    /**
     * 读取[02 Input Status 1x]类型 开关数据
     *
     * @param slaveId
     * @param startAddress   起始寄存器位置
     * @param numberOfPoints   读取寄存器数量
     * @return
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public static BatchResults<Integer> readInputStatus(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints)
            throws ModbusTransportException, ErrorResponseException{
        BatchRead<Integer> batch = new BatchRead<Integer>();
        for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){
            batch.addLocator(j, BaseLocator.inputStatus(slaveId, i));
        }
        batch.setContiguousRequests(false);
        BatchResults<Integer> results = master.send(batch);
        return results;

    }

    /**
     * 读取[03 Holding Register类型 2x]模拟量数据
     *
     * @param slaveId  slave Id
     * @param startAddress   起始寄存器位置
     * @param numberOfPoints   读取寄存器数量
     * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     */
    public static BatchResults<Integer> readHoldingRegister(ModbusMaster master, int slaveId, int startAddress, int numberOfPoints, int dataType)
            throws ModbusTransportException, ErrorResponseException {

        BatchRead<Integer> batch = new BatchRead<Integer>();
        for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){
            batch.addLocator(j, BaseLocator.holdingRegister(slaveId, i, dataType));
        }
        batch.setContiguousRequests(false);
        BatchResults<Integer> results = master.send(batch);
        return results;
    }


    /**
     * 读取[04 Input Registers 3x]类型 模拟量数据
     *
     * @param slaveId  slaveId
     * @param startAddress   起始寄存器位置
     * @param numberOfPoints   读取寄存器数量
     * @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
     * @return 返回结果
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     */
    public static BatchResults<Integer> readInputRegisters(ModbusMaster master, int slaveId,  int startAddress, int numberOfPoints, int dataType)
            throws ModbusTransportException, ErrorResponseException {
        BatchRead<Integer> batch = new BatchRead<Integer>();
        for (int i=startAddress,j=0;i<numberOfPoints;i++,j++){
            batch.addLocator(j, BaseLocator.inputRegister(slaveId, i, dataType));
        }
        batch.setContiguousRequests(false);
        BatchResults<Integer> results = master.send(batch);
        return results;

    }

    /**
     * 批量读取使用方法
     *
     * @throws ModbusTransportException
     * @throws ErrorResponseException
     * @throws ModbusInitException
     */
    public static void batchRead(ModbusMaster master) 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));


        batch.setContiguousRequests(false);
        BatchResults<Integer> results = master.send(batch);
        System.out.println(results.getValue(0));
        System.out.println(results.getValue(1));
    }


}

这个工具类封装了创建工厂,读取数据的所有方法

java 复制代码
package org.modbus.util;

import com.serotonin.modbus4j.serial.SerialPortWrapper;
import com.fazecast.jSerialComm.SerialPort;
import java.io.InputStream;
import java.io.OutputStream;

public class SerialPortWrapperJSerialImpl implements SerialPortWrapper {
    private SerialPort serialPort;
    private final String portName;
    private final int baudRate;
    private final int dataBits;
    private final int stopBits;
    private final int parity;

    public SerialPortWrapperJSerialImpl(String portName, int baudRate, int dataBits, int stopBits, int parity) {
        this.portName = portName;
        this.baudRate = baudRate;
        this.dataBits = dataBits;
        this.stopBits = stopBits;
        this.parity = parity;
    }



    @Override
    public int getBaudRate() {
        return baudRate;
    }

    @Override
    public int getDataBits() {
        return dataBits;
    }

    @Override
    public int getStopBits() {
        return stopBits;
    }

    @Override
    public int getParity() {
        return parity;
    }

    @Override
    public int getFlowControlIn() {
        return 0;
    }

    @Override
    public int getFlowControlOut() {
        return 0;
    }

    @Override
    public void open() throws Exception {
        if (serialPort != null && serialPort.isOpen()) {
            return;
        }
        serialPort = SerialPort.getCommPort(portName);
        serialPort.setBaudRate(baudRate);
        serialPort.setNumDataBits(dataBits);
        serialPort.setNumStopBits(stopBits);
        // 转换modbus4j parity常量到jSerialComm
        switch (parity) {
            case 0:
                serialPort.setParity(SerialPort.NO_PARITY);
                break;
            case 1:
                serialPort.setParity(SerialPort.ODD_PARITY);
                break;
            case 2:
                serialPort.setParity(SerialPort.EVEN_PARITY);
                break;
            default:
                serialPort.setParity(SerialPort.NO_PARITY);
        }
        // 关闭硬件流控,485不需要
        serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
        // 超时防止阻塞
        serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 1000, 0);
        serialPort.openPort();
    }

    @Override
    public void close() throws Exception {
        if (serialPort != null && serialPort.isOpen()) {
            serialPort.closePort();
        }
        serialPort = null;
    }


    @Override
    public InputStream getInputStream() {
        return serialPort.getInputStream();
    }

    @Override
    public OutputStream getOutputStream() {
        return serialPort.getOutputStream();
    }
}

这个工具用来初始化串口

好了,一切准备就绪,开始测试

java 复制代码
package org.modbus;

import com.fazecast.jSerialComm.SerialPort;
import com.serotonin.modbus4j.BatchResults;
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.serial.SerialPortWrapper;
import org.modbus.util.Modbus4jUtils;
import org.modbus.util.SerialPortWrapperJSerialImpl;


public class Demo02 {
    public static void main(String[] args) throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        redTest();
    }
    private static void redTest() throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 1. 实例化JSSC串口包装类,8N1无校验
        SerialPortWrapper wrapper = new SerialPortWrapperJSerialImpl("COM1",9600,8,1,0);
        ModbusMaster master = Modbus4jUtils.getMaster(wrapper, 1);
        master.init();

        //读取从站1的保持型寄存器0~9
        BatchResults<Integer> res = Modbus4jUtils.readHoldingRegister(master, 1, 0, 9, DataType.TWO_BYTE_INT_UNSIGNED);
        System.out.println(res);

        //读取从站1的线圈状态0~7
        BatchResults<Integer> coils = Modbus4jUtils.readCoilStatus(master,1,0,8);
        System.out.println(coils);

        //读取从站1的输入线圈0~7
        BatchResults<Integer> inputs = Modbus4jUtils.readInputStatus(master,1,0,8);
        System.out.println(inputs);

        //读取从站5的输入寄存器0~4
        BatchResults<Integer> inputRegisters = Modbus4jUtils.readInputRegisters(master,5,0,5,DataType.TWO_BYTE_INT_UNSIGNED);
        System.out.println(inputRegisters);


    }
}

配置主站(我这里使用的是虚拟的)

然后我们run一下,看一下效果

家人们,牛逼坏了我,搞了一天啊!!!

相关推荐
小锋java12342 小时前
Maven 4 要来了:15 年后,Java 构建工具迎来"彻底重构"
java·maven
卓怡学长2 小时前
w269基于spring boot + vue 候鸟监测数据管理系统
java·数据库·spring boot·spring·intellij-idea
孫治AllenSun2 小时前
【MDC】使用 MDC 封装一个 LogMdcUtil 工具类
java·spring boot·后端
济*沧*海2 小时前
BaseMapper的介绍
java·windows·tomcat
plainGeekDev3 小时前
依赖管理 → Version Catalog
android·java·kotlin
淘源码d4 小时前
医学影像云PACS系统——PACS诊断工作站
java·源码·影像处理·医院管理系统·影像工作站·云pacs系统·dicom 标准通信
NiceCloud喜云5 小时前
Anthropic 一周三连发:Cowork 多端、Fable 5 按需付费、J-space 论文的技术解读
java·服务器·网络·人工智能·ai
SamDeepThinking5 小时前
组合还是继承?我在项目里主要看这两个判断
java·后端·面试
momo5 小时前
JAVA基础知识
java·开发语言