基于STM32设计的环境检测设备

简介: 设计以STM32微控制器为平台,采用DHT11温湿度传感器、烟雾传感器MQ-2、易燃气体传感器MQ-4、空气质量检测传感器MQ-135对室内温湿度和危险气体进行采集。通过wifi无线网络将数据传送给微控制器,STM32微控制器处理数据后,由自带oled液晶屏显示。当室内温度达到预警值或有危险气体时,系统将会自动警报并将警报信息通过wifi网络传输给客户手机。且每隔一段时间会通过wifi自动发送监测信息到手机,从而实现对室内环境的监测及报警功能。

1. 前言

随着人们生活质量的提高,对于生活环境的问题,人们的关注度进一步提高,同时政府部门采取了许多措施来改善环境状况。但是总体上来说我国的环境监测技术水平比较落后,传统上的监测手段比较单一,监测数据也不够准确,耗尽了大量的人力和财力,却成效不高。

针对上述缺点,当前文章综合了嵌入式处理技术、传感器技术、无线网络通信等技术,设计了一个基于STM32的无线环境监测系统,系统主要实现了对湿度、温度、有毒气体、烟雾浓度、空气质量等参数进行实时监测的功能。为了实现无线数据传输功能,采用了无线wifi技术。系统的测试分析表明系统整体数据采集性能良好,数据传输稳定性可靠,到达了预期目标。

系统与传统的监测技术相比,具有监测数据准确,监测范围广,智能化高等特点。且系统具有一定的创新性,在实际的工程运用和理论研究上体现出了一定的研究价值最后通过实物的调试,各项参数及功能符合设计要求,能达到预期的目的。

设计以STM32微控制器为平台,采用DHT11温湿度传感器、烟雾传感器MQ-2、易燃气体传感器MQ-4、空气质量检测传感器MQ-135对室内温湿度和危险气体进行采集。通过wifi无线网络将数据传送给微控制器,STM32微控制器处理数据后,由自带oled液晶屏显示。当室内温度达到预警值或有危险气体时,系统将会自动警报并将警报信息通过wifi网络传输给客户手机。且每隔一段时间会通过wifi自动发送监测信息到手机,从而实现对室内环境的监测及报警功能。

软件源码完整下载地址: https://download.csdn.net/download/xiaolong1126626497/63979263

视频演示地址: https://live.csdn.net/v/182605

image-20220110153238804

2. 实现功能与整体框架图

开发板采用STM32最小系统板,主控CPU采用STM32F103C8T6,其他传感器采用模块的形式连接到开发板。

主要实现以下功能实现:
1、通过DHT11温湿度传感器、烟雾传感器MQ-2、易燃气体传感器MQ-4、空气质量检测传感器MQ-135对室内温湿度和危险气体进行采集。
2、通过传感器用ADC模拟数字的转换,采集到的数据显示在oled屏幕上。
3、当检测到的数据超过设定的安全值时,屏幕上会显示警报。
4、检测到的数据能定时通过ESP8266 wifi无线传输发送到所连接的用户的手机上,实现监测功能。

系统框架图如下:

image-20220110145054386

3. 硬件特点介绍

(1) 温湿度传感器
温湿度传感器采用DHT11,这是一款直接输出数字信号的温湿度传感器;其精度湿度±5%RH, 温度±2℃,量程湿度5~95%RH, 温度-20~+60℃。通过单总线时序输出,占用的IO口也比较少,工作电压3V~5V,单片机连接控制很方便。

(2) MQ系列的气体检测传感器
烟雾传感器MQ-2、易燃气体传感器MQ-4、空气质量检测传感器MQ-135,这些传感器都是输出模拟信号。
配置好STM32的ADC采集接口,采集数据进行处理即可。

(3) ESP8266 WIFI
联网的模块采用ESP8266 WIFI,ESP8266在物联网里使用非常多,有很多成熟的案例.WIFI本身也支持二次开发,默认集成的SDK支持AT指令控制,单片机可以通过串口方式控制ESP8266完成网络通信,非常方便.

(4) OLED显示屏
OLED显示屏采用中景园电子的0.96寸OLED,分辨率是128x64,使用的SPI引脚接口屏幕,刷屏速度很快,控制简单

