基于STM32的环境监测预警系统

目录

项目概述:

 材料:

注:

一 LCD1602

介绍

STM32驱动LCD1602

接线

代码

二 DHT11温湿度检测

介绍于时序分析

温湿度数据串口传输给上位机

难点

代码

三 ADC读取烟雾传感器

四 环境监测系统

参考接线

代码实现

五 预警功能

串口功能测试

代码实现

警报功能实现

六 快速自制上位机APP

 UI设计

逻辑设计

连接蓝牙下位机

按钮发送指令

蓝牙接收信息 


基于STM32的环境监测预警系统

项目概述:

主控STM32f103c8t6 。利用 DHT11 和烟雾传感器,进行温湿度检测和烟雾值检测,并实时在LCD屏幕上进行数值显示,同时通过串口通信将数据信息传至上位机显示。当检测到温湿度高出设定阈值,将打开排风扇进行通风;当检测到有烟雾时,将关闭通风风扇防止火灾蔓延,开启蜂鸣器警报并持续向上位机发送警报信息。

 材料:

STM32f103c8t6DHT11烟雾传感器
LCD1602HC蓝牙模块(用于串口通信)蜂鸣器
直流电机驱动模块/继电器直流电机和风扇桨叶杜邦线面包板电源等

注:

资料包已上传,包含源代码和自制上位机APP。

一 LCD1602

介绍

详情参考前文。 

(59条消息) C51外设:LCD1602_我有在好好学习的博客-CSDN博客

STM32驱动LCD1602

接线

仅供参考,电源线不要接错,其它线可以按自己意愿接

代码

示例:显示两排文字

把gpio口都对应着宏定义出来,参考产品手册的封装对应函数

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define RS_GPIO_Port GPIOB
#define RW_GPIO_Port GPIOB
#define EN_GPIO_Port GPIOB
#define RS_GPIO_PIN	 GPIO_PIN_1
#define RW_GPIO_PIN	 GPIO_PIN_2
#define EN_GPIO_PIN	 GPIO_PIN_10

#define RS_HIGH	HAL_GPIO_WritePin(RS_GPIO_Port, RS_GPIO_PIN, GPIO_PIN_SET)
#define RS_LOW 	HAL_GPIO_WritePin(RS_GPIO_Port, RS_GPIO_PIN, GPIO_PIN_RESET)
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port, RW_GPIO_PIN, GPIO_PIN_SET)
#define RW_LOW 	HAL_GPIO_WritePin(RW_GPIO_Port, RW_GPIO_PIN, GPIO_PIN_RESET)
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port, EN_GPIO_PIN, GPIO_PIN_SET)
#define EN_LOW 	HAL_GPIO_WritePin(EN_GPIO_Port, EN_GPIO_PIN, GPIO_PIN_RESET)

/* USER CODE END PM */


/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//查忙
void check_busy()
{
	char tmp = 0x80;
	GPIOA->ODR = 0x80;
	
	while(tmp & 0x80){
		RS_LOW;//低电平选指令寄存器
		RW_HIGH;//高电平时进行读操作
		
		EN_LOW;//变成低电平时,液晶模块执行命令
		HAL_Delay(1);
		EN_HIGH;
		HAL_Delay(1);
		tmp = GPIOA->ODR;
		EN_LOW;
		HAL_Delay(1);
	}
}
//发指令
void Write_Cmd_Func(char cmd){
	
	//check_busy();
	
	RS_LOW;
	RW_LOW;
	EN_LOW;
	GPIOA->ODR = cmd;
	HAL_Delay(1);
	EN_HIGH;
	HAL_Delay(1);
	EN_LOW;
	HAL_Delay(1);
}
//发数据
void Write_Data_Func(char Data){
	
	//check_busy();
	
	RS_HIGH;
	RW_LOW;
	EN_LOW;
	GPIOA->ODR = Data;
	HAL_Delay(1);
	EN_HIGH;
	HAL_Delay(1);
	EN_LOW;
	HAL_Delay(1);
}


