嵌入式項目:基于QT與Hi3861的物聯網智能大棚集成控制系統

關鍵詞:MQTT、物聯網、QT、網絡連接、遠程控制

一、系統概述

本系統是一套完整的智能大棚監控解決方案,由兩部分構成:

  1. 基于Hi3861的嵌入式硬件系統(負責環境數據采集和設備控制)
  2. 基于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,都是可以嘗試的方向。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86433.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86433.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86433.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

揭開 Git 裸倉庫的神秘面紗:`git clone --mirror` 詳解與使用指南

大家好&#xff01;在使用 Git 進行版本控制時&#xff0c;我們最熟悉的莫過于那些帶有工作目錄的本地倉庫了——我們在里面編輯文件、提交代碼&#xff0c;然后推送到遠程倉庫。但有時候&#xff0c;我們可能會遇到一種特殊的倉庫&#xff1a;裸倉庫&#xff08;Bare Reposito…

opensuse安裝rabbitmq

您好&#xff01;安裝 RabbitMQ 消息隊列是一個非常棒的選擇&#xff0c;它是許多現代應用架構中的核心組件。 在 openSUSE Tumbleweed 上安裝 RabbitMQ 主要有兩種流行的方式&#xff1a;一種是使用系統的包管理器 zypper&#xff0c;另一種是使用 Docker 容器。我將為您詳細…

超詳細YOLOv8/11圖像菜品分類全程概述:環境、數據準備、訓練、驗證/預測、onnx部署(c++/python)詳解

文章目錄 一、環境準備二、數據準備三、訓練四、驗證與預測五、模型部署 一、環境準備 我的都是在Linux系統下&#xff0c;訓練部署的&#xff1b;模型訓練之前&#xff0c;需要配置好環境&#xff0c;Anaconda、顯卡驅動、cuda、cudnn、pytorch等&#xff1b; 參考&#xff1…

JUC:4.線程常見操作與兩階段終止模式

在線程中&#xff0c;wait()、join()、sleep()三個方法都是進行阻塞的方法。對應可以使用interrupt()方法進行打斷&#xff0c;被打斷后線程會拋出打斷異常&#xff0c;但是不會修改IsInterrupt&#xff0c;也就是此時去調用IsInterrupted()方法后獲得的實際上是false。 而當線…

分布式session解決方案

在實際項目中&#xff0c;前臺代碼部署在nginx中&#xff0c;后臺服務內嵌了tomcat運行在不同的節點中&#xff0c;常見的架構如下&#xff1a; 在上述架構中&#xff0c;nginx轉發前臺請求&#xff0c;第一次登錄后&#xff0c;將用戶登錄信息寫入到一臺服務session中&#xf…

UDP 緩沖區

UDP 有接收緩沖區&#xff0c;沒有發送緩沖區 引申問題 1、為什么沒有發送緩沖區&#xff1f; 直接引用原文 “因為 UDP 是不可靠的&#xff0c;它不必保存應用進程的數據拷貝&#xff0c;因此無需一個真正的發送緩沖區” 2、沒有發送緩沖區的情況下&#xff0c;sendto 的數…

解密 C++ 中的左值(lvalue)與右值(rvalue)的核心內容

在 C 中&#xff0c;表達式&#xff08;expression&#xff09; 可以被歸類為左值或右值。最簡單的理解方式是&#xff1a; 左值&#xff08;lvalue&#xff09;&#xff1a; 能放在賦值號 左邊的表達式&#xff0c;通常表示一個有名字、有內存地址、可以持續存在的對象。你可…

MATLAB(2)選擇結構

選擇結構又可以叫做分支結構&#xff0c;它根據給定的條件是否成立&#xff0c;決定程序運行的方向。在不同的條件下執行不同的操作。 MATLAB可以用來實現選擇結構的語句有三種&#xff1a;if語句、switch語句、try語句。 一.if語句 1.if語句 1.1條件為矩陣的情況 if語句的…

Ehcache、Caffeine、Spring Cache、Redis、J2Cache、Memcached 和 Guava Cache 的主要區別

主流緩存技術 Ehcache、Caffeine、Spring Cache、Redis、J2Cache、Memcached 和 Guava Cache 的主要區別&#xff0c;涵蓋其架構、功能、適用場景和優缺點等方面&#xff1a; Ehcache 類型: 本地緩存&#xff08;JVM 內存緩存&#xff09; 特點: 輕量級&#xff0c;運行在 JV…

谷歌瀏覽器截圖全屏擴展程序