(5) 上位机设计

手机APP和PC端没有单独设计精美的界面,只是简单的展示了数据显示。

image-20220110151824586

4. 核心源码

image-20220110151944247

4.1 DHT11温湿度代码

#include "dht11.h"
#include "delay.h"

//复位DHT11
void DHT11_Rst(void)       
{                 
      DHT11_IO_OUT();     //SET OUTPUT
    DHT11_DQ_OUT=0;     //拉低DQ
    DelayMs(20);        //拉低至少18ms
    DHT11_DQ_OUT=1;     //DQ=1 
      delay_us(30);          //主机拉高20~40us
}
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_Check(void)        
{   
    u8 retry=0;
    DHT11_IO_IN();//SET INPUT     
    while (DHT11_DQ_IN&&retry<100)//DHT11会拉低40~80us
    {
        retry++;
        delay_us(1);
    };     
    if(retry>=100)return 1;
    else retry=0;
    while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高40~80us
    {
        retry++;
        delay_us(1);
    };
    if(retry>=100)return 1;        
    return 0;
}
//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void)              
{
     u8 retry=0;
    while(DHT11_DQ_IN&&retry<100)//等待变为低电平
    {
        retry++;
        delay_us(1);
    }
    retry=0;
    while(!DHT11_DQ_IN&&retry<100)//等待变高电平
    {
        retry++;
        delay_us(1);
    }
    delay_us(40);//等待40us
    if(DHT11_DQ_IN)return 1;
    else return 0;           
}

//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)    
{        
    u8 i,dat;
    dat=0;
    for (i=0;i<8;i++) 
    {
           dat<<=1; 
        dat|=DHT11_Read_Bit();
    }                            
    return dat;
}


//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
u8 DHT11_Read_Data(u8 *temp,u8 *humi)    
{        
     u8 buf[5];
    u8 i;
    DHT11_Rst();
    //printf("------------------------\r\n");
    if(DHT11_Check()==0)
    {
        for(i=0;i<5;i++)//读取40位数据
        {
            buf[i]=DHT11_Read_Byte();
        }
        if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
        {
            *humi=buf[0];
            *temp=buf[2];
        }
    }else return 1;
    return 0;        
}


//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在         
u8 DHT11_Init(void)
{
    RCC->APB2ENR|=1<<2;    //使能PORTG口时钟 
    GPIOA->CRL&=0XFF0FFFFF;//PORTG.11 推挽输出
    GPIOA->CRL|=0X00300000;
    GPIOA->ODR|=1<<5;      //输出1                    
    DHT11_Rst();
    return DHT11_Check();
}

4.2 ESP8266代码

#include "esp8266.h"
extern u8  USART3_RX_BUF[USART3_MAX_RECV_LEN];         //接收缓冲,最大USART3_MAX_RECV_LEN字节
extern u8  USART3_TX_BUF[USART3_MAX_SEND_LEN];         //发送缓冲,最大USART3_MAX_SEND_LEN字节
extern vu16 USART3_RX_STA;                                               //接收数据状态

/////////////////////////////////////////////////////////////////////////////////////////////////////////// 
//用户配置区

//连接端口号:8086,可自行修改为其他端口.
const u8 portnum[]="8089";         

//WIFI STA模式,设置要去连接的路由器无线参数,请根据你自己的路由器设置,自行修改.
const u8 wifista_ssid[]="wbyq1";            //路由器SSID号
const u8 wifista_encryption[]="wpa2_aes";    //wpa/wpa2 aes加密方式
const u8 wifista_password[]="123456789";     //连接密码

//WIFI AP模式,模块对外的无线参数,可自行修改.
const u8 wifiap_ssid[]="Cortex_M3";              //对外SSID号
const u8 wifiap_encryption[]="wpawpa2_aes";    //wpa/wpa2 aes加密方式
const u8 wifiap_password[]="12345678";           //连接密码 


