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一下,看一下效果

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

相关推荐
IKUN家族8 小时前
Spring MVC(一)
java·spring·mvc
老马识途2.09 小时前
关于跨域问题的总结
java·前端
都叫我大帅哥10 小时前
Java日期时间三十年战争:从Date考古到LocalDateTime革命,以及数据库与前端的那点事儿
java
Muscleheng10 小时前
SpringBoot 集成 DeepSeek 实现 RAG 文档问答
java·spring boot·ai·springai
2501_9364156911 小时前
可变参数&综合练习&斗地主游戏
java·windows·游戏
极客先躯12 小时前
高级java每日一道面试题-2026年05月03日-实战篇[Docker]-如何实现容器化环境的数据加密?
java·运维·docker·容器·金融·加解密·高级面试
xqqxqxxq12 小时前
Java Socket 多人聊天室(私聊+群聊)技术笔记(V4版本)
java·网络·笔记
wang090713 小时前
自己动手写一个spring之aop_1
java·后端·spring
国科安芯13 小时前
FreeRTOS RISC-V 浮点上下文切换移植:在 IAR 工程中完整保存 FPU 寄存器
java·开发语言·单片机·嵌入式硬件·算法·系统架构·risc-v
deng120413 小时前
【Thread类的用法(1)-- Thread类的基本用法】
java·java-ee