基于STM32的智能盆栽浇水系统设计与实现

关键词:STM32、蓝牙控制、传感器融合

一、应用背景

随着智能家居的发展,植物养护也走向自动化。本项目基于STM32C8T6设计了一款智能盆栽浇水系统,支持蓝牙远程控制、环境监测与自动灌溉,适用于家庭与办公场景。

二、硬件设计

1. 核心硬件清单

模块型号/参数功能说明
主控芯片STM32F103C8T6系统控制核心
蓝牙模块HC-05手机指令接收
温湿度传感器DHT11空气温湿度采集
土壤湿度传感器模拟量传感器土壤湿度检测(ADC读取)
执行机构SG90舵机控制水阀开关
显示模块0.96寸OLED环境数据显示
报警模块有源蜂鸣器+LED状态提示

2. 硬件连接

       将DHT11温湿度传感器的VCC引脚连接到STM32的3.3V引脚,GND引脚连接到GND,数据引脚连接到STM32的GPIO引脚(例如PB0)。

      将SG90舵机的输入引脚连接到STM32的GPIO引脚(例如PA8),VCC引脚连接到STM32的5V引脚,GND引脚连接到GND.

     将OLED显示屏的VCC引脚连接到STM32的3.3V引脚,GND引脚连接到GND,SCL引脚连接到STM32的SCL引脚(例如PA0),SDA引脚连接到STM32的SDA引脚(例如PA1)。

      将独立按键的一个引脚连接到STM32的GPIO引脚(例如PB6_9),另一个引脚连接到GND。

      将LED的负极引脚连接到STM32的GPIO引脚(例如PA5),正极引脚连接到3.3V。

      将HC-05蓝牙模块的VCC引脚连接到STM32的3.3V引脚,GND引脚连接到GND,RXD引脚连接到STM32的GPIO引脚(例如PB10),TXD引脚连接到STM32的GPIO引脚(例如PB11)。

      将蜂鸣器的VCC引脚连接到STM32的3.3V引脚,GND引脚连接到GND,输入引脚连接到STM32的GPIO引脚(例如PA6)

      将土壤湿度传感器VCC引脚连接到STM32的3.3V引脚,GND引脚连接到GND,模拟输出接口接入GPIO引脚(例如PB1)

/* 核心引脚定义(部分) */  
// 舵机控制 -> PA8(TIM1_CH1)  
// 蓝牙模块 -> USART1_TX(PA9)/RX(PA10)  
// 土壤湿度 -> PB1(ADC1)  
// OLED显示 -> I2C1(SCL:PB6, SDA:PB7) 

三、软件设计

1. 系统架构

2. 核心功能实现

(1) 蓝牙指令解析(关键代码)(中断代码)
void TIM2_IRQHandler (void)
{
	if( TIM_GetITStatus(TIM2, TIM_IT_Update)==SET)
	{
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
		i++;	
		
		if(i==1000)//一秒钟计时
		{
			a++;//延时计时时常
			s++;
			if(s==60)
			{ s=0; }
			i=0;
		}
	}
	
}

