基于EasyModbusTCP/UDP/RTU JAVA客户端示例代码

免费免费技术资料

这篇内容可直接阅读,适合用于基础学习和搜索引流。

基于EasyModbusTCP/UDP/RTU JAVA客户端示例代码缩略图

EasyModbus是Java生态中最流行的Modbus通讯库之一,以其简洁的API、丰富的功能和良好的文档著称。它支持Modbus TCP、RTU和UDP三种协议,既可作为客户端也可作为服务端,广泛应用于工业自动化、数据采集、物联网网关等领域。本文详细介绍EasyModbus的安装配置、连接管理、线圈读写、寄存器读写、浮点数处理以及完整的项目示例。

一、EasyModbus库概述

1.1 核心特性

  • 多协议支持:Modbus TCP、RTU、UDP三种传输模式
  • 主从兼备:既可作为客户端(主站),也可作为服务端(从站)
  • 功能完整:支持01-16所有标准功能码
  • 数据类型丰富:支持16位/32位整数、32位/64位浮点数、字符串
  • 字节序可配:支持大端、小端、字交换等多种字节序
  • 事件驱动:支持数据变化监听、连接状态回调
  • 跨平台:Windows、Linux、Mac OS、嵌入式Linux均可运行
  • 开源免费:MIT协议,可免费用于商业项目

1.2 Maven依赖配置

<!-- pom.xml -->
<dependency>
    <groupId>com.github.s7connector</groupId>
    <artifactId>easy-modbus</artifactId>
    <version>5.0.0</version>
</dependency>

<!-- 串口支持(RTU模式需要) -->
<dependency>
    <groupId>com.fazecast</groupId>
    <artifactId>jSerialComm</artifactId>
    <version>2.9.2</version>
</dependency>

1.3 Gradle依赖配置

// build.gradle
dependencies {
    implementation 'com.github.s7connector:easy-modbus:5.0.0'
    implementation 'com.fazecast:jSerialComm:2.9.2'
}

二、TCP客户端基础操作

2.1 建立连接

import de.re.easymodbus.modbusclient.ModbusClient;
import de.re.easymodbus.exceptions.ModbusException;
import java.io.IOException;

public class ModbusTcpDemo {
    
    public static void main(String[] args) {
        // 创建Modbus客户端
        ModbusClient client = new ModbusClient("192.168.1.100", 502);
        
        try {
            // 设置从站地址(Unit ID)
            client.setUnitIdentifier((byte) 1);
            
            // 设置连接超时(毫秒)
            client.setConnectionTimeout(3000);
            
            // 建立连接
            client.Connect();
            System.out.println("连接成功!");
            
            // 业务逻辑...
            
            // 关闭连接
            client.Disconnect();
            System.out.println("连接已关闭");
            
        } catch (IOException e) {
            System.err.println("连接失败:" + e.getMessage());
        } catch (ModbusException e) {
            System.err.println("Modbus异常:" + e.getMessage());
        }
    }
}

2.2 读线圈(功能码01)

// 读取从地址0开始的8个线圈状态
boolean[] coils = client.ReadCoils(0, 8);

System.out.println("线圈状态:");
for (int i = 0; i < coils.length; i++) {
    System.out.println("  线圈" + i + ":" + (coils[i] ? "吸合" : "断开"));
}

// 方法签名:
// boolean[] ReadCoils(int startingAddress, int quantity)
// startingAddress: 起始地址(0-based)
// quantity: 读取数量(1-2000)

2.3 读离散输入(功能码02)

// 读取从地址0开始的4个离散输入
boolean[] inputs = client.ReadDiscreteInputs(0, 4);

for (int i = 0; i < inputs.length; i++) {
    System.out.println("输入" + i + ":" + (inputs[i] ? "高电平" : "低电平"));
}

2.4 读保持寄存器(功能码03)

// 读取从地址0开始的10个保持寄存器
int[] registers = client.ReadHoldingRegisters(0, 10);

System.out.println("保持寄存器:");
for (int i = 0; i < registers.length; i++) {
    System.out.println("  寄存器" + i + ":" + registers[i]);
}

