基于C#实现Modbus RTU通信

 更新时间:2026年03月12日 09:29:10   作者:yongui47834  
本文主要介绍了使用C#和Modbus RTU模式与下位机进行通信,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、环境配置与依赖安装

# NuGet包安装(支持.NET Framework 4.5+)
Install-Package NModbus4
Install-Package System.IO.Ports

二、核心代码实现

1. 串口初始化与连接

using System.IO.Ports;
using Modbus.Device;

public class ModbusRtuMaster
{
    private SerialPort _serialPort;
    private IModbusSerialMaster _master;

    public void Connect(string portName, int baudRate = 9600, Parity parity = Parity.None, 
                       int dataBits = 8, StopBits stopBits = StopBits.One)
    {
        try
        {
            _serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits)
            {
                ReadTimeout = 3000,
                WriteTimeout = 3000
            };
            
            _serialPort.Open();
            _master = ModbusSerialMaster.CreateRtu(_serialPort);
            _master.Transport.Retries = 3; // 设置重试次数
            _master.Transport.WriteTimeout = 2000;
            _master.Transport.ReadTimeout = 2000;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"连接失败: {ex.Message}");
            throw;
        }
    }

    public void Disconnect()
    {
        _master?.Close();
        _serialPort?.Close();
    }
}

2. 数据读取操作

public ushort[] ReadHoldingRegisters(byte slaveId, ushort startAddr, ushort count)
{
    try
    {
        return _master.ReadHoldingRegisters(slaveId, startAddr, count);
    }
    catch (ModbusException ex)
    {
        Console.WriteLine($"Modbus错误: {ex.ErrorCode}");
        return null;
    }
    catch (IOException ex)
    {
        Console.WriteLine($"通信异常: {ex.Message}");
        return null;
    }
}

// 示例调用
var data = master.ReadHoldingRegisters(1, 0, 10); // 从站1读取保持寄存器0-9

3. 数据写入操作

public void WriteSingleRegister(byte slaveId, ushort address, ushort value)
{
    _master.WriteSingleRegister(slaveId, address, value);
}

public void WriteMultipleCoils(byte slaveId, ushort startAddr, bool[] values)
{
    _master.WriteMultipleCoils(slaveId, startAddr, values);
}

三、关键功能实现

1. CRC16校验(Modbus标准)

public static class Crc16
{
    public static byte[] Compute(byte[] data)
    {
        ushort crc = 0xFFFF;
        foreach (byte b in data)
        {
            crc ^= (ushort)b;
            for (int i = 0; i < 8; i++)
            {
                if ((crc & 0x0001) != 0)
                {
                    crc >>= 1;
                    crc ^= 0xA001;
                }
                else
                {
                    crc >>= 1;
                }
            }
        }
        return new[] { (byte)crc, (byte)(crc >> 8) };
    }

    public static bool Validate(byte[] dataWithCrc)
    {
        if (dataWithCrc.Length < 2) return false;
        byte[] data = dataWithCrc.Take(dataWithCrc.Length - 2).ToArray();
        byte[] crc = Compute(data);
        return crc[0] == dataWithCrc[dataWithCrc.Length - 2] 
            && crc[1] == dataWithCrc[dataWithCrc.Length - 1];
    }
}

2. 帧间延迟计算

private int CalculateInterFrameDelay()
{
    int charBits = 1 + _serialPort.DataBits + 
                  (_serialPort.StopBits == StopBits.One ? 1 : 2) + 
                  (_serialPort.Parity == Parity.None ? 0 : 1);
    return (int)(3500.0 * charBits / _serialPort.BaudRate); // 3.5字符时间
}

四、错误处理与调试

1. 异常处理策略

try
{
    var data = ReadHoldingRegisters(1, 0, 10);
    if (data == null) throw new InvalidOperationException("读取失败");
}
catch (TimeoutException)
{
    Console.WriteLine("通信超时,请检查设备状态");
    Reconnect();
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"操作失败: {ex.Message}");
}

2. 自动重连机制

private void Reconnect()
{
    Disconnect();
    Thread.Sleep(1000);
    Connect(_config.PortName, _config.BaudRate);
}

五、性能优化

1批量操作优化

// 批量读取多个寄存器区域
public Dictionary<ushort, ushort[]> BatchRead(
    params (byte SlaveId, ushort StartAddr, ushort Count)[] requests)
{
    var results = new Dictionary<ushort, ushort[]>();
    Parallel.ForEach(requests, request => 
    {
        var data = ReadHoldingRegisters(request.SlaveId, request.StartAddr, request.Count);
        lock (results) results[request.SlaveId] = data;
    });
    return results;
}

2异步通信实现

public async Task<ushort[]> ReadAsync(byte slaveId, ushort startAddr, ushort count)
{
    return await Task.Run(() => ReadHoldingRegisters(slaveId, startAddr, count));
}

