2026/8/29 8:27:42

ESP32实战:搭建室内空气质量监测系统 从传感器选型到数据存储

ESP32实战:搭建室内空气质量监测系统 从传感器选型到数据存储 1. 项目思路与整体方案选型1.1 为什么自己做空气质量监测而不是直接买成品先说点实际的。市面上空气质量检测仪从几十块的“心理安慰仪”到大几千的“专业设备”价格跨度极大但用过的人基本都有体会便宜的不准贵的又不知道准在哪。我自己先后买过三台不同价位的设备发现它们最大的问题不是“测不准”而是“测了什么说不清”——多数廉价设备只给一个综合评分或者LED灯环颜色你根本不知道它测的是PM2.5、TVOC还是单纯湿度更别提数据能不能导出、能不能历史回溯。这个项目的核心价值不是“拥有一台检测仪”而是“搭一套自己能完全掌控的数据采集系统”。自己动手做意味着你可以选择测哪些参数、用什么传感器、数据存哪里、怎么展示后续想接智能家居联动、想做远程监控、想加新传感器都在自己手里。这个系列我打算分几篇写本篇是第一部分目标很明确搭好硬件底座把核心数据稳定读出来并实现本地展示与存储。后面再考虑联网上传、可视化面板甚至联动家里的新风和加湿器。如果你正好是这几类人这篇内容会很对胃口家里有新风系统想验证效果、对过敏原或灰尘敏感想长期跟踪室内颗粒物变化、或者纯粹是喜欢折腾嵌入式硬件的玩家。我不打算把每个细节都讲成“保姆级”但关键步骤和参数选择逻辑会尽量说明白保证做过一遍之后你手里的不是一块会闪灯的电路板而是一套真正有用的数据基础设施。1.2 方案选型先搞清楚要测什么再决定怎么测搭建系统之前最忌讳的就是“先买一堆模块再说”。我这次按需求倒推家用场景下哪些空气参数最值得监测一是颗粒物PM2.5/PM10这基本上是室内空气质量的“标配指标”涉及灰尘、烟雾、花粉等也是大家最关注的健康指标。二是温湿度虽然不算“污染物”但直接影响体感舒适度而且后面做数据校正时温湿度是必须的参考量比如湿度对颗粒物传感器读数的影响就很明显。三是CO₂浓度这个常被忽略但室内二氧化碳是通风状况的“指示器”关窗睡觉一晚上CO₂轻松破2000ppm人会觉得昏沉。四是TVOC/甲醛新装修房子里最关心的指标。然后是硬件选型。主控我用的是ESP32理由很直接价格便宜、双核160MHz以上、自带Wi-Fi和蓝牙后面做数据上传完全不需要额外加模块。如果你手里有闲置的Arduino Uno跑通基础流程也没问题但后续做联网会卡壳还得换芯片。传感器方案我参照了很多开源项目的经验最终选了这几款参数传感器输出方式精度参考备注PM2.5/PM10攀藤PMS5003或SDS011UART/串口±10%PMS5003反应快SDS011便宜但较慢温湿度博世BME280或DHT22I2C±0.5°C / ±3%RHBME280还能测气压推荐CO₂Sensirion SCD30或SCD41I2C/UART±30ppm3%贵但稳定NDIR原理TVOC/甲醛Sensirion SGP30I2C相对值需校准只能测相对变化不能当精准ppm这套配置下来物料成本大概在150到400元之间取决于你选的传感器型号。如果预算紧张可以先砍CO₂和TVOC毕竟这两块传感器最贵但颗粒物和温湿度建议一步到位因为它们是整个系统的基础。1.3 这套系统的整体构架硬件之外我更想强调“系统”的架构思路。我把它分为三层采集层各类传感器按固定时间周期我设置为每10秒读取一次原始值采集数据通过I2C或UART总线上报给主控。处理层主控ESP32接收原始数据后做三件事——换算成标准单位比如将传感器原始计数换算为µg/m³、做数据有效性判断传感器值异常时丢弃或标记、计算一段时间内的均值。呈现与存储层同一块小屏幕上实时显示当前读数同时将历史数据写入MicroSD卡格式为CSV文件每10秒追加一行方便后续用Excel、Python做趋势分析。如果你愿意还可以在本地开一个最简单的HTTP服务同一局域网内用手机浏览器直接查看数据。这部分我放在“Part 2”讲远程访问时再展开本篇先把基础打牢。这个三层架构最大的好处是“每层可独立替换”。比如后续你觉得SGP30测TVOC不准想换SGP40只需要在采集层换模块、微调处理层的换算函数即可展示和存储完全不用动。系统设计上留好扩展位比一次性把所有功能堆满要重要得多。2. 核心硬件选型与传感器接线2.1 ESP32开发板怎么选避坑要点ESP32的板子市面上非常多NodeMCU-32S、DevKitC、FireBeetle、TinyPICO等等挑得人眼花。我的建议是新手别追小众选标准的DevKitC-V4或NodeMCU-32S就行。这两种板子引脚引出完整、资料最多、遇到问题搜索时答案也最好找。有个坑是很多廉价板子用的USB转串口芯片是CH340G而不是CP2102。CH340G在Windows下需要手动装驱动macOS和Linux一般免驱。这不是质量问题只是别等板子拿到手才发现电脑识别不了提前装好驱动就行。另外务必确认板子上的Flash容量是4MB而不是16MB。后者虽然更大但如果你用的Arduino设置里选错了分区方案后面烧录时会出现奇怪的“sketch too big”之类错误对新手来说排查难度比较大。2.2 传感器细节为什么选这些不选那些先说PMS5003这款颗粒物传感器。它的工作原理是激光散射内部有一个风扇把空气吸进一个小暗室激光照射空气中的颗粒物颗粒物散射的光被光电探测器接收经过算法反推质量浓度。它的数据输出是串口UART9600波特率每秒主动往外吐一帧数据。我选它而不是更便宜的SDS011主要原因是它响应速度更快——SDS011的采样周期约10秒PMS5003实测大概1到2秒就能反映数值变化这样后续做联动控制时反应更及时。BME280温湿度气压传感器是I2C接口接线非常简单VCC接3.3V、GND接GND、SCL接GPIO22、SDA接GPIO21ESP32的默认I2C引脚。注意BME280有多个版本有些模块板上已经焊好了上拉电阻有的没焊如果你用杜邦线连接且读数异常大概率是I2C总线缺少上拉电阻可以焊上两个4.7kΩ电阻在SCL和SDA上分别接到3.3V。我踩过这个坑后面会细讲。SCD30 CO₂传感器用的是非色散红外NDIR原理核心部件是一个红外光源和一个探测器探测器通过测量CO₂分子吸收特定波长的红外光来推算浓度。它支持I2C和UART两种接口默认是I2C地址0x61。这款传感器精度高、长期稳定性好但价格也贵一片就要120元以上。如果预算有限可以考虑SCD41体积更小更适合电池供电场景两者的驱动方式几乎一样。SGP30 TVOC传感器是金属氧化物MOX原理相当于一个“电子鼻”通过气敏材料电阻变化来判断空气中的还原性气体浓度。它输出的TVOC值不是绝对浓度而是相对参考值——意思是你不能把它测出的数字当成精确的“ppb/ppm”来引用更合理的使用方式是通过多日的趋势变化来判断室内污染事件比如新家具挥发周期、做饭油烟影响等。它有个特点第一次上电需要约12小时的“预老化”时间前12小时读数会不稳定且缓慢漂移这是正常的物理特性不是坏了。2.3 接线顺序与供电注意事项接线本身不复杂但顺序有讲究。我推荐按照“先把供电搞定再接数据线”的原则操作断开所有电源确保开发板和传感器都不带电。接电源线所有传感器的VCC统一接到ESP32的3.3V引脚注意是3.3V而不是5V接口标的电压不一定等于实际需要务必看每个模块的数据手册。GND统一接到GND。接数据线I2C设备BME280、SGP30、SCD30的SCL和SDA分别并联接到ESP32的GPIO22和GPIO21。UART设备PMS5003的TX接ESP32的RXGPIO16RX接ESP32的TXGPIO17。注意如果你用的是5V版本的传感器模块有些板子自带电平转换比如某些老款DHT22模块驱动电压接5V是可以的但数据线的逻辑电平必须确认与ESP32的3.3V兼容。电平不匹配是烧毁GPIO的第一大原因。供电还有一个容易犯的错ESP32开发板上的3.3V引脚输出能力有限如果你把整套传感器全部挂上去峰值电流可能超过500mA导致电压跌落、传感器读数异常甚至反复重启。稳妥的做法是用一个单独的3.3V稳压模块如AMS1117-3.3给传感器统一供电ESP32的3.3V只负责自己的逻辑电路。我测试过PMS5003启动瞬间的风扇电流就有100mA左右SCD30的加热脉冲可能瞬时到200mA加上BME280和SGP30整体峰值确实不低。给传感器单独供电比换一个“大功率开发板”更靠谱。3. 基础系统搭建与数据采集3.1 环境准备Arduino IDE配置与必要库软件部分我用的Arduino IDE虽然它界面老派但胜在生态成熟、新手资源多。要支持的开发板是ESP32需要先配置开发板管理器地址。打开“文件-偏好设置”在“附加开发板管理器网址”中填入https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json然后到“工具-开发板-开发板管理器”中搜索“esp32”安装Espressif官方包。这个包比较大几百MB网络不好的时候可能要等一会儿耐心等就好。接下来要安装几个库。用“工具-管理库”搜索并安装Adafruit BME280BME280传感器驱动。Adafruit SGP30SGP30传感器驱动注意最新版可能需要Adafruit BusIO作为依赖IDE会自动装。SparkFun SCD30SCD30传感器驱动也可以用Adafruit的版本两者选一个即可我用的是SparkFun版长期稳定。PMS读PMS5003的轻量库也可以不用库直接自己解析串口数据帧但用库会省很多事。这些东西都装好之后最好先烧一个最简单的Blink示例确认板子和串口链路正常再一步步加传感器。别一上来就烧全套代码排查起来会很痛苦。3.2 代码实现从“单传感器读值”到“多传感器整合”我来展示一个我实际在用的基础代码框架它不追求花哨但结构清晰适合作为Part 1的核心。#include Wire.h #include Adafruit_BME280.h #include Adafruit_SGP30.h #include SparkFun_SCD30_Arduino_Library.h #include PMS.h // 传感器对象声明 Adafruit_BME280 bme; Adafruit_SGP30 sgp; SCD30 scd30; PMS pms(Serial2); PMS::DATA pmsData; #define SEALEVELPRESSURE_HPA (1013.25) void setup() { Serial.begin(115200); Wire.begin(); // 初始化BME280 if (!bme.begin(0x76)) { Serial.println(BME280 not found!); } // 初始化SGP30 if (!sgp.begin()) { Serial.println(SGP30 not found!); } sgp.setHumidity(50.0); // 初始相对湿度后续可以用BME280的值定期更新 // 初始化SCD30 if (scd30.begin() false) { Serial.println(SCD30 not found!); } else { scd30.setMeasurementInterval(5); // 设置测量周期为5秒 } // 初始化PMS5003串口UART2 Serial2.begin(9600, SERIAL_8N1, 16, 17); Serial.println(Air Quality Monitor starting...); } void loop() { // 读取BME280 float temp bme.readTemperature(); float humi bme.readHumidity(); float pres bme.readPressure() / 100.0F; // 用BME280的湿度值更新SGP30的湿度补偿 sgp.setHumidity(humi); // 读取SGP30 float tvoc 0, eco2 0; if (sgp.measureTVOC()) { tvoc sgp.TVOC; eco2 sgp.eCO2; } // 读取SCD30 float co2 0; if (scd30.dataAvailable()) { co2 scd30.getCO2(); } // 读取PMS5003 float pm25 0, pm10 0; if (pms.readUntil(pmsData, 1000)) { pm25 pmsData.PM_AE_UG_2_5; pm10 pmsData.PM_AE_UG_10_0; } // 打印到串口监视器 Serial.print(Temp: ); Serial.print(temp); Serial.println( C); Serial.print(Humi: ); Serial.print(humi); Serial.println( %); Serial.print(Pres: ); Serial.print(pres); Serial.println( hPa); Serial.print(CO2: ); Serial.print(co2); Serial.println( ppm); Serial.print(PM2.5: ); Serial.print(pm25); Serial.println( ug/m3); Serial.print(PM10: ); Serial.print(pm10); Serial.println( ug/m3); Serial.print(TVOC: ); Serial.print(tvoc); Serial.println( ppb); Serial.print(eCO2: ); Serial.print(eco2); Serial.println( ppm); Serial.println(---); delay(10000); // 10秒一个采集周期 }代码逻辑很直白setup()里面初始化所有传感器和串口loop()每10秒读一次所有数据并打印到串口监视器。这个间隔设置是刻意的——家用空气质量变化是一个缓慢过程10秒已经足够密集同时给传感器足够时间稳定。如果你后续要做数据存储或联网上传10秒一个数据点也不会产生太大负载。3.3 串口监视器里的“陷阱”以及如何验证读数是可靠的代码烧录后打开串口监视器波特率115200正常情况下你会看到类似这样的输出Temp: 26.31 C Humi: 52.48 % Pres: 1012.87 hPa CO2: 612.24 ppm PM2.5: 14.89 ug/m3 PM10: 21.36 ug/m3 TVOC: 67 ppb eCO2: 468 ppm ---这时你要问自己三个问题这些值合理吗它们会缓慢变化吗还是每秒都在疯狂跳变第一个判断标准是“常识范围”。室内温度通常在18到30℃之间湿度在30%到70%之间CO₂在400到2000ppm之间PM2.5在0到200µg/m³之间除非有人在室内抽烟或炒菜。如果读数跑到离谱的区间比如CO₂显示0ppm或者9000ppm大概率是传感器初始化失败或者I2C地址冲突导致读取了错误寄存器的值。第二个判断标准是“稳定性”。空气质量数据应当呈现缓慢连续的变化趋势而不是随着采集周期剧烈抖动。如果你发现连续十次读数都完全一致那反而要警惕——说明传感器数据可能被缓存了。这在PMS5003上尤其常见它每秒输出多帧数据你用readUntil读取时如果处理不及时可能会重复读同一帧。解决办法是在读完数据后短暂清空串口缓冲或者把读取间隔拉长到与传感器输出周期错开。第三个判断标准是“对比验证”。拿一个已知可信的参考源对比一下比如把传感器放在户外通风处读数应该接近当地空气质量站的数据同一数量级就行不用追求完全一致或者用一个新买的成品检测仪对比注意两者的采样时间和位置要尽量贴近。这种对比不是要证明你做的设备比成品更准而是要确认没有系统性的偏差——比如PMS5003被灰尘污染后读数会系统性偏高环境校准是做不了这种检测的。4. 本地数据展示与存储4.1 数据展示从OLED屏幕到“本地服务”我踩过的坑数据能稳定读出来之后下一步就是怎么把它展示出来。最容易上手的是用一块SSD1306 OLED屏0.96寸I2C接口128x64分辨率价格十块钱左右。接线同样是I2CSCL到GPIO22SDA到GPIO21用Adafruit SSD1306库即可。在屏幕上轮流显示几组关键数据做成两页或三页轮播。但OLED屏有个天然限制信息密度太低。一次只能显示四五行文字每行还塞不了几个完整的数值。后来我换了一种思路用ESP32开一个基于WebServer的本地页面在手机浏览器上打开http://192.168.1.100ESP32的局域网IP直接看数据带简单的颜色标注——PM2.5低于35绿色35到75黄色高于75红色。这个方案其实只增加了几十行代码但体验完全不一样而且为下一部分的远程访问和数据可视化铺了路。不过本地服务只在调试时开就好你要是把它作为常态方案长期运行会发现ESP32的WebServer占用内存比较大可能导致数据采集不稳定。我的做法是Part 1阶段先用OLED屏串口输出把数据采集和存储跑稳本地Web界面放到Part 2再说届时会单独用一块开发板做数据服务主采集板只负责采集和写入SD卡。4.2 数据存储SD卡记录CSV注意这几个细节数据存SD卡是Part 1的另一个重要环节。原理很简单ESP32通过SPI接口连接MicroSD卡模块每次采集完成后往CSV文件里追加一行。但实际操作有几处需要注意。先看CSV写入代码的核心逻辑#include SD.h #include SPI.h #define SD_CS 5 // 根据你的SD卡模块CS引脚调整 File dataFile; void appendCSV(float temp, float humi, float pres, float co2, float pm25, float pm10, float tvoc, float eco2) { dataFile SD.open(/airquality.csv, FILE_APPEND); if (dataFile) { dataFile.print(millis()); dataFile.print(,); dataFile.print(temp); dataFile.print(,); dataFile.print(humi); dataFile.print(,); dataFile.print(pres); dataFile.print(,); dataFile.print(co2); dataFile.print(,); dataFile.print(pm25); dataFile.print(,); dataFile.print(pm10); dataFile.print(,); dataFile.print(tvoc); dataFile.print(,); dataFile.println(eco2); dataFile.close(); } else { Serial.println(SD card write failed!); } }有几个细节是容易踩坑的第一SD卡模块的CS引脚不能随意选。ESP32的SPI默认使用VSPI默认CS是GPIO5。如果你用的是其他引脚需要在SD.begin()时指定否则初始化失败。而且某些SD卡模块板上还有电平转换电路供电要求5V逻辑电平才是3.3V——搞清楚你的模块是哪种供电接错了会发烫。第二每次写入都要open-close。这段代码里SD.open(..., FILE_APPEND)之后写完立即close()看起来效率低但这是必要的——如果一直保持文件打开状态一旦断电最后几笔数据大概率会丢失文件系统没有及时同步元数据。损失几行数据没关系文件损坏才是大问题。第三CSV文件会越来越大。10秒一行一天就是8640行一年约315万行。作为文本文件这会到几百MB的量级。我的处理方式是每天0点轮换一个新文件文件名用日期标识比如airquality_20250601.csv。这样单个文件不会无限膨胀后面用Python做月度分析时也方便。代码上用一个变量记录当前日期到loop()里判断变了就切换文件路径。4.3 如何验证数据记录是“真能用的”而不是“看起来在记录”写完存储逻辑后我强烈建议做一次“去伪存真”的测试把SD卡拔出来插到电脑上用Excel或Python读取CSV检查三点。一是时间戳连续性如果你的代码里用的是millis()上电后经过的毫秒数那么重启后这个计数会归零同一天的CSV里就会出现重复的时间戳。这个不算大问题但如果要用时间轴画图最好在数据里同时记录“运行序列号”或者干脆用RTC模块DS3231维护真实时间。Part 1先用millis()问题不大反正后续会升级。二是数值范围合理性全选数据列看看有没有0、负数、或者极端值。这些异常值往往对应传感器在初始化未稳定时的输出或者是I2C读取失败时的默认返回值。处理方案是在采集代码里加上范围校验超出合理范围就不写入文件if (co2 400 co2 5000) { // 写入有效CO2值 } else { // 数据无效跳过本项或写入N/A }三是采样间隔均匀性正常情况下CSV里相邻两行的毫秒差应该在10000左右。如果出现大量间隔为20000甚至30000的行说明程序在loop()里某一步阻塞了最常见的元凶是pms.readUntil等串口超时。这种阻塞会导致整个采集周期拉长数据分析时也会引入偏差。我在前期调试时经常遇到这个问题后面把readUntil的超时时间从1000ms缩短到500ms并把串口读取放到单独的任务里情况就好多了。5. 常见问题与排查技巧实录5.1 传感器读数异常怎么定位“是传感器坏了还是代码错了”这套系统涉及多个传感器、多种接口出问题时最先要确定“是哪个环节坏了”。我把几个典型问题的排查思路整理成表格现象可能原因快速排查方法所有I2C设备都读不到I2C地址冲突、上拉电阻缺失用I2C扫描程序列出所有在线设备地址BME280读数为0或跳变供电不稳、地址错误0x76/0x77单独给BME280供电检查地址跳线PMS5003一直无数据接线TX/RX接反、波特率不对用串口调试工具直接看Serial2输出SGP30读数偏高且长期漂移未进行12小时老化、湿度补偿未开启连续通电24小时后再做趋势对比SCD30偶尔读不到测量周期与读取频率不匹配调大读取间隔确认数据可用标志位SD卡写入失败CS引脚不对、供电不足、FAT格式不对换一张已知OK的卡用SD.begin()错误码定位这里重点强调I2C扫描程序。它是排查I2C问题的第一利器在setup()里加上几行就能列出总线上所有有效地址。void scanI2C() { byte error, address; int nDevices 0; for (address 1; address 127; address) { Wire.beginTransmission(address); error Wire.endTransmission(); if (error 0) { Serial.print(I2C device found at address 0x); if (address 16) Serial.print(0); Serial.println(address, HEX); nDevices; } } if (nDevices 0) Serial.println(No I2C devices found); }执行后正常应该看到三个地址BME280的0x76或0x77、SGP30的0x58、SCD30的0x61。如果只显示两个缺哪个就去查哪个的接线和供电。5.2 串口监视器中文乱码或者数据刷太快是小事但很烦人这不是硬件问题但如果你不处理调试体验会非常差。串口监视器乱码最常见的原因是波特率不匹配——代码里Serial.begin(115200)但监视器右下角选的是9600两边对不上自然乱码。简单但真有人忽略。还有一个小技巧是给输出加“节流”数据里面加一个“标记字符串”帮助快速搜索比如在每次更新前都打印一行SAMPLE。这样在串口监视器里搜索“SAMPLE”就能快速判断程序是否在正常运行定位死循环或长时间阻塞非常管用。我自己的代码里至今保留着这个习惯。5.3 传感器“刚上电读数正常运行几小时后就飘了”这是标定问题还是硬件老化“漂移”是空气质量传感器最常见的现象尤其表现在SGP30和PMS5003上。SGP30是MOX传感器气敏材料会随环境温度和湿度缓慢变化再加上它本身测量的是“相对值”在长期运行后基线漂移是必然的。PMS5003的风扇会吸灰灰尘积累在光学腔体里会直接影响激光散射的强度导致读数持续偏高。处理办法分两种简单版本定期手动校准——把设备放到室外通风处运行半小时记录读数的平均值然后当作“基线”去修正室内读数。这是物理层面的“归零”。进阶版本在代码里做湿度补偿校正。官方文档显示PMS5003在低湿度环境下的读数较为准确高湿度下PM2.5读数会系统性偏高最多偏差达30%。你可以引入一个基于相对湿度的修正系数去拟合出厂标定数据。这个不是科学论文级别的精确度但在家用场景下修正后的数据趋势比原始值要更可信。还有一种“每天定时自校准”的玩法利用作息规律比如凌晨2点到4点家里通常没人、没有明显污染源这时记录的颗粒物最低值可以作为当天的“环境本底值”程序自动保存在EEPROM里第二天在UI上显示“今日相对本底浓度”。虽然实现不复杂但能在很大程度上提升数据的解释力。这部分我后续打算展开讲这里先提一个方向。6. 实操总结与个人心得6.1 从这套系统里我真正得到的是什么亲手做完这套系统我最大的感受是数据从“别人的说法”变成了“自己的依据”。以前看市售检测仪上的一个数字我只能选择信或者不信现在我知道这个数字来自哪个传感器、测量原理是什么、有哪些误差来源这种掌控感是成品设备永远给不了的。家里的空气情况也确实比我想象的复杂我以为通风就够了结果发现冬季关窗后CO₂能轻松破2000ppm我以为灰尘主要来自室外结果PM2.5在开空气净化器半小时内从80降到20关掉一晚上又会缓慢回升——这些变化在成品设备上也能看到但看不到10秒钟级别的波动细节更无法把数据导出做成曲线对比。6.2 如果你只能记住三点我建议是这三点第一传感器选型比代码重要得多。代码写错了可以改传感器选错了就是物理层面的局限改不了。预算不足时优先保颗粒物和温湿度CO₂和TVOC可以后加。第二接线和供电是90%故障的根源。不要一上来就怀疑代码先检查线有没有插紧、供电够不够、信号电平对不对。我用万用表测试过很多时候插上第二块I2C设备后SDA线上的信号波形就变形了肉眼看上去好好的杜邦线实际上触点已经氧化。第三Part 1阶段一定要把数据存储做扎实。没有可靠的历史数据后面的趋势分析、智能联动全都是空中楼阁。我见过很多朋友做这类项目前面玩得很嗨到存数据这一步就敷衍了事结果想复盘的时候发现只有零散几段记录完全没法用。6.3 下一步打算怎么扩展按照这个系列的计划Part 2我会做两件事一是把数据传到本地MQTT让系统具备实时远程查看能力二是做一个简单的Web可视化面板让家人不用打开串口监视器用手机就能看到当前空气质量和24小时趋势。再往后我会尝试加入阈值报警和联动控制——比如PM2.5超标时自动开启净化器、CO₂超标时提醒开窗甚至跟家里的智能插座做一个小闭环。这些内容都建立在Part 1的硬底座之上所以这一篇的基础打得越扎实后面越省心。我自己做这个项目的过程踩过很多坑也推翻过很多初始设计能写在文章里的都是验证过、确定可行的方案。如果你也正在做类似的项目或者准备开始做希望这一篇能帮你少走几段弯路。