STM32项目分享:植物生长环境监测系统

【低轨】Starlink星链-Ku波段下行信号信号结构解析 Starlink播发位于10.7~12.7GHz之间的下行信号,该信号的设计意图是用于宽带网络通信,但是,文章通过对该信号信号体制的详细分析,以及解调出所调制的完整码序列,为利用Starlink信号进行定位解算提供了必要条件。(注,码序列对应的是卫星版本0.9,1.0和1.5) 阅读详情

目录

一、引言

二、硬件设计

2.1 模块组成

2.2  原理图

2.3  PCB

三、软件设计

3.1 功能

3.2 main代码

四、系统实现与测试

4.1 B站视频介绍

五、结论与展望

5.1  小结

六、资料内容及获取


一、引言

本项目基于STM32主控,集成多路传感器与ESP8266通信模块,构建了一个集环境数据采集、本地智能决策于一体的自动化系统。通过实时监测温湿度、土壤水分及光照,并联动控制风扇、水泵、LED等设备,为植物创造一个精准可控的最佳生长环境。

实物:

二、硬件设计

2.1 模块组成
  •  STM32F103C8T6主控

传感/执行模块

  • 温湿度传感器

  • 土壤湿度传感器

  • 光照传感器

  • DRV8833驱动模块

  • 5V风扇

  • 5V水泵

人机交互模块

  • OLED显示屏

  • 按键输入

通信模块

  • ESP826601SWiFi通信

2.2  原理图

2.3  PCB

三、软件设计

3.1 功能
  • 环境参数实时监测:通过DHT11传感器采集空气温湿度,土壤湿度传感器检测土壤水分,光照传感器测量光照强度,全面监控植物生长环境

  • 双模式智能控制:支持自动和手动两种工作模式,自动模式下系统根据预设阈值自动调节风扇、水泵、LED补光灯等设备,手动模式下允许用户直接控制

  • 多界面人机交互:提供4个OLED显示界面,分别用于数据显示、设备控制、温湿度阈值设置、土壤光照阈值设置,通过按键进行切换和参数调节

  • 物联网远程监控:集成ESP8266 WiFi模块,定时向巴法云平台上传环境数据,并接收APP下发的控制指令和阈值设置命令

  • 智能报警功能:当任何环境参数超出阈值范围时,系统会通过蜂鸣器发出报警提示,并在云平台标记异常状态

  • 数据持久化存储:利用STM32内部Flash存储所有阈值参数,确保系统重启后设置不丢失,提高系统可靠性

3.2 main代码
#include "delay.h"
#include "sys.h"
#include "led.h" //控制器
#include "oled.h"
#include "esp8266.h"
#include "Timer.h"
#include "Key.h"
#include "stmflash.h"


// 传感/执行模块
#include "dht11.h"
#include "Soil.h"     // 土壤湿度传感器
#include "Light.h"    // 光照强度传感器

//C库
#include <string.h>
#include "stdio.h"
#include "math.h"
#include "stdbool.h"



/*********************************** 变量声明**********************************/

extern unsigned char esp8266_buf[buf_len];

typedef enum {
    auto_mode,
    manual_mode,
}control_mode;
//控制模式变量
control_mode  MODE= manual_mode;//开机为手动模式



//阈值结构体
struct Set_data
{
  u16 temp_H;    //温度上限
	u16 temp_L;    //温度下限
	u16 humi_H;    //湿度上限
	u16 humi_L;    //湿度下限
	u16 soil_H;    //土壤湿度上限
	u16 soil_L;    //土壤湿度下限
	u16 light_H;   //光照强度上限
	u16 light_L;   //光照强度下限
};

struct Set_data Out_data={30,20,80,40,70,30,80,20};//初始化阈值

//数据获取
u16 temp_value=0;
u16 humi_value=0;
u16 soil_value=0;
u16 light_value=0;

DHT11_Data_TypeDef DHT11_Data;//dht11添加变量 

////按键相关的变量
u8 key_num=0,Page_flag=1;
char TIMER_IT=0,WiFiInit_time,Flagout=1;

//oled相关变量
u8 oled_Fill_flag=1;//OLED清屏标志
char oled_str[100];//OLED显示缓存
char date[200];//esp8266发送缓存

bool beep_flag=false;//蜂鸣器标志位
u8 alarm_fiag=0;//环境异常报警标志位
/********************************函数声明**********************************/
// 在main函数前添加函数声明
void Page_data(void);    // 数据显示界面
void Page_control(void);    //控制外设
void Page_setting1(void); // 设置界面
void Page_setting2(void); // 设置界面
void APP_Control(void); //APP数据下发
void BI_BI(void); //哔哔提醒数据下发


