C#Modbus Rtu的实现
Modbus Rtu的实现与Modbus Tcp的实现类似 C#ModBus Tcp的学习及Master的实现
我们还是需要借用一个开源库NModbus4,在vs中.打开NuGet管理器.安装NModbus4
具体实现,具体实现与之前的Modbus Tcp的实现类似 ,只是在实例化master时将TCPClient换为串行端口资源SerialPort,并在实例化是设置好端口所需参数(端口名,波特率,校验位,停止位,数据位)
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.Threading;
using System.IO.Ports; namespace ModbusRtu
{
public partial class Form1 : Form
{
private static IModbusMaster master;
private static SerialPort port;
//写线圈或写寄存器数组
private bool[] coilsBuffer;
private ushort[] registerBuffer;
//功能码
private string functionCode;
//参数(分别为站号,起始地址,长度)
private byte slaveAddress;
private ushort startAddress;
private ushort numberOfPoints;
//串口参数
private string portName;
private int baudRate;
private Parity parity;
private int dataBits;
private StopBits stopBits; public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
cmb_portname.SelectedIndex = ;
cmb_baud.SelectedIndex = ;
cmb_parity.SelectedIndex = ;
cmb_databBits.SelectedIndex = ;
cmb_stopBits.SelectedIndex = ;
}
private SerialPort InitSerialPortParameter()
{
if (cmb_portname.SelectedIndex < || cmb_baud.SelectedIndex < || cmb_parity.SelectedIndex < || cmb_databBits.SelectedIndex < || cmb_stopBits.SelectedIndex < )
{
MessageBox.Show("请选择串口参数");
return null;
}
else
{ portName = cmb_portname.SelectedItem.ToString();
baudRate = int.Parse(cmb_baud.SelectedItem.ToString());
switch (cmb_parity.SelectedItem.ToString())
{
case "奇":
parity = Parity.Odd;
break;
case "偶":
parity = Parity.Even;
break;
case "无":
parity = Parity.None;
break;
default:
break;
}
dataBits = int.Parse(cmb_databBits.SelectedItem.ToString());
switch (cmb_stopBits.SelectedItem.ToString())
{
case "":
stopBits = StopBits.One;
break;
case "":
stopBits = StopBits.Two;
break;
default:
break;
}
port = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
return port;
}
}
/// <summary>
/// 读/写
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
try
{
//初始化串口参数
InitSerialPortParameter(); master = ModbusSerialMaster.CreateRtu(port); ExecuteFunction();
}
catch (Exception)
{
MessageBox.Show("初始化异常");
}
} private async void ExecuteFunction()
{
try
{
//每次操作是要开启串口 操作完成后需要关闭串口
//目的是为了slave更换连接是不报错
if (port.IsOpen == false)
{
port.Open();
}
if (functionCode != null)
{
switch (functionCode)
{
case "01 Read Coils"://读取单个线圈
SetReadParameters();
coilsBuffer = master.ReadCoils(slaveAddress, startAddress, numberOfPoints); for (int i = ; i < coilsBuffer.Length; i++)
{
SetMsg(coilsBuffer[i] + " ");
}
SetMsg("\r\n");
break;
case "02 Read DisCrete Inputs"://读取输入线圈/离散量线圈
SetReadParameters(); coilsBuffer = master.ReadInputs(slaveAddress, startAddress, numberOfPoints);
for (int i = ; i < coilsBuffer.Length; i++)
{
SetMsg(coilsBuffer[i] + " ");
}
SetMsg("\r\n");
break;
case "03 Read Holding Registers"://读取保持寄存器
SetReadParameters();
registerBuffer = master.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints);
for (int i = ; i < registerBuffer.Length; i++)
{
SetMsg(registerBuffer[i] + " ");
}
SetMsg("\r\n");
break;
case "04 Read Input Registers"://读取输入寄存器
SetReadParameters();
registerBuffer = master.ReadInputRegisters(slaveAddress, startAddress, numberOfPoints);
for (int i = ; i < registerBuffer.Length; i++)
{
SetMsg(registerBuffer[i] + " ");
}
SetMsg("\r\n");
break;
case "05 Write Single Coil"://写单个线圈
SetWriteParametes();
await master.WriteSingleCoilAsync(slaveAddress, startAddress, coilsBuffer[]);
break;
case "06 Write Single Registers"://写单个输入线圈/离散量线圈
SetWriteParametes();
await master.WriteSingleRegisterAsync(slaveAddress, startAddress, registerBuffer[]);
break;
case "0F Write Multiple Coils"://写一组线圈
SetWriteParametes();
await master.WriteMultipleCoilsAsync(slaveAddress, startAddress, coilsBuffer);
break;
case "10 Write Multiple Registers"://写一组保持寄存器
SetWriteParametes();
await master.WriteMultipleRegistersAsync(slaveAddress, startAddress, registerBuffer);
break;
default:
break;
} }
else
{
MessageBox.Show("请选择功能码!");
}
port.Close();
}
catch (Exception ex)
{ MessageBox.Show(ex.Message);
}
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex >= )
{
groupBox2.Enabled = true;
groupBox1.Enabled = false;
}
else
{
groupBox1.Enabled = true;
groupBox2.Enabled = false;
}
comboBox1.Invoke(new Action(() => { functionCode = comboBox1.SelectedItem.ToString(); }));
} /// <summary>
/// 初始化读参数
/// </summary>
private void SetReadParameters()
{
if (txt_startAddr1.Text == "" || txt_slave1.Text == "" || txt_length.Text == "")
{
MessageBox.Show("请填写读参数!");
}
else
{
slaveAddress = byte.Parse(txt_slave1.Text);
startAddress = ushort.Parse(txt_startAddr1.Text);
numberOfPoints = ushort.Parse(txt_length.Text);
}
}
/// <summary>
/// 初始化写参数
/// </summary>
private void SetWriteParametes()
{
if (txt_startAddr2.Text == "" || txt_slave2.Text == "" || txt_data.Text == "")
{
MessageBox.Show("请填写写参数!");
}
else
{
slaveAddress = byte.Parse(txt_slave2.Text);
startAddress = ushort.Parse(txt_startAddr2.Text);
//判断是否写线圈
if (comboBox1.SelectedIndex == || comboBox1.SelectedIndex == )
{
string[] strarr = txt_data.Text.Split(' ');
coilsBuffer = new bool[strarr.Length];
//转化为bool数组
for (int i = ; i < strarr.Length; i++)
{
// strarr[i] == "0" ? coilsBuffer[i] = true : coilsBuffer[i] = false;
if (strarr[i] == "")
{
coilsBuffer[i] = false;
}
else
{
coilsBuffer[i] = true;
}
}
}
else
{
//转化ushort数组
string[] strarr = txt_data.Text.Split(' ');
registerBuffer = new ushort[strarr.Length];
for (int i = ; i < strarr.Length; i++)
{
registerBuffer[i] = ushort.Parse(strarr[i]);
}
}
}
} /// <summary>
/// 清除文本
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
}
/// <summary>
/// SetMessage
/// </summary>
/// <param name="msg"></param>
public void SetMsg(string msg)
{
richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(msg); }));
} }
}
接下来开始测试
在这里 因为要用到串口,而我的笔记本没有串口,所以需要借助一个工具
Virtual Serial Port Dirver 虚拟串口工具
链接:https://pan.baidu.com/s/1opGre3GS-HWFoA_dP9qYYg
提取码:2afu
借用这个工具我们添加两个虚拟串口 COM1和COM2 点击Add Virtual Pair 添加
设置Modbus Slave,选择连接方式为串口,选择对应端口,模式选择RTU,建立连接
接下来运行我们自己的Modbus RTU Master
设置串口参数(波特率,数据位,奇偶校验,停止位)要与Slave的串口参数一致
我们测试 功能码 0x01 读一组线圈
测试完成,数据正常,其他的功能码经测试数据正常,有兴趣的可以自行测试
到此为止,Modbus的学习到此告一段落
以上都为我自行学习并实现,如有错误之处,望大家不吝赐教,感谢(抱拳~)
程序源代码:
链接:https://pan.baidu.com/s/1mPAhRixLbsDb7h2ePENTRA
提取码:b5w6
C#Modbus Rtu的实现的更多相关文章
- Modbus RTU程序开发解读
Modbus是很好的串口通信协议,其中RTU协议最为常用,通过Modbus RTU,我们可以轻松读写串口信息. 从官网下载libModbus,观察modbus.h可知关键的结构体为: typedef ...
- 0-20ma 0-5V,0-10V ,0-15V ,0-20V,0-30V模拟量(范围可以定制)多功能采集模块,支持1路继电器输出,2路Di输入,8路Ai输入,可电脑控制,支持485 modbus rtu协议。端口参数可以配置保存,支持定制修改。
多功能模拟量采集模块MRD-5017具有8 通道模拟量采集(支持0-20mA,0-5V,0-10V混合测量),2路DI,1路继电器输出,1路485接口(支持MODBUS RTU),能实现8路AI(12 ...
- Modbus RTU 通信工具设计(转)
Modbus RTU 通信工具设计 Modbus 是一个工业上常用的通讯协议.一种通讯约定. ModBus 协议是应用层报文传输协议(OSI 模型第7层),它定义了一个与通信层无关的协议数据单元(PD ...
- Modbus协议栈实现Modbus RTU多主站支持
前面我们已经详细讲解过Modbus协议栈的开发过程,并且利用协议栈封装了Modbus RTU主站和从站,Modbus TCP服务器与客户端,Modbus ASCII主站与从站应用.但在使用过程中,我们 ...
- Modbus库开发笔记之六:Modbus RTU Master开发
这一节我们来封装最后一种应用(Modbus RTU Master应用),RTU主站的开发与TCP客户端的开发是一致的.同样的我们也不是做具体的应用,而是实现RTU主站的基本功能.我们将RTU主站的功能 ...
- Modbus库开发笔记之五:Modbus RTU Slave开发
Modbus在串行链路上分为Slave和Master,这一节我们就来开发Slave.对于Modbus RTU从站来说,需要实现的功能其实与Modbus TCP的服务器端是一样的.其操作过程也是一样的. ...
- C# 开发Modbus Rtu客户端 modbus测试Demo,Modbus 串口通信 , 虚拟MODBUS-RTU测试
前言 本文将使用一个NuGet公开的组件技术来实现一个ModBus RTU的客户端,方便的对Modbus rtu的服务器进行读写,这个服务器可以是电脑端C#设计的,也可以是PLC实现的,也可以是其他任 ...
- ModBus通信协议的【Modbus RTU 协议使用汇总】
1.RTU模式 当控制器设为在Modbus网络上以RTU(远程终端单元)模式通信,在消息中的每个8Bit字节包含两个4Bit的十六进制字符.这种方式的主要优点是:在同样的波特率下,可比ASCII方式传 ...
- modbus ASCII和MODBUS RTU区别
下表是MODBUS ASCII协议和RTU协议的比较: 协议 开始标记 结束标记 校验 传输效率 程序处理 ASCII :(冒号) CR,LF LRC 低 直观,简单,易调试 RTU 无 无 CRC ...
- Modbus RTU 协议使用汇总
原创地址:https://blog.csdn.net/u012166958/article/details/64920144 标准的Modbus 口是使用RS-232C 兼容串行接口,它定义了连接口的 ...
随机推荐
- Alibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas
Arthas 用户文档 — Arthas 3.1.0 文档https://alibaba.github.io/arthas/ alibaba/arthas: Alibaba Java Diagnost ...
- postgre查询表和记录数,查表字段
select relname as TABLE_NAME, reltuples as rowCounts from pg_class where relkind = 'r' and relnamesp ...
- (待续)【转载】 DeepMind发Nature子刊:通过元强化学习重新理解多巴胺
原文地址: http://www.dataguru.cn/article-13548-1.html -------------------------------------------------- ...
- JS实现动态添加和删除div
实现方式一:只在最后一个数据中动态添加或者删除 | 背景需要做一个页面,页面可以输入参数,点击确认按钮可以发请求给某接口.但是接口的某个字段是数组类型,所以在页面上需要实现添加或者删除元素的功能. | ...
- cookie加载不正确的问题
华为系统更新后安装了一个谷歌6月安全补丁的东西,然后之前写的调h5页面的部分就出现了问题,后台查过发现是Android端调h5页面时cookie没能带过去,导致了登录失败.于是对setCookie部分 ...
- python flask框架学习(一)——准备工作和环境配置与安装
Flask装备: 学习自:知了课堂Python Flask框架——全栈开发 1.Python版本:3.6 2.Pycharm软件: 3.安装虚拟环境: (1)安装virtualenv: pip ins ...
- AI佳作解读系列(五) - 目标检测二十年技术综述
计算机视觉中的目标检测,因其在真实世界的大量应用需求,比如自动驾驶.视频监控.机器人视觉等,而被研究学者广泛关注. 上周四,arXiv新出一篇目标检测文献<Object Detection ...
- easyui datagrid 让某行复选框置灰不能选
easyui中datagrid 让某行复选框置灰不能进行选中操作,以下为主要部分的code. //加载完毕后获取所有的checkbox遍历 onLoadSuccess: function(data){ ...
- iOS-打印控件
20.UIPrintFormatterUIPrintFormatter时打印格式化的抽象基类:展示了传统的可打印的内容对象可以跨页边界.由于打印格式化,打印系统,可以自动打印与打印格式化的内容相关联的 ...
- Sap MM 定义物料号码范围
Sap里面的物料编号可以设置内部给号或者外部给号,外部的意思就是通过手动输入,内部就是系统自动根据号码段分配. 物料号是根据物料类型定义范围的. 笔记 作者:明光烁亮 出处:http://www.cn ...