STM32驱动的智能农业灌溉系统与华为云管理平台
项目开发背景
随着全球人口的增长和气候变化的影响,农业面临着提高产量和资源效率的双重压力。传统灌溉方法往往依赖人工经验,容易导致水资源浪费和作物生长不均,这不仅增加了生产成本,还加剧了水资源的稀缺性。此外,农田环境的实时监测不足,使得 farmers 难以及时响应天气变化或土壤条件波动,从而影响作物健康和质量。
智能农业技术的兴起为解决这些问题提供了新途径。通过集成物联网设备、传感器和自动化控制系统,农业可以实现精准灌溉和数据分析,从而优化资源使用、减少人工干预并提升可持续性。这种转型不仅有助于应对粮食安全挑战,还能推动农业向数字化和智能化方向发展,适应现代环境需求。
本项目开发的智能农业灌溉系统正是基于这一背景,利用STM32微控制器作为核心处理单元,结合多种传感器实时监测土壤湿度、环境温度和光照强度。系统通过自动阈值控制水泵启停,确保灌溉的精准性和及时性,同时支持远程手动控制,增强了灵活性和用户体验。此外,数据上传至华为云平台,实现了农田信息的集中管理和分析,为决策提供数据支持。
华为云管理平台的集成进一步扩展了系统的功能,允许用户通过QT上位机界面可视化农田地图、数据仪表盘和历史记录,从而实现远程监控和智能化管理。这不仅提升了农业操作的效率,还降低了运维成本,并为未来大数据分析和预测模型奠定了基础。
总体而言,该项目代表了农业现代化的一种实践,通过技术创新促进水资源节约、产量提升和可持续发展,具有广泛的应用前景和社会价值。
设计实现的功能
(1)监测土壤湿度、环境温度及光照强度。
(2)根据阈值自动控制水泵启停,支持手动远程控制。
(3)灌溉记录及传感器数据上传至华为云。
(4)QT上位机显示农田地图、数据仪表盘及灌溉历史记录。
项目硬件模块组成
(1)STM32F103C8T6最小系统核心板(主控)
(2)土壤湿度传感器(FC-28)
(3)DHT11温湿度传感器
(4)GY-30光照传感器
(5)5V直流水泵+继电器模块(灌溉控制)
(6)ESP8266-01S Wi-Fi模块(华为云通信)
(7)洞洞板焊接电源控制电路,杜邦线连接外设
设计意义
该智能农业灌溉系统通过集成STM32微控制器和多种传感器,实现了对农田环境的实时监测与自动控制,显著提升了农业灌溉的智能化水平。系统能够自动根据土壤湿度、温度及光照强度阈值启停水泵,确保作物在最佳环境下生长,同时减少水资源浪费,提高灌溉效率。
支持手动远程控制和数据上传至华为云,使农民能够通过互联网远程监控和管理农田,及时响应环境变化,降低了人工巡检的成本和风险。这种远程访问功能特别适用于大面积或分散的农田管理,增强了农业操作的灵活性和可靠性。
通过QT上位机显示农田地图、数据仪表盘及灌溉历史记录,系统提供了直观的数据可视化界面,便于用户分析历史趋势和做出决策。数据记录和云存储功能为农业研究和管理提供了宝贵的数据支持,有助于优化灌溉策略和提高作物产量。
硬件组成基于常见的低成本组件如STM32F103C8T6、ESP8266模块和标准传感器,使得系统易于构建和部署,适合推广到中小型农场。这种设计注重实用性和经济性,推动了智能农业技术的普及和应用。
设计思路
设计思路基于STM32F103C8T6最小系统核心板作为主控制器,负责协调整个系统的运行。系统通过集成多种传感器实时监测农田环境参数,包括土壤湿度传感器FC-28用于检测土壤水分含量,DHT11温湿度传感器获取环境温度数据,以及GY-30光照传感器测量光照强度。这些传感器数据被STM32定期采集并通过ADC或数字接口进行处理,确保数据的准确性和实时性。
控制逻辑部分,系统根据预设的阈值自动决策水泵的启停。例如,当土壤湿度低于设定值时,STM32输出信号驱动继电器模块,从而控制5V直流水泵进行灌溉;同时,系统支持手动远程控制,用户可以通过华为云平台发送指令,经Wi-Fi模块传输到STM32,实现水泵的远程开关,增强了系统的灵活性和实用性。
数据通信方面,ESP8266-01S Wi-Fi模块负责与华为云管理平台建立连接。STM32将采集到的传感器数据以及灌溉记录封装成JSON格式或其他协议,通过串口发送给ESP8266,由Wi-Fi模块上传至华为云。这确保了数据的持久化存储和远程访问,为后续分析和监控提供基础。
QT上位机部分设计为图形化界面,用于显示农田地图、实时数据仪表盘和灌溉历史记录。上位机通过API从华为云获取数据,可视化展示传感器读数、水泵状态和历史趋势,帮助用户直观管理农田灌溉情况,支持数据查询和报表生成。
硬件实现上,所有外设通过杜邦线连接到STM32核心板,电源控制电路基于洞洞板焊接,提供稳定的5V和3.3V电源分配,确保传感器、水泵和Wi-Fi模块的可靠供电。整个系统注重实用性和可靠性,避免不必要的复杂度,以实际农业应用为导向。
框架图
系统总体设计
系统总体设计基于STM32F103C8T6最小系统核心板作为主控制器,集成多种传感器和执行器,实现智能农业灌溉的自动化与远程管理。系统通过土壤湿度传感器FC-28、DHT11温湿度传感器和GY-30光照传感器实时采集环境数据,这些数据由STM32处理并用于控制决策。硬件连接采用洞洞板焊接电源控制电路和杜邦线,确保稳定供电和信号传输。
数据采集部分由STM32通过ADC接口读取土壤湿度传感器的模拟信号,同时通过数字接口获取DHT11的温度和湿度数据,以及通过I2C协议读取GY-30光照传感器的光照强度。这些传感器数据定期采样并存储在STM32的本地内存中,用于后续的控制和上传。
控制逻辑基于预设阈值,STM32比较传感器数据与设定值,自动决定水泵的启停状态,通过继电器模块驱动5V直流水泵。系统还支持手动远程控制,用户可以通过华为云平台或上位机发送指令,STM32解析这些指令并执行相应操作,同时记录灌溉事件。
数据传输通过ESP8266-01S Wi-Fi模块实现,STM将传感器数据和灌溉记录打包后,通过串口通信发送给ESP8266模块,后者连接到华为云平台并上传数据。华为云负责存储和管理这些信息,为远程监控和数据分析提供基础。
上位机部分采用QT开发的应用程序,从华为云平台获取数据,显示农田地图的布局、实时传感器数据的仪表盘以及灌溉历史记录。用户可以通过上位机界面直观查看系统状态和历史趋势,辅助决策和远程管理。
系统功能总结
功能 | 描述 |
---|---|
土壤湿度监测 | 使用FC-28传感器实时监测土壤湿度值 |
环境温度监测 | 使用DHT11传感器监测环境温度 |
光照强度监测 | 使用GY-30传感器监测光照强度 |
自动灌溉控制 | STM32根据预设阈值自动控制水泵启停 |
手动远程控制 | 通过华为云平台远程手动控制水泵 |
数据上传 | 将传感器数据和灌溉记录通过ESP8266上传至华为云 |
上位机显示 | QT上位机应用程序显示农田地图、实时数据仪表盘和灌溉历史记录 |
设计的各个功能模块描述
STM32F103C8T6最小系统核心板作为主控制器,负责采集来自土壤湿度传感器FC-28、DHT11温湿度传感器和GY-30光照传感器的数据,实时监测土壤湿度、环境温度及光照强度,并将这些数据用于系统决策和处理。
系统根据预设的阈值自动控制水泵的启停,当土壤湿度低于设定值时启动水泵进行灌溉,达到阈值后停止,同时支持通过手动远程控制方式操作水泵,实现灵活的灌溉管理。
通过ESP8266-01S Wi-Fi模块,系统将灌溉记录及传感器数据上传至华为云管理平台,实现数据的远程存储和访问,便于后续监控和分析。
QT上位机软件显示农田地图、数据仪表盘及灌溉历史记录,用户可以通过上位机直观查看实时数据和历史信息,辅助农田管理决策。
硬件连接采用洞洞板焊接电源控制电路,并通过杜邦线连接所有外设,包括传感器、水泵继电器模块和Wi-Fi模块,确保系统供电稳定和信号传输可靠。
上位机代码设计
以下是基于QT框架的C++上位机代码设计,用于显示农田地图、数据仪表盘和灌溉历史记录。代码假设华为云提供REST API返回JSON格式的数据,包括传感器数据和水泵控制记录。UI使用QT Designer创建,但这里用代码实现以保持完整性。
文件结构:
- •
main.cpp
: 应用程序入口点。 - •
mainwindow.h
: 主窗口类声明。 - •
mainwindow.cpp
: 主窗口类实现。 - •
datamanager.h
: 数据管理类声明(处理网络请求)。 - •
datamanager.cpp
: 数据管理类实现。
代码实现:
1. main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
2. mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QLabel>
#include <QProgressBar>
#include <QTableWidget>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QPushButton>
#include <QTimer>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void fetchData();
void onDataReceived(QNetworkReply *reply);
void updateDashboard(const QJsonObject &data);
void updateHistoryTable(const QJsonArray &history);
void onManualControlClicked();
private:
QNetworkAccessManager *networkManager;
QTimer *dataTimer;
QLabel *soilHumidityLabel;
QLabel *temperatureLabel;
QLabel *lightIntensityLabel;
QProgressBar *soilHumidityBar;
QProgressBar *temperatureBar;
QProgressBar *lightIntensityBar;
QTableWidget *historyTable;
QGraphicsScene *mapScene;
QGraphicsView *mapView;
QPushButton *manualControlButton;
bool pumpStatus;
void setupUI();
void setupConnections();
};
#endif // MAINWINDOW_H
3. mainwindow.cpp
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGroupBox>
#include <QHeaderView>
#include <QGraphicsRectItem>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), networkManager(new QNetworkAccessManager(this)), dataTimer(new QTimer(this)), pumpStatus(false)
{
setupUI();
setupConnections();
fetchData(); // Initial fetch
dataTimer->start(5000); // Update every 5 seconds
}
MainWindow::~MainWindow()
{
}
void MainWindow::setupUI()
{
setWindowTitle("智能农业灌溉系统监控");
setGeometry(100, 100, 1000, 600);
// Central widget and layout
QWidget *centralWidget = new QWidget(this);
QHBoxLayout *mainLayout = new QHBoxLayout(centralWidget);
// Left side: Map
QGroupBox *mapGroup = new QGroupBox("农田地图");
mapScene = new QGraphicsScene(this);
mapView = new QGraphicsView(mapScene);
mapView->setFixedSize(300, 300);
// Simple map representation - a rectangle for the field
QGraphicsRectItem *field = new QGraphicsRectItem(0, 0, 200, 200);
field->setBrush(Qt::green);
mapScene->addItem(field);
QVBoxLayout *mapLayout = new QVBoxLayout;
mapLayout->addWidget(mapView);
mapGroup->setLayout(mapLayout);
// Center: Dashboard
QGroupBox *dashboardGroup = new QGroupBox("数据仪表盘");
soilHumidityLabel = new QLabel("土壤湿度: --%");
temperatureLabel = new QLabel("温度: --°C");
lightIntensityLabel = new QLabel("光照强度: -- lux");
soilHumidityBar = new QProgressBar;
soilHumidityBar->setRange(0, 100);
temperatureBar = new QProgressBar;
temperatureBar->setRange(-10, 50);
lightIntensityBar = new QProgressBar;
lightIntensityBar->setRange(0, 10000);
QVBoxLayout *dashboardLayout = new QVBoxLayout;
dashboardLayout->addWidget(soilHumidityLabel);
dashboardLayout->addWidget(soilHumidityBar);
dashboardLayout->addWidget(temperatureLabel);
dashboardLayout->addWidget(temperatureBar);
dashboardLayout->addWidget(lightIntensityLabel);
dashboardLayout->addWidget(lightIntensityBar);
dashboardGroup->setLayout(dashboardLayout);
// Right side: History and control
QGroupBox *historyGroup = new QGroupBox("灌溉历史记录");
historyTable = new QTableWidget(0, 4);
historyTable->setHorizontalHeaderLabels({"时间", "土壤湿度", "温度", "光照强度"});
historyTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
manualControlButton = new QPushButton("手动控制水泵");
manualControlButton->setCheckable(true);
QVBoxLayout *historyLayout = new QVBoxLayout;
historyLayout->addWidget(historyTable);
historyLayout->addWidget(manualControlButton);
historyGroup->setLayout(historyLayout);
// Add to main layout
mainLayout->addWidget(mapGroup);
mainLayout->addWidget(dashboardGroup);
mainLayout->addWidget(historyGroup);
setCentralWidget(centralWidget);
}
void MainWindow::setupConnections()
{
connect(dataTimer, &QTimer::timeout, this, &MainWindow::fetchData);
connect(networkManager, &QNetworkAccessManager::finished, this, &MainWindow::onDataReceived);
connect(manualControlButton, &QPushButton::clicked, this, &MainWindow::onManualControlClicked);
}
void MainWindow::fetchData()
{
QUrl url("https://your-huawei-cloud-api.com/sensor-data"); // Replace with actual API endpoint
QNetworkRequest request(url);
networkManager->get(request);
}
void MainWindow::onDataReceived(QNetworkReply *reply)
{
if (reply->error() == QNetworkReply::NoError) {
QByteArray response = reply->readAll();
QJsonDocument jsonDoc = QJsonDocument::fromJson(response);
QJsonObject jsonObj = jsonDoc.object();
if (jsonObj.contains("current") && jsonObj["current"].isObject()) {
updateDashboard(jsonObj["current"].toObject());
}
if (jsonObj.contains("history") && jsonObj["history"].isArray()) {
updateHistoryTable(jsonObj["history"].toArray());
}
} else {
QMessageBox::warning(this, "Error", "Failed to fetch data from cloud.");
}
reply->deleteLater();
}
void MainWindow::updateDashboard(const QJsonObject &data)
{
int soilHumidity = data["soil_humidity"].toInt();
float temperature = data["temperature"].toDouble();
int lightIntensity = data["light_intensity"].toInt();
soilHumidityLabel->setText(QString("土壤湿度: %1%").arg(soilHumidity));
soilHumidityBar->setValue(soilHumidity);
temperatureLabel->setText(QString("温度: %1°C").arg(temperature));
temperatureBar->setValue(static_cast<int>(temperature));
lightIntensityLabel->setText(QString("光照强度: %1 lux").arg(lightIntensity));
lightIntensityBar->setValue(lightIntensity);
}
void MainWindow::updateHistoryTable(const QJsonArray &history)
{
historyTable->setRowCount(history.size());
for (int i = 0; i < history.size(); ++i) {
QJsonObject record = history[i].toObject();
historyTable->setItem(i, 0, new QTableWidgetItem(record["timestamp"].toString()));
historyTable->setItem(i, 1, new QTableWidgetItem(QString::number(record["soil_humidity"].toInt())));
historyTable->setItem(i, 2, new QTableWidgetItem(QString::number(record["temperature"].toDouble())));
historyTable->setItem(i, 3, new QTableWidgetItem(QString::number(record["light_intensity"].toInt())));
}
}
void MainWindow::onManualControlClicked()
{
pumpStatus = !pumpStatus;
QString action = pumpStatus ? "on" : "off";
QUrl url(QString("https://your-huawei-cloud-api.com/control-pump?action=%1").arg(action)); // Replace with actual API
QNetworkRequest request(url);
networkManager->get(request);
manualControlButton->setText(pumpStatus ? "关闭水泵" : "开启水泵");
}
4. datamanager.h
(可选,但已集成到MainWindow)
由于网络处理直接在主窗口中实现,这里不需要单独的DataManager类。但如果需要扩展,可以创建。
说明:
- • 代码假设华为云API端点返回JSON数据,包含"current"对象和"history"数组。
- • UI包括简单的农田地图(绿色矩形)、仪表盘(标签和进度条)和历史记录表格。
- • 定时每5秒获取数据。
- • 手动控制按钮发送请求到云API控制水泵。
注意:替换API URL为实际华为云端点,并处理认证如果 needed(代码中未包含认证,实际使用时可能需要添加)。
模块代码设计
#include <stdint.h>
// Register definitions
#define RCC_BASE 0x40021000
#define RCC_CR (*((volatile uint32_t *)(RCC_BASE + 0x00)))
#define RCC_CFGR (*((volatile uint32_t *)(RCC_BASE + 0x04)))
#define RCC_APB2ENR (*((volatile uint32_t *)(RCC_BASE + 0x18)))
#define RCC_APB1ENR (*((volatile uint32_t *)(RCC_BASE + 0x1C)))
#define GPIOA_BASE 0x40010800
#define GPIOA_CRL (*((volatile uint32_t *)(GPIOA_BASE + 0x00)))
#define GPIOA_CRH (*((volatile uint32_t *)(GPIOA_BASE + 0x04)))
#define GPIOA_IDR (*((volatile uint32_t *)(GPIOA_BASE + 0x08)))
#define GPIOA_ODR (*((volatile uint32_t *)(GPIOA_BASE + 0x0C)))
#define GPIOB_BASE 0x40010C00
#define GPIOB_CRL (*((volatile uint32_t *)(GPIOB_BASE + 0x00)))
#define GPIOB_CRH (*((volatile uint32_t *)(GPIOB_BASE + 0x04)))
#define GPIOB_IDR (*((volatile uint32_t *)(GPIOB_BASE + 0x08)))
#define GPIOB_ODR (*((volatile uint32_t *)(GPIOB_BASE + 0x0C)))
#define GPIOC_BASE 0x40011000
#define GPIOC_CRL (*((volatile uint32_t *)(GPIOC_BASE + 0x00)))
#define GPIOC_CRH (*((volatile uint32_t *)(GPIOC_BASE + 0x04)))
#define GPIOC_IDR (*((volatile uint32_t *)(GPIOC_BASE + 0x08)))
#define GPIOC_ODR (*((volatile uint32_t *)(GPIOC_BASE + 0x0C)))
#define ADC1_BASE 0x40012400
#define ADC1_SR (*((volatile uint32_t *)(ADC1_BASE + 0x00)))
#define ADC1_CR1 (*((volatile uint32_t *)(ADC1_BASE + 0x04)))
#define ADC1_CR2 (*((volatile uint32_t *)(ADC1_BASE + 0x08)))
#define ADC1_SMPR1 (*((volatile uint32_t *)(ADC1_BASE + 0x0C)))
#define ADC1_SMPR2 (*((volatile uint32_t *)(ADC1_BASE + 0x10)))
#define ADC1_SQR1 (*((volatile uint32_t *)(ADC1_BASE + 0x2C)))
#define ADC1_SQR2 (*((volatile uint32_t *)(ADC1_BASE + 0x30)))
#define ADC1_SQR3 (*((volatile uint32_t *)(ADC1_BASE + 0x34)))
#define ADC1_DR (*((volatile uint32_t *)(ADC1_BASE + 0x4C)))
#define I2C1_BASE 0x40005400
#define I2C1_CR1 (*((volatile uint32_t *)(I2C1_BASE + 0x00)))
#define I2C1_CR2 (*((volatile uint32_t *)(I2C1_BASE + 0x04)))
#define I2C1_DR (*((volatile uint32_t *)(I2C1_BASE + 0x10)))
#define I2C1_SR1 (*((volatile uint32_t *)(I2C1_BASE + 0x14)))
#define I2C1_SR2 (*((volatile uint32_t *)(I2C1_BASE + 0x18)))
#define I2C1_CCR (*((volatile uint32_t *)(I2C1_BASE + 0x1C)))
#define I2C1_TRISE (*((volatile uint32_t *)(I2C1_BASE + 0x20)))
#define USART1_BASE 0x40013800
#define USART1_SR (*((volatile uint32_t *)(USART1_BASE + 0x00)))
#define USART1_DR (*((volatile uint32_t *)(USART1_BASE + 0x04)))
#define USART1_BRR (*((volatile uint32_t *)(USART1_BASE + 0x08)))
#define USART1_CR1 (*((volatile uint32_t *)(USART1_BASE + 0x0C)))
#define USART1_CR2 (*((volatile uint32_t *)(USART1_BASE + 0x10)))
#define USART1_CR3 (*((volatile uint32_t *)(USART1_BASE + 0x14)))
// Delay functions (crude implementation)
void delay_ms(uint32_t ms) {
for (uint32_t i = 0; i < ms * 1000; i++) {
__asm__("nop");
}
}
void delay_us(uint32_t us) {
for (uint32_t i = 0; i < us; i++) {
__asm__("nop");
}
}
// System initialization
void SystemInit(void) {
// Enable HSI
RCC_CR |= (1 << 0);
while (!(RCC_CR & (1 << 1)));
// Set system clock to HSI
RCC_CFGR &= ~(0x3 << 0);
// Enable clocks for GPIOA, GPIOB, GPIOC, ADC1, USART1, I2C1
RCC_APB2ENR |= (1 << 2) | (1 << 3) | (1 << 4) | (1 << 9) | (1 << 14);
RCC_APB1ENR |= (1 << 21);
}
// GPIO initialization
void GPIO_Init(void) {
// PA0: ADC1 input (soil moisture)
GPIOA_CRL &= ~(0xF << 0);
// PA1: DHT11 (one-wire, initially output)
GPIOA_CRL &= ~(0xF << 4);
GPIOA_CRL |= (0x1 << 4); // Output push-pull, 10MHz
// PA9: USART1 TX (alternate push-pull)
GPIOA_CRH &= ~(0xF << 4);
GPIOA_CRH |= (0xA << 4); // Alternate output push-pull, 2MHz
// PA10: USART1 RX (input floating)
GPIOA_CRH &= ~(0xF << 8);
GPIOA_CRH |= (0x1 << 8); // Input floating
// PB6: I2C1 SCL (alternate open-drain)
GPIOB_CRL &= ~(0xF << 24);
GPIOB_CRL |= (0xB << 24); // Alternate open-drain, 2MHz
// PB7: I2C1 SDA (alternate open-drain)
GPIOB_CRL &= ~(0xF << 28);
GPIOB_CRL |= (0xB << 28); // Alternate open-drain, 2MHz
// PC13: Pump control (output push-pull)
GPIOC_CRH &= ~(0xF << 20);
GPIOC_CRH |= (0x4 << 20); // Output push-pull, 10MHz
}
// ADC1 initialization
void ADC1_Init(void) {
// Set sample time for channel 0 (239.5 cycles)
ADC1_SMPR2 |= (0x7 << 0);
// Single conversion, sequence length 1
ADC1_SQR1 &= ~(0xF << 20);
ADC1_SQR1 |= (0x0 << 20);
// Set channel 0 as first in sequence
ADC1_SQR3 &= ~(0x1F << 0);
ADC1_SQR3 |= (0x0 << 0);
// Enable ADC1
ADC1_CR2 |= (1 << 0);
// Calibrate ADC
ADC1_CR2 |= (1 << 3);
while (ADC1_CR2 & (1 << 3));
ADC1_CR2 |= (1 << 2);
while (ADC1_CR2 & (1 << 2));
}
// I2C1 initialization
void I2C1_Init(void) {
// Set clock frequency (8MHz)
I2C1_CR2 = 8;
// Set CCR for 100kHz (assuming APB1 clock 4MHz)
I2C1_CCR = 20;
// Set rise time
I2C1_TRISE = 5;
// Enable I2C1
I2C1_CR1 |= (1 << 0);
}
// USART1 initialization
void USART1_Init(void) {
// Set baud rate to 9600 (8MHz clock)
USART1_BRR = 0x3415;
// Enable USART1, TX, and RX
USART1_CR1 |= (1 << 13) | (1 << 3) | (1 << 2);
}
// DHT11 functions
void DHT11_Start(void) {
// Set PA1 as output push-pull
GPIOA_CRL &= ~(0xF << 4);
GPIOA_CRL |= (0x1 << 4);
// Pull low for 20ms
GPIOA_ODR &= ~(1 << 1);
delay_ms(20);
// Set as input floating
GPIOA_CRL &= ~(0xF << 4);
GPIOA_CRL |= (0x4 << 4);
GPIOA_ODR |= (1 << 1); // Enable pull-up
}
uint8_t DHT11_Read_Byte(void) {
uint8_t data = 0;
for (int i = 0; i < 8; i++) {
while (!(GPIOA_IDR & (1 << 1))); // Wait for high
delay_us(30);
if (GPIOA_IDR & (1 << 1)) {
data |= (1 << (7 - i));
while (GPIOA_IDR & (1 << 1)); // Wait for low
}
}
return data;
}
// I2C1 helper functions
void I2C1_Start(void) {
I2C1_CR1 |= (1 << 8);
while (!(I2C1_SR1 & (1 << 0)));
}
void I2C1_Stop(void) {
I2C1_CR1 |= (1 << 9);
}
void I2C1_Write_Byte(uint8_t data) {
I2C1_DR = data;
while (!(I2C1_SR1 & (1 << 7)));
}
uint8_t I2C1_Read_Byte(void) {
while (!(I2C1_SR1 & (1 << 6)));
return I2C1_DR;
}
// GY-30 (BH1750) initialization
void GY30_Init(void) {
I2C1_Start();
I2C1_Write_Byte(0x46); // Address 0x23 write
I2C1_Write_Byte(0x01); // Power on
I2C1_Stop();
}
uint16_t GY30_Read_Light(void) {
I2C1_Start();
I2C1_Write_Byte(0x46);
I2C1_Write_Byte(0x10); // Continuous high res mode
I2C1_Stop();
delay_ms(180);
I2C1_Start();
I2C1_Write_Byte(0x47); // Address 0x23 read
uint8_t high = I2C1_Read_Byte();
uint8_t low = I2C1_Read_Byte();
I2C1_Stop();
return (high << 8) | low;
}
// ADC read function
uint16_t ADC1_Read(uint8_t channel) {
ADC1_CR2 |= (1 << 0);
while (!(ADC1_SR & (1 << 1)));
return ADC1_DR;
}
// USART1 helper functions
void USART1_Send_Byte(uint8_t data) {
while (!(USART1_SR & (1 << 7)));
USART1_DR = data;
}
void USART1_Send_String(char *str) {
while (*str) {
USART1_Send_Byte(*str++);
}
}
uint8_t USART1_Receive_Byte(void) {
while (!(USART1_SR & (1 << 5)));
return USART1_DR;
}
// Main function
int main(void) {
SystemInit();
GPIO_Init();
ADC1_Init();
I2C1_Init();
USART1_Init();
GY30_Init();
// Initialize ESP8266 (simplified)
USART1_Send_String("AT\r\n");
delay_ms(1000);
USART1_Send_String("AT+CWMODE=1\r\n");
delay_ms(1000);
USART1_Send_String("AT+CWJAP=\"SSID\",\"password\"\r\n");
delay_ms(5000);
USART1_Send_String("AT+CIPSTART=\"TCP\",\"192.168.1.100\",80\r\n");
delay_ms(2000);
#define SOIL_THRESHOLD 500
while (1) {
// Read soil moisture
uint16_t soil_moisture = ADC1_Read(0);
// Read DHT11
DHT11_Start();
while (GPIOA_IDR & (1 << 1));
while (!(GPIOA_IDR & (1 << 1)));
uint8_t humidity_int = DHT11_Read_Byte();
uint8_t humidity_dec = DHT11_Read_Byte();
uint8_t temp_int = DHT11_Read_Byte();
uint8_t temp_dec = DHT11_Read_Byte();
uint8_t checksum = DHT11_Read_Byte();
if ((humidity_int + humidity_dec + temp_int + temp_dec) != checksum) {
continue;
}
float temperature = temp_int + temp_dec / 10.0;
float humidity = humidity_int + humidity_dec / 10.0;
// Read light intensity
uint16_t light = GY30_Read_Light();
// Control pump
if (soil_moisture < SOIL_THRESHOLD) {
GPIOC_ODR |= (1 << 13);
} else {
GPIOC_ODR &= ~(1 << 13);
}
// Send data to Huawei Cloud
char data_str[50];
int len = sprintf(data_str, "temp=%.1f&humidity=%.1f&light=%d&soil=%d", temperature, humidity, light, soil_moisture);
char send_cmd[20];
sprintf(send_cmd, "AT+CIPSEND=%d\r\n", len);
USART1_Send_String(send_cmd);
delay_ms(100);
USART1_Send_String(data_str);
delay_ms(1000);
// Check for manual control
if (USART1_SR & (1 << 5)) {
uint8_t cmd = USART1_Receive_Byte();
if (cmd == '1') {
GPIOC_ODR |= (1 << 13);
} else if (cmd == '0') {
GPIOC_ODR &= ~(1 << 13);
}
}
delay_ms(5000);
}
}
项目核心代码
#include "stm32f10x.h"
#include <string.h>
// 假设的驱动函数声明
void ADC1_Init(void);
uint16_t ADC1_Read(uint8_t channel);
void DHT11_Init(void);
void DHT11_Read(float *temperature, float *humidity);
void GY30_Init(void);
uint16_t GY30_ReadLight(void);
void UART1_Init(uint32_t baudrate);
void UART1_SendString(char *str);
void Relay_Init(void);
void Relay_On(void);
void Relay_Off(void);
void ESP8266_SendData(float temp, float humi, uint16_t light, uint16_t soil_moisture);
// 全局变量和定义
#define SOIL_MOISTURE_THRESHOLD 500 // ADC阈值示例,需校准
#define PUMP_ON 1
#define PUMP_OFF 0
volatile uint8_t pump_status = PUMP_OFF;
volatile uint8_t manual_control = 0; // 0:自动模式, 1:手动模式
char uart_rx_buffer[64];
volatile uint8_t uart_rx_index = 0;
// 简单的延时函数
void delay_ms(volatile uint32_t count) {
for(volatile uint32_t i = 0; i < count * 1000; i++);
}
// UART1中断处理函数(用于接收数据)
void USART1_IRQHandler(void) {
if(USART1->SR & USART_SR_RXNE) {
char received_char = USART1->DR;
if(received_char == '\n' || uart_rx_index >= sizeof(uart_rx_buffer) - 1) {
uart_rx_buffer[uart_rx_index] = '\0';
if(strcmp(uart_rx_buffer, "PUMP_ON") == 0) {
manual_control = 1;
Relay_On();
pump_status = PUMP_ON;
} else if(strcmp(uart_rx_buffer, "PUMP_OFF") == 0) {
manual_control = 1;
Relay_Off();
pump_status = PUMP_OFF;
} else if(strcmp(uart_rx_buffer, "AUTO") == 0) {
manual_control = 0;
}
uart_rx_index = 0;
} else {
uart_rx_buffer[uart_rx_index++] = received_char;
}
}
}
int main(void) {
// 启用外设时钟
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_ADC1EN | RCC_APB2ENR_USART1EN;
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
// 初始化外设
ADC1_Init();
DHT11_Init();
GY30_Init();
UART1_Init(115200);
Relay_Init();
// 启用UART1接收中断
USART1->CR1 |= USART_CR1_RXNEIE;
NVIC_EnableIRQ(USART1_IRQn);
while(1) {
float temperature, humidity;
uint16_t light_intensity, soil_moisture;
// 读取传感器数据
soil_moisture = ADC1_Read(0); // 假设土壤湿度传感器在ADC通道0
DHT11_Read(&temperature, &humidity);
light_intensity = GY30_ReadLight();
// 自动控制逻辑
if(!manual_control) {
if(soil_moisture < SOIL_MOISTURE_THRESHOLD) {
Relay_On();
pump_status = PUMP_ON;
} else {
Relay_Off();
pump_status = PUMP_OFF;
}
}
// 上传数据到华为云
ESP8266_SendData(temperature, humidity, light_intensity, soil_moisture);
// 延时5秒
delay_ms(5000);
}
}
总结
该系统是一个基于STM32微控制器的智能农业灌溉解决方案,旨在通过实时监测关键环境参数来实现高效的农田管理。系统能够自动检测土壤湿度、环境温度和光照强度,并根据预设阈值智能控制水泵的启停,同时支持用户通过远程手动方式进行干预,确保了灌溉的灵活性和可靠性。
在硬件配置上,系统以STM32F103C8T6最小系统核心板作为主控单元,集成了土壤湿度传感器FC-28、DHT11温湿度传感器和GY-30光照传感器用于数据采集,并通过5V直流水泵和继电器模块执行灌溉操作。ESP8266-01S Wi-Fi模块负责与华为云平台进行通信,而洞洞板焊接的电源控制电路和杜邦线连接确保了外设的稳定供电和信号传输。
软件层面,系统将传感器数据和灌溉记录实时上传至华为云管理平台,实现了数据的云端存储和远程访问。此外,QT开发的上位机界面提供了直观的可视化功能,包括农田地图展示、数据仪表盘监控以及灌溉历史记录查询,大大增强了系统的可操作性和用户体验。
总体而言,该系统结合了硬件传感、云平台集成和软件可视化,实现了农业灌溉的智能化和自动化,不仅提升了水资源利用效率,还为现代精准农业提供了可靠的技术支持。
- 点赞
- 收藏
- 关注作者
评论(0)