C# UDP与Arduino通信异常:程序冻结、接收无响应问题求助
问题解决:C#与Arduino UDP通信冻结及接收失效问题
问题根源
- 程序冻结:原代码同步执行UDP操作,无超时机制,无响应时阻塞UI线程;异步方法调用未正确使用
await,导致线程混乱 - 接收失效:接收消息时未切换到UI线程更新控件,UDP客户端实例复用不当;Arduino引脚模式设置错误,导致按钮状态判断逻辑异常
解决方案(持续监听+随时发送模式)
1. 修复C#端代码
以下代码实现异步收发,避免UI冻结,确保消息接收正常:
using System.Net; using System.Net.Sockets; using System.Text; namespace control_drone { public partial class Form1 : Form { private UdpClient? _udpListener; private readonly IPEndPoint _arduinoEndPoint = new IPEndPoint(IPAddress.Parse("192.168.1.126"), 1234); private CancellationTokenSource? _listenTokenSource; public Form1() { InitializeComponent(); StartUdpListening(); } // 后台异步启动UDP监听 private async void StartUdpListening() { _listenTokenSource = new CancellationTokenSource(); _udpListener = new UdpClient(1234); try { while (!_listenTokenSource.Token.IsCancellationRequested) { var receivedResult = await _udpListener.ReceiveAsync(_listenTokenSource.Token); string text = Encoding.ASCII.GetString(receivedResult.Buffer); // 切换到UI线程更新控件 this.Invoke(() => this.Text = text); } } catch (OperationCanceledException) { } catch (Exception ex) { MessageBox.Show($"监听出错: {ex.Message}"); } finally { _udpListener?.Close(); } } // 异步发送消息,带超时处理 public async Task SendMessageAsync(string textToSend) { using var sendingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); sendingSocket.SendTimeout = 200; // 设置200ms超时,避免无响应阻塞 byte[] sendBuffer = Encoding.ASCII.GetBytes(textToSend); try { await sendingSocket.SendToAsync(sendBuffer, SocketFlags.None, _arduinoEndPoint); } catch (SocketException ex) { MessageBox.Show($"发送失败: {ex.Message}"); } } // 窗体关闭时停止监听 private void Form1_FormClosing(object sender, FormClosingEventArgs e) { _listenTokenSource?.Cancel(); } // 按钮点击事件改为异步调用 private async void D1H_Click(object sender, EventArgs e) => await SendMessageAsync("Yellow hovering"); private async void D1RTH_Click(object sender, EventArgs e) => await SendMessageAsync("COMPUTERPR"); private async void D2H_Click(object sender, EventArgs e) => await SendMessageAsync("COMPUTERYH"); private async void D2RTH_Click(object sender, EventArgs e) => await SendMessageAsync("COMPUTERYR"); private async void D3H_Click(object sender, EventArgs e) => await SendMessageAsync("COMPUTERGH"); private async void D3RTH_Click(object sender, EventArgs e) => await SendMessageAsync("COMPUTERGR"); private async void D4H_Click(object sender, EventArgs e) => await SendMessageAsync("COMPUTERWH"); private async void D4RTH_Click(object sender, EventArgs e) => await SendMessageAsync("COMPUTERWR"); private void Green(object sender, PaintEventArgs e) => splitContainer1.Panel2.BackColor = Color.AliceBlue; private void split2_panel1_new_colour(object sender, PaintEventArgs e) => splitContainer2.Panel2.BackColor = Color.Blue; private void split2_panel2_new_colour(object sender, PaintEventArgs e) { } } }
2. 修正Arduino端代码
修复引脚模式错误,调整按钮状态判断逻辑:
#include "WiFi.h" #include "AsyncUDP.h" const char *ssid = "你的WiFi名称"; const char *password = "你的WiFi密码"; const int button1 = 3; const int button2 = 2; const int relay = 9; AsyncUDP udp; void setup() { // 修正引脚模式:按钮设为输入上拉,继电器设为输出 pinMode(button1, INPUT_PULLUP); pinMode(button2, INPUT_PULLUP); pinMode(relay, OUTPUT); Serial.begin(115200); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); if (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("WiFi Failed"); while (1) delay(1000); } if (udp.listen(1234)) { Serial.print("UDP Listening on IP: "); Serial.println(WiFi.localIP()); udp.onPacket([](AsyncUDPPacket packet) { Serial.print("Received: "); Serial.write(packet.data(), packet.length()); Serial.println(); int state1 = digitalRead(button1); int state2 = digitalRead(button2); String response; // INPUT_PULLUP模式下,按钮按下时引脚为LOW if (state1 == LOW && state2 == HIGH) { response = "first side is pressed"; } else if (state2 == LOW && state1 == HIGH) { response = "button number 2"; } else { response = "No button pressed"; } // 回复客户端 packet.print(response); }); } } void loop() { delay(1000); Serial.println("Running..."); }
关键修改说明
- C#端:异步执行UDP收发,设置发送超时避免冻结;用
Invoke确保UI更新在主线程;窗体关闭时释放监听资源 - Arduino端:修正按钮引脚模式,调整状态判断逻辑(适配INPUT_PULLUP特性);增加串口打印方便调试
内容的提问来源于stack exchange,提问作者FNQchemFly
相关产品推荐
相关产品推荐

