目录
一、项目概述
二、实现功能
1、功能详解:
2、项目清单:
3、演示视频:
三、硬件介绍
PCB硬件设计:
四、程序设计
五、项目成品效果图
六、项目总结
七、包含资料
一、项目概述
本项目基于STM32F103C8T6设计智能大棚环境控制系统,集成DS18B20防水温度传感器、土壤湿度传感器、光敏传感器,实时采集土壤温度、湿度及光照强度,并显示于OLED屏幕。用户可通过按键设置温度上下限、土壤湿度及光照阈值,并切换自动/手动模式。自动模式下,系统根据阈值自动控制风扇、加热片、水泵及补光灯,实现温湿度与光照的闭环调节;手动模式下用户按键独立控制各设备。通过BT04A蓝牙模块连接手机APP,实现远程数据监测与控制,适用于家庭种植、温室大棚等场景。
二、实现功能
1、功能详解:
主控:STM32F103C8T6最小系统板作为核心控制,负责传感器数据采集、逻辑判断、设备驱动及蓝牙通信。
检测:DS18B20测土壤温度,土壤湿度模块测湿度,光敏电阻测光照。
显示:OLED实时显示温湿度、光照及模式状态。
阈值:按键设定温度上下限、湿度及光照阈值,断电保存。
模式:按键切换自动/手动模式。
自动模式:超温开风扇、低温加热、干土报警并加水、弱光补光。
手动模式:按键独立控制加热片、水泵、风扇、照明灯。
蓝牙:BT04A连接手机APP,远程查看数据与控制设备。
2、项目清单:
STM32F103C8T6最小系统板
0.96OLED屏幕
BT04A蓝牙模块
DS18B20防水温度传感器
土壤湿度传感器
LED照明灯模块
有源蜂鸣器
光敏传感器
继电器模块
风扇模块
加热片
水泵
3、演示视频:
- 哔哩哔哩视频链接点击观看:https://www.bilibili.com/video/BV1fvM86uExf/?share_source=copy_web&vd_source=f4437c8e4429101e5bf1054c39d5474f
三、硬件介绍
PCB硬件设计:
四、程序设计
#include "delay.h" #include "sys.h" #include "usart.h" #include "key.h" #include "stdio.h" #include "control.h" #include "oled.h" #include "adc.h" #include "dht11.h" #include "IoData.h" #include "pwm.h" #include "timer.h" #include "ds18b20.h" #include "jw01.h" #include "uln2003.h" #include "aliyun.h" #include "esp8266.h" int temp_door = 35; //温度阈值 int hum_door = 30; //湿度阈值 int co2_door = 1000; //二氧化碳阈值 int light_door = 500; //光照阈值 int auto_mode = 0; //自动模式开关 0关闭 1开启 int wind_status = 0; //风扇开关状态 int water_status = 0; //水泵开关状态 int valve_status = 0; //燃气阀开关状态 int led_status = 0; //照明开关状态 int Heat_status=0; //加热片开关状态 int Uln2003_status=0; //雨棚状态 //代表着步进电机 int Body_status=0; //物体状态 int Uln2003_flag=0; //步进电机标志位 int Rain=0; int Beep_status=0; uint8_t keu_num = 0; //按键 uint8_t page_num = 1; //界面 uint8_t option = 0; //选项 uint8_t net_flag = 1; //函数声明 void OLED_Show_OnePage(); //第一屏显示内容 void OLED_Show_TwoPage(); //第二屏显示内容 void OLED_Show_ThreePage(); //第三屏显示内容 void net_page(); void get_all_sensor_data(); // 传感器采集的函数 void Key_event_handle(void); //按键功能函数 void All_Pin_Init(void); //系统初始化函数 void Sensor_Data_Handle(); //不同模式下控制效果 int main(void) { delay_init(); //系统延时函数初始化 OLED_Init(); //屏幕初始化 uart3_init(115200); //串口3初始化,波特率9600 uart1_init(115200); //串口1初始化,波特率115200 uart2_init(9600); //串口2初始化,波特率9600 DS18B20_GPIO_Init(); DS18B20_Init(); //初始化DS18B20 KEY_Init(); //按键初始化 DHT11_Init(); //DHT11温湿度初始化 Adc_Init(); //雨棚,光照,土壤初始化 IoData_Init(); //火焰传感器、人体传感器 Control_Init(); //声光报警,风扇、水泵,加热片,初始化 LED=1; Uln2003_Init(); net_page(); TIME_Config(); while(net_flag){ keu_num = KEY_Scan(0); //按键扫描赋值 if(keu_num==1){net_flag=0;} else if(keu_num==2){net_flag=0;ESP8266_Init();} } OLED_Clear(); while(1) //循环模式 { if(Time_Period[1]>1000) //Time_Period[1] 在定时器中累加 { get_all_sensor_data(); //传感器数据采集 Mqtt_Publiash_Aliyun(); Time_Period[1] = 0; } Key_event_handle(); //按键监测 if(page_num == 1){ //屏幕更新 OLED_Show_OnePage(); }else if(page_num == 2){ OLED_Show_TwoPage(); }else if(page_num == 3){ OLED_Show_ThreePage(); } Sensor_Data_Handle(); } } void Key_event_handle(void) //(Key)按键 event(事情、逻辑)handle(处理) { keu_num = KEY_Scan(0); //按键=扫描赋值 switch(keu_num) { case 1: //按键1按下 if(page_num==1){ //如果在页面1的状态下 option=0; page_num=2; // 按下按键1会跳转在页面2 OLED_Clear(); // 屏幕清楚 }else if(page_num==2){ //如果在页面2的状态下 option=0; page_num=3;; // 按下按键1会跳转在页面3 OLED_Clear(); // 屏幕清楚 }else if(page_num==3){ //如果在页面3的状态下 option=0; page_num=1; // 按下按键3会跳转在页面1, 继续操作按键1,还是会循环跳转页面 OLED_Clear();//// 屏幕清楚 } break; case 2: //按键2按下 if(page_num==2) { option++; //option选项 继续按、按键2会跳到下一个选项, if(option>4){ //如果页面选项是3个、再次按键次数超过3次后 option=0; //先会、不会框选每一个选项,在按后会再次循环 } }else if(page_num==3){ option++; //option选项 继续按、按键2会跳到下一个选项, if(option>5){ //如果页面选项是4个、再次按键次数超过4次后 option=0; ////先会、不会框选每一个选项,在按后会再次循环 } } break; case 3: //按键3按下 if(page_num==1){ auto_mode = 1; }else if(page_num==2){ if(option==1)temp_door++; else if(option==2)hum_door++; else if(option==3)co2_door+=25; else if(option==4)light_door+=25; }else if (page_num==3){ if(auto_mode==0){ if(option==1){ Wind=1; wind_status=1; }else if(option==2){ Water=1; water_status=1; }else if (option==3){ Heat=1; Heat_status=1; }else if(option==4){ LED=0; led_status=1; }else if(option==5){ Uln2003_flag=1; Uln2003_status=1; Stepper_RotateByLoop(1,1,1); } } } break; case 4: //按键4按下 if(page_num==1){ auto_mode = 0; }else if(page_num==2){ if(option==1)temp_door--; else if(option==2)hum_door--; else if(option==3)co2_door-=25; else if(option==4)light_door-=25; }else if (page_num==3){ if(auto_mode==0){ if(option==1){ Wind=0; wind_status=0; }else if(option==2){ Water=0; water_status=0; }else if (option==3){ Heat=0; Heat_status=0; }else if(option==4){ LED=1; led_status=0; }else if(option==5){ Uln2003_flag=1; Uln2003_status=0; Stepper_RotateByLoop(0,1,1); } } } break; } } void get_all_sensor_data() //获取所有传感器数据 { Get_Body_Value(&body_status); //检测动物 Get_CO2_Data(); //获取CO2浓度 DS18B20_TEMP=ReadTemperature(); //获取温度 Get_Light_Value(&Light_Value); //获取光照数据 Get_Soil_Value(&Soil_Value); //获取土壤湿度 Get_Rain_Value(&Rain_status); //检测降雨 } void Sensor_Data_Handle() //传感器的采集数据的处理 { if(auto_mode==1){ if(DS18B20_TEMP<temp_door){ Heat=1; Heat_status=1; }else{ Heat=0; Heat_status=0; } if(Soil_Value<hum_door){ Water=1; water_status=1; }else{ Water=0; water_status=0; } if(Light_Value<light_door){ LED=0; led_status=1; }else{ LED=1; led_status=0; } if(body_status==1){ Beep=1; }else{ Beep=0; } if(Kqzl.CO2>co2_door){ Wind=1; wind_status=1; }else{ Wind=0; wind_status=0; } if(Rain_status==1){ //检测是否降雨 if(Uln2003_status==0){ // 此时注意判断条件 雨棚(步进电机)是否开启 Uln2003_status=1; // 打开雨棚; Stepper_RotateByLoop(1,1,1); //步进电机转动一圈 } }else{ if(Uln2003_status==1){ //检测雨棚的状态 如果没有检测到降雨 不用开启雨棚 但需要先判断 如果打开了就关闭 Uln2003_status=0; //关闭雨棚 Stepper_RotateByLoop(0,1,1); //关闭雨棚 } } }else if(auto_mode==0){ if(Uln2003_flag==1){ Uln2003_flag=0; Stepper_RotateByLoop(Uln2003_status,1,1); } Beep=0; } } // 土(0) 壤(1) 温(2) 湿(3) 度(4) 光(5) 照(6) 强(7) 度(8) 浓(9) 度(10) 天(11) 气(12) 有(13) 无(14) 雨(15) // 模(16) 式(17) 自(18) 手(19) 动(20) 动(21) 物(22) 阈(23) 值(24) 通(25) 风(26) 水(27) 泵(28) 加(29) 热(30) 照(31) // 明(32) 雨(33) 棚(34) 开(35) 关(36) ℃(37) void OLED_Show_OnePage() { OLED_ShowString(16*0,16*0,"T:",16,1); OLED_ShowNum(16*1,16*0,DS18B20_TEMP,2,16,1); //显示温度数值 OLED_ShowChinese(16*2,16*0,37,16,1); //℃ OLED_ShowString(16*0,16*1,"H: %RH",16,1); OLED_ShowNum(16*1,16*1,Soil_Value,2,16,1); OLED_ShowChinese(16*3+8,16*0,11,16,1); //天 OLED_ShowChinese(16*4+8,16*0,12,16,1); //气 OLED_ShowChar(16*5+8,16*0,':',16,1); // if(Rain_status==1)OLED_ShowChinese(16*6,16*0,13,16,1);//有 else OLED_ShowChinese(16*6,16*0,14,16,1); //无 OLED_ShowChinese(16*7,16*0,15,16,1); //雨 OLED_ShowChinese(16*4,16*1,21,16,1); //动 OLED_ShowChinese(16*5,16*1,22,16,1); //物 OLED_ShowChar(16*6,16*1,':',16,1);// 字符 if(body_status==1)OLED_ShowChinese(16*6+8,16*1,13,16,1);//有 else OLED_ShowChinese(16*6+8,16*1,14,16,1); //无 OLED_ShowChinese(16*0,16*2,5,16,1); //光 OLED_ShowChinese(16*1,16*2,6,16,1); //照 OLED_ShowChinese(16*2,16*2,7,16,1); //强 OLED_ShowChinese(16*3,16*2,8,16,1); //度 OLED_ShowChar(16*4,16*2,':',16,1); OLED_ShowNum(16*4+8,16*2,Light_Value,4,16,1); OLED_ShowString(16*0,16*3,"CO2",16,1); OLED_ShowChinese(16*1+8,16*3,9,16,1); //浓 OLED_ShowChinese(16*2+8,16*3,8,16,1); //度 OLED_ShowString(16*3+8,16*3,":",16,1); OLED_ShowNum(16*4,16*3,Kqzl.CO2,4,16,1); if(auto_mode==0){ OLED_ShowChinese(16*7,16*2,19,16,1); //手 OLED_ShowChinese(16*7,16*3,20,16,1); //动 }else{ OLED_ShowChinese(16*7,16*2,18,16,1); //自 OLED_ShowChinese(16*7,16*3,20,16,1); //动 } OLED_Refresh(); } void OLED_Show_TwoPage() { uint8_t a,b,c,d; if(option==1){a=0;b=1;c=1;d=1;} else if(option==2){a=1;b=0;c=1;d=1;} else if(option==3){a=1;b=1;c=0;d=1;} else if(option==4){a=1;b=1;c=1;d=0;} else{a=1;b=1;c=1;d=1;} OLED_ShowChinese(16*0,16*0,2,16,1); //温 OLED_ShowChinese(16*1,16*0,4,16,1); //度 OLED_ShowChinese(16*2,16*0,23,16,1); //阈 OLED_ShowChinese(16*3,16*0,24,16,1); //值 OLED_ShowString(16*4,16*0,":",16,1); OLED_ShowNum(16*4+8,16*0,temp_door,3,16,a); OLED_ShowChinese(16*0,16*1,3,16,1); //湿 OLED_ShowChinese(16*1,16*1,4,16,1); //度 OLED_ShowChinese(16*2,16*1,23,16,1); //阈 OLED_ShowChinese(16*3,16*1,24,16,1); //值 OLED_ShowChar(16*4,16*1,':',16,1); OLED_ShowNum(16*4+8,16*1,hum_door,3,16,b); OLED_ShowString(16*0,16*2,"CO2",16,1); // 二氧化碳 OLED_ShowChinese(16*1+8,16*2,23,16,1);//阈 OLED_ShowChinese(16*2+8,16*2,24,16,1);//值 OLED_ShowChar(16*3+8,16*2,':',16,1); // 字符 OLED_ShowNum(16*4,16*2,co2_door,4,16,c); //CO阈值 OLED_ShowChinese(16*0,16*3,5,16,1); //光 OLED_ShowChinese(16*1,16*3,6,16,1); //照 OLED_ShowChinese(16*2,16*3,23,16,1); //阈 OLED_ShowChinese(16*3,16*3,24,16,1); //值 OLED_ShowChar(16*4,16*3,':',16,1); OLED_ShowNum(16*4+8,16*3,light_door,4,16,d); OLED_Refresh(); } void OLED_Show_ThreePage() { uint8_t a,b,c,d,e; if(option==1){a=0;b=1;c=1;d=1;e=1;} else if(option==2){a=1;b=0;c=1;d=1;e=1;} else if(option==3){a=1;b=1;c=0;d=1;e=1;} else if(option==4){a=1;b=1;c=1;d=0;e=1;} else if(option==5){a=1;b=1;c=1;d=1;e=0;} else{a=1;b=1;c=1;d=1;e=1;} OLED_ShowChinese(16*0,16*0,25,16,1);//通 OLED_ShowChinese(16*1,16*0,26,16,1);//风 OLED_ShowChar(16*2,16*0,':',16,1); if(wind_status==1)OLED_ShowChinese(16*2+8,16*0,35,16,a);// 开 else OLED_ShowChinese(16*2+8,16*0,36,16,a);//关 OLED_ShowChinese(16*0,16*1,27,16,1);//水 OLED_ShowChinese(16*1,16*1,28,16,1);//泵 OLED_ShowChar(16*2,16*1,':',16,1); if(water_status==1)OLED_ShowChinese(16*2+8,16*1,35,16,b);//开 else OLED_ShowChinese(16*2+8,16*1,36,16,b);//关 OLED_ShowChinese(16*0,16*2,29,16,1);//加 OLED_ShowChinese(16*1,16*2,30,16,1);//热 OLED_ShowChar(16*2,16*2,':',16,1); if(Heat_status==1)OLED_ShowChinese(16*2+8,16*2,35,16,c);// 开 else OLED_ShowChinese(16*2+8,16*2,36,16,c);//关 OLED_ShowChinese(16*0,16*3,31,16,1);//照 OLED_ShowChinese(16*1,16*3,32,16,1);//明 OLED_ShowChar(16*2,16*3,':',16,1); if(led_status==1)OLED_ShowChinese(16*2+8,16*3,35,16,d);//开 else OLED_ShowChinese(16*2+8,16*3,36,16,d);//关 OLED_ShowChinese(16*4,16*0,33,16,1);//雨 OLED_ShowChinese(16*5,16*0,34,16,1);//棚 OLED_ShowChar(16*6,16*0,':',16,1); if(Uln2003_status==1)OLED_ShowChinese(16*6+8,16*0,35,16,e);// 开 else OLED_ShowChinese(16*6+8,16*0,36,16,e);//关 OLED_Refresh(); } void net_page(){ uint8_t a,b,c,d,i; if(option==1){a=0;b=1;c=1;d=1;} else if(option==2){a=1;b=0;c=1;d=1;} else if(option==3){a=1;b=1;c=0;d=1;} else {a=1;b=1;c=1;d=1;} i = 38; OLED_ShowChinese(16*2,16,i,16,1); // “是” OLED_ShowChinese(16*3,16,i+1,16,1); // “否” OLED_ShowChinese(16*4,16,i+2,16,1); // “配” OLED_ShowChinese(16*5,16,i+3,16,1); // “网” OLED_ShowChar(16*6,16,'?',16,1); // “:” OLED_ShowString(16*0,32,"KEY1 KEY2",16,1); OLED_ShowChinese(16*0,48,i+4,16,1); // “跳” OLED_ShowChinese(16*1,48,i+5,16,1); // “过” OLED_ShowChinese(16*6,48,i+6,16,1); // “确” OLED_ShowChinese(16*7,48,i+7,16,1); // “认” OLED_Refresh(); }五、项目成品效果图
六、项目总结
本系统实现了土壤温湿度与光照的自动闭环控制,自动模式下各设备响应迅速(温度超限2秒内启动风扇/加热片,湿度低于阈值立即报警并加水),蓝牙APP远程控制延迟低于1秒。阈值参数存入EEPROM实现断电记忆,解决了传统大棚依赖人工巡检的痛点。开发中攻克了DS18B20时序读取、土壤湿度ADC滤波、多路继电器时序协调等关键技术。整体采用模块化编程,预留扩展接口,成本低廉、实用性强,适合农业物联网初学者参考。
七、包含资料
本资料免费分享,获取全套资料请看主页,并将告诉助手需要资料的项目编号,助手收到消息后,会发送资料给你。
资料获取:查看主页介绍“充哥单片机设计”
如果需要项目源码与配套工程文件,欢迎大家在评论区留言交流~
文章若对你有所帮助,期待点赞收藏,后续会持续分享各类单片机实战案例。
内容仅作为学习参考,有任何不妥之处,欢迎大家一起探讨!