C#通过ModbusTcp协议读写西门子PLC中的浮点数

news2025/1/9 16:25:32

一、Modbus TCP通信概述 

MODBUS/TCP是简单的、中立厂商的用于管理和控制自动化设备的MODBUS系列通讯协议的派生产品,显而易见,它覆盖了使用TCP/IP协议的“Intranet”和“Internet”环境中MODBUS报文的用途。协议的最通用用途是为诸如PLC,I/O模块,以及连接其它简单域总线或I/O模块的网关服务的。

Modbus TCP协议是在RTU协议前面添加MBAP报文头,由于TCP是基于可靠连接的服务,RTU协议中的CRC校验码就不再需要,所以在Modbus TCP协议中是没有CRC校验码。(使用上的主要区别)。MBAP报文头: 识( 2字节 ) 长度( 2字节 ) 单元标识符(1字节 )

目前Modbus TCP/IP协议主要应用领域Internet或Intranet中,而以太网传输距离远、传输速度快,使得应用范围广泛传输距离远、传输速度快,使得应用范围广泛。

 二. Modbus TCP使用的功能代码

modbus的操作对象有四种:线圈、离散输入、输入寄存器、保持寄存器

线圈:PLC的输出位,开关量,在MODBUS中可读可写
离散量:PLC的输入位,开关量,在MODBUS中只读
输入寄存器:PLC中只能从模拟量输入端改变的寄存器,在MODBUS中只读
保持寄存器:PLC中用于输出模拟量信号的寄存器,在MODBUS中可读可写
根据对象的不同,modbus的功能码有:

0x01:读线圈
0x02:读离散量输入
0x03:读保持寄存器

0x04:读输入寄存器

0x05:写单个线圈
0x06:写单个保持寄存器
0x10:写多个保持寄存器
0x0F:写多个线圈

 三、nmodbus4指南

 NModbus4是一个基于C#的Modbus协议库,可用于与Modbus RTU、ASCII、TCP和UDP设备进行通信。NModbus4中文版相当于对原版进行了翻译,使得不懂英文的人能够更方便地使用这个开源库进行编程。NModbus4是用C#编写的Modbus通信协议库,它支持的Modbus协议包括Modbus RTU、ASCII、TCP和UDP,可用于编程读写Modbus设备的寄存器和线圈。它完全符合Modbus协议规范,同时通过使用的事件调用机制,能够实现断线重连的功能。

NModbus4是一个完全开源的库,可以在GitHub上免费下载和使用

四、Modbus TCP通讯应用举例 

 4.1:搭建西门子博途V15的环境

搭建西门子仿真环境,需要先前掌握这些,看本人这些博客

windows10企业版安装西门子博途V15---01准备环境

windows10企业版安装西门子博途V15---02安装软件

windows10企业版安装西门子博途V15---03安装仿真软件

windows10企业版安装西门子博途V15---04连接测试
 

4.2:熟悉modbusTCP环境

需要先前掌握这些,看本人这些博客

4.3:创建PLC仿真环境

4.4: 博途V15创建项目

 本文最后会提供这个项目,只要打开即可

 4.5:创建数据块变量

4.6:创建tcp连接数据块

4.7:创建modbustcp通信模块

请注意,这里为什么是BYTE 20,是因为变量mf1到mf5共10寄存器,每个寄存器占2个字节,所以是20个字节,编译完成后,下载到Plc中

4.8:创建监控表

 

以上8个步骤就完成了modbustcp服务器,接下来搞程序,来读写Plc中的浮点数

4.9:创建winform项目

打开VS2019,创建窗体项目,布局很简单,4个button按钮

 

4.10:添加nmodbus4库

 

4.11:编写“nmodbus4读取一个float”代码

 /// <summary>
        /// nmodbus4读取一个float
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button4_Click(object sender, EventArgs e)
        {
            //nmodbus4读取到的数据都是ushort类型
            tcpClient = new TcpClient();
            tcpClient.Connect("192.168.1.199", 6800);//连接到主机
            master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站  
            byte slaveAddr = byte.Parse("1");//从站地址
            //ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints);表示读保持寄存器
            //slaveAddress从站地址(默认为1,通常也是1)
            //startAddress寄存器开始地址(这个地址是modbus的地址,不是Plc变量地址)
            //numberOfPoints寄存器数量(real类型占2个寄存器数量)
            ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("0"), ushort.Parse("2"));
            byte[] t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);//ushort数组转byte数组
            float[] floats = FloatLib.GetFloatArrayFromByteArray(t);//byte数组转float数组
            string fw1a = string.Join(",", floats);
            float fw1b = FloatLib.GetFloatFromByteArray(t, 0);//byte数组转float
            MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());

            uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("2"), ushort.Parse("2"));
            t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
            floats = FloatLib.GetFloatArrayFromByteArray(t);
            fw1a = string.Join(",", floats);
            fw1b = FloatLib.GetFloatFromByteArray(t, 0);
            MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());

            uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("4"), ushort.Parse("2"));
            t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
            floats = FloatLib.GetFloatArrayFromByteArray(t);
            fw1a = string.Join(",", floats);
            fw1b = FloatLib.GetFloatFromByteArray(t, 0);
            MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());

            uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("6"), ushort.Parse("2"));
            t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
            floats = FloatLib.GetFloatArrayFromByteArray(t);
            fw1a = string.Join(",", floats);
            fw1b = FloatLib.GetFloatFromByteArray(t, 0);
            MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());

            uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("8"), ushort.Parse("2"));
            t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
            floats = FloatLib.GetFloatArrayFromByteArray(t);
            fw1a = string.Join(",", floats);
            fw1b = FloatLib.GetFloatFromByteArray(t, 0);
            MessageBox.Show("方式a:" + fw1a.ToString() + ",方式b:" + fw1b.ToString());
            master.Dispose();
            tcpClient.Dispose();
        }

