C# 开发Modbus Rtu客户端 modbus测试Demo,Modbus 串口通信 , 虚拟MODBUS-RTU测试

前言



本文将使用一个NuGet公开的组件技术来实现一个ModBus RTU的客户端,方便的对Modbus rtu的服务器进行读写,这个服务器可以是电脑端C#设计的,也可以是PLC实现的,也可以是其他任何支持这个通信协议的服务器。

github地址:https://github.com/dathlin/HslCommunication 如果喜欢可以star或是fork,还可以打赏支持。

在Visual Studio 中的NuGet管理器中可以下载安装,也可以直接在NuGet控制台输入下面的指令安装:

Install-Package HslCommunication

NuGet安装教程  http://www.cnblogs.com/dathlin/p/7705014.html

技术支持QQ群:592132877  (组件的版本更新细节也将第一时间在群里发布)组件API地址:http://www.cnblogs.com/dathlin/p/7703805.html

特别感谢


  • 网友:陈恩富                  对float,int数据的读取测试,才修复了权重位颠倒的BUG。
  • 网友:U4幸福的蜗牛      发现了博客上错误的一个方法名称,已于2018年1月8日13:34:39更新。并反馈了一些特殊设备(modbus tcp服务器)的读取数据的BUG。已修复。

随便聊聊



此处的设计模式是客户端主动请求服务器数据,然后接收服务器的反馈数据,支持原生的指令收发,支持其他一些方便的API收发。特殊功能码需要使用原生收发的API,本组件支持如下的功能操作:

  • 0x01    读取线圈的操作,
  • 0x02    读取离散的操作,
  • 0x03    读取寄存器的值,
  • 0x05    写一个线圈操作,
  • 0x06    写一个寄存器值,
  • 0x0F    批量写线圈操作,
  • 0x10    批量写寄存器值,

如果你的设备需要这些功能之外的数据,可以使用原生API方法,但是这个方法的前提就是你对MODBUS 协议非常清晰才可以,如果你不了解这个协议,可以参照下面的博客说明:

http://blog.csdn.net/thebestleo/article/details/52269999

如果你需要搭建自己的ModBus服务器,可以参照这边文章:http://www.cnblogs.com/dathlin/p/7782315.html

访问测试项目

需要下载一个串口虚拟的软件

Virtual Serial Port Driver

下载地址:https://virtual-serial-port-driver.en.softonic.com/

然后虚拟化两个串口出来,COM4 ,COM5 默认是连接在一起的。这样我们就可以进行本地的测试了

在你开发自己的客户端程序之前,可以先用MODBUS测试工具进行测试,以下地址的一个开源项目就是基于这个组件开发的Modbus rtu测试工具,可直接用于读写测试。

ModbusTcpServer.zip   先启动服务,然后启动串口



下面的一个项目是这个组件的访问测试项目,您可以进行初步的访问的测试,免去了您写测试程序的麻烦,这个项目是和三菱,西门子PLC的访问写在一起的。可以同时参考。

下载地址为:HslCommunicationDemo.zip

Reference



ModBus组件所有的功能类都在 HslCommunication.ModBus命名空间,所以再使用之前先添加

using HslCommunication.ModBus;
using HslCommunication;

  

How to Use



实例化:

在使用读写功能之前必须先进行实例化:

private ModbusRtu busRtuClient = new ModbusRtu( station );

注意:在Modbus服务器的设备里,大部分的设备都是从地址0开始的,有些特殊的设备是从地址1开始的,所以本组件里面,默认从地址0开始,如果想要从地址1开始,那么就需要如下的配置:

busRtuClient.AddressStartWithZero = False;

然后接下来需要初始化参数,对串口来说,通常的参数有串口名称,波特率,数据位,停止位,校验位,提供了一个委托设置的方式

            try
            {
                busRtuClient.SerialPortInni( sp =>
                 {
                     sp.PortName = "COM5";
                     sp.BaudRate = 9600;
                     sp.DataBits = 8;
                     sp.StopBits = System.IO.Ports.StopBits.One;
                     sp.Parity = System.IO.Ports.Parity.None;
                 } );
                busRtuClient.Open( ); // 打开
            }
            catch (Exception ex)
            {
                MessageBox.Show( ex.Message );
            }

