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

freeFree Technical Resource

This content is free to read, suitable for basic learning and search traffic.

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

在産業用IoT和智能制造系统中,Java应用程序经常需要与PLC、Sensors、继电器モジュール等Modbusデバイス进行通讯。本記事详细介绍使い方は?EasyModbus库実装Java Modbus TCPクライアント側は,包括环境搭建、连接管理、コイル読み書き、レジスタ読み書き、浮動小数点数処理、異常処理以及完全的项目サンプル。

一、EasyModbus库介绍

EasyModbus是一个功能强大的Java Modbus通讯库,サポートModbus TCP、Modbus RTU和Modbus UDP三种协议。它具有以下特点:

  • 协议サポート包括的:サポートModbus TCP、RTU、UDP,可作为クライアント側は和サーバー側。
  • API简洁易用:方法命名直观,几行代码即可完成读書き込み操作。
  • データ型丰富:サポート16ビット整数、32ビット浮動小数点数、64位长整数、ストリング·ストリング等
  • 自動再接続。:内置连接ステータス检测和自動再接続。机制
  • 线程セキュリティ:サポート多线程环境下的并发アクセス
  • 开源無料:基于MITProtocol,可無料用于商业项目

二、环境搭建

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包

  • 从GitHubDownloadEasyModbusTCP.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 リードコイルを読む(function code01)

// 読み取り住所から。0始まりました。8个コイルの状態
boolean[] coils = modbusClient.ReadCoils(0, 8);

// 出力每1つのコイル的ステータス
for (int i = 0; i < coils.length; i++) {
    System.out.println("コイル" + i + "status:" + (coils[i] ? "ON" : "OFF"));
}

// 方法説明:
// ReadCoils(int startingAddress, int quantity)
// startingAddress: starting address(0-based)
// quantity: Reading quantity(1-2000)
// 戻る: boolean配列,true=コイル吸合,false=コイル断开

3.3 read discrete inputs(function code02)

// 読み取り住所から。0始まりました。4個別入力です。
boolean[] inputs = modbusClient.ReadDiscreteInputs(0, 4);

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

3.4 read holding registers(function code03)

// 読み取り住所から。0始まりました。10レジスタを保持する。
int[] registers = modbusClient.ReadHoldingRegisters(0, 10);

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

// 方法説明:
// ReadHoldingRegisters(int startingAddress, int quantity)
// 戻る: int配列,每个元素为16ビット符号なし整数(0-65535)
// もし需要記号付き。整数,需要自行转换:(short) registers[i]

3.5 read input registers(function code04)

// 読み取り住所から。0始まりました。2入力レジスタです。(通常用于センサーはデータ)
int[] inputRegs = modbusClient.ReadInputRegisters(0, 2);

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

3.6 write single coil(function code05)

// 控制アドレス0的コイル吸合
modbusClient.WriteSingleCoil(0, true);

// 控制アドレス0的コイル断开
modbusClient.WriteSingleCoil(0, false);

// 方法説明:
// WriteSingleCoil(int address, boolean value)
// value: true=FF00(吸合),false=0000(断开)

3.7 write single register(function code06)

// 向アドレス0写入数值1234
modbusClient.WriteSingleRegister(0, 1234);

// 写入記号付き。数(需要转换为符号なし。)
short signedValue = -100;
modbusClient.WriteSingleRegister(0, signedValue & 0xFFFF);

3.8 write multiple coils(function code15)

// 同时控制前41つのコイル
boolean[] coilValues = {true, false, true, false};
modbusClient.WriteMultipleCoils(0, coilValues);

// 方法説明:
// WriteMultipleCoils(int startingAddress, boolean[] values)
// values配列長さ即为写入数量

3.9 write multiple registers(function code16)

// 向アドレス0开始写入31つのレジスタ
int[] regValues = {100, 200, 300};
modbusClient.WriteMultipleRegisters(0, regValues);

四、浮動小数点数与32位データの処理

Modbusプロトコル本身只传输16位レジスタ,32ビット浮動小数点数需要占用2つ连续的レジスタ。EasyModbus提供了便捷的转换方法。

4.1 read32ビット浮動小数点数

// 読み取りアドレス11始まりました。21つのレジスタ,转换为32ビット浮動小数点数
float value = ModbusClient.ConvertRegistersToFloat(
    modbusClient.ReadHoldingRegisters(11, 2)
);
System.out.println("浮動小数点数值:" + value);

// 指定バイトオーダー(デフォルトABCD,可选BADC、CDAB、DCBA)
float value2 = ModbusClient.ConvertRegistersToFloat(
    modbusClient.ReadHoldingRegisters(11, 2),
    ModbusClient.RegisterOrder.HighByteFirst  // 或 LowByteFirst
);
System.out.println("浮動小数点数值(指定バイトオーダー):" + value2);