以下是一些支持跟隨鼠標滾輪滾動截圖的谷歌全屏截圖擴展程序插件&#xff1a; GoFullPage&#xff1a;這是一款專門截取整個網頁的截圖插件。安裝后&#xff0c;點擊瀏覽器右上角的圖標或使用快捷鍵AltShiftP&#xff0c;插件就會自動開始滾動并捕獲當前訪問的網站&#xff0c…

專線服務器具體是指什么?

專線服務器主要是指在互聯網或者是局域網中&#xff0c;為特定用戶或者是應用程序所提供的專用服務器設備&#xff0c;專線服務器與傳統的共享服務器相比較來說&#xff0c;有著更高的安全性和更為穩定的網絡連接&#xff0c;下面我們就來共同了解一下專線服務器的具體內容吧&a…

Jenkins JNLP與SSH節點連接方式對比及連接斷開問題解決方案

一、JNLP vs SSH 連接方式優缺點對比 對比維度JNLP&#xff08;Java Web Start&#xff09;SSH&#xff08;Secure Shell&#xff09;核心原理代理節點主動連接Jenkins主節點&#xff0c;通過加密通道通信&#xff0c;支持動態資源分配。Jenkins通過SSH協議遠程登錄代理節點執…

Git - Commit命令

git commit 是 Git 版本控制系統中核心的提交命令&#xff0c;用于將暫存區&#xff08;Stage/Index&#xff09;中的修改&#xff08;或新增/刪除的文件&#xff09;永久記錄到本地倉庫&#xff08;Repository&#xff09;&#xff0c;生成一個新的提交記錄&#xff08;Commit…

Android System WebView Canary:探索前沿,體驗最新功能

在移動互聯網時代&#xff0c;WebView作為Android系統的核心組件之一&#xff0c;承擔著在原生應用中顯示Web內容的重要任務。它不僅為用戶提供了便捷的網頁瀏覽體驗&#xff0c;還為開發者提供了強大的混合式開發能力。Android System WebView Canary&#xff08;金絲雀版本&a…

kubernetes架構原理

目錄 一. 為什么需要 Kubernetes 1. 對于開發人員 2. 對于運維人員 3. Kubernetes 帶來的挑戰 二. Kubernetes 架構解析 1. master 節點的組件 2. Node 節點包含的組件 3. kubernetes網絡插件 三. kubeadm塊速安裝kubernetes集群 1. 基礎環境準備(此步驟在三個節點都執…

服務器的安裝與安全設置 域環境的搭建和管理 Windows基本配置 網絡服務常用網絡命令的應用 安全管理Windows Server 2019

高等職業教育計算機網絡技術專業實訓指導書 2025年目 錄 實訓的目的和意義 實訓的具體目標及主要內容 實訓完成后需要提交的內容 項目一 服務器的安裝與安全設置 項目二 域環境的搭建和管理 項目三 Windows基本配置 項目四 網絡服務 項目五 常用網絡命令的應用 項目六…

Springcloud解決jar包運行時無法拉取nacos遠程配置文件

問題描述 springcloud微服務&#xff0c;在idea中運行代碼&#xff0c;能夠正常拉去nacos上的配置文件&#xff0c;打包后&#xff0c;通過jar包啟動 java -jar xxx.jar&#xff0c;出現錯誤&#xff1a;java.nio.charset.MalformedlnputException: Input length 1 問題原因…

【Leetcode刷題隨筆】01. 兩數之和

1. 題目描述 給定一個整數數組 nums 和一個目標值 target&#xff0c;請你在該數組中找出和為目標值的那 兩個 整數&#xff0c;并返回他們的數組下標。 你可以假設每種輸入只會對應一個答案。但是&#xff0c;數組中同一個元素不能使用兩遍。 示例: 給定 nums [2, 7, 11,…

【機器學習深度學習】多層神經網絡的構成

目錄 一、神經網絡模型的結構化組成方式 1. 最底層&#xff1a;神經網絡模型 (Model) 2. 中間層&#xff1a;單個神經網絡層 (Layer) 3. 最頂層&#xff1a;訓練參數的細節 (Parameters & Variables) 二、關鍵理解要點 三、類比理解 場景一&#xff1a;工廠運作 場…

設計模式:揭秘Java原型模式——讓復雜對象的創建不再復雜

原型模式 原型模式介紹 定義: 原型模式(Prototype Design Pattern)用一個已經創建的實例作為原型&#xff0c;通過復制該原型對象來創建一個和原型對象相同的新對象。 西游記中的孫悟空&#xff0c;拔毛變小猴&#xff0c;孫悟空這種根據自己的形狀復制出多個身外化身的技巧&…