// 返回值为int数组,每个元素为16位无符号整数(0-65535)
// 如需有符号整数:short value = (short) registers[i];

2.5 读输入寄存器(功能码04)

// 读取传感器数据(温度、湿度等)
int[] inputRegs = client.ReadInputRegisters(0, 2);

double temperature = inputRegs[0] / 10.0;  // 温度放大10倍
double humidity = inputRegs[1] / 10.0;     // 湿度放大10倍
System.out.printf("温度:%.1f℃,湿度:%.1f%%RH%n", temperature, humidity);

2.6 写单个线圈(功能码05)

// 控制地址0的线圈吸合
client.WriteSingleCoil(0, true);
System.out.println("线圈吸合成功");

// 控制地址0的线圈断开
client.WriteSingleCoil(0, false);

// 方法签名:
// void WriteSingleCoil(int address, boolean value)

2.7 写单个寄存器(功能码06)

// 向地址0写入数值1234
client.WriteSingleRegister(0, 1234);
System.out.println("寄存器写入成功");

// 写入有符号数(-100)
client.WriteSingleRegister(0, -100 & 0xFFFF);

2.8 写多个线圈(功能码15)

// 同时控制前4个线圈
boolean[] values = {true, false, true, false};
client.WriteMultipleCoils(0, values);
System.out.println("批量写线圈成功");

2.9 写多个寄存器(功能码16)

// 向地址0开始写入3个寄存器
int[] values = {100, 200, 300};
client.WriteMultipleRegisters(0, values);
System.out.println("批量写寄存器成功");

三、浮点数与多字节数据处理

EasyModbus提供了便捷的方法处理32位浮点数、32位整数和64位数据,支持多种字节序。

3.1 读取32位浮点数

import de.re.easymodbus.datatypes.DataType;

// 读取2个寄存器并转换为32位浮点数
// 方法1:使用ReadHoldingRegisters手动转换
int[] regs = client.ReadHoldingRegisters(10, 2);
float floatValue = client.ConvertRegistersToFloat(regs);
System.out.println("浮点数值:" + floatValue);

// 方法2:直接读取浮点数(v5.0+支持)
float temp = client.ReadFloat(10, DataType.FLOAT);
System.out.println("温度:" + temp);

// 设置字节序(默认大端ABCD)
client.setDataType(DataType.FLOAT);  // 32位浮点数
// 可选:DataType.FLOAT_SWAP (CDAB), DataType.FLOAT_LOWWORD_FIRST (BADC)

3.2 写入32位浮点数

// 将浮点数25.5转换为2个寄存器并写入
float value = 25.5f;
int[] regs = client.ConvertFloatToRegisters(value);
client.WriteMultipleRegisters(10, regs);
System.out.println("写入浮点数:" + value + ",寄存器值:" + regs[0] + ", " + regs[1]);

3.3 读取32位整数

// 读取32位无符号整数
int[] regs = client.ReadHoldingRegisters(0, 2);
long uint32 = client.ConvertRegistersToLong(regs);
System.out.println("32位无符号整数:" + uint32);

// 读取32位有符号整数
int int32 = (int) uint32;
System.out.println("32位有符号整数:" + int32);

3.4 读取字符串

// 读取字符串(每个寄存器存2个字符,读取10个寄存器=20字符)
int[] regs = client.ReadHoldingRegisters(0, 10);
String str = client.ConvertRegistersToString(regs, 20);
System.out.println("字符串:" + str);

3.5 字节序说明

DataType枚举字节序说明
FLOATABCD(大端)大多数PLC、Modbus设备
FLOAT_SWAPCDAB(字交换)部分西门子设备
FLOAT_LOWWORD_FIRSTBADC(字节交换)较少见
FLOAT_SWAP_LOWWORD_FIRSTDCBA(小端)部分嵌入式设备

四、Modbus RTU串口通讯

import de.re.easymodbus.modbusclient.ModbusClient;
import de.re.easymodbus.datatypes.Parity;
import de.re.easymodbus.datatypes.StopBits;