int main(void){
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	DHT11_Init();//初始化DHT11
	Soil_Init();//初始化土壤湿度传感器
	Light_Init();//初始化光照强度传感器
	
	LED_Init();

	FAN=OFF;//风扇关闭
	PUMP=OFF;//水泵关闭
	LED=OFF;//LED关闭
	BEEP=OFF;//蜂鸣器关闭

  Key_Init();//按键初始化
  OLED_Init();//OLED初始化
  OLED_Clear(0);
	    
	do
	{
			OLED_ShowText(0, 0,(u8*) "是否需要联网?", 0);
			OLED_ShowText(8, 4,(u8*) "1.是  2.否", 0);
			key_num=KEY_Scan(0);//按键扫描
			if(key_num==1)//需要联网
			{
					OLED_Clear(0);
					Timer_Init();
					ESP8266_Init(115200);
					break;
			}
	}while(key_num!=2); 
		
   OLED_Clear(0);

    /*读出内存数据*/
    STMFLASH_Read(FLASH_SAVE_ADDR,  &Out_data.temp_H,1);
    delay_ms(100);
		STMFLASH_Read(FLASH_SAVE_ADDR+2,  &Out_data.temp_L,1);
    delay_ms(100);
		STMFLASH_Read(FLASH_SAVE_ADDR+4,  &Out_data.humi_H,1);
    delay_ms(100);
		STMFLASH_Read(FLASH_SAVE_ADDR+6,  &Out_data.humi_L,1);
    delay_ms(100);
		STMFLASH_Read(FLASH_SAVE_ADDR+8,  &Out_data.soil_H,1);
    delay_ms(100);
		STMFLASH_Read(FLASH_SAVE_ADDR+10,  &Out_data.soil_L,1);
    delay_ms(100);
		STMFLASH_Read(FLASH_SAVE_ADDR+12,  &Out_data.light_H,1);
    delay_ms(100);
		STMFLASH_Read(FLASH_SAVE_ADDR+14,  &Out_data.light_L,1);
    delay_ms(100);
   
   

	while(1)
	{
     /*********************************数据处理区*********************************/ 
		
		     //获取温湿度 
			   Read_DHT11(&DHT11_Data);    
					temp_value= DHT11_Data.temp_int; //获取温度
					humi_value= DHT11_Data.humi_int; //获取湿度
					
					//获取土壤湿度
					soil_value = (u16)Soil_Value_Conversion(); //获取土壤湿度
					
					//获取光照强度
					light_value = (u16)Light_Value_Conversion(); //获取光照强度
		    
        /*******************向巴法云发送数据***************************************/
        if(TIMER_IT)
        {
            TIMER_IT=0;
            sprintf(date,"cmd=2&uid=%s&topic=data&msg=#%d#%d#%d#%d#%d#%d#sensordata#\r\n",\
            BEMFA_ID,
            MODE,(int)temp_value,(int)humi_value,(int)soil_value,(int)light_value,(int)alarm_fiag);
            ESP8266_SendData((unsigned char *)date);//ESP8266发送数据
        }	
   /*********************************人机交互区**************************************************/      
				key_num=KEY_Scan(0);//按键扫描
        if(key_num==1)//界面切换键
        {
            Page_flag++;
            Page_flag=Page_flag%4; // 修改为4个界面
            OLED_Clear(0);//清屏
        }      
        //界面切换功能
        switch(Page_flag)
        {    
            case 1: 
                Page_data();    // 第一界面 - 数据显示
                break;
            case 2: 
                Page_control();    // 第二界面 - 控制界面
                break;
            case 3: 
                Page_setting1(); // 第三界面 - 设置界面1(温度和湿度)
                break;
            case 0: 
                Page_setting2(); // 第四界面 - 设置界面2(土壤湿度和光照强度)
                break;
        }

	/*********************************模式控制区*********************************/
  
	/******************APP指令下发********************************/
  APP_Control();//阈值下发  模式转换
    			
	/********************自动模式******************************/
		
				
		
	if(MODE==auto_mode){
		
       // 温度控制逻辑
       if(temp_value > Out_data.temp_H)
       {
           FAN = ON;  // 温度过高,开启风扇降温
       }
       else if(temp_value < Out_data.temp_L)
       {
           FAN = OFF; // 温度过低,关闭风扇
       }
       
       // 湿度控制逻辑
       if(humi_value < Out_data.humi_L)
       {
           PUMP = ON; // 湿度过低,开启水泵加湿
       }
       else if(humi_value > Out_data.humi_H)
       {
           PUMP = OFF; // 湿度过高,关闭水泵
       }
       
			 
       // 土壤湿度控制逻辑
       if(soil_value < Out_data.soil_L)
       {
           PUMP = ON; // 土壤湿度过低,开启水泵浇水
       }
       else if(soil_value > Out_data.soil_H)
       {
           PUMP = OFF; // 土壤湿度过高,关闭水泵
       }
       
       // 光照强度控制逻辑
       if(light_value < Out_data.light_L)
       {
           LED = ON; // 光照不足,开启LED补光
       }
       else if(light_value > Out_data.light_H)
       {
           LED = OFF; // 光照充足,关闭LED
       }
       
       // 统一蜂鸣器控制逻辑:任何参数超出阈值范围时开启报警
       if(temp_value > Out_data.temp_H || temp_value < Out_data.temp_L ||
          humi_value < Out_data.humi_L || humi_value > Out_data.humi_H ||
          soil_value < Out_data.soil_L || soil_value > Out_data.soil_H ||
          light_value < Out_data.light_L || light_value > Out_data.light_H)
       {
           beep_flag = true; // 有参数异常,开启报警
				   alarm_fiag=1;//环境异常
       }
       else
       {
           beep_flag = false; // 所有参数正常,关闭报警
				   alarm_fiag=0;//环境正常
       }
			
		}//自动模式
		
	
		
  /********************手动模式*****************************/
			
	if(MODE==manual_mode)//手动模式
		{
			/****************APP控制***************************/
				//LED控制
				if(strstr((const char *)esp8266_buf,"LED_ON")!=0)
				{
							 LED=ON;
								 BI_BI();//哔哔提醒
								 ESP8266_Clear();//清除缓存
				}
				else if(strstr((const char *)esp8266_buf,"LED_OFF")!=0)
				{
							LED=OFF;
						 BI_BI();//哔哔提醒
						 ESP8266_Clear();//清除缓存
				} 
				//风扇控制
				if(strstr((const char *)esp8266_buf,"FAN_ON")!=0)
				{
							 FAN=ON;
						 BI_BI();//哔哔提醒
						 ESP8266_Clear();//清除缓存
				}
				else if(strstr((const char *)esp8266_buf,"FAN_OFF")!=0)
				{
							FAN=OFF;
						 BI_BI();//哔哔提醒
						 ESP8266_Clear();//清除缓存
				}
				//水泵控制
				if(strstr((const char *)esp8266_buf,"PUMP_ON")!=0)
				{
							 PUMP=ON;
						 BI_BI();//哔哔提醒
						 ESP8266_Clear();//清除缓存
				}
				else if(strstr((const char *)esp8266_buf,"PUMP_OFF")!=0)
				{
							PUMP=OFF;
						 BI_BI();//哔哔提醒
						 ESP8266_Clear();//清除缓存
				}
				
		}//手动模式

     
    }//while
}//main



