文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java 整合 Modbus TCP

2023-08-30 12:04

关注

1.概述

概念

Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。

优势

通讯方式

ASCII模式
当控制器设为在Modbus网络上以ASCII模式通信,在消息中的每个8Bit字节都作为两个ASCII字符发送。这种方式的主要优点是字符发送的时间间隔可达到1秒而不产生错误。

RTU模式
当控制器设为Modbus网络上以RTU(远程终端单元)模式通信,在消息中的每个8Bit字节包含两个4Bit的十六进制字符。这种方式的主要优点是:在同样的波特率下,可比ASCII方式传送更多的数据。

3、Modbus TCP
在Modbus TCP/IP协议中,串行链路中的主/从设备分别演变为客户端/服务器端设备。即客户端相当于主站设备,服务器端相当于从站设备。基于TCP/IP网络的传输特性,串行链路上一主多从的构造也演变为多客户端/多服务器端的构造模型。Modbus TCP/IP服务器端通常使用端口502作为接收报文的端口, IANA(Internet Assigned Numbers Authority,互联网编号分配管理机构)给Modbus协议赋予TCP端口号为502,这是目前在仪表与自动化行业中唯一分配到的端口号。

2.组成

物理模型

ModBus 基础模型

线圈寄存器:实际上就可以类比为开关量(继电器状态),每一个bit对应一个信号的开关状态。所以一个byte就可以同时控制8路的信号。比如控制外部8路io的高低。 线圈寄存器支持读也支持写,写在功能码里面又分为写单个线圈寄存器和写多个线圈寄存器。对应上面的功能码也就是:0x01 0x05 0x0f离散输入寄存器:如果线圈寄存器理解了这个自然也明白了。离散输入寄存器就相当于线圈寄存器的只读模式,他也是每个bit表示一个开关量,而他的开关量只能读取输入的开关信号,是不能够写的。比如我读取外部按键的按下还是松开。所以功能码也简单就一个读的 0x02保持寄存器:这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。一般对应参数设置,比如我我设置时间年月日,不但可以写也可以读出来现在的时间。写也分为单个写和多个写,所以功能码有对应的三个:0x03 0x06 0x10输入寄存器:这个和保持寄存器类似,但是也是只支持读而不能写,一般是读取各种实时数据。一个寄存器也是占据两个byte的空间。类比我我通过读取输入寄存器获取现在的AD采集值。对应的功能码也就一个 0x0

ModbusTCP数据帧: MBAP + PDU

MBAP(报文头)

内容大小描述
00 002字节报文的序列号,一般通信之后要加1来区分不同的通信数据报文。
00 002字节00 00标识Modus TCP协议
00 062字节数据的长度,单位字节
011字节设备地址

PDU(数据体)

功能码(1字节)+数据(不确定)

例如

​ 0x03 : 读保持寄存器

​ 请求:MBAP(7字节) 功能码(1字节) 起始地址(2字节) 寄存器数量(2字节)

​ 响应:MBAP(7字节) 功能码(1字节) 数据长度(1字节) 数据n个(2n字节)

​ 请求

00 01 00 00 00 06 01 03 00 00 00 03

00 01 :代表该连接的第1个请求

​ 00 00 :代表Modbus TCP协议

00 06 :后面数据长度,单位字节

01 :设备地址

03 :功能码

00 00 :起始地址

​ 00 03 :寄存器数量

​ 响应

00 01 00 00 00 09 01 03 06 00 21 00 00 00 00

​ 06 :后面数据长度

00 21 00 00 00 00:标识三个寄存器中的值(00 21代表第一个,00 00 第二 , 00 00 第三个 )

功能码

0x01: 读线圈寄存器0x02: 读离散输入寄存器0x03: 读保持寄存器0x04: 读输入寄存器0x05: 写单个线圈寄存器0x06: 写单个保持寄存器0x0f:  写多个线圈寄存器0x10: 写多个保持寄存器

3.虚拟机

