首页>>互联网>>物联网->详解物联网Modbus通讯协议

详解物联网Modbus通讯协议

时间:2023-11-29 本站 点击:0

1、概述

随着IT技术的快速发展,当前已经步入了智能化时代,其中的物联网技术将在未来占据越来越重要的地位。根据百度百科的定义,物联网(Internet of things,简称IOT )即“万物相连的互联网”,是互联网基础上的延伸和扩展的网络,物联网将各种信息有机的结合起来,实现任何时间、任何地点,人、机、物的互联互通。物联网从技术上来说,很重要的核心是通讯协议,即如何按约定的通讯协议,把机、物和人与互联网相连接,进行信息通信,以实现对人、机和物的智能化识别、定位、跟踪、监控和管理的一种网络。

一般来说,常见的物联网通讯协议众多,如蓝牙、Zigbee、WiFi、ModBus、PROFINET、EtherCAT、蜂窝等。而在众多的物联网通讯协议中,Modbus是当前非常流行的一种通讯协议。它一种串行通信协议,是Modicon公司于1979年为使用可编程逻辑控制器(PLC)通信而制定的,可以说,它已经成为工业领域通信协议的业界标准。其优势如下:

免费无版税限制

容易部署

灵活限制少

2、ModBus协议概述

Modbus通讯协议使用请求-应答机制在主(Master)(客户端Client)和从(Slave)(服务器Server)之间交换信息。Client-Server原理是通信协议的模型,其中一个主设备控制多个从设备。这里需要注意的是:Modbus通讯协议当中的Master对应Client,而Slave对应Server。Modbus通讯协议的官网为www.modbus.org。目前官网组织已经建议将Master-Slave替换为Client-Server。从协议类型上可以分为:Modbus-RTU(ASCII)、Modbus-TCP和Modbus-Plus。本文主要介绍Modbus-RTU(ASCII)的通讯协议原理。标准的Modbus协议物理层接口有RS232、RS422、RS485和以太网接口。

通讯示意图如下:

一般来说,Modbus通信协议原理具备如下的特征:

一次只有一个主机(Master)连接到网络

只有主设备(Master)可以启动通信并向从设备(Slave)发送请求

主设备(Master)可以使用其特定地址单独寻址每个从设备(Slave),也可以使用地址0(广播)同时寻址所有从设备(Slave)

从设备(Slave)只能向主设备(Master)发送回复

从设备(Slave)无法启动与主设备(Master)或其他从设备(Slave)的通信

Modbus协议可使用2种通信模式交换信息:

单播模式

广播模式

不管是请求报文还是答复报文,数据结构如下:

即报文(帧数据)由4部分构成:地址(Slave Number)+功能码(Function Codes)+数据(Data)+校验(Check) 。其中的地址代表从设备的ID地址,作为寻址的信息。功能码表示当前的请求执行具体什么操作,比如读还是写。数据代表需要通讯的业务数据,可以根据实际情况来确定。最后一个校验则是验证数据是否有误。其中的功能码说明如下:

比如功能码为03代表读取当前寄存器内一个或多个二进制值,而06代表将二进制值写入单一寄存器。为了模拟Modbus通讯协议过程,这里可以借助模拟软件:

Modbus Poll(Master)

Modbus Slave

具体的安装过程这里不再赘述。首先这里需要模拟一个物联网传感器设备,这里用Modbus Slave来定义,首先打开此软件,并定义一个ID为1的设备:

此功能码为03。另外,设置连接参数,示例界面如下:

下面再用Modbus Poll软件来模拟主机,来获取从设备的数据。首先定义一个读写报文。

然后再定义一个连接信息:

注意:两个COM口要使用不同的名称。

成功建立通讯后,通信的报文格式如下:

Tx代表请求报文,而Rx代表答复报文。

3、ModBusJava实现

下面介绍一下如何用Java来实现一个Modbus TCP通信。这里Java框架采用Spring Boot,首先需要引入Modbus库。Maven依赖库的pom.xml定义如下:

<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.5</version><relativePath/><!--lookupparentfromrepository--></parent><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>DemoprojectforSpringBoot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--ModbusMaster--><dependency><groupId>com.digitalpetri.modbus</groupId><artifactId>modbus-master-tcp</artifactId><version>1.2.0</version></dependency><!--ModbusSlave--><dependency><groupId>com.digitalpetri.modbus</groupId><artifactId>modbus-slave-tcp</artifactId><version>1.2.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

