以下是完整优化后的文章版本,结构清晰、代码可直接复制运行,适合工业调试人员快速上手。


10分钟搞定C#串口通信:写个能发能收的工具(直接对接传感器/PLC)

在工业调试中,经常需要快速验证传感器、PLC、变频器等设备的串口通信是否正常。本文教你用 C# WinForms 10分钟开发一个即开即用的串口调试工具,支持参数配置、字符串/十六进制收发、带时间戳显示等功能,可直接对接各类Modbus RTU设备。

最终效果

  • 简洁实用界面
  • 支持COM口自动检测
  • 字符串与十六进制自由切换
  • 收发数据带时间戳和颜色区分
  • 一键清空 + 自动滚动

步骤1:创建项目(2分钟)

  1. 打开 Visual Studio 2022
  2. 新建项目 → Windows 窗体应用 (.NET Framework)(推荐 .NET 4.8)
  3. 项目名称:SerialTool
  4. 点击“创建”

步骤2:界面设计(3分钟)

Form1 上拖入以下控件并设置名称:

控件 名称 关键属性设置
GroupBox groupConfig Text = “串口配置”
ComboBox cmbPort DropDownStyle = DropDownList
ComboBox cmbBaud Items: 9600,19200,38400,115200,460800
ComboBox cmbParity Items: None, Odd, Even
ComboBox cmbDataBits Items: 8
ComboBox cmbStopBits Items: One
Button btnOpen Text = “打开串口”
TextBox txtSend Multiline = True
CheckBox chkHexSend Text = “十六进制发送”
CheckBox chkAutoScroll Text = “自动滚动”(默认勾选)
Button btnSend Text = “发送”
Button btnClear Text = “清空接收区”
RichTextBox rtbReceive Dock = Fill(接收显示区)

步骤3:完整核心代码(5分钟)

Form1.cs(完整可直接复制)

using System;
using System.Drawing;
using System.IO.Ports;
using System.Text;
using System.Windows.Forms;

namespace SerialTool
{
    public partial class Form1 : Form
    {
        private SerialPort _serialPort = new SerialPort();

        public Form1()
        {
            InitializeComponent();
            InitSerialPort();
            LoadPorts();
        }

        private void InitSerialPort()
        {
            _serialPort.DataReceived += SerialPort_DataReceived;
            _serialPort.Encoding = Encoding.UTF8;
        }

        // 加载可用串口
        private void LoadPorts()
        {
            cmbPort.Items.Clear();
            cmbPort.Items.AddRange(SerialPort.GetPortNames());
            if (cmbPort.Items.Count > 0)
                cmbPort.SelectedIndex = 0;
        }

        // 打开/关闭串口
        private void btnOpen_Click(object sender, EventArgs e)
        {
            try
            {
                if (!_serialPort.IsOpen)
                {
                    _serialPort.PortName = cmbPort.Text;
                    _serialPort.BaudRate = int.Parse(cmbBaud.Text);
                    _serialPort.Parity = (Parity)Enum.Parse(typeof(Parity), cmbParity.Text);
                    _serialPort.DataBits = int.Parse(cmbDataBits.Text);
                    _serialPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), cmbStopBits.Text);

                    _serialPort.Open();
                    btnOpen.Text = "关闭串口";
                    btnOpen.BackColor = Color.LightGreen;
                    AppendLog("系统", $"串口 {_serialPort.PortName} 打开成功", Color.DarkGreen);
                }
                else
                {
                    _serialPort.Close();
                    btnOpen.Text = "打开串口";
                    btnOpen.BackColor = SystemColors.Control;
                    AppendLog("系统", "串口已关闭", Color.Orange);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("操作失败:" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // 发送数据
        private void btnSend_Click(object sender, EventArgs e)
        {
            if (!_serialPort.IsOpen)
            {
                MessageBox.Show("请先打开串口!", "提示");
                return;
            }

            if (string.IsNullOrWhiteSpace(txtSend.Text)) return;

            try
            {
                string data = txtSend.Text.Trim();

                if (chkHexSend.Checked)
                {
                    byte[] buffer = StringToHexByte(data);
                    _serialPort.Write(buffer, 0, buffer.Length);
                }
                else
                {
                    _serialPort.WriteLine(data);
                }

                AppendLog("发送", data, Color.Blue);
            }
            catch (Exception ex)
            {
                MessageBox.Show("发送失败:" + ex.Message);
            }
        }

        // 接收数据事件
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                string data = _serialPort.ReadExisting();
                this.Invoke(new Action(() =>
                {
                    AppendLog("接收", data, Color.Green);
                }));
            }
            catch { }
        }

        // 添加日志(带颜色和时间戳)
        private void AppendLog(string dir, string content, Color color)
        {
            rtbReceive.SelectionColor = color;
            rtbReceive.AppendText($"[{DateTime.Now:HH:mm:ss}] {dir} >> {content}\r\n");

            if (chkAutoScroll.Checked)
                rtbReceive.ScrollToCaret();
        }

        // 字符串转十六进制字节数组
        private byte[] StringToHexByte(string hex)
        {
            hex = hex.Replace(" ", "").Replace("\r", "").Replace("\n", "");
            byte[] bytes = new byte[hex.Length / 2];
            for (int i = 0; i < bytes.Length; i++)
            {
                bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
            }
            return bytes;
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            rtbReceive.Clear();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_serialPort.IsOpen)
                _serialPort.Close();
        }
    }
}

使用方法

  1. 运行程序
  2. 选择正确的串口和波特率
  3. 点击“打开串口”
  4. 在发送框输入指令,可切换十六进制模式
  5. 实时查看接收数据(带颜色区分)

适用场景

  • Modbus RTU 传感器调试
  • PLC 串口指令测试
  • 单片机、变频器、仪表通讯验证

进阶建议

  • 增加自动重连功能
  • 支持保存常用指令模板
  • 升级为 .NET 8/9 + Native AOT 单文件工具

需要我继续补充 Modbus RTU 专用版本(带CRC自动计算)、数据导出Excel功能,还是**.NET MAUI 跨平台版本**?

更多推荐