void TIM2_IRQHandler(void)
{
 if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
    {

        TIMER_IT=1;
        
        if(beep_flag)
        {
            BEEP=!BEEP;
        }
        else
        {
            BEEP=0;
        }
        
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}


void APP_Control(void)
{
   //模式切换
   if( strstr((const char *)esp8266_buf,"ZD")!=0 )
   { 
       MODE=auto_mode;
       BI_BI();//哔哔提醒
       ESP8266_Clear();//清除缓存
   }
   else if( strstr((const char *)esp8266_buf,"SD")!=0 )
   {
       MODE=manual_mode;
       beep_flag=false;
       BEEP=OFF;
       PUMP=OFF;
       FAN=OFF;
       LED=OFF;
			alarm_fiag=0;
  
		   BI_BI();//哔哔提醒
       ESP8266_Clear();//清除缓存
   } 
   
   // 温度阈值设置
   char *th_ptr = strstr((char *)esp8266_buf, "TH");
   if (th_ptr != NULL && sscanf(th_ptr + 2, "=%hu", &Out_data.temp_H) == 1) 
   {	
     BI_BI();//哔哔提醒
     ESP8266_Clear();//清除缓存
     STMFLASH_Write(FLASH_SAVE_ADDR, &Out_data.temp_H, 1);
   }
   
   char *tl_ptr = strstr((char *)esp8266_buf, "TL");
   if (tl_ptr != NULL && sscanf(tl_ptr + 2, "=%hu", &Out_data.temp_L) == 1) 
   {	
     BI_BI();//哔哔提醒
     ESP8266_Clear();//清除缓存
     STMFLASH_Write(FLASH_SAVE_ADDR+2, &Out_data.temp_L, 1);
   }
   
   // 湿度阈值设置
   char *hh_ptr = strstr((char *)esp8266_buf, "HH");
   if (hh_ptr != NULL && sscanf(hh_ptr + 2, "=%hu", &Out_data.humi_H) == 1) 
   {	
     BI_BI();//哔哔提醒
     ESP8266_Clear();//清除缓存
     STMFLASH_Write(FLASH_SAVE_ADDR+4, &Out_data.humi_H, 1);
   }
   
   char *hl_ptr = strstr((char *)esp8266_buf, "HL");
   if (hl_ptr != NULL && sscanf(hl_ptr + 2, "=%hu", &Out_data.humi_L) == 1) 
   {	
     BI_BI();//哔哔提醒
     ESP8266_Clear();//清除缓存
     STMFLASH_Write(FLASH_SAVE_ADDR+6, &Out_data.humi_L, 1);
   }
   
   // 土壤湿度阈值设置
   char *sh_ptr = strstr((char *)esp8266_buf, "SH");
   if (sh_ptr != NULL && sscanf(sh_ptr + 2, "=%hu", &Out_data.soil_H) == 1) 
   {	
     BI_BI();//哔哔提醒
     ESP8266_Clear();//清除缓存
     STMFLASH_Write(FLASH_SAVE_ADDR+8, &Out_data.soil_H, 1);
   }
   
   char *sl_ptr = strstr((char *)esp8266_buf, "SL");
   if (sl_ptr != NULL && sscanf(sl_ptr + 2, "=%hu", &Out_data.soil_L) == 1) 
   {	
     BI_BI();//哔哔提醒
     ESP8266_Clear();//清除缓存
     STMFLASH_Write(FLASH_SAVE_ADDR+10, &Out_data.soil_L, 1);
   }
   
   // 光照强度阈值设置
   char *lh_ptr = strstr((char *)esp8266_buf, "LH");
   if (lh_ptr != NULL && sscanf(lh_ptr + 2, "=%hu", &Out_data.light_H) == 1) 
   {	
     BI_BI();//哔哔提醒
     ESP8266_Clear();//清除缓存
     STMFLASH_Write(FLASH_SAVE_ADDR+12, &Out_data.light_H, 1);
   }
   
   char *ll_ptr = strstr((char *)esp8266_buf, "LL");
   if (ll_ptr != NULL && sscanf(ll_ptr + 2, "=%hu", &Out_data.light_L) == 1) 
   {	
     BI_BI();//哔哔提醒
     ESP8266_Clear();//清除缓存
     STMFLASH_Write(FLASH_SAVE_ADDR+14, &Out_data.light_L, 1);
   }
}

// 数据显示界面
void Page_data(void)
{
		// 添加按键2的模式切换功能
    if(key_num==2)//模式切换键
    {
        MODE++;
        if(MODE > manual_mode) MODE = auto_mode; // 循环切换:自动->手动->自动
        
        // 切换到手动模式时关闭报警和设备
        if(MODE == manual_mode)
        {
            beep_flag=false;
            BEEP=OFF;
					  PUMP=OFF;
					  FAN=OFF;
						LED=OFF;
					alarm_fiag=0;
       
        }
    }
  
		
		
    //温度显示
    sprintf(oled_str,"温度:%2d℃",temp_value);
    OLED_ShowText(0,0,(u8*)oled_str,0);
	  //湿度显示
	  sprintf(oled_str,"湿度:%2d%%",humi_value);
    OLED_ShowText(0,2,(u8*)oled_str,0);
		//土壤湿度显示
		sprintf(oled_str,"土壤湿度:%3d%%",soil_value);
    OLED_ShowText(0,4,(u8*)oled_str,0);
		//光照强度显示
		sprintf(oled_str,"光照强度:%3d%%",light_value);
    OLED_ShowText(0,6,(u8*)oled_str,0);
		
	  //模式状态
    sprintf(oled_str,"%s",MODE?"手动":"自动");
    OLED_ShowText(95,0,(u8*)oled_str,0);
    
    
   
}

//控制界面
void Page_control(void)
{
    static u8 Index_flag=1;

    sprintf(oled_str,"LED:%s",LED?"ON ":"OFF");
    OLED_ShowText(16,0,(u8 *)oled_str,0);
    
  	sprintf(oled_str,"风扇:%s",FAN?"ON ":"OFF");
    OLED_ShowText(16,2,(u8 *)oled_str,0);
	
	  sprintf(oled_str,"水泵:%s",PUMP?"ON ":"OFF");
    OLED_ShowText(16,4,(u8 *)oled_str,0);
	
      
    // 处理按键和箭头选择逻辑
    if(key_num==2)
    {
        Index_flag++;
        Index_flag=Index_flag%3; // 修改为3个选项
    }               
    
    // 箭头显示和控制逻辑
    switch(Index_flag)
    {   
        case 1:      
            OLED_ShowString(0,0,(u8 *)">",16) ; 
            OLED_ShowString(0,2,(u8 *)" ",16) ;
            OLED_ShowString(0,4,(u8 *)" ",16) ;            
            break;
        case 2:      
            OLED_ShowString(0,0,(u8 *)" ",16) ; 
            OLED_ShowString(0,2,(u8 *)">",16) ;
            OLED_ShowString(0,4,(u8 *)" ",16) ;            
            break; 
				case 0:      
            OLED_ShowString(0,0,(u8 *)" ",16) ; 
            OLED_ShowString(0,2,(u8 *)" ",16) ;
            OLED_ShowString(0,4,(u8 *)">",16) ;            
            break;
								 
    }    
    if(MODE==manual_mode)//手动模式下
    {
        if(Index_flag==1&&key_num==3) LED=ON;
        if(Index_flag==1&&key_num==4) LED=OFF;
        
				if(Index_flag==2&&key_num==3) FAN=ON;
        if(Index_flag==2&&key_num==4) FAN=OFF;
			
        if(Index_flag==0&&key_num==3) PUMP=ON;
        if(Index_flag==0&&key_num==4) PUMP=OFF;
        
    }
}

// 设置界面1 - 温度和湿度阈值设置
void Page_setting1(void)
{
    static u8 Index_flag=1;
    
    // 显示设置参数
    sprintf(oled_str,"温度上限:%d  ",Out_data.temp_H);
    OLED_ShowText(16,0,(u8 *)oled_str,0);
    sprintf(oled_str,"温度下限:%d  ",Out_data.temp_L);
    OLED_ShowText(16,2,(u8 *)oled_str,0);
    sprintf(oled_str,"湿度上限:%d  ",Out_data.humi_H);
    OLED_ShowText(16,4,(u8 *)oled_str,0);
    sprintf(oled_str,"湿度下限:%d  ",Out_data.humi_L);
    OLED_ShowText(16,6,(u8 *)oled_str,0);
               
    // 处理参数调节逻辑
    if(key_num==2)
    {
        Index_flag++;
        Index_flag=Index_flag%4; // 修改为4个选项
    }               
    
    // 箭头显示和参数调节逻辑
    switch(Index_flag)
    {
        case 1:      
            OLED_ShowString(0,0,(u8 *)">",16) ; 
            OLED_ShowString(0,2,(u8 *)" ",16) ; 
            OLED_ShowString(0,4,(u8 *)" ",16) ; 
            OLED_ShowString(0,6,(u8 *)" ",16) ;             
            break;
        case 2:      
            OLED_ShowString(0,0,(u8 *)" ",16) ; 
            OLED_ShowString(0,2,(u8 *)">",16) ; 
            OLED_ShowString(0,4,(u8 *)" ",16) ; 
            OLED_ShowString(0,6,(u8 *)" ",16) ;             
            break;
        case 3:      
            OLED_ShowString(0,0,(u8 *)" ",16) ; 
            OLED_ShowString(0,2,(u8 *)" ",16) ; 
            OLED_ShowString(0,4,(u8 *)">",16) ; 
            OLED_ShowString(0,6,(u8 *)" ",16) ;             
            break;
        case 0:      
            OLED_ShowString(0,0,(u8 *)" ",16) ; 
            OLED_ShowString(0,2,(u8 *)" ",16) ; 
            OLED_ShowString(0,4,(u8 *)" ",16) ; 
            OLED_ShowString(0,6,(u8 *)">",16) ;             
            break; 	 	 	 	 	 	  
    }    

    // 处理参数调节
    
    //温度上限
    if(Index_flag==1&&key_num==3) 
    {
        Out_data.temp_H < 100 ?  Out_data.temp_H++ : 100;//温度上限不能超过100
        STMFLASH_Write(FLASH_SAVE_ADDR, &Out_data.temp_H,1);
    }
    if(Index_flag==1&&key_num==4) 
    {
        Out_data.temp_H > 0 ?  Out_data.temp_H-- : 0; //温度上限不能低于0
        STMFLASH_Write(FLASH_SAVE_ADDR, &Out_data.temp_H,1);
    }
    
    //温度下限
    if(Index_flag==2&&key_num==3) 
    {
        Out_data.temp_L < 100 ?  Out_data.temp_L++ : 100;//温度下限不能超过100
        STMFLASH_Write(FLASH_SAVE_ADDR+2, &Out_data.temp_L,1);
    }
    if(Index_flag==2&&key_num==4) 
    {
        Out_data.temp_L > 0 ?  Out_data.temp_L-- : 0; //温度下限不能低于0
        STMFLASH_Write(FLASH_SAVE_ADDR+2, &Out_data.temp_L,1);
    }
    
    //湿度上限
    if(Index_flag==3&&key_num==3) 
    {
        Out_data.humi_H < 100 ?  Out_data.humi_H++ : 100;//湿度上限不能超过100
        STMFLASH_Write(FLASH_SAVE_ADDR+4, &Out_data.humi_H,1);
    }
    if(Index_flag==3&&key_num==4) 
    {
        Out_data.humi_H > 0 ?  Out_data.humi_H-- : 0; //湿度上限不能低于0
        STMFLASH_Write(FLASH_SAVE_ADDR+4, &Out_data.humi_H,1);
    }
    
    //湿度下限
    if(Index_flag==0&&key_num==3) 
    {
        Out_data.humi_L < 100 ?  Out_data.humi_L++ : 100;//湿度下限不能超过100
        STMFLASH_Write(FLASH_SAVE_ADDR+6, &Out_data.humi_L,1);
    }
    if(Index_flag==0&&key_num==4) 
    {
        Out_data.humi_L > 0 ?  Out_data.humi_L-- : 0; //湿度下限不能低于0
        STMFLASH_Write(FLASH_SAVE_ADDR+6, &Out_data.humi_L,1);
    }
}

// 设置界面2 - 土壤湿度和光照强度阈值设置
void Page_setting2(void)
{
    static u8 Index_flag=1;
    
    // 显示设置参数
    sprintf(oled_str,"土壤上限:%d ",Out_data.soil_H);
    OLED_ShowText(16,0,(u8 *)oled_str,0);
    sprintf(oled_str,"土壤下限:%d  ",Out_data.soil_L);
    OLED_ShowText(16,2,(u8 *)oled_str,0);
    sprintf(oled_str,"光照上限:%d  ",Out_data.light_H);
    OLED_ShowText(16,4,(u8 *)oled_str,0);
    sprintf(oled_str,"光照下限:%d  ",Out_data.light_L);
    OLED_ShowText(16,6,(u8 *)oled_str,0);
               
    // 处理参数调节逻辑
    if(key_num==2)
    {
        Index_flag++;
        Index_flag=Index_flag%4; // 修改为4个选项
    }               
    
    // 箭头显示和参数调节逻辑
    switch(Index_flag)
    {
        case 1:      
            OLED_ShowString(0,0,(u8 *)">",16) ; 
            OLED_ShowString(0,2,(u8 *)" ",16) ; 
            OLED_ShowString(0,4,(u8 *)" ",16) ; 
            OLED_ShowString(0,6,(u8 *)" ",16) ;             
            break;
        case 2:      
            OLED_ShowString(0,0,(u8 *)" ",16) ; 
            OLED_ShowString(0,2,(u8 *)">",16) ; 
            OLED_ShowString(0,4,(u8 *)" ",16) ; 
            OLED_ShowString(0,6,(u8 *)" ",16) ;             
            break;
        case 3:      
            OLED_ShowString(0,0,(u8 *)" ",16) ; 
            OLED_ShowString(0,2,(u8 *)" ",16) ; 
            OLED_ShowString(0,4,(u8 *)">",16) ; 
            OLED_ShowString(0,6,(u8 *)" ",16) ;             
            break;
        case 0:      
            OLED_ShowString(0,0,(u8 *)" ",16) ; 
            OLED_ShowString(0,2,(u8 *)" ",16) ; 
            OLED_ShowString(0,4,(u8 *)" ",16) ; 
            OLED_ShowString(0,6,(u8 *)">",16) ;             
            break; 	 	 	 	 	 	  
    }    

    // 处理参数调节
    
    //土壤湿度上限
    if(Index_flag==1&&key_num==3) 
    {
        Out_data.soil_H < 100 ?  Out_data.soil_H++ : 100;//土壤湿度上限不能超过100
        STMFLASH_Write(FLASH_SAVE_ADDR+8, &Out_data.soil_H,1);
    }
    if(Index_flag==1&&key_num==4) 
    {
        Out_data.soil_H > 0 ?  Out_data.soil_H-- : 0; //土壤湿度上限不能低于0
        STMFLASH_Write(FLASH_SAVE_ADDR+8, &Out_data.soil_H,1);
    }
    
    //土壤湿度下限
    if(Index_flag==2&&key_num==3) 
    {
        Out_data.soil_L < 100 ?  Out_data.soil_L++ : 100;//土壤湿度下限不能超过100
        STMFLASH_Write(FLASH_SAVE_ADDR+10, &Out_data.soil_L,1);
    }
    if(Index_flag==2&&key_num==4) 
    {
        Out_data.soil_L > 0 ?  Out_data.soil_L-- : 0; //土壤湿度下限不能低于0
        STMFLASH_Write(FLASH_SAVE_ADDR+10, &Out_data.soil_L,1);
    }
    
    //光照强度上限
    if(Index_flag==3&&key_num==3) 
    {
        Out_data.light_H < 100 ?  Out_data.light_H++ : 100;//光照强度上限不能超过100
        STMFLASH_Write(FLASH_SAVE_ADDR+12, &Out_data.light_H,1);
    }
    if(Index_flag==3&&key_num==4) 
    {
        Out_data.light_H > 0 ?  Out_data.light_H-- : 0; //光照强度上限不能低于0
        STMFLASH_Write(FLASH_SAVE_ADDR+12, &Out_data.light_H,1);
    }
    
    //光照强度下限
    if(Index_flag==0&&key_num==3) 
    {
        Out_data.light_L < 100 ?  Out_data.light_L++ : 100;//光照强度下限不能超过100
        STMFLASH_Write(FLASH_SAVE_ADDR+14, &Out_data.light_L,1);
    }
    if(Index_flag==0&&key_num==4) 
    {
        Out_data.light_L > 0 ?  Out_data.light_L-- : 0; //光照强度下限不能低于0
        STMFLASH_Write(FLASH_SAVE_ADDR+14, &Out_data.light_L,1);
    }
}


void BI_BI(void){
 BEEP=ON;
 delay_ms(150);
 BEEP=OFF;

}

四、系统实现与测试

可在B站关注博主,获取资料哦!!!

4.1 B站视频介绍

植物生长环境监测系统

五、结论与展望

5.1  小结

可以拓展语音控制功能;执行器添加加湿器、步进电机控制遮阳、保温类地毯;监测类模块,添加PH值模块。

六、资料内容及获取

B站关注UP主,一键三联,私信博主!!!

【YOLOv8改进 - 注意力机制】GAM(Global Attention Mechanism):全局注意力机制,减少信息损失并放大全局维度交互特征 为了提高各种计算机视觉任务的性能,研究了多种注意力机制。然而,现有方法忽视了保留通道和空间两个方面信息的重要性,以增强跨维度的交互。因此,我们提出了一种全局注意力机制,通过减少信息损失和放大全局交互表示来提升深度神经网络的性能。我们引入了带有多层感知器的3D排列用于通道注意力,同时结合了卷积空间注意力子模块。在CIFAR-100和ImageNet-1K上的图像分类任务评估中,表明我们的方法在ResNet和轻量级MobileNet上稳定地优于几种最新的注意力机制。 阅读详情

相关推荐

基于QGIS的DEM地形数据精细化处理:从裁剪、合并到成果输出

本文详细介绍了使用QGIS进行DEM地形数据的精细化处理流程,包括数据获取、裁剪、合并及成果输出等关键步骤。通过实战技巧和注意事项,帮助用户高效处理地形数据,适用于GIS分析、三维建模等应用场景。

weixin_29093017的博客 452

基于51单片机的智能温室环境监测系统设计与实现

本文详细介绍了基于51单片机的智能温室环境监测系统的设计与实现。系统通过DHT11、DS18B20等传感器实时监测温湿度、光照等环境参数,利用51单片机进行数据处理和无线传输,实现自动化报警和数据显示。文章还分享了硬件选型、电路设计、软件优化及调试经验,为温室大棚环境监测提供了高性价比的解决方案。

weixin_29161785的博客 266

SAP S4 MM模块自动记账全流程解析:从评估范围到科目分组的实战配置

本文深入解析SAP S4 MM模块自动记账的全流程,从核心概念评估范围与评估控制入手,详细阐述了评估类、科目分组等关键配置的逻辑关系。通过实战配置步骤,指导如何建立从物料移动(如收货、发料)到自动生成准确财务凭证的完整体系,确保库存价值与总账实时同步,为企业构建可靠的财务业务一体化基石。

weixin_29313547的博客 425

【单片机毕业设计】【mcuclub-dz-225】基于单片机的植物生长检测系统的设计

目前,随着我国经济的快速发展和国民生活质量的提高,人们在吃饱的基础上 开始追求吃好和食品绿色安全,因此解决这一问题的植物生长环境监测行业发展也越发迅速起来。当前相关设备功能相对单一,控制系统较为简单,智能化程度有限,不适用于大规模实验,而且成本高,数据传输采集能力不足,不能很好地满足研究需要。在这个问题的基础上,此次设计采用STM32单片机作为主控芯片,结合植物生长环境中影响因素较大的参数,如植物生长环境的温湿度,植物生长环境中的光照强度和植物当前生长环境中的大气压,用户可以根据需求进行这些参数上下限值的设

weixin_61121477的博客 367

炉石传说游戏系统拆解和部分系统分析

一.游戏综述 《炉石传说:魔兽英雄传》是一款由暴雪娱乐公司出品的策略类卡牌游戏。中国大陆地区的独家运营由网易公司代理。2014年3月13日全球同步正式运营。游戏背景设定于暴雪的魔兽系列,共十位魔兽中的角色作为十种不同的职业。而玩家要做的,就是根据己方现有的卡牌组建合适的卡组,指挥英雄,驱动随从,施展法术,与游戏好友或素不相识的对手一决高下。 二.游戏系统拆解图 三.游戏系统部分分析 1. 对战系统 (1)战斗准备 炉石传说开始前都需要配置至少30张卡牌作为出战卡组 .....

Jimmy72236的博客 4926

SAP 分期付款,付款条件的要件定义方法

以上内容做好后,可以在F-02或者F-60里看设置后的结果。找到相关路径选择【维护支付条件】点击进入后,选择【新条目】再设置一个总的支付规则,注意这里【付款条款】一定要选择。例:默认付款条件SP00,改成刚定义好的0028即可。将刚才基础创建的付款条件,以以下方式录入,注意。028A- 30天到期,过账日期基础。028B- 60天到期,过账日期基础。028C- 90天到期,过账日期基础。目的:新建支付条款(本次以。的选择,然后保存后,完成。,【基准日期缺省值】选择。基础创建好了以后,选择【

晴天的博客分享技术知识 2936

【STM32】 ADC 完全指南:从模拟信号采样量化到五大架构原理

本文系统介绍ADC核心概念与五大主流架构。模拟信号具时间与幅值连续性,需经采样(满足奈奎斯特准则防混叠)、量化(引入±1/2LSB误差,SNR≈6.02N+1.76dB)、编码三步转为数字信号。重点解析Flash(并行高速但受限于比较器数量)、SAR(逐次逼近,低功耗适合MCU如STM32)、双积分(高精度抗工频,用于万用表)、流水线(高速吞吐)及Σ-Δ(高分辨率,靠过采样与噪声整形)五类架构原理、优缺点与应用场景。强调分辨率≠精度,选型本质是速度、精度、功耗与面积的权衡。

专注后端与底层技术,分享实战干货与学习笔记,梦想进入字节跳动,步履不停,热爱不止。 519

fpga的希尔伯特变换实现

fpga的希尔伯特变换实现零.实现背景一.希尔伯特实现的基本原理2.实现1)matlab仿真1)fpga滤波器实现实现成本:fpga实现采用VIvado的FIR的IP核, 零.实现背景 因为今明工作项目需要使用到希尔伯特变换来处理问题,所以顺便就把设计流程和代码都整理下来,以便供他人使用。选用Xilinx的7系列板子,Vivado平台。 一.希尔伯特实现的基本原理 希尔伯特变换的物理意义十分简单:...

