小闹 發表於 2024-10-12 23:48:00

C#轻松实现Modbus通信

<h3 data-first-child="">1、前言</h3>
<p>大家好!我是付工。前面给大家介绍了一系列关于RS485与Modbus的知识。</p>
<p>终于有人把RS485说清楚了<br data-filtered="filtered">终于有人把Modbus说明白了<br data-filtered="filtered">通透!终于把ModbusRTU弄明白了<br data-filtered="filtered">这样看来,ModbusTCP协议太简单了<br data-filtered="filtered">今天跟大家聊聊关于C#如何实现Modbus通信。</p>
<h3>2、开源通信库</h3>
<p>
通信库是对通信协议的封装,一般是以dll动态链接库的形式存在,对于编程者来说,只需要调用库的各种方法即可实现数据读写。</p>
<p><strong>通信库有两种,一种是开源的,即使开源,也要注意看下开源许可证,开源并不一定免费,另外一种就是自己开发封装的,这个需要具备一定的开发能力。</strong></p>
<p>Modbus通信有很多开源通信库,这其中使用较为广泛的是NModbus4,NModbus4是一个开源且免费的Modbus通信库,它的开源许可证是MIT,是一个相对宽松的软件授权条款,可以商用。</p>
<h3>3、ModbusRTU通信</h3>

1、在项目解决方案资源管理器中,选择【引用】右击,在弹出的界面中,点击【管理NuGet程序包】选项,如下图所示:
<p><img src="https://img2024.cnblogs.com/blog/1719657/202410/1719657-20241012234633615-1198552012.webp"></p>
<p>&nbsp;</p>
<p>2、在打开的选项卡中,选择【浏览】,然后输入NModbus4进行搜索,搜索到之后,选择最新稳定版2.1.0,点击【安装】即可:</p>
<p><img src="https://img2024.cnblogs.com/blog/1719657/202410/1719657-20241012234640700-1945035566.webp"></p>
<p>&nbsp;</p>
<p>3、在NModbus4基础上封装一个打开串口和关闭串口的方法:<code></code></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> SerialPort serialPort;
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> ModbusSerialMaster master;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> Open(<span style="color: rgba(0, 0, 255, 1)">string</span> portName, <span style="color: rgba(0, 0, 255, 1)">int</span> baudRate, Parity parity, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> dataBits, StopBits stopBits)
{
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.serialPort != <span style="color: rgba(0, 0, 255, 1)">null</span> &amp;&amp; <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.serialPort.IsOpen)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.serialPort.Close();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.serialPort = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> SerialPort(portName, baudRate, parity, dataBits, stopBits);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.serialPort.Open();
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.master = ModbusSerialMaster.CreateRtu(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.serialPort);
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.master.Transport.WriteTimeout = <span style="color: rgba(128, 0, 128, 1)">2000</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.master.Transport.ReadTimeout = <span style="color: rgba(128, 0, 128, 1)">2000</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.master.Transport.WaitToRetryMilliseconds = <span style="color: rgba(128, 0, 128, 1)">500</span><span style="color: rgba(0, 0, 0, 1)">;
    </span><span style="color: rgba(0, 0, 255, 1)">this</span>.master.Transport.Retries = <span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Close()
{
   </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.serialPort != <span style="color: rgba(0, 0, 255, 1)">null</span> &amp;&amp; <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.serialPort.IsOpen)
   {
         </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.serialPort.Close();
   }
   </span><span style="color: rgba(0, 0, 255, 1)">this</span>.master = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<p>4、在NModbus4基础上封装各种读写的方法,这里以读取保持型寄存器为例,其他方法都是类似的:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">byte</span>[] ReadHoldingRegisters(<span style="color: rgba(0, 0, 255, 1)">byte</span> slaveId, <span style="color: rgba(0, 0, 255, 1)">ushort</span> start, <span style="color: rgba(0, 0, 255, 1)">ushort</span><span style="color: rgba(0, 0, 0, 1)"> length)
{
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
    {
      </span><span style="color: rgba(0, 0, 255, 1)">ushort</span>[] data = <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.master.ReadHoldingRegisters(slaveId, start, length);
      List</span>&lt;<span style="color: rgba(0, 0, 255, 1)">byte</span>&gt; result = <span style="color: rgba(0, 0, 255, 1)">new</span> List&lt;<span style="color: rgba(0, 0, 255, 1)">byte</span>&gt;<span style="color: rgba(0, 0, 0, 1)">();
      </span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> item <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> data)
      {
            result.AddRange(BitConverter.GetBytes(item).Reverse());
      }
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result.ToArray();
    }
    </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Exception(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">【读取保持寄存器】失败:</span><span style="color: rgba(128, 0, 0, 1)">"</span> +<span style="color: rgba(0, 0, 0, 1)"> ex.Message);
    }
}</span></pre>
</div>
<blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="55" data-source-title="">基于NModbus4实现ModbusRTU通信,不需要关注协议及报文,只需要对NModbus4库二次封装即可。</blockquote>
<h3>4、ModbusTCP通信</h3>
<p>NModbus4不仅支持ModbusRTU通信,也同样支持ModbusTCP通信,ModbusTCP与ModbusRTU的封装过程非常类似,主要是存在以下两个不同点:</p>
<ul class="list-paddingleft-1">
<li>
<p>ModbusRTU是基于串口通信,因此主要使用的是SerialPort类,而ModbusTCP是基于以太网通信,主要使用的是TcpClient类。</p>
</li>
<li>
<p>ModbusRTU的读取和写入方法中都必须包含从站地址,而ModbusTCP可以把SlaveAddress作为一个可选项。</p>
</li>
</ul>
ModbusTCP通信库封装过程如下:1、在NModbus4基础上封装一个TCP连接和断开的方法:
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">private TcpClient tcpClient;
private ModbusIpMaster master
public void Connect(string ip, int port)
{
    tcpClient = new TcpClient();
    tcpClient.Connect(IPAddress.Parse(ip), port);
    this.master = ModbusIpMaster.CreateIp(this.tcpClient);
    this.master.Transport.WriteTimeout = 2000;
    this.master.Transport.ReadTimeout = 2000;
    this.master.Transport.WaitToRetryMilliseconds = 500;
    this.master.Transport.Retries = 3;
}
public void DisConnect()
{
   if (this.tcpClient != null &amp;&amp; this.tcpClient.Connected)
   {
         this.tcpClient.Close();
   }
   this.master = null;
}</pre>
</div>
2、封装一个读取输出线圈的方法,其他读写方法都是类似的:
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">bool</span>[] ReadOutputCoils(<span style="color: rgba(0, 0, 255, 1)">ushort</span> start, <span style="color: rgba(0, 0, 255, 1)">ushort</span> length, <span style="color: rgba(0, 0, 255, 1)">byte</span> slaveAddress = <span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)
{
    </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
    {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.master.ReadCoils(slaveAddress, start, length);
    }
    </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
    {
      </span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> Exception(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">【读取输出线圈】失败:</span><span style="color: rgba(128, 0, 0, 1)">"</span> +<span style="color: rgba(0, 0, 0, 1)"> ex.Message);
    }
}</span></pre>
</div>
<pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer"><span class="code-snippet__function"><span class="code-snippet__keyword">&nbsp;</span></span></span></code></pre><br><br>
来源:https://www.cnblogs.com/xbdedu/p/18461727
頁: [1]
查看完整版本: C#轻松实现Modbus通信