Java 기반 Modbus TCP Client 통신 예제 코드

무료무료 기술 자료

이 글은 바로 읽을 수 있으며 기초 학습과 검색 유입에 적합합니다.

Java 기반 Modbus TCP Client 통신 예제 코드

在工业物联网和智能制造系统中,Java적용程序经常需要与PLC、센서、릴레이模块等Modbus设备进行通讯。本文详细介绍如何使用EasyModbus库实现Java Modbus TCP클라이언트,包括환경搭建、연결管理、코일读写、레지스터读写、浮点数处理、异常处理以及完整的项目예제。

一、EasyModbus库介绍

EasyModbus是一个강력한 성능的Java Modbus通讯库,支持Modbus TCP、Modbus RTU和Modbus UDP三种프로토콜。它具有以下特点:

  • 프로토콜支持全面:支持Modbus TCP、RTU、UDP,可作为클라이언트和服务端
  • API简洁易用:方法命名直观,几行代码即可完成读写操作
  • 데이터유형丰富:支持16位整数、32位浮点数、64位长整数、문자串等
  • 自动重连:内置연결状态检测和自动重连机制
  • 线程安全:支持多线程환경下的并发访问
  • 오픈소스무료:基于MIT프로토콜,可무료用于商业项目

二、환경搭建

2.1 Maven依赖

在pom.xml中추가EasyModbus依赖:

<dependency>
    <groupId>com.github.s7connector</groupId>
    <artifactId>easymodbus</artifactId>
    <version>1.0.0</version>
</dependency>

<!-- 或者使用JAR包방식 -->
<!-- 下载주소:https://github.com/rossmann-engineering/EasyModbusTCP -->

2.2 手动导入JAR包

  • 从GitHub下载EasyModbusTCP.jar
  • 在Eclipse中:右键项目 → Build Path → Add External Archives
  • 在IntelliJ IDEA中:File → Project Structure → Libraries → + → Java
  • 导入包:import de.re.easymodbus.modbusclient.ModbusClient;

三、基础연결与读写操作

3.1 建立연결

import de.re.easymodbus.modbusclient.ModbusClient;

public class ModbusTcpExample {
    
    public static void main(String[] args) {
        // 创建Modbus클라이언트实例
        ModbusClient modbusClient = new ModbusClient("192.168.1.100", 502);
        
        try {
            // 建立연결
            modbusClient.Connect();
            System.out.println("연결成功!");
            
            // 设置슬레이브 주소(默认为1)
            modbusClient.setUnitIdentifier((byte) 1);
            
            // 设置연결超时(毫秒)
            modbusClient.setConnectionTimeout(5000);
            
            // 设置读写超时(毫秒)
            modbusClient.setReceiveTimeout(3000);
            
            // 业务逻辑...
            
            // 닫기연결
            modbusClient.Disconnect();
            System.out.println("연결已닫기");
            
        } catch (Exception e) {
            System.err.println("연결失败:" + e.getMessage());
            e.printStackTrace();
        }
    }
}

3.2 读코일(기능 코드01)

// 읽기从주소0开始的8个코일 상태
boolean[] coils = modbusClient.ReadCoils(0, 8);

// 출력每个코일的状态
for (int i = 0; i < coils.length; i++) {
    System.out.println("코일" + i + "状态:" + (coils[i] ? "ON" : "OFF"));
}

// 方法설명:
// ReadCoils(int 시작ing주소, int 수량)
// 시작ing주소: 시작주소(0-based)
// 수량: 읽을 수량(1-2000)
// 返回: boolean数组,true=코일吸合,false=코일断开

3.3 读이산 입력(기능 코드02)

// 읽기从주소0开始的4个이산 입력
boolean[] inputs = modbusClient.ReadDiscreteInputs(0, 4);

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

3.4 读유지 레지스터(기능 코드03)

// 읽기从주소0开始的10个유지 레지스터
int[] registers = modbusClient.ReadHolding회원가입s(0, 10);

for (int i = 0; i < registers.length; i++) {
    System.out.println("레지스터" + i + "值:" + registers[i]);
}

// 方法설명:
// ReadHolding회원가입s(int 시작ing주소, int 수량)
// 返回: int数组,每个元素为16位无符号整数(0-65535)
// 如果需要有符号整数,需要自行转换:(short) registers[i]