其中关于Modbus库的依赖项为com.digitalpetri.modbus,它分modbus-master-tcp和modbus-slave-tcp 。此示例用Java项目模拟了一个Modbus Master端,用Modbus Slave软件模拟了Slave端,通信连接方式选择Modbus TCP/IP方式,IP地址和端口限定了Slave设备。示意图如下:

由于此处连接方式采用Modbus TCP方式,因此在Modbus Slave的连接配置的地方,需要调整连接方式,示意截图如下:

Java核心代码如下:

packagecom.example.demo.modbus;importjava.util.List;importjava.util.Random;importjava.util.concurrent.CompletableFuture;importjava.util.concurrent.CopyOnWriteArrayList;importjava.util.concurrent.Executors;importjava.util.concurrent.ScheduledExecutorService;importjava.util.concurrent.TimeUnit;importcom.digitalpetri.modbus.codec.Modbus;importcom.digitalpetri.modbus.master.ModbusTcpMaster;importcom.digitalpetri.modbus.master.ModbusTcpMasterConfig;importcom.digitalpetri.modbus.requests.ReadHoldingRegistersRequest;importcom.digitalpetri.modbus.responses.ReadHoldingRegistersResponse;importio.netty.buffer.ByteBufUtil;importio.netty.util.ReferenceCountUtil;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;publicclassMBMaster{privatefinalLoggerlogger=LoggerFactory.getLogger(getClass());privatefinalScheduledExecutorServicescheduler=Executors.newSingleThreadScheduledExecutor();privatefinalList<ModbusTcpMaster>masters=newCopyOnWriteArrayList<>();privatevolatilebooleanstarted=false;privatefinalintnMasters;privatefinalintnRequests;publicMBMaster(intnMasters,intnRequests){if(nMasters<1){nMasters=1;}if(nRequests<1){nMasters=1;}this.nMasters=nMasters;this.nRequests=nRequests;}//启动publicvoidstart(){started=true;ModbusTcpMasterConfigconfig=newModbusTcpMasterConfig.Builder("127.0.0.1").setPort(50201).setInstanceId("S-001").build();newThread(()->{while(started){try{Thread.sleep(3000);}catch(InterruptedExceptione){e.printStackTrace();}doublemean=0.0;intmcounter=0;for(ModbusTcpMastermaster:masters){mean+=master.getResponseTimer().getMeanRate();mcounter+=master.getResponseTimer().getCount();}logger.info("MeanRate={},counter={}",mean,mcounter);}}).start();for(inti=0;i<nMasters;i++){ModbusTcpMastermaster=newModbusTcpMaster(config);master.connect();masters.add(master);for(intj=0;j<nRequests;j++){sendAndReceive(master);}}}//发送请求privatevoidsendAndReceive(ModbusTcpMastermaster){if(!started)return;//10个寄存器CompletableFuture<ReadHoldingRegistersResponse>future=master.sendRequest(newReadHoldingRegistersRequest(0,10),0);//响应处理future.whenCompleteAsync((response,ex)->{if(response!=null){//System.out.println("Response:"+ByteBufUtil.hexDump(response.getRegisters()));System.out.println("Response:"+ByteBufUtil.prettyHexDump(response.getRegisters()));//[00310046000000b30000000000000000]byte[]bytes=ByteBufUtil.getBytes(response.getRegisters());System.out.println("ResponseValue="+bytes[3]);//根据业务情况获取寄存器数值ReferenceCountUtil.release(response);}else{logger.error("ErrorMsg={}",ex.getMessage(),ex);}scheduler.schedule(()->sendAndReceive(master),1,TimeUnit.SECONDS);},Modbus.sharedExecutor());}publicvoidstop(){started=false;masters.forEach(ModbusTcpMaster::disconnect);masters.clear();}publicstaticvoidmain(String[]args){//启动Client进行数据交互newMBMaster(1,1).start();}}

首先,需要用ModbusTcpMasterConfig来初始化一个Modbus Tcp Master主机的配置信息,比如IP地址(127.0.0.1)和端口号(50201),此需要和Slave一致。其次,将配置信息config作为参数传递到ModbusTcpMaster对象中,构建一个 master实例。最后,用master.sendRequest(newReadHoldingRegistersRequest(0, 10), 0)对象来查询数据,此功能码为03,寄存器数据为10。在Modbus Slave开启连接后,设置界面如下所示:

运行Java程序。控制台输出示例如下所示:

由此,可以知晓,返回的报文中在0到f这15个位置中,有需要的业务数据,具体获取哪个位置,取决于Slave设备的设置。

本文分享自华为云社区,作者: jackwangcumt。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/IOT/1541.html