void EXTI9_5_IRQHandler ()
{
	if (EXTI_GetITStatus(Key2_EXTI_Line)==SET)
	{
		EXTI_ClearITPendingBit(Key2_EXTI_Line);
		ch[4]='0';
		ch[0]='0';
		ch[2]='0';
		ch[0]='O';//直接开启
	}
	if (EXTI_GetITStatus(Key4_EXTI_Line)==SET)
	{
		EXTI_ClearITPendingBit(Key4_EXTI_Line);
			ch[4]='0';
			ch[0]='0';
			ch[2]='0';
			ch[0]='S';;//强制关闭电机
	}
}
void DEBUG_USART_IRQHandler(void)
{
	if(USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE)!=RESET)
	{
		h=0;
		j=0;
		ucTemp=USART_ReceiveData(DEBUG_USARTx);//接收数据
		
		USART_SendData(DEBUG_USARTx, ucTemp);	//发送数据
		ch[v]=ucTemp;
		v++;
		if(ucTemp=='!')
		{
		
			if (ch[4]=='A')
				{				 
				 h=((uint16_t)ch[2]-48)*10+((uint16_t)ch[3]-48);
				 t=((uint16_t)ch[0]-48)*10+((uint16_t)ch[1]-48);
				 printf("Automatic mode\n");
				 printf("Seting temperature %d minutesn \n",t);
				 printf("Seting humidness %d C` \n",h);
	       printf("temperature   %d.%d\n",ht,lt);
				 printf("humidness     %d.%d\n",hh,lh);
				 printf("soil humidness:%.2f\n",soil_humi);
				}
      else if (ch[0]=='O')
				{printf("Open watering\n");
		     printf("temperature   %d.%d\n",ht,lt);
				 printf("humidness     %d.%d\n",hh,lh);
				 printf("soil humidness:%.2f\n",soil_humi);}
	    else if (ch[2]=='T')
		    { 
				 j=((uint16_t)ch[0]-48)*10+((uint16_t)ch[1]-48); 
				 printf("Timing mode\n");
				 printf("Seting time %d minutesn \n",j);
	       printf("Start watering in %d minutesn \n",j);	
				 y=j-1;
				}
			else if (ch[0]=='S')	
			  {printf("Close the servo to stop irrigation.\n");
			   printf("temperature   %d.%d\n",ht,lt);
				 printf("humidness     %d.%d\n",hh,lh);
				 printf("soil humidness:%.2f\n",soil_humi);}
			else
				 printf("Please select a mode\n");
			v=0;
		}
	}
}
(2)主函数代码
#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "bmp.h"
#include "Led.h"
#include "server.h"
#include "usart.h"
#include "dht11.h"
#include "time.h"
#include "key_exti.h"
#include "buzzer.h"
#include "bsp_adc.h"
#include "stdio.h"
extern uint8_t ucTemp,a,h,j,s,v,t,y;
uint8_t x;
extern uint8_t ht,lt,hh,lh;
extern uint8_t ch[20];
uint8_t tempH,tempL,humiH,humiL;
uint8_t humiH2,humiL2;
__IO extern uint16_t ADC_ConvertedValue;
extern float soil_humi;
static void Get_Soil_humi()
{
	soil_humi =100*(float)ADC_ConvertedValue/4096;
//	printf("soil humidness:%.2f\n",soil_humi);
	humiH=(uint8_t)soil_humi;
	humiL=(uint16_t)(soil_humi*100)%100;
}
static void Show_Temperature_and_humidity()
{
	
	ht=tempH,lt=tempL,hh=humiH,lh=humiL;
	Get_Soil_humi();
	OLED_ShowChinese(0,18,4,16,1);//温
	OLED_ShowChinese(18,18,5,16,1);//度
	OLED_ShowChinese(0,36,6,16,1);//湿
	OLED_ShowChinese(18,36,5,16,1);//度
	DHT11_Read_Data(&humiH2, &humiL2,&tempH,&tempL);//获取温度和湿度
	OLED_ShowString(54,36,".",8,1);//.
	OLED_ShowString(54,18,".",8,1);//.
	OLED_ShowNum(36,36,humiH,2,16,1);//湿度的整数
	OLED_ShowNum(72,36,humiL,2,16,1);//湿度的小数
	OLED_ShowNum(36,18,tempH,2,16,1);//温度的整数
	OLED_ShowNum(72,18,tempL,2,16,1);//温度的小数
}