参考代码 C#利用modbus rtu模式与下位机通讯 www.youwenfan.com/contentcsr/112701.html

六、完整项目结构

ModbusRTUDemo/
├── src/
│   ├── ModbusMaster/          // 主站核心逻辑
│   ├── SerialPortConfig.cs    // 串口配置管理
│   └── ProtocolHandler.cs     // 协议解析器
├── tests/
│   ├── ModbusTests.cs         // 单元测试
│   └── StressTests.cs         // 压力测试
└── docs/
    └── 开发指南.md

七、调试与测试工具

  1. 虚拟串口工具

    使用VSPD创建虚拟串口对(如COM3<->COM4),方便调试

  2. Wireshark抓包分析

    过滤Modbus RTU协议,观察完整的请求/响应帧结构

  3. Modbus从站模拟

    使用Modbus Slave工具创建测试从站设备

八、扩展应用场景

1.多从站管理

public class ModbusNetwork
{
    private Dictionary<byte, ModbusRtuMaster> _slaves = new();
    
    public void AddSlave(byte address, string portName)
    {
        var master = new ModbusRtuMaster();
        master.Connect(portName);
        _slaves[address] = master;
    }
}

2.数据缓存机制

public class DataCache
{
    private Dictionary<ushort, (DateTime Timestamp, ushort Value)[]> _cache = new();
    
    public void UpdateCache(ushort[] data, ushort startAddr)
    {
        _cache[startAddr] = data.Select((v, i) => 
            (DateTime.Now, v)).ToArray();
    }
}

九、最佳实践建议

参数配置规范

  • 波特率:9600/19200/115200(根据设备手册选择)
  • 数据格式:8N1(默认配置)
  • 超时时间:读操作1000ms,写操作500ms

安全防护措施

  • 添加设备认证机制
  • 实现数据加密传输
  • 设置访问权限控制

到此这篇关于基于C#实现Modbus RTU通信的文章就介绍到这了,更多相关C# Modbus RTU通信内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C# WinForm实现自动更新程序之客户端的示例代码

    C# WinForm实现自动更新程序之客户端的示例代码

    这篇文章主要为大家详细介绍了利用C# WinForm实现自动更新程序之客户端的实现方法,文中的示例代码讲解详细,感兴趣的小伙伴可以尝试一下
    2022-10-10
  • 基于WPF实现简单的值转换器

    基于WPF实现简单的值转换器

    值转换器是 WPF 项目中具有特色的组成部分,这篇文章将带大家实现一个标准的值转换器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下
    2025-02-02
  • C#实现窗口之间的传值

    C#实现窗口之间的传值

    本文给大家介绍的是C#中利用静态类和静态变量来实现窗口间传值的方法和示例,非常的实用,有需要的小伙伴可以参考下。
    2015-06-06
  • C# CefSharp 根据输入日期段自动选择日期的操作代码

    C# CefSharp 根据输入日期段自动选择日期的操作代码

    这篇文章主要介绍了C# CefSharp 根据输入日期段自动选择日期的操作代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-01-01
  • c#使用热键实现程序窗口隐藏示例

    c#使用热键实现程序窗口隐藏示例

    下面我们将演示如何通过热键隐藏窗口,会用到DllImports of Win32 API、CallBacks/Delegates,定制事件与事件的句柄,大家参考使用吧友
    2014-01-01
  • 基于C#实现文档打印功能

    基于C#实现文档打印功能

    在软件开发过程中,文档打印是一个常见的功能需求,本文将详细介绍如何在C#中实现文档打印,并通过代码示例介绍的非常详细,对大家的学习或工作有一定帮助,需要的朋友可以参考下
    2024-10-10
  • WinForm跨线程访问UI及UI卡死的解决方案

    WinForm跨线程访问UI及UI卡死的解决方案

    在WinForm开发过程中,跨线程访问UI控件和界面卡死是常见的技术难题,由于Windows窗体应用程序的UI控件默认只能在主线程(UI线程)上操作,直接在其他线程中修改UI会导致异常,本文通过实际测试案例,总结了Invoke和BeginInvoke在不同场景下的使用方法及注意事项
    2025-07-07
  • c# 获得局域网主机列表实例

    c# 获得局域网主机列表实例

    c# 获得局域网主机列表实例...
    2007-03-03
  • C#使用foreach语句搜索数组元素的方法

    C#使用foreach语句搜索数组元素的方法

    这篇文章主要介绍了C#使用foreach语句搜索数组元素的方法,涉及C#使用foreach语句遍历数组实现搜索功能的技巧,非常具有实用价值,需要的朋友可以参考下
    2015-04-04
  • C#中GDI+绘制圆弧及圆角矩形等比缩放的绘制

    C#中GDI+绘制圆弧及圆角矩形等比缩放的绘制

    这篇文章主要介绍了C#中GDI+绘制圆弧及圆角矩形等比缩放的绘制,文章围绕主题展开详细的内容戒杀,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08

最新评论