目录
前言:
项目效果演示:
一、简介
二、硬件需求准备
三、硬件框图
四、CubeMX配置
4.1、按键、蜂鸣器GPIO口配置
4.2、ADC输入配置
4.3、IIC——驱动OLED
4.4、DHT11温湿度读取
4.5、PWM配置——光照灯、水泵、风扇
4.6、串口——esp8266模块
4.7、定时器配置——按键消抖所需
五、部分代码实现思路
5.1、菜单实现思路
5.2、OLED驱动
5.3、按键消抖
5.4、DHT11温湿度驱动
5.5、光照强度、土壤湿度ADC转换
5.6、esp8266连接阿里云
六、功能拓展
前言:
注意事项:本项目使用的是u8g2库进行显示驱动OLED,需要学会移植或者直接使用我的模板,具体可以看我的这个博文:手把手移植基于STM移植U8g2图形库教程——0.96寸OLED(附送整理资料)
买来的esp8266模可能不支持连接阿里云,需要烧写MQTT固件,具体操作可以看看我的这个博文:手把手连接阿里云教程
里面都会有所需的固件和工具和教程!
项目效果演示:
智能农业大棚
一、简介
这可以作为一个简单的课设或者用来水毕设的一个小项目,总体来说挺简单的,我做完后就打算开源出来大家一起学习一下,具体功能如下:
1、按键:
按键一:切换菜单
按键二:切换选项
按键三:+-或调节风扇 | 水泵 | 灯光
按键四:确认 | 连接阿里云平台
2、液晶屏幕显示:
菜单1:主界面显示空气温湿度,土壤湿度,光照强度,检测是否超过或低于临界值
菜单2:临界值调节
菜单3:风扇手动开关
菜单4:灯光手动开关
菜单5:水泵手动开关
菜单6:阿里云连接菜单界面
3、土壤湿度、空气温湿度、光照强度高于或低于临界值,蜂鸣器就会响
4、土壤湿度过低自动打开水泵
5、光照强度过低自动打开照明灯
6、空气温度过高会自动打开风扇
7、连接阿里云平台,可以在电脑上面查看各个数据
二、硬件需求准备
1、STM32F103最小核心开发板
2、0.96寸OLED屏幕
3、按键X4
4、TB6612FNG点击驱动模块
5、水泵
6、电机(带小风扇)
7、DHT11温湿度模块
8、esp8266模块
9、蜂鸣器
10、土壤湿度传感器
11、光照强度传感器
12、照明灯模块
三、硬件框图
四、CubeMX配置
这里只演示配置使用的外设配置过程!
4.1、按键、蜂鸣器GPIO口配置
按键配置为上拉模式、蜂鸣器低电平输出有效
4.2、ADC输入配置
ADC为读取土壤湿度和光照强度的ADC数值,这里开启两个ADC分别读取
ADC1——读取土壤湿度:
ADC2——读取光照强度:
4.3、IIC——驱动OLED
4.4、DHT11温湿度读取
这个使用过程中会对弈随意变动上下拉模式,所以这里就不配置了,使用的GPIO口为PB8
4.5、PWM配置——光照灯、水泵、风扇
4.6、串口——esp8266模块
开启中断:
4.7、定时器配置——按键消抖所需
开启中断:
最后生成工程即可。
五、部分代码实现思路
5.1、菜单实现思路
最常用的就是菜单索引法还有链表法,我这里菜单结构非常简单,所以我就使用索引法了,这里简单说一下,就是每一个菜单都对应一个结构体((这个项目没有二级界面,所以用不到进入索引和返回索引)):
typedef struct
{int current_index;//当前索引int last_index; //下一索引int enter_index; //进入索引int back_index; //返回索引void(*current)(u8g2_t u8g2);}Menu;
结构体成员包括以上的几个。
这个小项目有5个菜单页面:所以定义5个结构体(这个项目没有二级界面,所以用不到进入索引和返回索引):
Menu Menu_Table[30] =
{
// 当 下 进 返{0, 1, 0, 0,(*Home_Menu)}, //主页显示界面{1, 2, 0, 0,(*Set_Menu)}, //设置临界值界面{2, 3, 0, 0,(*Fan_Menu)}, //风扇控制界面{3, 4, 0, 0,(*Light_Menu)},//灯光控制界面{4, 5, 0, 0,(*Water_Menu)},//水泵控制界面{5, 0, 0, 0,(*Wifi_Menu)},//连接阿里云控制界面};
然后定义一个当前显示菜单的索引值和当前菜单显示绘制函数:
int Current_Menu_index = 0;//当前菜单索引
void(*Current)(u8g2_t u8g2);//当前菜单索引执行绘制函数
然后初始化好按键,按下后执行跳转:
切换下一菜单使用例子(如果是其他的可以八next换成其他的结构体成员):
Current_Menu_index = Menu_Table[Current_Menu_index].last_index;//变换索引值Current = Menu_Table[Current_Menu_index].current;//函数复制
(*Current)(u8g2);//执行绘制函数
在举一个例子帮助大家理解一下,比如,我们当前程序处在索引号为2(临界值界面),就会执行Set_Menu函数。此时,如果按下next按键,程序当前索引号就会变为3,并且执行索引号为3时候的Fan_Menu函数。以此类推!
5.2、OLED驱动
这里我是使用开源的u8g2库进行开发,这个库显示效果很好,而且也很好移植!具体可以参考我的这个博文:移植u8g2库,移植成功后使用也可以看看我的另外要给博文:u8g2库函数使用
5.3、按键消抖
我这里使用的是中断消抖:
unsigned char Key_PIN_Read()
{unsigned char Temp=0;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12) == GPIO_PIN_RESET) Temp = 1;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_RESET) Temp = 2;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET) Temp = 3;if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_15) == GPIO_PIN_RESET) Temp = 4;return Temp;} void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //中断回调函数
{Key_Slow++;if(Key_Slow == 10) Key_Slow = 0;//按键消抖
}void Key_Pro()
{if(Key_Slow) return;//按键减速Key_Slow=1;Key_Val=Key_PIN_Read();Key_Down=Key_Val & (Key_Val ^ Key_Old);//捕捉下降沿Key_Up = ~Key_Val & (Key_Old ^ Key_Val);//捕捉按键上降沿Key_Old=Key_Val;switch(Key_Up){case 1:break;case 2:break;case 3:break;case 4:break;}}
5.4、DHT11温湿度驱动
手动配置GPIO模式:
#define DHT_HIGHT HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET)
#define DHT_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET)
#define DHT_VALUE HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8)uint8_t datas[5];//空气温湿度数据void delay_us(uint16_t cnt)
{uint8_t i;while(cnt){for (i = 0; i < 10; i++){} cnt--;}
}void DHT_GPIO_Init(uint32_t Mode)
{GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOB_CLK_ENABLE();GPIO_InitStruct.Pin = GPIO_PIN_8;GPIO_InitStruct.Mode = Mode;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}void DHT11_Start(void)
{DHT_GPIO_Init(GPIO_MODE_OUTPUT_PP);DHT_HIGHT;DHT_LOW;HAL_Delay(30);DHT_HIGHT;DHT_GPIO_Init(GPIO_MODE_INPUT);while(DHT_VALUE);while(!DHT_VALUE);while(DHT_VALUE);
}void Read_Data_From_DHT()
{int i;//轮int j;//每一轮读多少次char tmp;char flag;DHT11_Start();DHT_GPIO_Init(GPIO_MODE_INPUT);for(i= 0;i < 5;i++){for(j=0;j<8;j++){while(!DHT_VALUE);//等待卡g点delay_us(40);if(DHT_VALUE == 1){flag = 1;while(DHT_VALUE);}else{flag = 0;}tmp = tmp << 1;tmp |= flag;}datas[i] = tmp;}
}
数据显示以及文字显示:
void Printf_DHT11(u8g2_t u8g2,u8g2_uint_t x, u8g2_uint_t y, const uint8_t *font)
{char var_buf[100];char var_buf1[100];Read_Data_From_DHT();//读取温湿度数据u8g2_DrawXBMP(&u8g2, x, y, 16, 16, kong);u8g2_DrawXBMP(&u8g2, x+16, y, 16, 16, qi);u8g2_DrawXBMP(&u8g2, x+32, y, 16, 16, wen);u8g2_DrawXBMP(&u8g2, x+48, y, 16, 16, du);u8g2_DrawXBMP(&u8g2, x, y+16, 16, 16, kong);u8g2_DrawXBMP(&u8g2, x+16, y+16, 16, 16, qi);u8g2_DrawXBMP(&u8g2, x+32, y+16, 16, 16, wen);u8g2_DrawXBMP(&u8g2, x+48, y+16, 16, 16, du);sprintf(var_buf , ": %d.%d C",datas[2],datas[3]);sprintf(var_buf1, ": %d.%d",datas[0],datas[1]);u8g2_SetFont(&u8g2, font);u8g2_DrawStr(&u8g2, x+64, y+16, var_buf);u8g2_DrawStr(&u8g2, x+64, y+32, var_buf1);
}
5.5、光照强度、土壤湿度ADC转换
int Printf_Soil(u8g2_t u8g2,u8g2_uint_t x, u8g2_uint_t y, const uint8_t *font)
{char var[100];int value;HAL_ADC_Start(&hadc1); //启动ADC单次转换HAL_ADC_PollForConversion(&hadc1, 50); //等待ADC转换完成value = HAL_ADC_GetValue(&hadc1); //读取ADC转换数据value = 100 - value * 100 / 4096;
// Soil = value;u8g2_DrawXBMP(&u8g2, x, y, 16, 16, tu);u8g2_DrawXBMP(&u8g2, x+16, y, 16, 16, rang);u8g2_DrawXBMP(&u8g2, x+32, y, 16, 16, shi);u8g2_DrawXBMP(&u8g2, x+48, y, 16, 16, du);sprintf(var , ": %d %%", value);u8g2_SetFont(&u8g2, font);u8g2_DrawStr(&u8g2, x+64, y+16, var);return value;}int Printf_Light(u8g2_t u8g2,u8g2_uint_t x, u8g2_uint_t y, const uint8_t *font)
{char var[100];int value;HAL_ADC_Start(&hadc2); //启动ADC单次转换HAL_ADC_PollForConversion(&hadc2, 50); //等待ADC转换完成value = HAL_ADC_GetValue(&hadc2); //读取ADC转换数据value = 100 - value * 100 / 4096;
// Light = value;u8g2_DrawXBMP(&u8g2, x, y, 16, 16, guang);u8g2_DrawXBMP(&u8g2, x+16, y, 16, 16, zhao);u8g2_DrawXBMP(&u8g2, x+32, y, 16, 16, qiang);u8g2_DrawXBMP(&u8g2, x+48, y, 16, 16, du);sprintf(var , ": %d %%", value);u8g2_SetFont(&u8g2, font);u8g2_DrawStr(&u8g2, x+64, y+16, var);return value;}
5.6、esp8266连接阿里云
这里我为了保险起见就是用了很多的延迟时间(连接时间过长),你们可以自己调节时间!
#define USERNAME "test&k28qfTAtkBg" //用户名
// "test&k28qfTAtkBg"
#define PASSWORD "d7c3b27a5d8ca954487de1e1946a4a08439a2c2508242268e9d90ce178178f39" //密码
// "d7c3b27a5d8ca954487de1e1946a4a08439a2c2508242268e9d90ce178178f39"
#define CLIENTID "k28qfTAtkBg.test|securemode=2\\,signmethod=hmacsha256\\,timestamp=1736673123047|" //设备名称
// "k28qfTAtkBg.test|securemode=2\\,signmethod=hmacsha256\\,timestamp=1736673123047|"
#define PRODUCTID "k28qfTAtkBg" //产品ID
// "k28qfTAtkBg"
#define DOMAINNAME "iot-06z00fj5kcoes6j.mqtt.iothub.aliyuncs.com" //域名
// "iot-06z00fj5kcoes6j.mqtt.iothub.aliyuncs.com"
#define DEVICENAME "test"//WiFi连接函数
void Wifi_Connect()
{printf("AT\r\n");//避免报错HAL_Delay(500);printf("AT+RESTORE\r\n");//恢复出厂HAL_Delay(5000);// printf("AT+RST\r\n");//复位
// HAL_Delay(5000);printf("ATE0\r\n");//关闭回显HAL_Delay(5000);printf("AT+CWMODE=3\r\n");//设置双模式HAL_Delay(5000);printf("AT+CWJAP=\"USER_E191B0\",\"98599714\"\r\n");//设置WIFI密码和账号HAL_Delay(5000);printf("AT+MQTTUSERCFG=0,1,\"NULL\",\"%s\",\"%s\",0,0,\"\"\r\n",USERNAME,PASSWORD);//设置MQTT的username和passwordHAL_Delay(5000);printf("AT+MQTTCLIENTID=0,\"%s\"\r\n",CLIENTID); //设置CLIENTIDHAL_Delay(5000);printf("AT+MQTTCONN=0,\"%s\",1883,1\r\n",DOMAINNAME);//设置域名HAL_Delay(5000);printf("AT+MQTTSUB=0,\"/%s/%s/user/get\",1\r\n",PRODUCTID,DEVICENAME);//订阅}
六、功能拓展
后续拓展:
1、电脑阿里云平台控制单片机
2、添加语音模块
。。。。。。。。。。。。其他
后续打了PCB板子后有空会实现给大家,觉得有帮助的可以点点关注!