文章目录

  • 实现效果
  • 实现代码
    • 主站
    • 从站
  • 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 主从站通信样例相关推荐

  1. Windows下的Powerlink主从站通信-(现场总线作业——NJIT)

    Windows下的Powerlink主从站通信-(现场总线作业--NJIT) 一.设计任务(要求) 二.Powerlink工作原理 1.Powerlink简介: 2.Powerlink设备类型及设备模 ...

  2. zhilizhili-ui 2016始动 开始做个样例站吧 (一)

    对 我又挖坑了 不过其实也不算挖坑 因为ui构建中就会有填坑的文章 之前一直在写<编写大型web页面 结合现有前端形势思考未来前端>这是一篇巨难写的文章 估计要到年中才能写好了 写作的过程 ...

  3. 父子进程管道通信(附简单样例)

    0x00思路 为了给TinyHTTPd源码分析打下基础,先写一个简单父子进程管道通信的样例 1,先定义,并绑定read,write函数和缓冲区,创建pipe管道数组(下标0对于读段,1对应写端,这是强 ...

  4. ApiSix基础入门:协议解析-样例详解

    一 .http协议配置 1.反向代理测试 ①启动一个正常的web服务器 也可以使用已经安装的openresty #启动web服务器 默认80端口 /usr/local/openresty/bin/op ...

  5. 高性能 TCP amp; UDP 通信框架 HP-Socket v3.2.3 正式宣布

    HP-Socket 是一套通用的高性能 TCP/UDP 通信框架,包括服务端组件.client组件和 Agent 组件.广泛适用于各种不同应用场景的 TCP/UDP 通信系统,提供 C/C++.C#. ...

  6. ESB项目培训样例说明

    公司项目整体分为两类:一类是产品加交付类项目,另一类为产品加培训类项目.公司所有的解决方案都离不开ESB企业服务总线,所以不管是技术人员还是研发人员对ESB的产品功能都要非常了解,因为在涉及到ESB项 ...

  7. redis配置文件样例(二)

    原文:https://raw.githubusercontent.com/antirez/redis/2.8/redis.conf 主从备份.使用slaveof创建一个Redis实例作为另一个Redi ...

  8. Linux 网卡驱动学习(一)(分析一个虚拟硬件的网络驱动样例)

    在Linux,网络分为两个层,各自是网络堆栈协议支持层,以及接收和发送网络协议的设备驱动程序层. 网络堆栈是硬件中独立出来的部分.主要用来支持TCP/IP等多种协议,网络设备驱动层是连接网络堆栈协议层 ...

  9. java设计模式演示样例

    创建模式 1.工厂方法模式(Factory Method)  将程序中创建对象的操作,单独出来处理,创建一个产品的工厂接口,把实际的工作转移到详细的子类.大大提高了系统扩展的柔性,接口的抽象化处理给相 ...

最新文章

  1. 解决删除从表,主表存在外键的问题
  2. 有关不蒜子访问统计无法显示的解决方法
  3. 接口传值后不起作用_聊一聊 API 接口测试
  4. synchronized 王的后宫总管,线程是王妃
  5. LeetCode: Single Number I II
  6. 浅析java中clone()方法
  7. fastjson 版本_以快标榜的fastjson,快没快多少,倒是漏洞一大堆
  8. 以下实例在用户点击按钮后对字符串进行反转操作
  9. 编写高质量代码改善C#程序的157个建议[泛型集合、选择集合、集合的安全]
  10. JDK与API下载与使用
  11. MYSQL数据库管理与应用
  12. Linux系统小说源码网站,Linux系统小说源码网站
  13. 黑马程序员_Java基础_枚举 和 单例模式实例
  14. [zabbix]cat ‘/home/cjw/create (1).sql/data‘ | mysql -uzabbix -p123456 -Dzabbix ERROR 1049 (42000):
  15. 【学习打卡 Free-Excel 】Task4 表合并
  16. window10 几款好用的屏幕录制制作动图工具
  17. Java实现云端存储、短信、邮件、沙盒支付
  18. SQL Server 追踪Profiler,SQL Server的性能优化工具
  19. 需求文档 | 产品需求文档(PRD)
  20. Appium-desktop的下载安装

热门文章

  1. 校园网免认证软件无为WiFi卡DNS解决教程
  2. 递归算法删除某一链表指定节点为什么不会发生断链?(解析原因)
  3. 智慧树课程问答怎么得分
  4. Java月薪24k_一位月薪1.2w的北漂程序员真实生活!
  5. 经典Android开发教程!面试字节跳动两轮后被完虐,附面试题答案
  6. 用ps增加照片的气氛--镜头光晕
  7. 骑行、318总结心得啊经验什么的
  8. IDE、SCSI接口区别
  9. STAT0023 STATISTICS FOR PRACTICAL COMPUTING
  10. 决策树sklearn实现泰坦尼克号幸存者的预测 及决策树优缺点、参数、属性、接口总结