目录

前言:

一、项目介绍和演示视频

二、硬件需求准备

三、硬件框图

四、CubeMX配置

1. 按键、蜂鸣器GPIO口配置

2. ADC输入配置

3. IIC——驱动OLED

4. DHT11温湿度读取

5. PWM配置——光照灯、水泵、风扇

6. 串口——esp8266模块

7. 定时器配置——按键消抖所需

五、部分代码实现思路

1. 菜单实现思路

2. OLED驱动

3. 按键消抖

4. DHT11温湿度驱动

5. 光照强度、土壤湿度ADC转换

6. esp8266连接阿里云


前言:

源代码下载链接:

需要实物的可以私信博主或者在文章最下方添加好友。

一、项目介绍和演示视频

最近帮别人做了个毕设,具体功能如下:

1、按键:

      按键一:切换菜单

      按键二:切换选项

      按键三:+-或调节风扇 | 水泵 | 灯光

      按键四:确认 | 连接阿里云平台

2、液晶屏幕显示:

     菜单1:主界面显示空气温湿度,土壤湿度,光照强度,检测是否超过或低于临界值

     菜单2:临界值调节

     菜单3:风扇手动开关

     菜单4:灯光手动开关

     菜单5:水泵手动开关

     菜单6:阿里云连接菜单界面

3、土壤湿度、空气温湿度、光照强度高于或低于临界值,蜂鸣器就会响

4、土壤湿度过低自动打开水泵

5、光照强度过低自动打开照明灯

6、空气温度过高会自动打开风扇

7、连接阿里云平台,可以在电脑上面查看各个数据

实物图:

演示视频:面包板演示

基于STM32的阿里云大棚

二、硬件需求准备

1、STM32F103最小核心开发板

2、0.96寸OLED屏幕

3、按键X4

4、TB6612FNG点击驱动模块

5、水泵

6、电机(带小风扇)

7、DHT11温湿度模块

8、esp8266模块

9、蜂鸣器

10、土壤湿度传感器

11、光照强度传感器

12、照明灯模块

三、硬件框图

四、CubeMX配置

这里只演示配置使用的外设配置过程!

1. 按键、蜂鸣器GPIO口配置

按键配置为上拉模式、蜂鸣器低电平输出有效

2. ADC输入配置

ADC为读取土壤湿度和光照强度的ADC数值,这里开启两个ADC分别读取

ADC1——读取土壤湿度:

ADC2——读取光照强度:

3. IIC——驱动OLED

4. DHT11温湿度读取

这个使用过程中会对弈随意变动上下拉模式,所以这里就不配置了,使用的GPIO口为PB8

5. PWM配置——光照灯、水泵、风扇

6. 串口——esp8266模块

开启中断:

7. 定时器配置——按键消抖所需

开启中断:

最后生成工程即可。

五、代码框架

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函数。以此类推!

2. OLED驱动

这里我是使用开源的u8g2库进行开发,这个库显示效果很好,而且也很好移植!

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;
    }

}

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. 光照强度、土壤湿度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;
	
}

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和password
	HAL_Delay(5000);
	printf("AT+MQTTCLIENTID=0,\"%s\"\r\n",CLIENTID);	//设置CLIENTID
	HAL_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);//订阅

}

                                                                     👇🏻 需要实物 添加 博主👇🏻

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