关闭的话,调用如下的方法

busRtuClient.Close( );

以下代码演示常用的读写操作,为了方便起见,不再对IsSuccess判断,一般都是成功的:

        private void userButton30_Click(object sender, EventArgs e)
        {
            // 读取操作
            bool coil100 = busRtuClient.ReadCoil("100").Content;   // 读取线圈100的通断
            short short100 = busRtuClient.ReadInt16("100").Content; // 读取寄存器100的short值
            ushort ushort100 = busRtuClient.ReadUInt16("100").Content; // 读取寄存器100的ushort值
            int int100 = busRtuClient.ReadInt32("100").Content;      // 读取寄存器100-101的int值
            uint uint100 = busRtuClient.ReadUInt32("100").Content;   // 读取寄存器100-101的uint值
            float float100 = busRtuClient.ReadFloat("100").Content; // 读取寄存器100-101的float值
            long long100 = busRtuClient.ReadInt64("100").Content;    // 读取寄存器100-103的long值
            ulong ulong100 = busRtuClient.ReadUInt64("100").Content; // 读取寄存器100-103的ulong值
            double double100 = busRtuClient.ReadDouble("100").Content; // 读取寄存器100-103的double值
            string str100 = busRtuClient.ReadString("100", 5).Content;// 读取100到104共10个字符的字符串

            // 写入操作
            busRtuClient.WriteCoil("100", true);// 写入线圈100为通
            busRtuClient.Write("100", (short)12345);// 写入寄存器100为12345
            busRtuClient.Write("100", (ushort)45678);// 写入寄存器100为45678
            busRtuClient.Write("100", 123456789);// 写入寄存器100-101为123456789
            busRtuClient.Write("100", (uint)123456778);// 写入寄存器100-101为123456778
            busRtuClient.Write("100", 123.456);// 写入寄存器100-101为123.456
            busRtuClient.Write("100", 12312312312414L);//写入寄存器100-103为一个大数据
            busRtuClient.Write("100", 12634534534543656UL);// 写入寄存器100-103为一个大数据
            busRtuClient.Write("100", 123.456d);// 写入寄存器100-103为一个双精度的数据
            busRtuClient.Write("100", "K123456789");

        }

下面再分别讲解严格的操作,以及批量化的复杂的读写操作,假设你要读取1000个M,循环读取1千次可能要3秒钟,如果用了下面的批量化读取,只需要50ms,但是需要你对字节的原理比较熟悉才能得心应手的处理

读取线圈API:

在此处举例读取地址为0,长度为10的线圈数量,读取出来的数据已经自动转化成了bool数组,方便的进行二次处理:

        private void userButton8_Click(object sender,EventArgs e)
        {
            HslCommunication.OperateResult<bool[]> read = busRtuClient.ReadCoil("0", 10);
            if(read.IsSuccess)
            {
                bool coil_0 = read.Content[0];
                bool coil_1 = read.Content[1];
                bool coil_2 = read.Content[2];
                bool coil_3 = read.Content[3];
                bool coil_4 = read.Content[4];
                bool coil_5 = read.Content[5];
                bool coil_6 = read.Content[6];
                bool coil_7 = read.Content[7];
                bool coil_8 = read.Content[8];
                bool coil_9 = read.Content[9];
            }
            else
            {
                MessageBox.Show(read.ToMessageShowString());
            }
        }

  当然也可以用组件提供的数据转换API实现数据提取:

读取离散数据:

读取离散数据和读取线圈的代码几乎是一致的,处理方式也是一致的,只是方法名称改成了:

        private void userButton8_Click(object sender,EventArgs e)
        {
            HslCommunication.OperateResult<bool[]> read = busRtuClient.ReadDiscrete("0", 10);
            if(read.IsSuccess)
            {
                bool coil_0 = read.Content[0];
                bool coil_1 = read.Content[1];
                bool coil_2 = read.Content[2];
                bool coil_3 = read.Content[3];
                bool coil_4 = read.Content[4];
                bool coil_5 = read.Content[5];
                bool coil_6 = read.Content[6];
                bool coil_7 = read.Content[7];
                bool coil_8 = read.Content[8];
                bool coil_9 = read.Content[9];
            }
            else
            {
                MessageBox.Show(read.ToMessageShowString());
            }
        }