运行效果

 全部读取到了PLC中的数据

 nmodbus4读取到的数据都是ushort类型

 ushort[] ReadHoldingRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints);表示读保持寄存器
            slaveAddress从站地址(默认为1,通常也是1)
            startAddress寄存器开始地址(这个地址是modbus的地址,不是Plc变量地址)
            numberOfPoints寄存器数量(real类型占2个寄存器数量)

 ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("0"), ushort.Parse("2"));

这个意思是读取从站地址1中的从0开始的2个寄存器数据,即%DB4.DBD0中的数据,结果是1.1

很多人搞不清楚,这个为何是开始地址0,数量是2,这就需要明白PLC中的地址与MODBUS地址的关系,另外nmodbus4读取到的数据都是ushort类型,因此需要进行类型转换,将ushort数组转byte数组,再将byte数组转float数组

4.12:编写“nmodbus4读取全部float”代码

 /// <summary>
        /// nmodbus4读取全部
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button5_Click(object sender, EventArgs e)
        {
            tcpClient = new TcpClient();
            tcpClient.Connect("192.168.1.188", 6800);//连接到主机
            master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站  
            byte slaveAddr = byte.Parse("1");
            ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, ushort.Parse("5"), ushort.Parse("10"));
            byte[] t = ByteArrayLib.GetByteArrayFromUShortArray(uDatas);
            float[] floats = FloatLib.GetFloatArrayFromByteArray(t);
            string fw1a = string.Join(",", floats);
            MessageBox.Show(fw1a.ToString());

            master.Dispose();
            tcpClient.Dispose();
        }

运行效果

4.13:编写“nmodbus4写入单个浮点”代码

/// <summary>
        /// nmodbus4写入单个浮点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button7_Click(object sender, EventArgs e)
        {
            tcpClient = new TcpClient();
            tcpClient.Connect("192.168.1.199", 6800);//连接到主机
            master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站   
            //从站地址
            byte slaveAddr = byte.Parse("1");
            开始地址
            ushort startAddr = ushort.Parse("0");
            数据的值
            string vals = ("12.625");
            float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
            byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);
            ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);
            //void WriteMultipleRegisters(byte slaveAddress, ushort startAddress, ushort[] data);//写入多保持寄存器,意思是指向多个寄存器地址写入数据,也就是指同时向多个寄存器写入数据
            //slaveAddress表示从站地址,通常为1,默认也为1
            //startAddress表示寄存器开始地址,必须是ushort类型
            //data表示写入的具体数值,必须是ushort数组
            master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);//向第一个寄存器(地址是0)写入数据12.625
            MessageBox.Show("【 通过多保持寄存器】写入正数成功!");

            float floatValue = 12.625f;
            startAddr = ushort.Parse("2");
            byte[] byteArray = ByteArrayLib.GetByteArrayFromFloat(floatValue);
            //将字节数组中第0个开始的2个字节转换成ushort类型,即0,1
            ushort ua = UShortLib.GetUShortFromByteArray(byteArray, 0, DataFormat.ABCD);
            master.WriteSingleRegister(slaveAddr, startAddr, ua);//向从站地址1中的第3个寄存器(地址为2)写入数据ua
            //将字节数组中第2个开始的2个字节转换成ushort类型,即2,3 
            ushort ub = UShortLib.GetUShortFromByteArray(byteArray, 2, DataFormat.ABCD);
            startAddr = ushort.Parse("3");
            master.WriteSingleRegister(slaveAddr, startAddr, ub);//向从站地址1中的第4个寄存器(地址为3)写入数据ub
            MessageBox.Show("【 通过单保持寄存器】写入正数成功!"); 

            vals = ("-18.326");
            startAddr = ushort.Parse("8");
            uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
            y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);
            ushorts = UShortLib.GetUShortArrayFromByteArray(y);
            master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);
            MessageBox.Show("【 通过多保持寄存器】写入负数成功!");

            master.Dispose();
            tcpClient.Dispose();
        }

 运行效果

 4.14:编写“nmodbus4写入多个浮点"代码

 /// <summary>
        /// nmodbus4写入多个浮点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button6_Click(object sender, EventArgs e)
        {
            tcpClient = new TcpClient();
            tcpClient.Connect("192.168.1.199", 6800);//连接到主机
            master = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站  

            //从站地址
            byte slaveAddr = byte.Parse("1");
            //开始地址
            ushort startAddr = ushort.Parse("0");
            //数据的值
            string vals = ("4.9635,6.9635,-1.28,67,-902");
            float[] uVals02 = vals.Split(',').Select(s => float.Parse(s)).ToArray();
            byte[] y = ByteArrayLib.GetByteArrayFromFloatArray(uVals02);
            ushort[] ushorts = UShortLib.GetUShortArrayFromByteArray(y);
            master.WriteMultipleRegisters(slaveAddr, startAddr, ushorts);
            MessageBox.Show("【 多保持寄存器】写入成功!");
            master.Dispose();
            tcpClient.Dispose();
        }

 运行效果

