串口通信故障主因是Arduino与C#端参数不一致,需统一波特率、数据位、停止位和校验位;DataReceived事件需缓存再解析,避免阻塞;注意DTR复位问题及发送后延时;协议宜简不宜繁。

串口通信必须匹配波特率、数据位、停止位等基础参数
Arduino 和 C# 端的串口配置不一致,是 90% 以上“收不到数据”“乱码”“卡死”的根源。不是程序写错了,而是两边“说的不是同一种方言”。
-
Serial.begin(9600)在 Arduino 中设了波特率,C# 的SerialPort构造函数里也必须用new SerialPort("COM3", 9600)—— 不能一个写 9600,一个写 115200 - 默认情况下,Arduino 使用 8 数据位、1 停止位、无校验(
Serial.begin(9600)隐含此配置),C# 也得保持一致:serialPort.DataBits = 8、serialPort.StopBits = StopBits.One、serialPort.Parity = Parity.None - Windows 上某些 USB 转串口芯片(尤其 CH340 克隆版)在高波特率(如 115200)下不稳定,首次调试建议统一用
9600,验证通路后再提速
DataReceived 事件不是“每字节触发一次”,别在里面直接 ReadLine()
这是新手最常掉进的坑:以为 DataReceived 一触发,串口缓冲区里就一定有一整行数据。实际上它只表示“有新字节来了”,而 ReadLine() 会阻塞等待 \n,如果没等到,线程就卡住,后续事件全丢。
- 正确做法是把接收到的字节先缓存到
StringBuilder或byte[],再按换行符或自定义分隔符(比如"\r\n")切分完整消息 - 务必在事件处理中加
try/catch,避免因读取异常导致整个事件订阅失效 - 不要在
DataReceived里做耗时操作(如 UI 更新、文件写入),应通过Invoke或BeginInvoke切回主线程
Arduino 收不到 C# 发的数据?先检查 DtrEnable 和自动复位
很多 UNO 板子(尤其国产 CH340)插上电脑后,C# 打开串口瞬间会触发 Arduino 自动复位,导致刚上传的程序重启,错过前几帧数据。这不是代码 bug,是硬件握手机制问题。
- 在 C# 打开串口后、发送数据前,加一句:
serialPort.DtrEnable = true(或false,视板子而定),可抑制复位;部分板子还需配合serialPort.RtsEnable = true - 更稳妥的做法:Arduino 端在
setup()开头加几秒延时(delay(2000)),或等收到第一个有效指令(如"READY")再进入主逻辑 - 如果 C# 写完就关串口,Arduino 可能根本来不及响应——发完后至少
Thread.Sleep(10)再关,或监听返回确认
字符串协议越简单越可靠,别一上来就传 JSON 或浮点数组
你看到别人用 C# 发 "1.5,3.7,2" 给 Arduino,自己照抄却得到 ".7,2????,大概率是缓冲区未清空、接收逻辑没处理粘包、或 Arduino 用 Serial.read() 逐字节读但没判断长度边界。
- 练手阶段,坚持单字符协议:
'o'开灯、'f'关灯、's'查询状态。Arduino 用if (Serial.available()) { char c = Serial.read(); ... }就够用 - 真要传结构化数据,用固定长度 + 分隔符,比如
"HUM:45.2;TEMP:23.8;\n",Arduino 用String.indexOf()和substring()解析,比手动拼 byte 数组安全得多 - C# 发送时用
WriteLine()而非Write(),确保结尾带\n,否则 Arduino 的ReadLine()永远等不到结束符
真正卡住人的,往往不是语法不会,而是串口那根线背后藏着的时序、电平、驱动、缓冲区这些看不见的环节。多看一眼设备管理器里的 COM 号有没有跳变,多抓一次串口监视器对比收发内容,比重写十遍逻辑都管用。


















