Easy Mod bus TCP / U DP / R TU Java 클 라이언 트 샘플 코드

freeFree Technical Resource

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

Easy Mod bus TCP / U DP / R TU Java 클 라이언 트 샘플 코드

EasyModbus是Java生态中最流行的Modbus通讯库之一,以其简洁的API、丰富的功能和良好的ドキュメント著称。它サポートModbus TCP、RTU和UDP三种协议,既可作为クライアント側は也可作为サーバー側。,广泛应用于工业自動化、Data Acquisition、IoTゲートウェイ等领域。本記事详细介绍EasyModbus的インストール設定、连接管理、コイル読み書き、レジスタ読み書き、浮動小数点数処理以及完全的项目サンプル。

一、EasyModbus库概述

1.1 核心特性

  • 多协议サポート:Modbus TCP、RTU、UDP3つの伝送モード
  • 主从兼备:既可作为クライアント側は(master),也可作为サーバー側。(slave)
  • 功能完全:サポート01-16すべての標準機能コード
  • データ型丰富:サポート16位/32ビット整数、32位/64ビット浮動小数点数、ストリング·ストリング
  • バイトオーダー可配:サポートビッグエンディアン、リトルエンディアン、言葉の交換等多种バイトオーダー
  • 事件驱动:サポートデータ变化监听、连接ステータス回调
  • 跨プラットフォーム:Windows、Linux、Mac OS、embeddedLinux均可运行
  • 开源無料:MITProtocol,可無料用于商业项目

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

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

System.out.println("Coil status:");
for (int i = 0; i < coils.length; i++) {
    System.out.println("  コイル" + i + ":" + (coils[i] ? "吸合" : "断开"));
}

// 方法签名:
// boolean[] ReadCoils(int startingAddress, int quantity)
// startingAddress: starting address(0-based)
// quantity: Reading quantity(1-2000)

2.3 read discrete inputs(function code02)

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

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

2.4 read holding registers(function code03)

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

System.out.println("レジスタを保持する。:");
for (int i = 0; i < registers.length; i++) {
    System.out.println("  register" + i + ":" + registers[i]);
}

// 戻り値は为int配列,每个元素为16ビット符号なし整数(0-65535)
// 如需記号付き。整数:short value = (short) registers[i];

2.5 read input registers(function code04)

// 読み取りセンサーはデータ(temperature、湿度等)
int[] inputRegs = client.ReadInputRegisters(0, 2);

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

2.6 write single coil(function code05)

// 控制アドレス0的コイル吸合
client.WriteSingleCoil(0, true);
System.out.println("コイル吸合成功");

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

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

2.7 write single register(function code06)

// 向アドレス0写入数值1234
client.WriteSingleRegister(0, 1234);
System.out.println("レジスタ写入成功");

// 写入記号付き。数(-100)
client.WriteSingleRegister(0, -100 & 0xFFFF);

2.8 write multiple coils(function code15)

// 同时控制前41つのコイル
boolean[] values = {true, false, true, false};
client.WriteMultipleCoils(0, values);
System.out.println("バッチ書き込みコイル成功");

2.9 write multiple registers(function code16)

// 向アドレス0开始写入31つのレジスタ
int[] values = {100, 200, 300};
client.WriteMultipleRegisters(0, values);
System.out.println("一括書き込みき込みレジスタ成功");

三、浮動小数点数与多バイトデータの処理

EasyModbus提供了便捷的方法処理32ビット浮動小数点数、32ビット整数和64ビット·データ,サポート多种バイトオーダー。

3.1 read32ビット浮動小数点数

import de.re.easymodbus.datatypes.DataType;

// read21つのレジスタ并转换为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("temperature:" + temp);

// 設定バイトオーダー(デフォルトビッグエンディアンABCD)
client.setDataType(DataType.FLOAT);  // 32ビット浮動小数点数
// 可选:DataType.FLOAT_SWAP (CDAB), DataType.FLOAT_LOWWORD_FIRST (BADC)

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

// 将浮動小数点数25.5转换为21つのレジスタ并写入
float value = 25.5f;
int[] regs = client.ConvertFloatToRegisters(value);
client.WriteMultipleRegisters(10, regs);
System.out.println("写入浮動小数点数:" + value + ",register value:" + regs[0] + ", " + regs[1]);

3.3 read32ビット整数

// read32ビット符号なし整数
int[] regs = client.ReadHoldingRegisters(0, 2);
long uint32 = client.ConvertRegistersToLong(regs);
System.out.println("32ビット符号なし整数:" + uint32);

// read32ビット符号付き整数
int int32 = (int) uint32;
System.out.println("32ビット符号付き整数:" + int32);

3.4 読み取りストリング·ストリング

// 読み取りストリング·ストリング(すべてのレジスタ。存2个文字,read101つのレジスタ=20文字)
int[] regs = client.ReadHoldingRegisters(0, 10);
String str = client.ConvertRegistersToString(regs, 20);
System.out.println("ストリング·ストリング:" + str);