//lcd初始化
void LCD1602_INIT()
{
	//(1)延时 15ms
	HAL_Delay(15);
	//(2)写指令 38H(不检测忙信号)
	Write_Cmd_Func(0x38);
	//(3)延时 5ms
	HAL_Delay(5);
	//(4)以后每次写指令,读/写数据操作均需要检测忙信号
	//(5)写指令 38H:显示模式设置
	Write_Cmd_Func(0x38);
	//(6)写指令 08H:显示关闭
	Write_Cmd_Func(0x08);
	//(7)写指令 01H:显示清屏
	Write_Cmd_Func(0x01);
	//(8)写指令 06H:显示光标移动设置
	Write_Cmd_Func(0x06);
	//(9)写指令 0CH:显示开及光标设置}
	Write_Cmd_Func(0x0c);
}

//显示字符串
void LCD1602_showLine(char row, char col, char *string)
{
	
	switch(row){

		case 1:
				Write_Cmd_Func(0x80+col);
				while(*string){
					Write_Data_Func(*string);
					string++;
				}
				break;
		
		case 2:
				Write_Cmd_Func(0x80+0x40+col);
				while(*string){
					Write_Data_Func(*string);
					string++;
				}
				break;
	}
}

/* USER CODE END 0 */




  /* USER CODE BEGIN 2 */
	LCD1602_INIT();
	
	LCD1602_showLine(1,5,"NO.22222");
	LCD1602_showLine(2,0,"YZQ handsome");
	
  /* USER CODE END 2 */

二 DHT11温湿度检测

介绍于时序分析

参考前文

(59条消息) 基于C51的 温湿度监测系统(及蓝牙温控风扇)_我有在好好学习的博客-CSDN博客

温湿度数据串口传输给上位机

难点

由于模块仅有一个Data口,又要输出又要输入,那么需要自定义GPIO口,这里选用GPIO B7 

代码

这里选用串口1

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#define DHT_HIGH HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET)
#define DHT_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET)
#define DHT_VALUE HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7)

char datas[5];

void delay_us(uint16_t cnt)
{
    uint8_t i;

    while(cnt)
    {
        for (i = 0; i < 10; i++)
        {

        }
        cnt--;
    }
}

//根据传入参数‘mode’来选择GPIO口模式
//"GPIO_MODE_OUTPUT_PP"和"GPIO_MODE_INPUT"两种
void DHT_GPIO_Init(uint32_t mode)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	__HAL_RCC_GPIOB_CLK_ENABLE();//打开时钟

  /*Configure GPIO pin : PB8 */
  GPIO_InitStruct.Pin = GPIO_PIN_7;
  GPIO_InitStruct.Mode = mode;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

void DHT11_Start()
{
	DHT_GPIO_Init(GPIO_MODE_OUTPUT_PP);
	DHT_HIGH;
	DHT_LOW;
	HAL_Delay(30);
	DHT_HIGH;
	
	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++){
		//卡g点:while(!dht)       有效数据都是高电平,持续时间不一样,50us读,低电平0 高电平
		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;
	}
}

int fputc(int ch, FILE *f)
{      
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);  
    return ch;
}
/* USER CODE END 0 */



    /* USER CODE BEGIN 3 */
		Read_Data_From_DHT();
		printf("Temp: %d.%d ", datas[2], datas[3]);
		printf("Humi: %d.%d\r\n", datas[0], datas[1]);
		HAL_Delay(1000);
  }
  /* USER CODE END 3 */

三 ADC读取烟雾传感器

 完完整整的在前文实现过,不再赘述

(59条消息) STM32:ADC_我有在好好学习的博客-CSDN博客

四 环境监测系统

检测温度、湿度、烟雾值,显示在LCD上,同时串口上传上位机(这里演示用串口蓝牙透传)。

 

参考接线

将所有GPIO拉高,蜂鸣器暂时没用。

如果也使用面包板+stm32最小系统,要注意面包板接线准则,

