【C#】CS学习之Modbus通讯


摘要
本文详细描述了如何在在C#的Winform应用程序中使用NModbus库实现Modbus通讯,包括读取保持寄存器、以及相应的UI界面设计和事件处理。


前言

​应用场景

Modbus 从站广泛应用于工业自动化领域:
1、传感器数据采集(如温度、压力等)
2、执行器控制(如电机、阀门等)
3、设备监控与故障诊断。

Modbus 从站(Slave)是 Modbus 通信协议中的响应设备,负责接收并执行来自主站(Master)的请求。

Modbus 从站是被动设备,它不能主动发起通信,只能响应主站的请求。从站的主要功能包括:
1、接收主站的指令(如读取数据或执行操作)。
2、执行相应的操作(如读取寄存器值或设置参数)。
3、返回响应数据或错误码给主站。

通信模式

Modbus 从站支持多种通信模式:
​1、Modbus RTU/ASCII:通过串口(如 RS485)进行通信,从站监听串口链路并响应主站指令。
​2、Modbus TCP:通过以太网进行通信,从站作为服务器监听 TCP 端口(默认 502 端口)并处理客户端请求。

​寄存器类型

Modbus 从站通常管理四种类型的寄存器:
1、线圈(Coils):可读写的二进制状态(如开关状态)。
2、离散输入(Discrete Inputs):只读的二进制状态。
3、保持寄存器(Holding Registers):可读写的16位数据。
4、输入寄存器(Input Registers):只读的16位数据。

实现步骤

1、创建TcpListener并启动 侦听
2、创建Modbus从站数据存储
3、初始化数据
4、创建Modbus从站
5、创建Modbus TCP从站网络
6、添加从站到网络
7、异步侦听网络

运行结果

在这里插入图片描述

代码

Frm_ModbusService

    public partial class Frm_ModbusService : Form{#region 字段//定时器Timer timer = null;ModbusService modbusService = null;#endregion#region 构造函数、初始化public Frm_ModbusService(){InitializeComponent();CenterToParent();CenterToScreen();}private void ModbusService_Load(object sender, EventArgs e){Initialize();}private void ModbusService_FormClosing(object sender, FormClosingEventArgs e){modbusService.Stop();}#endregion/// <summary>/// 初始化控件状态/// </summary>public void InitializeControlsState(){tbx_IPAddress.Text = modbusService.IpAddress.ToString();tbx_OpenPort.Text = modbusService.Port.ToString();tbx_StartAddress.Text = modbusService.StartAddress.ToString();tbx_SlaveID.Text = modbusService.SlaveId.ToString();  }/// <summary>/// 初始化/// </summary>public void Initialize(){modbusService = new ModbusService();modbusService.MessageUpdateHandler += OnUpdataMessage;InitializeControlsState();UpdataControlsState(modbusService.IsRunning);timer = new Timer();timer.Interval = 100;timer.Tick += Timer_Tick;dataGridView.Columns[0].Width = 100;dataGridView.Columns[1].Width = 100;dataGridView.Columns[0].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;dataGridView.Columns[1].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;dataGridView.RowHeadersVisible = false;//10行dataGridView.Rows.Add(new object[] { 0, 0});dataGridView.Rows.Add(new object[] { 1, 0 });dataGridView.Rows.Add(new object[] { 2, 0 });dataGridView.Rows.Add(new object[] { 3, 0 });dataGridView.Rows.Add(new object[] { 4, 0 });dataGridView.Rows.Add(new object[] { 5, 0 });dataGridView.Rows.Add(new object[] { 6, 0 });dataGridView.Rows.Add(new object[] { 7, 0 });dataGridView.Rows.Add(new object[] { 8, 0 });dataGridView.Rows.Add(new object[] { 9, 0 });}/// <summary>/// 定时器方法/// </summary>private void Timer_Tick(object sender, EventArgs e){var array = modbusService.DataStore.HoldingRegisters.ReadPoints(modbusService.StartAddress, 10);for (int i = 0; i < array.Length; i++){dataGridView.Rows[i].Cells[0].Value = (modbusService.StartAddress+ i);dataGridView.Rows[i].Cells[1].Value = array[i];}}/// <summary>/// 启动按钮/// </summary>private void btn_Start_Click(object sender, EventArgs e){try{if (!modbusService.IsRunning){modbusService.Start();timer.Start();UpdataControlsState(modbusService.IsRunning);}else{modbusService.Stop();timer.Stop();UpdataControlsState(modbusService.IsRunning);UpdataMessage("Modbus服务停止");}}catch (Exception ex){modbusService.Stop();timer.Stop();UpdataControlsState(modbusService.IsRunning);UpdataMessage($"Modbus服务异常停止:{ex.Message}");}}/// <summary>/// 更新消息/// </summary>private void UpdataMessage(string message){tbx_Output.BeginInvoke(new Action(() =>{if (tbx_Output.Lines.Length>100){tbx_Output.Clear();}tbx_Output.AppendText($"{DateTime.Now.ToString()}{message}\r\n");}));}/// <summary>/// 更新消息/// </summary>private void OnUpdataMessage(object sender, string message){UpdataMessage(message);}/// <summary>/// 更新控件状态/// </summary>private void UpdataControlsState(bool isRunning){if (isRunning){tbx_IPAddress.Enabled = false;tbx_OpenPort.Enabled = false;tbx_SlaveID.Enabled = false;btn_Start.Text = "关闭";}else{tbx_IPAddress.Enabled = true;tbx_OpenPort.Enabled = true;tbx_SlaveID.Enabled = true;btn_Start.Text = "启动";}}#region 参数变更/// <summary>/// IP地址变更/// </summary>private void tbxIPAddress_TextChanged(object sender, EventArgs e){if (IPAddress.TryParse(tbx_IPAddress.Text,out IPAddress result)){modbusService.IpAddress = result;}else{tbx_IPAddress.Text = modbusService.IpAddress.ToString();}}/// <summary>/// 端口变更/// </summary>private void tbxOpenPort_TextChanged(object sender, EventArgs e){if (ushort.TryParse(tbx_OpenPort.Text, out ushort result)){modbusService.Port = result;}else{tbx_OpenPort.Text = modbusService.Port.ToString();}}/// <summary>/// 起始地址变更/// </summary>private void tbx_StartAddress_TextChanged(object sender, EventArgs e){if (ushort.TryParse(tbx_StartAddress.Text, out ushort result)){modbusService.StartAddress = result;}else{tbx_StartAddress.Text = modbusService.StartAddress.ToString();}}/// <summary>/// 站ID变更/// </summary>private void tbx_SlaveID_TextChanged(object sender, EventArgs e){if (byte.TryParse(tbx_SlaveID.Text, out byte result)){modbusService.SlaveId = result;}else{tbx_SlaveID.Text = modbusService.SlaveId.ToString();}}#endregion}
}

