C# NModbus4 TCP 主从站通信样例

实现效果

在这里插入图片描述

实现代码

主站

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 5
                    ModbusDataCollection<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 15
                    ModbusDataCollection<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 RTU010400 0000 1671 C4
Modbus TCP00 00 00 00 00 06 010400 0000 16

写指令对比(0x10)

MBAP报文头地址码功能码寄存器地址寄存器数量数据长度正文CRC校验
Modbus RTU001000 2000 010200 00AC A0
Modbus TCP00 00 00 00 00 09 001000 2000 010200 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

  • 7
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值