不要乱接跳线,比如两个点位能用长跳线一部到位,就别用两条短线续连。

乱接跳线很容易导致单片机收到电磁干扰而无法正常工作。

代码实现

 main.c

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "dht.h"
#include "lcd.h"
/* USER CODE END Includes */


/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
extern uint8_t temp_humi[5];//温湿度信息

uint8_t smoke_value;//烟雾数值

//主程序发送数据的字符串缓冲区
uint8_t temp_humi_Mes[16];//温湿度信息字符串

uint8_t smoke_Mes[8];//烟雾值信息字符串

/* USER CODE END PV */


/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//覆写printf
int fputc(int ch, FILE *f)
{      
    unsigned char temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,0xffff);  
    return ch;
}

//生成信息字符串
void MesCreate()
{
	sprintf((char *)temp_humi_Mes,"T:%d.%d%cC H:%d.%d%%", temp_humi[2], temp_humi[3],0xDF,temp_humi[0], temp_humi[1]);
	sprintf((char *)smoke_Mes,"S:%d%%",smoke_value);
}

/* USER CODE END 0 */



  /* USER CODE BEGIN 2 */
	LCD1602_INIT();

	LCD1602_showLine(1,5,"Start");
	printf("Start\r\n");
	
	HAL_Delay(2000);
	printf("OK\r\n");
	
  /* USER CODE END 2 */


  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		
		Read_Data_From_DHT();//读取温湿度信息
		MesCreate();//生成温湿度信息字符串
		
		HAL_ADC_Start(&hadc1); //启动ADC单次转换
		HAL_ADC_PollForConversion(&hadc1, 50); //等待ADC转换完成
		smoke_value = (uint8_t)(HAL_ADC_GetValue(&hadc1) / (4096.00/100.00)); //读取ADC转换数据

		//LCD显示温湿度和烟雾值
		LCD1602_showLine(1,0,(char *)temp_humi_Mes);
		LCD1602_showLine(2,0,(char *)smoke_Mes);
		
		//串口上传数据至PC
		printf("%s %s\r\n", temp_humi_Mes,smoke_Mes);
		
		HAL_Delay(1000);
		
  }
  /* USER CODE END 3 */

lcd.c

#include "lcd.h"
#include "gpio.h"

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define RS_GPIO_Port GPIOB
#define RW_GPIO_Port GPIOB
#define EN_GPIO_Port GPIOB
#define RS_GPIO_PIN	 GPIO_PIN_1
#define RW_GPIO_PIN	 GPIO_PIN_10
#define EN_GPIO_PIN	 GPIO_PIN_11

#define RS_HIGH	HAL_GPIO_WritePin(RS_GPIO_Port, RS_GPIO_PIN, GPIO_PIN_SET)
#define RS_LOW 	HAL_GPIO_WritePin(RS_GPIO_Port, RS_GPIO_PIN, GPIO_PIN_RESET)
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port, RW_GPIO_PIN, GPIO_PIN_SET)
#define RW_LOW 	HAL_GPIO_WritePin(RW_GPIO_Port, RW_GPIO_PIN, GPIO_PIN_RESET)
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port, EN_GPIO_PIN, GPIO_PIN_SET)
#define EN_LOW 	HAL_GPIO_WritePin(EN_GPIO_Port, EN_GPIO_PIN, GPIO_PIN_RESET)

/* USER CODE END PM */