3.5 バイトオーダー説明

DataType枚举endianness説明
FLOATABCD(ビッグエンディアン)大多数PLC、Modbusdevice
FLOAT_SWAPCDAB(Word exchange)部分シーメンスデバイス
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");  // Serial Port Device(Windows为"COM3")
            client.setBaudRate(9600);               // Baud rate
            client.setParity(Parity.None);          // check digit:None/Even/Odd
            client.setStopBits(StopBits.One);       // stop bit:One/Two
            client.setDataBits(8);                  // data bit
            
            // 設定駅からの住所
            client.setUnitIdentifier((byte) 1);
            
            // 建立连接
            client.Connect();
            System.out.println("RTU接続成功です。");
            
            // 读書き込み操作。与TCP完全相同
            int[] regs = client.ReadHoldingRegisters(0, 10);
            System.out.println("register:" + java.util.Arrays.toString(regs));
            
            client.Disconnect();
            
        } catch (Exception e) {
            System.err.println("Error:" + 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("接続の失敗(No." + 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());
            }
        }
    }
    
    /**
     * Read and hold register(带重试)
     */
    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("Read failed(No." + attempt + "次):" + e.getMessage());
                reconnect();
            }
        }
        throw new RuntimeException("読み取りレジスタ失敗,address:" + addr);
    }
    
    /**
     * read32ビット浮動小数点数
     */
    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);
    }
    
    /**
     * Write multiple registers
     */
    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("写入失敗(No." + attempt + "次):" + e.getMessage());
                reconnect();
            }
        }
        throw new RuntimeException("写入レジスタ失敗,address:" + addr);
    }
    
    /**
     * write single coil
     */
    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("temperature:%.2f℃%n", temp);
            
            // read101つのレジスタ
            int[] regs = mb.readRegisters(0, 10);
            System.out.println("register:" + java.util.Arrays.toString(regs));
            
            // 控制继电器
            mb.writeCoil(0, true);
            System.out.println("Relay1已吸合");
            
            // 読み取り继电器ステータス
            boolean[] coils = mb.readCoils(0, 8);
            System.out.println("继电器ステータス:" + java.util.Arrays.toString(coils));
            
            mb.disconnect();
        }
    }
}

六、Modbusサーバー側。(slave)実装

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;  // temperature
            server.holdingRegisters[1] = 50;   // 湿度
            server.coils[0] = true;            // Relay1
            
            // 启动サーバー側。
            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 common problems

  • 接続タイムアウト:チェックIPaddress、ポート番号、ファイアウォール,確認デバイスオンライン且ポート502开放
  • 戻る異常な応答:チェック駅からの住所(Unit ID)是否正确,関数コードとアドレス是否在デバイスサポート範囲内
  • 浮動小数点数解析エラー:尝试不同的DataType(FLOAT/FLOAT_SWAP等),機器の確認的データ格式
  • RTUシリアルポート打不开:チェックシリアルデバイス名,確認权限(Linux需加入dialout组),关闭其他占用シリアルポート的程序
  • データ読み取り为0:確認レジスター·アドレス正确,注意0-based和1-basedアドレス違いはありません
  • 频繁断连:タイムアウト時間の増加,チェック网络稳定性,追加自動再接続。机制

8.2 ベストプラクティス

  • 使用try-with-resources:确保连接正确关闭
  • バッチ読み取り:尽量一次読み取り多1つのレジスタ,减少通讯次数
  • 合理設定タイムアウト:局域网建议1-3秒,RTU建议3-5秒
  • 追加重试机制:网络不稳定时自動再試行2-3次
  • 控制ポーリング频率:避免过快ポーリング,建议間隔不小于100ms
  • 異常処理:捕获ModbusException和IOException,区分连接エラー和协议エラー
  • ジャーナル·レコード:记录每次読み書き的アドレス、データと耗时,便于トラブルシューティング
  • 多デバイス管理:每个デバイス使用独立的ModbusClient实例,避免共享连接

九、其他Java Modbus库对比

库名协议サポート特点適用可能なシーン
EasyModbusTCP/RTU/UDPAPI简洁,ドキュメント完善,功能丰富大多数项目首选
j2modTCP/RTU/ASCIIj2mod的活跃分支,功能包括的需要ASCIIProtocol
JamodTCP/RTU经典库,稳定但更新慢遗留项目维护
Modbus4JTCP/RTUPowerful Features,API较复杂复杂项目
modbus-masterTCP/RTU轻量级,仅マスター简单読み書き场景

EasyModbus以其简洁的API和丰富的功能,成为Java Modbus开发的首选库。本記事提供的工具类可直接使用する。于项目开发,建议先使用Modbus Poll等工具機器の確認通信は正常,再将Java代码集成到实际项目中。

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 *.