ModbusService

public class ModbusService
{#region 字段、属性public event EventHandler<string> MessageUpdateHandler;private IPAddress ipAddress = IPAddress.Parse("127.0.0.1");private int port = 502;    			//端口private byte slaveId = 1;    		// 从站地址private ushort startAddress = 0;    //起始地址private bool isRunning = false;     //运行状态private TcpListener tcpListener = null;    			//创建Modbus服务器private DefaultSlaveDataStore dataStore = null;    //默认从站数据存储private ModbusFactory factory = null;    			//Bodbus工厂private IModbusSlave slave = null;    			//modbus从站IModbusTcpSlaveNetwork slaveNetwork = null;    	//ModbusTcp从站网络public IPAddress IpAddress { get => ipAddress; set => ipAddress = value; }public int Port { get => port; set => port = value; }public byte SlaveId { get => slaveId; set => slaveId = value; }public ushort StartAddress { get => startAddress; set => startAddress = value; }public bool IsRunning { get => isRunning; set => isRunning = value; }public TcpListener TcpListener { get => tcpListener; set => tcpListener = value; }public DefaultSlaveDataStore DataStore { get => dataStore; set => dataStore = value; }public ModbusFactory Factory { get => factory; set => factory = value; }public IModbusSlave Slave { get => slave; set => slave = value; }public IModbusTcpSlaveNetwork SlaveNetwork { get => slaveNetwork; set => slaveNetwork = value; }#endregionpublic ModbusService() {}/// <summary>/// 初始化从站数据存储/// </summary>private void InitializeDataStore(DefaultSlaveDataStore dataStore){// 初始化保持寄存器dataStore.HoldingRegisters.WritePoints(0, new ushort[] { 0 });// 初始化输入寄存器dataStore.InputRegisters.WritePoints(0, new ushort[] { 0 });// 初始化线圈dataStore.CoilDiscretes.WritePoints(0, new bool[] { false });// 初始化离散输入dataStore.CoilInputs.WritePoints(0, new bool[] { false });}/// <summary>/// 启动按钮/// </summary>public void Start(){try{if (!IsRunning){// 创建TcpListener并启动TcpListener = new TcpListener(IpAddress, Port);//启动侦听TcpListener.Start();// 创建Modbus从站数据存储DataStore = new DefaultSlaveDataStore();// 初始化数据InitializeDataStore(DataStore);// 创建Modbus从站Factory = new ModbusFactory();Slave = Factory.CreateSlave(1, DataStore);// 创建Modbus TCP从站网络SlaveNetwork = Factory.CreateSlaveNetwork(TcpListener);// 添加从站到网络SlaveNetwork.AddSlave(Slave);//异步侦听网络SlaveNetwork.ListenAsync();IsRunning = true;OnUpdataMessage("Modbus服务启动");}}catch (Exception ex){Stop();OnUpdataMessage($"Modbus服务异常停止:{ex.Message}");}}public void Stop(){TcpListener?.Stop();SlaveNetwork?.Dispose();DataStore = null;Factory = null;Slave = null;IsRunning = false;}/// <summary>/// 消息更新/// </summary>private void OnUpdataMessage(string message){MessageUpdateHandler?.Invoke(this,message);}
}