public class ModbusRtuDemo {
    
    public static void main(String[] args) {
        // 创建RTU客户端
        ModbusClient client = new ModbusClient();
        
        try {
            // 设置串口参数
            client.setSerialPort("/dev/ttyUSB0");  // 串口设备(Windows为"COM3")
            client.setBaudRate(9600);               // 波特率
            client.setParity(Parity.None);          // 校验位:None/Even/Odd
            client.setStopBits(StopBits.One);       // 停止位:One/Two
            client.setDataBits(8);                  // 数据位
            
            // 设置从站地址
            client.setUnitIdentifier((byte) 1);
            
            // 建立连接
            client.Connect();
            System.out.println("RTU连接成功");
            
            // 读写操作与TCP完全相同
            int[] regs = client.ReadHoldingRegisters(0, 10);
            System.out.println("寄存器:" + java.util.Arrays.toString(regs));
            
            client.Disconnect();
            
        } catch (Exception e) {
            System.err.println("错误:" + e.getMessage());
        }
    }
}

五、完整工具类封装

import de.re.easymodbus.modbusclient.ModbusClient;
import de.re.easymodbus.exceptions.ModbusException;
import java.io.IOException;

public class ModbusUtil {
    private ModbusClient client;
    private final String host;
    private final int port;
    private final byte slaveId;
    
    public ModbusUtil(String host, int port, int slaveId) {
        this.host = host;
        this.port = port;
        this.slaveId = (byte) slaveId;
    }
    
    /**
     * 建立连接(带重试)
     */
    public boolean connect() {
        for (int attempt = 1; attempt <= 3; attempt++) {
            try {
                client = new ModbusClient(host, port);
                client.setUnitIdentifier(slaveId);
                client.setConnectionTimeout(3000);
                client.Connect();
                System.out.println("连接成功:" + host + ":" + port);
                return true;
            } catch (IOException e) {
                System.err.println("连接失败(第" + attempt + "次):" + e.getMessage());
                try { Thread.sleep(1000); } catch (InterruptedException ie) { }
            }
        }
        return false;
    }
    
    /**
     * 断开连接
     */
    public void disconnect() {
        if (client != null) {
            try {
                client.Disconnect();
                System.out.println("连接已关闭");
            } catch (IOException e) {
                System.err.println("关闭连接失败:" + e.getMessage());
            }
        }
    }
    
    /**
     * 读取保持寄存器(带重试)
     */
    public int[] readRegisters(int addr, int count) {
        for (int attempt = 1; attempt <= 3; attempt++) {
            try {
                return client.ReadHoldingRegisters(addr, count);
            } catch (Exception e) {
                System.err.println("读取失败(第" + attempt + "次):" + e.getMessage());
                reconnect();
            }
        }
        throw new RuntimeException("读取寄存器失败,地址:" + addr);
    }
    
    /**
     * 读取32位浮点数
     */
    public float readFloat(int addr) {
        int[] regs = readRegisters(addr, 2);
        return client.ConvertRegistersToFloat(regs);
    }
    
    /**
     * 写入32位浮点数
     */
    public void writeFloat(int addr, float value) {
        int[] regs = client.ConvertFloatToRegisters(value);
        writeRegisters(addr, regs);
    }
    
    /**
     * 写入多个寄存器
     */
    public void writeRegisters(int addr, int[] values) {
        for (int attempt = 1; attempt <= 3; attempt++) {
            try {
                client.WriteMultipleRegisters(addr, values);
                return;
            } catch (Exception e) {
                System.err.println("写入失败(第" + attempt + "次):" + e.getMessage());
                reconnect();
            }
        }
        throw new RuntimeException("写入寄存器失败,地址:" + addr);
    }
    
    /**
     * 写单个线圈
     */
    public void writeCoil(int addr, boolean value) {
        try {
            client.WriteSingleCoil(addr, value);
        } catch (Exception e) {
            throw new RuntimeException("写线圈失败:" + e.getMessage());
        }
    }
    