浮点数包括整数,小数,也包括正数或负数,所以正整数,负整数,正小数,负小数都可以写入

五:modbustcp协议小结

MODBUS TCP 结合了以太网物理网络和网络标准 TCP/IP 以及以 MODBUS 作为应用协议标准的数据表示方法。MODBUS TCP 通信报文被封装于以太网 TCP/IP 数据包中,MODBUS 协议规范一帧数据的最大长度为 256 个字节。

MODBUS TCP/IP 的通信系统中有两种类型的设备:MODBUS TCP/IP 客户端和服务器设备。

1.MODBUS 客户端

客户端(TCP Client)主动向服务器(TCP Server)发起连接请求,连接建立成功,仅允许客户端主动发起通讯请求。

以太网机型作为 MODBUS TCP 客户端时,通过 S_OPEN 指令建立 TCP 连接,通过 M_TCP 指令发起 MODBUS 请求。

2.MODBUS 服务器

服务器主动监听 502 端口,等待客户端连接请求,连接建立成功,响应符合 Modbus TCP 协议规范的数据通讯请求。

3.优势 

优势: 免费、简单、容易使用,Modbus协议是现在国内工业领域应用最多的协议,不只PLC设备,各种终端设备,比如水控机、水表、电表、工业秤、各种采集设备,

4.特点

  1. 采用主从问答方式进行通信

  2. Modbus TCP是应用层协议,基于传输层TCP协议实现

  3. Modbus TCP端口号默认为502,但在本案例中修改成6800,当然你也可以改成别的

六:代码下载

 链接:https://pan.baidu.com/s/1mARLDATOBphLKbecj4sW8g 
提取码:lggv  

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是rem单位和em单位?它们有什么区别?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ rem 和 em 单位⭐ rem 单位&#xff08;Root Em&#xff09;⭐ em 单位⭐ 区别总结⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入…

.net core 上传文件大小限制

微软官网文档中给的解释是.net core 默认上传文件大小限制是30M&#xff0c;所以即便你项目里没有限制&#xff0c;这里也有个默认限制。 官网链接地址 总结了一下解决办法&#xff1a; 1.首先项目里添加一个web.config自定义配置文件 在配置文件中加上这段配置 <!--//…

2024年java面试--多线程(3)

系列文章目录 2024年java面试&#xff08;一&#xff09;–spring篇2024年java面试&#xff08;二&#xff09;–spring篇2024年java面试&#xff08;三&#xff09;–spring篇2024年java面试&#xff08;四&#xff09;–spring篇2024年java面试–集合篇2024年java面试–redi…

Echarts使用

Echarts使用 1.ECharts简介 ECharts是一个使用JavaScript实现的开源可视化库&#xff0c;可以流畅的运行在PC和移动设备上&#xff0c;兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等)&#xff0c;底层依赖矢量图形库ZRender,.提供直观&#xff0c;交互丰富&am…

Spring Cloud--从零开始搭建微服务基础环境【四】

&#x1f600;前言 本篇博文是关于Spring Cloud–从零开始搭建微服务基础环境【四】&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;…

自然语言处理: 第十一章BERT(Bidirectional Encoder Representation from Transformers)

论文地址:[1810.04805] BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding (arxiv.org) 理论基础 之前介绍GPT的搭建的方式的时候&#xff0c;将BERT与GPT进行了对比&#xff0c;我们可以知道BERT和GPT都是基于transformer架构下的分支&…