结语

总结来说,Modbus 从站是 Modbus 网络中的关键组成部分,负责响应主站指令并执行相应操作。广泛应用于工业自动化和设备控制。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pycharm社区版创建Flask项目详解

一、创建工程项目 二、配置工程目录 新建的空项目下创建目录。 1、新建app.py文件 2、app.py代码如下&#xff1a; from flask import Flask, render_templateapp Flask(__name__)app.route("/") def root():"""主页:return: Index.html"&qu…

Linux 基础入门操作 第十二章 TINY Web 服务器

1 服务器基础架构 1.1 背景知识 Web 服务器使用 HTTP 协议与客户端&#xff08;即浏览器&#xff09;通信&#xff0c;而 HTTP 协议又基于 TCP/IP 协议。因此我们要做的工作就是利用 Linux 系统提供的 TCP 通信接口来实现 HTTP 协议。 而 Linux 为我们提供了哪些网络编程接口…

RAG优化:python从零实现[吃一堑长一智]循环反馈Feedback

本文将介绍一种有反馈循环机制的RAG系统,让当AI学会"吃一堑长一智",给传统RAG装了个"后悔"系统,让AI能记住哪些回答被用户点赞/拍砖,从此告别金鱼记忆: 每次回答都像在玩roguelike:失败结局会强化下次冒险悄悄把优质问答变成新知识卡牌,实现"以…

基于SpringBoot的名著阅读网站

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

[AI建模] 使用Pinokio本地化部署混元2D到3D AI建模服务

近年来,AI驱动的2D转3D建模技术发展迅猛,而Pinokio作为一个强大的AI模型管理与部署平台,使得在本地部署这些复杂的AI模型变得更加简单高效。本文将介绍如何使用Pinokio在本地部署混元2D到3D AI建模服务,并快速生成带或不带Texture的3D模型。 1. 在Pinokio Discover页面找到…

Qt 导入TagLib库

文章目录 0. 前言和环境介绍1. 下载TagLib2. 下载zlib3. 修改.pro文件4. 测试代码 0. 前言和环境介绍 最近在使用Qt写一个播放器&#xff0c;需要解析mp3文件&#xff0c;于是研究了一下如何导入TagLib库 Qt构建套件:Desktop Qt6.8.2 MinGW64-bit Qt Creator安装目录: D:\bit…

【前端面试题】计算机网络相关

总结面试前端过程可能会问到的计算机网络相关知识点 1.HTTP和HTTPS的区别 &#xff08;1&#xff09;HTTPS HTTP 安全加密 HTTPS 是 HTTP 的 加密版&#xff0c;通过 SSL/TLS 保障数据安全&#xff0c;防止窃听和篡改。 &#xff08;2&#xff09;HTTPS 如何保护数据&…

【RabbitMQ高级特性】消息确认机制、持久化、发送方确认、TTL和死信队列

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【中间件】企业级中间件剖析 一、消息确认机制 消费者确认机制确保消息被正确处理后才从队列中删除。如果消费者处理失败&#xff08;如业务异常或宕机&#xff09;&#xff0c;Broker 会重新投递消息…

调用百度api实现语音识别(python)

该代码实现了一个企业级的语音识别解决方案,通过调用百度语音识别API,实现实时录音识别和对已有音频语音识别功能。 百度智能云:请自行访问百度智能云,开通免费的语音识别功能,获取API_KEY和SECRET_KEY。操作按照百度流程即可,可免费申请。 首先,配置下百度API和描述下错…

Python实现小红书app版爬虫