/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//查忙
void check_busy()
{
	char tmp = 0x80;
	GPIOA->ODR = 0x80;
	
	while(tmp & 0x80){
		RS_LOW;//低电平选指令寄存器
		RW_HIGH;//高电平时进行读操作
		
		EN_LOW;//变成低电平时,液晶模块执行命令
		HAL_Delay(1);
		EN_HIGH;
		HAL_Delay(1);
		tmp = GPIOA->ODR;
		EN_LOW;
		HAL_Delay(1);
	}
}
//发指令
void Write_Cmd_Func(char cmd){
	
	//check_busy();
	
	RS_LOW;
	RW_LOW;
	EN_LOW;
	GPIOA->ODR = cmd;
	HAL_Delay(1);
	EN_HIGH;
	HAL_Delay(1);
	EN_LOW;
	HAL_Delay(1);
}
//发数据
void Write_Data_Func(char Data){
	
	//check_busy();
	
	RS_HIGH;
	RW_LOW;
	EN_LOW;
	GPIOA->ODR = Data;
	HAL_Delay(1);
	EN_HIGH;
	HAL_Delay(1);
	EN_LOW;
	HAL_Delay(1);
}


//lcd初始化
void LCD1602_INIT()
{
	//(1)延时 15ms
	HAL_Delay(15);
	//(2)写指令 38H(不检测忙信号)
	Write_Cmd_Func(0x38);
	//(3)延时 5ms
	HAL_Delay(5);
	//(4)以后每次写指令,读/写数据操作均需要检测忙信号
	//(5)写指令 38H:显示模式设置
	Write_Cmd_Func(0x38);
	//(6)写指令 08H:显示关闭
	Write_Cmd_Func(0x08);
	//(7)写指令 01H:显示清屏
	Write_Cmd_Func(0x01);
	//(8)写指令 06H:显示光标移动设置
	Write_Cmd_Func(0x06);
	//(9)写指令 0CH:显示开及光标设置}
	Write_Cmd_Func(0x0c);
}

//显示字符串
void LCD1602_showLine(char row, char col, char *string)
{
	
	switch(row){

		case 1:
				Write_Cmd_Func(0x80+col);
				while(*string){
					Write_Data_Func(*string);
					string++;
				}
				break;
		
		case 2:
				Write_Cmd_Func(0x80+0x40+col);
				while(*string){
					Write_Data_Func(*string);
					string++;
				}
				break;
	}
}

/* USER CODE END 0 */

dht.c

#include "main.h"
#include "dht.h"

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

#define DHT_HIGH HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET)
#define DHT_LOW HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET)
#define DHT_VALUE HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7)

uint8_t temp_humi[5];//温湿度信息

void delay_us(uint16_t cnt)
{
    uint8_t i;

    while(cnt)
    {
        for (i = 0; i < 10; i++)
        {

        }
        cnt--;
    }
}

//根据传入参数‘mode’来选择GPIO口模式
//"GPIO_MODE_OUTPUT_PP"和"GPIO_MODE_INPUT"两种
void DHT_GPIO_Init(uint32_t mode)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	__HAL_RCC_GPIOB_CLK_ENABLE();//打开时钟

  /*Configure GPIO pin : PB7 */
  GPIO_InitStruct.Pin = GPIO_PIN_7;
  GPIO_InitStruct.Mode = mode;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

void DHT11_Start()
{
	DHT_GPIO_Init(GPIO_MODE_OUTPUT_PP);
	DHT_HIGH;
	DHT_LOW;
	HAL_Delay(30);
	DHT_HIGH;
	
	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++){
		//卡g点:while(!dht)       有效数据都是高电平,持续时间不一样,50us读,低电平0 高电平
		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;
		}
		temp_humi[i] = tmp;
	}
}

lcd.h 和 dht.h ,记得要用 #ifndef 和 #endif 的形式

#ifndef __LCD_H__
#define __LCD_H__


void LCD1602_INIT(void);
void LCD1602_showLine(char row, char col, char *string);

#endif


#ifndef __DHT_H__
#define __DHT_H__

void Read_Data_From_DHT(void);

#endif

五 预警功能

串口功能测试

打开串口中断,从串口接收命令,来分别控制蜂鸣器和风扇的电机驱动。

同时lcd显示蜂鸣器和风扇状态。

可以看到下图中风扇是开的,对应屏幕上fOn(fan On),蜂鸣器关bOff(buzzer Off)。

代码实现