/*
函数功能:向ESP82668266发送命令
函数参数:
                cmd:发送的命令字符串
                ack:期待的应答结果,如果为空,则表示不需要等待应答
                waittime:等待时间(单位:10ms)
返 回 值:
                 0,发送成功(得到了期待的应答结果)
         1,发送失败
*/
u8 ESP8266_SendCmd(u8 *cmd,u8 *ack,u16 waittime)
{
    u8 res=0; 
    USART3_RX_STA=0;
    UsartStringSend(USART3,cmd);//发送命令
    if(ack&&waittime)        //需要等待应答
    {
        while(--waittime)    //等待倒计时
        {
            DelayMs(10);
            if(USART3_RX_STA&0X8000)//接收到期待的应答结果
            {
                if(ESP8266_CheckCmd(ack))
                {
                    res=0;
                    //printf("cmd->ack:%s,%s\r\n",cmd,(u8*)ack);
                    break;//得到有效数据 
                }
                    USART3_RX_STA=0;
            } 
        }
        if(waittime==0)res=1; 
    }
    return res;
}


/*
函数功能:ESP8266发送命令后,检测接收到的应答
函数参数:str:期待的应答结果
返 回 值:0,没有得到期待的应答结果
                 其他,期待应答结果的位置(str的位置)
*/
u8* ESP8266_CheckCmd(u8 *str)
{
    char *strx=0;
    if(USART3_RX_STA&0X8000)  //接收到一次数据了
    { 
        USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符
        strx=strstr((const char*)USART3_RX_BUF,(const char*)str); //查找是否应答成功
        printf("RX=%s",USART3_RX_BUF);
    }
    return (u8*)strx;
}

/*
函数功能:向ESP8266发送指定数据
函数参数:
                data:发送的数据(不需要添加回车)
                ack:期待的应答结果,如果为空,则表示不需要等待应答
                waittime:等待时间(单位:10ms)
返 回 值:0,发送成功(得到了期待的应答结果)luojian
*/
u8 ESP8266_SendData(u8 *data,u8 *ack,u16 waittime)
{
    u8 res=0; 
    USART3_RX_STA=0;
    UsartStringSend(USART3,data);//发送数据
    if(ack&&waittime)        //需要等待应答
    {
        while(--waittime)    //等待倒计时
        {
            DelayMs(10);
            if(USART3_RX_STA&0X8000)//接收到期待的应答结果
            {
                if(ESP8266_CheckCmd(ack))break;//得到有效数据 
                USART3_RX_STA=0;
            } 
        }
        if(waittime==0)res=1; 
    }
    return res;
}
    
    

/*
函数功能:ESP8266退出透传模式
返 回 值:0,退出成功;
         1,退出失败
*/
u8 ESP8266_QuitTrans(void)
{
    while((USART3->SR&0X40)==0);    //等待发送空
    USART3->DR='+';      
    DelayMs(15);                    //大于串口组帧时间(10ms)
    while((USART3->SR&0X40)==0);    //等待发送空
    USART3->DR='+';      
    DelayMs(15);                    //大于串口组帧时间(10ms)
    while((USART3->SR&0X40)==0);    //等待发送空
    USART3->DR='+';      
    DelayMs(500);                    //等待500ms
    return ESP8266_SendCmd("AT","OK",20);//退出透传判断.
}


/*
函数功能:获取ESP82668266模块的AP+STA连接状态
返 回 值:0,未连接;1,连接成功
*/
u8 ESP8266_ApStaCheck(void)
{
    if(ESP8266_QuitTrans())return 0;              //退出透传 
    ESP8266_SendCmd("AT+CIPSTATUS",":",50);    //发送AT+CIPSTATUS指令,查询连接状态
    if(ESP8266_CheckCmd("+CIPSTATUS:0")&&
         ESP8266_CheckCmd("+CIPSTATUS:1")&&
         ESP8266_CheckCmd("+CIPSTATUS:2")&&
         ESP8266_CheckCmd("+CIPSTATUS:4"))
        return 0;
    else return 1;
}


/*
函数功能:获取ESP8266模块的连接状态
返 回 值:0,未连接;1,连接成功.
*/
u8 ESP8266_ConstaCheck(void)
{
    u8 *p;
    u8 res;
    if(ESP8266_QuitTrans())return 0;              //退出透传 
    ESP8266_SendCmd("AT+CIPSTATUS",":",50);    //发送AT+CIPSTATUS指令,查询连接状态
    p=ESP8266_CheckCmd("+CIPSTATUS:"); 
    res=*p;                                                                    //得到连接状态    
    return res;
}