下载地址 : Modbus Slave Sim V3.2 x 64

进入页面

模拟器初始页面

连接

模拟器中设置链接

配置选择不同的寄存器/线圈

选择不同的寄存器

4.Java实现

关于Modbus相关的jar包

代码使用 Modbus4J 来演示

引入Modbus4Jjar包

        <dependencies>        <dependency>            <groupId>com.infiniteautomationgroupId>            <artifactId>modbus4jartifactId>            <version>3.0.4version>        dependency>dependencies><repositories>        <repository>            <releases>                <enabled>falseenabled>            releases>            <snapshots>                <enabled>trueenabled>            snapshots>            <id>ias-snapshotsid>            <name>Infinite Automation Snapshot Repositoryname>            <url>https://maven.mangoautomation.net/repository/ias-snapshot/url>        repository>        <repository>            <releases>                <enabled>trueenabled>            releases>            <snapshots>                <enabled>falseenabled>            snapshots>            <id>ias-releasesid>            <name>Infinite Automation Release Repositoryname>            <url>https://maven.mangoautomation.net/repository/ias-release/url>        repository>    repositories>

代码实现读取和写入

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.*;public class ModbusUtils {        static ModbusFactory modbusFactory;    static ModbusMaster modbusMaster;    static {        if(modbusFactory == null){            modbusFactory = new ModbusFactory();        }    }        public static ModbusMaster getMaster() throws ModbusInitException {        if(modbusMaster == null){            IpParameters ipParameters = new IpParameters();            ipParameters.setHost("127.0.0.1");            ipParameters.setPort(502);            modbusMaster = modbusFactory.createTcpMaster(ipParameters, true);            modbusMaster.init();            return modbusMaster;        }        return modbusMaster;    }        public static Boolean readCoilStatus(int slaveId,int offset) throws ModbusInitException, ModbusTransportException, ErrorResponseException {        BaseLocator<Boolean> coilStatus = BaseLocator.coilStatus(slaveId, offset);        Boolean res = getMaster().getValue(coilStatus);        return res;    }        public static Boolean inputStatus(int slaveId,int offset) throws ModbusInitException, ModbusTransportException, ErrorResponseException {        BaseLocator<Boolean> inputStatus = BaseLocator.inputStatus(slaveId, offset);        Boolean res = getMaster().getValue(inputStatus);        return res;    }        public static Number holdingRegister(int slaveId, int offset, int dataType) throws ModbusInitException, ModbusTransportException, ErrorResponseException {        BaseLocator<Number> holdingRegister = BaseLocator.holdingRegister(slaveId, offset, dataType);        Number value = getMaster().getValue(holdingRegister);        return value;    }        public static Number inputRegister(int slaveId, int offset, int dataType) throws ModbusInitException, ModbusTransportException, ErrorResponseException {        BaseLocator<Number> inputRegister = BaseLocator.inputRegister(slaveId, offset, dataType);        Number value = getMaster().getValue(inputRegister);        return value;    }        public static Boolean writeCoilStatus(int slaveId,int offset,boolean status) throws ModbusTransportException, ModbusInitException {        boolean coilValue = status;        WriteCoilRequest coilRequest = new WriteCoilRequest(slaveId, offset, coilValue);        WriteCoilResponse coilResponse = (WriteCoilResponse) getMaster().send(coilRequest);        return !coilResponse.isException();    }        public static Boolean writeRegister(int slaveId,int offset,int vlaue) throws ModbusTransportException, ModbusInitException {        WriteRegisterRequest registerRequest = new WriteRegisterRequest(slaveId,offset,vlaue);        WriteRegisterResponse registerResponse = (WriteRegisterResponse) getMaster().send(registerRequest);        return !registerResponse.isException();    }        public static Boolean writeCoils(int slaveId,int offset,boolean[] booleans) throws ModbusTransportException, ModbusInitException {        WriteCoilsRequest writeCoils = new WriteCoilsRequest(slaveId, offset, booleans);        WriteCoilsResponse coilsResponse = (WriteCoilsResponse) getMaster().send(writeCoils);        return !coilsResponse.isException();    }        public static Boolean writeRegisters(int slaveId,int offset,short[] nums) throws ModbusTransportException, ModbusInitException {        WriteRegistersRequest writeRegisters = new WriteRegistersRequest(slaveId, offset, nums);        WriteRegistersResponse registersResponse = (WriteRegistersResponse) getMaster().send(writeRegisters);        return !registersResponse.isException();    }    public static void main(String[] args) throws ModbusInitException, ModbusTransportException, ErrorResponseException {        // 01测试//        Boolean v0001 = readCoilStatus(1, 0);//        Boolean v0002 = readCoilStatus(1, 1);//        Boolean v0008 = readCoilStatus(1, 7);//        System.out.println("get v0001 :" + v0001);//        System.out.println("get v0002 :" + v0002);//        System.out.println("get v0008 :" + v0008);        // 03测试//        Number v0001 = holdingRegister(136, 3, DataType.TWO_BYTE_INT_SIGNED);//        Number v0003 = holdingRegister(1, 2, DataType.TWO_BYTE_INT_SIGNED);//        Number v0009 = holdingRegister(1, 8, DataType.TWO_BYTE_INT_SIGNED);//        System.out.println("get v0001  result:" + v0001);//        System.out.println("get v0003  result:" + v0003);//        System.out.println("get v0009  result:" + v0009);        // 04测试//        Number v0001 = inputRegister(136, 0, DataType.TWO_BYTE_INT_SIGNED);//        Number v0003 = inputRegister(136, 2, DataType.TWO_BYTE_INT_SIGNED);//        Number v0009 = inputRegister(136, 8, DataType.TWO_BYTE_INT_SIGNED);//        System.out.println("get v0001  result:" + v0001);//        System.out.println("get v0003  result:" + v0003);//        System.out.println("get v0009  result:" + v0009);        // 05测试//        Boolean v0001 = writeCoilStatus(1, 0, true);//        Boolean v0002 = writeCoilStatus(1, 1, false);//        Boolean v0007 = writeCoilStatus(1, 6, true);//        System.out.println("update v0001 status result:" + v0001);//        System.out.println("update v0002 status result:" + v0002);//        System.out.println("update v0007 status result:" + v0007);        // 06测试//        Boolean v0001 = writeRegister(136, 0, 98);//        Boolean v0002 = writeRegister(136, 1, 0);//        Boolean v0007 = writeRegister(136, 6, 100);//        System.out.println("update v0001 status result:" + v0001);//        System.out.println("update v0002 status result:" + v0002);//        System.out.println("update v0007 status result:" + v0007);        // 0f测试        //Boolean res1 = writeCoils(1, 1, new boolean[]{true, true, false, true});        // 10测试        //Boolean res2 = writeRegisters(136, 7, new short[]{1, 2, 3});        //Boolean res3 = writeRegisters(136, 7, new short[]{991, 778, 25, 0});    }}

测试结果

​ 01测试 (读取线圈寄存器状态)

测试读取线圈状态数据结果

 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 01 00 00 00 06 01 01 00 00 00 01  [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 01 00 00 00 04 01 01 01 01  [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 02 00 00 00 06 01 01 00 01 00 01  [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 02 00 00 00 04 01 01 01 00  [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Encap Request: 00 03 00 00 00 06 01 01 00 07 00 01  [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Sending on port: 502 [main] DEBUG com.serotonin.modbus4j.ip.tcp.TcpMaster - Response: 00 03 00 00 00 04 01 01 01 01 get v0001 :trueget v0002 :falseget v0008 :true

读取对应的寄存器/线圈 系统中能获取到模拟器中的数据。

写入对应的寄存器/线圈 模拟器中的数据也会发生变化。

来源地址:https://blog.csdn.net/m0_58567231/article/details/129349968

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-人工智能
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