注意不要在串口中断里直接添加显示LCD文字,会使得程序卡死。我这里用了标志位。 

#define OFF  0
#define ON   1

uint8_t buzzer = OFF;
uint8_t fan = OFF;

//=====串口(中断)=======
//串口接收缓存(1字节)
uint8_t buf=0;
//定义最大接收字节数 200,可根据需求调整
#define UART1_REC_LEN 200
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART1_REC_LEN个字节
uint8_t UART1_RX_Buffer[UART1_REC_LEN];
//  接收状态
//  bit15,      接收完成标志
//  bit14,      接收到0x0d
//  bit13~0,    接收到的有效字节数目
uint16_t UART1_RX_STA=0;


// 串口中断:接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	// 判断中断是由哪个串口触发的
	if(huart->Instance == USART1)
	{
		// 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
		if((UART1_RX_STA & 0x8000) == 0)
		{
			// 如果已经收到了 0x0d (回车),
			if(UART1_RX_STA & 0x4000)
			{
				// 则接着判断是否收到 0x0a (换行)
				if(buf == 0x0a)
				{
					// 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
					UART1_RX_STA |= 0x8000;
					
					//=======中断信息处理=======
					if (!strcmp((const char *)UART1_RX_Buffer, "bOn")) {
						HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
						if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_RESET){
							printf("蜂鸣器已打开\r\n");
							buzzer = ON;
						}
					}else if(!strcmp((const char *)UART1_RX_Buffer, "bOff")) {
						HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
						if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET){
							printf("蜂鸣器已关闭\r\n");
							buzzer = OFF;
						}
							
					}else if(!strcmp((const char *)UART1_RX_Buffer, "fOn")) {
						HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
						HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
						if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_RESET){
							printf("风扇已打开\r\n");
							fan = ON;
						}
					}else if(!strcmp((const char *)UART1_RX_Buffer, "fOff")) {
						HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
						HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
						if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_SET){
							printf("风扇已关闭\r\n");
							fan = OFF;
						}
					}else {
						if(UART1_RX_Buffer[0] != '\0')
							printf("指令发送错误:%s\r\n", UART1_RX_Buffer);
					}
					
					memset(UART1_RX_Buffer, 0, strlen((const char *)UART1_RX_Buffer));
			 
						// 重新开始下一次接收
					UART1_RX_STA = 0;
					//==========================
				}
				else
					// 否则认为接收错误,重新开始
					UART1_RX_STA = 0;
			}
			else	// 如果没有收到了 0x0d (回车)
			{
				//则先判断收到的这个字符是否是 0x0d (回车)
				if(buf == 0x0d)
				{
					// 是的话则将 bit14 位置为1
					UART1_RX_STA |= 0x4000;
				}
				else
				{
					// 否则将接收到的数据保存在缓存数组里
					UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
					UART1_RX_STA++;
					
					// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
					if(UART1_RX_STA > UART1_REC_LEN - 1)
						UART1_RX_STA = 0;
				}
			}
		}
		// 重新开启中断
		HAL_UART_Receive_IT(&huart1, &buf, 1);
	}
}


//==================================main===============================

  /* USER CODE BEGIN 2 */
	
	LCD1602_INIT();//LCD初始化
	LCD1602_showLine(1,5,"Start");
	
	// 开启接收中断
	HAL_UART_Receive_IT(&huart1, &buf, 1);
	printf("Start\r\n");
	
	HAL_Delay(2000);
	printf("OK\r\n");
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */



    /* USER CODE BEGIN 3 */
		
		Read_Data_From_DHT();//读取温湿度信息
		MesCreate();//生成温湿度信息字符串
		
		HAL_ADC_Start(&hadc1); //启动ADC单次转换
		HAL_ADC_PollForConversion(&hadc1, 50); //等待ADC转换完成
		smoke_value = (uint8_t)(HAL_ADC_GetValue(&hadc1) / (4096.00/100.00)); //读取ADC转换数据

		//LCD显示温湿度和烟雾值
		LCD1602_showLine(1,0,(char *)temp_humi_Mes);
		LCD1602_showLine(2,0,(char *)smoke_Mes);
		
		if(buzzer == ON)	LCD1602_showLine(2,7,"bOn ");
		else	LCD1602_showLine(2,7,"bOff");
		
		if(fan == ON)	LCD1602_showLine(2,12,"fOn ");
		else	LCD1602_showLine(2,12,"fOff");
		
		//串口上传数据至PC
		printf("%s %s\r\n", temp_humi_Mes,smoke_Mes);
		
		HAL_Delay(1000);
		
  }
  /* USER CODE END 3 */
}

