拓冰建站拓冰建站
首页 / 资讯中心 / 正文

DIY家庭空气质量监测:ESP32+传感器实战指南

1. 从灰尘到数据为什么我会对家里的空气质量较真搬到现在的房子三年了头两年我从来没想过空气质量这件事。直到去年冬天有一段时间每天早上起来鼻腔都干得发疼喉咙像被砂纸擦过孩子也频繁揉眼睛。开窗通风之后症状明显缓解但我总想知道屋子里到底什么超标了凭感觉猜实在不靠谱。坦白讲普通家庭对空气质量的认知基本停留在两个极端要么觉得“天天住着能有什么事”要么被各种除醛、净化产品制造焦虑。我决定自己动手做一个家庭空气质量监测装置用数据把这件事搞清楚。项目目标不复杂实时检测PM2.5、PM10、二氧化碳趋势、TVOC和温湿度把数据传到手机上攒下长期运行记录后面可以慢慢分析规律。这篇文章是全程记录的第1部分从需求拆解、传感器选型到组装和固件开发后面会再写第2部分讲数据上传、告警配置和长期趋势分析。适合动手能力尚可、会一点代码哪怕只会抄代码的朋友参考预算大约三百块出头不需要焊台一把电烙铁、几根杜邦线就够了。先说结论这个活没有想象中复杂但也没有想象中简单。难的不在硬件连接而在“怎么让数据可信”。传感器买回来就能读数但读数对不对、有没有漂移、该放在哪里才是真正决定项目成败的事。2. 指标太多先分清这 5 个传感器参数再动手2.1 PM2.5与PM10粒径不同来源和危害都不同PM2.5是空气动力学直径小于等于2.5微米的颗粒物PM10是小于等于10微米的颗粒物。打个比方PM10是粗沙子PM2.5是细面粉。粗颗粒大多来自扬尘、花粉、宠物皮屑能被鼻腔阻挡一部分细颗粒能穿过鼻毛和上呼吸道直接进入肺泡甚至进入血液循环。把PM2.5和PM10放在一起测就能粗略判断污染来源。如果PM10高但PM2.5不高说明是地面扬尘或花粉这类粗颗粒为主如果两者都很高且比值接近大概率是燃烧产物、二手烟、烹饪油烟这类细颗粒污染。2.2 二氧化碳你在室内犯困的隐形元凶二氧化碳本身不是毒气但室内CO2浓度过高会让人昏昏欲睡、注意力下降。一个15平方米的卧室关窗睡一整晚两个成年人可以把CO2浓度从500ppm推到2000ppm以上。数值超过2000ppm时不少人会出现头痛、烦躁。我选择用SGP30来估算eCO2等效二氧化碳因为它的成本比专业红外CO2传感器如MH-Z19B低不少。需要提前说明的是SGP30的eCO2不是真正的红外测量而是根据TVOC负载推算出来的等效值绝对值精度一般但趋势判断完全够用。2.3 TVOC装修污染的主力军团TVOC总挥发性有机物涵盖苯、甲苯、二甲苯、乙苯、苯乙烯等一大类物质。刚装修完的房子TVOC往往超标数倍而且释放周期长达数月甚至数年。SGP30内置了VOC算法芯片可以直接输出TVOC浓度单位ppb。但SGP30也有短板它需要一段时间的“基线校准”初次上电的前48小时读数会明显漂移。这一点后面详细说。2.4 温湿度不做装饰空气净化器的性能受它影响温度和湿度单独看不是污染物但会直接影响颗粒物传感器的计数结果和化学传感器的反应速度。湿度过高时颗粒物吸湿膨胀PM2.5数值可能会明显偏高温度影响VOC传感器内部的加热电路工作状态。把温湿度纳入监测一方面是为了修正其他传感器读数的解读逻辑另一方面是排查“为什么数值突然变化”时必备的对照变量。2.5 甲醛要不要现在就上我选择暂时不做甲醛在家装污染里的地位是C位明星但它对传感器的要求很苛刻。电化学甲醛传感器的寿命通常在2到3年需要定期校准便宜模块的交叉敏感度高酒精、香水都会干扰价格也不低。第1版我决定先不上电化学甲醛传感器把重点放在PM2.5/PM10/CO2趋势/TVOC/温湿度这一组参数上。等这套系统稳定运行两三个月之后再考虑扩展甲醛模块。提示如果你预算充足或者刚装修完可以一步到位上ZE08-CH2O电化学甲醛模块。但要接受一个事实所有电化学传感器都会随使用时间老化校准周期远远短于激光颗粒物传感器。3. 硬件选型预算、精度、可玩性的取舍3.1 主控ESP32是当前最优解主控芯片我选的是ESP32开发板价格在25到50元之间。选择理由有三条自带WiFi和蓝牙后续数据上传不用外挂通信模块计算能力对传感器数据处理的负载绰绰有余后面就算再加屏幕显示或者本地跑简单滤波算法也够用生态成熟Arduino示例代码遍地都是踩坑资料丰富。如果你手头有树莓派当然也可以做但功耗和体积都会更大成本也高出不少。从“7×24小时长期开机”这个需求出发ESP32的低功耗优势非常明显。3.2 颗粒物传感器Plantower PMS5003是口碑之作PMS5003是北京攀藤科技的一款激光散射颗粒物传感器价格70到90元可以同时输出PM1.0、PM2.5、PM10浓度。它内部的激光器照射气流中的颗粒物由光电二极管接收散射光信号再通过算法换算成质量浓度。同系列有PMS7003、PMS9003等型号接口定义基本一致。选PMS5003的理由是它的信号稳定性和数据输出格式资料最全社群使用者最多后期排查问题方便。3.3 气体传感器SGP30兼顾TVOC和eCO2SGP30是Sensirion公司的一块金属氧化物半导体传感器I2C接口价格在35到50元之间。它输出的TVOC和eCO2指标虽然不如实验室级别精确但在民用监测场景下是具有参考价值的。另外一个选择是CCS811同样是VOC传感器但初版固件问题较多需要手动升级驱动的概率比较高。SGP30相对省心一些我最后选了它。3.4 温湿度传感器SHT30的稳定表现温湿度模块有DHT11、DHT22、BME280、SHT30等一票选择。DHT11精度太差DHT22凑合但时序协议用起来烦BME280价格略高。SHT30温湿度精度为±0.2°C和±2%RHI2C接口方便价格约20元是最合理的中间选择。3.5 其他必需品12V/1A电源适配器PMS5003的激光器需要5V供电但建议用12V适配器再搭一个稳压模块供电更稳定LM2596降压模块把12V降到5V给ESP32和PMS5003供电约5元5V转3.3V的逻辑电平转换模块如果ESP32和传感器之间需要电平匹配但SGP30和SHT30本身支持3.3V供电这事可以简化PMS5003的数字输出口是3.3V电平ESP32的GPIO输入也是3.3V直接用杜邦线连接即可。面包板、杜邦线、一个小型亚克力外壳或者3D打印外壳。整个硬件的预算明细粗略列一下ESP32开发板 35元 PMS5003颗粒物传感器 85元 SGP30气体传感器 45元 SHT30温湿度传感器 20元 LM2596降压模块 6元 12V/1A适配器 15元 面包板、杜邦线、外壳等零碎 30元 合计 236元左右如果想再压预算把SGP30换成CCS81135元总价能控制在220元附近。但建议不要为了省六七十块钱在核心传感器上降级数据和体验差距比较大。4. 接线与安装从零开始的组装细节4.1 电源接线先供电再接信号线整体供电链路是墙插220V → 12V适配器 → LM2596降压到5V → 一路给ESP32的5V引脚一路给PMS5003的5V引脚。SGP30和SHT30都支持3.3V供电直接接ESP32的3.3V引脚。LM2596模块在使用前要用万用表调整输出电压它上面有个蓝色电位器边调边测精确调到5.0V。千万别跳过这一步直接接负载模块出厂电压常常高于5V接错会烧板子。注意ESP32的5V引脚就是USB供电时的VBUS引脚接外部5V电源时不用再插USB否则可能出现双电源冲突损坏USB口。4.2 信号接线I2C和串口的配合SGP30和SHT30都挂在ESP32的I2C总线上SGP30的地址是0x58SHT30的地址是0x44地址不冲突并联没问题。PMS5003使用UART串口和ESP32通信默认波特率9600bps。但ESP32的硬件串口只有三个其中一个是烧录口为了避免干扰我直接用SoftwareSerial随便指定两个GPIO引脚来接收数据这样布局更灵活。接法汇总模块引脚ESP32引脚SGP30VCC3.3VSGP30GNDGNDSGP30SCLGPIO22SGP30SDAGPIO21SHT30VCC3.3VSHT30GNDGNDSHT30SCLGPIO22SHT30SDAGPIO21PMS5003VCC5VPMS5003GNDGNDPMS5003TXDGPIO16用SoftwareSerial RXPMS5003RXDGPIO17用SoftwareSerial TX本方案不用PMS5003只需要读取数据不需要发送指令所以它的RXD引脚不接也行。4.3 外壳必须保证传感器和空气充分接触这一步最容易被忽略也最容易毁掉整组数据。我第一次把PMS5003装进了一个全封闭的塑料盒子里只留了两个小孔走线结果PM2.5读数比其他设备低了一倍。原因很简单PMS5003的激光探头需要气流持续通过空气不流通测到的就是盒子内部那点“死空气”的颗粒物浓度。外壳设计需要注意两点颗粒物传感器要留明显的进风口和出风口风道不能被遮挡且出风口不要对着进风口SGP30和SHT30需要和空气接触外壳相应位置必须开孔但不要放在PMS5003的正风口直吹否则颗粒物传感器风扇带来的气流扰动会影响VOC传感器的局部环境。我最后用亚克力板做了一个开放式的框架六个面只封闭底面和背面其他面都留出大面积开口再配了一个小风扇从侧面往PMS5003的进风口方向缓吹具体来说是一个5V的4010风扇距离PMS5003大约5厘米。这个方案实测下来数据基本稳定。4.4 首次上电检查别急着写代码焊接和接线完成后先不烧录程序用万用表测一下各组电压5V电源两端是否稳定在4.8~5.2V之间ESP32的3.3V输出是否正常确保SGP30和SHT30的VCC引脚没有接到5V。确认无误后再用USB线连电脑烧录一个最简单的I2C扫描程序分别确认0x44和0x58两个地址能否被识别。这一步能把你和“莫名其妙连不上传感器”的问题隔离开。5. 固件设计如何让数据真正“说话”5.1 代码框架Arduino 库固件的核心逻辑分四块I2C初始化、PMS串口数据解析、循环采样、数据外发。第1部分先把数据外发做成调试输出的格式第2部分再对接MQTT和数据库。需要安装的库Adafruit SGP30 SensorAdafruit SHT31SHT30也可以用内置的Wire.h和SoftwareSerial5.2 PMS5003数据解析串口帧格式PMS5003每秒钟发一组数据帧帧长32字节。帧头有两个固定字节0x42 0x4D第2和第3字节是帧长后面的数据区包含PM1.0、PM2.5、PM10的CF1标准浓度和大气环境浓度。从第4字节开始数据区偏移 0-1PM1.0标准浓度 数据区偏移 2-3PM2.5标准浓度 数据区偏移 4-5PM10标准浓度这里有个容易踩的坑数据是高位在前Big-endian也就是高字节在前、低字节在后。如果你直接把两个字节相加算出来的数值会错得离谱。正确做法是(data[4] 8) | data[5]。标准浓度CF1指的是在“标准颗粒物”特性条件下的质量浓度主要用于传感器一致性对比大气环境浓度则是经过空气质量修正后的数值。日常显示建议直接使用大气环境浓度就是偏移6开始的那组数据。5.3 采样策略不能只看瞬时值颗粒物浓度是不断波动的特别是在有人走动、做饭、开窗等场景下瞬时值跳动很大。我采用的是“每1秒读一次原始帧连续20秒后取平均值”的策略每20秒输出一组平滑后的数据。SGP30读取频率不宜过高官方建议最快1Hz我设置为每5秒读一次。SGP30内部有算法芯片需要持续供电才能维持基线不要频繁断电重置。温湿度每10秒读一次就够变化速度远不如颗粒物和VOC。5.4 基础代码片段下面给出一段核心采样逻辑只保留最关键的传感器读取和解析部分#include Wire.h #include Adafruit_SGP30.h #include Adafruit_SHT31.h #include SoftwareSerial.h #define PMS_SET_PIN 16 // PMS5003 TXD接GPIO16 SoftwareSerial pmsSerial(16, 17); Adafruit_SGP30 sgp; Adafruit_SHT31 sht Adafruit_SHT31(); unsigned long lastPmsRead 0; unsigned long lastSgpRead 0; unsigned long lastShtRead 0; float pm25Sum 0; float pm10Sum 0; int pmCount 0; struct PmsData { float pm25; float pm10; }; bool readPmsData(PmsData d) { uint8_t buf[32]; int idx 0; unsigned long start millis(); while (millis() - start 1000) { if (pmsSerial.available()) { uint8_t b pmsSerial.read(); if (idx 0 b ! 0x42) continue; // 等帧头 if (idx 1 b ! 0x4D) { idx 0; continue; } buf[idx] b; if (idx 32) break; } } if (idx 32) return false; // 校验帧长 uint16_t frameLen (buf[2] 8) | buf[3]; if (frameLen ! 28) return false; // 大气环境浓度偏移6/8/10 d.pm25 ((buf[8] 8) | buf[9]) * 1.0; d.pm10 ((buf[10] 8) | buf[11]) * 1.0; return true; } void setup() { Serial.begin(115200); pmsSerial.begin(9600); Wire.begin(); if (!sgp.begin()) Serial.println(SGP30 not found); if (!sht.begin(0x44)) Serial.println(SHT30 not found); sgp.setIAQBaseline(0x8B8A, 0x4A4C); // 可加载已保存基线 } void loop() { unsigned long now millis(); if (now - lastPmsRead 1000) { lastPmsRead now; PmsData d; if (readPmsData(d)) { pm25Sum d.pm25; pm10Sum d.pm10; pmCount; } if (pmCount 20) { float avgPm25 pm25Sum / pmCount; float avgPm10 pm10Sum / pmCount; pm25Sum 0; pm10Sum 0; pmCount 0; Serial.printf(PM2.5: %.1f ug/m3, PM10: %.1f ug/m3\n, avgPm25, avgPm10); } } if (now - lastSgpRead 5000) { lastSgpRead now; uint16_t tvoc, eco2; if (sgp.IAQmeasure()) { tvoc sgp.TVOC; eco2 sgp.eCO2; Serial.printf(eCO2: %d ppm, TVOC: %d ppb\n, eco2, tvoc); } } if (now - lastShtRead 10000) { lastShtRead now; float t sht.readTemperature(); float h sht.readHumidity(); if (!isnan(t) !isnan(h)) { Serial.printf(Temp: %.1f C, Humidity: %.1f %%\n, t, h); } } }5.5 基线校准SGP30的开局教训SGP30上电后会自动进行基线校准典型时间是48小时。这个过程里它会把当前环境的VOC水平作为基准进行内部算法拟合。如果你一开始在通风良好的阳台校准基线然后搬进一个甲醛超标的房间前两天的TVOC读数会很低直到算法逐步追踪到新环境。这意味着两件事第一次使用别急着看绝对数值先让它在最终部署位置连续运行至少48小时如果中途断电超过几分钟基线会丢失重新上电后又要重新校准。所以我的做法是在代码里掉电前把基线保存到EEPROM启动时通过setIAQBaseline恢复。但这个功能要在稳定运行一段时间之后才可靠初期不推荐折腾让它自然地跑两天就好。6. 放置传感器同一台设备位置不同结果天差地别6.1 别放在窗边别放在墙角传感器放置是“数据可信度”的生命线。放在窗边特别是朝南的窗户边上白天阳光直射会让温度传感器读数偏高开窗时直接测到室外空气而你要监测的是人在室内活动区域的空气质量不是窗户边上那一条换气带。放在墙角空气流动不畅颗粒物和VOC都会在局部集聚或滞留。衣柜旁边测出来的甲醛浓度可能比其他区域高出一大截而那不是你日常主要活动的地方。正确的位置应该是离地面1.2到1.5米大致是人坐着时呼吸带的高度距离墙面至少0.5米避开空调出风口、加湿器、空气净化器出风口、厨房排烟口。6.2 放置场景客厅和卧室的取舍一台设备只能测量一个点这是物理限制。你的家不是一个均匀混合的盒子每个房间的空气质量都不一样。我建议的排序是优先放卧室。人在卧室连续待的时间最长而且睡眠时门窗紧闭CO2和TVOC的累积效应最明显其次放客厅。客厅是白天活动中心污染源多油烟、家具释放、家电扬尘最后考虑儿童房。如果小孩有过敏、哮喘史儿童房的空气质量优先级应该排到最前面。我第一台设备放在了卧室因为最近困扰我最多的就是每天早上起床的鼻腔干痛和犯困感。放进去的第一晚数据就狠狠打了我一巴掌。6.3 和空气净化器保持距离如果你的家中有空气净化器传感器不能离它太近。净化器出风口附近的PM2.5浓度可能低到比真实值低几个数量级。这就像你在空调出风口下面测室温怎么测都是空调的风温而不是房间平均温度。我建议传感器和净化器之间至少保持2米距离并且保证传感器不在净化器的直吹路径上。7. 数据初测第一周我看到了什么惊喜发现7.1 卧室的CO2一个每天被忽视的“隐形高峰”设备在卧室稳定运行了两天基线校准完成之后我开始认真记录数据。最惊人的发现是CO2的变化曲线。晚上11点入睡两个成年人关窗关门卧室CO2从大约600ppm起步到凌晨3点已经超过1800ppm早上7点出门前接近2200ppm。白天开窗通风之后立刻回落到450到500ppm。这个数据彻底解释了我之前“早上起来头昏脑涨”的问题。2200ppm对于健康成年人来说不至于中毒但足以让睡眠变浅、晨起乏力。解决方案也很简单睡觉时把窗户开一条缝或者装一个带通风功能的窗式通风器。7.2 做饭对客厅的影响被严重低估我把设备临时搬到客厅做了一次对照实验。晚饭时间开火炒菜厨房门没关客厅PM2.5从30多μg/m³直接飙升到180μg/m³持续了将近四十分钟才慢慢降下来。注意这是“客厅”还不是厨房里的颗粒物浓度。油烟扩散的速度远超我预期厨房门不关等于整个房子都在“被迫抽油烟”。从那以后我做饭一定关厨房门、开油烟机而且炒完菜再多开五分钟把残余油烟抽干净。7.3 湿度对PM2.5读数的干扰案例有一天晚上我开加湿器发现PM2.5读数从40涨到了90。当时我还以为传感器坏了后来查了很多资料才反应过来加湿器喷出的水雾本身就是颗粒物。激光颗粒物传感器分不清“水滴颗粒”和“粉尘颗粒”湿度上来读数就跟着上去。这个案例提醒了我如果发现PM2.5短时间内异常升高先看湿度曲线。湿度同时上涨那大概率是水雾干扰不是真的污染。7.4 一周数据的初步印象用Excel简单统计了一下第一周的数据指标低谷值高峰值常见范围PM2.512180做饭时25-60PM1018260做饭时35-80eCO24502200夜间卧室500-1200TVOC20ppb320ppb30-100温度18°C27°C20-25°C湿度32%78%40-60%TVOC在白天家里开暖气的那几天出现过短期峰值推测和温度升高加速家具材料释放有关。这个趋势值得持续观察我会在第2部分结合更长时间的记录做详细分析。8. 校准、维护与长期运行的细节经验8.1 颗粒物传感器需要定期清理激光颗粒物传感器内部有一个小风扇长期运行会积累灰尘风道堵塞会导致进风量下降读数偏低。清理周期取决于环境灰尘程度一般2到3个月清理一次。清理方法很简单断电打开外壳用气吹或压缩空气罐吹掉风扇叶片和风道里的灰尘。注意不要用湿布擦激光镜头也不要用酒精清洁避免损坏镜片镀膜。8.2 SGP30的基线掉电不是小事SGP30的基线维护是我这次踩过最大的坑。有一天下雨断电了几分钟重新上电后我没有恢复基线SGP30视作“全新传感器”重新校准导致接下来两天TVOC和eCO2数据明显偏低。后来我在固件里加了第2部分要讲的“基线定期保存和恢复”逻辑每半小时读一次sgp.getIAQBaseline()把baseline值写入EEPROM启动时检测到EEPROM里有数据就setIAQBaseline恢复。这样断电重启基本不丢基线。8.3 不要和手机空气质量App的数据直接比绝对值手机天气App上的空气质量数据来自城市监测站代表的是宏观区域水平。家用传感器的进风口在室内测的是室内微环境。两者数据有差异是正常的直接对比绝对值没有意义。更有价值的对比方式是看室内外数据的差值趋势。室外PM2.5 200μg/m³室内仍然50μg/m³说明门窗气密性不错室内比室外高说明室内有持续污染源需要排查。8.4 传感器要不要校准怎么校准这也是新手问得最多的问题。严格来说PM2.5激光传感器需要和基准仪器比如β射线法监测仪做同点位对比校准普通用户没有这个条件。可行的替代方案是把设备带到室外环境和城市监测站的数据对比观察偏差趋势。注意监测站数据计算周期通常是一小时对比前自己也要取一小时平均值购买一台经过校准的民用参考设备比如某品牌雾霾表做同一位置对照看差值是否在合理范围内使用香烟烟雾或蚊香气溶胶做相对变化的趋势测试只看曲线形状不看绝对值。我在实测中发现PMS5003出厂精度在多数场景下已经够用重点是保持风道畅通而不是频繁校准。提示不要被“精度不达标就不敢用”的心理困住。居家空气质量监测的核心价值在趋势判断和相对变化而不是实验室级别的绝对精度。你真正需要知道的是“什么情况下超标了、持续时间多长、怎么改善”这些数据完全可以支撑得出结论。8.5 下一步为了长期运行供电和网络要稳定第1阶段我在USB供电串口调试输出下运行这个状态只能短期用不能长期放在卧室里拉着一根USB线太难看而且掉电后数据就断了。第2部分我会把它改造成预埋12V电源线到监测位置降压后给ESP32和传感器供电增加MQTT数据上报把数据存到本地服务器再接入可视化面板增加断电重连、数据缓存补传机制在设备状态异常时通过微信推送到手机。我在这个项目里学到的最重要的一件事是空气质量监测不是“买一个空气检测仪放桌上”那么简单它的核心价值在于持续、可靠、有参照系的数据。一个便宜的DIY设备只要放置合理、长期稳定运行积累下来的数据可能比一次专业检测更有指导意义因为专业检测只给你一个时间点的快照而你自己搭的系统能告诉你每一天、每一小时、每一个生活行为对室内环境的影响。第2部分我会把整套数据采集架构、可视化方案和告警逻辑完整写出来到时候见。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门