modbus-tcp
功能码:
● 0x01:读线圈 Coil Status ON=1, OFF=0
● 0x02:读离散量输入 Input Status ON=1, OFF=0
● 0x03:读保持寄存器 Holding Register 指定数据类型, (可以是Float类型)
● 0x04:读输入寄存器 Input Register 指定数据类型 (可以是Float类型)
使用最多的是 读保持寄存器
jlibmodbus
pom 文件中引入 jar包
<dependency>
<groupId>com.intelligt.modbus</groupId>
<artifactId>jlibmodbus</artifactId>
<version>1.2.9.7</version>
</dependency>
代码:
public class App {
public static void main(String[] args) {
try {
// 设置主机TCP参数
TcpParameters tcpParameters = new TcpParameters();
// 设置TCP的ip地址
InetAddress address = InetAddress.getByName("127.0.0.1");
// TCP参数设置ip地址
// tcpParameters.setHost(InetAddress.getLocalHost());
tcpParameters.setHost(address);
// TCP设置长连接
tcpParameters.setKeepAlive(true);
// TCP设置端口,这里设置是默认端口502
tcpParameters.setPort(Modbus.TCP_PORT);
// 创建一个主机
ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
Modbus.setAutoIncrementTransactionId(true);
int slaveId = 1;//从机地址
int offset = 0;//寄存器读取开始地址
int quantity = 10;//读取的寄存器数量
try {
if (!master.isConnected()) {
master.connect();// 开启连接
}
// 读取对应从机的数据,readInputRegisters读取的写寄存器,功能码04 int
// int[] registerValues = master.readInputRegisters(slaveId, offset, quantity);
// 读功能码03 int
// int[] registerValues = master.readHoldingRegisters(slaveId, offset, quantity);
// 读功能码02 bool 类型
// boolean[] registerValues = master.readDiscreteInputs(slaveId, offset, quantity);
// 读功能码01 bool 类型
// boolean[] registerValues = master.readCoils(slaveId, offset, quantity);
// ---------------------------------------------------------
// Single写bool: 一次写单个bool
// master.writeSingleCoil(slaveId, offset, true);
// master.writeSingleCoil(slaveId, offset+1, true);
// master.writeSingleCoil(slaveId, offset+2, true);
// master.writeSingleCoil(slaveId, offset+3, true);
// master.writeSingleCoil(slaveId, offset+4, true);
// Multiple一次写多个bool
// master.writeMultipleCoils(slaveId,offset,new boolean[]{true, true, false});
// 写多个int
// master.writeMultipleRegisters(slaveId,offset,new int[]{1,45,65,0,3});
// 读写并存 功能码04 writeMultipleRegisters与readInputRegisters的合并
// master.readWriteMultipleRegisters(slaveId,readAddress,quantity,writeAddress,new int[]{1,343,432,56,0});
// 功能码03
master.maskWriteRegister(slaveId,offset,1,6);
master.maskWriteRegister(slaveId,offset+1,3,9);
// 控制台输出
// for (int value : registerValues) {
// System.out.println("Address: " + offset++ + ", Value: " + value);
// }
} catch (ModbusProtocolException e) {
e.printStackTrace();
} catch (ModbusNumberException e) {
e.printStackTrace();
} catch (ModbusIOException e) {
e.printStackTrace();
} finally {
try {
master.disconnect();
} catch (ModbusIOException e) {
e.printStackTrace();
}
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
}
}
仿真软件: https://ioufev.lanzout.com/inPtpxp9yef
- 西门子地址对应关系:
地址对应关系 举个🌰
1(modbus address) - 40001 (plc address) - 再来对应他们西门子的数据块DB*.DBW0
- bool类型读取顺序的字节序规则:
根据modbus地址 读
举个栗子🌰
modbus地址: 40001 bool bit0位
modbus地址: 40001 bool bit1位
modbus地址: 40001 bool bit2位
…
modbus地址: 40001 bool bit15位
其中, 这个 40001 在代码中代表的 offset变量, 读取到的数值 , 转化成 2进制16位 分别与 bit0 … bit15 对应起来
- 西门子读取bool规则 (西门子的字节序规则)
举个🌰
0010 0100 0001 1001
8位一个字节, 一个地址两个字节 分为前 8 位与后 8位
先从 前 8位 中 0010 0100
这8 位 从 ← 读 (从第8位往第1位读)
再 从 后 8 位 中 0001 1001
这后 8位 从 ← 读 (从第16位往第9位读)
把上面读到2个字节,
变成 我们传统的顺序读取方式(从左到右读 →)
0010 0100 0001 1001 -----> 0010 0100 1001 1000