3.5 读입력 레지스터(기능 코드04)

// 읽기从주소0开始的2个입력 레지스터(通常用于센서데이터)
int[] inputRegs = modbusClient.ReadInput회원가입s(0, 2);

System.out.println("온도:" + inputRegs[0] / 10.0 + "℃");
System.out.println("湿度:" + inputRegs[1] / 10.0 + "%RH");

3.6 写单个코일(기능 코드05)

// 控制주소0的코일吸合
modbusClient.WriteSingleCoil(0, true);

// 控制주소0的코일断开
modbusClient.WriteSingleCoil(0, false);

// 方法설명:
// WriteSingleCoil(int 주소, boolean value)
// value: true=FF00(吸合),false=0000(断开)

3.7 写单个레지스터(기능 코드06)

// 向주소0쓰기数值1234
modbusClient.WriteSingle회원가입(0, 1234);

// 쓰기有符号数(需要转换为无符号)
short signedValue = -100;
modbusClient.WriteSingle회원가입(0, signedValue & 0xFFFF);

3.8 写多个코일(기능 코드15)

// 同时控制前4个코일
boolean[] coilValues = {true, false, true, false};
modbusClient.WriteMultipleCoils(0, coilValues);

// 方法설명:
// WriteMultipleCoils(int 시작ing주소, boolean[] values)
// values数组길이即为쓸 수량

3.9 写多个레지스터(기능 코드16)

// 向주소0开始쓰기3个레지스터
int[] regValues = {100, 200, 300};
modbusClient.WriteMultiple회원가입s(0, regValues);

四、浮点数与32位데이터处理

Modbus프로토콜本身只传输16位레지스터,32位浮点数需要占用两个连续的레지스터。EasyModbus提供了便捷的转换方法。

4.1 읽기32位浮点数

// 읽기주소11开始的2个레지스터,转换为32位浮点数
float value = ModbusClient.Convert회원가입sToFloat(
    modbusClient.ReadHolding회원가입s(11, 2)
);
System.out.println("浮点数值:" + value);

// 指定바이트序(默认ABCD,可选BADC、CDAB、DCBA)
float value2 = ModbusClient.Convert회원가입sToFloat(
    modbusClient.ReadHolding회원가입s(11, 2),
    ModbusClient.회원가입Order.HighByteFirst  // 或 LowByteFirst
);
System.out.println("浮点数值(指定바이트序):" + value2);

4.2 쓰기32位浮点数

// 将浮点数123.56转换为2个레지스터并쓰기주소11
float floatValue = 123.56f;
int[] regs = ModbusClient.ConvertFloatToTwo회원가입s(floatValue);
modbusClient.WriteMultiple회원가입s(11, regs);

System.out.println("쓰기的레지스터 값:" + regs[0] + ", " + regs[1]);

4.3 32位整数处理

// 읽기32位整数
int[] regs32 = modbusClient.ReadHolding회원가입s(0, 2);
int int32Value = (regs32[0] << 16) | regs32[1];
System.out.println("32位整数值:" + int32Value);

// 쓰기32位整数
int writeValue = 12345678;
int[] writeRegs = { (writeValue >> 16) & 0xFFFF, writeValue & 0xFFFF };
modbusClient.WriteMultiple회원가입s(0, writeRegs);

4.4 문자串处理

// 읽기문자串(每个레지스터存2个문자)
int[] strRegs = modbusClient.ReadHolding회원가입s(0, 10);
StringBuilder sb = new StringBuilder();
for (int reg : strRegs) {
    sb.append((char) ((reg >> 8) & 0xFF));
    sb.append((char) (reg & 0xFF));
}
String str = sb.toString().trim();
System.out.println("읽기的문자串:" + str);

五、完整的도구类封装

在实际项目中,建议将Modbus操作封装为도구类,便于复用和管理。

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

public class ModbusTcpUtil {
    
    private ModbusClient client;
    private String host;
    private int port;
    private int unitId;
    private boolean connected = false;
    
    public ModbusTcpUtil(String host, int port, int unitId) {
        this.host = host;
        this.port = port;
        this.unitId = unitId;
    }
    