weixin_42219690的博客 1万+

嵌入式:2.STM32底层基础(存储、寄存器、时钟、总线、GPIO、寄存器点灯)

存储系统用来存放程序、数据、设备状态,核心三大作用:存放程序代码、保存运行时数据(全局、局部、栈、堆)、数据持久化(比如W25Q64 Flash,可以搭配文件系统)。

2401_86731574的博客 375

OpenCV入门(一)Python环境的搭建

因为有点Python基础,并且Python是比较好入门的编程语言,所以,机器视觉后面打算在Python这个平台下进行。

小幽余生不加糖 8123

020、Sensor 供电设计:AVDD、DVDD、IOVDD 的上电时序与纹波要求

本文总结了图像传感器(Sensor)供电设计中的关键要点与常见问题:1. 三路供电特性差异:AVDD(模拟供电)对纹波最敏感(通常<30mVpp),DVDD需应对大电流瞬态变化,IOVDD需满足MIPI接口电压稳定要求。2. 上电时序至关重要,应遵循"模拟→数字→IO"的启动顺序,时序偏差可能导致初始化失败。3. 纹波控制需合理选择电容组合(如10μF+0.1μF),注意布局走线及电容ESR/ESL特性。4. 调试建议使用示波器多通道测量时序和纹波,重点关注传感器启动瞬间的电源特性。5. 经验教训包括预留设

