WinForm 与 Arduino 进行串口通信实现 LED 灯开关操作

参考

环境

软件/系统 版本 说明
Windows windows 10 专业版 22H2 64 位操作系统, 基于 x64 的处理器
Microsoft Visual Studio Community 2022 (64 位) - Current 版本 17.14.5
.NET Framework 4.8
Arduino IDE 2.3.6
硬件/设备 数量 说明
Arduino UNO 1
LED灯 1
220Ω电阻 1
杜邦线 若干

预览

  1. 上位机界面
    image

  2. Arduino接线

    图片来源 https://blog.csdn.net/lhwanglei/article/details/108569051

    image

    image

正文

上位机

  1. Form1.Designer.cs
    namespace ConsoleSerialPortTest
    {
    	partial class Form1
    	{
    		/// <summary>
    		/// 必需的设计器变量。
    		/// </summary>
    		private System.ComponentModel.IContainer components = null;
    
    		/// <summary>
    		/// 清理所有正在使用的资源。
    		/// </summary>
    		/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
    		protected override void Dispose(bool disposing)
    		{
    			if (disposing && (components != null))
    			{
    				components.Dispose();
    			}
    			base.Dispose(disposing);
    		}
    
    		#region Windows 窗体设计器生成的代码
    
    		/// <summary>
    		/// 设计器支持所需的方法 - 不要修改
    		/// 使用代码编辑器修改此方法的内容。
    		/// </summary>
    		private void InitializeComponent()
    		{
    			this.label1 = new System.Windows.Forms.Label();
    			this.textBox1 = new System.Windows.Forms.TextBox();
    			this.label2 = new System.Windows.Forms.Label();
    			this.radioButton1 = new System.Windows.Forms.RadioButton();
    			this.radioButton2 = new System.Windows.Forms.RadioButton();
    			this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
    			this.button1 = new System.Windows.Forms.Button();
    			this.button2 = new System.Windows.Forms.Button();
    			this.textBox2 = new System.Windows.Forms.TextBox();
    			this.label3 = new System.Windows.Forms.Label();
    			this.comboBox1 = new System.Windows.Forms.ComboBox();
    			this.button3 = new System.Windows.Forms.Button();
    			this.flowLayoutPanel1.SuspendLayout();
    			this.SuspendLayout();
    			// 
    			// label1
    			// 
    			this.label1.AutoSize = true;
    			this.label1.Location = new System.Drawing.Point(226, 89);
    			this.label1.Name = "label1";
    			this.label1.Size = new System.Drawing.Size(82, 24);
    			this.label1.TabIndex = 0;
    			this.label1.Text = "波特率";
    			// 
    			// textBox1
    			// 
    			this.textBox1.Location = new System.Drawing.Point(345, 86);
    			this.textBox1.Name = "textBox1";
    			this.textBox1.Size = new System.Drawing.Size(200, 35);
    			this.textBox1.TabIndex = 1;
    			this.textBox1.Text = "9600";
    			// 
    			// label2
    			// 
    			this.label2.AutoSize = true;
    			this.label2.Location = new System.Drawing.Point(240, 143);
    			this.label2.Name = "label2";
    			this.label2.Size = new System.Drawing.Size(58, 24);
    			this.label2.TabIndex = 2;
    			this.label2.Text = "开灯";
    			// 
    			// radioButton1
    			// 
    			this.radioButton1.AutoSize = true;
    			this.radioButton1.Location = new System.Drawing.Point(3, 3);
    			this.radioButton1.Name = "radioButton1";
    			this.radioButton1.Size = new System.Drawing.Size(65, 28);
    			this.radioButton1.TabIndex = 3;
    			this.radioButton1.Text = "开";
    			this.radioButton1.UseVisualStyleBackColor = true;
    			this.radioButton1.CheckedChanged += new System.EventHandler(this.radioButton_CheckedChanged);
    			// 
    			// radioButton2
    			// 
    			this.radioButton2.AutoSize = true;
    			this.radioButton2.Checked = true;
    			this.radioButton2.Location = new System.Drawing.Point(74, 3);
    			this.radioButton2.Name = "radioButton2";
    			this.radioButton2.Size = new System.Drawing.Size(65, 28);
    			this.radioButton2.TabIndex = 4;
    			this.radioButton2.TabStop = true;
    			this.radioButton2.Text = "关";
    			this.radioButton2.UseVisualStyleBackColor = true;
    			this.radioButton2.CheckedChanged += new System.EventHandler(this.radioButton_CheckedChanged);
    			// 
    			// flowLayoutPanel1
    			// 
    			this.flowLayoutPanel1.Controls.Add(this.radioButton1);
    			this.flowLayoutPanel1.Controls.Add(this.radioButton2);
    			this.flowLayoutPanel1.Location = new System.Drawing.Point(347, 143);
    			this.flowLayoutPanel1.Name = "flowLayoutPanel1";
    			this.flowLayoutPanel1.Size = new System.Drawing.Size(200, 40);
    			this.flowLayoutPanel1.TabIndex = 5;
    			// 
    			// button1
    			// 
    			this.button1.Location = new System.Drawing.Point(244, 218);
    			this.button1.Name = "button1";
    			this.button1.Size = new System.Drawing.Size(117, 45);
    			this.button1.TabIndex = 6;
    			this.button1.Text = "连接";
    			this.button1.UseVisualStyleBackColor = true;
    			this.button1.Click += new System.EventHandler(this.button1_Click);
    			// 
    			// button2
    			// 
    			this.button2.Location = new System.Drawing.Point(430, 218);
    			this.button2.Name = "button2";
    			this.button2.Size = new System.Drawing.Size(117, 45);
    			this.button2.TabIndex = 7;
    			this.button2.Text = "关闭";
    			this.button2.UseVisualStyleBackColor = true;
    			this.button2.Click += new System.EventHandler(this.button2_Click);
    			// 
    			// textBox2
    			// 
    			this.textBox2.Location = new System.Drawing.Point(69, 288);
    			this.textBox2.Multiline = true;
    			this.textBox2.Name = "textBox2";
    			this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
    			this.textBox2.Size = new System.Drawing.Size(736, 772);
    			this.textBox2.TabIndex = 8;
    			// 
    			// label3
    			// 
    			this.label3.AutoSize = true;
    			this.label3.Location = new System.Drawing.Point(226, 34);
    			this.label3.Name = "label3";
    			this.label3.Size = new System.Drawing.Size(106, 24);
    			this.label3.TabIndex = 9;
    			this.label3.Text = "串口列表";
    			// 
    			// comboBox1
    			// 
    			this.comboBox1.FormattingEnabled = true;
    			this.comboBox1.Location = new System.Drawing.Point(345, 31);
    			this.comboBox1.Name = "comboBox1";
    			this.comboBox1.Size = new System.Drawing.Size(200, 32);
    			this.comboBox1.TabIndex = 10;
    			// 
    			// button3
    			// 
    			this.button3.Location = new System.Drawing.Point(579, 24);
    			this.button3.Name = "button3";
    			this.button3.Size = new System.Drawing.Size(117, 45);
    			this.button3.TabIndex = 11;
    			this.button3.Text = "刷新";
    			this.button3.UseVisualStyleBackColor = true;
    			this.button3.Click += new System.EventHandler(this.button3_Click);
    			// 
    			// Form1
    			// 
    			this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 24F);
    			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    			this.ClientSize = new System.Drawing.Size(918, 1087);
    			this.Controls.Add(this.button3);
    			this.Controls.Add(this.comboBox1);
    			this.Controls.Add(this.label3);
    			this.Controls.Add(this.textBox2);
    			this.Controls.Add(this.button2);
    			this.Controls.Add(this.button1);
    			this.Controls.Add(this.flowLayoutPanel1);
    			this.Controls.Add(this.label2);
    			this.Controls.Add(this.textBox1);
    			this.Controls.Add(this.label1);
    			this.Name = "Form1";
    			this.Text = "Form1";
    			this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
    			this.Load += new System.EventHandler(this.Form1_Load);
    			this.flowLayoutPanel1.ResumeLayout(false);
    			this.flowLayoutPanel1.PerformLayout();
    			this.ResumeLayout(false);
    			this.PerformLayout();
    
    		}
    
    		#endregion
    
    		private System.Windows.Forms.Label label1;
    		private System.Windows.Forms.TextBox textBox1;
    		private System.Windows.Forms.Label label2;
    		private System.Windows.Forms.RadioButton radioButton1;
    		private System.Windows.Forms.RadioButton radioButton2;
    		private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
    		private System.Windows.Forms.Button button1;
    		private System.Windows.Forms.Button button2;
    		private System.Windows.Forms.TextBox textBox2;
    		private System.Windows.Forms.Label label3;
    		private System.Windows.Forms.ComboBox comboBox1;
    		private System.Windows.Forms.Button button3;
    	}
    }
    
    
    
  2. Form1.cs
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.IO.Ports;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using static System.Windows.Forms.VisualStyles.VisualStyleElement;
    
    namespace ConsoleSerialPortTest
    {
    	public partial class Form1 : Form
    	{
    		static SerialPort _serialPort;
    		static Thread readThread;
    		public Form1()
    		{
    			InitializeComponent();
    
    		}
    		private void Form1_Load(object sender, EventArgs e)
    		{
    			//
    			 readThread = new Thread(Read);
    			readThread.Start();
    			//
    			GetComs();
    		}
    
    		private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    		{
    			readThread.Abort();
    		}
    
    		public void LinkArduino()
    		{
    			_serialPort = new SerialPort(comboBox1.SelectedItem.ToString(), int.Parse(textBox1.Text));
    			_serialPort.Encoding = System.Text.Encoding.UTF8;
    			// 定时读取串口数据,超时会出发异常
    			//_serialPort.ReadTimeout = 500;
    			//_serialPort.WriteTimeout = 500;
    			try
    			{
    				_serialPort.Open();
    				Log("串口连接成功!");
    			}
    			catch (Exception ex)
    			{
    				Log($"串口连接失败:{ex.Message}");
    			}
    		}
    		// 使用 SerialPort.DataReceive 事件更好,接受数据后自动触发
    		public void Read()
    		{
    			while (true)
    			{
    				// 非连接状态下,直接跳过
    				if (!IsOpen())
    				{
    					continue;
    				}
    				try
    				{
    					string message = _serialPort.ReadLine();
    					// 去除空字符与换行,就可以接受正式数据了
    					if (!string.IsNullOrEmpty(message) && message != "\r")
    					{
    						// base64 是为了捕获换行符,然后进行过滤
    						// Convert.ToBase64String(Encoding.UTF8.GetBytes(message))
    						Log($"收到串口返回数据:{message}");
    					}
    				}
    				catch (Exception e)
    				{
    					Debug.WriteLine(e.Message);
    					continue;
    				}
    			}
    		}
    		/// <summary>
    		/// 检测串口是否打开
    		/// </summary>
    		/// <returns></returns>
    		public static bool IsOpen()
    		{
    			if (_serialPort is null)
    			{
    				return false;
    			}
    			return _serialPort.IsOpen;
    		}
    
    		public void GetComs()
    		{
    			string[] ports = System.IO.Ports.SerialPort.GetPortNames();
    			this.comboBox1.Items.Clear();
    			for (int i = 0; i < ports.Length; i++)
    			{
    				this.comboBox1.Items.Add(ports[i]);
    			}
    			if (this.comboBox1.Items.Count > 0)
    			{
    				this.comboBox1.SelectedIndex = 0;
    			}
    		}
    
    		private void button3_Click(object sender, EventArgs e)
    		{
    			this.GetComs();
    		}
    
    		private void button1_Click(object sender, EventArgs e)
    		{
    			LinkArduino();
    		}
    		/// <summary>
    		/// 开启LED
    		/// </summary>
    		/// <param name="sender"></param>
    		/// <param name="e"></param>
    		private void radioButton_CheckedChanged(object sender, EventArgs e)
    		{
    			var rb = sender as RadioButton;
    			if (rb.Checked && IsOpen())
    			{
    				Log(rb.Text);
    				if ("开".Equals(rb.Text))
    				{
    					_serialPort.WriteLine("1");
    				}
    				if ("关".Equals(rb.Text))
    				{
    					_serialPort.WriteLine("0");
    				}
    			}
    
    		}
    
    		/// <summary>
    		/// 
    		/// </summary>
    		public void Log(string msg)
    		{
    			Debug.WriteLine(msg);
    			// 判断是否在同一线程
    			if (!this.textBox2.InvokeRequired)
    			{
    				this.textBox2.Text += msg + "\r\n";
    			}
    			else
    			{
    				this.Invoke(new Action(
    					() =>
    					{
    						this.textBox2.Text += msg + "\r\n";
    					}
    				));
    			}
    		}
    
    		private void button2_Click(object sender, EventArgs e)
    		{
    			_serialPort.Close();
    		}
    	}
    }
    
    

Arduino

  1. main.ino
    // 初始化引脚
    int LED=9;
    void setup()
    {
    	Serial.begin(9600);
    	Serial.println("start arduino");
    	//
    	pinMode(LED,OUTPUT);
    }
    // 读取数据
    String readAllPort(){
      String comdata = "";
      while (Serial.available() > 0){
    	  comdata += char(Serial.read());  //每次读一个char字符,并相加
    	  delay(2);
      }
      return comdata;
    }
    
    // https://docs.arduino.cc/language-reference/en/variables/constants/highLow/
    void loop()
    {
    	String comdata = readAllPort();
    	if (comdata.length() > 0){
    		Serial.println("收到上位机消息 received:"+comdata);
    		int isOpen = comdata.toInt();
    		if(isOpen == 1){
    			//设置该引脚为高电平,点亮LED / HIGH = 1 or true
    			digitalWrite(LED,HIGH);      
    		}
    		if(isOpen == 0){
    			//设置该引脚为低电平,关闭LED  / LOW = 0 or false
    			digitalWrite(LED,LOW);
    		}
    		comdata = "";
    	}
    }
    
posted @ 2025-06-16 16:18  夏秋初  阅读(66)  评论(2)    收藏  举报