Android 1.2.1 使用Eclipse + ADT + SDK开发Android APP

1.2.1 使用Eclipse ADT SDK开发Android APP 1.前言 这里我们有两条路可以选&#xff0c;直接使用封装好的用于开发Android的ADT Bundle&#xff0c;或者自己进行配置 因为谷歌已经放弃了ADT的更新&#xff0c;官网上也取消的下载链接&#xff0c;这里提供谷歌放弃更新前最新…

PHP8数组的类型-PHP8知识详解

php 8 引入了对数组的类型提示&#xff0c;以帮助开发者更准确地定义和验证数组的结构。以下是 PHP 8 中支持的数组类型&#xff1a;索引数组、关联数组、混合类型数组。 1、索引数组 (Indexed arrays): PHP索引数组一般表示数组元素在数组中的位置&#xff0c;它由数字组成&a…

centos7关闭防火墙和selinux(内核防火墙)

centos7关闭防火墙和selinux&#xff08;内核机制或叫内核防火墙&#xff09; 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.关闭防火墙&#xff0c;centos7默认是firewalld #关闭 systemctl stop firewalld.service #关闭开机自启 systemctl disable firewalld.ser…

Opencv 图像金字塔----高斯和拉普拉斯

原文&#xff1a;图像金字塔----高斯和拉普拉斯 图像金字塔是图像中多尺度表达的一种&#xff0c;最初用于机器视觉和图像压缩&#xff0c;最主要用于图像的分割、融合。 高斯金字塔 ( Gaussian pyramid): 高斯金字塔是由底部的最大分辨率图像逐次向下采样得到的一系列图像…

Vue.js 报错:Cannot read property ‘validate‘ of undefined“

错误解决 起因&#xff0c;是我将elemnt-ui登录&#xff0c;默认放在mounted()函数里面&#xff0c;导致vue初始化就调用这个函数。 找了网上&#xff0c;有以下错误原因&#xff1a; 1.一个是你ref写错了&#xff0c;导致获取不了这个表单dom&#xff0c;我这显然不是。 2.…

基于改进莱维飞行和混沌映射的粒子群优化算法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

机器人中的数值优化(十)——线性共轭梯度法

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

【Linux】如何手动挂载和卸载文件系统?

按块设备名称挂载按文件系统UUID挂载卸载文件系统感谢 &#x1f496; 我们必须了解&#xff0c;只有root用户可以手动挂载和卸载文件系统。 当我们切换到root用户后&#xff0c;可以使用mount命令将存储设备上的文件系统挂载到文件系统层次结构中用作挂载点的目录。 mount 命令…

Beego项目实战

Beego项目实战 beego博客项目-创建项目beego博客项目-集成gormbeego博客项目-集成Bootstrap创建用户表单beego项目实现-添加用户controller和routerBeego博客项目-设计静态页面beego博客项目-用户注册beego博客项目-用户登录beego博客项目-集成markdown编辑器beego博客项目-创建…

CCF是什么?

CCF是计算机学会&#xff08;China Computer Federation&#xff09;的英文缩写&#xff0c;是一个在中国从事计算机领域学术研究和技术发展的国家性非营利学术团体。其宗旨是促进和推动计算机科学技术的发展和应用&#xff0c;发挥学术团体在学术研究、学术交流、学术评价、学…

伺服阀放大器使用手册

控制通用型不带反馈信号输入的伺服阀放大器&#xff0c;对射流管式电液伺服阀、喷嘴挡板式电液伺服阀及国外各类电液伺服阀进行控制。 通过系统参数有10V和4~20mA输入指令信号选择&#xff1b; 供电电源: 24VDC&#xff08;标准&#xff09; 输出电流&#xff1a;最大可达10…

PHP8的多维数组-PHP8知识详解

今天分享的是php8的数组中的多维数组&#xff0c;主要内容有&#xff1a;多维数组的概念、创建和输出二维数组、创建和输出三维数组。 1、多维数组的概念 多维数组是包含一个或多个数组的数组。在多维数组中&#xff0c;主数组中的每一个元素也可以是一个数组&#xff0c;子数…

DHTMLX Gantt 8.0.5 Crack -甘特图

8.0.5 2023 年 9 月 1 日。错误修复版本 修复 修复通过gantt.getGanttInstance配置启用扩展而触发的错误警告修复启用skip_off_time配置时gantt.exportToExcel()的不正确工作示例查看器的改进 8.0.4 2023 年 7 月 31 日。错误修复版本 修复 修复数据处理器不跟踪资源数据…

RT-Thread UART

UART 简介 UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;通用异步收发传输器&#xff0c;UART 作为异步串口通信协议的一种&#xff0c;工作原理是将传输数据的每个字符一位接一位地传输。是在应用程序开发过程中使用频率最高的数据总线。 UART …