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

297 阅读5分钟
  • 摘要:本文描述如何使用C#调用NModbus4库 和System.IO.Ports库实现ModbusRTU通信功能。
  • 【原文】:mp.weixin.qq.com/s/kngStuHBJhyS37vKL8ch9Q
  • 【作者】:编程笔记in

前言

  • 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

一、预览

(一)运行效果

image.png

二、代码

(一)MainForm类代码

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

    • 1、初始化窗体、加载窗体、初始化配置参数。
    • 2、连接、读取、写入。
    • 3、定时器:连接状态、循环读取显示数据。
    • 4、控件使能更新、参数变更。
    • 5、操作消息更新。
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、通讯参数的获取方法。
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中自带的控件,编写一个简单的界面,通过完成案例的反馈,获取学习的兴趣感。希望文章能对你有帮助,既是分享,也是备份。

image.png

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