简介&#xff1a;由于数据需求的日益增大&#xff0c;小红书网页版已经不能满足我们日常工作的需求&#xff0c;为此&#xff0c;小编特地开发了小红书手机版算法&#xff0c;方便大家获取更多的数据&#xff0c;提升工作效率。 手机版接口主要包括&#xff1a;搜素&#xff0…

【AndroidRTC-11】如何理解webrtc的Source、TrackSink

Android-RTC系列软重启&#xff0c;改变以往细读源代码的方式 改为 带上实际问题分析代码。增加实用性&#xff0c;方便形成肌肉记忆。同时不分种类、不分难易程度&#xff0c;在线征集问题切入点。 问题1&#xff1a;如何理解VideoSource、VideoTrack&VideoSink三者的关系…

Windows安装Rust环境(详细教程)

一、 安装mingw64(C语言环境) Rust默认使用的C语言依赖Visual Studio&#xff0c;但该工具占用空间大安装也较为麻烦&#xff0c;可以选用轻便的mingw64包。 1.1 安装地址 (1) 下载地址1-GitHub&#xff1a;Releases niXman/mingw-builds-binaries GitHub (2) 下载地址2-W…

英伟达黄仁勋谈人工智能趋势,首提代理式AI,后续机器人将登场

近日&#xff0c;英伟达 GTC 2025 大会主题演讲中&#xff0c;英伟达 CEO 黄仁勋再次身穿皮衣登场。黄仁勋一上来就提到了 AI 发展的未来&#xff0c;现在我们处于生成式 AI&#xff08;Generative AI&#xff09;阶段&#xff0c;但根据黄仁勋的路线图&#xff0c;我们将迈向一…

LCR 187. 破冰游戏(python3解法)

难度&#xff1a;简单 社团共有 num 位成员参与破冰游戏&#xff0c;编号为 0 ~ num-1。成员们按照编号顺序围绕圆桌而坐。社长抽取一个数字 target&#xff0c;从 0 号成员起开始计数&#xff0c;排在第 target 位的成员离开圆桌&#xff0c;且成员离开后从下一个成员开始计数…

水星(MERCURY)监控初始化的恢复和转码方法

水星(MERCURY)的安防监控恢复了很多&#xff0c;其嵌入式文件系统也一直迭代更新。做为数据恢复从业者每天处理最多的就是恢复数据&#xff0c;但是有的时候业务的需要我们不仅仅恢复出数据&#xff0c;还需要能够转码成通用的MP4类文件并要求画面和声音实现“同步”。 故障存…

基于SpringBoot的实现的客户关系管理系统(CRM)(源码+数据库)

464客户关系管理系统&#xff08;CRM&#xff09;&#xff0c;主要功能如下 【后台功能】 权限管理模块: 包括系统的登录与注册功能 用户管理模块: 基于RBAC的权限模型设计, 实现分配角色的功能功能 客户管理模块: 对客户信息进行新增 修改 删除 查看 联络信息管理模块: 对联络…

关于网络的一点知识(持续更新)

1、IP地址和子网掩码、端口号: IP地址是设备在网络上的地址,相当于一栋房子的门牌号。子网掩码相当于房子所在的街道。同一条街道的房子间是通过街道直通的,主人可以互相拜访。 举个例子,如下图所示。 说明:将两台设备的IP和子网掩码转化为二进制,然后将各自的IP地址和…

Python---数据分析(Pandas八:二维数组DataFrame数据操作一: 数据清洗,数据转换)

一、 数据清洗 1.1、 isnull() 用于检测 DataFrame 中的缺失值&#xff0c;它会返回一个相同形状的布尔型 DataFrame&#xff0c;其中每个元素表示原始 DataFrame 中相应位置的元素是否是缺失 值。 import pandas as pd import numpy as np# 创建一个包含缺失值的 DataFrame …

智能汽车图像及视频处理方案,支持视频星轨拍摄能力

美摄科技作为智能汽车图像及视频处理领域的先行者&#xff0c;正以革新性的技术引领着行业的未来发展。美摄科技智能汽车图像及视频处理方案&#xff0c;一个集高效性、智能化、画质增强于一体的创新解决方案&#xff0c;旨在重塑智能汽车图像画质的新标准&#xff0c;并支持前…

Flask接口开发--GET接口

安装Flask 1.安装命令&#xff1a; pip3 install Flask2.查看Flask版本&#xff1a; pip3 show flask如图我的Flask版本号是2.0.3 项目创建 1、在PyCharm中&#xff0c;我们点击左上方的 file&#xff0c;选择 New Project&#xff0c;创建一个Flask项目。&#xff08;Py…