sundong_81的博客 530

TMS320F28335 学习笔记(二):系统时钟、PLL 与看门狗

本文系统介绍F28335的时钟架构与看门狗应用,涵盖外部晶振、PLL倍频生成系统时钟(SYSCLKOUT)、高速/低速外设时钟分频配置,以及看门狗的启用、关闭与喂狗机制。强调时钟配置准确性对延时、PWM、通信等外设的重要性,指出常见错误如晶振不匹配、未等待PLL锁定、随意修改PLL或喂狗周期过长等问题,并提供初始化框架与验证方法,帮助开发者正确配置时钟系统,确保程序稳定运行。

2403_83044722的博客 370

Transformer-LSTM预测 | 基于Transformer-LSTM的多变量单步时间序列预测(Matlab)

Transformer-LSTM预测 | 基于Transformer-LSTM的多变量单步时间序列预测(Matlab)

机器学习之心的博客,关注并私信文章链接,获取对应文章源码和数据。 192

K210+STM32+LVGL:手搓边缘AI智能安防监控网关

在传统的物联网或嵌入式毕设中,“智能安防”往往沦为“温湿度+蜂鸣器”的简单堆砌。由于单片机(如STM32)算力有限,无法在本地运行复杂的视觉AI模型,导致很多项目只能在云端处理图像,带来高延迟和隐私风险。本文将分享一个真正的边缘计算安防网关项目。我们采用“K210视觉协处理 + STM32F407主控 + LVGL高颜值UI”的分布式架构,完美解决了单芯片算力不足的痛点。项目不仅实现了明火与陌生人脸识别,还加入了多源传感器融合、5秒智能防抖、以及硬件与UI的双向闭环控制,是一套工业级标准的开源方案。