/*舵机开启模式*/
void Open_mode(void)
{		
	h++;
	if(h==1)
	{
	TIM_SetCompare1(TIM1,25);//舵机逆时针转
	delay_ms(565);
//    Open_worte_Pump();
	TIM_SetCompare1(TIM1,15);//舵机停止
	delay_ms(50000);
	}
		OLED_Refresh();
		delay_ms(500);
		OLED_Clear();	
		OLED_ShowChinese(0,0,0,16,1);//电
		OLED_ShowChinese(18,0,1,16,1);//机
		OLED_ShowChinese(36,0,2,16,1);//状
		OLED_ShowChinese(54,0,3,16,1);//态
		OLED_ShowChinese(72,0,22,16,1);//启
		OLED_ShowChinese(90,0,23,16,1);//动
	
	  Show_Temperature_and_humidity();
		if(tempH<=20| (int)soil_humi>=100)/*温度小于20,土壤湿度大于90时自动关闭,只能固定设置*/
		{									
			j=0;
		  ch[4]='0';
			ch[0]='0';
			ch[2]='0';
			ch[0]='S';//关闭舵机
		}
}
/*停止舵机模式*/
void Sdop_mode(void)
{		
	j++;
	if(j==1)
	{
	TIM_SetCompare1(TIM1,5);//舵机顺时针转
	delay_ms(565);
//	Stop_worte_Pump();
	TIM_SetCompare1(TIM1,15);//舵机停止
	delay_ms(50000);
	}
		OLED_Refresh();//更新显存到OLED
		delay_ms(500);
		OLED_Clear();//清屏	
		OLED_ShowChinese(0,0,0,16,1);//电
		OLED_ShowChinese(18,0,1,16,1);//机
		OLED_ShowChinese(36,0,2,16,1);//状
		OLED_ShowChinese(54,0,3,16,1);//态
		OLED_ShowChinese(72,0,24,16,1);//关
		OLED_ShowChinese(90,0,25,16,1);//闭
		
		Show_Temperature_and_humidity();
}
/*延时模式*/
void Real_time_mode()
{
	  u8 g,i=0;		
	  TIM_Cmd(TIM2, ENABLE);
	  g=60-s;
		if(s>=59)
		y--;
		OLED_Refresh();
		delay_ms(500);
		OLED_Clear();
		OLED_ShowChinese(0,0,7,16,1);//定
		OLED_ShowChinese(18,0,8,16,1);//时
		OLED_ShowChinese(36,0,9,16,1);//模
		OLED_ShowChinese(54,0,10,16,1);//式
	  OLED_ShowNum(0,18,y,2,16,1);//延时总时间
	  OLED_ShowString(18,18,":",8,1);//:
	  OLED_ShowNum(26,18,g,2,16,1);//60s倒计时
		OLED_ShowChinese(44,18,15,16,1);//分
		OLED_ShowChinese(62,18,16,16,1);//钟
		OLED_ShowChinese(80,18,19,16,1);//后
		OLED_ShowChinese(0,36,17,16,1);//开
		OLED_ShowChinese(18,36,22,16,1);//启
	  OLED_ShowChinese(36,36,13,16,1);//水
	  OLED_ShowChinese(54,36,14,16,1);//阀
		if(a>=j*60)/*经过延时后开启舵机,开启后有三秒报警,时间通过按键设置*/
		{
			h=0;
			a=0;
			s=0;
				for(i=0;i<6;i++)//LED闪烁和蜂鸣器响三秒,做开启报警
			{
			GPIO_ResetBits(GPIOB, GPIO_Pin_12);
			GPIO_ResetBits(GPIOA, GPIO_Pin_6);
			delay_ms(250);
			GPIO_SetBits(GPIOB, GPIO_Pin_12);
			GPIO_SetBits(GPIOA, GPIO_Pin_6);
			delay_ms(250);
			}
			ch[4]='0';
			ch[0]='0';
			ch[2]='0';
			ch[0]='O';//开启舵机
		}
}
/*自动开启模式*/
void Automatic_mode()
{	
	uint8_t i;
	OLED_Refresh();
	delay_ms(500);
	OLED_Clear();	
	OLED_ShowChinese(0,0,11,16,1);//自
	OLED_ShowChinese(18,0,12,16,1);//动
	OLED_ShowChinese(36,0,21,16,1);//浇
	OLED_ShowChinese(54,0,13,16,1);//水
	OLED_ShowChinese(72,0,9,16,1);//模
	OLED_ShowChinese(90,0,10,16,1);//式
	
	Show_Temperature_and_humidity();
		if(tempH>=t|| soil_humi<=10)
		{
			h=0;
			for(i=0;i<3;i++)//LED闪烁和蜂鸣器响三秒,做高温开启报警
			{
			GPIO_ResetBits(GPIOB, GPIO_Pin_12);
			GPIO_ResetBits(GPIOA, GPIO_Pin_6);
			delay_ms(250);
			GPIO_SetBits(GPIOB, GPIO_Pin_12);
			GPIO_SetBits(GPIOA, GPIO_Pin_6);
			delay_ms(250);
			}
	    ch[4]='0';
			ch[0]='0';
			ch[2]='0';
			ch[0]='O';//开启舵机
		}
}

