在産業用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 | 连接或読み書きタイムアウト | タイムアウト時間の増加,チェック网络延迟 |
| ModbusException | Modbusプロトコルエラー | チェック駅からの住所、function code、register address |
| IllegalFunctionException | 違法な機能コード | デバイスはこの機能をサポートしません码 |
| IllegalAddressException | 違法な住所。 | read/写入アドレス超出デバイス範囲 |
| IllegalValueException | Illegal 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 을 통해 직 렬 포 트에 액세스 할 수 있습니다 .
Leave a Reply