204

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

从寄存器层面重学STM32(7)——定时器理论

本文系统梳理了STM32定时器的架构与工作原理,以时基单元为核心,涵盖计数模式、预分频、影子寄存器及定时周期计算。重点解析输出比较(PWM生成)与输入捕获(脉冲测量)机制,阐明CCR与CNT比较逻辑及波形生成流程。深入探讨通用定时器的从模式(如编码器、触发、门控等)与主模式(TRGO同步),明确主从定时器间信号传递关系。结合寄存器配置(CR1、SMCR、CCMR、CCER等),总结定时器在中断、DMA、PWM输出、信号捕获中的应用,实现精准定时与事件控制。

2403_88517351的博客 333

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

【官方原创】LAT1721 使用STM32CubeIDE 调试STM32N6 XIP应用的一种方法

本文我们以STM32Cube_FW_N6_V1.3.0包中Template_FSBL_XIP为例,介绍一种使用STM32CubeIDE在DEV模式下直接调试应用的方法,不需要切换Boot模式。

2501_92678806的博客 600

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

CubeMX+STM32】SD卡U盘实现__USB+FATFS (F103 篇)

即回复USB主机的询问:“你这个存储设备有多大?如果不想有弹窗,可以随便指定一个空闲的引脚,这样在生成时就不会弹警告了,但是需要在生成的工程代码里,注释掉引脚检测功能。当不指定检测引脚,在最后生成工程时,会有弹窗警告,不用管它,到时点击Yes即可,将会正常生成工程,代码上不会有任何影响。不推荐 Yes,因为它将针对已使能的SDIO、USB进行必须值的配置,而已设置好的系统时钟,将会被修改成其它值。这个文件,CubeMX已生成了需要用到的接口空函数,目的是为了把USB大容量类的通信与SD卡的读写挂钩起来。