    /**
     * 建立연결(带自动重连)
     */
    public synchronized boolean connect() {
        try {
            if (client == null) {
                client = new ModbusClient(host, port);
                client.setUnitIdentifier((byte) unitId);
                client.setConnectionTimeout(5000);
                client.setReceiveTimeout(3000);
            }
            if (!connected) {
                client.Connect();
                connected = true;
                System.out.println("Modbus연결成功:" + host + ":" + port);
            }
            return true;
        } catch (Exception e) {
            System.err.println("Modbus연결失败:" + e.getMessage());
            connected = false;
            return false;
        }
    }
    
    /**
     * 断开연결
     */
    public synchronized void disconnect() {
        try {
            if (client != null && connected) {
                client.Disconnect();
                connected = false;
            }
        } catch (Exception e) {
            System.err.println("断开연결异常:" + e.getMessage());
        }
    }
    
    /**
     * 유지 레지스터 읽기 (FC03)(带重试)
     */
    public int[] readHolding회원가입s(int addr, int 수량) {
        int retry = 3;
        while (retry-- > 0) {
            try {
                if (!connected) connect();
                return client.ReadHolding회원가입s(addr, 수량);
            } catch (Exception e) {
                System.err.println("읽기失败,剩余重试次数:" + retry);
                connected = false;
                try { Thread.sleep(500); } catch (InterruptedException ie) {}
            }
        }
        throw new RuntimeException("유지 레지스터 읽기 (FC03)失败,주소:" + addr);
    }
    
    /**
     * 읽기浮点数
     */
    public float readFloat(int addr) {
        int[] regs = readHolding회원가입s(addr, 2);
        return ModbusClient.Convert회원가입sToFloat(regs);
    }
    
    /**
     * 쓰기浮点数
     */
    public void writeFloat(int addr, float value) {
        int[] regs = ModbusClient.ConvertFloatToTwo회원가입s(value);
        writeMultiple회원가입s(addr, regs);
    }
    
    /**
     * 写多个레지스터(带重试)
     */
    public void writeMultiple회원가입s(int addr, int[] values) {
        int retry = 3;
        while (retry-- > 0) {
            try {
                if (!connected) connect();
                client.WriteMultiple회원가입s(addr, values);
                return;
            } catch (Exception e) {
                System.err.println("쓰기失败,剩余重试次数:" + retry);
                connected = false;
                try { Thread.sleep(500); } catch (InterruptedException ie) {}
            }
        }
        throw new RuntimeException("쓰기레지스터失败,주소:" + addr);
    }
    
    /**
     * 读코일
     */
    public boolean[] readCoils(int addr, int 수량) {
        try {
            if (!connected) connect();
            return client.ReadCoils(addr, 수량);
        } catch (Exception e) {
            connected = false;
            throw new RuntimeException("读코일失败:" + e.getMessage());
        }
    }
    
    /**
     * 写单个코일
     */
    public void writeCoil(int addr, boolean value) {
        try {
            if (!connected) connect();
            client.WriteSingleCoil(addr, value);
        } catch (Exception e) {
            connected = false;
            throw new RuntimeException("写코일失败:" + e.getMessage());
        }
    }
    
    /**
     * 检查연결状态
     */
    public boolean isConnected() {
        return connected;
    }
}

六、实际적용예제:릴레이模块控制

public class RelayControlDemo {
    
    public static void main(String[] args) {
        // 연결到8路네트워크릴레이模块
        ModbusTcpUtil modbus = new ModbusTcpUtil("192.168.1.100", 502, 1);
        
        try {
            modbus.connect();
            
            // 1. 읽기所有릴레이状态
            System.out.println("=== 当前릴레이状态 ===");
            boolean[] states = modbus.readCoils(0, 8);
            for (int i = 0; i < states.length; i++) {
                System.out.println("릴레이" + (i+1) + ":" + (states[i] ? "吸合" : "断开"));
            }
            
            // 2. 控制第1路릴레이吸合
            System.out.println("\n=== 控制第1路릴레이吸合 ===");
            modbus.writeCoil(0, true);
            Thread.sleep(500);
            
            // 3. 읽기数字입력 상태
            System.out.println("\n=== 数字입력 상태 ===");
            boolean[] inputs = modbus.readCoils(0, 8); // 实际应使用ReadDiscreteInputs
            for (int i = 0; i < inputs.length; i++) {
                System.out.println("입력" + (i+1) + ":" + (inputs[i] ? "有信号" : "无信号"));
            }
            
            // 4. 읽기模块온도(假设在레지스터100)
            System.out.println("\n=== 模块온도 ===");
            float temp = modbus.readFloat(100);
            System.out.println("온도:" + temp + "℃");
            
            // 5. 依次吸合所有릴레이(跑马灯效果)
            System.out.println("\n=== 跑马灯测试 ===");
            for (int i = 0; i < 8; i++) {
                modbus.writeCoil(i, true);
                Thread.sleep(200);
                modbus.writeCoil(i, false);
            }
            
            // 6. 전체断开
            boolean[] allOff = {false, false, false, false, false, false, false, false};
            // modbus.writeMultipleCoils(0, allOff); // 需要封装此方法
            
            System.out.println("\n测试完成!");
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            modbus.disconnect();
        }
    }
}

