news 2026/4/23 16:13:34

基于qserialport的温度监控系统:完整示例实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于qserialport的温度监控系统:完整示例实现

用QSerialPort打造一个真正能跑的温度监控系统:从串口读数到动态图表全实战

你有没有过这样的经历?手头有个DS18B20或DHT22,接在Arduino上,数据明明已经在串口助手里“啪啪”地跳着出来,但就是不知道怎么把它变成一个像模像样、能实时画曲线、带报警、还能记住历史的桌面程序?

别再用记事本复制粘贴温度值了。今天我们就来干一票大的——用Qt + QSerialPort,从零开始构建一个工业级可用的温度监控系统。不是玩具Demo,是那种你可以拿去给老板演示、写进简历项目的完整工程。


别再被“串口卡顿”折磨了:为什么你的上位机总是不流畅?

先说个真相:很多人做的“温度监控”,其实就是一个串口助手加个LCD控件,点一下刷新一次。界面卡顿、数据丢失、偶尔崩溃……根本没法连续运行超过十分钟。

问题出在哪?同步阻塞读取 + 主线程搞通信

而我们今天的方案,核心就一句话:

让QSerialPort在后台默默监听,数据来了自动通知UI,GUI永远丝滑如初。

这背后靠的就是 Qt 的两大法宝:信号与槽(Signals & Slots)事件驱动机制(Event-Driven Architecture)


QSerialPort 不是“读写文件”,它是你和硬件之间的“对讲机”

别看QSerialPort继承自QIODevice,就能用read()write(),但它的工作方式和读文件完全不同。

想象一下,你在工地现场,工人(MCU)每隔几秒通过 walkie-talkie 报告一次温度:“报告!当前温度25.3度!”
你不需要一直举着对讲机听——那样太累。你应该做的是:把对讲机调成响铃模式,等它“嘀”一声,再拿起来说:“哦,知道了,更新面板。”

这就是readyRead()信号的本质。

connect(serial, &QSerialPort::readyRead, this, &TemperatureMonitor::readSerialData);

只要串口缓冲区有新数据,这个信号就会触发,你的readSerialData()函数就会被执行——完全异步,不占用主线程,GUI不卡顿


硬核配置:如何打开一个“稳定不死”的串口?

很多初学者一上来就serial->open(QIODevice::ReadOnly),结果弹出“Permission error”或者“Resource busy”。为什么?

因为你没做好端口配置。下面这段代码,是你必须掌握的“串口初始化模板”:

void TemperatureMonitor::openSerialPort() { serial->setPortName(ui->portComboBox->currentText()); serial->setBaudRate(115200); // 高速传输,减少延迟 serial->setDataBits(QSerialPort::Data8); // 8个数据位 serial->setParity(QSerialPort::NoParity); // 无校验 serial->setStopBits(QSerialPort::OneStop); // 1个停止位 serial->setFlowControl(QSerialPort::NoFlowControl); // 无流控(除非硬件支持) if (serial->open(QIODevice::ReadOnly)) { ui->statusLabel->setText("✅ 已连接"); ui->openButton->setEnabled(false); } else { ui->statusLabel->setText("❌ 打开失败: " + serial->errorString()); } }

经验之谈:波特率优先选115200。9600 太慢,尤其当你想每秒采样多次时,数据会堆积、延迟飙升。


数据来了,但你怎么知道它是一帧完整的报文?

这是最坑新手的地方:你用readAll()一口气读了100字节,结果只收到了半包数据,比如:

TEMP:24.

下一次才收到:

7\r\n

这就是典型的粘包/断包问题

怎么办?两种思路:

方案一:文本协议 + 分隔符(推荐新手)

假设下位机发送格式为:

TEMP:25.3\r\n

我们就可以按\r\n拆分,逐行解析:

void TemperatureMonitor::readSerialData() { static QByteArray buffer; // 缓存未完整接收的数据 buffer += serial->readAll(); while (buffer.contains("\r\n")) { int index = buffer.indexOf("\r\n"); QByteArray line = buffer.left(index); buffer = buffer.mid(index + 2); parseTemperatureLine(line); } // 防止 buffer 无限增长 if (buffer.length() > 1024) { buffer.clear(); } } void TemperatureMonitor::parseTemperatureLine(const QByteArray &line) { QString str = QString::fromUtf8(line).trimmed(); if (str.startsWith("TEMP:")) { bool ok; double temp = str.mid(5).toDouble(&ok); if (ok) { updateTemperatureDisplay(temp); } } }

看到没?加了个static buffer,专门用来拼接不完整的帧。这才是工业级做法。


UI更新:不只是显示数字,还要“动起来”

光在LCD上显示个数字?太静态了。我们要的是趋势图

虽然 Qt 官方Qt Charts模块有点重,但对付温度监控绰绰有余。你只需要几行代码,就能画出实时曲线:

// 假设你有一个 QChartView *chartView 成员 QLineSeries *series = new QLineSeries(); QChart *chart = new QChart(); chart->addSeries(series); chart->createDefaultAxes(); chart->axisX()->setRange(0, 50); // 显示最近50个点 chart->axisY()->setRange(0, 100); // 温度范围0~100℃ chartView->setChart(chart); // 在收到温度时添加点 void TemperatureMonitor::updateTemperatureDisplay(double temp) { static int x = 0; series->append(x++, temp); // 只保留最近50个点 if (series->count() > 50) { QLineSeries *toRemove = series->points().first(); series->remove(toRemove); } ui->lcdNumber->display(temp); }

效果如下:

[📈 实时曲线] ↗️↗️↘️↗️... [🌡️ 当前温度] 25.3°C

是不是瞬间专业感拉满?


下位机怎么配?给你一份Arduino参考代码

别忘了,上位机做得再好,下位机不配合也白搭。下面是 Arduino 发送温度的标准范式:

#include <OneWire.h> #include <DallasTemperature.h> #define ONE_WIRE_BUS 2 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); void setup() { Serial.begin(115200); sensors.begin(); } void loop() { sensors.requestTemperatures(); float temperatureC = sensors.getTempCByIndex(0); if (temperatureC != DEVICE_DISCONNECTED_C) { Serial.print("TEMP:"); Serial.print(temperatureC, 1); // 保留1位小数 Serial.println("°C"); } else { Serial.println("TEMP:ERR"); } delay(1000); // 每秒上报一次 }

⚠️ 注意:Serial.println()自动加\r\n,正好匹配我们的帧解析逻辑。


那些你迟早会踩的坑,现在就避开

❌ 坑1:频繁调用readAll()导致CPU飙到100%

原因:你在QTimer里轮询读串口,比如每10ms读一次。

正确做法:删掉定时器!只依赖readyRead()信号。有数据才处理,没有就歇着。

❌ 坑2:串口打不开,提示“权限不足”

Linux/macOS 用户注意:USB转TTL设备通常属于dialout组。解决方法:

sudo usermod -aG dialout $USER

重启生效。

❌ 坑3:程序退出后串口“被占用”

忘记close()会导致下次无法打开。务必在析构函数中关闭:

TemperatureMonitor::~TemperatureMonitor() { if (serial->isOpen()) { serial->close(); } delete ui; }

进阶玩法:让它更像一个“工业产品”

你现在有了基础版本,接下来可以轻松扩展这些功能:

🔔 超温报警

if (temp > 80.0) { QApplication::beep(); // 蜂鸣器 ui->alarmLabel->setText("⚠️ 高温警告!"); }

💾 记录日志到文件

QFile log("temp_log.txt"); if (log.open(QIODevice::Append)) { QTextStream out(&log); out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") << " - " << temp << "°C" << "\n"; log.close(); }

🧩 多通道支持

修改协议为:

CH1:25.3,CH2:26.1\r\n

然后拆解字段即可。

🛠️ 配置记忆

QSettings保存上次用的串口和波特率:

QSettings settings("MyCompany", "TempMonitor"); settings.setValue("port", ui->portComboBox->currentText()); // 下次启动时读取 ui->portComboBox->setCurrentText(settings.value("port").toString());

写在最后:这不仅仅是个温度监控

你今天学会的,是一套通用的嵌入式上位机开发范式

  • 串口通信QSerialPort + readyRead
  • 协议解析→ 缓冲 + 分帧 + 校验
  • UI更新→ 信号驱动,非阻塞刷新
  • 健壮性→ 异常捕获、资源释放、配置持久化

这套模式,换一个传感器(湿度、压力、电压),换一个协议(Modbus、自定义二进制),就能复用90%的代码。

QSerialPort 是你通往工业控制世界的第一把钥匙。它不炫酷,但足够可靠;它不复杂,但足以支撑起真正的工程项目。

如果你正在找工作,把这个项目放进简历,面试官问:“你能独立开发上位机吗?”
你可以直接打开电脑,说:“我做过一个温度监控系统,要看看吗?”


📌关键词:qserialport、串口通信、温度监控、Qt、实时数据采集、信号槽、事件驱动、跨平台、arduino、传感器、工业测控、数据解析、gui、嵌入式、mcu

如果你实现了这个系统,欢迎在评论区晒图交流!下一个功能你想加什么?数据库?远程报警?MQTT上传?我们一起搞。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 13:19:47

如何快速掌握游戏存档编辑:uesave工具完整操作指南

如何快速掌握游戏存档编辑&#xff1a;uesave工具完整操作指南 【免费下载链接】uesave-rs 项目地址: https://gitcode.com/gh_mirrors/ue/uesave-rs 想要完全掌控你的游戏进度吗&#xff1f;是否曾经遇到过存档损坏无法恢复的困境&#xff1f;或者想要调整游戏参数却无…

作者头像 李华
网站建设 2026/4/23 13:17:08

Keil5安装教程常见问题解决:51单片机适配要点

Keil5安装与51单片机开发&#xff1a;从零配置到实战避坑全指南 你是不是也遇到过这样的情况&#xff1f; 刚装好Keil5&#xff0c;信心满满地新建一个项目&#xff0c;想写个简单的LED闪烁程序&#xff0c;结果一编译就报错&#xff1a;“ sfr undefined ”或者“ file r…

作者头像 李华
网站建设 2026/4/23 13:18:26

Windows下Miniconda-Python3.11镜像安装PyTorch GPU版本详细步骤

Windows下Miniconda-Python3.11镜像安装PyTorch GPU版本详细步骤 在深度学习项目中&#xff0c;环境配置常常是第一步也是最令人头疼的一步。明明代码写得没问题&#xff0c;却因为torch.cuda.is_available()返回False而卡住&#xff1b;或者刚装好PyTorch&#xff0c;一跑模型…

作者头像 李华
网站建设 2026/4/23 14:49:36

百度网盘免登录高速下载终极方案:3步实现全速下载

百度网盘免登录高速下载终极方案&#xff1a;3步实现全速下载 【免费下载链接】baiduwp-php A tool to get the download link of the Baidu netdisk / 一个获取百度网盘分享链接下载地址的工具 项目地址: https://gitcode.com/gh_mirrors/ba/baiduwp-php 还在为百度网盘…

作者头像 李华
网站建设 2026/4/18 21:18:54

Marlin固件配置实战:从零到精通的避坑指南

你是否曾在配置Marlin固件时陷入无尽的编译错误&#xff1f;面对数百个配置项&#xff0c;是否感到无从下手&#xff1f;作为一名资深3D打印技术专家&#xff0c;我将带你用系统化思维解决配置难题&#xff0c;让你的3D打印机性能发挥到极致。 【免费下载链接】Marlin Marlin 是…

作者头像 李华
网站建设 2026/4/23 13:19:30

html2sketch:网页转设计文件的完整解决方案

html2sketch&#xff1a;网页转设计文件的完整解决方案 【免费下载链接】html2sketch parser HTML to Sketch JSON 项目地址: https://gitcode.com/gh_mirrors/ht/html2sketch 项目简介 html2sketch 是一个强大的工具模块&#xff0c;专门用于将 HTML 元素转换为符合 S…

作者头像 李华