Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C#实现Modbus RTU主站通信(一)

C#实现Modbus RTU主站通信(一)

原创
作者头像
编程笔记in
发布于 2025-05-07 04:03:22
发布于 2025-05-07 04:03:22
4130
举报
文章被收录于专栏:C#C#
  • 摘要:本文描述如何使用C#调用NModbus4库 和System.IO.Ports库实现ModbusRTU通信功能。


前言

  • Modbus RTU 是一种串行通信协议,通讯机制为主设备依次询问从设备的轮询机制,。该协议通常用于工业领域。此案例内容是关于如何使用C#实现Modbus RTU通讯的简单练手案例,通过该案例初步了解其基本功能。\
  • 案例通过在C#程序中调用NModbus4和 System.IO.Ports 实现ModbusRTU通讯功能。案例只实现功能调用,所以直接使用其自带的读取、写入方法,不关心底层报文。当然也有NModbus4 默认不暴露原始报文的原因,本文没有实现显示原始报文,会在后续案例中实现。

(一) 实现功能(ModbusRTU)

  • 功能码01:读取线圈状态 (ReadCoils)
  • 功能码02:读取输入状态(ReadInputs)
  • 功能码03:读取保持寄存器(ReadHoldingRegisters)
  • 功能码04:读取输入寄存器(ReadInputRegisters)
  • 功能码05:写入单个线圈(WriteSingleCoil)
  • 功能码06:写入单个寄存器(WriteSingleRegister)
  • 功能码15:写入多个线圈(WriteMultipleCoils)功能码16:写入多个寄存器(WriteMultipleRegisters)

运行环境
  • 操作系统:Win11
  • 编程软件:Visual Studio 2022
  • .Net版本:.Net Framework 4.8.0
  • 依赖版本:NModbus4 2.1.0

一、预览

(一)运行效果

二、代码

(一)MainForm类代码

MainForm类代码大概实现如下功能:

  • 1、初始化窗体、加载窗体、初始化配置参数。
  • 2、连接、读取、写入。
  • 3、定时器:连接状态、循环读取显示数据。
  • 4、控件使能更新、参数变更。
  • 5、操作消息更新。

代码语言:csharp
AI代码解释
复制
public partial class MainForm : WinFormBase
{
    #region 对象
    ModbusRtuMaster modbusMaster;
    States connectState = States.Stop;
    Timer connectStateTimer = new Timer();
    Timer resultShowTimer = new Timer();
    #endregion

    #region 初始化窗体、加载窗体、初始化配置参数
    public MainForm()
    {
        InitializeComponent();
        this.CenterToParent();
    }
    private void MainForm_Load(object sender, EventArgs e)
    {
        Initialize();
    }
    private void Initialize()
    {
        //初始化Modbus
        modbusMaster = new ModbusRtuMaster();
        //初始化参数
        cbx_SerialPort.DataSource = modbusMaster.GetSerialPortArray();
        cbx_BaudRate.DataSource = modbusMaster.GetBaudRateArray();
        cbx_Parity.DataSource = modbusMaster.GetParityArray();
        cbx_DataBits.DataSource = modbusMaster.GetDataBitArray();
        cbx_StopBits.DataSource = modbusMaster.GetStopBitArray();
        cbx_FuncCode.SelectedIndex = 0;
        //初始化定时器
        connectStateTimer = new Timer();
        connectStateTimer.Interval = 500;
        connectStateTimer.Tick += ConnectStateTimer_Tick;
        resultShowTimer = new Timer();
        resultShowTimer.Interval = 500;
        connectStateTimer.Tick += ResultShowTimer_Tick;
        //初始化表格
        dataGridView.ReadOnly = true;
        dataGridView.Columns[0].Width = 100;
        dataGridView.Columns[1].Width = 100;
        dataGridView.Columns[0].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
        dataGridView.Columns[1].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
        dataGridView.RowHeadersVisible = false;
        //初始化表格数据
        for (int i = 0; i < 10; i++)
        {
            dataGridView.Rows.Add(new object[] { i, 0 });
        }
        //初始化
        rtbx_Message.ForeColor = Color.Gray;
        btn_WriteData.Enabled = false;
        rsc_ConnectState.State = States.Stop;
    }
    #endregion

