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 | 説明 |
|---|---|---|
| FLOAT | ABCD(ビッグエンディアン) | 大多数PLC、Modbusdevice |
| FLOAT_SWAP | CDAB(Word exchange) | 部分シーメンスデバイス |
| FLOAT_LOWWORD_FIRST | BADC(バイトスワップ) | 较少见 |
| FLOAT_SWAP_LOWWORD_FIRST | DCBA(リトルエンディアン) | 部分組込みデバイス |
四、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库对比
| 库名 | 协议サポート | 特点 | 適用可能なシーン |
|---|---|---|---|
| EasyModbus | TCP/RTU/UDP | API简洁,ドキュメント完善,功能丰富 | 大多数项目首选 |
| j2mod | TCP/RTU/ASCII | j2mod的活跃分支,功能包括的 | 需要ASCIIProtocol |
| Jamod | TCP/RTU | 经典库,稳定但更新慢 | 遗留项目维护 |
| Modbus4J | TCP/RTU | Powerful Features,API较复杂 | 复杂项目 |
| modbus-master | TCP/RTU | 轻量级,仅マスター | 简单読み書き场景 |
EasyModbus以其简洁的API和丰富的功能,成为Java Modbus开发的首选库。本記事提供的工具类可直接使用する。于项目开发,建议先使用Modbus Poll等工具機器の確認通信は正常,再将Java代码集成到实际项目中。
Leave a Reply