QT串口温湿度控制系统
基于QT的串口温湿度控制系统,包含实时数据显示、历史曲线、阈值报警和设备控制功能。
系统架构
+-----------------------+
| 用户界面层 |
| - 主控制窗口 |
| - 数据可视化 |
| - 参数设置 |
+-----------------------+
|
v
+-----------------------+
| 业务逻辑层 |
| - 串口通信管理 |
| - 数据处理与分析 |
| - 报警管理 |
| - 设备控制逻辑 |
+-----------------------+
|
v
+-----------------------+
| 硬件抽象层 |
| - 串口通信接口 |
| - 传感器数据解析 |
| - 控制命令生成 |
+-----------------------+
完整代码实现
主窗口类 (mainwindow.h)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QChartView>
#include <QLineSeries>
#include <QValueAxis>
#include <QTimer>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QSystemTrayIcon>
#include <QMenu>
#include <QAction>
#include <QSystemTrayIcon>
#include <QCloseEvent>
QT_CHARTS_USE_NAMESPACE
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
protected:
void closeEvent(QCloseEvent *event) override;
private slots:
void on_btnConnect_clicked();
void on_btnDisconnect_clicked();
void on_btnRefreshPorts_clicked();
void on_btnSend_clicked();
void on_btnClearLog_clicked();
void on_btnStartLogging_clicked();
void on_btnStopLogging_clicked();
void on_btnSetThresholds_clicked();
void on_chkAutoControl_toggled(bool checked);
void on_chkSoundAlarm_toggled(bool checked);
void on_chkEmailAlert_toggled(bool checked);
void readSerialData();
void handleSerialError(QSerialPort::SerialPortError error);
void updateConnectionStatus();
void updateDataDisplay();
void checkThresholds();
void controlDevice(const QString &device, bool state);
void logDataToFile();
void updateCharts();
void showAboutDialog();
void toggleWindowVisibility();
void showNotification(const QString &title, const QString &message);
private:
Ui::MainWindow *ui;
// 串口相关
QSerialPort *serialPort;
QTimer *dataTimer;
QTimer *chartUpdateTimer;
QTimer *loggingTimer;
// 数据存储
QList<QPointF> tempData;
QList<QPointF> humiData;
QList<QPointF> timePoints;
int timeCounter;
// 系统状态
bool isConnected;
bool isLogging;
bool autoControlEnabled;
bool soundAlarmEnabled;
bool emailAlertEnabled;
// 配置参数
double tempThresholdHigh;
double tempThresholdLow;
double humiThresholdHigh;
double humiThresholdLow;
QString logFilePath;
// 图表
QChart *tempChart;
QChart *humiChart;
QLineSeries *tempSeries;
QLineSeries *humiSeries;
QValueAxis *axisX;
QValueAxis *axisYTemp;
QValueAxis *axisYHumi;
// 系统托盘
QSystemTrayIcon *trayIcon;
QMenu *trayMenu;
// 方法
void initUI();
void initCharts();
void initSerialPort();
void initSystemTray();
void loadSettings();
void saveSettings();
void updateDeviceStatus();
void sendCommand(const QString &cmd);
void parseData(const QString &data);
void addLogEntry(const QString &entry);
void showAlarm(const QString &message);
void sendEmailAlert(const QString &message);
};
#endif // MAINWINDOW_H
主窗口实现 (mainwindow.cpp)
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QSerialPortInfo>
#include <QDebug>
#include <QDateTime>
#include <QSettings>
#include <QDesktopServices>
#include <QUrl>
#include <QStyle>
#include <QIcon>
#include <QSystemTrayIcon>
#include <QMenu>
#include <QAction>
#include <QCloseEvent>
#include <QMessageBox>
#include <QFileDialog>
#include <QStandardPaths>
#include <QSoundEffect>
#include <QProcess>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
serialPort(nullptr),
isConnected(false),
isLogging(false),
autoControlEnabled(false),
soundAlarmEnabled(true),
emailAlertEnabled(false),
timeCounter(0),
tempThresholdHigh(30.0),
tempThresholdLow(10.0),
humiThresholdHigh(70.0),
humiThresholdLow(30.0)
{
ui->setupUi(this);
// 初始化UI
initUI();
// 初始化图表
initCharts();
// 初始化串口
initSerialPort();
// 初始化系统托盘
initSystemTray();
// 加载设置
loadSettings();
// 设置定时器
dataTimer = new QTimer(this);
connect(dataTimer, &QTimer::timeout, this, &MainWindow::updateDataDisplay);
dataTimer->start(1000); // 每秒更新一次显示
chartUpdateTimer = new QTimer(this);
connect(chartUpdateTimer, &QTimer::timeout, this, &MainWindow::updateCharts);
chartUpdateTimer->start(5000); // 每5秒更新一次图表
loggingTimer = new QTimer(this);
connect(loggingTimer, &QTimer::timeout, this, &MainWindow::logDataToFile);
// 设置初始状态
ui->btnDisconnect->setEnabled(false);
ui->btnSend->setEnabled(false);
ui->actionConnect->setEnabled(true);
ui->actionDisconnect->setEnabled(false);
// 设置窗口标题
setWindowTitle("温湿度监控系统 v1.0");
// 设置样式
qApp->setStyle("Fusion");
QPalette palette;
palette.setColor(QPalette::Window, QColor(53,53,53));
palette.setColor(QPalette::WindowText, Qt::white);
palette.setColor(QPalette::Base, QColor(25,25,25));
palette.setColor(QPalette::AlternateBase, QColor(53,53,53));
palette.setColor(QPalette::ToolTipBase, Qt::white);
palette.setColor(QPalette::ToolTipText, Qt::white);
palette.setColor(QPalette::Text, Qt::white);
palette.setColor(QPalette::Button, QColor(53,53,53));
palette.setColor(QPalette::ButtonText, Qt::white);
palette.setColor(QPalette::BrightText, Qt::red);
palette.setColor(QPalette::Highlight, QColor(142,45,197).lighter());
palette.setColor(QPalette::HighlightedText, Qt::black);
qApp->setPalette(palette);
}
MainWindow::~MainWindow()
{
if (serialPort && serialPort->isOpen()) {
serialPort->close();
}
saveSettings();
delete ui;
}
void MainWindow::initUI()
{
// 设置表格列宽
ui->tableData->setColumnWidth(0, 120);
ui->tableData->setColumnWidth(1, 100);
ui->tableData->setColumnWidth(2, 100);
ui->tableData->setColumnWidth(3, 100);
// 设置状态栏
ui->statusBar->addPermanentWidget(ui->lblStatus);
ui->lblStatus->setText("未连接");
// 设置按钮图标
ui->btnConnect->setIcon(style()->standardIcon(QStyle::SP_DialogOkButton));
ui->btnDisconnect->setIcon(style()->standardIcon(QStyle::SP_DialogCancelButton));
ui->btnRefreshPorts->setIcon(style()->standardIcon(QStyle::SP_BrowserReload));
ui->btnSend->setIcon(style()->standardIcon(QStyle::SP_ArrowRight));
ui->btnClearLog->setIcon(style()->standardIcon(QStyle::SP_DialogResetButton));
ui->btnStartLogging->setIcon(style()->standardIcon(QStyle::SP_DriveFDIcon));
ui->btnStopLogging->setIcon(style()->standardIcon(QStyle::SP_DialogCancelButton));
// 设置复选框状态
ui->chkAutoControl->setChecked(autoControlEnabled);
ui->chkSoundAlarm->setChecked(soundAlarmEnabled);
ui->chkEmailAlert->setChecked(emailAlertEnabled);
// 设置阈值输入框
ui->spinTempHigh->setValue(tempThresholdHigh);
ui->spinTempLow->setValue(tempThresholdLow);
ui->spinHumiHigh->setValue(humiThresholdHigh);
ui->spinHumiLow->setValue(humiThresholdLow);
// 初始化表格
QStringList headers;
headers << "时间" << "温度(℃)" << "湿度(%)" << "状态";
ui->tableData->setHorizontalHeaderLabels(headers);
ui->tableData->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// 初始化日志文本框
ui->txtLog->setReadOnly(true);
ui->txtLog->setStyleSheet("background-color: #1e1e1e; color: #dcdcdc; font-family: Consolas;");
// 添加菜单动作
QAction *aboutAction = new QAction("关于", this);
connect(aboutAction, &QAction::triggered, this, &MainWindow::showAboutDialog);
ui->menuHelp->addAction(aboutAction);
}
void MainWindow::initCharts()
{
// 创建温度图表
tempSeries = new QLineSeries();
tempSeries->setName("温度(℃)");
tempSeries->setColor(Qt::red);
tempChart = new QChart();
tempChart->addSeries(tempSeries);
tempChart->setTitle("温度变化曲线");
tempChart->legend()->hide();
tempChart->setTheme(QChart::ChartThemeDark);
axisX = new QValueAxis;
axisX->setRange(0, 60); // 显示最近60个点
axisX->setLabelFormat("%d");
axisX->setTitleText("时间(分钟)");
axisYTemp = new QValueAxis;
axisYTemp->setRange(0, 50);
axisYTemp->setLabelFormat("%.1f");
axisYTemp->setTitleText("温度(℃)");
tempChart->addAxis(axisX, Qt::AlignBottom);
tempChart->addAxis(axisYTemp, Qt::AlignLeft);
tempSeries->attachAxis(axisX);
tempSeries->attachAxis(axisYTemp);
ui->chartTemp->setChart(tempChart);
ui->chartTemp->setRenderHint(QPainter::Antialiasing);
// 创建湿度图表
humiSeries = new QLineSeries();
humiSeries->setName("湿度(%)");
humiSeries->setColor(Qt::blue);
humiChart = new QChart();
humiChart->addSeries(humiSeries);
humiChart->setTitle("湿度变化曲线");
humiChart->legend()->hide();
humiChart->setTheme(QChart::ChartThemeDark);
axisYHumi = new QValueAxis;
axisYHumi->setRange(0, 100);
axisYHumi->setLabelFormat("%.1f");
axisYHumi->setTitleText("湿度(%)");
humiChart->addAxis(axisX, Qt::AlignBottom);
humiChart->addAxis(axisYHumi, Qt::AlignLeft);
humiSeries->attachAxis(axisX);
humiSeries->attachAxis(axisYHumi);
ui->chartHumi->setChart(humiChart);
ui->chartHumi->setRenderHint(QPainter::Antialiasing);
}
void MainWindow::initSerialPort()
{
serialPort = new QSerialPort(this);
connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::readSerialData);
connect(serialPort, &QSerialPort::errorOccurred, this, &MainWindow::handleSerialError);
// 刷新串口列表
on_btnRefreshPorts_clicked();
}
void MainWindow::initSystemTray()
{
trayIcon = new QSystemTrayIcon(this);
trayIcon->setIcon(QIcon(":/icons/temperature.png"));
trayIcon->setToolTip("温湿度监控系统");
trayMenu = new QMenu(this);
QAction *showAction = trayMenu->addAction("显示窗口");
connect(showAction, &QAction::triggered, this, &MainWindow::toggleWindowVisibility);
QAction *connectAction = trayMenu->addAction("连接");
connect(connectAction, &QAction::triggered, this, &MainWindow::on_btnConnect_clicked);
QAction *disconnectAction = trayMenu->addAction("断开连接");
connect(disconnectAction, &QAction::triggered, this, &MainWindow::on_btnDisconnect_clicked);
trayMenu->addSeparator();
QAction *exitAction = trayMenu->addAction("退出");
connect(exitAction, &QAction::triggered, qApp, &QApplication::quit);
trayIcon->setContextMenu(trayMenu);
trayIcon->show();
connect(trayIcon, &QSystemTrayIcon::activated, this, [this](QSystemTrayIcon::ActivationReason reason) {
if (reason == QSystemTrayIcon::DoubleClick) {
toggleWindowVisibility();
}
});
}
void MainWindow::loadSettings()
{
QSettings settings("ThermoMonitor", "SerialMonitor");
// 串口设置
ui->cmbPort->setCurrentText(settings.value("port", "").toString());
ui->cmbBaud->setCurrentText(settings.value("baud", "9600").toString());
ui->cmbDataBits->setCurrentText(settings.value("databits", "8").toString());
ui->cmbParity->setCurrentText(settings.value("parity", "无").toString());
ui->cmbStopBits->setCurrentText(settings.value("stopbits", "1").toString());
// 阈值设置
tempThresholdHigh = settings.value("tempHigh", 30.0).toDouble();
tempThresholdLow = settings.value("tempLow", 10.0).toDouble();
humiThresholdHigh = settings.value("humiHigh", 70.0).toDouble();
humiThresholdLow = settings.value("humiLow", 30.0).toDouble();
// 其他选项
autoControlEnabled = settings.value("autoControl", false).toBool();
soundAlarmEnabled = settings.value("soundAlarm", true).toBool();
emailAlertEnabled = settings.value("emailAlert", false).toBool();
logFilePath = settings.value("logFilePath", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/thermo_log.csv").toString();
ui->spinTempHigh->setValue(tempThresholdHigh);
ui->spinTempLow->setValue(tempThresholdLow);
ui->spinHumiHigh->setValue(humiThresholdHigh);
ui->spinHumiLow->setValue(humiThresholdLow);
}
void MainWindow::saveSettings()
{
QSettings settings("ThermoMonitor", "SerialMonitor");
// 串口设置
settings.setValue("port", ui->cmbPort->currentText());
settings.setValue("baud", ui->cmbBaud->currentText());
settings.setValue("databits", ui->cmbDataBits->currentText());
settings.setValue("parity", ui->cmbParity->currentText());
settings.setValue("stopbits", ui->cmbStopBits->currentText());
// 阈值设置
settings.setValue("tempHigh", tempThresholdHigh);
settings.setValue("tempLow", tempThresholdLow);
settings.setValue("humiHigh", humiThresholdHigh);
settings.setValue("humiLow", humiThresholdLow);
// 其他选项
settings.setValue("autoControl", autoControlEnabled);
settings.setValue("soundAlarm", soundAlarmEnabled);
settings.setValue("emailAlert", emailAlertEnabled);
settings.setValue("logFilePath", logFilePath);
}
void MainWindow::on_btnConnect_clicked()
{
if (ui->cmbPort->currentText().isEmpty()) {
QMessageBox::warning(this, "连接错误", "请选择串口");
return;
}
// 配置串口
serialPort->setPortName(ui->cmbPort->currentText());
serialPort->setBaudRate(ui->cmbBaud->currentText().toInt());
// 设置数据位
if (ui->cmbDataBits->currentText() == "5") serialPort->setDataBits(QSerialPort::Data5);
else if (ui->cmbDataBits->currentText() == "6") serialPort->setDataBits(QSerialPort::Data6);
else if (ui->cmbDataBits->currentText() == "7") serialPort->setDataBits(QSerialPort::Data7);
else serialPort->setDataBits(QSerialPort::Data8);
// 设置校验位
if (ui->cmbParity->currentText() == "偶校验") serialPort->setParity(QSerialPort::EvenParity);
else if (ui->cmbParity->currentText() == "奇校验") serialPort->setParity(QSerialPort::OddParity);
else if (ui->cmbParity->currentText() == "空格校验") serialPort->setParity(QSerialPort::SpaceParity);
else serialPort->setParity(QSerialPort::NoParity);
// 设置停止位
if (ui->cmbStopBits->currentText() == "1.5") serialPort->setStopBits(QSerialPort::OneAndHalfStop);
else if (ui->cmbStopBits->currentText() == "2") serialPort->setStopBits(QSerialPort::TwoStop);
else serialPort->setStopBits(QSerialPort::OneStop);
// 打开串口
if (serialPort->open(QIODevice::ReadWrite)) {
isConnected = true;
ui->btnConnect->setEnabled(false);
ui->btnDisconnect->setEnabled(true);
ui->btnSend->setEnabled(true);
ui->actionConnect->setEnabled(false);
ui->actionDisconnect->setEnabled(true);
ui->lblStatus->setText("已连接: " + ui->cmbPort->currentText());
ui->txtLog->append(QDateTime::currentDateTime().toString("[hh:mm:ss] ") + "串口连接成功");
// 发送初始化命令
sendCommand("INIT");
} else {
QMessageBox::critical(this, "连接错误", "无法打开串口: " + serialPort->errorString());
ui->txtLog->append(QDateTime::currentDateTime().toString("[hh:mm:ss] ") + "串口连接失败: " + serialPort->errorString());
}
}
void MainWindow::on_btnDisconnect_clicked()
{
if (serialPort && serialPort->isOpen()) {
serialPort->close();
isConnected = false;
ui->btnConnect->setEnabled(true);
ui->btnDisconnect->setEnabled(false);
ui->btnSend->setEnabled(false);
ui->actionConnect->setEnabled(true);
ui->actionDisconnect->setEnabled(false);
ui->lblStatus->setText("未连接");
ui->txtLog->append(QDateTime::currentDateTime().toString("[hh:mm:ss] ") + "串口已断开");
}
}
void MainWindow::on_btnRefreshPorts_clicked()
{
ui->cmbPort->clear();
const auto ports = QSerialPortInfo::availablePorts();
for (const QSerialPortInfo &port : ports) {
ui->cmbPort->addItem(port.portName());
}
}
void MainWindow::on_btnSend_clicked()
{
if (!isConnected || !serialPort->isOpen()) {
QMessageBox::warning(this, "发送错误", "串口未连接");
return;
}
QString command = ui->txtCommand->text().trimmed();
if (command.isEmpty()) {
QMessageBox::warning(this, "发送错误", "命令不能为空");
return;
}
sendCommand(command);
ui->txtCommand->clear();
}
void MainWindow::sendCommand(const QString &cmd)
{
if (serialPort && serialPort->isOpen()) {
serialPort->write((cmd + "\r\n").toUtf8());
ui->txtLog->append(QDateTime::currentDateTime().toString("[hh:mm:ss] ") + "发送: " + cmd);
}
}
void MainWindow::readSerialData()
{
if (!serialPort) return;
QByteArray data = serialPort->readAll();
while (serialPort->waitForReadyRead(10)) {
data += serialPort->readAll();
}
QString receivedData = QString::fromUtf8(data).trimmed();
if (receivedData.isEmpty()) return;
ui->txtLog->append(QDateTime::currentDateTime().toString("[hh:mm:ss] ") + "接收: " + receivedData);
// 解析数据
parseData(receivedData);
}
void MainWindow::parseData(const QString &data)
{
// 示例数据格式: "TEMP:25.5,HUMI:60.0,STATUS:NORMAL"
QStringList parts = data.split(',');
double temp = 0.0;
double humi = 0.0;
QString status = "未知";
for (const QString &part : parts) {
if (part.startsWith("TEMP:")) {
temp = part.mid(5).toDouble();
} else if (part.startsWith("HUMI:")) {
humi = part.mid(5).toDouble();
} else if (part.startsWith("STATUS:")) {
status = part.mid(7);
}
}
// 更新UI显示
ui->lcdTemp->display(QString::number(temp, 'f', 1));
ui->lcdHumi->display(QString::number(humi, 'f', 1));
ui->lblStatusText->setText(status);
// 更新状态指示灯
if (status == "NORMAL") {
ui->lblTempStatus->setPixmap(QPixmap(":/icons/green_led.png"));
ui->lblHumiStatus->setPixmap(QPixmap(":/icons/green_led.png"));
} else if (status == "WARNING") {
ui->lblTempStatus->setPixmap(QPixmap(":/icons/yellow_led.png"));
ui->lblHumiStatus->setPixmap(QPixmap(":/icons/yellow_led.png"));
} else if (status == "ALARM") {
ui->lblTempStatus->setPixmap(QPixmap(":/icons/red_led.png"));
ui->lblHumiStatus->setPixmap(QPixmap(":/icons/red_led.png"));
}
// 添加到表格
int row = ui->tableData->rowCount();
ui->tableData->insertRow(row);
ui->tableData->setItem(row, 0, new QTableWidgetItem(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")));
ui->tableData->setItem(row, 1, new QTableWidgetItem(QString::number(temp, 'f', 1)));
ui->tableData->setItem(row, 2, new QTableWidgetItem(QString::number(humi, 'f', 1)));
ui->tableData->setItem(row, 3, new QTableWidgetItem(status));
// 滚动到最后一行
ui->tableData->scrollToBottom();
// 添加到数据序列
tempData.append(QPointF(timeCounter, temp));
humiData.append(QPointF(timeCounter, humi));
timePoints.append(QPointF(timeCounter, 0));
timeCounter++;
// 限制数据点数量
if (tempData.size() > 100) {
tempData.removeFirst();
humiData.removeFirst();
timePoints.removeFirst();
}
// 检查阈值
checkThresholds();
// 自动控制
if (autoControlEnabled) {
if (temp > tempThresholdHigh) {
controlDevice("COOLER", true);
controlDevice("HEATER", false);
} else if (temp < tempThresholdLow) {
controlDevice("HEATER", true);
controlDevice("COOLER", false);
} else {
controlDevice("COOLER", false);
controlDevice("HEATER", false);
}
if (humi > humiThresholdHigh) {
controlDevice("HUMIDIFIER", false);
controlDevice("DEHUMIDIFIER", true);
} else if (humi < humiThresholdLow) {
controlDevice("HUMIDIFIER", true);
controlDevice("DEHUMIDIFIER", false);
} else {
controlDevice("HUMIDIFIER", false);
controlDevice("DEHUMIDIFIER", false);
}
}
}
void MainWindow::checkThresholds()
{
double temp = ui->lcdTemp->value();
double humi = ui->lcdHumi->value();
QString alarmMsg;
if (temp > tempThresholdHigh) {
alarmMsg = QString("温度过高: %1℃ > %2℃").arg(temp).arg(tempThresholdHigh);
ui->lblTempStatus->setPixmap(QPixmap(":/icons/red_led.png"));
if (soundAlarmEnabled) {
QSoundEffect::play(":sounds/alarm.wav");
}
if (emailAlertEnabled) {
sendEmailAlert(alarmMsg);
}
} else if (temp < tempThresholdLow) {
alarmMsg = QString("温度过低: %1℃ < %2℃").arg(temp).arg(tempThresholdLow);
ui->lblTempStatus->setPixmap(QPixmap(":/icons/blue_led.png"));
if (soundAlarmEnabled) {
QSoundEffect::play(":sounds/alarm.wav");
}
if (emailAlertEnabled) {
sendEmailAlert(alarmMsg);
}
}
if (humi > humiThresholdHigh) {
alarmMsg = QString("湿度过高: %1%% > %2%%").arg(humi).arg(humiThresholdHigh);
ui->lblHumiStatus->setPixmap(QPixmap(":/icons/red_led.png"));
if (soundAlarmEnabled) {
QSoundEffect::play(":sounds/alarm.wav");
}
if (emailAlertEnabled) {
sendEmailAlert(alarmMsg);
}
} else if (humi < humiThresholdLow) {
alarmMsg = QString("湿度过低: %1%% < %2%%").arg(humi).arg(humiThresholdLow);
ui->lblHumiStatus->setPixmap(QPixmap(":/icons/blue_led.png"));
if (soundAlarmEnabled) {
QSoundEffect::play(":sounds/alarm.wav");
}
if (emailAlertEnabled) {
sendEmailAlert(alarmMsg);
}
}
if (!alarmMsg.isEmpty()) {
showAlarm(alarmMsg);
addLogEntry(alarmMsg);
}
}
void MainWindow::controlDevice(const QString &device, bool state)
{
QString cmd = device.toUpper() + ":" + (state ? "ON" : "OFF");
sendCommand(cmd);
// 更新设备状态显示
if (device == "HEATER") {
ui->lblHeaterStatus->setText(state ? "开启" : "关闭");
ui->lblHeaterStatus->setStyleSheet(state ? "color: red;" : "color: green;");
} else if (device == "COOLER") {
ui->lblCoolerStatus->setText(state ? "开启" : "关闭");
ui->lblCoolerStatus->setStyleSheet(state ? "color: red;" : "color: green;");
} else if (device == "HUMIDIFIER") {
ui->lblHumidifierStatus->setText(state ? "开启" : "关闭");
ui->lblHumidifierStatus->setStyleSheet(state ? "color: red;" : "color: green;");
} else if (device == "DEHUMIDIFIER") {
ui->lblDehumidifierStatus->setText(state ? "开启" : "关闭");
ui->lblDehumidifierStatus->setStyleSheet(state ? "color: red;" : "color: green;");
}
}
void MainWindow::on_btnSetThresholds_clicked()
{
tempThresholdHigh = ui->spinTempHigh->value();
tempThresholdLow = ui->spinTempLow->value();
humiThresholdHigh = ui->spinHumiHigh->value();
humiThresholdLow = ui->spinHumiLow->value();
saveSettings();
QMessageBox::information(this, "设置成功", "阈值已更新");
addLogEntry("阈值更新: 温度(" + QString::number(tempThresholdLow) + "~" +
QString::number(tempThresholdHigh) + "℃), 湿度(" +
QString::number(humiThresholdLow) + "~" +
QString::number(humiThresholdHigh) + "%)");
}
void MainWindow::on_chkAutoControl_toggled(bool checked)
{
autoControlEnabled = checked;
saveSettings();
addLogEntry(QString("自动控制已%1").arg(checked ? "启用" : "禁用"));
}
void MainWindow::on_chkSoundAlarm_toggled(bool checked)
{
soundAlarmEnabled = checked;
saveSettings();
addLogEntry(QString("声音报警已%1").arg(checked ? "启用" : "禁用"));
}
void MainWindow::on_chkEmailAlert_toggled(bool checked)
{
emailAlertEnabled = checked;
saveSettings();
addLogEntry(QString("邮件报警已%1").arg(checked ? "启用" : "禁用"));
}
void MainWindow::on_btnStartLogging_clicked()
{
if (isLogging) return;
logFilePath = QFileDialog::getSaveFileName(this, "选择日志文件",
logFilePath,
"CSV文件 (*.csv)");
if (logFilePath.isEmpty()) return;
// 创建文件并写入标题行
QFile file(logFilePath);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream stream(&file);
stream << "时间,温度(℃),湿度(%)\n";
file.close();
}
isLogging = true;
loggingTimer->start(60000); // 每分钟记录一次
ui->btnStartLogging->setEnabled(false);
ui->btnStopLogging->setEnabled(true);
addLogEntry("开始记录日志到: " + logFilePath);
}
void MainWindow::on_btnStopLogging_clicked()
{
if (!isLogging) return;
isLogging = false;
loggingTimer->stop();
ui->btnStartLogging->setEnabled(true);
ui->btnStopLogging->setEnabled(false);
addLogEntry("停止记录日志");
}
void MainWindow::logDataToFile()
{
if (!isLogging || logFilePath.isEmpty()) return;
QFile file(logFilePath);
if (file.open(QIODevice::Append | QIODevice::Text)) {
QTextStream stream(&file);
stream << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") << ","
<< ui->lcdTemp->value() << ","
<< ui->lcdHumi->value() << "\n";
file.close();
}
}
void MainWindow::updateDataDisplay()
{
// 更新设备状态显示
updateDeviceStatus();
}
void MainWindow::updateDeviceStatus()
{
// 在实际应用中,这里应该从设备获取状态
// 这里简化处理,直接显示当前状态
}
void MainWindow::updateCharts()
{
// 更新温度图表
tempSeries->replace(tempData);
axisX->setRange(timeCounter - 60, timeCounter);
axisYTemp->setRange(0, qMax(50.0, *std::max_element(tempData.constBegin(), tempData.constEnd(),
[](const QPointF &a, const QPointF &b) { return a.y() < b.y(); }) + 5.0));
// 更新湿度图表
humiSeries->replace(humiData);
axisYHumi->setRange(0, 100);
}
void MainWindow::handleSerialError(QSerialPort::SerialPortError error)
{
if (error == QSerialPort::NoError) return;
QString errorMsg = "串口错误: ";
switch (error) {
case QSerialPort::DeviceNotFoundError:
errorMsg += "设备未找到";
break;
case QSerialPort::PermissionError:
errorMsg += "权限不足";
break;
case QSerialPort::OpenError:
errorMsg += "设备忙或无法打开";
break;
case QSerialPort::NotOpenError:
errorMsg += "设备未打开";
break;
case QSerialPort::WriteError:
errorMsg += "写入错误";
break;
case QSerialPort::ReadError:
errorMsg += "读取错误";
break;
case QSerialPort::ResourceError:
errorMsg += "资源不可用";
break;
case QSerialPort::UnsupportedOperationError:
errorMsg += "不支持的操作";
break;
case QSerialPort::TimeoutError:
errorMsg += "操作超时";
break;
default:
errorMsg += "未知错误";
break;
}
ui->txtLog->append(QDateTime::currentDateTime().toString("[hh:mm:ss] ") + errorMsg);
showNotification("串口错误", errorMsg);
// 如果是严重错误,断开连接
if (error != QSerialPort::NoError && error != QSerialPort::TimeoutError) {
on_btnDisconnect_clicked();
}
}
void MainWindow::on_btnClearLog_clicked()
{
ui->txtLog->clear();
}
void MainWindow::addLogEntry(const QString &entry)
{
ui->txtLog->append(QDateTime::currentDateTime().toString("[hh:mm:ss] ") + entry);
}
void MainWindow::showAlarm(const QString &message)
{
trayIcon->showMessage("温湿度报警", message, QSystemTrayIcon::Critical, 5000);
ui->statusBar->showMessage(message, 5000);
}
void MainWindow::showNotification(const QString &title, const QString &message)
{
trayIcon->showMessage(title, message, QSystemTrayIcon::Information, 3000);
}
void MainWindow::sendEmailAlert(const QString &message)
{
// 在实际应用中,这里应该实现邮件发送功能
// 这里简化处理,只记录日志
addLogEntry("邮件报警: " + message);
}
void MainWindow::toggleWindowVisibility()
{
if (isVisible()) {
hide();
} else {
showNormal();
activateWindow();
}
}
void MainWindow::showAboutDialog()
{
QMessageBox::about(this, "关于温湿度监控系统",
"<h3>温湿度监控系统 v1.0</h3>"
"<p>基于QT的串口温湿度监控系统</p>"
"<p>功能:</p>"
"<ul>"
"<li>串口通信与数据采集</li>"
"<li>实时数据显示与曲线绘制</li>"
"<li>阈值报警与自动控制</li>"
"<li>数据记录与日志管理</li>"
"</ul>"
"<p>© 2023 工业自动化实验室</p>");
}
void MainWindow::closeEvent(QCloseEvent *event)
{
if (trayIcon->isVisible()) {
hide();
event->ignore();
} else {
event->accept();
}
}
主函数 (main.cpp)
#include "mainwindow.h"
#include <QApplication>
#include <QIcon>
#include <QTranslator>
#include <QLocale>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 设置应用程序信息
QApplication::setApplicationName("温湿度监控系统");
QApplication::setApplicationVersion("1.0");
QApplication::setOrganizationName("工业自动化实验室");
// 设置应用程序图标
QApplication::setWindowIcon(QIcon(":/icons/temperature.png"));
// 加载翻译文件(如果需要国际化)
QTranslator translator;
const QString locale = QLocale::system().name();
if (translator.load(QString("thermo_") + locale, ":/translations")) {
QApplication::installTranslator(&translator);
}
MainWindow w;
w.show();
return a.exec();
}
UI文件 (mainwindow.ui)
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>800</height>
</rect>
</property>
<property name="windowTitle">
<string>温湿度监控系统</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBoxConnection">
<property name="title">
<string>串口连接</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>串口号:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cmbPort"/>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnRefreshPorts">
<property name="text">
<string>刷新</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>波特率:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cmbBaud">
<item>
<property name="text">
<string>9600</string>
</property>
</item>
<item>
<property name="text">
<string>19200</string>
</property>
</item>
<item>
<property name="text">
<string>38400</string>
</property>
</item>
<item>
<property name="text">
<string>57600</string>
</property>
</item>
<item>
<property name="text">
<string>115200</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>数据位:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cmbDataBits">
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>7</string>
</property>
</item>
<item>
<property name="text">
<string>6</string>
</property>
</item>
<item>
<property name="text">
<string>5</string>
</property>
</item>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>校验位:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="cmbParity">
<item>
<property name="text">
<string>无</string>
</property>
</item>
<item>
<property name="text">
<string>奇校验</string>
</property>
</item>
<item>
<property name="text">
<string>偶校验</string>
</property>
</item>
<item>
<property name="text">
<string>空格校验</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>停止位:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cmbStopBits">
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>1.5</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnConnect">
<property name="text">
<string>连接</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDisconnect">
<property name="text">
<string>断开连接</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QGroupBox" name="groupBoxData">
<property name="title">
<string>实时数据</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>温度:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLCDNumber" name="lcdTemp">
<property name="digitCount">
<number>5</number>
</property>
<property name="segmentStyle">
<enum>QLCDNumber::Filled</enum>
</property>
<property name="smallDecimalPoint">
<bool>true</bool>
</property>
<property name="value" stdset="0">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="lblTempStatus">
<property name="pixmap">
<pixmap resource="resources.qrc">:/icons/gray_led.png</pixmap>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>湿度:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLCDNumber" name="lcdHumi">
<property name="digitCount">
<number>5</number>
</property>
<property name="segmentStyle">
<enum>QLCDNumber::Filled</enum>
</property>
<property name="smallDecimalPoint">
<bool>true</bool>
</property>
<property name="value" stdset="0">
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="lblHumiStatus">
<property name="pixmap">
<pixmap resource="resources.qrc">:/icons/gray_led.png</pixmap>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>状态:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblStatusText">
<property name="text">
<string>未连接</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QLabel" name="labelDeviceStatus">
<property name="text">
<string>设备状态:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>加热器:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="lblHeaterStatus">
<property name="text">
<string>关闭</string>
</property>
<property name="styleSheet">
<string notr="true">color: green;</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>冷却器:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="lblCoolerStatus">
<property name="text">
<string>关闭</string>
</property>
<property name="styleSheet">
<string notr="true">color: green;</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>加湿器:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="lblHumidifierStatus">
<property name="text">
<string>关闭</string>
</property>
<property name="styleSheet">
<string notr="true">color: green;</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>除湿器:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="lblDehumidifierStatus">
<property name="text">
<string>关闭</string>
</property>
<property name="styleSheet">
<string notr="true">color: green;</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTabWidget" name="tabWidget">
<widget class="QWidget" name="tabDashboard">
<attribute name="title">
<string>仪表盘</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QChartView" name="chartTemp"/>
</item>
<item row="0" column="1">
<widget class="QChartView" name="chartHumi"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTableWidget" name="tableData">
<column>
<property name="text">
<string>时间</string>
</property>
</column>
<column>
<property name="text">
<string>温度(℃)</string>
</property>
</column>
<column>
<property name="text">
<string>湿度(%)</string>
</property>
</column>
<column>
<property name="text">
<string>状态</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabControl">
<attribute name="title">
<string>控制</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBoxThresholds">
<property name="title">
<string>阈值设置</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>温度上限(℃):</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="spinTempHigh">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-50.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.500000000000000</double>
</property>
<property name="value" stdset="0">
<double>30.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>温度下限(℃):</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="spinTempLow">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-50.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>0.500000000000000</double>
</property>
<property name="value" stdset="0">
<double>10.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>湿度上限(%):</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="spinHumiHigh">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value" stdset="0">
<double>70.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>湿度下限(%):</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="spinHumiLow">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value" stdset="0">
<double>30.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QPushButton" name="btnSetThresholds">
<property name="text">
<string>应用阈值</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QGroupBox" name="groupBoxAutomation">
<property name="title">
<string>自动控制</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0">
<widget class="QCheckBox" name="chkAutoControl">
<property name="text">
<string>启用自动控制</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="chkSoundAlarm">
<property name="text">
<string>声音报警</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="chkEmailAlert">
<property name="text">
<string>邮件报警</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>手动控制:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="btnHeaterOn">
<property name="text">
<string>加热器开</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="btnHeaterOff">
<property name="text">
<string>加热器关</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QPushButton" name="btnCoolerOn">
<property name="text">
<string>冷却器开</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QPushButton" name="btnCoolerOff">
<property name="text">
<string>冷却器关</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabLog">
<attribute name="title">
<string>日志</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<widget class="QTextEdit" name="txtLog"/>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="btnClearLog">
<property name="text">
<string>清空日志</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnStartLogging">
<property name="text">
<string>开始记录</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnStopLogging">
<property name="text">
<string>停止记录</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tabCommands">
<attribute name="title">
<string>命令</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_9">
<item row="0" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>发送命令:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="txtCommand"/>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="btnSend">
<property name="text">
<string>发送</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QTextEdit" name="txtCommandLog"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusBar">
<widget class="QLabel" name="lblStatus"/>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1200</width>
<height>26</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>文件</string>
</property>
<addaction name="actionConnect"/>
<addaction name="actionDisconnect"/>
<addaction name="separator"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>帮助</string>
</property>
</widget>
<addaction name="menuFile"/>
<addaction name="menuHelp"/>
</widget>
<action name="actionConnect">
<property name="text">
<string>连接</string>
</property>
</action>
<action name="actionDisconnect">
<property name="text">
<string>断开连接</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>退出</string>
</property>
</action>
</widget>
<resources>
<include location="resources.qrc"/>
</resources>
<connections/>
</ui>
系统功能说明
1. 串口通信管理
- 支持多种波特率(9600-115200)
- 可配置数据位、校验位和停止位
- 自动检测可用串口
- 实时显示串口通信日志
2. 数据显示与可视化
- 实时显示温度和湿度数值
- 状态指示灯(正常/警告/报警)
- 温度变化曲线图
- 湿度变化曲线图
- 历史数据表格展示
3. 阈值报警系统
- 可设置温度/湿度上下限
- 超限时触发声光报警
- 系统托盘通知
- 邮件报警功能(需配置SMTP)
4. 设备控制功能
- 加热器/冷却器控制
- 加湿器/除湿器控制
- 自动控制模式(根据阈值自动调节)
- 手动控制模式
5. 数据记录与分析
- CSV格式数据记录
- 定时自动保存
- 历史数据查询
- 数据导出功能
6. 系统管理
- 系统托盘图标
- 最小化到托盘
- 开机自启动选项
- 多语言支持(预留接口)
参考代码 QT 串口温湿度控制系统 www.youwenfan.com/contentcsw/122853.html
系统特点
-
现代化UI设计
- 深色主题界面
- 响应式布局
- 直观的数据可视化
- 自定义控件样式
-
强大的串口通信
- 支持多种串口参数配置
- 自动错误检测与恢复
- 实时通信日志
- 命令发送与响应显示
-
智能报警系统
- 多级报警机制(警告/报警)
- 多种报警方式(声音/视觉/通知)
- 报警历史记录
- 报警阈值灵活配置
-
完善的数据管理
- 实时数据存储
- 历史数据查询
- 数据导出功能
- 数据可视化分析
-
可靠的系统架构
- 模块化设计
- 异常处理机制
- 配置文件保存
- 系统日志记录
部署与使用
系统要求
- Windows/Linux/macOS
- Qt 5.12+
- C++11编译器
- 串口设备驱动
安装步骤
- 安装Qt开发环境
- 克隆项目仓库
- 配置串口参数
- 编译并运行程序
使用流程
- 连接串口设备
- 配置串口参数(端口、波特率等)
- 点击"连接"按钮
- 设置报警阈值
- 启用自动控制或手动控制设备
- 查看实时数据和曲线
- 需要时记录数据到文件
扩展功能建议
-
云平台集成
void uploadToCloud(const QString &data) { QNetworkAccessManager *manager = new QNetworkAccessManager(this); QNetworkRequest request(QUrl("https://api.example.com/data")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QJsonObject json; json["temperature"] = ui->lcdTemp->value(); json["humidity"] = ui->lcdHumi->value(); json["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate); QNetworkReply *reply = manager->post(request, QJsonDocument(json).toJson()); connect(reply, &QNetworkReply::finished, this, [reply]() { reply->deleteLater(); }); } -
数据库存储
#include <QSqlDatabase> #include <QSqlQuery> void initDatabase() { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName("thermo_data.db"); if (!db.open()) { qDebug() << "无法打开数据库"; return; } QSqlQuery query; query.exec("CREATE TABLE IF NOT EXISTS readings (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "timestamp DATETIME DEFAULT CURRENT_TIMESTAMP," "temperature REAL," "humidity REAL," "status TEXT)"); } void saveToDatabase(double temp, double humi, const QString &status) { QSqlQuery query; query.prepare("INSERT INTO readings (temperature, humidity, status) " "VALUES (:temp, :humi, :status)"); query.bindValue(":temp", temp); query.bindValue(":humi", humi); query.bindValue(":status", status); query.exec(); } -
WebSocket实时推送
#include <QWebSocketServer> #include <QWebSocket> void initWebSocketServer() { QWebSocketServer *server = new QWebSocketServer("ThermoMonitor", QWebSocketServer::NonSecureMode, this); if (server->listen(QHostAddress::Any, 8080)) { connect(server, &QWebSocketServer::newConnection, this, [server, this]() { QWebSocket *client = server->nextPendingConnection(); clients << client; connect(client, &QWebSocket::disconnected, this, [client, this]() { clients.removeAll(client); client->deleteLater(); }); }); } } void broadcastData(double temp, double humi) { QJsonObject json; json["temperature"] = temp; json["humidity"] = humi; json["timestamp"] = QDateTime::currentDateTime().toString(Qt::ISODate); QJsonDocument doc(json); QString message = doc.toJson(QJsonDocument::Compact); for (QWebSocket *client : clients) { client->sendTextMessage(message); } } -
移动端适配
#ifdef Q_OS_ANDROID void requestPermissions() { QtAndroid::requestPermissionsSync({"android.permission.INTERNET", "android.permission.ACCESS_FINE_LOCATION"}); } void setupMobileUI() { // 适配移动端的UI布局 ui->centralwidget->setStyleSheet("background-color: #f0f0f0;"); ui->tabWidget->setTabPosition(QTabWidget::South); } #endif -
AI预测分析
#include <tensorflow/lite/interpreter.h> #include <tensorflow/lite/model.h> void predictTrend() { // 加载TensorFlow Lite模型 std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile("trend_predict.tflite"); // 创建解释器 tflite::ops::builtin::BuiltinOpResolver resolver; std::unique_ptr<tflite::Interpreter> interpreter; tflite::InterpreterBuilder(*model, resolver)(&interpreter); // 设置输入数据(最近10个数据点) float input[10] = {...}; memcpy(interpreter->typed_input_tensor<float>(0), input, sizeof(input)); // 运行推理 interpreter->Invoke(); // 获取预测结果 float *output = interpreter->typed_output_tensor<float>(0); float predictedTemp = output[0]; float predictedHumi = output[1]; // 显示预测结果 ui->lblPrediction->setText(QString("预测: %1℃, %2%").arg(predictedTemp).arg(predictedHumi)); }
常见问题解决
-
串口无法打开
- 检查串口是否被其他程序占用
- 确认串口名称和参数是否正确
- 尝试以管理员权限运行程序
- 检查USB转串口驱动是否安装
-
数据显示异常
- 检查传感器数据格式
- 验证串口通信协议
- 查看通信日志定位问题
- 尝试不同的波特率
-
报警不触发
- 确认阈值设置是否正确
- 检查报警开关是否启用
- 验证声音设备是否正常
- 查看系统日志是否有错误
-
数据记录失败
- 检查文件路径权限
- 确认磁盘空间充足
- 验证CSV文件格式
- 尝试手动指定文件路径
-
界面卡顿
- 减少图表更新频率
- 关闭不必要的日志显示
- 优化数据处理算法
- 升级硬件配置