/*初始选择页面*/
void Init_page()
{
	OLED_Refresh();
	delay_ms(500);
	OLED_Clear();	
	OLED_ShowChinese(0,0,30,16,1);//请
	OLED_ShowChinese(18,0,31,16,1);//选
	OLED_ShowChinese(36,0,32,16,1);//择
	OLED_ShowChinese(54,0,9,16,1);//模
	OLED_ShowChinese(72,0,10,16,1);//式
	
	Show_Temperature_and_humidity();
}
int main(void)
{
	USART_Config();
	Key_Exti_Config();
//	Pump_GPIO_Init();
	delay_init();
	OLED_Init();
	LED_GPIO_Config();
	BUZZER_GPIO_Config();
	TIM8_PWM_Init();	
	ADCx_Init();
	TIM2_Init(250-1,72-1);	
	TIM_Cmd(TIM2, DISABLE);
	OLED_ColorTurn(0);//0正常显示,1 反色显示
  OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
	GPIO_SetBits(GPIOA, GPIO_Pin_6);
	GPIO_SetBits(GPIOB, GPIO_Pin_12);
	while(DHT11_Init()!=0)//DHT11初始化及检测DHT11是否正常
	{
	OLED_Refresh();
	delay_ms(500);
	OLED_Clear();
	OLED_ShowString(8,16,"error",16,1);//DHT11报错
	}
	printf("Please select a mode\n");
	printf("Input:(temperature and humidity)Auto!     Automatic mode\n");
	printf("Input:Open!     Open watering\n");
	printf("Input:(delay time)Time!     Timing mode\n");
	printf("Input:Stop!     Close the servo to stop irrigation.\n");
	while(1)
	{	
		if (ch[4]=='A')//自动开启模式 温湿度检测,温度和湿度达到一定时自动打开舵机,温度湿度通过蓝牙输入
			{				 
			 Automatic_mode();
			}
		else if (ch[0]=='O')//舵机开启模式 通过蓝牙输入字符开启舵机进行浇水动作
			{
				Open_mode();
			}
		else if (ch[2]=='T')//延时开启模式 通过蓝牙输入延时时长并开启此模式后,
			{ 
			 Real_time_mode();			
			}
		else if (ch[0]=='S')	//舵机停止模式 通过蓝牙关闭水龙头
			{Sdop_mode();
			}
		else
			Init_page();/*初始选择页面*/
	}
}

四、功能演示

1. 蓝牙控制指令集

指令格式功能说明
Open!立即开启浇水
Stop!立即停止浇水
30Time!延时30分钟后启动
2530Auto!自动模式:温度>25℃或湿度<30%时触发

2. 操作流程

  1. 手机通过**微信小程序(蓝牙串口)**发送指令

  2. STM32接收指令并执行对应操作

  3. OLED显示当前环境数据与系统状态

  4. 触发浇水时蜂鸣器+LED闪烁提示

  5. 按键可以直接开启或关闭

五、常见问题解决

Q1:土壤湿度检测不准确

解决方案

  • 采用软件滤波算法(中值平均滤波)

c

复制

#define FILTER_NUM 5  // 采样5次取平均
uint16_t ADC_Filter() {
    static uint16_t buf[FILTER_NUM];
    for(int i=0; i<FILTER_NUM; i++) {
        buf[i] = ADC_Read();
        delay_ms(10);
    }
    // 排序后取中间值
    bubble_sort(buf, FILTER_NUM);
    return buf[FILTER_NUM/2];
}

Q2:舵机抖动问题

解决方案

  • 在PWM信号中增加死区控制

  • 为舵机单独供电(避免与MCU共地干扰)

六、改进建议

     1、增加语音模块,实现声音控制;2增加WiFi模块;3、由于资源有限,这里oled屏幕较小显示信息不多,可用大的lcd屏可以显示更多信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值