4.2 写入32ビット浮動小数点数

// 将浮動小数点数123.56转换为21つのレジスタ并写入アドレス11
float floatValue = 123.56f;
int[] regs = ModbusClient.ConvertFloatToTwoRegisters(floatValue);
modbusClient.WriteMultipleRegisters(11, regs);

System.out.println("写入的レジスタの値:" + regs[0] + ", " + regs[1]);

4.3 32ビット整数処理

// read32ビット整数
int[] regs32 = modbusClient.ReadHoldingRegisters(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.WriteMultipleRegisters(0, writeRegs);

4.4 ストリング·ストリング処理

// 読み取りストリング·ストリング(すべてのレジスタ。存2个文字)
int[] strRegs = modbusClient.ReadHoldingRegisters(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());
        }
    }
    
    /**
     * Read and hold register(带重试)
     */
    public int[] readHoldingRegisters(int addr, int quantity) {
        int retry = 3;
        while (retry-- > 0) {
            try {
                if (!connected) connect();
                return client.ReadHoldingRegisters(addr, quantity);
            } catch (Exception e) {
                System.err.println("Read failed,剩余リトライ回数:" + retry);
                connected = false;
                try { Thread.sleep(500); } catch (InterruptedException ie) {}
            }
        }
        throw new RuntimeException("リードホールドレジスタ失敗,address:" + addr);
    }
    
    /**
     * 読み取り浮動小数点数
     */
    public float readFloat(int addr) {
        int[] regs = readHoldingRegisters(addr, 2);
        return ModbusClient.ConvertRegistersToFloat(regs);
    }
    
    /**
     * 写入浮動小数点数
     */
    public void writeFloat(int addr, float value) {
        int[] regs = ModbusClient.ConvertFloatToTwoRegisters(value);
        writeMultipleRegisters(addr, regs);
    }
    
    /**
     * write multiple registers(带重试)
     */
    public void writeMultipleRegisters(int addr, int[] values) {
        int retry = 3;
        while (retry-- > 0) {
            try {
                if (!connected) connect();
                client.WriteMultipleRegisters(addr, values);
                return;
            } catch (Exception e) {
                System.err.println("写入失敗,剩余リトライ回数:" + retry);
                connected = false;
                try { Thread.sleep(500); } catch (InterruptedException ie) {}
            }
        }
        throw new RuntimeException("写入レジスタ失敗,address:" + addr);
    }
    
    /**
     * リードコイルを読む
     */
    public boolean[] readCoils(int addr, int quantity) {
        try {
            if (!connected) connect();
            return client.ReadCoils(addr, quantity);
        } catch (Exception e) {
            connected = false;
            throw new RuntimeException("リードコイルを読む失敗:" + e.getMessage());
        }
    }
    
    /**
     * write single coil
     */
    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("Relay" + (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("temperature:" + 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プロトコルエラーチェック駅からの住所、function code、register address
IllegalFunctionException違法な機能コードデバイスはこの機能をサポートしません码
IllegalAddressException違法な住所。read/写入アドレス超出デバイス範囲
IllegalValueExceptionIllegal data value写入的データ不符合デバイス要求

7.2 ベストプラクティス

  • 使用连接池:频繁作成和销毁连接会影响性能,建议复用连接
  • 追加重试机制:网络不稳定时,読み書き失敗应自動再試行2-3次
  • 控制読み書き频率:避免过快ポーリング,建议間隔不小于100ms
  • 批量読み書き:尽量一次読み取り多1つのレジスタ,减少通讯次数
  • 異常恢复:捕获異常后重新建立连接,不要直接退出程序
  • 线程セキュリティ:多线程アクセス使用するときsynchronized或锁机制
  • リソース释放:使用完毕后调用Disconnect()释放连接
  • ジャーナル·レコード:记录每次読み書き的アドレス、データと耗时,便于トラブルシューティング問題

7.3 パフォーマンス最適化建议

// 优化サンプル:バッチ読み取り vs 单次読み取り
// 低效:循环読み取り101つのレジスタ
for (int i = 0; i < 10; i++) {
    int val = modbusClient.ReadHoldingRegisters(i, 1)[0]; // 10次通讯
}

// 高效:一次読み取り101つのレジスタ
int[] vals = modbusClient.ReadHoldingRegisters(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 을 통해 직 렬 포 트에 액세스 할 수 있습니다 .

Related Tags
Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC verification and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support

Leave a Reply

Your email address will not be published. Required fields are marked *.