    #region 连接、读取写入
    private void btn_OpenRTU_Click(object sender, EventArgs e)
    {
        Open();
    }
    private void btn_WriteData_Click(object sender, EventArgs e)
    {
        WriteData();
    }
    private void Open()
    {
        if (btn_OpenRTU.Text.Equals("连接"))
        {
            MessageUpdate("正在创建ModbusRTU连接...", Color.Green);
            btn_OpenRTU.Enabled = false;
            if (modbusMaster.IsConnected) modbusMaster.DisConnect();
            if (!modbusMaster.Connect(modbusMaster.protModel.PortName))
            {
                modbusMaster.DisConnect();
                Debug.WriteLine("无法与ModbusRTU从站建立连接");
                MessageUpdate("无法与ModbusRTU从站建立连接", Color.Red);
                btn_OpenRTU.Enabled = true;
                return;
            }
            try
            {
                MessageUpdate("创建ModbusRTU成功...", Color.Green);
                connectState = States.Running;
                Task<bool[]> registers = modbusMaster.ReadCoilsAsync(modbusMaster.SlaveId, 0, 1);
                if (registers.Result != null)
                {
                    Debug.WriteLine($"尝试读取值: value = {registers.Result[0]}");
                    MessageUpdate($"尝试读取值: value = {registers.Result[0]}", Color.Green);
                }
                ControlEnableUpdate();
                connectStateTimer.Start();
                resultShowTimer.Start();
                btn_OpenRTU.Enabled = true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"触发异常:{ex.Message}");
                MessageUpdate($"尝试读取值失败,连接失败...异常原因:{ex.Message}", Color.Red);
                modbusMaster.DisConnect();
                connectState = States.Stop;
                connectStateTimer.Stop();
                resultShowTimer.Stop();
                ControlEnableUpdate();
                btn_OpenRTU.Enabled = true;
            }
        }
        else
        {
            MessageUpdate("断开ModbusRTU通讯连接...", Color.Red);
            modbusMaster.DisConnect();
            connectState = States.Stop;
            connectStateTimer.Stop();
            resultShowTimer.Stop();
            rsc_ConnectState.State = connectState;
            ControlEnableUpdate();
            btn_OpenRTU.Enabled = true;
        }
    }
    /// <summary>
    /// 写数据
    /// </summary>
    private void WriteData()
    {
        ushort startAddress = (ushort)nudx_WriteStartAddress.Value;
        try
        {
            switch (modbusMaster.FuncCode)
            {
                case "05":
                    modbusMaster.WriteSingleCoilAsync(modbusMaster.SlaveId, startAddress, bool.Parse(tbx_WriteData.Text));
                    UpdateDataShow($"从站={modbusMaster.SlaveId},功能码 = {modbusMaster.FuncCode}," +
                        $"起始地址 = {modbusMaster.StartAddress},写入值={tbx_WriteData.Text}");
                    break;
                case "06":
                    modbusMaster.WriteSingleRegisterAsync(modbusMaster.SlaveId, startAddress, ushort.Parse(tbx_WriteData.Text));
                    UpdateDataShow($"从站={modbusMaster.SlaveId},功能码 = {modbusMaster.FuncCode}," +
                       $"起始地址 = {modbusMaster.StartAddress},写入值={tbx_WriteData.Text}");
                    break;
                case "15":
                    bool[] dataBool = ParseArray<bool>(tbx_WriteData.Text);
                    modbusMaster.WriteMultipleCoilsAsync(modbusMaster.SlaveId, startAddress, dataBool);
                    UpdateDataShow($"从站={modbusMaster.SlaveId},功能码 = {modbusMaster.FuncCode}," +
                       $"起始地址 = {modbusMaster.StartAddress},写入值={tbx_WriteData.Text}");
                    break;
                case "16":
                    ushort[] dataUshort = ParseArray< ushort > (tbx_WriteData.Text);
                    modbusMaster.WriteMultipleRegistersAsync(modbusMaster.SlaveId, startAddress, dataUshort);
                    UpdateDataShow($"从站={modbusMaster.SlaveId},功能码 = {modbusMaster.FuncCode}," +
                      $"起始地址 = {modbusMaster.StartAddress},写入值={tbx_WriteData.Text}");
                    break;
                default:
                    MessageUpdate($"功能码 = {modbusMaster.FuncCode},不匹配...",Color.Red);
                    break;
            }
        }
        catch (Exception ex)
        {
            UpdateDataShow($"写入异常,{ex.Message}");
        }
    }
    /// <summary>
    /// 字符串转换数组
    /// </summary>
    private T[] ParseArray<T>(string input)
    {
        string[] items = input.Trim('[', ']').Split(',');
        return items.Select(item => (T)Convert.ChangeType(item.Trim(), typeof(T))).ToArray();
    }
    #endregion

    #region 定时器

    /// <summary>
    /// 连接状态定时器
    /// </summary>
    private void ConnectStateTimer_Tick(object sender, EventArgs e)
    {
        if (modbusMaster.IsConnected)
        {
            rsc_ConnectState.Invoke(new Action(() => {
                if (connectState == States.Running)
                {
                    rsc_ConnectState.State = (rsc_ConnectState.State == States.None ? States.Running : States.None);
                    Task.Delay(500);
                }
            }));
        }
    }

    /// <summary>
    /// 地址结果定时器
    /// </summary>
    private void ResultShowTimer_Tick(object sender, EventArgs e)
    {
        if (!modbusMaster.IsConnected || !checkBx_LoopRead.Checked) return;
        try
        {
            dataGridView.Invoke(new Action(() =>
            {
                Task<bool[]> result;
                Task<ushort[]> registers;
                switch (modbusMaster.FuncCode)
                {
                    case "01":
                        result = modbusMaster.ReadCoilsAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
                        UpdateDataShow<bool>(result?.Result);
                        break;
                    case "02":
                        result = modbusMaster.ReadInputsAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
                        UpdateDataShow<bool>(result?.Result);
                        break;
                    case "03":
                        registers = modbusMaster.ReadHoldingRegistersAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
                        UpdateDataShow<ushort>(registers?.Result);
                        break;
                    case "04":
                        registers = modbusMaster.ReadInputRegistersAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
                        UpdateDataShow<ushort>(registers?.Result);
                        break;
                    case "05":
                        result = modbusMaster.ReadCoilsAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
                        UpdateDataShow<bool>(result?.Result);
                        break;
                    case "06":
                        registers = modbusMaster.ReadHoldingRegistersAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
                        UpdateDataShow<ushort>(registers.Result);
                        break;
                    case "15":
                        result = modbusMaster.ReadCoilsAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
                        UpdateDataShow<bool>(result?.Result);
                        break;
                    case "16":
                        registers = modbusMaster.ReadHoldingRegistersAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
                        UpdateDataShow<ushort>(registers?.Result);
                        break;
                    default:
                        result = modbusMaster.ReadCoilsAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
                        UpdateDataShow<bool>(result?.Result);
                        break;
                }
            }));
        }
        catch (Exception ex)
        {
            MessageUpdate($"{ex.Message}",Color.Red);
        }
    }
    /// <summary>
    /// 数据更新显示
    /// </summary>
    private void UpdateDataShow<T>(T[] array,string appendText = null)
    {
        for (int i = 0; i < modbusMaster.DataLength && i< 10; i++)
        {
            dataGridView.Rows[i].Cells[0].Value = (modbusMaster.StartAddress + i);
            dataGridView.Rows[i].Cells[1].Value = array[i];
        }
        if (!checkBx_LoopShow.Checked) return;
            MessageUpdate($"[{modbusMaster.StartAddress}][{ArrayToString<T>(array)}]", Color.Blue, "# 接收 ASCII>");
    }
    private void UpdateDataShow<T>(T result, string appendText = null)
    {
        MessageUpdate($"[{modbusMaster.StartAddress}][{result}]", Color.Green, "# 发送 ASCII>");
    }
    /// <summary>
    /// 数组转字符串
    /// </summary>
    private string ArrayToString<T>(T[] values, string sep = " ")
    {
        return string.Join(sep, values.Select(r => Convert.ToString(r)).ToArray());
    }
    #endregion

    #region 控件使能更新
    /// <summary>
    /// 控件使能更新
    /// </summary>
    private void ControlEnableUpdate()
    {
        ControlEnabled(cbx_SerialPort, !modbusMaster.IsConnected);
        ControlEnabled(cbx_BaudRate, !modbusMaster.IsConnected);
        ControlEnabled(cbx_Parity, !modbusMaster.IsConnected);
        ControlEnabled(cbx_DataBits, !modbusMaster.IsConnected);
        ControlEnabled(cbx_StopBits, !modbusMaster.IsConnected);
        ControlEnabled(nudx_SlaveId, !modbusMaster.IsConnected);
        btn_OpenRTU.Invoke(new Action(() => {
            btn_OpenRTU.Text = modbusMaster.IsConnected ? "关闭" : "连接";
        }));
    }
    /// <summary>
    /// 控件使能启用
    /// </summary>
    public void ControlEnabled(Control control,bool flag)
    {
        control.Invoke(new Action(() =>
        {
            control.Enabled = flag;
        }));
    }
    #endregion

    #region 参数变更
    private void cbx_SerialPort_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (cbx_SerialPort == null || cbx_SerialPort.SelectedItem == null) return;
            modbusMaster.protModel.PortName = cbx_SerialPort.SelectedItem.ToString();
    }
    private void cbx_BaudRate_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (cbx_BaudRate == null || cbx_BaudRate.SelectedItem == null) return;
        if (int.TryParse(cbx_BaudRate.SelectedItem.ToString(),out int result))
        {
            modbusMaster.protModel.BaudRate = result;
        }
        else
        {
            cbx_BaudRate.SelectedItem = modbusMaster.protModel.BaudRate.ToString();
        }

    }
    private void cbx_Parity_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (Enum.TryParse(cbx_Parity.SelectedItem.ToString(), out Parity result))
        {
            modbusMaster.protModel.Parity = result;
        }
        else
        {
            cbx_Parity.SelectedItem = modbusMaster.protModel.Parity.ToString();
        }
    }
    private void cbx_DataBits_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (int.TryParse(cbx_DataBits.SelectedItem.ToString(), out int result))
        {
            modbusMaster.protModel.DataBits = result;
        }
        else
        {
            cbx_DataBits.SelectedItem = modbusMaster.protModel.DataBits.ToString();
        }
    }
    private void cbx_StopBits_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (Enum.TryParse(cbx_StopBits.SelectedItem.ToString(), out StopBits result))
        {
            modbusMaster.protModel.StopBits = result;
        }
        else
        {
            cbx_StopBits.SelectedItem = modbusMaster.protModel.StopBits.ToString();
        }
    }
    private void cbx_FuncCode_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (cbx_FuncCode==null)return;
            modbusMaster.FuncCode = cbx_FuncCode.SelectedItem.ToString().Split('_')[0];
        byte funcCode = byte.Parse(modbusMaster.FuncCode);
        if (modbusMaster.IsConnected &&  funcCode== 5 || funcCode == 6 || funcCode == 15 || funcCode == 16)
        {
            btn_WriteData.Enabled = true;
        }
        else
        {
            btn_WriteData.Enabled = false;
        }

    }
    private void nudx_StartAddress_ValueChanged(object sender, EventArgs e)
    {
        if (nudx_StartAddress.Value < ushort.MaxValue)
        {
            modbusMaster.StartAddress = (ushort)nudx_StartAddress.Value;
        }
        else
        {
            nudx_StartAddress.Value = modbusMaster.StartAddress;
        }
    }
    private void nudx_DataLength_ValueChanged(object sender, EventArgs e)
    {
        if (ushort.Parse(nudx_DataLength.Value.ToString()) < ushort.MaxValue)
        {
            modbusMaster.DataLength = (ushort)nudx_DataLength.Value;
        }
        else
        {
            nudx_DataLength.Value = modbusMaster.DataLength;
        }
    }
    private void nudx_SlaveId_ValueChanged(object sender, EventArgs e)
    {
        if (byte.Parse(nudx_SlaveId.Value.ToString()) < byte.MaxValue)
        {
            modbusMaster.SlaveId = (byte)nudx_SlaveId.Value;
        }
        else
        {
            nudx_SlaveId.Value = modbusMaster.SlaveId;
        }
    }
    private void nudx_WriteStartAddress_ValueChanged(object sender, EventArgs e)
    {
        if (ushort.Parse(nudx_WriteStartAddress.Value.ToString()) < ushort.MaxValue)
        {
            modbusMaster.WriteStartAddress = (ushort)nudx_WriteStartAddress.Value;
        }
        else
        {
            nudx_WriteStartAddress.Value = modbusMaster.WriteStartAddress;
        }
    }
    private void nudx_WriteDataLength_ValueChanged(object sender, EventArgs e)
    {
        if (ushort.Parse(nudx_WriteDataLength.Value.ToString()) < ushort.MaxValue)
        {
            modbusMaster.WriteDataLength = (ushort)nudx_WriteDataLength.Value;
        }
        else
        {
            nudx_WriteDataLength.Value = modbusMaster.WriteDataLength;
        }
    }
    #endregion

    #region 操作消息更新
    /// <summary>
    /// 操作消息更新
    /// </summary>
    /// <param name="data"></param>
    /// <param name="color"></param>
    /// <param name="appendText"></param>
    /// <param name="maxLineNum"></param>
    /// <param name="isAppendTime"></param>
    private void MessageUpdate(string data, Color color, string appendText = null, int maxLineNum = 1000, bool isAppendTime = true)
    {
        // 空数据检查
        if (string.IsNullOrEmpty(data)) return;
        // 线程安全调用
        if (rtbx_Message.InvokeRequired)
        {
            rtbx_Message.BeginInvoke(new Action(() =>MessageUpdate(data, color,  appendText, maxLineNum, isAppendTime)));
            return;
        }
        lock (rtbx_Message)
        {
            rtbx_Message.SuspendLayout(); // 暂停重绘提高性能
            try
            {

                if (rtbx_Message.Lines.Length > maxLineNum)
                {
                    rtbx_Message.Clear();
                }
                if (isAppendTime)
                {
                    rtbx_Message.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}]:");
                }
                if (!string.IsNullOrEmpty(appendText))
                {
                    rtbx_Message.AppendText($"{appendText}{Environment.NewLine}");
                }
                else
                {
                    rtbx_Message.AppendText($"{Environment.NewLine}");
                }
                int startIndex = rtbx_Message.TextLength;
                rtbx_Message.ScrollToCaret();
                rtbx_Message.SelectionStart = rtbx_Message.TextLength;
                rtbx_Message.AppendText($"{data}{Environment.NewLine}");
                SetTextColor(rtbx_Message, startIndex, data.Length, color);
            }
            finally
            {
                rtbx_Message.ResumeLayout(); // 恢复重绘
            }
        }
    }
    /// <summary>
    /// 设置文本框指定范围内的文本颜色
    /// </summary>
    private void SetTextColor(RichTextBox rtb, int startIndex, int length, Color color)
    {
        rtb.Invoke(new Action(() => {
            // 保存当前选择状态
            int originalStart = rtb.SelectionStart;
            int originalLength = rtb.SelectionLength;
            // 设置新选择范围
            rtb.Select(startIndex, length);
            // 更改选中文本的颜色
            rtb.SelectionColor = color;
            // 恢复原始选择状态
            rtb.Select(originalStart, originalLength);
        }));
    }

    #endregion
}

