【STM32项目开源】基于STM32的智慧农业大棚系统

目录

一、设计背景和意义

1.1设计背景

1.2设计意义

二、实物效果展示

2.1实物图片

2.2实物演示视频

三、硬件功能简介

3.1项目功能详解

3.2元器件清单

四、主框图与软件流程图

五、硬件PCB展示

六、软件程序设计

七、项目资料包内容

资料获取:查看主页介绍"充哥单片机设计"


一、设计背景和意义

1.1设计背景

随着我国人口结构变化和农村劳动力持续减少,传统农业的粗放式管理模式已难以满足现代农业对"高产、高效、绿色、智能"的发展目标。国家在"十四五"规划和乡村振兴战略中,明确提出要加快智慧农业建设,推动信息技术、自动化设备与农业深度融合,实现农业生产从"经验型"向"数据驱动型"的根本转变。在这一背景下,设施农业------特别是蔬菜水果大棚种植,因其具备相对封闭、易于管理、高产高效的特性,成为推动农业智能化发展的重要突破口。

然而,当前大量大棚管理仍依赖人工经验调节温湿度、水分和光照,存在反应滞后、调控精度低、资源浪费严重的问题。例如,过度浇水会造成根系腐烂与肥力流失,光照不足无法及时补光将影响果蔬品质,而空气温度、湿度及CO₂浓度变化若不能及时处理,则易引发病虫害、作物早衰等问题。这些问题不仅影响了作物生长周期和产量,也制约了农业智能化水平的提升。

1.2设计意义

因此,设计一套集成多种传感器、具备自动控制能力、支持远程交互的智能农业环境调控系统,成为解决上述问题的有效途径。通过对空气温湿度、土壤湿度与温度、光照强度及CO2浓度的实时监测,并依据预设阈值自动调节风扇、水泵与补光灯工作状态,能够显著提升大棚环境调控的精准性与时效性。同时,借助ESP8266 WiFi模块接入"机智云"等物联网平台,使用户可通过APP远程查看数据、控制设备、查询历史信息,实现管理"去人工化"和决策"数据化"。

本课题研究意义不仅体现在应用层面,还具有较强的综合工程实践价值与学术研究价值。课题融合了嵌入式系统开发、传感器数据采集与融合、自动控制理论、物联网通信、云平台接口调用等多个专业方向,能够有效锻炼学生在项目规划、软硬件设计、系统集成与调试等方面的综合能力。此外,该系统设计具有良好的扩展性与可移植性,未来可推广至果园、苗圃、智能盆栽等多种应用场景,具有广泛的推广前景。

二、实物效果展示

2.1实物图片

2.2实物演示视频

【开源】基于STM32的智慧农业大棚系统

三、硬件功能简介

3.1项目功能详解

  1. 传感器检测:检测温湿度、土壤湿度、二氧化碳、光照强度、土壤温度等数据
  2. 数据显示:0.96OLED屏幕显示全部的传感器数据以及传感器的阈值等数据。
  3. 执行机构:控制通风扇通风、水泵灌溉、补光灯补光等。
  4. 接入云平台:系统通过ESP8266 WIFI联网后,接入机智云平台。
  5. App远程监控:通过App远程监控全部传感器数据;App远程控制所有执行机构。
  6. 阈值数据设定:系统通过按键设定阈值,也可以通过手机App远程设定。
  7. 模式切换:可以通过按键或者手机App实现自动/手动模式的切换。
  8. 手动模式:通过手机App或小程序控制风扇、水泵、补光灯等。
  9. 自动模式:当环境温度或湿度超过阈值,自动开启风扇进行降温或者除湿;当土壤湿度低于阈值或者温度高于阈值,会自动打开水泵灌溉降温;当光照强度低于阈值,自动开启补光灯进行补光;当二氧化碳高于阈值,自动开启风扇通风;

3.2元器件清单

  1. STM32F103C8T6主控
  2. 0.96OLED 显示屏幕
  3. ESP8266-WiFi
  4. DHT11温湿度
  5. 光敏电阻光照检测
  6. 土壤湿度检测
  7. DS18B20土壤温度检测
  8. JW01二氧化碳传感器
  9. LED补光灯
  10. 继电器+水泵
  11. 继电器+风扇
  12. 蜂鸣器声光报警
  13. 步进电机(施肥)
  14. 按键

四、主框图与软件流程图

主框图

流程图

五、硬件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 *)&currentDataPoint);	//数据上传至机智云					
	}
}

七、项目资料包内容

资料获取:查看主页介绍"充哥单片机设计"

相关推荐
路弥行至2 小时前
C语言入门教程 | 第一讲:C语言零基础入门教程:第一个程序到变量运算详解
c语言·开发语言·经验分享·笔记·单片机·其他·课程设计
JiaWen技术圈4 小时前
机器人小脑的核心技术有哪些 ?
单片机·嵌入式硬件·机器人·硬件架构
要做朋鱼燕5 小时前
解析UART空闲中断与DMA接收机制
开发语言·笔记·单片机·嵌入式硬件·rtos·嵌入式软件
沪漂的码农5 小时前
MCU时钟源深度解析:内部晶振与外部晶振的技术博弈
c语言·单片机·嵌入式硬件
Mr成文5 小时前
【usb】windows usb驱动框架简介
windows·stm32·单片机
10001hours6 小时前
(基于江协科技)51单片机入门:2.独立按键
科技·嵌入式硬件·51单片机
Wave8457 小时前
STM32---了解
stm32·单片机·嵌入式硬件
STC_USB_CAN_80517 小时前
STC32G144K246-视频级动画效果演示
单片机·51单片机
jianqiang.xue7 小时前
ESP32-S3 入门教程:从环境搭建到物联网应用实战
c语言·单片机·嵌入式硬件·物联网·青少年编程·51单片机·嵌入式