qq_49053936的博客 213

水深水位传感器(STM32)

本文介绍基于STM32F103C8T6的水深水位传感器实验,采用HS-S37A模块通过导电原理实现水位检测。传感器输出模拟电压,经ADC采集后在OLED显示实时数据。程序通过多次采样求平均值,提升测量稳定性。适用于花盆积水、漏水报警等场景,适合教学与物联网开发。项目代码完整,含初始化、读取与显示功能,资料可于文末获取。

qq_44016222的博客 142

STM32——GPIO篇

本文系统讲解STM32F103 GPIO的原理与应用,涵盖概念、电气特性、推挽/开漏输出、寄存器控制及输入模式。重点解析了GPIO与引脚的关系、钳位保护、MOS管工作原理、寄存器配置(CRL/CRH、ODR、BSRR等)、上下拉输入及施密特触发器的作用,构建从硬件到软件的完整知识体系,助力深入理解嵌入式开发中的核心外设机制。

wiketsy的博客 254

【电路笔记 STM32】RTC配置:晶振选择+闹钟Configuration+中断设置

【电路笔记 STM32】RTC配置:晶振选择+闹钟Configuration+中断设置

ResumeProject的博客 427

STM32移植FreeRTOS(Cmake+CubeMX)

说明:基于STM32F103C8T6最小系统开发板。