(二)ModbusRtuMaster类

Mobus主站类实现如下功能

  • 1、通讯的连接、断开。
  • 2、数据的读取、写入。
  • 3、通讯参数的获取方法。

代码语言:csharp
AI代码解释
复制
public class ModbusRtuMaster
{
    private SerialPort _serialPort;
    private IModbusSerialMaster _master;
    public SerialPortModel protModel;
    public byte SlaveId { get; set; } = 1;

    public bool IsConnected {  get; private set; } = false;
    public string FuncCode { get; internal set; } = "01";

    public ushort StartAddress { get; set; } = 0;
    public ushort DataLength { get; set; } = 1;
    public ushort WriteStartAddress { get; set; } = 0;
    public ushort WriteDataLength { get; set; } = 1;
    public ModbusRtuMaster()
    {
        protModel = new SerialPortModel();
        _serialPort = new SerialPort();
    }

    /// <summary>
    /// 波特率、数据位、停止位、校验位
    /// </summary>
    /// <param name="portName"></param>
    /// <param name="baudRate">波特率</param>
    /// <param name="parity">校验位</param>
    /// <param name="dataBits">数据位</param>
    /// <param name="stopBits">停止位</param>
    /// <returns>连接</returns>
    public bool Connect(string portName,
        int baudRate = 9600, int dataBits = 8,
        Parity parity = Parity.None, StopBits stopBits = StopBits.One)
    {
        try
        {
            if (_serialPort == null)
                _serialPort = new SerialPort();
            _serialPort.PortName = portName;
            _serialPort.BaudRate = baudRate;
            _serialPort.DataBits = dataBits;
            _serialPort.Parity = parity;
            _serialPort.StopBits = stopBits;
            SetTimeout();
            _serialPort.Open();
            _master = ModbusSerialMaster.CreateRtu(_serialPort);
            Debug.WriteLine($"初始化成功!");
            IsConnected = true;
            return true;
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"初始化失败: {ex.Message}");
            IsConnected = false;
            return false;
        }
    }
    public bool Connect(SerialPortModel portModel)
    {
        return Connect(portModel.PortName,
         portModel.BaudRate, portModel.DataBits,
         portModel.Parity, portModel.StopBits);
    }
    public void DisConnect()
    {
        _master?.Dispose();
        _serialPort?.Close();
        _serialPort.Dispose();
        IsConnected =false;
    }
    /// <summary>
    /// 超时设置
    /// </summary>
    public void SetTimeout(int readTimeout = 2000, int writeTimeout = 2000)
    {
        _serialPort.ReadTimeout = readTimeout;
        _serialPort.WriteTimeout = writeTimeout;
    }
    #region 读取
    /// <summary>
    /// 读取线圈状态 (功能码01)
    /// </summary>
    public Task<bool[]> ReadCoilsAsync(byte slaveId, ushort startAddress, ushort numberOfPoints)
    {
        try
        {
            Task<bool[]> result = _master.ReadCoilsAsync(slaveId, startAddress, numberOfPoints);
            return result;

        }
        catch (Exception ex)
        {
            Debug.WriteLine($"读取线圈失败: {ex.Message}");
            return null;
        }
    }
    /// <summary>
    /// 读取输入状态 (功能码02)
    /// </summary>
    public Task<bool[]> ReadInputsAsync(byte slaveId, ushort startAddress, ushort numberOfPoints)
    {
        try
        {
            return _master.ReadInputsAsync(slaveId, startAddress, numberOfPoints);
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"读取输入状态失败: {ex.Message}");
            return null;
        }
    }
    /// <summary>
    /// 读取保持寄存器 (功能码03)
    /// </summary>
    public Task<ushort[]> ReadHoldingRegistersAsync(byte slaveId, ushort startAddress, ushort numberOfPoints)
    {
        try
        {
            return _master.ReadHoldingRegistersAsync(slaveId, startAddress, numberOfPoints);
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"读取保持寄存器失败: {ex.Message}");
            throw new Exception($"读取保持寄存器失败: {ex.Message}");

        }
    }
    /// <summary>
    /// 读取输入寄存器 (功能码04)
    /// </summary>
    public Task<ushort[]> ReadInputRegistersAsync(byte slaveId, ushort startAddress, ushort numberOfPoints)
    {
        try
        {
            return _master.ReadInputRegistersAsync(slaveId, startAddress, numberOfPoints);
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"读取输入寄存器失败: {ex.Message}");
            return null;
        }
    }
    #endregion

    #region 写入
    /// <summary>
    /// 写入单个线圈 (功能码05)
    /// </summary>
    public bool WriteSingleCoilAsync(byte slaveId, ushort coilAddress, bool value)
    {
        try
        {
            _master.WriteSingleCoilAsync(slaveId, coilAddress, value);
            return true;
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"写入单个线圈失败: {ex.Message}");
            return false;
        }
    }
    /// <summary>
    /// 写入单个寄存器 (功能码06)
    /// </summary>
    public bool WriteSingleRegisterAsync(byte slaveId, ushort registerAddress, ushort value)
    {
        try
        {
            _master.WriteSingleRegisterAsync(slaveId, registerAddress, value);
            return true;
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"写入单个寄存器失败: {ex.Message}");
            return false;
        }
    }
    /// <summary>
    /// 写入多个线圈 (功能码15)
    /// </summary>
    public bool WriteMultipleCoilsAsync(byte slaveId, ushort startAddress, bool[] data)
    {
        try
        {
            _master.WriteMultipleCoilsAsync(slaveId, startAddress, data);
            return true;
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"写入多个线圈失败: {ex.Message}");
            return false;
        }
    }
    /// <summary>
    /// 写入多个寄存器(功能码16)
    /// </summary>
    public bool WriteMultipleRegistersAsync(byte slaveId, ushort startAddress, ushort[] data)
    {
        try
        {
            _master.WriteMultipleRegistersAsync(slaveId, startAddress, data);
            return true;
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"写入多个寄存器失败: {ex.Message}");
            return false;
        }
    }
    #endregion

    #region 获取串口参数
    //波特率数组
    public int[] _BaudRateArray = { 9600, 14400, 19200, 38400, 57600, 115200 };
    //数据位数组
    public int[] _DataBitArray = { 8, 7, 6, 5 };
    public  string[] GetSerialPortArray()
    {
        return SerialPort.GetPortNames();
    }
    public Array GetBaudRateArray()
    {
        return _BaudRateArray;
    }
    public Array GetDataBitArray()
    {
        return _DataBitArray;
    }
    public string[] GetParityArray()
    {
        return Enum.GetNames(typeof(Parity));
    }
    public string[] GetStopBitArray()
    {
        return Enum.GetNames(typeof(StopBits)).Skip(1).ToArray();
    }
    #endregion
}

