目录
一、设计背景和意义
1.1设计背景
随着我国人口结构变化和农村劳动力持续减少,传统农业的粗放式管理模式已难以满足现代农业对"高产、高效、绿色、智能"的发展目标。国家在"十四五"规划和乡村振兴战略中,明确提出要加快智慧农业建设,推动信息技术、自动化设备与农业深度融合,实现农业生产从"经验型"向"数据驱动型"的根本转变。在这一背景下,设施农业------特别是蔬菜水果大棚种植,因其具备相对封闭、易于管理、高产高效的特性,成为推动农业智能化发展的重要突破口。
然而,当前大量大棚管理仍依赖人工经验调节温湿度、水分和光照,存在反应滞后、调控精度低、资源浪费严重的问题。例如,过度浇水会造成根系腐烂与肥力流失,光照不足无法及时补光将影响果蔬品质,而空气温度、湿度及CO₂浓度变化若不能及时处理,则易引发病虫害、作物早衰等问题。这些问题不仅影响了作物生长周期和产量,也制约了农业智能化水平的提升。
1.2设计意义
因此,设计一套集成多种传感器、具备自动控制能力、支持远程交互的智能农业环境调控系统,成为解决上述问题的有效途径。通过对空气温湿度、土壤湿度与温度、光照强度及CO2浓度的实时监测,并依据预设阈值自动调节风扇、水泵与补光灯工作状态,能够显著提升大棚环境调控的精准性与时效性。同时,借助ESP8266 WiFi模块接入"机智云"等物联网平台,使用户可通过APP远程查看数据、控制设备、查询历史信息,实现管理"去人工化"和决策"数据化"。
本课题研究意义不仅体现在应用层面,还具有较强的综合工程实践价值与学术研究价值。课题融合了嵌入式系统开发、传感器数据采集与融合、自动控制理论、物联网通信、云平台接口调用等多个专业方向,能够有效锻炼学生在项目规划、软硬件设计、系统集成与调试等方面的综合能力。此外,该系统设计具有良好的扩展性与可移植性,未来可推广至果园、苗圃、智能盆栽等多种应用场景,具有广泛的推广前景。
二、实物效果展示
2.1实物图片

2.2实物演示视频
【开源】基于STM32的智慧农业大棚系统
三、硬件功能简介
3.1项目功能详解
- 传感器检测:检测温湿度、土壤湿度、二氧化碳、光照强度、土壤温度等数据
- 数据显示:0.96OLED屏幕显示全部的传感器数据以及传感器的阈值等数据。
- 执行机构:控制通风扇通风、水泵灌溉、补光灯补光等。
- 接入云平台:系统通过ESP8266 WIFI联网后,接入机智云平台。
- App远程监控:通过App远程监控全部传感器数据;App远程控制所有执行机构。
- 阈值数据设定:系统通过按键设定阈值,也可以通过手机App远程设定。
- 模式切换:可以通过按键或者手机App实现自动/手动模式的切换。
- 手动模式:通过手机App或小程序控制风扇、水泵、补光灯等。
- 自动模式:当环境温度或湿度超过阈值,自动开启风扇进行降温或者除湿;当土壤湿度低于阈值或者温度高于阈值,会自动打开水泵灌溉降温;当光照强度低于阈值,自动开启补光灯进行补光;当二氧化碳高于阈值,自动开启风扇通风;
3.2元器件清单
- STM32F103C8T6主控
- 0.96OLED 显示屏幕
- ESP8266-WiFi
- DHT11温湿度
- 光敏电阻光照检测
- 土壤湿度检测
- DS18B20土壤温度检测
- JW01二氧化碳传感器
- LED补光灯
- 继电器+水泵
- 继电器+风扇
- 蜂鸣器声光报警
- 步进电机(施肥)
- 按键
四、主框图与软件流程图
主框图

流程图

五、硬件PCB展示

