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

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

相关推荐
yxlalm42 分钟前
Spring AI+RAG 01-项目背景与技术选型
java·人工智能·spring
程序员清风1 小时前
Java 后端高并发设计:线程池、限流、熔断与降级
java·数据库·oracle
隐擎fox2 小时前
深入理解网络传输层安全:TLS 指纹识别(JA3/JA4)原理与 Python 协议层检测实战
爬虫·python·网络协议·安全·网络安全·https
游戏开发爱好者82 小时前
网络连接排查指南,谁在电脑后台偷跑流量,哪些程序在联网
网络协议·计算机网络·网络安全·ios·adb·https·udp
Jeremy_WW2 小时前
QSFP/QSFP-DD/OSFP 通用管理接口规范(CMIS)解读:13 Page 9Fh
网络·网络协议·信息与通信·光模块·cmis
Seoyoneh2 小时前
Agentic Workflow编排架构:云客服从“被动响应”迈向“主动执行”的技术实现
java·开发语言·架构
海南java第二人2 小时前
对外 API 如何保证幂等性?插入数据 / 上传表格场景全方案汇总
java·幂等性
tryxr2 小时前
Chat2Excel 文件服务模块上传文件功能开发
java·项目·oss·easyexcel·文件服务
Tangyuewei2 小时前
Java 写 Agent:模型只是组件
java·开发语言
彧azz3 小时前
Java学习语法篇:变量
java·学习