当前位置:网站首页>烟雾、空气质量、温湿度…自己徒手做个环境检测设备
烟雾、空气质量、温湿度…自己徒手做个环境检测设备
2022-08-10 17:19:00 【华为云开发者联盟】
摘要:综合了嵌入式处理技术、传感器技术、无线网络通信等技术,设计了一个基于STM32的无线环境监测系统,系统主要实现了对湿度、温度、有毒气体、烟雾浓度、空气质量等参数进行实时监测的功能。
本文分享自华为云社区《基于STM32设计的环境检测设备》,作者:DS小龙哥 。
1. 前言
随着人们生活质量的提高,对于生活环境的问题,人们的关注度进一步提高,同时政府部门采取了许多措施来改善环境状况。但是总体上来说我国的环境监测技术水平比较落后,传统上的监测手段比较单一,监测数据也不够准确,耗尽了大量的人力和财力,却成效不高。
针对上述缺点,当前文章综合了嵌入式处理技术、传感器技术、无线网络通信等技术,设计了一个基于STM32的无线环境监测系统,系统主要实现了对湿度、温度、有毒气体、烟雾浓度、空气质量等参数进行实时监测的功能。为了实现无线数据传输功能,采用了无线wifi技术。系统的测试分析表明系统整体数据采集性能良好,数据传输稳定性可靠,到达了预期目标。
系统与传统的监测技术相比,具有监测数据准确,监测范围广,智能化高等特点。且系统具有一定的创新性,在实际的工程运用和理论研究上体现出了一定的研究价值最后通过实物的调试,各项参数及功能符合设计要求,能达到预期的目的。
设计以STM32微控制器为平台,采用DHT11温湿度传感器、烟雾传感器MQ-2、易燃气体传感器MQ-4、空气质量检测传感器MQ-135对室内温湿度和危险气体进行采集。通过wifi无线网络将数据传送给微控制器,STM32微控制器处理数据后,由自带oled液晶屏显示。当室内温度达到预警值或有危险气体时,系统将会自动警报并将警报信息通过wifi网络传输给客户手机。且每隔一段时间会通过wifi自动发送监测信息到手机,从而实现对室内环境的监测及报警功能。
基于STM32设计的环境检测设备视频演示地址

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

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端没有单独设计精美的界面,只是简单的展示了数据显示。

4. 核心源码

4.1 DHT11温湿度代码
#include "dht11.h"#include "delay.h"//复位DHT11void 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/0u8 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);//等待40usif(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);//等待500msreturn 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);//}}
边栏推荐
猜你喜欢

【硬件架构的艺术】学习笔记(4)流水线的艺术

win11安装deepin20.6双系统(双硬盘)

电力系统潮流计算(牛顿-拉夫逊法、高斯-赛德尔法、快速解耦法)【6节点 9节点 14节点 26节点 30节点 57节点】(Matlab代码实现)

MogDB学习笔记-从2开始(MogHA)

v-for指令:根据数据生成列表结构

网易云信亮相LiveVideoStackCon2022,解构基于WebRTC的开源低延时播放器实践

Word里表格跨页时自动断开,表格后留有空白部分,未布满整页,如何操作让表格上下页均匀布满?

Pytorch GPU模型推理时间探讨2——显卡warm up

重庆新壹汽与一汽集团达成新能源项目战略合作,赋能“碳中和”创造“碳财富”

v-show指令:切换元素的显示与隐藏
随机推荐
pytorch 模型GPU推理时间探讨3——正确计算模型推理时间
promise笔记(二)
Word里表格跨页时自动断开,表格后留有空白部分,未布满整页,如何操作让表格上下页均匀布满?
R语言使用ggpubr包的ggsummarystats函数可视化箱图(通过ggfunc参数设置)、在可视化图像的下方添加描述性统计结果表格、设置add参数为jitter添加抖动数据点
fastjson chain analysis (1.2.22-47)
R语言ggplot2可视化:使用ggpubr包的text_grob函数和as_ggplot函数可视化文本段落(将指定文本段落可视化出来、指定文本段可视化为图像)、face参数指定文本的字体样式
【科研】常见火灾数据集
Error creating bean with name ‘sqlSessionFactory‘ defined in class path reso「建议收藏」
C:枚举的优缺
redis分布式锁
基于AWS构建云上数仓第二步:AWS常见服务简介
shopee API 接入说明
程序员兼职踩到的坑,本人亲身经历
The sword refers to OfferⅡ 045. The bottommost leftmost value of the binary tree dfs
2022 CCF China Open Source Conference Notice (Fourth Round)
百度、四维图新、高德争“鲜”恐后
百日刷题挑战--错题01day
TradingView_学习笔记
自适应模糊神经网络与bp神经网络的区别
浅析端口扫描原理