警报功能实现

串口中断信息处理判断是否开启自动警报模式,关闭状态下可手动控制风扇和蜂鸣器,开启时监测温度大于30度开启排风风扇降温,烟雾值大于50则关闭风道防止火势扩散并且开启蜂鸣器警报。

//状态标志位
uint8_t buzzer = OFF;
uint8_t fan = OFF;
uint8_t Alarm_Mode = OFF;

//自动警报模式
void if_Alarm()
{
	//烟雾警报
	if(smoke_value >= 50)
	{
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
		if(buzzer == OFF){
			printf("蜂鸣器已打开\r\n");
			buzzer = ON;
		}
	}
	else
	{
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
		if(buzzer == ON){
			printf("蜂鸣器已关闭\r\n");
			buzzer = OFF;
		}
	}
	
	//温度警报,有火情不允许开风扇,防止火情蔓延
	if(temp_humi[2] >= 30 && smoke_value < 50)
	{
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
		if(fan == OFF){
			printf("风扇已打开\r\n");
			fan = ON;
		}
	}
	else
	{
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
		if(fan == ON){
			printf("风扇已关闭\r\n");
			fan = OFF;
		}
	}
}


// 串口中断:接收完成回调函数,收到一个数据后,在这里处理
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	// 判断中断是由哪个串口触发的
	if(huart->Instance == USART1)
	{
		// 判断接收是否完成(UART1_RX_STA bit15 位是否为1)
		if((UART1_RX_STA & 0x8000) == 0)
		{
			// 如果已经收到了 0x0d (回车),
			if(UART1_RX_STA & 0x4000)
			{
				// 则接着判断是否收到 0x0a (换行)
				if(buf == 0x0a)
				{
					// 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
					UART1_RX_STA |= 0x8000;
					
					//=======中断信息处理=======
					
					//判断是否开启自动警报模式
					if (!strcmp((const char *)UART1_RX_Buffer, "M1")) {
						Alarm_Mode = ON;
						printf("自动警报开启\r\n");
					}else if (!strcmp((const char *)UART1_RX_Buffer, "M0")) {
						Alarm_Mode = OFF;
						printf("自动警报关闭\r\n");
						
						//自动警报关闭时关闭风扇和蜂鸣器
						HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
						if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET){
							printf("蜂鸣器已关闭\r\n");
							buzzer = OFF;
						}
						HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
						HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
						if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_SET){
							printf("风扇已关闭\r\n");
							fan = OFF;
						}
					}
					
					if(Alarm_Mode == OFF)
					{
						if (!strcmp((const char *)UART1_RX_Buffer, "bOn")) {
							HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
							if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_RESET){
								printf("蜂鸣器已打开\r\n");
								buzzer = ON;
							}
						}else if(!strcmp((const char *)UART1_RX_Buffer, "bOff")) {
							HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
							if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET){
								printf("蜂鸣器已关闭\r\n");
								buzzer = OFF;
							}
						}else if(!strcmp((const char *)UART1_RX_Buffer, "fOn")) {
							HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
							HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
							if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_RESET){
								printf("风扇已打开\r\n");
								fan = ON;
							}
						}else if(!strcmp((const char *)UART1_RX_Buffer, "fOff")) {
							HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
							HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
							if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_SET){
								printf("风扇已关闭\r\n");
								fan = OFF;
							}
						}else {
							if(UART1_RX_Buffer[0] != '\0')
								printf("指令发送错误:%s\r\n", UART1_RX_Buffer);
						}
					}

					memset(UART1_RX_Buffer, 0, strlen((const char *)UART1_RX_Buffer));
			 
						// 重新开始下一次接收
					UART1_RX_STA = 0;
					//==========================
				}
				else
					// 否则认为接收错误,重新开始
					UART1_RX_STA = 0;
			}
			else	// 如果没有收到了 0x0d (回车)
			{
				//则先判断收到的这个字符是否是 0x0d (回车)
				if(buf == 0x0d)
				{
					// 是的话则将 bit14 位置为1
					UART1_RX_STA |= 0x4000;
				}
				else
				{
					// 否则将接收到的数据保存在缓存数组里
					UART1_RX_Buffer[UART1_RX_STA & 0X3FFF] = buf;
					UART1_RX_STA++;
					
					// 如果接收数据大于UART1_REC_LEN(200字节),则重新开始接收
					if(UART1_RX_STA > UART1_REC_LEN - 1)
						UART1_RX_STA = 0;
				}
			}
		}
		// 重新开启中断
		HAL_UART_Receive_IT(&huart1, &buf, 1);
	}
}


