You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:19:19