结语

  • 通过此案例学习基本的ModbusRTU功能,刚开始学习,暂时不想它的实现原理,先实现功能。通过使用Winform中自带的控件,编写一个简单的界面,通过完成案例的反馈,获取学习的兴趣感。希望文章能对你有帮助,既是分享,也是备份。

  • 如果你觉得这篇文章对你有帮助,不妨点个赞再走呗!
  • 如有其他疑问,欢迎评论区留言讨论!
  • 也可以关注微信公众号 [编程笔记in] ,一起学习交流!
  • 项目源码:gitee.com/incodenotes/csharp-modbus

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
电气工程师要了解的上位机知识,C#简单几行代码就能实现Modbus通信,源代码分享
之前一直想用C#写一个MODBUS通信的案例,本来想用SerialPort直接写的,然后发现可以用 nModbus类库,使用nModbus库来编写Modbus通信的C#案例是一个很好的选择,因为它大大简化了Modbus通信的实现过程。
自动化大师
2024/08/14
4550
电气工程师要了解的上位机知识,C#简单几行代码就能实现Modbus通信,源代码分享
C#实例:串口通讯
 最近在研究串口通讯,其中有几个比较重要的概念,RS-232这种适配于上位机和PC端进行连接,RS-232只限于PC串口和设备间点对点的通信。它很简单的就可以进行连接,由于串口通讯是异步的,也就是说你可以同时向两端或者更多进行数据发送,它们之间的传输数据类型是byte,串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。