/* USER CODE BEGIN 3 */
		
		Read_Data_From_DHT();//读取温湿度信息
		MesCreate();//生成温湿度信息字符串
		
		HAL_ADC_Start(&hadc1); //启动ADC单次转换
		HAL_ADC_PollForConversion(&hadc1, 50); //等待ADC转换完成
		smoke_value = (uint8_t)(HAL_ADC_GetValue(&hadc1) / (4096.00/100.00)); //读取ADC转换数据

		//警报模式下
		if(Alarm_Mode == ON)	if_Alarm();
		
		//LCD显示温湿度和烟雾值
		LCD1602_showLine(1,0,(char *)temp_humi_Mes);
		LCD1602_showLine(2,0,(char *)smoke_Mes);
		
		//LCD显示蜂鸣器、风扇的状态,以及当前警报模式
		if(buzzer == ON)	LCD1602_showLine(2,6,"B:1");
		else	LCD1602_showLine(2,6,"B:0");
		if(fan == ON)	LCD1602_showLine(2,10,"F:1");
		else	LCD1602_showLine(2,10,"F:0");
		if(Alarm_Mode == ON)	LCD1602_showLine(2,14,"M1");
		else	LCD1602_showLine(2,14,"M0");
		
		//串口上传数据至PC
		printf("%s %s\r\n", temp_humi_Mes,smoke_Mes);
		
		HAL_Delay(1000);
		
  }
  /* USER CODE END 3 */

六 快速自制上位机APP

使用 APP inventor 可以快捷制作APP:

MIT App Inventor (gzjkw.net)

 UI设计

 

逻辑设计

连接蓝牙下位机

按钮发送指令

 

蓝牙接收信息 

 

为了方便通信,对串口打印信息做一些修改

//下位机发送以下字符串,上位机检测是否收到以下字符串并且处理显示

printf("NULL;%d.%d;%d.%d;%d;NULL\r\n",temp_humi[2], temp_humi[3],temp_humi[0], temp_humi[1],smoke_value);//NULL预留的可以删掉
//“[温度整数].[温度小数];[湿度整数].[湿度小数];[烟雾值]\r\n”


//printf("自动警报开启\r\n");
printf("Mode_On\r\n");

//printf("自动警报关闭\r\n");
printf("Mode_Off\r\n");
			
//printf("蜂鸣器已打开\r\n");
printf("buzzer_On\r\n");
	
//printf("蜂鸣器已关闭\r\n");
printf("buzzer_Off\r\n");

//printf("风扇已打开\r\n");
printf("fan_On\r\n");

//printf("风扇已关闭\r\n");
printf("fan_Off\r\n");

  • 12
    点赞
  • 121
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值