C# NModbus4 TCP 主从站通信样例
文章目录
- 实现效果
- 实现代码
- 主站
- 从站
- Modbus 协议简介
- Modbus的功能码
- 读指令对比(0x04)
- 写指令对比(0x10)
- Modbus TCP MBAP
- Modbus 主从站关系
- Modbus主站和从站区别为:发出指令不同、唯一性不同、对接不同。
- 一、发出指令不同
- 二、唯一性不同
- 三、对接不同
- NModbus4
- demo下载
- 介绍NModbus的使用,适用于Core的环境
实现效果
实现代码
主站
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Modbus.Device;
using System.Net.Sockets;
using System.Net;
using Modbus.Data;
using System.Threading;
using Modbus.Utility;/** 注释:* 当使用Modbus-TCP时,往往使用Slave作为服务端* 所以NModbus-TCP中主站使用的时Slave对象*/
namespace NModBus4.Master
{public partial class Form1 : Form{private TcpListener listener;private ModbusSlave slave;public Form1(){InitializeComponent();}/// <summary>/// 启动/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button1_Click(object sender, EventArgs e){try{listener = new TcpListener(IPAddress.Parse(textBox1.Text), int.Parse(textBox2.Text));listener.Start();slave = ModbusTcpSlave.CreateTcp(1, listener);//创建寄存器存储对象slave.DataStore = DataStoreFactory.CreateDefaultDataStore();slave.DataStore.DataStoreWrittenTo += DataStore_DataStoreWrittenTo;slave.ModbusSlaveRequestReceived += Slave_ModbusSlaveRequestReceived;slave.WriteComplete += Slave_WriteComplete;slave.Listen();button1.Enabled = false;}catch (Exception){throw;}}private void Slave_WriteComplete(object sender, ModbusSlaveRequestEventArgs e){if (richTextBox1.InvokeRequired){this.BeginInvoke(new Action(delegate{richTextBox1.AppendText("[写入数据完成:]\r\n" + Newtonsoft.Json.JsonConvert.SerializeObject(e.Message) + "\r\n ******** \r\n \r\n");}));}}private void Slave_ModbusSlaveRequestReceived(object sender, ModbusSlaveRequestEventArgs e){if (richTextBox1.InvokeRequired){this.BeginInvoke(new Action(delegate{richTextBox1.AppendText("[收到数据:]\r\n" + Newtonsoft.Json.JsonConvert.SerializeObject(e.Message) + "\r\n ******** \r\n \r\n");}));}}private void DataStore_DataStoreWrittenTo(object sender, DataStoreEventArgs e){switch (e.ModbusDataType){case ModbusDataType.Coil: //code 5ModbusDataCollection<bool> discretes = slave.DataStore.CoilDiscretes;if (richTextBox1.InvokeRequired){this.BeginInvoke(new Action(delegate{richTextBox1.AppendText("[ModbusDataType.Coil]\r\n" + Newtonsoft.Json.JsonConvert.SerializeObject(discretes) + "\r\n ******** \r\n \r\n");}));}break;case ModbusDataType.HoldingRegister: //code 15ModbusDataCollection<ushort> holdingRegisters = slave.DataStore.HoldingRegisters;if (richTextBox1.InvokeRequired){this.BeginInvoke(new Action(delegate{richTextBox1.AppendText("[ModbusDataType.HoldingRegister]\r\n" + Newtonsoft.Json.JsonConvert.SerializeObject(holdingRegisters) + "\r\n ******** \r\n \r\n");}));}break;default:if (richTextBox1.InvokeRequired){this.BeginInvoke(new Action(delegate{richTextBox1.AppendText($"[{e.ModbusDataType}]\r\n" + Newtonsoft.Json.JsonConvert.SerializeObject(e.Data) + "\r\n ******** \r\n \r\n");}));}break;}}/// <summary>/// 断开/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button2_Click(object sender, EventArgs e){slave.Dispose();button1.Enabled = true;}/// <summary>/// 写数据/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button3_Click(object sender, EventArgs e){//CoilDiscretes表示一个Bit,也就是一个bool类型slave.DataStore.CoilDiscretes[(int)cliAddress.Value] = cliValue.Value == 1 ? true : false;//HoldingRegisters表示一个无符号的16位整数(2的16次幂:0-65535)slave.DataStore.HoldingRegisters[(int)holAddress.Value] = (ushort)holValue.Value;}}
}
从站
using Modbus.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.AxHost;namespace NModBus4.从站
{public partial class Form1 : Form{private TcpClient client;private ModbusIpMaster master;public CancellationTokenSource tokenSource= new CancellationTokenSource(); public Form1(){InitializeComponent();}/// <summary>/// 连接/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button1_Click(object sender, EventArgs e){client = new TcpClient();client.Connect(IPAddress.Parse(textBox1.Text.Trim()), int.Parse(textBox2.Text));master = ModbusIpMaster.CreateIp(client); }/// <summary>/// 断开连接/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button2_Click(object sender, EventArgs e){tokenSource.Cancel();master.Dispose();}/// <summary>/// 写数据/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button3_Click(object sender, EventArgs e){//CoilDiscretes表示一个Bit,也就是一个bool类型//master.WriteSingleCoil((ushort)cliAddress.Value ,cliValue.Value == 1 ? true : false);//HoldingRegisters表示一个无符号的16位整数(2的16次幂:0-65535)master.WriteSingleRegister((ushort)holAddress.Value,(ushort)holValue.Value);}/// <summary>/// 开启线程轮询读数据/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button4_Click(object sender, EventArgs e){Task.Run(async () => {while (!tokenSource.IsCancellationRequested){bool[] coils = master.ReadCoils(1, 0, 9);//ushort[] holding_register = master.ReadHoldingRegisters(1, 0, 9);this.BeginInvoke(new Action(delegate{richTextBox1.AppendText(string.Join(",", coils)+ "\r\n ******** \r\n \r\n");//richTextBox1.AppendText(string.Join(",", holding_register) + "\r\n ******** \r\n \r\n");}));await Task.Delay(TimeSpan.FromSeconds(10));}});}}
}
Modbus 协议简介
Modbus通信协议具有多个变种,支持串口(主要是RS-485总线),以太网多个版本,其中最著名的是Modbus RTU,Modbus ASCII
和Modbus TCP三种。在工业现场一般都是采用Modbus RTU协议,一般大家说的基于串口通信的Modbus通信协议都是指Modbus RTU通信协议。
与Modbus RTU协议相比较,Modbus TCP协议则是在RTU协议上加一个MBAP报文头,并且由于TCP是基于可靠连接的服务,RTU协议中的
CRC校验码就不再需要,所以在Modbus TCP协议中是没有CRC校验码的,所以就常用一句比较通俗的话来说:Modbus TCP协议就是
Modbus RTU协议在前面加上五个0以及一个6,然后去掉两个CRC校验码字节就OK。虽然这句话说得不是特别准确,但是也基本上把RTU与TCP
之间的区别说得比较清楚了。
Modbus的功能码
功能码 | 含义 |
---|---|
0x01 | 读线圈 |
0x02 | 读离散量输入 |
0x03 | 读保持寄存器 |
0x04 | 读输入寄存器 |
0x05 | 写单个线圈 |
0x06 | 写单个保持寄存器 |
0x0F | 写多个线圈 |
0x10 | 写多个保持寄存器 |
读指令对比(0x04)
MBAP报文头 | 地址码 | 功能码 | 寄存器地址 | 寄存器数量 | CRC校验 | |
---|---|---|---|---|---|---|
Modbus RTU | 无 | 01 | 04 | 00 00 | 00 16 | 71 C4 |
Modbus TCP | 00 00 00 00 00 06 01 | 04 | 00 00 | 00 16 | 无 |
写指令对比(0x10)
MBAP报文头 | 地址码 | 功能码 | 寄存器地址 | 寄存器数量 | 数据长度 | 正文 | CRC校验 | |
---|---|---|---|---|---|---|---|---|
Modbus RTU | 无 | 00 | 10 | 00 20 | 00 01 | 02 | 00 00 | AC A0 |
Modbus TCP | 00 00 00 00 00 09 00 | 10 | 00 20 | 00 01 | 02 | 00 00 | 无 |
Modbus TCP MBAP
Modbus TCP协议是在RTU协议前面添加MBAP报文头,由于TCP是基于可靠连接的服务,RTU协议中的CRC校验码就不再需要,所以在Modbus TCP协议中是没有CRC校验码。
MBAP报文头:
事务处理标识 | 协议标识 | 长度 | 单元标识符 |
---|---|---|---|
2字节 | 2字节 | 2字节 | 1字节 |
事务处理标识 | 可以理解为报文的序列号,一般每次通信之后就要加1以区别不同的通信数据报文 |
---|---|
协议标识符 | 00 00表示ModbusTCP协议 |
长度 | 表示接下来的数据长度,单位为字节 |
单元标识符 | 可以理解为设备地址 |
Modbus 主从站关系
Modbus主站和从站区别为:发出指令不同、唯一性不同、对接不同。
一、发出指令不同
1、Modbus主站:Modbus主站可以主动发出指令。
2、Modbus从站:Modbus从站不会主动发出指令。
二、唯一性不同
1、Modbus主站:Modbus主站具有唯一性。
2、Modbus从站:Modbus从站不具有唯一性,可以有多个。
三、对接不同
1、Modbus主站:Modbus主站可以对接多个Modbus从站。
2、Modbus从站:Modbus从站职能对接一个Modbus主站。
所以当使用Modbus/TCP时,主站一般作为客户端,从站一般作为服务端
NModbus4
- 在Nuget上能找到的最新包2.x版本,不支持.netcore
不在赘述关于NModbus4的使用了,有兴趣的看一下这个文章
https://blog.csdn.net/lzl640/article/details/106858263
demo下载
https://download.csdn.net/download/iml6yu/87060108
介绍NModbus的使用,适用于Core的环境
http://t.csdn.cn/uyzz3
https://github.com/NModbus4/NModbus4
C# NModbus4 TCP 主从站通信样例相关推荐
- Windows下的Powerlink主从站通信-(现场总线作业——NJIT)
Windows下的Powerlink主从站通信-(现场总线作业--NJIT) 一.设计任务(要求) 二.Powerlink工作原理 1.Powerlink简介: 2.Powerlink设备类型及设备模 ...
- zhilizhili-ui 2016始动 开始做个样例站吧 (一)
对 我又挖坑了 不过其实也不算挖坑 因为ui构建中就会有填坑的文章 之前一直在写<编写大型web页面 结合现有前端形势思考未来前端>这是一篇巨难写的文章 估计要到年中才能写好了 写作的过程 ...
- 父子进程管道通信(附简单样例)
0x00思路 为了给TinyHTTPd源码分析打下基础,先写一个简单父子进程管道通信的样例 1,先定义,并绑定read,write函数和缓冲区,创建pipe管道数组(下标0对于读段,1对应写端,这是强 ...
- ApiSix基础入门:协议解析-样例详解
一 .http协议配置 1.反向代理测试 ①启动一个正常的web服务器 也可以使用已经安装的openresty #启动web服务器 默认80端口 /usr/local/openresty/bin/op ...
- 高性能 TCP amp; UDP 通信框架 HP-Socket v3.2.3 正式宣布
HP-Socket 是一套通用的高性能 TCP/UDP 通信框架,包括服务端组件.client组件和 Agent 组件.广泛适用于各种不同应用场景的 TCP/UDP 通信系统,提供 C/C++.C#. ...
- ESB项目培训样例说明
公司项目整体分为两类:一类是产品加交付类项目,另一类为产品加培训类项目.公司所有的解决方案都离不开ESB企业服务总线,所以不管是技术人员还是研发人员对ESB的产品功能都要非常了解,因为在涉及到ESB项 ...
- redis配置文件样例(二)
原文:https://raw.githubusercontent.com/antirez/redis/2.8/redis.conf 主从备份.使用slaveof创建一个Redis实例作为另一个Redi ...
- Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动样例)
在Linux,网络分为两个层,各自是网络堆栈协议支持层,以及接收和发送网络协议的设备驱动程序层. 网络堆栈是硬件中独立出来的部分.主要用来支持TCP/IP等多种协议,网络设备驱动层是连接网络堆栈协议层 ...
- java设计模式演示样例
创建模式 1.工厂方法模式(Factory Method) 将程序中创建对象的操作,单独出来处理,创建一个产品的工厂接口,把实际的工作转移到详细的子类.大大提高了系统扩展的柔性,接口的抽象化处理给相 ...
最新文章
- 解决删除从表,主表存在外键的问题
- 有关不蒜子访问统计无法显示的解决方法
- 接口传值后不起作用_聊一聊 API 接口测试
- synchronized 王的后宫总管,线程是王妃
- LeetCode: Single Number I II
- 浅析java中clone()方法
- fastjson 版本_以快标榜的fastjson,快没快多少,倒是漏洞一大堆
- 以下实例在用户点击按钮后对字符串进行反转操作
- 编写高质量代码改善C#程序的157个建议[泛型集合、选择集合、集合的安全]
- JDK与API下载与使用
- MYSQL数据库管理与应用
- Linux系统小说源码网站,Linux系统小说源码网站
- 黑马程序员_Java基础_枚举 和 单例模式实例
- [zabbix]cat ‘/home/cjw/create (1).sql/data‘ | mysql -uzabbix -p123456 -Dzabbix ERROR 1049 (42000):
- 【学习打卡 Free-Excel 】Task4 表合并
- window10 几款好用的屏幕录制制作动图工具
- Java实现云端存储、短信、邮件、沙盒支付
- SQL Server 追踪Profiler,SQL Server的性能优化工具
- 需求文档 | 产品需求文档(PRD)
- Appium-desktop的下载安装