如何实现Arduino向C#窗体高效传整数以控制电脑鼠标?
解决VR头显串口传输的两个核心问题
问题1:串口字符串转整数失败
原因
- Arduino发送的内容可能附带多余的回车/换行符,或者串口传输时出现乱码、不完整数据,导致转换时格式错误
- 直接读取后转换未处理空白字符,也没有异常捕获机制,遇到无效数据直接崩溃
解决方法
- Arduino端确保只发送纯整数(避免额外调试输出),
Serial.println默认会在整数后添加\r\n,这部分可以保留,但要保证没有其他冗余输出 - C#端读取字符串后先调用
Trim()去除首尾空白字符(包括\r、\n),再尝试转换,同时添加try-catch捕获转换异常,避免程序崩溃
问题2:窗体卡顿严重
原因
- 在UI线程的
button1_Click事件里写了while(true)死循环,完全阻塞了UI消息队列,导致窗体无法响应任何更新操作 - 串口读取操作属于IO操作,放在UI线程执行会拖慢整个界面的响应速度
解决方法
- 移除UI线程内的死循环,改用
SerialPort的DataReceived事件异步处理串口数据,让UI线程专注于界面更新 - 控制Arduino的发送频率,避免短时间内发送大量数据导致串口缓存溢出
- 跨线程更新UI控件时,必须使用
Invoke方法确保线程安全
修正后的C#代码
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 Gma.System.MouseKeyHook; using System.Runtime.InteropServices; using System.Diagnostics; using System.IO.Ports; namespace MoveMouseAuto { public partial class Form1 : Form { private bool _isRunning = false; private Stopwatch _watch = Stopwatch.StartNew(); [DllImport("user32.dll", SetLastError = true)] public static extern bool SetCursorPos(int X, int Y); private IKeyboardMouseEvents _events; public Form1() { InitializeComponent(); // 绑定DataReceived事件 port.DataReceived += Port_DataReceived; } private void Port_DataReceived(object sender, SerialDataReceivedEventArgs e) { if (!_isRunning) return; // 控制读取频率,避免频繁处理 if (_watch.ElapsedMilliseconds < 200) return; try { string rawData = port.ReadLine().Trim(); if (int.TryParse(rawData, out int potValue)) { // 跨线程更新UI或控制鼠标 Invoke(new Action(() => { // 示例:更新标签显示数值 label1.Text = potValue.ToString(); // 移动鼠标 SetCursorPos(MousePosition.X + potValue, MousePosition.Y); })); } _watch.Restart(); } catch (Exception ex) { // 捕获异常,避免程序崩溃 Invoke(new Action(() => label1.Text = $"错误: {ex.Message}")); } } private void Subscribe(IKeyboardMouseEvents events) { _events = events; } private void Unsubscribe() { _events?.Dispose(); _events = null; } private void button1_Click(object sender, EventArgs e) { if (button1.Text == "ON") { button1.Text = "OFF"; _isRunning = true; _watch.Restart(); } else { button1.Text = "ON"; _isRunning = false; } } private void Form1_Load(object sender, EventArgs e) { try { port.Open(); } catch (Exception ex) { MessageBox.Show($"串口打开失败: {ex.Message}"); } Unsubscribe(); Subscribe(Hook.GlobalEvents()); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { Unsubscribe(); port.Close(); } protected override bool ProcessDialogKey(Keys keyData) { if (Form.ModifierKeys == Keys.None && keyData == Keys.Escape) { Close(); return true; } return base.ProcessDialogKey(keyData); } } }
修正后的Arduino代码
// 定义电位器引脚,根据实际接线修改 const int potPin = A0; int val; void setup() { Serial.begin(9600); Serial.setTimeout(10); } void loop() { val = analogRead(potPin) - 500; Serial.println(val); // 控制发送频率,每20ms发送一次,避免串口过载 delay(20); }
内容的提问来源于stack exchange,提问作者Joshua B.
相关产品推荐
相关产品推荐