七、异常处理与最佳实践

7.1 常见异常유형

异常原因解决方法
ConnectException연결被拒绝检查IP和포트,确认设备在线
SocketTimeoutException연결或读写超时추가超时时间,检查네트워크지연
ModbusExceptionModbus프로토콜错误检查슬레이브 주소、기능 코드、레지스터주소
Illegal기능Exception非法기능 코드设备不支持该기능 코드
Illegal주소Exception非法주소읽기/쓰기주소超出设备范围
IllegalValueException非法데이터值쓰기的데이터不符合设备要求

7.2 最佳实践

  • 使用연결池:频繁创建和销毁연결会影响性能,建议复用연결
  • 추가重试机制:네트워크不稳定时,读写失败应自动重试2-3次
  • 控制读写频率:避免过快轮询,建议间隔不小于100ms
  • 批量读写:尽量一次읽기多个레지스터,减少通讯次数
  • 异常恢复:捕获异常后重新建立연결,不要直接退出程序
  • 线程安全:多线程访问时使用synchronized或锁机制
  • 자료释放:使用完毕后调用Disconnect()释放연결
  • 로그记录:记录每次读写的주소、데이터和耗时,便于排查문제

7.3 性能优化建议

// 优化예제:批量읽기 vs 单次읽기
// 低效:循环읽기10个레지스터
for (int i = 0; i < 10; i++) {
    int val = modbusClient.ReadHolding회원가입s(i, 1)[0]; // 10次通讯
}

// 高效:一次읽기10个레지스터
int[] vals = modbusClient.ReadHolding회원가입s(0, 10); // 1次通讯

// 优化예제:合理设置超时
modbusClient.setConnectionTimeout(3000);  // 연결超时3秒
modbusClient.setReceiveTimeout(2000);     // 读写超时2秒
// 超时设置过短会导致频繁失败너무 길 면 프로그램이 막 힐 수 있습니다 .

다른 Java Mod bus 라이 브 러 리 비교

库名
EasyModbus
j2mod
Modbus4J
Jamod
JLibModbus

이 문서 에서 제공하는 코 드는 프로젝트 에서 직접 복 사 할 수 있습니다 . Mod bus Poll 과 같은 디 버 그 도구를 사용하여 장치 통신 이 정상 인지 확인 한 다음 Java 코드를 실제 프로젝트에 연결 하는 것이 좋습니다 . Mod bus R TU 직 렬 포 트 통신 을 위해 j 2 mod 또는 J Lib Mod bus 라이 브 러 리를 사용하여 R XT X 또는 j S erial C omm 을 통해 직 렬 포 트에 액세스 할 수 있습니다 .

相关标签
실제 프로젝트에 이 자료를 활용하시겠습니까?

도구 센터에서 프레임 분석, CRC 검증, 장비 디버깅을 진행하거나 요청을 제출하여 선정 및 연동 제안을 받아보세요.

엔지니어 회원

이 글을 실행 가능한 디버깅 자료로 활용하세요

가입하면 고급 프레임 분석, 자료 패키지 다운로드, 코드 예제, 엔지니어링 사례, 우선 기술 지원을 이용할 수 있어 실제 프로젝트 납품에 적합합니다.

고급 도구 무제한 이용
자료 패키지와 코드 패키지
완전한 엔지니어링 사례 라이브러리
우선 기술 지원 채널

답글 작성

이메일 주소는 공개되지 않습니다. 必填项已用 * 标注