• STM32F103+DHT11模块+步进电机28BYJ-48 简单实现 智能浇水系统demo


    @

    前言

    注意:浇水由LED1的亮灭进行模拟
    源码参考:
        正点原子定时器中断实验
        正点原子RTC实验
        正点原子RTC实验
        正点原子TFTLCD显示实验
        正点原子按键实验
        正点原子蜂鸣器实验
    开发板:正点原子 STM32F103 精英版
    语言:C语言
    开发环境:Keil5
    开发板使用了 LED KEY BEEP TFTLCD TIM3 RTC USART DHT11模块 步进电机28BYJ-48 ULN2003驱动
    程序仅供学习参考,会有一些bug(比如:报警时长、休息时长的问题)。
    步进电机部分参考:STM32F103+步进电机28BYJ-48+ULN2003 实现简单的正反转demo

    代码下载:

    码云 GitHub

    功能介绍:

    1. LCD显示当前时间(时:分:秒)、当前温度(CEL)、当前湿度(%RH)、温度上限(CEL)、湿度上限(%RH)、湿度下限(%RH)、浇水时长(min)、休息时长(min)、报警时长(sec)。已经当前设备处于的工作模式(working浇水中 resting休息中 running正常运行中)。
    2. LED0约2秒反转一次。LED1亮表示浇水,灭表示关水(浇水由LED1和步进电机来模拟)。
    3. 按键功能;KEY0 进入修改模式,分别针对当前时间、当前温度、当前湿度、温度上限、湿度上限、湿度下限、浇水时长、休息时长,报警时长的修改(处于修改下的数值会变红),最后退出修改模式。
      在修改模式下:KEY1数值+1(封顶循环),KEY_UP数值-1(封底循环)
      在普通模式下:KEY1进入休息,KEY_UP进入工作。(提供了手动控制)
    4. 当前湿度低于下限或湿度正常但温度高于上限时,报警设置的时长(这里时间<=设定),开始进入浇水(LED1点亮模拟浇水,电机正转90度),浇水过程中如果湿度高于上限,可以打断浇水(直接进入休息,休息时长是工作时长+休息时长-已工作时长),否则浇水完毕后会进入休息(时长为休息时长,电机反转90度)。休息完毕后会重新进行条件判断。
    5. 配置的数据存储于flash中(首地址0X08070000)
    6. 串口打印相应的信息,具体参考效果图。
    7. 不接入DHT11模块是不行的,会等待模块接入。
    8. TIM4对独立看门狗进行投食,(100ms一投食,125ms的等待投食)

    接线

    DHT11 DATA —> PG11 3.3V供电
    在这里插入图片描述
    步进电机28BYJ-48 ULN2003驱动

    +    —>   5V
    -    —>   GND
    IN1  —>   PF1
    IN2  —>   PF2
    IN3  —>   PF3
    IN4  —>   PF4
    

    在这里插入图片描述

    效果图

    在这里插入图片描述

    开始运行(我已经设置好了一些配置),现在温湿度都正常。(ps:时间不是很准)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    漏拍LED1了,补充
    在这里插入图片描述
    在这里插入图片描述

    核心代码

    main.c

    #include "led.h"
    #include "delay.h"
    #include "key.h"
    #include "sys.h"
    #include "lcd.h"
    #include "usart.h"
    #include "rtc.h"
    #include "beep.h"
    #include "stmflash.h"
    #include "dht11.h"
    #include "timer3.h"
    #include "timer4.h"
    #include "step.h"
    #include "wdg.h"
    
    #define FLASH_SAVE_ADDR 0X08070000 //设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
    
    // 配置的结构体
    typedef struct Config
    {
    	u16 temp_max;
    	u16 rh_max;
    	u16 rh_min;
    	u16 water_time;
    	u16 rest_time;
    	u16 alarm_time;
    }Config;
    
    /* 显示时间,index特殊处理 */
    void show_msg(u8 index, _calendar_obj calendar_temp, u16 temp, u16 rh, Config config);
    
    int main(void)
    {
        /* 按键返回值 */
        u8 key = 0;
        /* 修改指向下标 */
        u8 index = 0;
        /* 日历结构体 */
        _calendar_obj calendar_temp;
        // 修改模式标志
        u8 mode = 0;
    	// 计时用秒
    	u16 sec = 0;
    	// 工作标志位
    	u8 work_flag = 0;
    	// 休息标志位
    	u8 rest_flag = 0;
    	u16 temp = 0;
    	u16 rh = 0;
    	Config config = {40, 80, 50, 1, 1, 1};
    	// 电机标志位 1为正转了90度,浇水状态 0为关水状态
    	u8 motor_flag = 0;
    
        /* 延时函数初始化 */
        delay_init();
        /* 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级 */
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
        /* 串口初始化为115200 */
        uart_init(115200);
        /* LED端口初始化 */
        LED_Init();
        /* LCD初始化 */
        LCD_Init();
        /* 按键初始化 */
        KEY_Init();
        /* RTC初始化 */
        RTC_Init();
        /* 初始化蜂鸣器端口 */
        BEEP_Init();
    	// 10Khz的计数频率,计数到500为50ms  
    	TIM3_Int_Init(499, 7199);
    	// 独立看门狗初始化 预分频数为4*2^1=8,重载值为625,溢出时间为125ms
    	IWDG_Init(1,625);    
    	// 10Khz 的计数频率,计数到 1000 为 100ms
    	TIM4_Int_Init(999, 7199); 
    	/*                             
    		步进电机初始化
    		IN4: PF4  d
    		IN3: PF3  c
    		IN2: PF2  b
    		IN1: PF1  a
    	*/
    	Step_Motor_GPIO_Init();
    	
    	LED0 = 1;
    	
    	// DHT11初始化 DATA -> PG11
    	while(DHT11_Init())	
        {
            LCD_ShowString(30,130,200,16,16,"DHT11 Error");
            delay_ms(200);
            LCD_Fill(30,130,239,130+16,WHITE);
            delay_ms(200);
        }
    	
    	DHT11_Read_Data((u8 *)&temp, (u8 *)&rh);
    
        /*
        * 显示时间
        * 设置字体为蓝色
        */
        POINT_COLOR = BLUE;
        LCD_ShowString(60, 40, 300, 16, 16, "NOW      :  :  ");
    	LCD_ShowString(60, 66, 100, 16, 16, "Temp:     CEL");
    	LCD_ShowString(60, 82, 100, 16, 16, "RH  :     %RH");
        LCD_ShowString(60, 162, 200, 12, 12, "Temp   Max:    CEL");
    	LCD_ShowString(60, 174, 200, 12, 12, "RH     Max:    %RH");
    	LCD_ShowString(60, 186, 200, 12, 12, "RH     Min:    %RH");
    	LCD_ShowString(60, 198, 200, 12, 12, "Water Time:    min");
    	LCD_ShowString(60, 210, 200, 12, 12, "Rest  Time:    min");
    	LCD_ShowString(60, 222, 200, 12, 12, "Alarm Time:    sec");
    	POINT_COLOR = BLACK;
    	LCD_ShowString(60, 260, 200, 16, 16, "Mode: ");
    	POINT_COLOR = BLUE;
        calendar_temp = calendar;	
    	
    	delay_ms(1000);
    	DHT11_Read_Data((u8 *)&temp, (u8 *)&rh);
    	delay_ms(1000);
    	DHT11_Read_Data((u8 *)&temp, (u8 *)&rh);
    
        // 从指定地址开始读出指定长度的数据
        // ReadAddr:起始地址
        // pBuffer:数据指针
        // NumToWrite:半字(16位)数
        STMFLASH_Read(FLASH_SAVE_ADDR, &config.temp_max, 1);
        STMFLASH_Read(FLASH_SAVE_ADDR + 0X2, &config.rh_max, 1);
    	STMFLASH_Read(FLASH_SAVE_ADDR + 0X4, &config.rh_min, 1);
    	STMFLASH_Read(FLASH_SAVE_ADDR + 0X6, &config.water_time, 1);
    	STMFLASH_Read(FLASH_SAVE_ADDR + 0X8, &config.rest_time, 1);
    	STMFLASH_Read(FLASH_SAVE_ADDR + 0X10, &config.alarm_time, 1);
    	
    	// flash初始值
    	if(65535 == config.temp_max && 65535 == config.rh_max && 65535 == config.rh_min && 65535 == config.water_time && 65535 == config.rest_time && 65535 == config.alarm_time)
    	{
    		config.temp_max = 40;
    		config.rh_max = 80;
    		config.rh_min = 50;
    		config.water_time = 1;
    		config.rest_time = 1;
    		config.alarm_time = 1;
    	}
    	
    	printf("config.temp_max = %d, config.rh_max = %d, config.rh_min = %d, config.water_time = %d, config.rest_time = %d, config.alarm_time = %d
    ",
    		config.temp_max, config.rh_max, config.rh_min, config.water_time, config.rest_time, config.alarm_time);
    
        while (1)
        {
    		if(work_flag == 1)
    		{
    			POINT_COLOR = RED;
    			LCD_ShowString(108, 260, 100, 16, 16, "Working");
    		}
    		else if(rest_flag == 1)
    		{
    			POINT_COLOR = GREEN;
    			LCD_ShowString(108, 260, 100, 16, 16, "Resting");
    		}
    		else
    		{
    			POINT_COLOR = BLACK;
    			LCD_ShowString(108, 260, 100, 16, 16, "Running");
    		}
    		POINT_COLOR = BLUE;
    		
    		if(time % 40 == 0)
    		{
    			// 读取温湿度值
    			DHT11_Read_Data((u8 *)&temp, (u8 *)&rh);
    			// printf("Temp:%dCEL , Humidity:%d%%RH
    ", temp, rh);
    			
    			LED0 = !LED0;
    		}
    		
    		// 约1秒 工作或休息计时中时
    		if(time % 20 == 0 && (work_flag == 1 || rest_flag == 1))
    		{
    			sec++;
    		}
    		
    		// 不在工作和休息中,清空计时
    		if(work_flag == 0 && rest_flag == 0)
    		{
    			sec = 0;
    		}
    		
    		// 工作时间到达
    		if(work_flag == 1)
    		{
    			if(sec >= config.alarm_time)
    			{
    				BEEP = 0;
    			}
    			
    			if(sec / 60 >= config.water_time)
    			{
    				// 停止浇水
    				LED1 = 1;
    				printf("****  water finish  ****
    ");
    				// 工作标志位置0
    				work_flag = 0;
    				// 休息标志位置1
    				rest_flag = 1;
    				
    				if(motor_flag == 1)
    				{
    					// 反转90度
    					motor_circle(16, 0, 2);
    					motor_flag = 0;
    				}
    			}
    		}
    		
    		// 工作完后 休息时间到达
    		if(rest_flag == 1 && sec / 60 >= (config.water_time + config.rest_time))
    		{
    			printf("****  rest finish  ****
    ");
    			sec = 0;
    			rest_flag = 0;
    		}
    		
            /* 根据index显示 */
            if (0 == index)
                show_msg(index, calendar, temp, rh, config);
            else
                show_msg(index, calendar_temp, temp, rh, config);
    
            /*
            * 键处理函数
            * 返回按键值
            * mode:0,不支持连续按;1,支持连续按;
            * 0,没有任何按键按下
            * 1,KEY0按下
            * 2,KEY1按下
            * 3,KEY3按下 WK_UP
            */
            key = KEY_Scan(1);
            /* KEY0 进入修改模式,依次顺序循环 */
            if (1 == key)
            {
                index++;
                index = index % 10;
                /* 进入修改 */
                if (1 == index)
                {
                    calendar_temp = calendar;
                    mode = 1;
                }
                /* 退出修改 */
                else if (0 == index)
                {
                    calendar = calendar_temp;
                    RTC_Set(calendar_temp.w_year, calendar_temp.w_month, calendar_temp.w_date, calendar_temp.hour, calendar_temp.min, calendar_temp.sec);
                    // 从指定地址开始写入指定长度的数据
                    // WriteAddr:起始地址(此地址必须为2的倍数!!)
                    // pBuffer:数据指针
                    // NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
    				STMFLASH_Write(FLASH_SAVE_ADDR, &config.temp_max, 1);
    				STMFLASH_Write(FLASH_SAVE_ADDR + 0X2, &config.rh_max, 1);
    				STMFLASH_Write(FLASH_SAVE_ADDR + 0X4, &config.rh_min, 1);
    				STMFLASH_Write(FLASH_SAVE_ADDR + 0X6, &config.water_time, 1);
    				STMFLASH_Write(FLASH_SAVE_ADDR + 0X8, &config.rest_time, 1);
    				STMFLASH_Write(FLASH_SAVE_ADDR + 0X10, &config.alarm_time, 1);
                    mode = 0;
                }
            }
            /* KEY1 选中值+1 */
            else if (2 == key)
            {
                if (1 == index)
                {
                    calendar_temp.hour++;
                    calendar_temp.hour = calendar_temp.hour > 23 ? 0 : calendar_temp.hour;
                }
                else if (2 == index)
                {
                    calendar_temp.min++;
                    calendar_temp.min = calendar_temp.min > 59 ? 0 : calendar_temp.min;
                }
                else if (3 == index)
                {
                    calendar_temp.sec++;
                    calendar_temp.sec = calendar_temp.sec > 59 ? 0 : calendar_temp.sec;
                }
                else if (4 == index)
                {
                    config.temp_max++;
                    config.temp_max = config.temp_max > 99 ? 0 : config.temp_max;
                }
                else if (5 == index)
                {
                    config.rh_max++;
                    config.rh_max = config.rh_max > 99 ? (config.rh_min+1) : config.rh_max;
                }
    			else if (6 == index)
                {
                    config.rh_min++;
                    config.rh_min = config.rh_min > (config.rh_max-1) ? 0 : config.rh_min;
                }
    			else if (7 == index)
                {
                    config.water_time++;
                    config.water_time = config.water_time > 9 ? 1 : config.water_time;
                }
    			else if (8 == index)
                {
                    config.rest_time++;
                    config.rest_time = config.rest_time > 9 ? 1 : config.rest_time;
                }
    			else if (9 == index)
                {
                    config.alarm_time++;
                    config.alarm_time = config.alarm_time > 9 ? 1 : config.alarm_time;
                }
    			// 普通模式下
                else if (0 == index)
                {
    				printf("****  KEY1 press, start rest  ****
    ");
    				LED1 = 1;
    				work_flag = 0;
    				// 休息计时开启
    				rest_flag = 1;
    				
    				if(motor_flag == 1)
    				{
    					// 反转90度
    					motor_circle(16, 0, 2);
    					motor_flag = 0;
    				}
                }
            }
            /* KEY_UP 选中值-1 */
            else if (3 == key)
            {
                if (1 == index)
                {
                    calendar_temp.hour = calendar_temp.hour == 0 ? 23 : calendar_temp.hour - 1;
                }
                else if (2 == index)
                {
                    calendar_temp.min = calendar_temp.min == 0 ? 59 : calendar_temp.min - 1;
                }
                else if (3 == index)
                {
                    calendar_temp.sec = calendar_temp.sec == 0 ? 59 : calendar_temp.sec - 1;
                }
                else if (4 == index)
                {
                    config.temp_max = config.temp_max == 0 ? 100 : config.temp_max - 1;
                }
                else if (5 == index)
                {
                    config.rh_max = config.rh_max == config.rh_min ? 100 : config.rh_max - 1;
                }
    			else if (6 == index)
                {
                    config.rh_min = config.rh_min == 0 ? config.rh_max - 1 : config.rh_min - 1;
                }
    			else if (7 == index)
                {
                    config.water_time = config.water_time == 0 ? 1 : config.water_time - 1;
                }
    			else if (8 == index)
                {
                    config.rest_time = config.rest_time == 0 ? 1 : config.rest_time - 1;
                }
    			else if (9 == index)
                {
                    config.alarm_time = config.alarm_time == 0 ? 1 : config.alarm_time - 1;
                }
                else if (0 == index)
                {
                    printf("****  KEY_UP press, start work  ****
    ");
    				LED1 = 0;
    				BEEP = 1;
    				work_flag = 1;
    				
    				if(motor_flag == 0)
    				{
    					// 正转90度
    					motor_circle(16, 1, 2);
    					motor_flag = 1;
    				}
                }
            }
            else if (0 == key)
            {
                // 普通模式下
                if (0 == mode)
                {
    				// 湿度过低 且 没有在工作计时和休息计时中
    				if(rh <= config.rh_min && work_flag == 0 && rest_flag == 0)
    				{
    					printf("****  The humidity is too low, start watering  ****
    ");
    					BEEP = 1;
    					work_flag = 1;
    					
    					// 模拟浇水
    					LED1 = 0;
    					if(motor_flag == 0)
    					{
    						// 正转90度
    						motor_circle(16, 1, 2);
    						motor_flag = 1;
    					}
    				}
    				// 湿度合适但温度过高 且 没有在工作计时和休息计时中
    				else if(config.rh_max > rh && rh > config.rh_min && temp >= config.temp_max && work_flag == 0 && rest_flag == 0)
    				{
    					printf("****  The temperature is too high, start watering  ****
    ");
    					BEEP = 1;
    					work_flag = 1;
    					
    					// 模拟浇水
    					LED1 = 0;
    					if(motor_flag == 0)
    					{
    						// 正转90度
    						motor_circle(16, 1, 2);
    						motor_flag = 1;
    					}
    				}
    				
    				// 湿度过高 且 不在休息计时中
    				if(rh >= config.rh_max && rest_flag == 0)
    				{
    					printf("****  If the humidity is too high, stop watering  ****
    ");
    					work_flag = 0;
    					// 休息计时开启
    					rest_flag = 1;
    					
    					// 停止浇水
    					LED1 = 1;
    					if(motor_flag == 1)
    					{
    						// 反转90度
    						motor_circle(16, 0, 2);
    						motor_flag = 0;
    					}
    				}
                }
            }
    
            delay_ms(20);
        }
    }
    
    /* 显示信息,index特殊处理 */
    void show_msg(u8 index, _calendar_obj calendar_temp, u16 temp, u16 rh, Config config)
    {
        POINT_COLOR = BLUE;
        if (1 == index)
            POINT_COLOR = RED;
        LCD_ShowNum(116, 40, calendar_temp.hour, 2, 16);
        POINT_COLOR = BLUE;
        if (2 == index)
            POINT_COLOR = RED;
        LCD_ShowNum(140, 40, calendar_temp.min, 2, 16);
        POINT_COLOR = BLUE;
        if (3 == index)
            POINT_COLOR = RED;
        LCD_ShowNum(164, 40, calendar_temp.sec, 2, 16);
        POINT_COLOR = BLUE;
    	if (4 == index)
            POINT_COLOR = RED;
        LCD_ShowNum(132, 162, config.temp_max, 2, 12);
        POINT_COLOR = BLUE;
    	if (5 == index)
            POINT_COLOR = RED;
        LCD_ShowNum(132, 174, config.rh_max, 2, 12);
        POINT_COLOR = BLUE;
    	if (6 == index)
            POINT_COLOR = RED;
        LCD_ShowNum(132, 186, config.rh_min, 2, 12);
        POINT_COLOR = BLUE;
    	if (7 == index)
            POINT_COLOR = RED;
        LCD_ShowNum(132, 198, config.water_time, 2, 12);
        POINT_COLOR = BLUE;
    	if (8 == index)
            POINT_COLOR = RED;
        LCD_ShowNum(132, 210, config.rest_time, 2, 12);
        POINT_COLOR = BLUE;
    	if (9 == index)
            POINT_COLOR = RED;
        LCD_ShowNum(132, 222, config.alarm_time, 2, 12);
        POINT_COLOR = BLUE;
    	
    	LCD_ShowNum(116, 66, temp, 2, 16);
    	LCD_ShowNum(116, 82, rh, 2, 16);
    }
    
    
    

    参考用图

    DHT11

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    步进电机28BYJ-48 ULN2003驱动

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    ArrayList和HashTable妙用一
    面向过程,面向对象的深入理解一
    jquery中限制部分字段不能输入
    MySql用int存储时间
    android绑定sqlite数据库与程序一起发布
    extjs 4和jquery整合
    jQuery.support 的实现方式
    常用JavaScripts方法
    SVN错误信息大全
    android封装的menu自定义菜单列表
  • 原文地址:https://www.cnblogs.com/ikaros-521/p/15323429.html
Copyright © 2020-2023  润新知