读取寄存器数据:

假设我们需要读取地址为0,长度为10的数据,也即是10个数据,每个数据2个字节,总计20个字节的数据。下面解析数据前,先进行了假设,你在解析自己的数据前可以参照下面的解析

        private void userButton10_Click(object sender, EventArgs e)
        {
            HslCommunication.OperateResult<byte[]> read = busRtuClient.Read("0", 10);
            if (read.IsSuccess)
            {
                // 共返回20个字节,每个数据2个字节,高位在前,低位在后
                // 在数据解析前需要知道里面到底存了什么类型的数据,所以需要进行一些假设:
                // 前两个字节是short数据类型
                short value1 = busTcpClient.ByteTransform.TransInt16(read.Content, 0);
                // 接下来的2个字节是ushort类型
                ushort value2 = busTcpClient.ByteTransform.TransUInt16(read.Content, 2);
                // 接下来的4个字节是int类型
                int value3 = busTcpClient.ByteTransform.TransInt32(read.Content, 4);
                // 接下来的4个字节是float类型
                float value4 = busTcpClient.ByteTransform.TransFloat(read.Content, 8);
                // 接下来的全部字节,共8个字节是规格信息
                string speci = Encoding.ASCII.GetString(read.Content, 12, 8);

                // 已经提取完所有的数据
            }
            else
            {
                MessageBox.Show(read.ToMessageShowString());
            }
        }

写一个线圈:

写一个线圈,这个相对比较简单,假设我们需要写入线圈0,为通

        private void userButton11_Click(object sender, EventArgs e)
        {
            HslCommunication.OperateResult write = busRtuClient.WriteCoil("0", true);
            if (write.IsSuccess)
            {
                // 写入成功
                textBox1.Text = "写入成功";
            }
            else
            {
                MessageBox.Show(write.ToMessageShowString());
            }
        }

写一个寄存器:

写一个寄存器的操作也是非常的方便,在这里提供了三个重载的方法,允许使用三种方式写入:分别写入,short,ushort,byte三种:

        private void userButton12_Click(object sender, EventArgs e)
        {
            short value = -1234;
            HslCommunication.OperateResult write = busRtuClient.WriteOneRegister("0", value);
            if (write.IsSuccess)
            {
                // 写入成功
                textBox1.Text = "写入成功";
            }
            else
            {
                MessageBox.Show(write.ToMessageShowString());
            }
        }

  

        private void userButton12_Click(object sender, EventArgs e)
        {
            ushort value = 56713;
            HslCommunication.OperateResult write = busRtuClient.WriteOneRegister("0", value);
            if (write.IsSuccess)
            {
                // 写入成功
                textBox1.Text = "写入成功";
            }
            else
            {
                MessageBox.Show(write.ToMessageShowString());
            }
        }

  

        private void userButton12_Click(object sender, EventArgs e)
        {
            // 0x00为高位,0x10为低位
            HslCommunication.OperateResult write = busRtuClient.WriteOneRegister("0", 0x00, 0x10);
            if (write.IsSuccess)
            {
                // 写入成功
                textBox1.Text = "写入成功";
            }
            else
            {
                MessageBox.Show(write.ToMessageShowString());
            }
        }

批量写入线圈:

private void userButton13_Click(object sender, EventArgs e)
        {
            // 线圈0为True,线圈1为false,线圈2为true.....等等,以此类推,数组长度多少,就写入多少线圈
            bool[] value = new bool[] { true, false, true, true, false, false };
            HslCommunication.OperateResult write = busRtuClient.WriteCoil("0", value);
            if (write.IsSuccess)
            {
                // 写入成功
                textBox1.Text = "写入成功";
            }
            else
            {
                MessageBox.Show(write.ToMessageShowString());
            }
        }

  