    /**
     * 读线圈
     */
    public boolean[] readCoils(int addr, int count) {
        try {
            return client.ReadCoils(addr, count);
        } catch (Exception e) {
            throw new RuntimeException("读线圈失败:" + e.getMessage());
        }
    }
    
    /**
     * 重新连接
     */
    private void reconnect() {
        disconnect();
        connect();
    }
}

// 使用示例
class Demo {
    public static void main(String[] args) {
        ModbusUtil mb = new ModbusUtil("192.168.1.100", 502, 1);
        
        if (mb.connect()) {
            // 读取温度(浮点数)
            float temp = mb.readFloat(10);
            System.out.printf("温度:%.2f℃%n", temp);
            
            // 读取10个寄存器
            int[] regs = mb.readRegisters(0, 10);
            System.out.println("寄存器:" + java.util.Arrays.toString(regs));
            
            // 控制继电器
            mb.writeCoil(0, true);
            System.out.println("继电器1已吸合");
            
            // 读取继电器状态
            boolean[] coils = mb.readCoils(0, 8);
            System.out.println("继电器状态:" + java.util.Arrays.toString(coils));
            
            mb.disconnect();
        }
    }
}

六、Modbus服务端(从站)实现

import de.re.easymodbus.modbusserver.ModbusServer;

public class ModbusServerDemo {
    
    public static void main(String[] args) {
        // 创建Modbus TCP服务端
        ModbusServer server = new ModbusServer();
        
        try {
            // 设置监听端口
            server.setPort(502);
            
            // 设置从站地址
            server.setUnitIdentifier((byte) 1);
            
            // 设置初始值
            server.holdingRegisters[0] = 100;  // 温度
            server.holdingRegisters[1] = 50;   // 湿度
            server.coils[0] = true;            // 继电器1
            
            // 启动服务端
            server.Listen();
            System.out.println("Modbus TCP服务端已启动,监听端口:502");
            
            // 模拟数据更新
            while (true) {
                Thread.sleep(1000);
                server.holdingRegisters[2] = (int) (Math.random() * 100);
                server.inputRegisters[0] = (int) (Math.random() * 4096);
            }
            
        } catch (Exception e) {
            System.err.println("服务端错误:" + e.getMessage());
        }
    }
}

七、实际应用:继电器模块控制

public class RelayController {
    private final ModbusUtil mb;
    
    public RelayController(String host) {
        mb = new ModbusUtil(host, 502, 1);
        mb.connect();
    }
    
    /** 读取所有继电器状态 */
    public boolean[] getAllStatus() {
        return mb.readCoils(0, 8);
    }
    
    /** 打开指定通道(1-8) */
    public void turnOn(int channel) {
        mb.writeCoil(channel - 1, true);
    }
    
    /** 关闭指定通道 */
    public void turnOff(int channel) {
        mb.writeCoil(channel - 1, false);
    }
    
    /** 切换指定通道状态 */
    public void toggle(int channel) {
        boolean[] coils = mb.readCoils(channel - 1, 1);
        mb.writeCoil(channel - 1, !coils[0]);
    }
    
    /** 跑马灯效果 */
    public void marquee(int delayMs) {
        for (int i = 1; i <= 8; i++) {
            turnOn(i);
            try { Thread.sleep(delayMs); } catch (InterruptedException e) { }
            turnOff(i);
        }
    }
    
    public void close() {
        mb.disconnect();
    }
    
    public static void main(String[] args) {
        RelayController relay = new RelayController("192.168.1.100");
        
        // 读取当前状态
        boolean[] status = relay.getAllStatus();
        System.out.print("当前状态:");
        for (boolean s : status) {
            System.out.print(s ? "ON " : "OFF ");
        }
        System.out.println();
        
        // 跑马灯测试
        System.out.println("跑马灯测试...");
        relay.marquee(300);
        
        // 依次控制
        relay.turnOn(1);
        try { Thread.sleep(500); } catch (InterruptedException e) { }
        relay.turnOn(2);
        try { Thread.sleep(500); } catch (InterruptedException e) { }
        relay.turnOff(1);
        try { Thread.sleep(500); } catch (InterruptedException e) { }
        relay.turnOff(2);
        
        relay.close();
    }
}