/*
函数功能:获取ip地址
函数参数:ipbuf:ip地址输出缓存区
*/
void ESP8266_GetWanip(u8* ipbuf)
{
      u8 *p,*p1;
        if(ESP8266_SendCmd("AT+CIFSR\r\n","OK",50))//获取WAN IP地址失败
        {
            ipbuf[0]=0;
            return;
        }        
        p=ESP8266_CheckCmd("\"");
        p1=(u8*)strstr((const char*)(p+1),"\"");
        *p1=0;
        sprintf((char*)ipbuf,"%s",p+1);    
}

/*
函数功能:将收到的AT指令应答数据返回给电脑串口
参    数:mode:0,不清零USART3_RX_STA;
                             1,清零USART3_RX_STA;
*/
void ESP8266_AtResponse(u8 mode)
{
    if(USART3_RX_STA&0X8000)        //接收到一次数据了
    { 
        USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符
        printf("%s",USART3_RX_BUF);    //发送到串口
        if(mode)USART3_RX_STA=0;
    } 
}


/*
函数功能:ESP8266 AP模式+TCP服务器模式测试
*/
void ESP8266_APorServer(void)
{
    u8 p[100];
    u8 ipbuf[20];
    
    
    while(ESP8266_SendCmd("AT\r\n","OK",20))//检查WIFI模块是否在线
    {
        ESP8266_QuitTrans();//退出透传
        ESP8266_SendCmd("AT+CIPMODE=0\r\n","OK",200);  //关闭透传模式    
        printf("未检测到模块,正在尝试连接模块...\r\n");
        DelayMs(800);
    }
    printf("ESP8266模块检测OK!\r\n");
    
    while(ESP8266_SendCmd("ATE0\r\n","OK",20)); //关闭回显
    
    printf("请用设备连接WIFI热点:%s,%s,%ss\r\n",(u8*)wifiap_ssid,(u8*)wifiap_encryption,(u8*)wifiap_password);
    
    /*1. 设置WIFI AP模式 */
    ESP8266_SendCmd("AT+CWMODE=2\r\n","OK",50);
    
    /*2. 重启模块 */
    ESP8266_SendCmd("AT+RST\r\n","OK",20);
    
    /*3. 延时3S等待重启成功*/
    DelayMs(1000);            
    DelayMs(1000);
    DelayMs(1000);
    
    /*5. 配置模块AP模式无线参数*/
    sprintf((char*)p,"AT+CWSAP=\"%s\",\"%s\",1,4\r\n",wifiap_ssid,wifiap_password); 
    ESP8266_SendCmd(p,"OK",1000);
    
    /*4. 设置多连接模式:0单连接,1多连接(服务器模式必须开启)*/
    ESP8266_SendCmd("AT+CIPMUX=1\r\n","OK",20); 
    
    /*5. 开启Server模式(0,关闭;1,打开),端口号为portnum */
    sprintf((char*)p,"AT+CIPSERVER=1,%s\r\n",(u8*)portnum);
    ESP8266_SendCmd(p,"OK",50);
    
    /*6. 获取当前模块的IP*/
    ESP8266_GetWanip(ipbuf);//
    printf("IP地址:%s 端口:%s",ipbuf,(u8*)portnum);
  
    USART3_RX_STA=0; //清空串口的接收标志位
//    while(1)
//    {
//        key=GetKeyVal(1);//退出测试
//        if(key==1)
//        { 
//        printf("退出测试!\r\n");            
//            ESP8266_QuitTrans();    //退出透传
//            ESP8266_SendCmd("AT+CIPMODE=0","OK",20);   //关闭透传模式
//            break;                                                 
//        }
//        else if(key==2)    //发送数据 
//        {
//                ESP8266_SendCmd("AT+CIPSEND=0,12\r\n","OK",200); //设置发送数据长度为12个
//                ESP8266_SendData("ESP8266测试!","OK",100);       //发送指定长度的数据
//                DelayMs(200);
//        }
//        t++;
//        DelayMs(10);
//        if(USART3_RX_STA&0X8000)          //接收到一次数据了
//        { 
//            rlen=USART3_RX_STA&0X7FFF;    //得到本次接收到的数据长度
//            USART3_RX_BUF[rlen]=0;          //添加结束符 
//            printf("接收的数据: rlen=%d,%s",rlen,USART3_RX_BUF);    //发送到串口   
//            USART3_RX_STA=0;
//            if(constate!=3)t=1000;          //状态为还未连接,立即更新连接状态
//            else t=0;                   //状态为已经连接了,10秒后再检查
//        }
//        if(t==1000)//连续10秒钟没有收到任何数据,检查连接是不是还存在.
//        {
////            constate=ESP8266_ConstaCheck();//得到连接状态
////            if(!constate)printf("连接失败!\r\n");
//            t=0;
//        }
//        if((t%20)==0)LED2=!LED2;
//        ESP8266_AtResponse(1);
//    }
}    
相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
3月前
|
传感器 编解码 数据处理
毕业设计|基于STM32单片机的水位浑浊度检测设计
毕业设计|基于STM32单片机的水位浑浊度检测设计
247 0
|
传感器 数据采集 监控
上千个完整设计的单片机、8086、STM32制作教程和资料-转发分享
在网上收集了接近上千个完整设计的单片机、8086、STM32制作教程和资料-转发分享(涵盖了大部分的毕设课设题目),学习单片机的最好教程,也可以作为帮助大家在做电子课设毕设时有利的帮助,可以从以下百度网盘下载(按照编号下载)。
1828 0
上千个完整设计的单片机、8086、STM32制作教程和资料-转发分享
|
6月前
STM32速成笔记(三)—按键检测
本文介绍了如何利用STM32进行按键检测,先介绍了原理,后面给出了配置步骤和应用例程。此外,本文还叙述了如何利用一个按键单独控制一个LED亮灭,以及如何检测按键长短按。
194 0
STM32速成笔记(三)—按键检测
|
3月前
|
传感器 存储 编解码
毕业设计|基于STM32的酒精酒驾检测设计
毕业设计|基于STM32的酒精酒驾检测设计
105 0
|
3月前
|
数据安全/隐私保护 芯片
【STM32基础 CubeMX】按键的检测
【STM32基础 CubeMX】按键的检测
|
存储 JSON 移动开发
基于STM32+ESP8266的奥运会奖牌榜设计之经典
基于STM32+ESP8266的奥运会奖牌榜设计之经典
193 0
基于STM32+ESP8266的奥运会奖牌榜设计之经典
|
12月前
|
传感器 机器人
STM32 E18-D80NK红外检测
E18-D8ONK 这是一种 集发射与接收于一体 的光电传感器,发射光经过调制后发出,接收头对反射光进行解调输出。有效的避免了可见光的干扰。透镜的使用,也使得这款传感器最远可以检测80厘米距离的问题(由于红外光的特性,不同颜色的物体,能探测的最大距离也有不同;白色物体最远,黑色物体最近)。
288 0
|
传感器 C语言 芯片
【STM32】I2C协议完成温湿度检测
I2C总线是英国的菲利普公司在八十年代初期退出来的一种串行的、半双工的总线,主要是用于一些近距离、低速的芯片之间的通信;I2C总线有两根双向的信号线,一根SDA用于收发数据、一根时钟线SCL用于通信双方时钟的同步。
【STM32】I2C协议完成温湿度检测
|
算法 芯片
基于STM32设计的计算器(实现基本运算)
当前文章介绍的是STM32+LCD触摸屏设计的一个触摸计算器功能,实现基本的加减乘除,二进制转换显示等功能。LCD屏使用的是3.5寸带触摸屏的显示屏,方便操作屏幕,MCU采用STM32F103ZET6。设计的这个计算器用到的硬件不多,主要是LCD屏和触摸屏,用到了一个W25Q64存储芯片。
378 0
|
传感器 算法
基于STM32设计的健康检测设备(测温心率计步)
本文介绍的项目是基于STM32设计的健康检测设备,支持体温测量,心率检测,支持运动计步(采用MPU6050陀螺仪实现),支持WIFI传输数据到手机APP打印显示。
320 0