批量写入寄存器:

第一种情况写入一串short数组,这种情况比较简单:

        private void userButton14_Click(object sender, EventArgs e)
        {
            short[] value = new short[] { -1234, 467, 12345 };
            HslCommunication.OperateResult write = busRtuClient.Write("0", value);
            if (write.IsSuccess)
            {
                // 写入成功
                textBox1.Text = "写入成功";
            }
            else
            {
                MessageBox.Show(write.ToMessageShowString());
            }
        }

第二情况写入一串ushort数组,也是比较简单:

        private void userButton14_Click(object sender, EventArgs e)
        {
            ushort[] value = new ushort[] { 46789, 467, 12345 };
            HslCommunication.OperateResult write = busRtuClient.Write("0", value);
            if (write.IsSuccess)
            {
                // 写入成功
                textBox1.Text = "写入成功";
            }
            else
            {
                MessageBox.Show(write.ToMessageShowString());
            }
        }

比较复杂的是写入自定义的数据,按照上述读取寄存器,比如我需要写入寄存器0,寄存器1共同组成的一个int数据,那么我们这么写:

        private void userButton15_Click(object sender, EventArgs e)
        {
            int value = 12345678;// 等待写入的一个数据

            HslCommunication.OperateResult write = busRtuClient.Write("0", value);
            if (write.IsSuccess)
            {
                // 写入成功
                textBox1.Text = "写入成功";
            }
            else
            {
                MessageBox.Show(write.ToMessageShowString());
            }
        }

其他数据参考这个就行,如果有不明白的,可以联系上面的QQ群。

究极数据操作,使用原生的报文来操作数据:

传入一个字节数组,数据内容和原生的数据一致,比如我要通过原生API读取寄存器地址为0,长度为3的数据,那么字节的HEX标识形式为 01 03 00 00 00 03 不包括CRC校验码

        private void button26_Click( object sender, EventArgs e )
        {
            try
            {
                OperateResult<byte[]> read = busRtuClient.ReadBase( HslCommunication.Serial.SoftCRC16.CRC16(HslCommunication.BasicFramework.SoftBasic.HexStringToBytes( "01 03 00 00 00 03" )) );
                if (read.IsSuccess)
                {
                    textBox11.Text = "结果:" + HslCommunication.BasicFramework.SoftBasic.ByteToHexString( read.Content,‘ ‘ );
                }
                else
                {
                    MessageBox.Show( "读取失败:" + read.ToMessageShowString( ) );
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show( "读取失败:" + ex.Message );
            }
        }

  上述代码在操作时用了一个转化机制,输入为十六进制的文本,转化为byte[]数据,中间的分割符可以为空格,可以为‘-‘,也可以为‘,‘,‘_‘等等等等,调用了组件基础的数据转化功能。

原文地址:https://www.cnblogs.com/dathlin/p/8974215.html

时间: 2024-10-25 11:15:58

C# 开发Modbus Rtu客户端 modbus测试Demo,Modbus 串口通信 , 虚拟MODBUS-RTU测试的相关文章

三菱PLC 串口通信开发心得经验

三菱PLC 串口通信开发心得 备注: 记得两年前刚开始从事软件开发工作时,第一份任务就是开发一个程序能够实现与三菱PLC 串口通信.所谓通信,其实质主要是对PLC 的D寄存器(dword)读写操作.但是因为日本为了保护其产品,并不开发串口通信协议.在不开发通信协议的情况,如果想实现通信,首先需要做的便是通过数据分析,破解其通信协议. 这里就不讲解如何破解了,主要是介绍下当时博主开发程序的背景. 写这篇博客的主要目的是为了分享过去自己的开发经验,因为自己在开发的过程中曾经接受过很多开源软件的帮助,

串口屏(触摸屏)组态软件+多台51单片机MODBUS RTU多机串口通信程序源码

串口屏(触摸屏)组态软件+多台51单片机MODBUS RTU多机串口通信程序源码实现触摸屏(串口屏)与单片机的通讯,主要是解决通讯协议的问题.本文使用开放的Modbus通讯协议,以广州易显的HMImaker触摸屏作主机(Master),单片机作从机(Slaver).HMImaker触摸屏本身支持Modbus通讯协议,只要单片机按照Modbus协议进行收发数据,就可以进行通信了.触摸屏与单片机之间采用RS-485标准接口直接连接,与多台51单片机MODBUS RTU多机串口通信一.包括如下实例:二

unity游戏开发之自定义事件测试demo

上文中写了unity游戏开发自定义消息事件点击打开链接 下面是测试demo 第一,打卡unity,新建一个场景,然后新建一个空的游戏对象,如图中的EventObj 第二步,测试代码EventTest.as,直接拖拽给上面的空游戏对象EentObj 测试代码如下: using UnityEngine; using System.Collections; public class EventTest : MonoBehaviour { // Use this for initialization v

使用node-webkit开发Clover桌面客户端的一些记录(一)

首先说一说Clover. 这是一个OA产品,是我们公司组建我们技术部以来最最重要的一项工作 -- 开发企业内部使用的管理系统."Clover"这个名字是我们老大起的,拆开看是"C"+"lover"."lover"都懂是啥,而这个"C"呢,其实就是"Code",因为我们老大是一个(狂热的)编码爱好者(汗...),精通很多门语言,所以就隐晦的把自己的爱好写进项目名称,就连我们测试服务器的地址

Wookmark-jQuery-master 瀑布流插件使用介绍,含个人测试DEMO

要求 必备知识 本文要求基本了解 Html/CSS,  JavaScript/JQuery. 开发环境 Dreamweaver CS6 / Chrome浏览器 演示地址 演示地址 资料下载 测试预览截图(抬抬你的鼠标就可以看到演示地址哦): 程序核心代码看这里: $(function(){ function show(){ var colors=["#BA4A3A","#5BB5D6","#8EA83B","#EE5C92",

【WP 8.1开发】手机客户端应用接收推送通知

上一篇文章中,已经完成了用于发送通知的服务器端,接下来我们就用这个服务端来测试一下. 在开始测试之前,我们要做一个接收通知的WP应用. 1.启动VS Express for Windows,新建项目,在项目模板中选择“空白应用程序(Windows Phone)”. 2.既然要接收通知,肯定少不了Toast.磁贴这几样常用的通知的,故我们得先准备一些图片. 在“解决方案资源管理器”中,双击打开清单文件,切换到“可见资产”选项卡,这个“资产”指的不你的银行卡存款有多少,而是你的应用中的一些如图片.音

大数据技术之_04_Hadoop学习_01_HDFS_HDFS概述+HDFS的Shell操作(开发重点)+HDFS客户端操作(开发重点)+HDFS的数据流(面试重点)+NameNode和SecondaryNameNode(面试开发重点)

第1章 HDFS概述1.1 HDFS产出背景及定义1.2 HDFS优缺点1.3 HDFS组成架构1.4 HDFS文件块大小(面试重点)第2章 HDFS的Shell操作(开发重点)第3章 HDFS客户端操作(开发重点)3.1 HDFS客户端环境准备3.2 HDFS的API操作3.2.1 HDFS文件上传(测试参数优先级)3.2.2 HDFS文件下载3.2.3 HDFS文件夹删除3.2.4 HDFS文件名更改3.2.5 HDFS文件详情查看3.2.6 HDFS文件和文件夹判断3.3 HDFS的I/O

ArcGIS API for JavaScript开发环境搭建及第一个实例demo

原文:ArcGIS API for JavaScript开发环境搭建及第一个实例demo ESRI公司截止到目前已经发布了最新的ArcGIS Server for JavaScript API v3.9,它提供了更为丰富而又强大的功能.     一.安装前准备 1.ArcGIS Server for JavaScript API各版本下载地址:http://support.esrichina-bj.cn/2011/0223/960.html,我们选择下载最新的"ArcGIS API for Ja

基于 SailingEase WinForm Framework 开发优秀的客户端应用程序(目录)

本系统文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以  SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客户端应用程序的设计与开发. 基于 SailingEase WinForm Framework 开发优秀的客户端应用程序(1:概述)