C# modbus TCP协议应用总结(一)

1.协议简介

Modbus由MODICON公司(现施耐德公司)于1979年开发,是一种工业现场总线协议标准。主要分为了RTU,ASCII,TCP三种协议类型。

本文我们只探讨C# modbusTCP的应用。 modbusTcp协议采用master/slave模型。在modbus总线中是以"一主多从"关系存在的。通讯方式是主站发出请求(广播或者单播),从站收到请求后应答。

2.Modbus Tcp 主从站关系

什么时候需要编写主站程序,什么时候需要编写从站程序,对于初学者总有些疑惑。

2.1 Modbus 主站(master):Modbus主站具有唯一性,可以主动发出读取、修改指令,对接多个Modbus从站。主站(master)一般作为工控机上位机程序来读取传感器设备的数据,编程时作为网络客户端(TCP Client),IP地址不需要固定。

2.2 Modbus从站(slave):Modbus从站可以有多个,不会主动发出读取指令,只能对接一个Modbus主站。从站(slave)一般是传感器部分的程序,需要一个固定的IP地址,从站编程时一般作为网络服务端(TCP Server),监听回应主站发来的请求。

3.Modbus的功能码

功能码

含义

0x01

读线圈

0x02

读离散量输入

0x03

读保持寄存器

0x04

读输入寄存器

0x05

写单个线圈

0x06

写单个保持寄存器

0x0F

写多个线圈

0x10

写多个保持寄存器

例程:0x03:读保持寄存器,从远程设备中读保持寄存器连续块的内容
    请求 :19 15 00 00 00 06 01 03 05 4A 00 01
    应答:19 15 00 00 00 05 01 03 02 11 22
        
        请求解析:
            19 15 为报文标识符(用户自定义),一般每次通信加1来表示区别不同报文
            00 00 表示modbusTCP协议
            00 06 表示后面的数据长度
            01 从站号
            03 功能码。
            05 4A 读取数据的起始地址
            00 01 读几位地址(寄存器数量)
            
        应答解析:
            19 15 为报文标识符
            00 00 表示modbusTCP协议
            00 05 表示后面的数据长度
            01 从站号
            03 功能码
            02 表示读到的数据长度
            11 22 表示读到的数据

4. 基于NModbus4的Modbus TCP主从站例程

下面程序参考了https://blog.csdn.net/qq_34699535/article/details/111658342网站。

4.1 Modbus客户端:Slave(一般为设备端)

//  Modbus TCP
using Modbus.Device;
using System.Net.Sockets;
using System.Net;
using Modbus.Data;
using System.Threading;
using Modbus.Utility;

private TcpListener listener;
private ModbusSlave slave;

//客户端监听端口
private void btn_SlaveListen_Click(object sender, EventArgs e)
{
    listener = new TcpListener(IPAddress.Parse(txt_SlaveIP.Text), (int)nud_SlavePort.Value);
    listener.Start();
    slave = ModbusTcpSlave.CreateTcp(1, listener);
    //创建寄存器存储对象
    slave.DataStore = DataStoreFactory.CreateDefaultDataStore();
    //订阅数据到达事件,可以在此事件中读取寄存器
    slave.DataStore.DataStoreWrittenTo += new EventHandler<DataStoreEventArgs>((obj, o) =>
    {
         switch (o.ModbusDataType)
         {
             case ModbusDataType.Coil:   //code 5
                  ModbusDataCollection<bool> discretes = slave.DataStore.CoilDiscretes;
                  if (ckb_CD_1.InvokeRequired)
                  {
                      this.BeginInvoke(new Action(delegate
                      {
                           ckb_CD_1.Checked = discretes[1];       
                      }));
                  }
                    break;
             case ModbusDataType.HoldingRegister:   //code 15
                   ModbusDataCollection<ushort> holdingRegisters = slave.DataStore.HoldingRegisters;
                  if (txt_HR_1.InvokeRequired)
                  {
                      this.BeginInvoke(new Action(delegate
                      {
                           txt_HR_1.Text = holdingRegisters[1].ToString();
                      }));
                  }
                    break;
           }
    });

    //此事件,待补充
    slave.ModbusSlaveRequestReceived += new EventHandler<ModbusSlaveRequestEventArgs>((obj, o) =>
    {
    });
    //此事件,待补充
    slave.WriteComplete += new EventHandler<ModbusSlaveRequestEventArgs>((obj, o) =>
    {
    });
    slave.Listen();
}

//写入寄存器
private void btn_SlaveSend_Click(object sender, EventArgs e)
{
    //CoilDiscretes表示一个Bit,也就是一个bool类型
    slave.DataStore.CoilDiscretes[(int)nud_SlaveCoilAds.Value] = nud_SlaveCoilVal.Value == 1 ? true : false;
    //HoldingRegisters表示一个无符号的16位整数(2的16次幂:0-65535)
    slave.DataStore.HoldingRegisters[(int)nud_SlaveHRAds.Value] = (ushort)nud_SlaveHRVal.Value;
}
//停止监听
private void btn_SlaveStop_Click(object sender, EventArgs e)
{
    slave.Dispose();
}

4.2 Modbus主机端:Master(一般为上位机)

 private TcpClient client;
 private ModbusIpMaster master;

 //连接
 private void btn_MasterConnect_Click(object sender, EventArgs e)
 {
     client = new TcpClient();
     client.Connect(IPAddress.Parse(txt_SlaveIP.Text.Trim()), (int)nud_SlavePort.Value);
     master = ModbusIpMaster.CreateIp(client);
 }
 //写入寄存器
 private void btn_MasterSend_Click(object sender, EventArgs e)
 {
     master.WriteSingleCoil((ushort)nud_MasterCoilAds.Value, nud_MasterCoilVal.Value == 1 ? true : false);
     master.WriteSingleRegister((ushort)nud_MasterHRAds.Value, (ushort)nud_MasterHRVal.Value);
 }
 //定时器中循环读取线圈和寄存器的值,当然,你也可以使用异步的方式
 private void timer1_Tick(object sender, EventArgs e)
 {
     bool[] coils = master.ReadCoils(1, 0, 9);
     ushort[] holding_register = master.ReadHoldingRegisters(1, 0, 9); 
 }

4.3 程序源码

Modbus TCP例程

参考网站:

https://www.cnblogs.com/ioufev/articles/10830028.html

https://blog.csdn.net/m0_57124501/article/details/127018946

https://blog.csdn.net/qq_34699535/article/details/111658342

  • 1
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值