资讯动态

基于STM32与ESP8266的环境监测系统:从传感器采集到云平台上报

发布时间:2026/10/14 19:48:29 来源:尧图企业网站定制
1. 项目缘起与整体设计思路1.1 这个项目到底在做什么先把这个项目的核心说清楚用一块STM32微控制器接上烟雾传感器、粉尘传感器和噪音传感器把采集到的环境数据通过WiFi模块上传到云平台实现远程监控和异常告警。说白了就是自己动手搭一套“环境哨兵”能盯着某个空间里的空气质量、颗粒物浓度和噪声水平数据实时推到云端手机或电脑打开网页就能看。我第一次接触这类需求是帮一个做小型仓储的朋友看的。他的库房里堆了不少纸箱和塑料制品夏天温度一高总担心线路老化冒烟又不可能24小时派人守着。市面上成套的环境监测设备动辄几千块功能还未必贴合需求。后来一合计用STM32加几个传感器自己搭成本能压到两百块以内数据还能按自己的逻辑上报。这个思路后来被我用在好几个场景里比如地下车库的空气质量监测、小型车间的噪音管控、甚至家里装修后的甲醛和粉尘观察甲醛需要换传感器但框架一样。这个项目适合谁我觉得有三类人值得动手试试一是电子类专业的学生想找一个能把单片机、传感器、无线通信、云平台串起来的综合练手项目二是物联网方向的初级开发者需要一套完整的端到端demo来理解数据从采集到上云的链路三是有点动手能力的爱好者想给自己关心的空间装一套低成本监测方案。不需要你精通RTOS或者复杂的网络协议但得会基本的C语言、能看懂电路图、愿意花时间调传感器。1.2 为什么选STM32而不是别的市面上做物联网终端的方案很多ESP32、Arduino、树莓派Pico都能干这活。我选STM32主要基于几个实际考量。第一是外设资源。STM32F103系列有多个ADC通道、多个UART、定时器资源丰富接三四个传感器绰绰有余。烟雾传感器和粉尘传感器通常输出模拟电压噪音传感器有的输出模拟量有的走数字接口STM32的ADC精度12位和采样速率完全够用。相比之下Arduino Uno的ADC只有10位分辨率差一些而且UART只有一个调试和WiFi通信会打架。第二是实时性。环境监测虽然不需要微秒级响应但噪音传感器需要按固定频率采样才能算准分贝值STM32的定时器中断能保证采样间隔稳定。我用TIM2做1ms中断在中断里触发ADC采样主循环负责数据处理和上报分工明确。第三是成本与生态。STM32F103C8T6最小系统板十块钱左右资料铺天盖地Keil和CubeMX工具链成熟。ESP32虽然自带WiFi但它的ADC线性度一直被诟病做模拟传感器采集时误差偏大。用STM32做采集、ESP8266做透传分工更清晰调试也方便——哪部分出问题就单独查哪部分。当然这个方案也有代价ESP8266需要额外占用一个UARTSTM32的UART1用于调试打印UART2接ESP8266UART3可以留给噪音传感器的数字接口如果选的是串口输出型号。引脚分配得提前规划好不然后面改起来麻烦。1.3 系统架构拆解整个系统分四层感知层、控制层、传输层、应用层。感知层就是三个传感器。烟雾传感器我选的是MQ-2它对液化气、丙烷、氢气、烟雾都有响应模拟输出与气体浓度正相关。粉尘传感器用GP2Y1010AU0F夏普那款经典的光学粉尘传感器输出模拟电压能测PM2.5大致趋势。噪音传感器用LM386放大驻极体麦克风的方案输出模拟电压通过ADC采样后计算有效值再转成分贝。控制层是STM32F103C8T6。它负责三件事定时采样三个ADC通道、对原始数据做滤波和换算、通过UART2把数据打包发给ESP8266。我在这里没有上RTOS因为任务不复杂用前后台架构就够了——定时器中断负责采样主循环负责处理和通信。传输层是ESP8266-01S模块跑AT固件。STM32通过AT指令控制它连接WiFi、建立TCP连接、发送HTTP POST请求。为什么不用MQTT因为云平台那边我选的是支持HTTP接入的调试阶段用HTTP更直观抓包就能看到数据。后面如果要做双向控制再换MQTT也不迟。应用层我用了一个支持HTTP接口的物联网云平台STM32发上来的JSON数据直接存到云端数据库平台自带可视化面板能看实时曲线和历史数据。告警规则也在云端配比如烟雾值超过阈值就发邮件或推送。注意云平台的选择上建议优先考虑支持HTTP/HTTPS接入、有免费额度的平台。调试阶段用HTTP正式部署建议上HTTPS虽然STM32端做TLS比较吃力但可以在ESP8266端做或者用平台提供的加密方案。1.4 数据流与上报策略数据从传感器到云端中间要经过几次转换。以烟雾传感器为例MQ-2输出0到5V的模拟电压STM32的ADC参考电压是3.3V所以需要分压电路把0到5V映射到0到3.3V。ADC读到的12位值0到4095先转成电压再根据MQ-2的灵敏度曲线换算成浓度值ppm。这个换算不是线性的我后面会详细说。上报策略我设的是每5秒上报一次。这个间隔是权衡的结果太快了云平台免费额度扛不住太慢了实时性差。5秒对于环境监测来说够用烟雾和粉尘的变化不会在几秒内剧烈波动。噪音的话我是在STM32端算1秒内的等效声级然后5秒上报一次平均值。数据格式用JSON结构如下{ device_id: env_node_01, smoke: 125, dust: 38, noise: 52, timestamp: 123456 }字段少解析快ESP8266的AT指令发送时不会因为数据太长而超时。timestamp我用的是STM32的RTC或者简单的运行计时云端收到后会用自己的时间戳覆盖所以终端时间不准也没关系。2. 核心细节解析与实操要点2.1 传感器选型与关键参数三个传感器里烟雾和粉尘是模拟输出噪音我选的是模拟输出方案。下面逐个说选型理由和关键参数。烟雾传感器MQ-2这款是半导体式气体传感器加热丝需要5V供电预热时间至少24小时才能稳定。我第一次用的时候没预热够读数一直漂后来查手册才知道要老化。它的输出是模拟电压负载电阻RL影响灵敏度典型电路里RL取10KΩ。灵敏度曲线是非线性的在1000ppm烟雾浓度下Rs/R0大约在0.4左右Rs是传感器电阻R0是洁净空气中的电阻。实际用的时候我建议先测洁净空气的ADC值作为基准然后按比例换算不要死套公式。粉尘传感器GP2Y1010AU0F夏普这款是光学式内部有红外LED和光电晶体管粉尘颗粒反射光线到接收端输出电压与粉尘浓度正相关。它的特点是需要脉冲驱动LED典型驱动周期是10ms其中LED亮0.32ms灭9.68ms。STM32端我用TIM3输出PWM控制LED驱动在LED亮的时候触发ADC采样。输出灵敏度大约是0.5V每100微克每立方米洁净空气下输出电压在0.9V左右。注意它的输出有噪声需要做滑动平均滤波。噪音传感器我用的是驻极体麦克风加LM386放大电路输出模拟电压。麦克风灵敏度约-44dBLM386增益设到200倍输出信号偏置在1.65VVCC的一半这样ADC能采到完整的交流波形。STM32端以8kHz采样率采1秒算有效值再转成分贝。这里有个坑LM386的输出偏置会随温度漂移需要定期校准或者用软件做直流分量剔除。传感器型号输出类型供电关键参数注意事项烟雾MQ-2模拟5V加热丝电流约150mA需预热24小时以上粉尘GP2Y1010AU0F模拟5V灵敏度0.5V/100μg/m³LED需脉冲驱动噪音LM386麦克风模拟3.3V增益200倍偏置需校准2.2 STM32端ADC采样与滤波STM32F103的ADC是12位逐次逼近型最大采样率1MHzADCCLK14MHz时。我用了三个通道PA0接烟雾PA1接粉尘PA2接噪音。ADC配置成扫描模式DMA搬运这样不占CPU。采样策略上烟雾和粉尘每100ms采一次噪音需要高速采样所以单独处理。噪音采样我用TIM2触发ADC8kHz频率DMA搬运到缓冲区采满8000个点后置标志位主循环算有效值。滤波方面烟雾和粉尘用滑动平均滤波窗口大小16。这个窗口是试出来的太小了波动大太大了响应慢。16个点对应1.6秒的平滑既能滤掉尖峰又不至于滞后太多。噪音的有效值计算用均方根公式// 噪音有效值计算 float sum 0; for (int i 0; i 8000; i) { float v (adc_buf[i] * 3.3f / 4095.0f) - 1.65f; // 去掉偏置 sum v * v; } float rms sqrtf(sum / 8000.0f); float db 20 * log10f(rms / 0.006f); // 0.006V对应0dB参考这里的0.006V是麦克风的参考灵敏度换算来的实际值需要根据麦克风手册和放大倍数调整。我实测下来安静室内db值在35到45之间正常说话在55到65用手机分贝计App对比过误差在3dB以内够用了。提示ADC参考电压建议用外部3.3V基准不要用VDDA直接当参考否则电源波动会直接影响读数。我在VREF引脚上加了100nF和10μF电容读数稳定性明显提升。2.3 ESP8266的AT指令通信ESP8266-01S默认跑AT固件STM32通过UART2发AT指令控制它。通信流程分几步复位模块、设置WiFi模式、连接热点、建立TCP连接、发送HTTP请求。AT指令的坑不少。首先是波特率ESP8266默认115200但有些模块出厂是9600需要先用ATUART_DEF统一。其次是回显调试阶段建议开回显ATE1正式用的时候关掉ATE0不然STM32要处理多余的回显数据。第三是超时每条AT指令都要等响应我设的超时是2秒超时后重试三次。发送HTTP POST请求的AT指令序列如下ATCIPSTARTTCP,api.example.com,80 ATCIPSEND85 POST /v1/device/data HTTP/1.1 Host: api.example.com Content-Type: application/json Content-Length: 62 {device_id:env_node_01,smoke:125,dust:38,noise:52}注意Content-Length要算准包括JSON字符串的长度。我一开始没算对服务器返回400错误查了半天才发现是长度少了2个字节换行符的问题。后来我在代码里用strlen动态计算就没再出过问题。ESP8266的供电也是个大坑。它瞬时电流能到300mA如果和STM32共用LDO电压会被拉低导致复位。我的做法是给ESP8266单独用一个AMS1117-3.3输入输出都加470μF电容彻底解决复位问题。2.4 云平台数据接入与告警配置云平台这边我用的是支持HTTP接入的通用物联网平台。设备创建后拿到设备ID和API KeySTM32上报数据时在Header里带上Key做鉴权。数据上报成功后平台会自动生成实时曲线。我在平台上配了三条告警规则烟雾值超过300、粉尘值超过150、噪音值超过75触发后发邮件通知。阈值是根据实际场景调的烟雾300对应有明显烟雾但还没明火的状态粉尘150对应PM2.5中度污染噪音75对应嘈杂街道水平。告警的防抖很重要。如果烟雾值在阈值附近波动会频繁触发告警。我在平台端设了“持续3个上报周期超过阈值才告警”在STM32端也做了简单处理只有连续5次采样都超标才标记为异常状态。注意云平台的免费额度通常限制每天的上报次数和数据存储时长。5秒上报一次一天是17280条有些平台免费额度只有10000条。我后来改成正常状态30秒上报一次异常状态5秒上报一次既省额度又不丢关键数据。3. 实操过程与核心环节实现3.1 硬件搭建与接线硬件清单如下STM32F103C8T6最小系统板一块、ESP8266-01S模块一个、MQ-2烟雾传感器一个、GP2Y1010AU0F粉尘传感器一个、LM386噪音模块一个、AMS1117-3.3稳压模块两个、电阻电容若干、洞洞板一块。接线方面STM32的PA0接MQ-2的模拟输出经过分压PA1接粉尘传感器的VoPA2接LM386的输出。ESP8266的TX接STM32的PA3UART2_RXRX接PA2UART2_TX——注意这里PA2被噪音占用了所以实际我用的是UART3接ESP8266PA2和PA3留给噪音和调试。重新分配后UART1PA9/PA10用于调试打印UART2PA2/PA3接ESP8266UART3PB10/PB11备用。粉尘传感器的LED驱动接STM32的PB0TIM3_CH3输出PWM。MQ-2的加热丝直接接5V注意电流有150mAUSB供电够用但不要用电池。分压电路MQ-2输出0到5VSTM32的ADC最大3.3V用两个电阻分压。我选R110KR220K分压比是20/(1020)0.6675V分压后是3.33V刚好在ADC量程内。实际焊的时候用1%精度的金属膜电阻误差小。3.2 STM32端代码框架代码用Keil MDK开发基于标准外设库。主框架如下// 全局变量 float smoke_val, dust_val, noise_db; uint8_t alert_flag 0; int main(void) { SystemInit(); ADC_Config(); // ADCDMA配置 TIM2_Config(); // 1ms定时器用于噪音采样触发 TIM3_Config(); // PWM输出驱动粉尘传感器LED UART1_Config(); // 调试串口 UART2_Config(); // ESP8266通信 ESP8266_Init(); // 初始化WiFi连接 while (1) { if (sample_flag) { // 100ms采样标志 sample_flag 0; smoke_val Filter_ADC(ADC_Buf[0]); dust_val Filter_ADC(ADC_Buf[1]); // 噪音在DMA完成中断里处理 } if (report_flag) { // 5秒上报标志 report_flag 0; Report_Data(); } Check_Alert(); } }ADC配置成扫描模式规则组三个通道DMA循环搬运。TIM2设成1ms中断在中断里软件分频每100次触发一次烟雾和粉尘的ADC转换每8次触发一次噪音的ADC转换8kHz。噪音的DMA缓冲区大小8000采满后中断置标志主循环算有效值。ESP8266初始化流程先发AT测试再发ATCWMODE1设成Station模式然后ATCWJAP连接热点最后ATCIPSTART建立TCP连接。每一步都要等“OK”响应超时重试。3.3 数据换算与校准MQ-2的浓度换算我用的是近似公式。先测洁净空气中的ADC值作为R0基准然后根据当前ADC值算Rs再用曲线拟合float smoke_ppm(float adc_val) { float v adc_val * 3.3f / 4095.0f / 0.667f; // 还原分压前电压 float rs (5.0f - v) / v * 10.0f; // RL10K float ratio rs / r0; // 近似曲线ppm 1000 * pow(ratio, -2.5) return 1000.0f * powf(ratio, -2.5f); }这个公式是经验拟合不同批次的传感器会有偏差建议用标准气体校准或者至少用打火机气体做一次粗略标定。我实测下来在1000ppm左右误差在20%以内对于趋势监测够用了。粉尘传感器的换算更直接输出电压减去洁净空气基准电压约0.9V再除以灵敏度0.5V/100μg/m³float dust_ugm3(float adc_val) { float v adc_val * 3.3f / 4095.0f; float dust (v - 0.9f) / 0.5f * 100.0f; return dust 0 ? 0 : dust; }噪音的分贝换算前面说过了关键是参考电压的选取。我用手机分贝计App在安静室内对比把参考值调到让STM32读数和App读数一致。这个校准只需要做一次后面就固定了。3.4 云端数据展示与告警联动云平台这边设备接入后创建一个数据模板定义三个属性smoke、dust、noise类型都是数值。STM32上报的JSON数据会自动映射到这三个属性上。可视化面板我配了三个实时曲线图和一个仪表盘。曲线图看趋势仪表盘看当前值。告警规则在平台的规则引擎里配当smoke 300持续3个周期触发邮件告警当noise 75持续5个周期触发推送。调试阶段我建议先用平台的“设备模拟”功能手动发几条数据看面板是否正常显示确认无误后再接真实设备。这样能把云端配置和终端调试分开出问题好定位。提示如果云平台支持Webhook可以把告警推送到自己的服务器或聊天工具。我用Webhook把告警转发到一个内部通知渠道响应比邮件快很多。4. 常见问题与排查技巧实录4.1 传感器读数异常排查问题一MQ-2读数一直偏高或偏低。最常见的原因是预热不足。MQ-2的加热丝需要连续通电24小时以上内部化学材料才能稳定。我第一次用的时候只预热了半小时读数漂了整整一天。另一个原因是负载电阻RL选得不对RL太大灵敏度低RL太小功耗高。10K是折中值如果测低浓度可以换成4.7K。问题二粉尘传感器读数跳动大。GP2Y1010AU0F的输出本身有噪声加上LED驱动和ADC采样的时序不同步跳动很正常。解决办法一是确保LED亮的时候才采样二是加滑动平均滤波三是给传感器单独供电不要和电机、继电器共用电源。我在传感器VCC和GND之间加了100μF电容跳动明显减小。问题三噪音值始终偏高。检查LM386的偏置电压是否在1.65V附近。如果偏置偏了有效值计算会把直流分量也算进去导致读数虚高。用万用表测LM386输出引脚调整偏置电阻直到电压为VCC的一半。另外麦克风的灵敏度个体差异大需要逐个校准。问题现象可能原因排查方法解决方案烟雾读数漂移预热不足连续通电24小时观察充分预热后再校准粉尘跳动大电源噪声示波器看VCC纹波加滤波电容单独供电噪音偏高偏置偏移万用表测输出直流调整偏置电阻ADC值不变通道配置错检查ADC_Init结构体确认通道和引脚对应4.2 WiFi通信失败排查ESP8266连不上WiFi先查供电。用万用表测模块VCC连接瞬间如果掉到3.0V以下就是供电不足。换大电容或者单独LDO。然后查AT指令用USB转TTL直接接ESP8266发AT看有没有回OK。如果没有检查波特率和接线TX接RXRX接TX。TCP连接失败的话先ping一下云平台域名确认网络通。然后检查ATCIPSTART的参数域名和端口要对。如果返回“ERROR CLOSED”可能是云平台限制了IP或者端口不对。HTTP请求返回400检查Content-Length和JSON格式。返回401检查API Key。我遇到过一次诡异的问题ESP8266能连WiFi但发不出数据查了半天发现是ATCIPSEND的长度参数包含了不可见字符。后来我在代码里用sprintf生成字符串后用strlen算长度问题解决。4.3 数据上报不稳定排查数据偶尔丢失先看STM32的串口发送是否完整。我用逻辑分析仪抓过UART2的波形发现有时候ESP8266返回的“OK”还没收完STM32就发下一条指令了。解决办法是每条AT指令后加足够的延时或者用状态机等响应。云平台端丢数据可能是上报频率超过了免费额度限制。登录平台看用量统计如果接近上限降低上报频率或者升级套餐。另外HTTP请求没有重试机制的话网络抖动就会丢数据。我在STM32端加了重试发送失败后等2秒重发最多重试3次。还有一个隐蔽的问题JSON字符串里的浮点数精度。STM32的sprintf默认浮点格式化可能占用大量栈空间导致HardFault。我改成先把浮点数转成整数乘以10或100上报整数云端再除以倍数还原。这样既省资源又避免精度问题。4.4 长期运行稳定性经验这套系统我连续跑过三个月总结了几条经验。第一看门狗必须开。STM32的独立看门狗IWDG设成2秒超时主循环里定期喂狗。ESP8266偶尔会死机我在STM32端加了检测如果连续3次AT指令超时就发ATRST复位ESP8266。第二数据存储要加缓冲。网络断开时数据不能丢。我在STM32的Flash里划了一块区域做环形缓冲网络恢复后补传。缓冲大小设成100条够撑几分钟的网络中断。第三定期重启。我设了每24小时自动重启一次STM32和ESP8266通过RTC闹钟触发。这个习惯是从其他项目带过来的能解决大部分莫名其妙的问题。第四日志要留。UART1接了一个CH340模块到电脑用串口助手记录日志。出问题的时候翻日志比盲猜快得多。日志里记录每次采样值、AT指令交互、上报结果信息量足够定位问题。注意长期运行的项目电源质量是第一位。我用示波器看过劣质USB电源的纹波能到200mV直接导致ADC读数波动。换成线性电源或者好的开关电源后数据稳定性提升一个档次。5. 功能扩展与场景延展5.1 从监测到控制的闭环现在这套系统只做了监测和告警但STM32的引脚还有很多空闲。加一个继电器模块就能实现联动控制烟雾超标自动启动排风扇粉尘超标启动空气净化器噪音超标触发提示灯。控制逻辑可以放在STM32端做本地判断也可以云端下发指令。本地判断的好处是响应快网络断了也能工作。我在代码里留了继电器控制接口阈值和云端同步。云端下发指令用HTTP GET或者MQTT订阅STM32解析后执行。注意继电器的控制信号和STM32引脚之间要加光耦隔离避免继电器动作时干扰MCU。5.2 多节点组网思路单个节点覆盖范围有限如果要监测多个房间可以布多个节点每个节点有独立的device_id云端按节点分组展示。节点之间不需要通信各自独立上报云端做聚合。如果节点之间距离近可以用ESP8266的Mesh功能组网但配置复杂不如每个节点独立连WiFi简单。我试过三个节点同时上报云平台端用不同的device_id区分面板上分开展示效果很好。成本上每个节点多花三十块钱左右比布线缆便宜多了。5.3 低功耗改造方向当前方案是持续供电的如果要做电池供电的版本需要改几个地方。一是传感器选低功耗型号MQ-2的加热丝功耗太大可以换成MQ-135或者用间歇加热。二是STM32进入Stop模式定时唤醒采样。三是ESP8266用深度睡眠只在需要上报时唤醒。粗略估算如果每5分钟上报一次每次唤醒工作2秒平均电流能降到1mA以下用2000mAh电池能撑两个月。但低功耗改造会牺牲实时性适合对时效要求不高的场景比如仓库环境趋势监测。5.4 数据分析和趋势预测数据上了云除了看实时曲线还能做简单分析。我在云平台端配了日报和周报统计每天的最大值、最小值和平均值。如果平台支持脚本可以算滑动平均和变化率提前发现异常趋势。比如噪音值连续三天缓慢上升可能意味着设备磨损或者环境变化提前排查比等告警触发更主动。这些分析不需要复杂的算法简单的统计就够了。关键是数据要连续所以上报策略要保证不丢数据。我个人在实际操作中的体会是这套系统的核心价值不在于技术多先进而在于完整跑通了一条从物理世界到数字世界的数据链路。传感器选型、ADC采样、滤波换算、无线传输、云端展示每个环节都有坑但每个坑填平之后你对物联网系统的理解就深一层。我建议第一次做的时候不要贪多先把一个传感器调通再逐个加最后接云端。这样出问题容易定位成就感也来得快。最后分享一个小技巧调试阶段在STM32端加一个按键按一下强制上报一次数据不用等5秒周期。这个按键在测试云端配置的时候特别有用省得干等。代码就几行但效率提升明显。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