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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 设计以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);
//    }
}    
相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
6月前
|
传感器 监控
基于STM32的智能农业环境监测系统设计与实现
基于STM32的智能农业环境监测系统设计与实现
478 0
|
6月前
|
传感器 编解码 数据处理
毕业设计|基于STM32单片机的水位浑浊度检测设计
毕业设计|基于STM32单片机的水位浑浊度检测设计
833 0
|
6月前
|
传感器 物联网 芯片
毕业设计 基于STM32单片机无线ZIGBEE智能大棚土壤湿度光照检测
毕业设计 基于STM32单片机无线ZIGBEE智能大棚土壤湿度光照检测
131 0
|
1月前
|
传感器 网络协议 物联网
基于STM32的环境监测系统 (esp8267)(下)
基于STM32的环境监测系统 (esp8267)(下)
91 0
|
1月前
|
传感器 测试技术 芯片
基于STM32的环境监测系统 (esp8266)(上)
基于STM32的环境监测系统 (esp8266)(上)
282 0
|
1月前
|
传感器 数据采集 监控
基于STM32的温室大棚环境检测及自动浇灌系统设计
基于STM32的温室大棚环境检测及自动浇灌系统设计
270 0
STM32速成笔记(三)—按键检测
本文介绍了如何利用STM32进行按键检测,先介绍了原理,后面给出了配置步骤和应用例程。此外,本文还叙述了如何利用一个按键单独控制一个LED亮灭,以及如何检测按键长短按。
666 0
STM32速成笔记(三)—按键检测
|
6月前
|
传感器 监控 数据挖掘
基于STM32的智能城市环境监测系统设计与实现
基于STM32的智能城市环境监测系统设计与实现
265 0
|
6月前
|
传感器 存储 编解码
毕业设计|基于STM32的酒精酒驾检测设计
毕业设计|基于STM32的酒精酒驾检测设计
294 0
|
6月前
|
数据安全/隐私保护 芯片
【STM32基础 CubeMX】按键的检测
【STM32基础 CubeMX】按键的检测
169 0