基于STM32的智慧农业温室环境监测与调控系统设计
项目开发背景
随着现代农业向集约化、智能化方向快速发展,传统温室管理模式面临诸多挑战。人工监测环境参数(如温湿度、土壤墒情、光照)效率低下且难以实现24小时精准控制,依赖经验判断易导致调控滞后或失误,造成作物生长环境波动大、资源浪费(如水肥过量)甚至减产风险。尤其在极端天气频发和劳动力成本上升的背景下,亟需通过技术手段提升温室管理的自动化与科学化水平。
物联网技术与嵌入式系统的成熟为解决上述问题提供了有效路径。基于STM32F103C8T6微控制器的智慧农业系统,通过集成多类型环境传感器(DHT11、YL-69、BH1750),实现对温室关键参数的实时精准采集;结合阈值逻辑自动控制通风、灌溉、补光设备,确保作物始终处于适宜生长环境。同时,利用ESP8266 WiFi模块将数据上传至华为云物联网平台,打破地域限制,为远程监管奠定基础。
该系统的核心价值在于构建“端-云-端”协同的闭环管理架构。云端不仅提供海量数据存储与分析能力,还通过开放API实现与QT开发的上位机应用无缝对接。农户可通过上位机的动态曲线直观掌握环境变化趋势,并在异常(如高温>35℃)时接收弹窗报警与日志记录;更重要的是,无论身处何地,都能通过华为云平台或QT界面远程手动干预设备(如紧急启动风扇),大幅提升应急响应速度与操作便捷性。
此外,本地SD卡模块的设计增强了系统鲁棒性,在网络中断时仍能缓存关键数据与报警日志,避免信息丢失,确保生产记录的完整性。综上所述,本项目的开发不仅响应了智慧农业对降本增效的核心诉求,更通过多层次的技术融合,为现代化温室提供了一套高可靠性、可扩展的数字化管理解决方案。
设计实现的功能
(1)多传感器数据融合采集:DHT11采集空气温湿度,YL-69土壤湿度传感器检测墒情,BH1750光照传感器监测光强。
(2)阈值联动控制:STM32自动比对预设阈值(如土壤湿度<30%启动灌溉),通过继电器驱动执行设备。
(3)华为云协议对接:ESP8266模块使用MQTT协议传输数据至华为云IoTDA服务。
(4)QT数据可视化:动态折线图展示环境参数变化趋势,按钮控件绑定设备开关指令。
(5)双向通信:上位机通过华为云API订阅设备消息,并下发控制指令(如手动开关补光灯)。
(6)本地数据缓存:SD卡模块存储异常数据备份,确保网络中断时数据不丢失。
项目硬件模块组成
(1)主控单元:STM32F103C8T6核心板。
(2)传感器组:DHT11(温湿度)、YL-69(土壤湿度)、BH1750(光照强度)。
(3)通信模块:ESP8266-01S WiFi模组(AT指令模式)。
(4)执行单元:5V四路继电器模块(控制12V直流风扇、5V微型水泵、220V补光灯)。
(5)存储模块:MicroSD卡读写模块(SPI接口,存储阈值配置与报警日志)。
(6)电源模块:LM2596降压电路(12V转5V供电),220V转12V适配器(总电源)。
(7)人机交互:0.96寸OLED显示屏(本地参数显示),蜂鸣器(本地异常报警)。
设计意义
本系统的设计意义在于显著提升现代农业温室管理的智能化水平。通过集成多类型环境传感器与自动控制设备,系统实现了对温室空气温湿度、土壤墒情及光照强度的全天候精准监测,并依据预设阈值自动触发通风、灌溉、补光等调控动作。这一闭环控制机制有效替代传统人工巡检,大幅降低劳动力成本,同时避免因环境突变导致的作物减产风险,例如高温自动通风可防止热害,土壤湿度联动灌溉能精准优化水资源利用。
系统依托华为云物联网平台构建远程监管能力,将实时环境数据通过WiFi模块上传至云端,使农户可通过QT上位机远程查看温室状态曲线。结合双向通信技术,用户无论在云端平台或本地QT界面均可手动干预设备启停,突破地理限制及时响应突发状况。该设计不仅为精细化种植提供数据支撑,更赋予生产管理高度的灵活性与及时性,尤其适合大规模连栋温室群的集中监控。
异常处理与数据冗余设计进一步强化系统可靠性。当检测到温度骤升等危险值时,QT界面自动弹窗报警并记录日志至SD卡,配合蜂鸣器实现声光双重警示。本地存储模块在网络中断时持续备份关键数据,确保生产记录完整可追溯。这种多层次保障机制显著降低因设备故障或网络波动导致的生产事故概率,为设施农业的稳定运行构建坚实技术基础。
设计思路
设计思路:
系统以STM32F103C8T6为主控制器,通过多接口协议实现环境数据采集与设备联动控制。传感器数据由DHT11(单总线协议)、YL-69(ADC模拟量采集)和BH1750(I2C总线)实时获取,经卡尔曼滤波处理后与预设阈值比对。当土壤湿度低于30%时,GPIO触发继电器启动灌溉泵;温度超限时启动通风扇;光照不足则开启补光灯,所有执行设备均通过光耦隔离继电器控制,确保强电弱电分离。
ESP8266 WiFi模块通过串口AT指令与STM32通信,采用MQTT协议将JSON格式数据上传至华为云物联网平台,同时订阅云平台下行指令Topic。网络异常时,传感器数据自动转存至SPI接口的MicroSD卡,记录时间戳及异常值,实现断网缓存。
QT上位机通过华为云API订阅设备消息,动态绘制温湿度/光照/土壤曲线图,并嵌入设备控制按钮。用户点击按钮时生成标准MQTT指令下发至云平台,STM32解析后执行对应操作。当检测到温度>35℃等异常数据时,QT界面触发弹窗报警并生成日志文件,同时STM32驱动蜂鸣器进行本地报警,OLED屏实时刷新环境参数状态。
电源系统采用220V转12V适配器供电,经LM2596降压模块输出5V为控制电路供电,继电器模块分别控制12V风扇、5V水泵及220V补光灯。系统初始化时从SD卡加载阈值配置,运行中持续监测各模块状态,确保稳定运行。
框架图
+-------------------------------------------------------------------------------------------------+
| 智慧农业温室环境监测与调控系统 |
| |
| +---------------------+ +---------------------------+ +-------------------------+ |
| | 感知层 | | 控制层 | | 执行层 | |
| | [DHT11温湿度] |----->| |----->| [继电器1]→通风扇(降温) | |
| | [YL-69土壤湿度] | UART | | GPIO | [继电器2]→灌溉泵(增湿) | |
| | [BH1750光照强度] |----->| STM32F103C8T6 |----->| [继电器3]→补光灯(调光) | |
| +---------------------+ | - 阈值比对与自动控制逻辑 | +-------------------------+ |
| | - 传感器数据融合处理 | |
| +---------------------+ | - 华为云协议封装 | +-------------------------+ |
| | 本地交互层 |<---->| - SD卡数据管理 |<---->| [OLED显示] | |
| | [0.96寸OLED] | I2C | | GPIO | [蜂鸣器报警] | |
| | [蜂鸣器] |<---->| | +-------------------------+ |
| +---------------------+ +------------+--------------+ |
| | UART |
| | |
| +---------------------+ +------------v--------------+ +-------------------------+ |
| | 通信层 | | 存储层 | | 云端平台 | |
| | [ESP8266 WiFi模块] |<---->| [MicroSD卡] | MQTT | [华为云物联网平台] | |
| | - MQTT协议传输 | SPI | - 存储阈值配置 |----->| - 设备状态监控 | |
| | - AT指令模式 | | - 异常数据备份 |<-----| - 指令下发 | |
| +---------------------+ +---------------------------+ +------------+------------+ |
| | API/Websocket |
| | |
| +-----------------------------------------------------------------------------v-------------+ |
| | 应用层 | |
| | [QT上位机] | |
| | - 动态环境数据曲线(温/湿/光/土) | |
| | - 设备状态实时显示(风扇/水泵/灯) | |
| | - 远程手动控制按钮 | |
| | - 异常弹窗报警(温度>35℃等) | |
| | - 操作日志记录 | |
| +------------------------------------------------------------------------------------------+ |
| |
| 电源架构: |
| [220V交流] → [220V转12V适配器] → [LM2596降压] → 5V系统供电(MCU/传感器/继电器控制端) |
| └─→ 12V供电(直流风扇) |
+-------------------------------------------------------------------------------------------------+
系统总体设计
系统总体设计围绕STM32F103C8T6微控制器构建,实现温室环境智能化监测与调控。该系统通过集成多类型传感器实时获取环境参数:DHT11传感器采集空气温湿度,YL-69土壤湿度传感器检测土壤墒情,BH1750光照传感器监测温室光照强度。主控芯片对采集数据进行融合处理,并与预设阈值进行自动比对(如土壤湿度低于30%或温度高于设定值)。
当环境参数超出阈值范围时,系统触发自动控制逻辑。STM32通过GPIO输出信号驱动5V四路继电器模块,进而控制执行设备动作:启动12V通风扇实现降温,开启5V微型灌溉泵增加土壤湿度,或接通220V补光灯调节光照强度。所有执行设备状态及传感器数据通过ESP8266-01S WiFi模块传输,该模块运行于AT指令模式,采用MQTT协议将数据稳定上传至华为云物联网平台(IoTDA),并接收来自云端的控制指令。
QT开发的上位机软件通过订阅华为云API接口,实现远程数据监控与设备管理。软件界面动态绘制环境参数(温湿度、土壤湿度、光照)的实时变化曲线,并显示继电器及执行设备状态。用户可在QT界面上点击按钮,远程手动控制通风扇、灌溉泵或补光灯的启停。当检测到严重异常数据(如温度>35℃)时,上位机自动触发弹窗报警并生成日志文件。
为增强系统可靠性,集成MicroSD卡存储模块(SPI接口)。该模块用于缓存传感器阈值配置信息,并在网络异常或检测到报警事件时,将关键环境数据及报警日志本地备份,确保数据不丢失。本地交互由0.96寸OLED显示屏实时显示核心环境参数与设备状态,蜂鸣器在检测到本地阈值超限时发出声光报警。系统供电采用220V转12V适配器作为总输入,经LM2596降压电路转换为5V为各模块供电。
系统功能总结
功能类别 | 功能描述 |
---|---|
环境监测 | 实时采集空气温湿度(DHT11)、土壤湿度(YL-69)、光照强度(BH1750) |
自动调控 | 超阈值自动控制设备: ? 温度过高 → 启动通风扇 ? 土壤过干 → 启动灌溉泵 ? 光照不足 → 开启补光灯 |
云平台通信 | 通过ESP8266 WiFi模组,基于MQTT协议将数据上传至华为云IoTDA平台 |
QT上位机 | ? 动态折线图展示环境参数趋势 ? 实时显示设备状态 ? 支持远程手动控制执行设备 ? 异常数据弹窗报警 |
远程控制 | 支持华为云平台与QT上位机双向通信,可远程手动开关通风扇、灌溉泵、补光灯 |
异常处理 | ? 超限数据(如温度>35℃)触发QT弹窗报警 ? 蜂鸣器本地报警 ? 记录异常日志至SD卡 |
数据存储 | MicroSD卡模块缓存异常数据与日志,确保断网时数据不丢失 |
本地交互 | 0.96寸OLED显示屏实时展示环境参数与设备状态 |
设计的各个功能模块描述
数据采集模块通过多种传感器实时获取温室环境参数。DHT11数字温湿度传感器采用单总线协议与STM32通信,连续采集空气温湿度数据;YL-69土壤湿度传感器通过ADC通道将模拟量转换为土壤墒情百分比;BH1750光照传感器通过I2C接口提供数字化的光照强度值。所有传感器数据经过中值滤波处理提升准确性。
自动控制模块基于阈值比较实现设备联动。STM32读取存储在SD卡中的预设阈值(温度上限、土壤湿度下限等),当检测到空气温度超过35℃时,通过GPIO触发继电器启动12V通风扇;土壤湿度低于30%时激活5V微型灌溉泵;光照不足时控制220V补光灯电路。继电器模块采用光耦隔离确保强电控制安全。
云通信模块依托ESP8266-01S建立物联网连接。STM32通过USART发送AT指令配置WiFi模块,采用MQTT协议对接华为云IoTDA服务。设备数据封装为JSON格式发布到云平台指定Topic,同时订阅控制指令Topic,实现参数上传与命令接收的双向通信,支持QoS1级消息保障。
上位机交互模块基于QT开发数据可视化界面。通过调用华为云Device API实时获取设备数据,动态绘制温湿度、光照等参数的折线趋势图。界面嵌入设备控制按钮,点击后生成MQTT控制指令经云平台下发。当接收到云平台推送的温度超限等异常消息时,自动弹出报警窗口并写入本地日志文件。
异常处理模块实现本地化预警与数据持久化。当检测到环境参数越界时,STM32立即驱动蜂鸣器发出声光警报,同时在OLED屏滚动显示异常信息。MicroSD卡模块通过SPI接口存储两种关键数据:FAT32文件系统下的阈值配置文件,以及按时间戳记录的CSV格式报警日志(含异常类型、数值、发生时间),确保断网期间数据不丢失。
人机交互模块提供本地状态监控。0.96寸OLED显示屏通过I2C驱动,分区域显示实时环境参数与设备开关状态。采用循环刷新机制,每2秒更新温度/湿度/光照的数值显示,并在设备动作时突出对应继电器状态图标,便于现场人员快速掌握系统运行情况。
上位机代码设计
上位机代码设计(基于Qt/C++)
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QtCharts>
#include <QMqttClient>
#include <QDateTime>
#include <QFile>
QT_CHARTS_USE_NAMESPACE
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
// MQTT通信
void connectToBroker();
void onMqttConnected();
void onMqttMessageReceived(const QByteArray &message, const QMqttTopicName &topic);
// 数据处理
void updateSensorData(const QJsonObject &data);
void checkAlarmConditions(const QJsonObject &data);
// 设备控制
void on_fanControl_clicked();
void on_pumpControl_clicked();
void on_lightControl_clicked();
// 报警处理
void showAlarmDialog(const QString &message);
void logAlarmEvent(const QString &event);
private:
Ui::MainWindow *ui;
QMqttClient *mqttClient;
// 图表数据序列
QLineSeries *tempSeries;
QLineSeries *humiditySeries;
QLineSeries *soilSeries;
QLineSeries *lightSeries;
// 时间轴
QDateTimeAxis *axisX;
// 报警日志文件
QFile alarmLogFile;
// 设备状态
bool fanState = false;
bool pumpState = false;
bool lightState = false;
// 阈值设置
const double TEMP_ALARM_THRESHOLD = 35.0;
const double SOIL_MOISTURE_THRESHOLD = 30.0;
};
#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QMessageBox>
#include <QTimer>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 初始化MQTT客户端
mqttClient = new QMqttClient(this);
mqttClient->setHostname("your_huaweicloud_iot_host"); // 替换为华为云地址
mqttClient->setPort(1883);
mqttClient->setClientId("QT_Client_" + QString::number(qrand()));
mqttClient->setUsername("your_username");
mqttClient->setPassword("your_password");
// 连接信号槽
connect(mqttClient, &QMqttClient::connected, this, &MainWindow::onMqttConnected);
connect(mqttClient, &QMqttClient::messageReceived, this, &MainWindow::onMqttMessageReceived);
// 初始化图表
QChart *chart = new QChart();
tempSeries = new QLineSeries();
humiditySeries = new QLineSeries();
soilSeries = new QLineSeries();
lightSeries = new QLineSeries();
tempSeries->setName("温度(℃)");
humiditySeries->setName("空气湿度(%)");
soilSeries->setName("土壤湿度(%)");
lightSeries->setName("光照强度(lux)");
chart->addSeries(tempSeries);
chart->addSeries(humiditySeries);
chart->addSeries(soilSeries);
chart->addSeries(lightSeries);
axisX = new QDateTimeAxis();
axisX->setFormat("hh:mm:ss");
axisX->setTitleText("时间");
chart->addAxis(axisX, Qt::AlignBottom);
QValueAxis *axisY = new QValueAxis();
axisY->setLabelFormat("%d");
axisY->setTitleText("数值");
axisY->setRange(0, 100);
chart->addAxis(axisY, Qt::AlignLeft);
tempSeries->attachAxis(axisX);
tempSeries->attachAxis(axisY);
humiditySeries->attachAxis(axisX);
humiditySeries->attachAxis(axisY);
soilSeries->attachAxis(axisX);
soilSeries->attachAxis(axisY);
QValueAxis *axisYRight = new QValueAxis();
axisYRight->setLabelFormat("%d");
axisYRight->setTitleText("光照强度");
axisYRight->setRange(0, 10000);
chart->addAxis(axisYRight, Qt::AlignRight);
lightSeries->attachAxis(axisX);
lightSeries->attachAxis(axisYRight);
ui->chartView->setChart(chart);
ui->chartView->setRenderHint(QPainter::Antialiasing);
// 初始化报警日志
alarmLogFile.setFileName("alarm_log.csv");
if(alarmLogFile.open(QIODevice::WriteOnly | QIODevice::Append)) {
QTextStream stream(&alarmLogFile);
stream << "时间,事件类型,数值\n";
}
// 连接MQTT代理
connectToBroker();
}
MainWindow::~MainWindow()
{
alarmLogFile.close();
delete ui;
}
void MainWindow::connectToBroker()
{
if(mqttClient->state() == QMqttClient::Disconnected) {
mqttClient->connectToHost();
}
}
void MainWindow::onMqttConnected()
{
// 订阅设备数据主题
mqttClient->subscribe("device/sensor/data", 1);
// 订阅设备状态主题
mqttClient->subscribe("device/status", 1);
}
void MainWindow::onMqttMessageReceived(const QByteArray &message, const QMqttTopicName &topic)
{
QJsonDocument doc = QJsonDocument::fromJson(message);
if(doc.isNull()) return;
QJsonObject obj = doc.object();
if(topic.name() == "device/sensor/data") {
updateSensorData(obj);
checkAlarmConditions(obj);
}
else if(topic.name() == "device/status") {
fanState = obj["fan"].toBool();
pumpState = obj["pump"].toBool();
lightState = obj["light"].toBool();
ui->fanStatus->setText(fanState ? "运行中" : "已停止");
ui->pumpStatus->setText(pumpState ? "运行中" : "已停止");
ui->lightStatus->setText(lightState ? "运行中" : "已停止");
}
}
void MainWindow::updateSensorData(const QJsonObject &data)
{
QDateTime currentTime = QDateTime::currentDateTime();
double temperature = data["temperature"].toDouble();
double humidity = data["humidity"].toDouble();
double soilMoisture = data["soil_moisture"].toDouble();
double lightIntensity = data["light_intensity"].toDouble();
// 更新实时数据标签
ui->tempLabel->setText(QString::number(temperature, 'f', 1) + " ℃");
ui->humidityLabel->setText(QString::number(humidity, 'f', 1) + " %");
ui->soilLabel->setText(QString::number(soilMoisture, 'f', 1) + " %");
ui->lightLabel->setText(QString::number(lightIntensity, 'f', 0) + " lux");
// 更新图表数据
tempSeries->append(currentTime.toMSecsSinceEpoch(), temperature);
humiditySeries->append(currentTime.toMSecsSinceEpoch(), humidity);
soilSeries->append(currentTime.toMSecsSinceEpoch(), soilMoisture);
lightSeries->append(currentTime.toMSecsSinceEpoch(), lightIntensity);
// 保持最近100个数据点
if(tempSeries->count() > 100) {
tempSeries->remove(0);
humiditySeries->remove(0);
soilSeries->remove(0);
lightSeries->remove(0);
}
// 更新X轴范围
axisX->setRange(QDateTime::currentDateTime().addSecs(-300),
QDateTime::currentDateTime().addSecs(10));
}
void MainWindow::checkAlarmConditions(const QJsonObject &data)
{
double temperature = data["temperature"].toDouble();
double soilMoisture = data["soil_moisture"].toDouble();
if(temperature > TEMP_ALARM_THRESHOLD) {
QString msg = QString("温度过高警报: %1 ℃ > %2 ℃").arg(temperature).arg(TEMP_ALARM_THRESHOLD);
showAlarmDialog(msg);
logAlarmEvent("温度警报," + QString::number(temperature));
}
if(soilMoisture < SOIL_MOISTURE_THRESHOLD) {
QString msg = QString("土壤干燥警报: %1% < %2%").arg(soilMoisture).arg(SOIL_MOISTURE_THRESHOLD);
showAlarmDialog(msg);
logAlarmEvent("土壤湿度警报," + QString::number(soilMoisture));
}
}
void MainWindow::on_fanControl_clicked()
{
QJsonObject json;
json["command"] = "fan";
json["state"] = !fanState;
mqttClient->publish(QMqttTopicName("device/control"),
QJsonDocument(json).toJson());
}
void MainWindow::on_pumpControl_clicked()
{
QJsonObject json;
json["command"] = "pump";
json["state"] = !pumpState;
mqttClient->publish(QMqttTopicName("device/control"),
QJsonDocument(json).toJson());
}
void MainWindow::on_lightControl_clicked()
{
QJsonObject json;
json["command"] = "light";
json["state"] = !lightState;
mqttClient->publish(QMqttTopicName("device/control"),
QJsonDocument(json).toJson());
}
void MainWindow::showAlarmDialog(const QString &message)
{
QMessageBox::critical(this, "系统警报", message);
}
void MainWindow::logAlarmEvent(const QString &event)
{
if(alarmLogFile.isOpen()) {
QTextStream stream(&alarmLogFile);
stream << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") << ","
<< event << "\n";
alarmLogFile.flush();
}
}
// main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.setWindowTitle("智慧农业温室监控系统");
w.resize(1200, 800);
w.show();
return a.exec();
}
<!-- mainwindow.ui (关键部分) -->
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<widget class="QWidget" name="centralWidget">
<layout class="QGridLayout" name="gridLayout">
<!-- 图表区域 -->
<widget class="QChartView" name="chartView"/>
<!-- 数据监控面板 -->
<widget class="QGroupBox" name="dataGroupBox">
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_temp">
<property name="text">
<string>当前温度:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="tempLabel">
<property name="text">
<string>-- ℃</string>
</property>
</widget>
</item>
<!-- 其他传感器数据标签 -->
</layout>
</widget>
<!-- 设备控制面板 -->
<widget class="QGroupBox" name="controlGroupBox">
<layout class="QGridLayout">
<!-- 风扇控制 -->
<item row="0" column="0">
<widget class="QPushButton" name="fanControl">
<property name="text">
<string>开关风扇</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="fanStatus">
<property name="text">
<string>已停止</string>
</property>
</widget>
</item>
<!-- 水泵控制 -->
<item row="1" column="0">
<widget class="QPushButton" name="pumpControl">
<property name="text">
<string>开关水泵</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="pumpStatus">
<property name="text">
<string>已停止</string>
</property>
</widget>
</item>
<!-- 补光灯控制 -->
<item row="2" column="0">
<widget class="QPushButton" name="lightControl">
<property name="text">
<string>开关补光灯</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lightStatus">
<property name="text">
<string>已停止</string>
</property>
</widget>
</item>
</layout>
</widget>
</layout>
</widget>
</widget>
</ui>
项目结构说明
智慧农业监控系统/
├── include/
│ └── mainwindow.h
├── src/
│ ├── main.cpp
│ └── mainwindow.cpp
├── resources/
│ └── alarm_log.csv
├── ui/
│ └── mainwindow.ui
└── 智慧农业监控系统.pro
功能实现细节
-
MQTT通信模块:
- 使用QtMqtt库连接华为云物联网平台
- 订阅设备传感器数据主题(
device/sensor/data
) - 订阅设备状态主题(
device/status
) - 发布控制指令到
device/control
主题
-
数据可视化:
- 使用QtCharts绘制四参数实时曲线
- 温度/湿度/土壤湿度共用左侧Y轴(0-100)
- 光照强度使用右侧独立Y轴(0-10000 lux)
- 自动滚动显示最近5分钟数据
-
设备控制:
- 三个独立控制按钮对应通风扇/灌溉泵/补光灯
- 按钮发送JSON格式控制指令:
{"command":"fan","state":true}
- 实时更新设备状态标签
-
报警系统:
- 温度>35℃触发弹窗警报
- 土壤湿度<30%触发弹窗警报
- 所有警报事件记录到CSV日志文件
- 日志格式:
时间,事件类型,数值
-
数据持久化:
- 报警日志自动保存为CSV格式
- 支持Excel直接打开分析
- 每日自动创建新文件(需扩展实现)
编译依赖
- Qt 5.12+ (含Charts、Mqtt模块)
- C++11编译器
- 华为云IoT Device SDK配置信息:
- 云平台地址
- 设备认证信息
- MQTT主题规划
注意:实际部署时需要替换以下占位符:
your_huaweicloud_iot_host
your_username
your_password
- 设备主题命名空间
模块代码设计
以下是基于STM32F103C8T6的完整传感器驱动代码(寄存器开发方式):
#include "stm32f10x.h"
// 硬件定义
#define DHT11_GPIO_PORT GPIOA
#define DHT11_PIN GPIO_Pin_0
#define RELAY_FAN_GPIO GPIOC
#define RELAY_FAN_PIN GPIO_Pin_13
#define RELAY_PUMP_GPIO GPIOC
#define RELAY_PUMP_PIN GPIO_Pin_14
#define RELAY_LIGHT_GPIO GPIOC
#define RELAY_LIGHT_PIN GPIO_Pin_15
// DHT11 温湿度传感器
uint8_t DHT11_Read(float *temp, float *humi) {
uint8_t data[5] = {0};
uint8_t i, j;
// 主机发送开始信号
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置为输出模式
GPIO_InitStruct.GPIO_Pin = DHT11_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
// 拉低18ms
GPIO_ResetBits(DHT11_GPIO_PORT, DHT11_PIN);
Delay_ms(18);
GPIO_SetBits(DHT11_GPIO_PORT, DHT11_PIN);
Delay_us(30);
// 切换为输入模式
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
// 等待DHT11响应
if (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_PIN) == SET) return 0;
while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_PIN) == RESET);
while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_PIN) == SET);
// 读取40位数据
for (i = 0; i < 5; i++) {
for (j = 0; j < 8; j++) {
while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_PIN) == RESET);
Delay_us(40);
if (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_PIN) == SET) {
data[i] |= (1 << (7 - j));
while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_PIN) == SET);
}
}
}
// 校验数据
if (data[4] == (data[0] + data[1] + data[2] + data[3])) {
*humi = data[0];
*temp = data[2];
return 1;
}
return 0;
}
// ADC初始化 (YL-69土壤湿度)
void ADC1_Init(void) {
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN;
// 配置PA1为模拟输入
GPIOA->CRL &= ~(GPIO_CRL_CNF1 | GPIO_CRL_MODE1);
// ADC校准
ADC1->CR2 |= ADC_CR2_ADON;
Delay_ms(1);
ADC1->CR2 |= ADC_CR2_RSTCAL;
while (ADC1->CR2 & ADC_CR2_RSTCAL);
ADC1->CR2 |= ADC_CR2_CAL;
while (ADC1->CR2 & ADC_CR2_CAL);
// 配置ADC
ADC1->CR1 &= ~ADC_CR1_SCAN;
ADC1->CR2 &= ~(ADC_CR2_CONT | ADC_CR2_ALIGN);
ADC1->SMPR2 |= ADC_SMPR2_SMP1_0 | ADC_SMPR2_SMP1_1 | ADC_SMPR2_SMP1_2; // 239.5周期采样
}
uint16_t Read_Soil_Moisture(void) {
ADC1->SQR3 = ADC_SQR3_SQ1_0; // 通道1
ADC1->CR2 |= ADC_CR2_ADON;
while (!(ADC1->SR & ADC_SR_EOC));
return ADC1->DR;
}
// BH1750光照传感器 (I2C1)
#define BH1750_ADDR 0x46
void I2C1_Init(void) {
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
// PB6(SCL), PB7(SDA) 开漏输出
GPIOB->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_MODE6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE7);
GPIOB->CRL |= GPIO_CRL_CNF6_1 | GPIO_CRL_MODE6 | GPIO_CRL_CNF7_1 | GPIO_CRL_MODE7;
GPIOB->ODR |= GPIO_ODR_ODR6 | GPIO_ODR_ODR7;
// I2C配置
I2C1->CR1 &= ~I2C_CR1_PE;
I2C1->CR2 = 36; // APB1时钟36MHz
I2C1->CCR = 180; // 100kHz (36M/(2*180)=100k)
I2C1->TRISE = 37; // 1000ns/(1/36M)=36+1
I2C1->CR1 |= I2C_CR1_PE;
}
void I2C_Write(uint8_t addr, uint8_t data) {
// 发送起始条件
I2C1->CR1 |= I2C_CR1_START;
while (!(I2C1->SR1 & I2C_SR1_SB));
// 发送地址
I2C1->DR = addr;
while (!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2; // 清除ADDR标志
// 发送数据
while (!(I2C1->SR1 & I2C_SR1_TXE));
I2C1->DR = data;
while (!(I2C1->SR1 & I2C_SR1_BTF));
// 停止条件
I2C1->CR1 |= I2C_CR1_STOP;
}
uint16_t BH1750_Read(void) {
uint8_t data[2] = {0};
I2C_Write(BH1750_ADDR, 0x10); // 连续测量模式
Delay_ms(180); // 等待测量完成
// 读取数据
I2C1->CR1 |= I2C_CR1_START;
while (!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = BH1750_ADDR | 0x01;
while (!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 接收第一个字节
while (!(I2C1->SR1 & I2C_SR1_RXNE));
data[0] = I2C1->DR;
// 接收第二个字节
while (!(I2C1->SR1 & I2C_SR1_RXNE));
data[1] = I2C1->DR;
I2C1->CR1 |= I2C_CR1_STOP;
return (data[0] << 8) | data[1];
}
// 继电器控制
void Relay_Control(uint8_t device, uint8_t state) {
switch(device) {
case 0: // 风扇
state ? GPIO_SetBits(RELAY_FAN_GPIO, RELAY_FAN_PIN) :
GPIO_ResetBits(RELAY_FAN_GPIO, RELAY_FAN_PIN);
break;
case 1: // 水泵
state ? GPIO_SetBits(RELAY_PUMP_GPIO, RELAY_PUMP_PIN) :
GPIO_ResetBits(RELAY_PUMP_GPIO, RELAY_PUMP_PIN);
break;
case 2: // 补光灯
state ? GPIO_SetBits(RELAY_LIGHT_GPIO, RELAY_LIGHT_PIN) :
GPIO_ResetBits(RELAY_LIGHT_GPIO, RELAY_LIGHT_PIN);
break;
}
}
// 系统初始化
void System_Init(void) {
// 时钟配置
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN;
// 继电器GPIO初始化
GPIOC->CRH &= ~(GPIO_CRH_CNF13 | GPIO_CRH_CNF14 | GPIO_CRH_CNF15);
GPIOC->CRH |= GPIO_CRH_MODE13 | GPIO_CRH_MODE14 | GPIO_CRH_MODE15;
GPIO_ResetBits(RELAY_FAN_GPIO, RELAY_FAN_PIN);
GPIO_ResetBits(RELAY_PUMP_GPIO, RELAY_PUMP_PIN);
GPIO_ResetBits(RELAY_LIGHT_GPIO, RELAY_LIGHT_PIN);
// 外设初始化
ADC1_Init();
I2C1_Init();
}
// 主控制循环
int main(void) {
System_Init();
float temp, humi;
uint16_t soil_moist, light;
while(1) {
// 传感器数据采集
if(DHT11_Read(&temp, &humi)) {
// 温度控制逻辑
if(temp > 30.0) Relay_Control(0, 1); // 开启风扇
else Relay_Control(0, 0);
}
soil_moist = Read_Soil_Moisture();
if(soil_moist < 800) Relay_Control(1, 1); // 开启水泵 (假设ADC值越小越湿)
else Relay_Control(1, 0);
light = BH1750_Read();
if(light < 10000) Relay_Control(2, 1); // 开启补光灯
else Relay_Control(2, 0);
// 数据上传间隔
Delay_ms(5000);
}
}
// 简单延时函数
void Delay_ms(uint32_t ms) {
for(uint32_t i=0; i<ms*8000; i++);
}
void Delay_us(uint32_t us) {
for(uint32_t i=0; i<us*8; i++);
}
代码说明:
-
DHT11温湿度传感器:
- 使用单总线协议,PA0引脚
- 提供温湿度整数读取(精度1℃/1%RH)
- 包含数据校验机制
-
YL-69土壤湿度:
- 使用ADC1通道1(PA1)
- 12位ADC分辨率(0-4095)
- 值越小表示土壤越湿润
-
BH1750光照传感器:
- I2C接口(PB6-SCL, PB7-SDA)
- 连续测量模式,分辨率1lx
- 直接返回光照强度值
-
继电器控制:
- 风扇:PC13
- 水泵:PC14
- 补光灯:PC15
- 低电平触发继电器吸合
-
控制逻辑:
- 温度>30℃开启风扇
- 土壤湿度阈值800(需根据实际校准)
- 光照<10000lx开启补光灯
注意事项:
- 实际阈值需根据传感器校准调整
- ADC读取值范围取决于YL-69的供电电压
- I2C时序需根据实际电路调整延时
- 继电器控制逻辑需增加防抖机制
- 需配合硬件连接图验证引脚分配
项目核心代码
#include "stm32f10x.h"
#include "dht11.h"
#include "bh1750.h"
#include "esp8266.h"
#include "sdio_sdcard.h"
#include "oled.h"
#include "relay.h"
#include "buzzer.h"
#include "systick.h"
// 传感器数据结构体
typedef struct {
float temp; // 温度
float humidity; // 空气湿度
uint16_t soil; // 土壤湿度
uint16_t light; // 光照强度
} SensorData;
// 全局变量
SensorData env_data;
uint8_t cloud_connected = 0;
uint8_t fan_status = 0, pump_status = 0, light_status = 0;
// 阈值设定
#define TEMP_THRESHOLD_HIGH 35.0
#define SOIL_THRESHOLD_LOW 30
#define LIGHT_THRESHOLD_LOW 100
// 函数声明
void System_Init(void);
void Read_Sensors(void);
void Auto_Control(void);
void Cloud_Upload(void);
void Check_Cloud_Commands(void);
void Log_Exception(const char* msg);
int main(void) {
System_Init();
// 初始化华为云连接
if(ESP8266_Connect_Cloud()) {
cloud_connected = 1;
OLED_ShowString(0, 0, "Cloud: Connected");
} else {
OLED_ShowString(0, 0, "Cloud: Error");
}
while(1) {
// 每5秒执行一次
if(SysTick_Get() >= 5000) {
SysTick_Reset();
Read_Sensors(); // 读取传感器数据
Auto_Control(); // 自动控制逻辑
OLED_Display(); // OLED刷新显示
if(cloud_connected) {
Cloud_Upload(); // 上传到华为云
Check_Cloud_Commands();// 检查云端命令
}
}
// 处理WiFi数据接收
if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET) {
ESP8266_Receive_Handler();
}
}
}
// 系统初始化
void System_Init(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
// 初始化外设
DHT11_Init();
BH1750_Init();
ESP8266_Init(115200);
SD_Card_Init();
OLED_Init();
Relay_Init();
Buzzer_Init();
SysTick_Init();
// 初始化继电器状态
Fan_Control(DISABLE);
Pump_Control(DISABLE);
Light_Control(DISABLE);
}
// 读取传感器数据
void Read_Sensors(void) {
if(DHT11_Read(&env_data.temp, &env_data.humidity) != SUCCESS) {
Log_Exception("DHT11 Error");
}
env_data.soil = SoilMoisture_Read();
env_data.light = BH1750_Read();
}
// 自动控制逻辑
void Auto_Control(void) {
// 温度过高启动风扇
if(env_data.temp > TEMP_THRESHOLD_HIGH) {
Fan_Control(ENABLE);
fan_status = 1;
if(cloud_connected) Log_Exception("High Temp!");
} else {
Fan_Control(DISABLE);
fan_status = 0;
}
// 土壤过干启动水泵
if(env_data.soil < SOIL_THRESHOLD_LOW) {
Pump_Control(ENABLE);
pump_status = 1;
} else {
Pump_Control(DISABLE);
pump_status = 0;
}
// 光照不足启动补光灯
if(env_data.light < LIGHT_THRESHOLD_LOW) {
Light_Control(ENABLE);
light_status = 1;
} else {
Light_Control(DISABLE);
light_status = 0;
}
}
// 数据上传华为云
void Cloud_Upload(void) {
char payload[128];
snprintf(payload, sizeof(payload),
"{\"temp\":%.1f,\"hum\":%.1f,\"soil\":%d,\"light\":%d,\"fan\":%d,\"pump\":%d,\"lamp\":%d}",
env_data.temp, env_data.humidity, env_data.soil, env_data.light,
fan_status, pump_status, light_status);
ESP8266_Send_Data(payload);
}
// 检查云端控制命令
void Check_Cloud_Commands(void) {
char* cmd = ESP8266_Get_Command();
if(cmd != NULL) {
// 解析JSON命令 (示例格式: {"fan":1})
if(strstr(cmd, "\"fan\":1")) {
Fan_Control(ENABLE);
fan_status = 1;
}
else if(strstr(cmd, "\"fan\":0")) {
Fan_Control(DISABLE);
fan_status = 0;
}
// 其他设备解析逻辑类似...
free(cmd); // 释放内存
}
}
// 异常日志记录
void Log_Exception(const char* msg) {
char log_msg[64];
snprintf(log_msg, sizeof(log_msg), "[ALERT] %s", msg);
// 本地蜂鸣器报警
Buzzer_Alert(3, 200);
// SD卡记录
if(SD_Card_Status() == SD_OK) {
SD_Write_Log(log_msg);
}
}
代码说明:
-
系统初始化
- 配置所有外设时钟
- 初始化传感器、WiFi模块、SD卡等硬件
- 设置继电器初始状态为关闭
-
主循环逻辑
- 5秒定时采集数据(温度、湿度、土壤湿度、光照)
- 自动控制:温度过高启风扇、土壤过干启水泵、光照不足启补光灯
- OLED实时显示数据
- 华为云数据上传(JSON格式)
- 云端指令监听处理
-
关键功能
- 异常处理:高温触发蜂鸣器报警+SD卡日志
- 断网保护:本地自动控制不受网络影响
- 双向通信:支持云端远程控制设备
- 数据缓存:SD卡存储异常日志
-
硬件接口
- USART1:ESP8266通信(AT指令+MQTT)
- I2C接口:BH1750光照传感器
- GPIO:DHT11温湿度、继电器控制
- SPI:SD卡数据存储
总结
本系统以STM32F103C8T6微控制器为核心,构建了一套高效可靠的智慧农业温室环境监测与调控方案。该系统通过集成DHT11、YL-69和BH1750等多传感器,实时采集空气温湿度、土壤湿度及光照强度数据,并基于预设阈值实现自动化控制,如土壤湿度低于30%时启动灌溉泵,确保温室环境参数维持在最优范围内。
借助ESP8266 WiFi模块与MQTT协议,系统将采集数据无缝上传至华为云物联网平台,同时支持QT上位机动态展示环境参数变化曲线和设备状态。用户可通过云平台或上位机进行远程手动控制(如开关补光灯),并实现双向通信,确保指令实时下发与响应。
异常处理机制通过本地OLED显示和蜂鸣器报警提供即时反馈,当检测到参数异常(如温度超过35℃)时,QT界面触发弹窗警告并记录日志至MicroSD卡模块,实现数据缓存以防网络中断。整体硬件设计包括继电器驱动执行设备、电源管理模块及存储单元,保障了系统的稳定性和实用性。
该设计显著提升了农业温室的智能化管理水平,实现环境精准调控与数据可视化,为现代精准农业提供了低成本、高效率的解决方案。
- 点赞
- 收藏
- 关注作者
评论(0)