zls365
2020/08/19
3.7K1
C#实例:串口通讯
C#上位机串口控制12864显示
实现的效果 上面是用Proteus仿真的,,对了如果自己想用proteus仿真需要安装下面这个软件 再看一下实物显示效果 先做上位机部分........... 为了程序一启动就把电脑上能用的串口号显示
杨奉武
2018/04/17
4.6K0
C#上位机串口控制12864显示
c#开发地磅称重软件
2012年时即做过一个地磅称重软件,最近公司又接了一个地磅过磅软件的项目,把遇到的问题总结一下以备后用。
跟着阿笨一起玩NET
2018/09/20
1.5K0
C#串口操作类,包括串口读写操作
本文转载自博客园:C#串口操作类,包括串口读写操作 串口进行操作的类,其中包括写和读操作,类可设置串口参数、设置接收函数、打开串口资源、关闭串口资源,操作完成后,一定要关闭串口、接收串口数据事件、接收数据出错事件、获取当前全部串口、把字节型转换成十六进制字符串等功能。这个串口类已经过了调试,可以使用,相关C#代码如下:
ccf19881030
2020/09/17
4.7K0
C#上位机开发(三)—— 构建SerialAssistant雏形
上一篇简单介绍了C#的一些基本知识,并成功的Hello,World,那么从这篇开始,我们来自己动手写一个串口助手:
vv彭
2020/11/12
3.2K0
C#上位机开发(三)—— 构建SerialAssistant雏形
C# Modbus 通讯
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/157958.html原文链接:https://javaforall.cn
全栈程序员站长
2022/09/14
1.7K0
C# SqlSugar框架的学习使用(四)-- 插入数据的详细用法
上一篇《C# SqlSugar框架的学习使用(三)-- 查询的多种用法》我们已经把SqlSugar的查询多种用法实现了,这篇我们就来说说插入数据的多种用法。
Vaccae
2019/08/20
9.7K0
C# SqlSugar框架的学习使用(四)-- 插入数据的详细用法
【C++】Qt:串口调试助手示例
串口助手是一种串口通讯测试工具,它可以用于打开、关闭、配置串口,读写串口数据等常见的串口通信操作。 在嵌入式系统调试、模块测试、通讯协议分析等领域都具有广泛的应用。
DevFrank
2024/07/24
7850
【C++】Qt:串口调试助手示例
第一个WPF程序(串口调试)
本例子仅支持“ASCII”编码的串口数据发送与接收,如果需要其他编码,请自行更改,谢谢!
CNXY
2020/02/18
9930
第一个WPF程序(串口调试)
【Unity3D】Unity3d 与串口的通信程序的开发,软件硬件结合
串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。(至于再详细,自己百度)
恬静的小魔龙
2020/03/11
2.7K0
【Unity3D】Unity3d 与串口的通信程序的开发,软件硬件结合
C#设备处理类操作
C#对于处理window操作系统下的设备有天然的优势,对于大多数设备读写等操作来说基本上够了,这里只讨论通过普通的大多数的设备的操作。涉及到两大类SerialPort类,Socket的一些操作。不一定好,但希望分享出去,让更多的人受益。。
FreeTimeWorker
2020/08/31
6660
分分钟钟让你从上位机小白变为大佬
我现在从事的C#工控机的开发,所以接下来会写一个系列关于上位机如何和工控机/PLC/各种仪表通信。希望能帮助到有需要的人(我假设你有过windows C#编程经验的)。
苏州程序大白
2021/08/13
3.5K0
分分钟钟让你从上位机小白变为大佬
WPF入门到放弃(五)| 串口的读取与写入(程序&附串口调试精灵)
WPF也是我今年刚开始深入去了解,看了不少的学习视频和书籍,受剑神Python入门到放弃的启发,想把这段时间学习内容做个总结,一是因为我相信技术总是需要不断的总结与练习才能有所进步,二是希望帮助初学者对WPF有个初步的了解,大家一起探讨学习进步。
剑指工控
2021/11/08
3K0
WPF入门到放弃(五)| 串口的读取与写入(程序&附串口调试精灵)
C#开发终端式短信的原理和方法
简介   没发过短信的年轻人肯定是属于那种受保护的稀有动物,通讯发达的今天短信已经成为人们交流的重要手段,其中也蕴含着巨大的市场和经济利益,掌握短信技术的人才也受到各大公司的追捧是目前职场上耀眼的明星。本文介绍了短信的原理和实现方法,重点说明了短信的编码方式、AT指令以及用C#实现串口通讯的方法。 前言   目前,主有三种发送短信的方式:   1、 网关方式:就是向当地的电信部门申请,不需要额外的设备,适用于大型的通信公司,像华为、傲天、中兴、亚信等。   2、 终端方式:就是借助像GSM MODEM之类的
阿新
2018/04/13
2.8K0
10min教你编写C#上位机控制Arduino板载LED
本文以一个编写串口上位机控制Arduino板载LED的小任务,来完整的展示如何从下位机做到上位机,包含了Arduino、串口、C#编程程,麻雀虽小,可是五脏俱全哦~
Mculover666
2020/07/16
3.9K0
C# UDP操作实例
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 System.Net.Sockets; using System.Ne
zls365
2020/08/19
1.2K0
C# UDP操作实例
[C#] 支持API的边缘网关开发笔记3-NModbus4封装
至此,Nmodbus4<2.1.0>封装完成.可以直接调用自己的DLL来使用了.
科控物联
2022/03/29
9950
[C#] 支持API的边缘网关开发笔记3-NModbus4封装
进阶|C#利用反射方式动态调用类库里的方法(附源码)
做为2020年第一篇技术文章,我们就直接分享一个进阶的的东西,利用反射方式动态调用类库执行方法。
Vaccae
2020/01/13
2.5K0
进阶|C#利用反射方式动态调用类库里的方法(附源码)
C#使用DataSet类、DataTable类、DataRow类、OleDbConnection类、OleDbDataAdapter类编写简单数据库应用
该文章介绍了如何通过C#和OleDbConnection类实现数据库操作,以及如何使用DataGridView控件进行数据展示。首先,讲解了如何创建数据库连接,然后介绍了如何往数据库中添加数据。之后,讲解了如何查询数据库中的数据,并将结果显示在DataGridView控件中。最后,介绍了如何删除数据库中的数据。
CNXY
2017/12/25
2.4K0
C#使用DataSet类、DataTable类、DataRow类、OleDbConnection类、OleDbDataAdapter类编写简单数据库应用
推荐阅读
相关推荐
电气工程师要了解的上位机知识,C#简单几行代码就能实现Modbus通信,源代码分享
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档