2402_84642203的博客 181

HAL库之GPIO(STM32 HAL库学习笔记)

本文系统讲解STM32 HAL库下GPIO的使用,涵盖引脚分布、复用与重映射、四种输出(推挽/开漏)及输入模式,结合闪灯与按钮实验,阐明配置流程。强调通过CubeMX生成工程后,在USERCODE区域编写代码,避免被覆盖;推挽输出适合驱动LED,开漏需外接上拉;输入模式依信号特性选上拉/下拉或浮空。掌握基础后可拓展至中断、PWM等高级应用。

xiaolitongxue222的博客 273

STM32智能窗户防控系统 云平台

本系统基于STM32F103C8T6,集成温湿度、光照、烟雾、雨滴、人体红外传感器,通过OLED显示数据,支持手动与自动模式。自动模式下依优先级控制步进电机开窗(三档)、风扇通风及蜂鸣器报警;支持阈值调节、ESP8266接入机智云平台,实现远程监控与控制。配备按键与APP双端操作,具备云端数据同步与高清演示视频,功能完整,适用于智能安防场景。

送外卖的工程师 353

STM32 电流采样为什么在开关沿抖:低边电阻、PWM 触发与 ADC 窗口

给 F103 驱动板加电流环时,采样值总在开关沿乱跳,改 PID 和加滤波都没用。这篇从示波器波形讲起,说明低边采样电阻、运放、PWM 触发与 ADC 注入组怎样对齐,以及采样窗口和 RC 滤波为什么不能互相替代。

Sakura_j_h_的博客 180

【STM32+MAX4466】音频采集与FFT频谱OLED效果呈现

本文聚焦STM32平台基于MAX4466麦克风的音频FFT频谱OLED显示项目,结合CMSIS-DSP库核心函数特性,梳理从ADC采集、去直流到幅值计算的全流程要点,为嵌入式音频可视化开发提供清晰实用的入门指引。本项目以STM32微控制器为核心,借助CMSIS-DSP库的FFT函数,通过麦克风模块完成音频信号采集,经基4 FFT算法完成频谱运算,将校准后的音频幅值呈现在OLED显示屏上。本项目采用基4傅里叶变换算法,因此FFT变换点数必须满足4的N次幂。

YouziTech的博客 254

【官方原创】LAT1717 STM32N6 如何使用单个XSPI驱动双片相同HyperRAM

STM32N6是基于Arm Cortex-M55内核的高性能MCU,其内存架构与传统STM32系列有较大差异。片上集成了4.2MB的SRAM,分布在多个域以支持并行处理和低延迟访问。 虽然片上RAM容量较大,但在实际应用中,特别是在涉及机器学习推理、图像处理、音频处理或大容量数据缓冲的场景下,STM32N6片上RAM仍有可能无法满足需求。此时,外部PSRAM或HyperRAM就成了扩展内存容量的理想选择。本文将介绍使用单个XSPI 驱动双片相同的HyperRAM的方法。

2501_92678806的博客 413
上一篇: STM32项目分享:基于STM32的智能家居系统
下一篇: STM32项目分享:基于STM32的空气净化器
大海学长的单片机设计
博客等级 码龄6年 74粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值