java实现Modbus通信
参考链接:
https://www.cnblogs.com/ioufev/p/10831289.html
https://blog.csdn.net/ioufev/article/details/100554691
Modbus协议
Modbus由MODICON公司于1979年开发,是一种工业现场总线协议标准。1996年施耐德公司推出基于以太网TCP/IP的Modbus协议:ModbusTCP。
Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。
标准的Modbus协议物理层接口有RS232、RS422、RS485和以太网接口,采用master/slave方式通信。
Modbus和RS485的关系:Modbus是协议,物理层接口有RS232、RS422、RS485和以太网接口几种
仿真软件和程序下载: 百度网盘提供
使用jlibmodbus
maven配置
<dependency><groupId>com.intelligt.modbus</groupId><artifactId>jlibmodbus</artifactId><version>1.2.9.7</version></dependency>
java代码从Modbus读取数据
package com.ioufev;import java.net.InetAddress;import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;
import com.intelligt.modbus.jlibmodbus.master.ModbusMaster;
import com.intelligt.modbus.jlibmodbus.master.ModbusMasterFactory;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;/*** Hello world!**/
public class App {public static void main(String[] args) {try {// 设置主机TCP参数TcpParameters tcpParameters = new TcpParameters();// 设置TCP的ip地址InetAddress adress = InetAddress.getByName("192.168.3.16");// TCP参数设置ip地址// tcpParameters.setHost(InetAddress.getLocalHost());tcpParameters.setHost(adress);// TCP设置长连接tcpParameters.setKeepAlive(true);// TCP设置端口,这里设置是默认端口502tcpParameters.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读取的写寄存器,功能码04int[] registerValues = master.readInputRegisters(slaveId, offset, quantity);// 控制台输出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();}}
}
使用modbus4j
maven配置
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13-beta-3</version><scope>test</scope></dependency><dependency><groupId>com.infiniteautomation</groupId><artifactId>modbus4j</artifactId><version>3.0.3</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.9</version></dependency><!-- 若想引用modbus4j需要引入下列repository id:ias-snapshots id:ias-releases 两个 ,使用默认仓库下载,不要使用阿里云仓库--><repositories><repository><releases><enabled>false</enabled></releases><snapshots><enabled>true</enabled></snapshots><id>ias-snapshots</id><name>Infinite Automation Snapshot Repository</name><url>https://maven.mangoautomation.net/repository/ias-snapshot/</url></repository><repository><releases><enabled>true</enabled></releases><snapshots><enabled>false</enabled></snapshots><id>ias-releases</id><name>Infinite Automation Release Repository</name><url>https://maven.mangoautomation.net/repository/ias-release/</url></repository></repositories>
java使用modbus4j读取数据
package com.ioufev;import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;/*** modbus通讯工具类,采用modbus4j实现**/
public class Modbus4jUtils {/*** 工厂。*/static ModbusFactory modbusFactory;static {if (modbusFactory == null) {modbusFactory = new ModbusFactory();}}/*** 获取master** @return* @throws ModbusInitException*/public static ModbusMaster getMaster() throws ModbusInitException {IpParameters params = new IpParameters();params.setHost("10.211.55.4");params.setPort(502);//// modbusFactory.createRtuMaster(wapper); //RTU 协议// modbusFactory.createUdpMaster(params);//UDP 协议// modbusFactory.createAsciiMaster(wrapper);//ASCII 协议ModbusMaster master = modbusFactory.createTcpMaster(params, false);// TCP 协议master.init();return master;}/*** 读取[01 Coil Status 0x]类型 开关数据** @param slaveId* slaveId* @param offset* 位置* @return 读取值* @throws ModbusTransportException* 异常* @throws ErrorResponseException* 异常* @throws ModbusInitException* 异常*/public static Boolean readCoilStatus(int slaveId, int offset)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 01 Coil StatusBaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);Boolean value = getMaster().getValue(loc);return value;}/*** 读取[02 Input Status 1x]类型 开关数据** @param slaveId* @param offset* @return* @throws ModbusTransportException* @throws ErrorResponseException* @throws ModbusInitException*/public static Boolean readInputStatus(int slaveId, int offset)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 02 Input StatusBaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);Boolean value = getMaster().getValue(loc);return value;}/*** 读取[03 Holding Register类型 2x]模拟量数据** @param slaveId* slave Id* @param offset* 位置* @param dataType* 数据类型,来自com.serotonin.modbus4j.code.DataType* @return* @throws ModbusTransportException* 异常* @throws ErrorResponseException* 异常* @throws ModbusInitException* 异常*/public static Number readHoldingRegister(int slaveId, int offset, int dataType)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 03 Holding Register类型数据读取BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);Number value = getMaster().getValue(loc);return value;}/*** 读取[04 Input Registers 3x]类型 模拟量数据** @param slaveId* slaveId* @param offset* 位置* @param dataType* 数据类型,来自com.serotonin.modbus4j.code.DataType* @return 返回结果* @throws ModbusTransportException* 异常* @throws ErrorResponseException* 异常* @throws ModbusInitException* 异常*/public static Number readInputRegisters(int slaveId, int offset, int dataType)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 04 Input Registers类型数据读取BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);Number value = getMaster().getValue(loc);return value;}/*** 批量读取使用方法** @throws ModbusTransportException* @throws ErrorResponseException* @throws ModbusInitException*/public static void batchRead() throws ModbusTransportException, ErrorResponseException, ModbusInitException {BatchRead<Integer> batch = new BatchRead<Integer>();batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));batch.addLocator(1, BaseLocator.inputStatus(1, 0));ModbusMaster master = getMaster();batch.setContiguousRequests(false);BatchResults<Integer> results = master.send(batch);System.out.println(results.getValue(0));System.out.println(results.getValue(1));}/*** 测试** @param args*/public static void main(String[] args) {try {// 01测试Boolean v011 = readCoilStatus(1, 0);Boolean v012 = readCoilStatus(1, 1);Boolean v013 = readCoilStatus(1, 6);System.out.println("v011:" + v011);System.out.println("v012:" + v012);System.out.println("v013:" + v013);// 02测试Boolean v021 = readInputStatus(1, 0);Boolean v022 = readInputStatus(1, 1);Boolean v023 = readInputStatus(1, 2);System.out.println("v021:" + v021);System.out.println("v022:" + v022);System.out.println("v023:" + v023);// 03测试Number v031 = readHoldingRegister(1, 1, DataType.FOUR_BYTE_FLOAT);// 注意,floatNumber v032 = readHoldingRegister(1, 3, DataType.FOUR_BYTE_FLOAT);// 同上System.out.println("v031:" + v031);System.out.println("v032:" + v032);// 04测试Number v041 = readInputRegisters(1, 0, DataType.FOUR_BYTE_FLOAT);//Number v042 = readInputRegisters(1, 2, DataType.FOUR_BYTE_FLOAT);//System.out.println("v041:" + v041);System.out.println("v042:" + v042);// 批量读取batchRead();} catch (Exception e) {e.printStackTrace();}}
}
java通过modbus4j对数据的写入
package com.ioufev;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.ModbusResponse;
import com.serotonin.modbus4j.msg.WriteCoilRequest;
import com.serotonin.modbus4j.msg.WriteCoilResponse;
import com.serotonin.modbus4j.msg.WriteCoilsRequest;
import com.serotonin.modbus4j.msg.WriteCoilsResponse;
import com.serotonin.modbus4j.msg.WriteRegisterRequest;
import com.serotonin.modbus4j.msg.WriteRegisterResponse;
import com.serotonin.modbus4j.msg.WriteRegistersRequest;/*** modbus4j写入数据**/
public class Modbus4jWriteUtils {static Log log = LogFactory.getLog(Modbus4jWriteUtils.class);/*** 工厂。*/static ModbusFactory modbusFactory;static {if (modbusFactory == null) {modbusFactory = new ModbusFactory();}}/*** 获取tcpMaster** @return* @throws ModbusInitException*/public static ModbusMaster getMaster() throws ModbusInitException {IpParameters params = new IpParameters();params.setHost("10.211.55.4");params.setPort(502);ModbusMaster tcpMaster = modbusFactory.createTcpMaster(params, false);tcpMaster.init();return tcpMaster;}/*** 写 [01 Coil Status(0x)]写一个 function ID = 5** @param slaveId* slave的ID* @param writeOffset* 位置* @param writeValue* 值* @return 是否写入成功* @throws ModbusTransportException* @throws ModbusInitException*/public static boolean writeCoil(int slaveId, int writeOffset, boolean writeValue)throws ModbusTransportException, ModbusInitException {// 获取masterModbusMaster tcpMaster = getMaster();// 创建请求WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);// 发送请求并获取响应对象WriteCoilResponse response = (WriteCoilResponse) tcpMaster.send(request);if (response.isException()) {return false;} else {return true;}}/*** 写[01 Coil Status(0x)] 写多个 function ID = 15** @param slaveId* slaveId* @param startOffset* 开始位置* @param bdata* 写入的数据* @return 是否写入成功* @throws ModbusTransportException* @throws ModbusInitException*/public static boolean writeCoils(int slaveId, int startOffset, boolean[] bdata)throws ModbusTransportException, ModbusInitException {// 获取masterModbusMaster tcpMaster = getMaster();// 创建请求WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);// 发送请求并获取响应对象WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request);if (response.isException()) {return false;} else {return true;}}/**** 写[03 Holding Register(4x)] 写一个 function ID = 6** @param slaveId* @param writeOffset* @param writeValue* @return* @throws ModbusTransportException* @throws ModbusInitException*/public static boolean writeRegister(int slaveId, int writeOffset, short writeValue)throws ModbusTransportException, ModbusInitException {// 获取masterModbusMaster tcpMaster = getMaster();// 创建请求对象WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request);if (response.isException()) {log.error(response.getExceptionMessage());return false;} else {return true;}}/**** 写入[03 Holding Register(4x)]写多个 function ID=16** @param slaveId* modbus的slaveID* @param startOffset* 起始位置偏移量值* @param sdata* 写入的数据* @return 返回是否写入成功* @throws ModbusTransportException* @throws ModbusInitException*/public static boolean writeRegisters(int slaveId, int startOffset, short[] sdata)throws ModbusTransportException, ModbusInitException {// 获取masterModbusMaster tcpMaster = getMaster();// 创建请求对象WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata);// 发送请求并获取响应对象ModbusResponse response = tcpMaster.send(request);if (response.isException()) {log.error(response.getExceptionMessage());return false;} else {return true;}}/*** 写入数字类型的模拟量(如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long)** @param slaveId* @param offset* @param value* 写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long* @param registerCount* ,com.serotonin.modbus4j.code.DataType* @throws ModbusTransportException* @throws ErrorResponseException* @throws ModbusInitException*/public static void writeHoldingRegister(int slaveId, int offset, Number value, int dataType)throws ModbusTransportException, ErrorResponseException, ModbusInitException {// 获取masterModbusMaster tcpMaster = getMaster();// 类型BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);tcpMaster.setValue(locator, value);}public static void main(String[] args) {try {//@formatter:off// 测试01
// boolean t01 = writeCoil(1, 0, true);
// System.out.println("T01:" + t01);// 测试02
// boolean t02 = writeCoils(1, 0, new boolean[] { true, false, true });
// System.out.println("T02:" + t02);// 测试03
// short v = -3;
// boolean t03 = writeRegister(1, 0, v);
// System.out.println("T03:" + t03);// 测试04
// boolean t04 = writeRegisters(1, 0, new short[] { -3, 3, 9 });
// System.out.println("t04:" + t04);//写模拟量writeHoldingRegister(1,0, 10.1f, DataType.FOUR_BYTE_FLOAT);//@formatter:on} catch (Exception e) {e.printStackTrace();}}}
使用modbus-master-tcp
modbus-master-tcp项目的底层是基于netty框架开发。天然的支持异步处理。在性能方面有很好的提升。
maven配置
<dependency><groupId>com.digitalpetri.modbus</groupId><artifactId>modbus-master-tcp</artifactId><version>1.1.0</version></dependency>
java使用modbus-master-tcp读取数据
package com.ioufev;import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;import com.digitalpetri.modbus.codec.Modbus;
import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.ReadCoilsRequest;
import com.digitalpetri.modbus.requests.ReadDiscreteInputsRequest;
import com.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;
import com.digitalpetri.modbus.requests.ReadInputRegistersRequest;
import com.digitalpetri.modbus.responses.ReadCoilsResponse;
import com.digitalpetri.modbus.responses.ReadDiscreteInputsResponse;
import com.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;
import com.digitalpetri.modbus.responses.ReadInputRegistersResponse;import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;/**** modbus TCP协议Java通讯读取例子***/
public class ModbusMasterTCPDemo {static ModbusTcpMaster master;/*** 获取TCP协议的Master** @return*/public static void initModbusTcpMaster() {if (master == null) {// 创建配置ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("10.211.55.4").setPort(502).build();master = new ModbusTcpMaster(config);}}/**** 释放资源*/public static void release() {if (master != null) {master.disconnect();}Modbus.releaseSharedResources();}/*** 读取Coils开关量** @param address* 寄存器开始地址* @param quantity* 数量* @param unitId* ID* @return 读取值* @throws InterruptedException* 异常* @throws ExecutionException* 异常*/public static Boolean readCoils(int address, int quantity, int unitId)throws InterruptedException, ExecutionException {Boolean result = null;CompletableFuture<ReadCoilsResponse> future = master.sendRequest(new ReadCoilsRequest(address, quantity),unitId);ReadCoilsResponse readCoilsResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理if (readCoilsResponse != null) {ByteBuf buf = readCoilsResponse.getCoilStatus();result = buf.readBoolean();ReferenceCountUtil.release(readCoilsResponse);}return result;}/*** 读取readDiscreteInputs开关量** @param address* 寄存器开始地址* @param quantity* 数量* @param unitId* ID* @return 读取值* @throws InterruptedException* 异常* @throws ExecutionException* 异常*/public static Boolean readDiscreteInputs(int address, int quantity, int unitId)throws InterruptedException, ExecutionException {Boolean result = null;CompletableFuture<ReadDiscreteInputsResponse> future = master.sendRequest(new ReadDiscreteInputsRequest(address, quantity), unitId);ReadDiscreteInputsResponse discreteInputsResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理if (discreteInputsResponse != null) {ByteBuf buf = discreteInputsResponse.getInputStatus();result = buf.readBoolean();ReferenceCountUtil.release(discreteInputsResponse);}return result;}/*** 读取HoldingRegister数据** @param address* 寄存器地址* @param quantity* 寄存器数量* @param unitId* id* @return 读取结果* @throws InterruptedException* 异常* @throws ExecutionException* 异常*/public static Number readHoldingRegisters(int address, int quantity, int unitId)throws InterruptedException, ExecutionException {Number result = null;CompletableFuture<ReadHoldingRegistersResponse> future = master.sendRequest(new ReadHoldingRegistersRequest(address, quantity), unitId);ReadHoldingRegistersResponse readHoldingRegistersResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理if (readHoldingRegistersResponse != null) {ByteBuf buf = readHoldingRegistersResponse.getRegisters();result = buf.readFloat();ReferenceCountUtil.release(readHoldingRegistersResponse);}return result;}/*** 读取InputRegisters模拟量数据** @param address* 寄存器开始地址* @param quantity* 数量* @param unitId* ID* @return 读取值* @throws InterruptedException* 异常* @throws ExecutionException* 异常*/public static Number readInputRegisters(int address, int quantity, int unitId)throws InterruptedException, ExecutionException {Number result = null;CompletableFuture<ReadInputRegistersResponse> future = master.sendRequest(new ReadInputRegistersRequest(address, quantity), unitId);ReadInputRegistersResponse readInputRegistersResponse = future.get();// 工具类做的同步返回.实际使用推荐结合业务进行异步处理if (readInputRegistersResponse != null) {ByteBuf buf = readInputRegistersResponse.getRegisters();result = buf.readDouble();ReferenceCountUtil.release(readInputRegistersResponse);}return result;}public static void main(String[] args) {try {// 初始化资源initModbusTcpMaster();// 执行操作// 读取开关量System.out.println(readCoils(0, 1, 1));System.out.println(readDiscreteInputs(0, 1, 1));System.out.println(readDiscreteInputs(1, 1, 1));// 读取模拟量System.out.println(readHoldingRegisters(0, 2, 1));System.out.println(readHoldingRegisters(2, 2, 1));System.out.println(readHoldingRegisters(4, 2, 1));System.out.println(readInputRegisters(2, 4, 1));System.out.println(readInputRegisters(6, 4, 1));// 释放资源release();} catch (Exception e) {e.printStackTrace();}}
}
总结
- jlibmodbus:集成多个串口通信开源库,有意思
- modbus4j:很有名
- modbus-master-tcp:底层netty,支持异步
- Jamod:Github上安卓开发modbus通信用的多