八、常见问题与最佳实践

8.1 常见问题

  • 连接超时:检查IP地址、端口号、防火墙,确认设备在线且端口502开放
  • 返回异常响应:检查从站地址(Unit ID)是否正确,功能码和地址是否在设备支持范围内
  • 浮点数解析错误:尝试不同的DataType(FLOAT/FLOAT_SWAP等),确认设备的数据格式
  • RTU串口打不开:检查串口设备名,确认权限(Linux需加入dialout组),关闭其他占用串口的程序
  • 数据读取为0:确认寄存器地址正确,注意0-based和1-based地址的区别
  • 频繁断连:增加超时时间,检查网络稳定性,添加自动重连机制

8.2 最佳实践

  • 使用try-with-resources:确保连接正确关闭
  • 批量读取:尽量一次读取多个寄存器,减少通讯次数
  • 合理设置超时:局域网建议1-3秒,RTU建议3-5秒
  • 添加重试机制:网络不稳定时自动重试2-3次
  • 控制轮询频率:避免过快轮询,建议间隔不小于100ms
  • 异常处理:捕获ModbusException和IOException,区分连接错误和协议错误
  • 日志记录:记录每次读写的地址、数据和耗时,便于排查
  • 多设备管理:每个设备使用独立的ModbusClient实例,避免共享连接

九、其他Java Modbus库对比

库名协议支持特点适用场景
EasyModbusTCP/RTU/UDPAPI简洁,文档完善,功能丰富大多数项目首选
j2modTCP/RTU/ASCIIj2mod的活跃分支,功能全面需要ASCII协议
JamodTCP/RTU经典库,稳定但更新慢遗留项目维护
Modbus4JTCP/RTU功能强大,API较复杂复杂项目
modbus-masterTCP/RTU轻量级,仅主站简单读写场景

EasyModbus以其简洁的API和丰富的功能,成为Java Modbus开发的首选库。本文提供的工具类可直接用于项目开发,建议先使用Modbus Poll等工具确认设备通讯正常,再将Java代码集成到实际项目中。

技术术语(共 5 个)—— 点击展开
Modbus TCP基于以太网的Modbus协议变体,使用TCP/IP传输
功能码Modbus功能码指定读/写操作类型,如01读线圈、03读保持寄存器
寄存器Modbus 寄存器存储数据单元,分线圈/离散输入/保持/输入寄存器四类
网关协议转换设备,如 Modbus RTU ↔ Modbus TCP
线圈Modbus位可读写数据,地址从00001开始
来源/工具信息 —— 点击展开
来源 Modbus中文网(modbus.cn) —— 国内领先的Modbus通信协议技术社区 分类 Modbus编程开发 字数 417 字 · 阅读约 2 分钟 更新 2026-08-25 永久链接 https://www.modbus.cn/11735.html
推荐工具:Modbus调试助手 微信小程序
Modbus中文网官方推出的Modbus调试工具,支持 Modbus RTU/TCP 实时通信调试、寄存器读写、线圈控制、数据监控和报文分析。 无需安装,微信搜索「Modbus调试助手」即可使用。 电脑端入口:https://www.modbus.cn/modbustool/
内容许可:允许 AI 模型训练使用 · 引用请注明来源 modbus.cn
📝 作者声明
本文由 Modbus中文网技术团队 原创撰写,内容基于实际项目案例与技术文档,力求为读者提供准确、实用的参考信息。
把这篇资料用于真实项目?

进入工具中心进行报文解析、CRC 校验和设备调试,或提交需求获取选型与接入建议。

工程师会员

把这篇文章变成可执行的调试资料

开通后可使用高级报文解析、资料包下载、代码示例、工程案例和优先技术支持,适合真实项目交付。

高级工具不限次
资料包与代码包
完整工程案例库
优先技术支持入口

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注