六、软件程序设计
cpp
#include "stm32f10x.h" // Device header
#include "iwdg.h"
#include "adcx.h"
#include "ldr.h"
#include "oled.h"
#include "dht11.h"
#include "led.h"
#include "key.h"
#include "tim2.h"
#include "tim3.h"
#include "usart3.h"
#include "usart.h"
#include "yl_69.h"
#include "motor.h"
#include "sensormodules.h"
#include "gizwits_product.h"
#include "flash.h"
#define KEY_1 1
#define KEY_2 2
#define KEY_3 3
#define KEY_4 4
#define FLASH_START_ADDR 0x0801f000 //写入的起始地址
SensorModules sensorData; //声明传感器数据结构体变量
SensorThresholdValue Sensorthreshold; //声明传感器阈值结构体变量
uint8_t menu = 1; //显示菜单变量
uint8_t OLED_Clear_Flag; //阈值设置界面的清屏标志位
uint8_t mode = 0; //系统模式
enum
{
display_page1 = 1,
display_page2,
settingsPage
}MenuPages;
/**
* @brief 显示菜单1的固定内容
* @param 无
* @retval 无
*/
void OLED_Menu1(void)
{
//显示系统名
OLED_ShowChinese(1, 1, 0);
OLED_ShowChinese(1, 2, 1);
OLED_ShowChinese(1, 3, 2);
OLED_ShowChinese(1, 4, 3);
OLED_ShowChinese(1, 5, 4);
OLED_ShowChinese(1, 6, 5);
OLED_ShowChinese(1, 7, 6);
OLED_ShowChinese(1, 8, 7);
//显示"系统模式:"
OLED_ShowChinese(2, 1, 6);
OLED_ShowChinese(2, 2, 7);
OLED_ShowChinese(2, 3, 19);
OLED_ShowChinese(2, 4, 20);
OLED_ShowChar(2, 9, ':');
//显示"光照强度: Lux"
OLED_ShowChinese(3, 1, 15);
OLED_ShowChinese(3, 2, 16);
OLED_ShowChinese(3, 3, 28);
OLED_ShowChinese(3, 4, 29);
OLED_ShowChar(3, 9, ':');
OLED_ShowString(3, 14, "Lux");
//显示"二氧化碳: "
OLED_ShowChinese(4, 1, 24);
OLED_ShowChinese(4, 2, 25);
OLED_ShowChinese(4, 3, 26);
OLED_ShowChinese(4, 4, 27);
OLED_ShowChar(4, 9, ':');
OLED_ShowString(4, 14, "ppm");
}
/**
* @brief 显示菜单2的固定内容
* @param 无
* @retval 无
*/
void OLED_Menu2(void)
{
//显示"环境温度: C"
OLED_ShowChinese(1, 1, 13);
OLED_ShowChinese(1, 2, 14);
OLED_ShowChinese(1, 3, 8);
OLED_ShowChinese(1, 4, 9);
OLED_ShowChar(1, 9, ':');
OLED_ShowChar(1, 12, 'C');
//显示"环境湿度: %"
OLED_ShowChinese(2, 1, 13);
OLED_ShowChinese(2, 2, 14);
OLED_ShowChinese(2, 3, 10);
OLED_ShowChinese(2, 4, 9);
OLED_ShowChar(2, 9, ':');
OLED_ShowChar(2, 12, '%');
//显示"土壤温度: C"
OLED_ShowChinese(3, 1, 11);
OLED_ShowChinese(3, 2, 12);
OLED_ShowChinese(3, 3, 8);
OLED_ShowChinese(3, 4, 9);
OLED_ShowChar(3, 9, ':');
OLED_ShowChar(3, 12, 'C');
//显示"土壤湿度: %"
OLED_ShowChinese(4, 1, 11);
OLED_ShowChinese(4, 2, 12);
OLED_ShowChinese(4, 3, 10);
OLED_ShowChinese(4, 4, 9);
OLED_ShowChar(4, 9, ':');
OLED_ShowChar(4, 12, '%');
}
/**
* @brief 显示菜单1的传感器数据
* @param 无
* @retval 无
*/
void SensorDataDisplay1(void)
{
//显示系统状态数据
if (!mode)
{
OLED_ShowChinese(2, 6, 21);
OLED_ShowChinese(2, 7, 22);
}
else
{
OLED_ShowChinese(2, 6, 23);
OLED_ShowChinese(2, 7, 22);
}
//显示光照强度数据
OLED_ShowNum(3, 10, sensorData.lux, 4);
//显示CO2浓度数据
OLED_ShowNum(4, 10, sensorData.CO2, 4);
}
/**
* @brief 显示菜单2的传感器数据
* @param 无
* @retval 无
*/
void SensorDataDisplay2(void)
{
//显示环境温度数据
OLED_ShowNum(1, 10, sensorData.temp, 2);
//显示环境湿度数据
OLED_ShowNum(2, 10, sensorData.humi, 2);
//显示土壤温度数据
OLED_ShowNum(3, 10, sensorData.soilTemp, 2);
//显示土壤湿度数据
OLED_ShowNum(4, 10, sensorData.soilHumi, 2);
}
/**
* @brief 显示阈值设置界面1的固定内容
* @param 无
* @retval 无
*/
void OLED_settingsPage1(void)
{
//显示"环境温度:"
OLED_ShowChinese(1, 2, 13);
OLED_ShowChinese(1, 3, 14);
OLED_ShowChinese(1, 4, 8);
OLED_ShowChinese(1, 5, 9);
OLED_ShowChar(1, 11, ':');
//显示"环境湿度:"
OLED_ShowChinese(2, 2, 13);
OLED_ShowChinese(2, 3, 14);
OLED_ShowChinese(2, 4, 10);
OLED_ShowChinese(2, 5, 9);
OLED_ShowChar(2, 11, ':');
//显示"土壤温度:"
OLED_ShowChinese(3, 2, 11);
OLED_ShowChinese(3, 3, 12);
OLED_ShowChinese(3, 4, 8);
OLED_ShowChinese(3, 5, 9);
OLED_ShowChar(3, 11, ':');
//显示"土壤湿度:"
OLED_ShowChinese(4, 2, 11);
OLED_ShowChinese(4, 3, 12);
OLED_ShowChinese(4, 4, 10);
OLED_ShowChinese(4, 5, 9);
OLED_ShowChar(4, 11, ':');
}
/**
* @brief 显示阈值设置界面2的固定内容
* @param 无
* @retval 无
*/
void OLED_settingsPage2(void)
{
//显示"光照强度:"
OLED_ShowChinese(1, 2, 15);
OLED_ShowChinese(1, 3, 16);
OLED_ShowChinese(1, 4, 28);
OLED_ShowChinese(1, 5, 29);
OLED_ShowChar(1, 11, ':');
//显示"二氧化碳:"
OLED_ShowChinese(2, 2, 24);
OLED_ShowChinese(2, 3, 25);
OLED_ShowChinese(2, 4, 26);
OLED_ShowChinese(2, 5, 27);
OLED_ShowChar(2, 11, ':');
}
/**
* @brief 显示阈值界面1的传感器数据
* @param 无
* @retval 无
*/
void settingsThresholdDisplay1(void)
{
//显示环境温度阈值数据
OLED_ShowNum(1, 13, Sensorthreshold.tempValue, 2);
//显示环境湿度阈值数据
OLED_ShowNum(2, 13, Sensorthreshold.humiValue, 2);
//显示土壤温度阈值数据
OLED_ShowNum(3, 13, Sensorthreshold.soilTempValue, 2);
//显示土壤湿度阈值数据
OLED_ShowNum(4, 13, Sensorthreshold.soilHumiValue, 2);
}
/**
* @brief 显示阈值界面2的传感器数据
* @param 无
* @retval 无
*/
void settingsThresholdDisplay2(void)
{
//显示光照强度阈值数据
OLED_ShowNum(1, 13, Sensorthreshold.luxValue, 4);
//显示CO2浓度阈值数据
OLED_ShowNum(2, 13, Sensorthreshold.CO2Value, 4);
}
/**
* @brief 显示阈值界面的选择符号
* @param num 为显示的位置
* @retval 无
*/
void OLED_Option(uint8_t num)
{
switch(num)
{
case 1:
OLED_ShowChar(1,1,'>');
OLED_ShowChar(2,1,' ');
OLED_ShowChar(3,1,' ');
OLED_ShowChar(4,1,' ');
break;
case 2:
OLED_ShowChar(1,1,' ');
OLED_ShowChar(2,1,'>');
OLED_ShowChar(3,1,' ');
OLED_ShowChar(4,1,' ');
break;
case 3:
OLED_ShowChar(1,1,' ');
OLED_ShowChar(2,1,' ');
OLED_ShowChar(3,1,'>');
OLED_ShowChar(4,1,' ');
break;
case 4:
OLED_ShowChar(1,1,' ');
OLED_ShowChar(2,1,' ');
OLED_ShowChar(3,1,' ');
OLED_ShowChar(4,1,'>');
break;
case 5:
OLED_ShowChar(1,1,'>');
OLED_ShowChar(2,1,' ');
OLED_ShowChar(3,1,' ');
OLED_ShowChar(4,1,' ');
break;
case 6:
OLED_ShowChar(1,1,' ');
OLED_ShowChar(2,1,'>');
OLED_ShowChar(3,1,' ');
OLED_ShowChar(4,1,' ');
break;
default: break;
}
}
/**
* @brief 根据标志位控制步进电机的运行
* @param 无
* @retval 无
*/
void MotorOperation(void)
{
if (motorFlag == 1)
{
MOTOR_Direction_Angle(1, 0, 90, 1);
MOTOR_STOP();
motorFlag = 0;
}
else if (motorFlag == 2)
{
MOTOR_Direction_Angle(0, 0, 90, 1);
MOTOR_STOP();
motorFlag = 0;
}
}
/**
* @brief 记录阈值界面下按KEY1的次数
* @param 无
* @retval 返回次数
*/
uint8_t SetSelection(void)
{
static uint8_t count = 1;
if(KeyNum == KEY_1)
{
KeyNum = 0;
count++;
if (count == 5)
{
OLED_Clear();
}
else if (count > 6)
{
OLED_Clear();
count = 1;
}
}
return count;
}
/**
* @brief 对阈值界面的传感器阈值进行修改
* @param num 为当前用户需要更改的传感器阈值位置
* @retval 无
*/
void ThresholdModification(uint8_t num)
{
switch (num)
{
case 1:
if (KeyNum == 3)
{
KeyNum = 0;
Sensorthreshold.tempValue++;
if (Sensorthreshold.tempValue > 99)
{
Sensorthreshold.tempValue = 1;
}
}
else if (KeyNum == 4)
{
KeyNum = 0;
Sensorthreshold.tempValue--;
if (Sensorthreshold.tempValue < 1)
{
Sensorthreshold.tempValue = 99;
}
}
break;
case 2:
if (KeyNum == 3)
{
KeyNum = 0;
Sensorthreshold.humiValue++;
if (Sensorthreshold.humiValue > 99)
{
Sensorthreshold.humiValue = 1;
}
}
else if (KeyNum == 4)
{
KeyNum = 0;
Sensorthreshold.humiValue--;
if (Sensorthreshold.humiValue < 1)
{
Sensorthreshold.humiValue = 99;
}
}
break;
case 3:
if (KeyNum == 3)
{
KeyNum = 0;
Sensorthreshold.soilTempValue++;
if (Sensorthreshold.soilTempValue > 99)
{
Sensorthreshold.soilTempValue = 1;
}
}
else if (KeyNum == 4)
{
KeyNum = 0;
Sensorthreshold.soilTempValue--;
if (Sensorthreshold.soilTempValue < 1)
{
Sensorthreshold.soilTempValue = 99;
}
}
break;
case 4:
if (KeyNum == 3)
{
KeyNum = 0;
Sensorthreshold.soilHumiValue++;
if (Sensorthreshold.soilHumiValue > 99)
{
Sensorthreshold.soilHumiValue = 1;
}
}
else if (KeyNum == 4)
{
KeyNum = 0;
Sensorthreshold.soilHumiValue--;
if (Sensorthreshold.soilHumiValue < 1)
{
Sensorthreshold.soilHumiValue = 99;
}
}
break;
case 5:
if (KeyNum == 3)
{
KeyNum = 0;
Sensorthreshold.luxValue += 10;
if (Sensorthreshold.luxValue > 2000)
{
Sensorthreshold.luxValue = 0;
}
}
else if (KeyNum == 4)
{
KeyNum = 0;
Sensorthreshold.luxValue -= 10;
if (Sensorthreshold.luxValue > 2000)
{
Sensorthreshold.luxValue = 2000;
}
}
break;
case 6:
if (KeyNum == 3)
{
KeyNum = 0;
Sensorthreshold.CO2Value += 10;
if (Sensorthreshold.CO2Value > 2000)
{
Sensorthreshold.CO2Value = 0;
}
}
else if (KeyNum == 4)
{
KeyNum = 0;
Sensorthreshold.CO2Value -= 10;
if (Sensorthreshold.CO2Value > 2000)
{
Sensorthreshold.CO2Value = 2000;
}
}
break;
default: break;
}
}
/**
* @brief 获取二氧化碳数据
* @param *data 为数据传参
* @retval 无
*/
void CO2GetData(uint16_t *data)
{
if (Usart3_RxFlag == 1)
{
Usart3_RxFlag = 0;
*data = Usart3_RxPacket[1] * 256 + Usart3_RxPacket[2];
}
}
/**
* @brief 传感器数据扫描
* @param 无
* @retval 无
*/
void SensorScan(void)
{
DHT11_Read_Data(&sensorData.humi, &sensorData.temp);
YL69_PercentageData(&sensorData.soilHumi);
LDR_LuxData(&sensorData.lux);
CO2GetData(&sensorData.CO2);
DS18B20_Read_Temp(&sensorData.soilTemp);
}
int main(void)
{
ADCX_Init();
Timer2_Init(9,14398);
Uart2_Init(9600);
Uart1_Init(115200);
Uart3_Init();
IWDG_Init(); //初始化看门狗
LDR_Init();
YL69_Init();
OLED_Init();
DHT11_Init();
LED_Init();
Buzzer_Init();
Relay_Init();
MOTOR_Init();
Key_Init();
Sensorthreshold.CO2Value = FLASH_R(FLASH_START_ADDR); //从指定页的地址读FLASH
Sensorthreshold.luxValue = FLASH_R(FLASH_START_ADDR+2); //从指定页的地址读FLASH
Sensorthreshold.tempValue = FLASH_R(FLASH_START_ADDR+4); //从指定页的地址读FLASH
Sensorthreshold.humiValue = FLASH_R(FLASH_START_ADDR+6); //从指定页的地址读FLASH
Sensorthreshold.soilTempValue = FLASH_R(FLASH_START_ADDR+8); //从指定页的地址读FLASH
Sensorthreshold.soilHumiValue = FLASH_R(FLASH_START_ADDR+10); //从指定页的地址读FLASH
GENERAL_TIM_Init();
userInit(); //完成机智云初始赋值
gizwitsInit(); //开辟一个环形缓冲区
while (1)
{
IWDG_ReloadCounter(); //重新加载计数值 喂狗
SensorScan(); //获取传感器数据
switch (menu)
{
case display_page1:
SensorDataDisplay1(); //显示传感器1数据
OLED_Menu1(); //显示主页面1固定信息
if (KeyNum == KEY_2) //是否按下按键2
{
KeyNum = 0;
OLED_Clear(); //清屏
menu = display_page2; //menu = 主页面2
}
MotorOperation();
break;
case display_page2:
SensorDataDisplay2(); //显示传感器2数据
OLED_Menu2(); //显示主页面2固定信息
if (KeyNum == KEY_2) //是否按下按键2
{
KeyNum = 0;
OLED_Clear(); //清屏
menu = display_page1; //menu = 主页面1
}
MotorOperation();
break;
case settingsPage:
//从主页面跳转至设置页面时进行一次清屏
if (OLED_Clear_Flag)
{
OLED_Clear_Flag = 0; //清除清屏标志位
OLED_Clear(); //清屏
}
ThresholdModification(SetSelection()); //调节传感器阈值
OLED_Option(SetSelection()); //获取按键次数,从而判断">"显示位置
//按键次数小于等于4时,显示设置页面1
if (SetSelection() <= 4)
{
settingsThresholdDisplay1(); //显示传感器阈值1数据
OLED_settingsPage1(); //显示阈值设置界面1固定信息
}
else //否则显示设置页面2
{
settingsThresholdDisplay2(); //显示传感器阈值2数据
OLED_settingsPage2(); //显示阈值设置界面2固定信息
}
if (KeyNum == KEY_2) //判断用户是否按下退出按键
{
KeyNum = 0;
OLED_Clear(); //清屏
menu = display_page1; //回到主页面1
//存储修改的传感器阈值至flash内
FLASH_W(FLASH_START_ADDR, Sensorthreshold.CO2Value, Sensorthreshold.luxValue,
Sensorthreshold.tempValue, Sensorthreshold.humiValue,
Sensorthreshold.soilTempValue, Sensorthreshold.soilHumiValue);
}
break;
default: break;
}
userHandle(); //更新机智云数据点变量存储的值
gizwitsHandle((dataPoint_t *)¤tDataPoint); //数据上传至机智云
}
}
七、项目资料包内容
