目录
项目概述:
材料:
注:
一 LCD1602
介绍
STM32驱动LCD1602
接线
代码
二 DHT11温湿度检测
介绍于时序分析
温湿度数据串口传输给上位机
难点
代码
三 ADC读取烟雾传感器
四 环境监测系统
参考接线
代码实现
五 预警功能
串口功能测试
代码实现
警报功能实现
六 快速自制上位机APP
UI设计
逻辑设计
连接蓝牙下位机
按钮发送指令
蓝牙接收信息
基于STM32的环境监测预警系统
主控STM32f103c8t6 。利用 DHT11 和烟雾传感器,进行温湿度检测和烟雾值检测,并实时在LCD屏幕上进行数值显示,同时通过串口通信将数据信息传至上位机显示。当检测到温湿度高出设定阈值,将打开排风扇进行通风;当检测到有烟雾时,将关闭通风风扇防止火灾蔓延,开启蜂鸣器警报并持续向上位机发送警报信息。
STM32f103c8t6 | DHT11 | 烟雾传感器 |
LCD1602 | HC蓝牙模块(用于串口通信) | 蜂鸣器 |
直流电机驱动模块/继电器 | 直流电机和风扇桨叶 | 杜邦线面包板电源等 |
资料包已上传,包含源代码和自制上位机APP。
详情参考前文。
(59条消息) C51外设:LCD1602_我有在好好学习的博客-CSDN博客
仅供参考,电源线不要接错,其它线可以按自己意愿接
示例:显示两排文字
把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 */
参考前文
(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 */
完完整整的在前文实现过,不再赘述
(59条消息) STM32:ADC_我有在好好学习的博客-CSDN博客
检测温度、湿度、烟雾值,显示在LCD上,同时串口上传上位机(这里演示用串口蓝牙透传)。
将所有GPIO拉高,蜂鸣器暂时没用。
如果也使用面包板+stm32最小系统,要注意面包板接线准则,
不要乱接跳线,比如两个点位能用长跳线一部到位,就别用两条短线续连。
乱接跳线很容易导致单片机收到电磁干扰而无法正常工作。
main.c
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include
#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 inventor 可以快捷制作APP:
MIT App Inventor (gzjkw.net)
为了方便通信,对串口打印信息做一些修改
//下位机发送以下字符串,上位机检测是否收到以下字符串并且处理显示
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");