Arduino串口向C#传输DHT11数据出现累积,如何实现单次读取?
解决串口数据累积,每次仅读取一组DHT11数据的方案
这个问题我之前做串口项目的时候也碰到过!核心原因是串口本身是无边界的字节流,它不会帮你区分“这是一组数据”和“那是另一组”,所以当Arduino连续发了几组数据,而C#端没及时读取或者一次性读了缓冲区所有内容,就会把多组数据粘在一起。解决的关键就是给每组数据加一个「结束标记」,然后C#端盯着这个标记来拆分数据。
第一步:修改Arduino代码,添加数据结束标记
你需要在每次发送完一组温度湿度数据后,加上一个明确的结束符号(比如换行符\n,或者\r\n),这样C#端就能识别出每组数据的边界。
假设你原来的发送代码是类似这样的:
// 读取DHT11数据后发送 Serial.print(temp); Serial.print(";"); Serial.print(humidity);
把它改成下面这样,用Serial.println()自动添加换行符(或者手动加\n):
// 读取DHT11数据后发送 Serial.print(temp); Serial.print(";"); Serial.println(humidity); // println会自动在末尾追加\r\n作为结束标记
或者如果你想手动控制结束符,也可以这样写:
Serial.print(temp); Serial.print(";"); Serial.print(humidity); Serial.print('\n'); // 手动添加换行符作为结束标记
第二步:修改C#代码,按结束标记读取完整数据
根据你添加的结束标记,C#端有两种常用的处理方式,选一种适合你的就行:
方式1:使用ReadLine()直接读取整行数据
SerialPort.ReadLine()方法会自动读取直到遇到换行符(默认是\n,可以通过NewLine属性修改),每次调用就能得到一组完整的数据。注意要设置合适的ReadTimeout避免无限阻塞。
示例代码:
private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { try { // 读取完整的一行数据(直到换行符) string rawData = serialPort.ReadLine().Trim(); // 去掉多余的\r或空格 if (!string.IsNullOrEmpty(rawData)) { // 分割温度和湿度 string[] dataParts = rawData.Split(';'); if (dataParts.Length == 2) { string temperature = dataParts[0]; string humidity = dataParts[1]; // 这里处理数据,比如显示到UI或者保存 Console.WriteLine($"当前温度:{temperature}℃,湿度:{humidity}%"); } } } catch (TimeoutException ex) { // 处理读取超时的情况,比如打日志 Console.WriteLine($"串口读取超时:{ex.Message}"); } }
方式2:手动处理缓冲区,兼容不完整数据
如果你的场景下不能用阻塞的ReadLine(),可以自己处理串口缓冲区,把上次未处理完的半组数据保存下来,下次读取时拼接后再拆分。这种方式更灵活,适合高并发或者需要实时处理的场景:
// 保存上次未处理完的半组数据 private string _unprocessedData = string.Empty; private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { // 读取缓冲区所有新数据 string newData = serialPort.ReadExisting(); // 拼接上次剩下的不完整数据 string allData = _unprocessedData + newData; // 按换行符拆分所有数据 string[] completeLines = allData.Split(new[] {'\n'}, StringSplitOptions.RemoveEmptyEntries); // 判断最后一行是否是完整数据(如果原数据不以换行结尾,说明最后一行是半组) if (!allData.EndsWith("\n")) { // 把最后一行未完成的数据保存下来,下次用 _unprocessedData = completeLines.Last(); // 移除最后一行,只处理完整的组 Array.Resize(ref completeLines, completeLines.Length - 1); } else { _unprocessedData = string.Empty; } // 遍历处理每一组完整数据 foreach (string line in completeLines) { string cleanLine = line.Trim('\r'); // 去掉可能的回车符 string[] dataParts = cleanLine.Split(';'); if (dataParts.Length == 2) { string temperature = dataParts[0]; string humidity = dataParts[1]; Console.WriteLine($"当前温度:{temperature}℃,湿度:{humidity}%"); } } }
额外注意事项
- 确保Arduino和C#的串口参数完全一致:波特率、数据位、停止位、奇偶校验必须一模一样(比如都是9600波特率,8数据位,1停止位,无校验)。
- 如果还是有问题,可以在Arduino端添加一点延迟(比如
delay(2000))确保每次发送的间隔足够,不过只要加了结束标记,这个不是必须的。
内容的提问来源于stack exchange,提问作者Andres Salazar Lopez
相关产品推荐
相关产品推荐

