關鍵詞:MQTT、物聯網、QT、網絡連接、遠程控制
一、系統概述
本系統是一套完整的智能大棚監控解決方案,由兩部分構成:
- 基于Hi3861的嵌入式硬件系統(負責環境數據采集和設備控制)
- 基于Qt開發的跨平臺控制軟件(提供可視化界面和遠程控制)
該系統實現對大棚環境的全方位監控:
- ??環境監測系統??:實時檢測溫度、濕度、光照強度、CO?濃度
- ??安防系統??:人體檢測報警(PIR傳感器)
- ??執行設備??:LED補光燈控制、通風風扇控制
- ??雙模控制??:手動控制/自動智能控制模式
- ??遠程監控??:實時數據面板 + 三態環境狀態指示
- ??異常處理??:臨界值提醒與自動執行保護策略
二、Qt控制界面設計亮點
1. 布局結構(用戶登錄界面和系統控制界面)
2. 動態數據可視化功能
??環境數據卡片設計:??
QWidget* Widget::createDataCard(const QString& title, const QString& unit, const QColor& color)
{// 創建帶有彩色邊框的數據卡片// 包含標題、實時數值、單位和趨勢指示器
}
??趨勢變化可視化:?
由于博主的QT版本過低,部分模塊及代碼無法兼容,所以無法通過繪制溫濕度、光照強度的折線圖觀察環境數據的具體趨勢,采用箭頭指示?的方式作為系統數據趨勢的指示器,當檢測到此次數據較上一次采集數據高時箭頭指向↑,以此類推。
// 更新趨勢指示器(上升↑、下降↓、穩定→)
if (value > prevValue) {trendLabel->setText("↑");trendLabel->setStyleSheet("color: red;");
} else if (value < prevValue) {trendLabel->setText("↓");trendLabel->setStyleSheet("color: blue;");
} else {trendLabel->setText("→");
}
???3. 三態預警系統??
我們創新性地采用三色指示燈直觀展示環境狀態(正常/臨界/異常),避免單一閾值判斷的局限性:
void Widget::updateStatusIndicator(QWidget* indicator, double value, double min, double max) {QLabel *light = indicator->findChild<QLabel*>("statusLight");if (!light) return;// 正常范圍:顯示綠色if (value >= min && value <= max) {light->setStyleSheet("border-radius: 15px; background-color: #4CAF50;");} // 臨界范圍:顯示黃色(預留±5緩沖區間)else if ((value >= min-5 && value < min) || (value > max && value <= max+5)) {light->setStyleSheet("border-radius: 15px; background-color: #FFC107;");} // 危險范圍:顯示紅色else {light->setStyleSheet("border-radius: 15px; background-color: #F44336;");}
}
應用場景示例:
- 溫度預警:20-28℃正常(綠)|15-19℃或29-33℃警告(黃)|<15℃或>33℃危險(紅)
- 該設計解決了傳統二元警報頻繁誤報問題,大幅提升監控準確性
??4. 人體檢測可視化??
采用動態指示燈+狀態文字雙重提示:
// 提取人體監測數據bool humanDetected = jsonObj.contains("HumanSensor") ? jsonObj["HumanSensor"].toBool() : false;// 更新人體監測指示燈if (humanDetected) {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: green;");humanMonitorStatus->setText("有人");} else {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: red;");humanMonitorStatus->setText("無人");}
安防聯動設計:
- 檢測到人員時觸發警報(可以嘗試添加人臉識別,制作檢測抓拍記錄功能)
- 夜間模式下自動開啟補光燈輔助監控
???三、ThingsCloud云平臺
?1.設備詳情
新建設備智能大棚,添加大棚燈、風機、溫濕度、人體監測等部分,需要注意的是由QT控制的部分(燈、風機、警報)需要將屬性設置為云端下發屬性,QT接收的溫濕度、光照強度、CO2等設置為設備上報屬性
2.設備看板
3.手機端云平臺
在ThingsCloud云平臺創建自己的UI界面,注意添加對應組件的屬性,保存后在掃描用戶應用中的微信小程序二維碼,登錄的手機端云平臺界面就是電腦端創建的UI界面,該界面與電腦端界面操作同步。
4.MQTT通信效果
我們在MQTTX創建設備連接,連接云平臺設備,在發送如下字符(設備上報)后發現看板組件確實安裝發送的溫濕度數據配置,同時在看板中打開燈,云端下發了如下字符,證明MQTT通信成功,可以連接。
四、雙模式控制機制?
??1. 手動模式
用戶可直接點擊控件按鈕,系統實時反饋設備狀態:
void Widget::toggleLight1() {if (!autoMode) { // 僅在手動模式響應light1On = !light1On;light1Button->setText(light1On ? "開" : "關");sendData(light1On ? "{\"light0\":true}" : "{\"light0\":false}");}
}
點擊按鍵后,QT根據按鍵向開發板發送固定字符,開發板通過接收的字符進行判斷,控制部分外設進行特定功能。
??2. 自動模式 ?
動態響應環境變化,用戶在自動模式下無法直接操作按鍵,由系統自行控制,同時系統日志顯示切換到自動模式。
void Widget::parseEnvironmentData(const QByteArray &data) {if (autoMode) {// 溫度>26°C開啟風扇,<25°C關閉(滯回控制防抖動)if (temp > 26.0 && !fanOn) sendControlCommand(FAN_ON); else if (temp <= 25.0 && fanOn) sendControlCommand(FAN_OFF);// 光照<80Lux開燈,>90Lux關燈if (lightIntensity < 80 && !light1On) sendControlCommand(LIGHT_ON);else if (lightIntensity >= 90 && light1On) sendControlCommand(LIGHT_OFF);// 人員檢測聯動報警if (humanDetected) sendControlCommand(ALARM_ON);else if (alarmOn) sendControlCommand(ALARM_OFF);}
}
策略優勢:
- 滯回區間設計:防止溫度波動導致風扇頻繁啟停
- 分級響應:不同設備采用獨立閾值策略
??五、通信與算法優化實踐??
??1. 高效UDP通信協議?
初始化udp套接字,以及開發板IP地址。注:確保電腦和開發板要連在同一WiFi熱點下,通過串口查找開發板設備IP地址,在QT代碼中設為連接目標,確保端口號與開發板程序設定的端口號一致(此處設置為9000,設備IP地址為192.168.34.8).?
// 初始化UDPudpSocket = new QUdpSocket(this);if (!udpSocket->bind(QHostAddress::Any, 9000)) {QMessageBox::critical(this, "初始化錯誤", "UDP套接字綁定失敗,請檢查端口是否被占用");} else {connect(udpSocket, SIGNAL(readyRead()), this, SLOT(recvData()));}
}
??2. 核心算法優化實例?
解析JSON數據:溫濕度,光照,CO2,人體監測狀態,判斷是否為自動模式,根據解析的數據進行面板顯示和狀態指示燈顯示。?
void Widget::parseEnvironmentData(const QByteArray &data)
{QJsonParseError jsonError;QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);if (jsonError.error == QJsonParseError::NoError && jsonDoc.isObject()) {QJsonObject jsonObj = jsonDoc.object();// 提取溫度數據(使用0作為默認值)double temperature = jsonObj.contains("Temp") ? jsonObj["Temp"].toDouble() : 0.0;// 更新溫度數據卡片if (tempCard) {QLabel *tempValueLabel = tempCard->findChild<QLabel*>("valueLabel");if (tempValueLabel) {tempValueLabel->setText(QString::number(temperature, 'f', 1));// 更新趨勢指示器QLabel *tempTrendLabel = tempCard->findChild<QLabel*>("trendLabel");if (tempTrendLabel) {if (temperature > prevTemp) {tempTrendLabel->setText("↑");tempTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (temperature < prevTemp) {tempTrendLabel->setText("↓");tempTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {tempTrendLabel->setText("→");tempTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevTemp = temperature;}}}// 提取濕度數據double humidity = jsonObj.contains("Humi") ? jsonObj["Humi"].toDouble() : 0.0;if (humCard) {QLabel *humValueLabel = humCard->findChild<QLabel*>("valueLabel");if (humValueLabel) {humValueLabel->setText(QString::number(humidity, 'f', 0));// 更新趨勢指示器QLabel *humTrendLabel = humCard->findChild<QLabel*>("trendLabel");if (humTrendLabel) {if (humidity > prevHum) {humTrendLabel->setText("↑");humTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (humidity < prevHum) {humTrendLabel->setText("↓");humTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {humTrendLabel->setText("→");humTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevHum = humidity;}}}// 提取CO?數據int co2 = jsonObj.contains("CO2") ? jsonObj["CO2"].toInt() : 0;if (co2Card) {QLabel *co2ValueLabel = co2Card->findChild<QLabel*>("valueLabel");if (co2ValueLabel) {co2ValueLabel->setText(QString::number(co2));// 更新趨勢指示器QLabel *co2TrendLabel = co2Card->findChild<QLabel*>("trendLabel");if (co2TrendLabel) {if (co2 > prevCO2) {co2TrendLabel->setText("↑");co2TrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (co2 < prevCO2) {co2TrendLabel->setText("↓");co2TrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {co2TrendLabel->setText("→");co2TrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevCO2 = co2;}}}// 提取光照強度數據int lightIntensity = jsonObj.contains("lumen") ? jsonObj["lumen"].toInt() : 0;if (lightCard) {QLabel *lightValueLabel = lightCard->findChild<QLabel*>("valueLabel");if (lightValueLabel) {lightValueLabel->setText(QString::number(lightIntensity));// 更新趨勢指示器QLabel *lightTrendLabel = lightCard->findChild<QLabel*>("trendLabel");if (lightTrendLabel) {if (lightIntensity > prevLight) {lightTrendLabel->setText("↑");lightTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (lightIntensity < prevLight) {lightTrendLabel->setText("↓");lightTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {lightTrendLabel->setText("→");lightTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevLight = lightIntensity;}}}// 更新狀態指示器updateStatusIndicator(tempStatus, temperature, 20, 28);updateStatusIndicator(humStatus, humidity, 40, 60);updateStatusIndicator(co2Status, co2, 0, 800);updateStatusIndicator(lightStatus, lightIntensity, 80, 100);// 提取人體監測數據bool humanDetected = jsonObj.contains("HumanSensor") ? jsonObj["HumanSensor"].toBool() : false;// 更新人體監測指示燈if (humanDetected) {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: green;");humanMonitorStatus->setText("有人");} else {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: red;");humanMonitorStatus->setText("無人");}// 更新溫度數據用于存儲if (!temperatureData.isEmpty()) {temperatureData.pop_front();temperatureData.append(temperature);}// 如果是自動模式,執行自動控制邏輯if (autoMode) {// 溫度高于26度時開啟風扇if (temperature > 26.0 && !fanOn) {sendData("{\"Fan0\":true}");fanOn = true;fanButton->setText("開");fanButton->setChecked(true);}// 溫度低于25度時關閉風扇else if (temperature <= 25.0 && fanOn) {sendData("{\"Fan0\":false}");fanOn = false;fanButton->setText("關");fanButton->setChecked(false);}// 檢測到有人時開啟報警if (humanDetected && !alarmOn) {sendData("{\"Alarm\":true}");alarmOn = true;alarmButton->setText("開");alarmButton->setChecked(true);}// 無人時關閉報警else if (!humanDetected && alarmOn) {sendData("{\"Alarm\":false}");alarmOn = false;alarmButton->setText("關");alarmButton->setChecked(false);}// 光照強度小于100時開啟燈1if (lightIntensity < 80 && !light1On) {sendData("{\"light0\":true}");light1On = true;light1Button->setText("開");light1Button->setChecked(true);}// 光照強度大于150時關閉燈1else if (lightIntensity >= 90 && light1On) {sendData("{\"light0\":false}");light1On = false;light1Button->setText("關");light1Button->setChecked(false);}}}
}
??六、代碼管理
1. QT登錄界面
?實現兩種登錄模式(用戶名密碼登錄、其他賬號登錄),這里博主為了省事直接將其他方式寫為點擊后彈出窗口顯示系統維護。。。具體實現方法和用戶名密碼登錄大體一致,感興趣的朋友可以自行嘗試其他方法登錄。
#include "loginwidget.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPainter>
#include <QPaintEvent>
#include <QStyleOption>
#include <QMessageBox>
#include <QTimer>
#include <QRadialGradient>
#include "widget.h" // 控制界面頭文件
#include <QGroupBox>LoginWidget::LoginWidget(QWidget *parent) : QWidget(parent)
{setWindowTitle("智能大棚控制系統");setFixedSize(1000, 700);setupUI();// 填充示例用戶名usernameEdit->setText("");passwordEdit->setText(""); // 實際使用時應該為空
}void LoginWidget::setupUI()
{// 主布局QHBoxLayout *mainLayout = new QHBoxLayout(this);mainLayout->setContentsMargins(0, 0, 0, 0);mainLayout->setSpacing(0);// 左側裝飾區(占位60%寬度)QWidget *leftDecoration = new QWidget(this);leftDecoration->setStyleSheet("background-color: #0f1120;");mainLayout->addWidget(leftDecoration, 6);// 替換左側裝飾區創建代碼//leftDecoration->setStyleSheet("background-color: #0f1120;"); // 保留這個以防圖片加載失敗QVBoxLayout *leftLayout = new QVBoxLayout(leftDecoration);leftLayout->setContentsMargins(0, 0, 0, 0);// 創建圖片標簽QLabel *bgLabel = new QLabel(leftDecoration);bgLabel->setScaledContents(true); // 設置圖片縮放以適應標簽leftLayout->addWidget(bgLabel);bgLabel->setPixmap(QPixmap(":/2025.png"));// 右側登錄表單區(占位40%寬度)QWidget *rightForm = new QWidget(this);rightForm->setStyleSheet("background-color: #f9fafb;");QVBoxLayout *formLayout = new QVBoxLayout(rightForm);formLayout->setContentsMargins(70, 100, 70, 100);formLayout->setSpacing(0);mainLayout->addWidget(rightForm, 4);// 標題區域QVBoxLayout *titleLayout = new QVBoxLayout;titleLayout->setSpacing(5);// 歡迎語QLabel *welcomeLabel = new QLabel("你好,xxx");welcomeLabel->setStyleSheet("font-size: 32px; font-weight: bold; color: #2c3e50;");titleLayout->addWidget(welcomeLabel);// 英文標題QLabel *subtitle = new QLabel("Welcome, Wang");subtitle->setStyleSheet("font-size: 20px; color: #7b8a9b; line-height: 1.5;");titleLayout->addWidget(subtitle);titleLayout->addSpacing(10);formLayout->addLayout(titleLayout);// 表單區域QGroupBox *formGroup = new QGroupBox;formGroup->setStyleSheet("QGroupBox { border: none; }");QVBoxLayout *inputLayout = new QVBoxLayout(formGroup);inputLayout->setSpacing(15);// 用戶名輸入QLabel *usernameLabel = new QLabel("用戶名");usernameLabel->setStyleSheet("font-size: 16px; color: #2c3e50;");inputLayout->addWidget(usernameLabel);usernameEdit = new QLineEdit;usernameEdit->setStyleSheet("QLineEdit {"" border: 1px solid #dfe6f0;"" border-radius: 6px;"" padding: 12px;"" font-size: 20px;"" background: white;""}""QLineEdit:focus { border: 1px solid #2196f3; }");inputLayout->addWidget(usernameEdit);// 密碼輸入QLabel *passwordLabel = new QLabel("密碼");passwordLabel->setStyleSheet("font-size: 16px; color: #2c3e50;");inputLayout->addWidget(passwordLabel);passwordEdit = new QLineEdit;passwordEdit->setEchoMode(QLineEdit::Password);passwordEdit->setStyleSheet("QLineEdit {"" border: 1px solid #dfe6f0;"" border-radius: 6px;"" padding: 12px;"" font-size: 14px;"" background: white;""}""QLineEdit:focus { border: 1px solid #2196f3; }");inputLayout->addWidget(passwordEdit);// 密碼管理區域QHBoxLayout *passwordOptions = new QHBoxLayout;QCheckBox *rememberCheckbox = new QCheckBox("記住密碼");rememberCheckbox->setStyleSheet("color: #7b8a9b;");passwordOptions->addWidget(rememberCheckbox);passwordOptions->addStretch();QLabel *forgotPassword = new QLabel("忘記密碼?");forgotPassword->setStyleSheet("font-size: 14px; color: #2196f3; text-decoration: underline;");passwordOptions->addWidget(forgotPassword);inputLayout->addLayout(passwordOptions);inputLayout->addSpacing(15);formLayout->addWidget(formGroup);// 登錄按鈕loginButton = new QPushButton("登錄");loginButton->setFixedHeight(48);loginButton->setStyleSheet("QPushButton {"" background-color: #2196f3;"" color: white;"" border-radius: 6px;"" font-size: 15px;"" font-weight: bold;""}""QPushButton:hover { background-color: #1e88e5; }""QPushButton:pressed { background-color: #1976d2; }");formLayout->addWidget(loginButton);// 第三方登錄區域 - 替換為單一按鈕QLabel *otherMethods = new QLabel("其他登錄方式");otherMethods->setAlignment(Qt::AlignCenter);otherMethods->setStyleSheet("font-size: 14px; color: #7b8a9b; margin: 30px 0 15px 0;");formLayout->addWidget(otherMethods);otherMethods->setAlignment(Qt::AlignCenter);otherMethods->setStyleSheet("font-size: 14px; color: #7b8a9b; margin: 30px 0 15px 0;");formLayout->addWidget(otherMethods);// 創建成都理工大學登錄按鈕QPushButton *cdutLoginButton = new QPushButton("使用xxxx賬號登錄");cdutLoginButton->setFixedHeight(48);cdutLoginButton->setStyleSheet("QPushButton {"" background-color: #2196f3;"" color: white;"" border: 1px solid #1d5ab4;"" border-radius: 6px;"" font-size: 14px;"" font-weight: bold;""}""QPushButton:hover {"" background-color: #e3f0ff;""}""QPushButton:pressed {"" background-color: #c7e1ff;""}");formLayout->addWidget(cdutLoginButton);// 連接登錄按鈕信號// 連接新按鈕的信號connect(cdutLoginButton, SIGNAL(clicked()), this, SLOT(onCdutLoginClicked()));connect(loginButton, SIGNAL(clicked()), this, SLOT(onLoginClicked()));
}void LoginWidget::onCdutLoginClicked()
{QMessageBox::information(this, "系統維護", "xxxx統一認證服務正在維護中,請稍后再試");
}void LoginWidget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);
}void LoginWidget::onLoginClicked()
{QString username = usernameEdit->text();QString password = passwordEdit->text();// 禁用登錄按鈕防止重復點擊loginButton->setEnabled(false);loginButton->setText("登錄中...");// 添加簡單的登錄動畫效果QTimer::singleShot(1000, this, [=]() {// 檢查用戶名和密碼是否都是"1"if (username == "1" && password == "1") {// 登錄成功openControlWidget();this->hide(); // 隱藏登錄窗口} else {// 登錄失敗QMessageBox::warning(this, "登錄失敗", "用戶名或密碼錯誤");// 重置登錄按鈕狀態loginButton->setEnabled(true);loginButton->setText("登錄");}});
}void LoginWidget::openControlWidget()
{// 創建控制界面Widget *controlWidget = new Widget();controlWidget->show();// 當控制界面關閉時,重新顯示登錄窗口connect(controlWidget, &Widget::destroyed, this, [=]() {// 重新啟用登錄按鈕(為下次登錄準備)loginButton->setEnabled(true);loginButton->setText("登錄");// 重新顯示登錄窗口this->show();});
}
2. QT用戶控制界面
#include "widget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPainter>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QSplitter>
#include <QTimer>
#include <cmath>
#include <QButtonGroup>
#include <QMessageBox>
#include <QDateTime> // 添加這行Widget::Widget(QWidget *parent) :QWidget(parent),light1On(false), light2On(false), fanOn(false), alarmOn(false),light1Button(nullptr), light2Button(nullptr), fanButton(nullptr), alarmButton(nullptr),udpInput(nullptr), udpTextEdit(nullptr),targetIp("192.168.34.8"),modeGroup(nullptr), manualButton(nullptr), autoButton(nullptr),autoMode(false), // 默認手動模式// 初始化卡片指針tempCard(nullptr), humCard(nullptr), co2Card(nullptr), lightCard(nullptr),// 初始化狀態指示器指針tempStatus(nullptr), humStatus(nullptr), co2Status(nullptr), lightStatus(nullptr),// 初始化上一次的值prevTemp(0.0), prevHum(0.0), prevCO2(0), prevLight(0),// 人體監測相關humanMonitorLabel(nullptr), humanMonitorStatus(nullptr)
{setWindowTitle("智能大棚控制系統");resize(1200, 800);setStyleSheet("background-color: #f0f8ff;");// 初始化溫度數據為0for(int i = 0; i < 60; i++) {temperatureData.append(0.0);}// 主布局QSplitter *mainSplitter = new QSplitter(Qt::Horizontal, this);// 左側控制面板mainSplitter->addWidget(createLeftPanel());// 右側面板(上下分割)QSplitter *rightSplitter = new QSplitter(Qt::Vertical);rightSplitter->addWidget(createRightTopPanel());rightSplitter->addWidget(createRightBottomPanel());mainSplitter->addWidget(rightSplitter);mainSplitter->setStretchFactor(0, 2); // 左側占40%mainSplitter->setStretchFactor(1, 3); // 右側占60%QHBoxLayout *mainLayout = new QHBoxLayout(this);mainLayout->setContentsMargins(10, 10, 10, 10);mainLayout->addWidget(mainSplitter);// 初始化UDPudpSocket = new QUdpSocket(this);if (!udpSocket->bind(QHostAddress::Any, 9000)) {QMessageBox::critical(this, "初始化錯誤", "UDP套接字綁定失敗,請檢查端口是否被占用");} else {connect(udpSocket, SIGNAL(readyRead()), this, SLOT(recvData()));}
}Widget::~Widget()
{if (udpSocket) {udpSocket->close();delete udpSocket;}
}// 創建數據卡片
QWidget* Widget::createDataCard(const QString& title, const QString& unit, const QColor& color)
{QWidget *card = new QWidget;card->setObjectName("dataCard");card->setStyleSheet("QWidget#dataCard {""background-color: white;""border-radius: 10px;""border: 2px solid " + color.name() + ";""}");// 添加陰影效果QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect;shadow->setBlurRadius(10);shadow->setColor(QColor(0, 0, 0, 70));shadow->setOffset(0, 2);card->setGraphicsEffect(shadow);QVBoxLayout *layout = new QVBoxLayout(card);layout->setAlignment(Qt::AlignCenter);layout->setSpacing(10);// 標題QLabel *titleLabel = new QLabel(title);titleLabel->setStyleSheet("font-size: 18px; font-weight: bold; color: " + color.name() + ";");titleLabel->setAlignment(Qt::AlignCenter);layout->addWidget(titleLabel);// 值QLabel *valueLabel = new QLabel("0");valueLabel->setObjectName("valueLabel");valueLabel->setStyleSheet("font-size: 32px; font-weight: bold;");valueLabel->setAlignment(Qt::AlignCenter);layout->addWidget(valueLabel);// 單位QLabel *unitLabel = new QLabel(unit);unitLabel->setStyleSheet("font-size: 16px;");unitLabel->setAlignment(Qt::AlignCenter);layout->addWidget(unitLabel);// 趨勢指示器(箭頭)QLabel *trendLabel = new QLabel("→");trendLabel->setObjectName("trendLabel");trendLabel->setStyleSheet("font-size: 24px;");trendLabel->setAlignment(Qt::AlignCenter);layout->addWidget(trendLabel);return card;
}// 創建狀態指示器
QWidget* Widget::createStatusIndicator(const QString& name, const QString& color)
{QWidget *indicator = new QWidget;indicator->setObjectName("statusIndicator");indicator->setStyleSheet("QWidget#statusIndicator {""background-color: white;""border: 1px solid " + color + ";""border-radius: 10px;""}");// 添加陰影效果QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect;shadow->setBlurRadius(8);shadow->setColor(QColor(0, 0, 0, 50));shadow->setOffset(0, 2);indicator->setGraphicsEffect(shadow);QVBoxLayout *layout = new QVBoxLayout(indicator);layout->setAlignment(Qt::AlignCenter);layout->setSpacing(10);// 狀態燈QLabel *light = new QLabel;light->setObjectName("statusLight");light->setFixedSize(30, 30);light->setStyleSheet("border-radius: 15px; background-color: gray;");layout->addWidget(light, 0, Qt::AlignHCenter);// 名稱標簽QLabel *nameLabel = new QLabel(name);nameLabel->setStyleSheet("font-size: 16px; color: " + color + "; font-weight: bold;");nameLabel->setAlignment(Qt::AlignCenter);layout->addWidget(nameLabel);return indicator;
}void Widget::recvData()
{while (udpSocket && udpSocket->hasPendingDatagrams()) {// 接收數據報QByteArray datagram;datagram.resize(udpSocket->pendingDatagramSize());QHostAddress sender;quint16 senderPort;udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);// 記錄接收信息QString message = QString("[%1] From %2:%3 - %4").arg(QDateTime::currentDateTime().toString("hh:mm:ss")).arg(sender.toString()).arg(senderPort).arg(QString::fromUtf8(datagram));// 在UI中顯示if (udpTextEdit) {udpTextEdit->append(message);}// 解析環境數據parseEnvironmentData(datagram);}
}void Widget::parseEnvironmentData(const QByteArray &data)
{QJsonParseError jsonError;QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);if (jsonError.error == QJsonParseError::NoError && jsonDoc.isObject()) {QJsonObject jsonObj = jsonDoc.object();// 提取溫度數據(使用0作為默認值)double temperature = jsonObj.contains("Temp") ? jsonObj["Temp"].toDouble() : 0.0;// 更新溫度數據卡片if (tempCard) {QLabel *tempValueLabel = tempCard->findChild<QLabel*>("valueLabel");if (tempValueLabel) {tempValueLabel->setText(QString::number(temperature, 'f', 1));// 更新趨勢指示器QLabel *tempTrendLabel = tempCard->findChild<QLabel*>("trendLabel");if (tempTrendLabel) {if (temperature > prevTemp) {tempTrendLabel->setText("↑");tempTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (temperature < prevTemp) {tempTrendLabel->setText("↓");tempTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {tempTrendLabel->setText("→");tempTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevTemp = temperature;}}}// 提取濕度數據double humidity = jsonObj.contains("Humi") ? jsonObj["Humi"].toDouble() : 0.0;if (humCard) {QLabel *humValueLabel = humCard->findChild<QLabel*>("valueLabel");if (humValueLabel) {humValueLabel->setText(QString::number(humidity, 'f', 0));// 更新趨勢指示器QLabel *humTrendLabel = humCard->findChild<QLabel*>("trendLabel");if (humTrendLabel) {if (humidity > prevHum) {humTrendLabel->setText("↑");humTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (humidity < prevHum) {humTrendLabel->setText("↓");humTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {humTrendLabel->setText("→");humTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevHum = humidity;}}}// 提取CO?數據int co2 = jsonObj.contains("CO2") ? jsonObj["CO2"].toInt() : 0;if (co2Card) {QLabel *co2ValueLabel = co2Card->findChild<QLabel*>("valueLabel");if (co2ValueLabel) {co2ValueLabel->setText(QString::number(co2));// 更新趨勢指示器QLabel *co2TrendLabel = co2Card->findChild<QLabel*>("trendLabel");if (co2TrendLabel) {if (co2 > prevCO2) {co2TrendLabel->setText("↑");co2TrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (co2 < prevCO2) {co2TrendLabel->setText("↓");co2TrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {co2TrendLabel->setText("→");co2TrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevCO2 = co2;}}}// 提取光照強度數據int lightIntensity = jsonObj.contains("lumen") ? jsonObj["lumen"].toInt() : 0;if (lightCard) {QLabel *lightValueLabel = lightCard->findChild<QLabel*>("valueLabel");if (lightValueLabel) {lightValueLabel->setText(QString::number(lightIntensity));// 更新趨勢指示器QLabel *lightTrendLabel = lightCard->findChild<QLabel*>("trendLabel");if (lightTrendLabel) {if (lightIntensity > prevLight) {lightTrendLabel->setText("↑");lightTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (lightIntensity < prevLight) {lightTrendLabel->setText("↓");lightTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {lightTrendLabel->setText("→");lightTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevLight = lightIntensity;}}}// 更新狀態指示器updateStatusIndicator(tempStatus, temperature, 20, 28);updateStatusIndicator(humStatus, humidity, 40, 60);updateStatusIndicator(co2Status, co2, 0, 800);updateStatusIndicator(lightStatus, lightIntensity, 80, 100);// 提取人體監測數據bool humanDetected = jsonObj.contains("HumanSensor") ? jsonObj["HumanSensor"].toBool() : false;// 更新人體監測指示燈if (humanDetected) {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: green;");humanMonitorStatus->setText("有人");} else {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: red;");humanMonitorStatus->setText("無人");}// 更新溫度數據用于存儲if (!temperatureData.isEmpty()) {temperatureData.pop_front();temperatureData.append(temperature);}// 如果是自動模式,執行自動控制邏輯if (autoMode) {// 溫度高于26度時開啟風扇if (temperature > 26.0 && !fanOn) {sendData("{\"Fan0\":true}");fanOn = true;fanButton->setText("開");fanButton->setChecked(true);}// 溫度低于25度時關閉風扇else if (temperature <= 25.0 && fanOn) {sendData("{\"Fan0\":false}");fanOn = false;fanButton->setText("關");fanButton->setChecked(false);}// 檢測到有人時開啟報警if (humanDetected && !alarmOn) {sendData("{\"Alarm\":true}");alarmOn = true;alarmButton->setText("開");alarmButton->setChecked(true);}// 無人時關閉報警else if (!humanDetected && alarmOn) {sendData("{\"Alarm\":false}");alarmOn = false;alarmButton->setText("關");alarmButton->setChecked(false);}// 光照強度小于100時開啟燈1if (lightIntensity < 80 && !light1On) {sendData("{\"light0\":true}");light1On = true;light1Button->setText("開");light1Button->setChecked(true);}// 光照強度大于150時關閉燈1else if (lightIntensity >= 90 && light1On) {sendData("{\"light0\":false}");light1On = false;light1Button->setText("關");light1Button->setChecked(false);}}}
}void Widget::updateStatusIndicator(QWidget* indicator, double value, double min, double max)
{QLabel *light = indicator ? indicator->findChild<QLabel*>("statusLight") : nullptr;if (!light) return;// 定義警告范圍(臨界范圍)的邊界double warnLow = min - 5; // 低于正常范圍5個單位視為臨界double warnHigh = max + 5; // 高于正常范圍5個單位視為臨界// 確定顏色(紅-黃-綠)if (value >= min && value <= max) {// 在正常范圍內,顯示綠色light->setStyleSheet("border-radius: 15px; background-color: green;");} else if ((value >= warnLow && value < min) || (value > max && value <= warnHigh)) {// 在臨界范圍內,顯示黃色light->setStyleSheet("border-radius: 15px; background-color: yellow;");} else {// 超出臨界范圍,顯示紅色light->setStyleSheet("border-radius: 15px; background-color: red;");}
}void Widget::sendData(const QString& data)
{if (udpSocket) {QByteArray byteArray = data.toUtf8();udpSocket->writeDatagram(byteArray, QHostAddress(targetIp), 9000);}
}void Widget::sendData(const char* data)
{if (udpSocket) {QByteArray byteArray(data);udpSocket->writeDatagram(byteArray, QHostAddress(targetIp), 9000);}
}void Widget::sendManualData()
{if (udpInput && !udpInput->text().isEmpty()) {sendData(udpInput->text());udpInput->clear();}
}void Widget::toggleLight1()
{if (!light1Button || !udpSocket) return;// 手動模式下才響應按鈕點擊if (!autoMode) {light1On = !light1On;// 更新按鈕顯示狀態light1Button->setText(light1On ? "開" : "關");light1Button->setChecked(light1On);// 發送控制命令sendData(light1On ? "{\"light0\":true}" : "{\"light0\":false}");QMessageBox::information(this, "燈1控制", light1On ? "燈1已開啟" : "燈1已關閉");}
}void Widget::toggleLight2()
{if (!light2Button || !udpSocket) return;// 手動模式下才響應按鈕點擊if (!autoMode) {light2On = !light2On;// 更新按鈕顯示狀態light2Button->setText(light2On ? "開" : "關");light2Button->setChecked(light2On);sendData(light2On ? "{\"light1\":true}" : "{\"light1\":false}");QMessageBox::information(this, "燈2控制", light2On ? "燈2已開啟" : "燈2已關閉");}
}void Widget::toggleFan()
{if (!fanButton || !udpSocket) return;// 手動模式下才響應按鈕點擊if (!autoMode) {fanOn = !fanOn;// 更新按鈕顯示狀態fanButton->setText(fanOn ? "開" : "關");fanButton->setChecked(fanOn);sendData(fanOn ? "{\"Fan0\":true}" : "{\"Fan0\":false}");QMessageBox::information(this, "風扇控制", fanOn ? "風扇已啟用" : "風扇已禁用");}
}void Widget::toggleAlarm()
{if (!alarmButton || !udpSocket) return;// 手動模式下才響應按鈕點擊if (!autoMode) {alarmOn = !alarmOn;// 更新按鈕顯示狀態alarmButton->setText(alarmOn ? "開" : "關");alarmButton->setChecked(alarmOn);// 發送控制命令sendData(alarmOn ? "{\"Alarm\":true}" : "{\"Alarm\":false}");QMessageBox::information(this, "報警控制", alarmOn ? "報警已啟用" : "報警已禁用");}
}// 切換到手動模式
void Widget::setManualMode()
{autoMode = false;manualButton->setStyleSheet("background-color: #42a5f5; color: white; border-radius: 5px; padding: 8px;");autoButton->setStyleSheet("background-color: #e0e0e0; color: #757575; border-radius: 5px; padding: 8px;");// 啟用手動控制按鈕light1Button->setEnabled(true);light2Button->setEnabled(true);fanButton->setEnabled(true);alarmButton->setEnabled(true);
}// 切換到自動模式
void Widget::setAutoMode()
{autoMode = true;manualButton->setStyleSheet("background-color: #e0e0e0; color: #757575; border-radius: 5px; padding: 8px;");autoButton->setStyleSheet("background-color: #42a5f5; color: white; border-radius: 5px; padding: 8px;");// 禁用手動控制按鈕light1Button->setEnabled(false);light2Button->setEnabled(false);fanButton->setEnabled(false);alarmButton->setEnabled(false);// 在狀態欄顯示切換到自動模式udpTextEdit->append(QString("[%1] 系統切換到自動模式").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
}QWidget* Widget::createLeftPanel()
{QWidget *panel = new QWidget;panel->setStyleSheet("background-color: white; border-radius: 8px;");QVBoxLayout *layout = new QVBoxLayout(panel);layout->setContentsMargins(15, 15, 15, 15);layout->setSpacing(15);// 標題區域QLabel *title = new QLabel("設備控制");title->setAlignment(Qt::AlignCenter);title->setStyleSheet("font-size: 22px; font-weight: bold; color: #42a5f5; margin-bottom: 15px;");layout->addWidget(title);// 添加手動/自動模式切換按鈕QWidget *modeWidget = new QWidget;QHBoxLayout *modeLayout = new QHBoxLayout(modeWidget);modeLayout->setContentsMargins(0, 0, 0, 0);modeGroup = new QButtonGroup(this);manualButton = new QPushButton("手動模式");manualButton->setCheckable(true);manualButton->setChecked(true); // 默認手動模式manualButton->setStyleSheet("background-color: #42a5f5; color: white; border-radius: 5px; padding: 8px;");connect(manualButton, SIGNAL(clicked()), this, SLOT(setManualMode()));autoButton = new QPushButton("自動模式");autoButton->setCheckable(true);autoButton->setStyleSheet("background-color: #e0e0e0; color: #757575; border-radius: 5px; padding: 8px;");connect(autoButton, SIGNAL(clicked()), this, SLOT(setAutoMode()));modeGroup->addButton(manualButton);modeGroup->addButton(autoButton);modeLayout->addWidget(manualButton);modeLayout->addWidget(autoButton);layout->addWidget(modeWidget);// ====== LED控制部分 ======QGroupBox *ledGroup = new QGroupBox("LED控制");ledGroup->setStyleSheet("QGroupBox { border: 1px solid #a3d1f5; border-radius: 8px; padding: 10px; }""QGroupBox::title { color: #42a5f5; font-weight: bold; font-size: 16px; }");QVBoxLayout *ledLayout = new QVBoxLayout(ledGroup);// LED開關選項QWidget *ledControl = new QWidget;QHBoxLayout *ledControlLayout = new QHBoxLayout(ledControl);ledControlLayout->setContentsMargins(0, 0, 0, 0);QLabel *ledSwitchLabel = new QLabel("燈1開關");ledSwitchLabel->setStyleSheet("font-size: 30px;");// LED開關按鈕light1Button = new QPushButton("關");light1Button->setCheckable(true);light1Button->setStyleSheet("QPushButton {"" background-color: #42a5f5;"" color: white;"" border-radius: 5px;"" padding: 6px;"" font-size: 30px;"" min-width: 50px;""}""QPushButton:checked { background-color: #66bb6a; }");connect(light1Button, SIGNAL(clicked()), this, SLOT(toggleLight1()));ledControlLayout->addWidget(ledSwitchLabel);ledControlLayout->addStretch();ledControlLayout->addWidget(light1Button);ledLayout->addWidget(ledControl);// 燈2開關QWidget *led2Control = new QWidget;QHBoxLayout *led2ControlLayout = new QHBoxLayout(led2Control);led2ControlLayout->setContentsMargins(0, 0, 0, 0);QLabel *led2SwitchLabel = new QLabel("燈2開關");led2SwitchLabel->setStyleSheet("font-size: 30px;");light2Button = new QPushButton("關");light2Button->setCheckable(true);light2Button->setStyleSheet("QPushButton {"" background-color: #42a5f5;"" color: white;"" border-radius: 5px;"" padding: 6px;"" font-size: 30px;"" min-width: 50px;""}""QPushButton:checked { background-color: #66bb6a; }");connect(light2Button, SIGNAL(clicked()), this, SLOT(toggleLight2()));led2ControlLayout->addWidget(led2SwitchLabel);led2ControlLayout->addStretch();led2ControlLayout->addWidget(light2Button);ledLayout->addWidget(led2Control);layout->addWidget(ledGroup);// ====== 風扇控制 ======QGroupBox *fanGroup = new QGroupBox("風扇控制");fanGroup->setStyleSheet("QGroupBox { border: 1px solid #a3d1f5; border-radius: 8px; padding: 10px; }""QGroupBox::title { color: #42a5f5; font-weight: bold; font-size: 16px; }");QVBoxLayout *fanLayout = new QVBoxLayout(fanGroup);// 風扇開關QWidget *fanControl = new QWidget;QHBoxLayout *fanControlLayout = new QHBoxLayout(fanControl);fanControlLayout->setContentsMargins(0, 0, 0, 0);QLabel *fanSwitchLabel = new QLabel("風扇開關");fanSwitchLabel->setStyleSheet("font-size: 30px;");fanButton = new QPushButton("關");fanButton->setCheckable(true);fanButton->setStyleSheet("QPushButton {"" background-color: #42a5f5;"" color: white;"" border-radius: 5px;"" padding: 6px;"" font-size: 30px;"" min-width: 50px;""}""QPushButton:checked { background-color: #66bb6a; }");connect(fanButton, SIGNAL(clicked()), this, SLOT(toggleFan()));fanControlLayout->addWidget(fanSwitchLabel);fanControlLayout->addStretch();fanControlLayout->addWidget(fanButton);fanLayout->addWidget(fanControl);layout->addWidget(fanGroup);// ====== 報警控制 ======QGroupBox *alarmGroup = new QGroupBox("報警控制");alarmGroup->setStyleSheet("QGroupBox { border: 1px solid #a3d1f5; border-radius: 8px; padding: 10px; }""QGroupBox::title { color: #42a5f5; font-weight: bold; font-size: 16px; }");QVBoxLayout *alarmLayout = new QVBoxLayout(alarmGroup);QWidget *alarmControl = new QWidget;QHBoxLayout *alarmControlLayout = new QHBoxLayout(alarmControl);alarmControlLayout->setContentsMargins(0, 0, 0, 0);QLabel *alarmSwitchLabel = new QLabel("報警開關");alarmSwitchLabel->setStyleSheet("font-size: 30px;");alarmButton = new QPushButton("關");alarmButton->setCheckable(true);alarmButton->setStyleSheet("QPushButton {"" background-color: #42a5f5;"" color: white;"" border-radius: 5px;"" padding: 6px;"" font-size: 30px;"" min-width: 50px;""}""QPushButton:checked { background-color: #66bb6a; }");connect(alarmButton, SIGNAL(clicked()), this, SLOT(toggleAlarm()));alarmControlLayout->addWidget(alarmSwitchLabel);alarmControlLayout->addStretch();alarmControlLayout->addWidget(alarmButton);alarmLayout->addWidget(alarmControl);layout->addWidget(alarmGroup);// ====== UDP手動發送區域 ======QGroupBox *udpGroup = new QGroupBox("UDP手動發送");udpGroup->setStyleSheet("QGroupBox { border: 1px solid #a3d1f5; border-radius: 8px; padding: 10px; }""QGroupBox::title { color: #42a5f5; font-weight: bold; font-size: 16px; }");QVBoxLayout *udpLayout = new QVBoxLayout(udpGroup);QHBoxLayout *inputLayout = new QHBoxLayout;udpInput = new QLineEdit;udpInput->setPlaceholderText("輸入要發送的數據");udpInput->setStyleSheet("font-size: 20px; padding: 8px;");QPushButton *sendButton = new QPushButton("發送");sendButton->setStyleSheet("QPushButton {"" background-color: #42a5f5;"" color: white;"" border-radius: 5px;"" padding: 8px;"" font-size: 20px;""}""QPushButton:hover { background-color: #1e88e5; }");connect(sendButton, SIGNAL(clicked()), this, SLOT(sendManualData()));inputLayout->addWidget(udpInput);inputLayout->addWidget(sendButton);udpLayout->addLayout(inputLayout);// UDP接收數據顯示udpTextEdit = new QTextEdit;udpTextEdit->setReadOnly(true);udpTextEdit->setStyleSheet("font-size: 14px; background-color: #f8f8f8; border: 1px solid #ddd;");udpLayout->addWidget(udpTextEdit);layout->addWidget(udpGroup);layout->addStretch();return panel;
}QWidget* Widget::createRightTopPanel()
{// 環境監測面板QWidget *panel = new QWidget;panel->setStyleSheet("background-color: #f0f8ff; border-radius: 8px; padding: 10px;");QGridLayout *layout = new QGridLayout(panel);layout->setContentsMargins(10, 10, 10, 10);layout->setHorizontalSpacing(15);layout->setVerticalSpacing(15);// 數據卡片tempCard = createDataCard("溫度", "°C", "#ff7043"); // 橙色layout->addWidget(tempCard, 0, 0);humCard = createDataCard("濕度", "%", "#42a5f5"); // 藍色layout->addWidget(humCard, 0, 1);co2Card = createDataCard("CO?", "ppm", "#66bb6a"); // 綠色layout->addWidget(co2Card, 0, 2);lightCard = createDataCard("光照", "lx", "#ffd700"); // 金色layout->addWidget(lightCard, 1, 0);// 人體監測部分QWidget *humanMonitorWidget = new QWidget;humanMonitorWidget->setObjectName("humanMonitorContainer");humanMonitorWidget->setStyleSheet("QWidget#humanMonitorContainer {""background-color: white;""border: 2px solid #9c27b0;""border-radius: 10px;""}");QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect;shadow->setBlurRadius(10);shadow->setColor(QColor(0, 0, 0, 70));shadow->setOffset(0, 2);humanMonitorWidget->setGraphicsEffect(shadow);QVBoxLayout *humanLayout = new QVBoxLayout(humanMonitorWidget);humanLayout->setAlignment(Qt::AlignCenter);humanLayout->setSpacing(8);// 指示燈humanMonitorLabel = new QLabel();humanMonitorLabel->setFixedSize(30, 30);humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: gray;");humanLayout->addWidget(humanMonitorLabel, 0, Qt::AlignHCenter);// 標題QLabel *humanTitle = new QLabel("人體監測");humanTitle->setStyleSheet("font-size: 18px; font-weight: bold; color: #9c27b0;");humanTitle->setAlignment(Qt::AlignCenter);humanLayout->addWidget(humanTitle);// 狀態humanMonitorStatus = new QLabel("無人");humanMonitorStatus->setObjectName("statusText");humanMonitorStatus->setStyleSheet("font-size: 16px; font-weight: bold;");humanMonitorStatus->setAlignment(Qt::AlignCenter);humanLayout->addWidget(humanMonitorStatus);// 添加人體監測到網格(橫跨兩列)layout->addWidget(humanMonitorWidget, 1, 1, 1, 2);return panel;
}QWidget* Widget::createRightBottomPanel()
{// 狀態監測面板QWidget *panel = new QWidget;panel->setStyleSheet("background-color: #f0f8ff; border-radius: 8px; padding: 15px;");QVBoxLayout *mainLayout = new QVBoxLayout(panel);mainLayout->setContentsMargins(10, 10, 10, 10);// 標題QLabel *title = new QLabel("環境狀態監測");title->setAlignment(Qt::AlignCenter);title->setStyleSheet("font-size: 22px; font-weight: bold; color: #3a4a6b;");mainLayout->addWidget(title);// 狀態燈網格布局QGridLayout *gridLayout = new QGridLayout;gridLayout->setHorizontalSpacing(20);gridLayout->setVerticalSpacing(20);gridLayout->setContentsMargins(20, 20, 20, 20);// 創建狀態指示器tempStatus = createStatusIndicator("溫度", "#ff7043"); // 橙色gridLayout->addWidget(tempStatus, 0, 0);humStatus = createStatusIndicator("濕度", "#42a5f5"); // 藍色gridLayout->addWidget(humStatus, 0, 1);co2Status = createStatusIndicator("CO?", "#66bb6a"); // 綠色gridLayout->addWidget(co2Status, 0, 2);lightStatus = createStatusIndicator("光照", "#ffd700"); // 金色gridLayout->addWidget(lightStatus, 1, 1);// 添加狀態燈到主布局QWidget *statusContainer = new QWidget;statusContainer->setLayout(gridLayout);mainLayout->addWidget(statusContainer);// 添加圖例說明QWidget *legendWidget = new QWidget;QHBoxLayout *legendLayout = new QHBoxLayout(legendWidget);legendLayout->setSpacing(15);QLabel *greenLegend = new QLabel;greenLegend->setFixedSize(20, 20);greenLegend->setStyleSheet("background-color: green; border-radius: 10px;");legendLayout->addWidget(greenLegend);legendLayout->addWidget(new QLabel("正常"));QLabel *yellowLegend = new QLabel;yellowLegend->setFixedSize(20, 20);yellowLegend->setStyleSheet("background-color: yellow; border-radius: 10px;");legendLayout->addWidget(yellowLegend);legendLayout->addWidget(new QLabel("臨界"));QLabel *redLegend = new QLabel;redLegend->setFixedSize(20, 20);redLegend->setStyleSheet("background-color: red; border-radius: 10px;");legendLayout->addWidget(redLegend);legendLayout->addWidget(new QLabel("異常"));legendLayout->addStretch();mainLayout->addWidget(legendWidget, 0, Qt::AlignCenter);return panel;
}void Widget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event); // 不使用event參數// 在這里添加繪制代碼QWidget::paintEvent(event);
}
3. HI3861主程序(部分)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "hi_io.h" // 海思 Pegasus SDK:IoT硬件設備操作接口-IO
#include "hi_time.h"
#include "hi_watchdog.h"
#include "hi_gpio.h"
// 海思 Pegasus SDK:IoT硬件設備操作接口-ADC
// Analog-to-digital conversion (ADC)
// 提供8個ADC通道,通道7為參考電壓,不能adc轉換
#include "hi_adc.h"
#include "hi_watchdog.h"
// 定義一個宏,用于標識ADC6通道,GPIO13管腳復用
#define LIGHT_SENSOR_CHAN_NAME HI_ADC_CHANNEL_3
#define LuMen_SENSOR_CHAN_NAME HI_ADC_CHANNEL_6#include "m_wifi.h"
#include "m_mqtt.h"#include "lwip/netifapi.h"
#include "lwip/sockets.h"
#include "lwip/api_shell.h"#include "m_key.h"
#include "m_led.h"
#include "m_dht11.h"
#include "oled_ssd1306.h" // OLED顯示屏簡化版驅動接口文件
#include "oled_fonts.h"//控制任務
#define WIFI_SSID "iQOO11"
#define WIFI_PAWD "88888888"#define SERVER_IP_ADDR "82.157.254.205" //broker.emqx.io //54.244.173.190
#define SERVER_IP_PORT 1883
#define MQTT_TOPIC_SUB "attributes/push"
#define MQTT_TOPIC_PUB "attributes"
#define TASK_INIT_TIME 2 // s
#define MQTT_RECV_TASK_TIME (200 * 1000) // us#define MQTT_USERNAME "tpqacbx5s052il1p"
#define MQTT_PASSWORD "kqy3EoVZUZ"#define BEEP_PIN HI_IO_NAME_GPIO_10 // for hispark_pegasus
#define BEEP_FUN_GPIO HI_IO_FUNC_GPIO_10_GPIO
#define HUMANSENSOR_PIN HI_IO_NAME_GPIO_14
#define HUMANSENSOR_FUN_GPIO HI_IO_FUNC_GPIO_14_GPIO
#define DUOJI_PIN HI_IO_NAME_GPIO_2
#define DUOJI_FUN_GPIO HI_IO_FUNC_GPIO_2_GPIO
static volatile hi_gpio_value g_duojiPinValue = 0;//-------------------------------------------------------------------------------------
int result;
uint8_t temp;
uint8_t humi;
uint8_t co2;
uint8_t Lum;
uint8_t renne;
char recvBuf[512];
int human_flag = 0;
hi_gpio_value human_sensor_val=HI_GPIO_VALUE0;int socket_fd = 0 ; // 套接字文件描述符
int result; // 函數返回值
char sendBuf[512]; // 發送緩沖區
struct sockaddr_in client_addr;
socklen_t addr_length; osThreadId_t mqtt_send_task_id; // mqtt訂閱數據任務
osThreadId_t mqtt_recv_task_id; // mqtt發布數據任務//人傳感器初始話
void human_sensor_init(void)
{hi_gpio_init();//初始化gpio管腳hi_io_set_pull(HUMANSENSOR_PIN,HI_IO_PULL_DOWN);//設置管腳下拉hi_io_set_func(HUMANSENSOR_PIN,HUMANSENSOR_FUN_GPIO);//設置復用功能為通用輸入輸出功能hi_gpio_set_dir(HUMANSENSOR_PIN,HI_GPIO_DIR_IN);//設置管腳為輸出
}void udp_judge()
{if(strcmp(recvBuf,"{\"light0\":true}")==0)//大棚燈0 開{printf("--- 大棚燈0 開 ---\r\n");led_ctl(LED_BLUE_GPIO, HI_GPIO_VALUE1);}else if(strcmp(recvBuf,"{\"light0\":false}")==0)//大棚燈0 關{printf("--- 大棚燈0 關 ---\r\n");led_ctl(LED_BLUE_GPIO, HI_GPIO_VALUE0);}if(strcmp(recvBuf,"{\"light1\":true}")==0)//大棚燈1 開{printf("--- 大棚燈1 開 ---\r\n");led_ctl(LED_GREEN_GPIO, HI_GPIO_VALUE1);}else if(strcmp(recvBuf,"{\"light1\":false}")==0)//大棚燈1 關{printf("--- 大棚燈1 關 ---\r\n");led_ctl(LED_GREEN_GPIO, HI_GPIO_VALUE0);}if(strcmp(recvBuf,"{\"Alarm\":true}")==0)//報警 開{printf("--- 報警 開 ---\r\n");led_ctl(LED_RED_GPIO, HI_GPIO_VALUE1);led_ctl(BEEP_PIN, HI_GPIO_VALUE0);//開蜂鳴器}else if(strcmp(recvBuf,"{\"Alarm\":false}")==0)//報警 關{printf("--- 報警 關 ---\r\n");led_ctl(LED_RED_GPIO, HI_GPIO_VALUE0);led_ctl(BEEP_PIN, HI_GPIO_VALUE1);//關蜂鳴器}if(strcmp(recvBuf,"{\"Fan0\":true}")==0)//風扇 開{printf("--- 風扇 開 ---\r\n");g_duojiPinValue=1;}else if(strcmp(recvBuf,"{\"Fan0\":false}")==0)//風扇 關{printf("--- 風扇 關 ---\r\n");g_duojiPinValue=0;}
}
//訂閱的回調函數
int8_t mqtt_sub_payload_callback(unsigned char *topic, char *payload)
{printf("[info] topic:[%s] recv<== **%s**\r\n", topic, payload);//判斷訂閱收到的消息內容/** 藍色燈代表 大棚燈0 ,綠色燈代表大棚燈1,紅色燈代表報警*/if(strcmp(payload,"{\"light0\":true}")==0)//大棚燈0 開{printf("--- 大棚燈0 開 ---\r\n");led_ctl(LED_BLUE_GPIO, HI_GPIO_VALUE1);}else if(strcmp(payload,"{\"light0\":false}")==0)//大棚燈0 關{printf("--- 大棚燈0 關 ---\r\n");led_ctl(LED_BLUE_GPIO, HI_GPIO_VALUE0);}if(strcmp(payload,"{\"light1\":true}")==0)//大棚燈1 開{printf("--- 大棚燈1 開 ---\r\n");led_ctl(LED_GREEN_GPIO, HI_GPIO_VALUE1);}else if(strcmp(payload,"{\"light1\":false}")==0)//大棚燈1 關{printf("--- 大棚燈1 關 ---\r\n");led_ctl(LED_GREEN_GPIO, HI_GPIO_VALUE0);}if(strcmp(payload,"{\"Alarm\":true}")==0)//報警 開{printf("--- 報警 開 ---\r\n");led_ctl(LED_RED_GPIO, HI_GPIO_VALUE1);led_ctl(BEEP_PIN, HI_GPIO_VALUE0);//開蜂鳴器}else if(strcmp(payload,"{\"Alarm\":false}")==0)//報警 關{printf("--- 報警 關 ---\r\n");led_ctl(LED_RED_GPIO, HI_GPIO_VALUE0);led_ctl(BEEP_PIN, HI_GPIO_VALUE1);//關蜂鳴器}if(strcmp(payload,"{\"Fan0\":true}")==0)//風扇 開{printf("--- 風扇 開 ---\r\n");g_duojiPinValue=1;}else if(strcmp(payload,"{\"Fan0\":false}")==0)//風扇 關{printf("--- 風扇 關 ---\r\n");g_duojiPinValue=0;}}//接收任務
void mqtt_recv_task(void)
{while (1) {MQTTClient_sub();usleep(MQTT_RECV_TASK_TIME);}
}//任務1---------------------------------------------------------------------------
void mqtt_send_task(void)
{uint8_t res=0;// 連接Wifiif (WiFi_connectHotspots(WIFI_SSID, WIFI_PAWD) != WIFI_SUCCESS) {printf("[error] WiFi_connectHotspots\r\n");}// 連接MQTT服務器if (MQTTClient_connectServer(SERVER_IP_ADDR, SERVER_IP_PORT) != 0) {printf("[error] MQTTClient_connectServer\r\n");} else {printf("[success] MQTTClient_connectServer\r\n");}sleep(TASK_INIT_TIME);// 初始化MQTT客戶端if (MQTTClient_init("mqtt_client_123", MQTT_USERNAME, MQTT_PASSWORD) != 0) {printf("[error] MQTTClient_init\r\n");} else {printf("[success] MQTTClient_init\r\n");}sleep(TASK_INIT_TIME);// 訂閱Topic(增加重試)
int retry = 0;
while (MQTTClient_subscribe(MQTT_TOPIC_SUB) != 0 && retry < 3) {printf("[retry %d] MQTTClient_subscribe...\n", retry+1);sleep(1);retry++;
}
if (retry >= 3) {printf("[error] MQTT訂閱最終失敗!\n");
} else {printf("[success] MQTT訂閱成功!\n");
}sleep(TASK_INIT_TIME); osThreadAttr_t options;options.name = "mqtt_recv_task";options.attr_bits = 0;options.cb_mem = NULL;options.cb_size = 0;options.stack_mem = NULL;options.stack_size = 1024*5;options.priority = osPriorityNormal;mqtt_recv_task_id = osThreadNew((osThreadFunc_t)mqtt_recv_task, NULL, &options);if (mqtt_recv_task_id != NULL) {printf("ID = %d, Create mqtt_recv_task_id is OK!\r\n", mqtt_recv_task_id);}while (1) {//MQTTClient_pub(MQTT_TOPIC_PUB, "hello world!!!", strlen("hello world!!!"));// global_temp=temp;// global_humi=humi;DHT11_Read_Data(&temp,&humi);printf("溫度=%d°C 濕度=%d%%RH\r\n",temp,humi);sleep(TASK_INIT_TIME);}
}
//任務2-------------------------------------------------------------------------------
static void AdcTask(void *arg)
{// 工作循環,每隔100ms獲取一次ADC6通道的值while (1) {// 保存ADC6通道的值unsigned short data = 0;// 獲取ADC6通道的值// 讀數速率較慢,請避免在中斷中使用if (hi_adc_read(LIGHT_SENSOR_CHAN_NAME, &data, HI_ADC_EQU_MODEL_4, HI_ADC_CUR_BAIS_DEFAULT, 0)== HI_ERR_SUCCESS) {// LIGHT_SENSOR_CHAN_NAME表示ADC4通道// HI_ADC_EQU_MODEL_4表示采樣數據平均濾波處理,平均次數4次// HI_ADC_CUR_BAIS_DEFAULT表示模數轉換采用默認電壓基準// 0表示從配置采樣到啟動采樣的延時時間計數為0// 返回值HI_ERR_SUCCESS表示成功// 打印ADC6通道的值//printf("ADC_VALUE = %d\n", (unsigned int)data);// 打印測量電壓的值//printf("電壓 = %.2f V\n", data*1.8*4/4096);}co2=(unsigned int)data;// 等待100msosDelay(10);hi_watchdog_feed();}
}
//任務3
void *HUMANSENSORTASK(void *arg)
{human_sensor_init();//hi_watchdog_enable();while(1){hi_gpio_get_input_val(HUMANSENSOR_PIN, &human_sensor_val);//讓LED3_PIN輸出高電平,led3亮if(human_sensor_val == HI_GPIO_VALUE1){osDelay(20);hi_gpio_get_input_val(HUMANSENSOR_PIN,&human_sensor_val);if(human_sensor_val == HI_GPIO_VALUE1){//printf("檢測到有人\n");}}if(human_flag != human_sensor_val){human_flag = human_sensor_val;if(human_flag == 0){renne=0;printf("檢測到無人了!!!\n");}else{renne=1;printf("檢測到有人了!!!\n");}}osDelay(100);//hi_watchdog_feed();}
}
//任務4
void duoji_init(void)
{hi_gpio_init();//初始化gpio管腳hi_io_set_func(DUOJI_PIN,DUOJI_FUN_GPIO);//設置DUOJI_PIN 復用功能為通用輸入輸出功能hi_gpio_set_dir(DUOJI_PIN,HI_GPIO_DIR_OUT);//設置DUOJI_PIN管腳為輸出
}
void *DUOJITASK(void *arg)
{int count = 0;uint16_t m_time = 1000;uint16_t l_time = 1500;uint16_t s_time = 4000;duoji_init();while(1)// 實現舵機旋轉{count++;if(count == 1000){count = 0;usleep(100);}if(g_duojiPinValue == HI_GPIO_VALUE1){hi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE1);//讓DUOJI_PIN輸出高電平hi_udelay(m_time);//延時1mshi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE0);//讓DUOJI_PIN輸出低電平hi_udelay(20000-m_time);//延時1ms}else if(g_duojiPinValue = HI_GPIO_VALUE0){//hi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE1);//讓DUOJI_PIN輸出高電平//hi_udelay(l_time);//延時1.5ms//hi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE0);//讓DUOJI_PIN輸出低電平//hi_udelay(20000-l_time);//延時1.5mshi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE1);//讓DUOJI_PIN輸出高電平hi_udelay(s_time);//延時3mshi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE0);//讓DUOJI_PIN輸出低電平hi_udelay(20000-s_time);//延時3ms}}return NULL;
}//任務五
void thread_func_udp(void *arg)
{// int socket_fd=0; // 函數返回值// int result; // 函數返回值char ip_buf[16]=""; // IP地址存儲緩沖區(IPv4最大15字符+結束符)hi_watchdog_enable();WiFi_connectHotspots(WIFI_SSID,WIFI_PAWD);socket_fd = socket(AF_INET, SOCK_DGRAM, 0);//AF_INET 是ipv4協議家族,SOCK_DGRAM 代表的是udp協議,0,協議棧自動識別協議類型。if((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){printf("creat socket failed!\r\n");return ;}struct sockaddr_in my_addr;my_addr.sin_family = AF_INET; // IPv4協議族my_addr.sin_port = htons(9000); // 端口號(主機轉網絡字節序)my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 綁定所有本地接口result = bind(socket_fd, (struct sockaddr*)&my_addr, sizeof(my_addr));if(result != 0){perror("bind");return 0;}//struct sockaddr_in client_addr;addr_length=sizeof(client_addr);while(1){//printf("%d2",socket_fd);memset(recvBuf, 0, sizeof(recvBuf)); // 清空接收緩沖區初始化地址結構體長度/* 接收數據(阻塞式) */result = recvfrom(socket_fd, recvBuf, sizeof(recvBuf)-1, 0,(struct sockaddr*)&client_addr, &addr_length);memset(ip_buf, 0, sizeof(ip_buf));inet_ntop(AF_INET, &(client_addr.sin_addr), ip_buf, INET_ADDRSTRLEN);printf("收到來自ip地址為:%s的數據,", ip_buf);printf("收到數據字節數:%d, recvBuf:%s\r\n", result, recvBuf);// if (result > 0)// {// printf("收到數據字節數:%d, recvBuf:%s\r\n", result, recvBuf);// 構造包含溫濕度的響應數據// char response[256] = {0};// snprintf(response, sizeof(response), // "{\"cmd\":\"%s\",\"temp\":%d,\"humi\":%d}", // recvBuf, global_temp, global_humi);// 發送數據到客戶端 [修正] 移除多余的length)// result = sendto(socket_fd, response, strlen(response), 0, // (struct sockaddr *)&client_addr, addr_length);// if (result > 0)// {// [修正] 輸出正確的發送內容// printf("發送成功字節數:%d, sendData:%s\r\n", result, response);// }//[優化] 只在接收到數據時處理命令// }udp_judge();/* 解析客戶端地址信息 */osDelay(100);hi_watchdog_feed(); }
}//任務六
static void lightAdcTask(void *arg)
{while (1) {unsigned short lumen = 0;//光照強度 讀ADC4通道if (hi_adc_read(LuMen_SENSOR_CHAN_NAME, &lumen, HI_ADC_EQU_MODEL_4, HI_ADC_CUR_BAIS_DEFAULT, 0)== HI_ERR_SUCCESS) {// LIGHT_SENSOR_CHAN_NAME表示ADC4通道// HI_ADC_EQU_MODEL_4表示采樣數據平均濾波處理,平均次數4次// HI_ADC_CUR_BAIS_DEFAULT表示模數轉換采用默認電壓基準// 0表示從配置采樣到啟動采樣的延時時間計數為0// 返回值HI_ERR_SUCCESS表示成功// 打印ADC6通道的值// printf("ADC_VALUE = %d\n", (unsigned int)lumen);// 打印測量電壓的值// printf("電壓 = %.2f V\n", lumen*1.8*4/4096);}float adc;adc=(float)lumen*(3.3/4096); Lum=3100-adc*1000;//printf("lumen = %d ", (unsigned int)lumen);//printf("adc = %f ", adc);//printf("LUM = %d ", Lum);// 等待100msosDelay(10);hi_watchdog_feed();}}//任務7
void Lcd_Task(void *arg)
{OledInit();OledFillScreen(0x00);//OledShowString(0,0,"a",FONT8x16);
while(1){char Temp[10];// char Humi[10];// char LUM[10];// char CO2[10];snprintf(Temp, sizeof(Temp), "Temp: %d C", temp);// snprintf(Humi, sizeof(Humi), "Humi: %d %%", humi);// snprintf(LUM, sizeof(LUM), "Light: %d lux", Lum);// snprintf(CO2, sizeof(CO2), "CO2: %d ppm", co2);OledFillScreen(0x00);OledShowString(0, 0, "Temp", FONT8x16);OledShowChar(2, 2, Temp, FONT8x16); // OledShowChinese2(29 + 0, 3, 2); // OledShowChinese2(29 + 18, 3, 1); // OledShowChar(29 + 36, 3, Humi, FONT6x8); // OledShowChinese2(29 + 0, 5, 3); // OledShowChinese2(29 + 18, 5, 4); // OledShowChar(29 + 36, 5, LUM, FONT6x8); // OledShowChinese2(29 + 0, 7, 5); // OledShowChinese2(29 + 18, 7, 6); // OledShowChinese2(29 + 36, 7, 7); // OledShowChinese2(29 + 54, 7, 8); // OledShowChinese2(29 + 72, 7, 9); // OledShowChinese2(29 + 90, 7, 1); // OledShowChar(29 + 108, 7, CO2, FONT6x8); }}//-----------------------------------------------------------------------------
//任務創建1 mqtt的發送
void wifi_mqtt_task_create(void)
{osThreadAttr_t taskOptions;p_MQTTClient_sub_callback = &mqtt_sub_payload_callback;taskOptions.name = "mqttTask"; // 任務的名字taskOptions.attr_bits = 0; // 屬性位taskOptions.cb_mem = NULL; // 堆空間地址taskOptions.cb_size = 0; // 堆空間大小taskOptions.stack_mem = NULL; // 棧空間地址taskOptions.stack_size = 1024*5; // 棧空間大小 單位:字節taskOptions.priority = osPriorityNormal; // 任務的優先級mqtt_send_task_id = osThreadNew((osThreadFunc_t)mqtt_send_task, NULL, &taskOptions); // 創建任務if (mqtt_send_task_id != NULL){printf("ID = %d, mqtt_send_task_id Create OK!\n", mqtt_send_task_id);}
}
//任務創建2 adc
void AdcDemo(void)
{// 定義線程屬性osThreadAttr_t attr;attr.name = "AdcTask";attr.attr_bits = 0U;attr.cb_mem = NULL;attr.cb_size = 0U;attr.stack_mem = NULL;attr.stack_size = 4096;attr.priority = osPriorityNormal;// 創建線程if (osThreadNew(AdcTask, NULL, &attr) == NULL) {printf("[AdcDemo] Falied to create ADCLightTask!\n");}
}
//任務創建3
static void human_sensor_demo(void)
{//定義線程屬性osThreadAttr_t attrr;attrr.name = "HUMANSENSORTASK";attrr.stack_size = 10*1024;attrr.priority = osPriorityNormal;if (osThreadNew((osThreadFunc_t)HUMANSENSORTASK, NULL, &attrr) == NULL) {printf("[ThreadTestTask] Falied to create Task01!\n");}
}
//任務創建4
static void duoji_demo(void)
{//定義線程屬性osThreadAttr_t attrrr;attrrr.name = "DUOJITASK";attrrr.stack_size = 10*1024;attrrr.priority = osPriorityNormal;if (osThreadNew((osThreadFunc_t)DUOJITASK, NULL, &attrrr) == NULL) {printf("[ThreadTestTask] Falied to create Task!\n");}
}//任務創建5
static void UDPTask(void)
{//定義線程屬性osThreadAttr_t attrrrr;attrrrr.name = "thread_func_udp";attrrrr.stack_size = 10*1024;attrrrr.priority = osPriorityNormal;if (osThreadNew((osThreadFunc_t)thread_func_udp, NULL, &attrrrr) == NULL) {printf("[ThreadTestTask] Falied to create Task01!\n");}
}//任務創建6
static void lightTask(void)
{//定義線程屬性osThreadAttr_t attrrrrr;attrrrrr.name = "lightAdcTask";attrrrrr.stack_size = 10*1024;attrrrrr.priority = osPriorityNormal;if (osThreadNew((osThreadFunc_t)lightAdcTask, NULL, &attrrrrr) == NULL) {printf("[ThreadTestTask] Falied to create Task01!\n");}
}//任務創建7
static void LCDTask(void)
{//定義線程屬性osThreadAttr_t attrrrrrr;attrrrrrr.name = "Lcd_Task";attrrrrrr.stack_size = 10*1024;attrrrrrr.priority = osPriorityNormal;if (osThreadNew((osThreadFunc_t)Lcd_Task, NULL, &attrrrrrr) == NULL) {printf("[ThreadTestTask] Falied to create Task01!\n");}
}/*** @description: 初始化并創建任務* @param {*}* @return {*}*/
static void template_demo(void)
{human_sensor_init();key_init();led_init();printf("WIFI MQTT實驗\r\n");wifi_mqtt_task_create();//任務1創建AdcDemo();//任務2創建human_sensor_demo();//任務3創建duoji_demo();//任務4創建UDPTask();lightTask();LCDTask();
}
SYS_RUN(template_demo);
??七、應用價值與創新突破??
?
1. 技術創新突破點??
- ??雙模無縫切換??:手動操作可臨時覆蓋自動策略,滿足緊急干預需求
- ??設備聯動引擎??:實現"光照不足→自動補光+溫度調節"的復合響應
- ??設備聯動??:采用QT控制開發板時云平臺圖標隨QT同步變化(前提是在云平臺將設備組件屬性改為共享模式)?
-
??QT界面:采用分塊布局,左側為控制面板,右側為監測面板,布局合理
- ?數據分析:舍棄折線圖形式,采用對比數據顯示箭頭表示數據變化趨勢、三態指示燈表示數據異常情況?
?八、寫在最后
1. 注意事項
a. 編寫主程序時需要注意變量的宏定義和引用問題,例如將UDP上傳放在按鍵中斷中,在主程序的任務中接收溫濕度,需要確定變量類型。
b. 值得注意的是,在使用云平臺控制開發板時我們打開MQTTX軟件觀察,發現MQTTX反復斷開后重連,起初我們認為是網絡連接不穩定,后來排除后發現云平臺每次只能和MQTTX或者開發板其一通信,兩者同時使用會產生沖突。
?在整個項目中出現了各個部分的bug包括主程序中線程間的占用問題、網絡連接問題、QT的UDP通信問題,甚至在MQTT無法連接時進行各種排查,發現是博主的隊友云平臺賬號被封號。。。。但是最后的完成度還是非常高,有興趣的朋友可以嘗試使用其他外設,添加語音、攝像頭、人臉識別,甚至接入deepseek,都是可以嘗試的方向。