
1. 项目缘起为什么需要自己动手监测室内空气质量最近几年大家对居住环境的关注度越来越高尤其是室内空气质量。你可能也遇到过这种情况在办公室待久了觉得头昏脑胀或者在家里总觉得空气闷闷的开窗通风吧又担心外面的噪音、灰尘或者温度不合适。这时候一个直观的空气质量数据就显得尤为重要了。市面上的空气质量检测仪选择很多从几十块到上千块的都有但功能要么太单一要么价格不菲而且你很难知道它内部传感器的精度到底如何数据是否真的可信。于是自己动手搭建一个室内环境监测站的想法就冒出来了。我的核心需求很简单能实时监测室内的温度、湿度以及空气质量尤其是CO2浓度成本要低数据要可靠最好还能自己定制显示方式或者做进一步的数据记录。经过一番调研和选型我最终锁定了两个非常经典且性价比极高的传感器DHT11和MQ-135并用Arduino作为控制核心来实现。这个组合可以说是入门级环境监测项目的“黄金搭档”既能满足基本需求又为后续的功能扩展比如连接显示屏、上传数据到云端留足了空间。接下来我就带你从零开始一步步拆解这个“室内气候监测站”的搭建过程。我会详细说明每个元件的选型理由、电路连接的关键细节、代码编写的逻辑以及在实际调试中可能遇到的“坑”和解决办法。无论你是刚接触Arduino的新手还是想找一个可靠的项目练手这篇文章都能给你提供一份可以直接“抄作业”的完整指南。2. 核心元件选型与原理深度解析在动手焊接第一根线之前我们必须先搞清楚手头这两个“主角”到底是怎么回事。知其然更要知其所以然这样在后续调试和数据处理时你才能心中有数遇到问题也知道该往哪个方向排查。2.1 DHT11温湿度传感器稳定可靠的“环境基础数据源”DHT11是一个集成了温度和湿度传感元件的数字式传感器。它的输出已经是经过内部校准和模数转换的数字信号这意味着我们不需要复杂的模拟电路去处理微弱的电压变化直接通过一根数据线就能读取到数值这对初学者非常友好。工作原理简述DHT11内部包含一个电阻式感湿元件和一个NTC负温度系数测温元件以及一个8位单片机。当你发起一次读取请求后这个单片机会采集传感器的模拟信号将其转换为40位5字节的数字数据包并通过单总线Single-Bus协议发送出来。这40位数据包含了整数和小数部分的湿度、温度值以及一个校验和。注意DHT11的响应速度相对较慢完成一次数据采集和传输需要大约2秒的时间。在编程时两次读取操作之间必须留有足够的间隔否则会读取失败。它的精度对于一般室内监测来说是足够的湿度±5%RH温度±2°C但如果你需要更高精度的数据可以考虑它的“大哥”DHT22。为什么选它成本极低单价通常在10元人民币以内是入门项目的绝佳选择。接口简单只需要一个数字IO口连接三根线VCC GND DATA。库支持完善Arduino社区有非常成熟的DHT sensor library几乎做到了开箱即用。足够家用对于判断室内是否过冷、过热、过于干燥或潮湿其精度完全够用。2.2 MQ-135气体传感器灵敏但需要“理解”的“空气嗅探器”MQ-135是这次项目的重点和难点。它是一个模拟输出的半导体气体传感器对氨气、硫化物、苯系蒸汽等气体有良好的灵敏度但更重要的是它常被用来间接监测二氧化碳CO2的浓度变化。这里必须强调“间接”二字这是理解MQ-135的关键。工作原理深度剖析MQ-135的核心是一个由二氧化锡SnO2制成的敏感元件。在清洁空气中二氧化锡的导电率较低。当存在还原性气体如CO2、酒精、烟雾时这些气体会与传感器表面的氧离子发生反应导致传感器的导电率增加。我们通过一个负载电阻通常集成在模块上将其导电率的变化转换为一个电压信号输出。Arduino的模拟输入引脚A0-A5读取这个电压值0-5V并映射为一个0-1023的数值。关键点它测的不是CO2浓度而是“空气洁净度”的综合指标。这是最容易产生误解的地方。MQ-135对多种气体都有响应它无法区分是CO2升高了还是你喷了酒精消毒或是有人在旁边抽烟。因此它输出的原始模拟值比如analogRead(A0)350不能直接等同于CO2的ppm值。它反映的是“相对于清洁空气当前空气被污染的程度”。我们需要通过一个标定和转换过程才能将这个值估算为近似CO2浓度。为什么选它对CO2变化敏感虽然不专一但对人体呼出的CO2浓度变化有明确的响应适合监测人员密集导致的空气质量下降。成本优势相比动辄数百元的专业NDIR非分散性红外CO2传感器MQ-135模块价格仅需20-30元。模拟输出易于处理直接连接Arduino的模拟引脚即可读取无需复杂的数字通信协议。选型对比与局限认知如果你需要精确的、唯一的CO2浓度值用于农业温室控制或楼宇自动化那么必须选择NDIR传感器如MH-Z19B。但如果你只是想做一个“空气质量告警器”用于提醒“房间该通风了”那么MQ-135以其极高的性价比是完全胜任的。我们的项目定位就是后者。2.3 Arduino Uno项目的“大脑”与协调中心Arduino Uno是基于ATmega328P微控制器的开发板。我选择它是因为生态无敌拥有最庞大的社区、教程和库文件支持几乎你遇到的任何问题都能找到答案。接口丰富提供了14个数字IO口其中6个可做PWM输出和6个模拟输入口足够连接本项目的传感器以及未来扩展的OLED屏幕等。供电方便可以通过USB供电也可以通过外部7-12V直流电源供电非常灵活。编程简单基于C/C的Arduino IDE让硬件编程的门槛大大降低。对于这个项目Uno的运算能力和存储空间绰绰有余。当然你也可以使用更小巧便宜的Nano或者功能更强大的ESP32后者可以直接连接Wi-Fi上传数据但Uno作为起点是最稳定、最不容易出幺蛾子的选择。3. 硬件连接与电路搭建详解理论清楚了现在开始动手连接。正确的硬件连接是项目成功的第一步这里任何一个接触不良或接错线都可能导致后续调试陷入困境。3.1 所需材料清单主控板Arduino Uno R3 × 1传感器DHT11温湿度传感器模块 × 1MQ-135气体传感器模块 × 1连接线杜邦线公对公若干供电USB数据线用于连接电脑编程和供电或9V电池套件3.2 分步连接指南请务必在断电状态下进行所有连接操作。第一步连接DHT11模块DHT11模块通常有3个引脚有些是4个引脚但其中1个空置。以3引脚为例VCC- 连接到Arduino的5V引脚。GND- 连接到Arduino的GND引脚。DATA- 连接到Arduino的数字引脚 2这里选择2你也可以选择其他数字引脚但代码中需相应修改。提示为了信号稳定建议在DHT11的DATA引脚和VCC之间连接一个4.7kΩ - 10kΩ的上拉电阻。好消息是大多数DHT11模块已经将这个电阻集成在板子上了如果你的模块有3个引脚通常已内置电阻无需额外添加。如果是单独的传感器元件则必须自己加这个电阻。第二步连接MQ-135模块MQ-135模块通常有4个引脚VCC- 连接到Arduino的5V引脚。GND- 连接到Arduino的GND引脚。DO-悬空不接。这是数字输出引脚当气体浓度超过模块上电位器设定的阈值时会输出低电平。本项目我们使用模拟输出所以不用它。AO- 连接到Arduino的模拟引脚 A0。这是我们读取空气质量数据的核心引脚。第三步整体检查连接完成后你的硬件连接应该如下图所示文字描述Arduino的5V引脚同时连接到DHT11和MQ-135的VCC。Arduino的GND引脚同时连接到DHT11和MQ-135的GND。DHT11的DATA引脚连接到Arduino数字引脚2。MQ-135的AO引脚连接到Arduino模拟引脚A0。上电前最后检查确保所有连接牢固没有松动。确认电源正负极VCC和GND没有接反。将Arduino通过USB线连接到电脑。3.3 关于MQ-135预热的重要说明MQ-135传感器有一个非常关键的特性需要预热。首次上电或长时间断电后重新上电传感器的敏感材料需要一段时间通常为24-48小时才能达到稳定状态。在预热期间其输出值会持续漂移此时读取的数据是无效的。实操建议在开始编写和测试代码之前先将连接好的MQ-135模块单独通电放在一个通风良好的地方预热至少24小时。你可以在这期间先完成DHT11部分的代码测试和硬件搭建。忽略预热步骤是很多人拿到“不准”数据的首要原因。4. 软件编程从读取数据到估算CO2浓度硬件准备就绪预热也在进行中现在我们来攻克代码部分。代码的逻辑分为三个层次1. 读取原始数据2. 处理DHT11数据3. 处理并转换MQ-135数据。4.1 开发环境准备与库安装确保你已安装Arduino IDE1.8.x或2.0版本均可。安装DHT传感器库。在IDE中点击“工具” - “管理库…”在搜索框中输入“DHT sensor library”找到由Adafruit维护的库进行安装。这个库同时支持DHT11、DHT22等型号。由于这个库依赖于Adafruit的统一传感器驱动库系统可能会提示你一并安装Adafruit Unified Sensor库请同意安装。4.2 核心代码逐行解析下面是一个完整的、带有详细注释的示例代码。你可以直接复制到Arduino IDE中但更重要的是理解每一行代码背后的意图。// 引入必要的库 #include DHT.h // 1. 定义引脚和传感器类型 #define DHTPIN 2 // DHT11数据线连接的Arduino数字引脚 #define DHTTYPE DHT11 // 指定使用的传感器类型为DHT11 #define MQ135_PIN A0 // MQ-135模拟输出连接的Arduino模拟引脚 // 2. 初始化DHT传感器对象 DHT dht(DHTPIN, DHTTYPE); // 3. 全局变量声明 float temperature 0.0; // 存储温度值 float humidity 0.0; // 存储湿度值 int mq135_raw 0; // 存储MQ-135的原始模拟读数0-1023 float rzero 0.0; // 存储计算出的R0值在清洁空气中的传感器电阻 float corrected_ppm 0.0; // 存储估算出的CO2浓度ppm // 4. MQ-135相关校准参数这些值需要根据你的具体环境和传感器进行调整 // 在清洁空气中测得的传感器电阻R0的参考值。这是一个初始估计值。 #define RLOAD 10.0 // 模块上的负载电阻值单位千欧姆常见为10K #define PARA 116.6020682 // 用于ppm计算的经验参数A #define PARB 2.769034857 // 用于ppm计算的经验参数B #define CORA 0.00035 // 温度修正系数A示例值需校准 #define CORB 0.02718 // 温度修正系数B示例值需校准 #define CORC 1.39538 // 温度修正系数C示例值需校准 #define CORD 0.0018 // 湿度修正系数D示例值需校准 // 计算传感器电阻Rs的函数 float getResistance(int raw_adc) { // 将ADC值0-1023转换为电压0-5V再根据分压公式计算Rs // 公式: Rs (Vc - Vrl) / Vrl * Rload其中Vrl raw_adc * (5.0 / 1023.0) float vrl raw_adc * (5.0 / 1023.0); // 防止除零错误 if (vrl 0) vrl 0.001; float rs (5.0 - vrl) / vrl * RLOAD; return rs; } // 根据Rs/R0的比值估算CO2 ppm的函数使用对数拟合模型 float getPPM(float rsr0_ratio, float temp_c, float humi) { // 首先进行温湿度补偿这是一个简化的线性补偿模型实际关系更复杂 float correction CORA * temp_c CORB * humi CORC; float corrected_ratio rsr0_ratio / correction; // 使用经验公式 ppm PARA * (Rs/R0)^(PARB) 进行估算 // 注意这个公式是基于特定气体如CO2在特定条件下的拟合仅供参考 float ppm PARA * pow(corrected_ratio, -PARB); return ppm; } void setup() { // 初始化串口通信用于向电脑发送数据 Serial.begin(9600); Serial.println(室内环境监测系统启动...); // 启动DHT传感器 dht.begin(); // 给传感器一点稳定的时间 delay(2000); // 初始校准在通风良好的室外或已知空气洁净的环境下读取一批数据求平均得到初始R0 Serial.println(正在进行初始R0校准请确保传感器处于洁净空气中...); long sum 0; for (int i 0; i 100; i) { sum analogRead(MQ135_PIN); delay(10); } float avg_raw sum / 100.0; float rs_air getResistance((int)avg_raw); rzero rs_air / 9.8; // 9.8是MQ-135在洁净空气中对于CO2的典型Rs/R0比值来自数据手册 Serial.print(初始R0计算值: ); Serial.println(rzero); } void loop() { // 1. 读取DHT11数据需要至少2秒的间隔 humidity dht.readHumidity(); temperature dht.readTemperature(); // 读取摄氏温度 // 检查DHT11读数是否有效 if (isnan(humidity) || isnan(temperature)) { Serial.println(读取DHT11数据失败); // 可以选择在此处重置传感器或等待 } else { // 2. 读取MQ-135原始模拟值 mq135_raw analogRead(MQ135_PIN); // 3. 计算当前传感器电阻Rs float rs_current getResistance(mq135_raw); // 4. 计算Rs/R0比值 float ratio rs_current / rzero; // 5. 估算CO2浓度使用温湿度进行粗略补偿 corrected_ppm getPPM(ratio, temperature, humidity); // 6. 通过串口打印所有数据 Serial.print(温度: ); Serial.print(temperature); Serial.print( °C\t湿度: ); Serial.print(humidity); Serial.print( %\t); Serial.print(MQ-135原始值: ); Serial.print(mq135_raw); Serial.print(\tRs: ); Serial.print(rs_current); Serial.print(Kohm\tRs/R0: ); Serial.print(ratio); Serial.print(\t估算CO2浓度: ); Serial.print(corrected_ppm); Serial.println( ppm); } // 每次循环延迟3秒避免DHT11读取过于频繁同时控制数据刷新率 delay(3000); }4.3 代码关键逻辑与校准难点剖析DHT11部分相对直接库函数readTemperature()和readHumidity()已经帮我们完成了所有底层通信和数据解析。MQ-135部分是核心难点主要集中在校准获取原始电阻Rs代码中的getResistance函数将ADC值转换成了传感器表面的电阻值Rs。这是所有计算的基础。理解R0零点电阻R0是传感器在特定洁净空气环境下的电阻值。它是一个基准值会随着传感器老化、环境温湿度变化而缓慢漂移。代码中setup()函数里做的“初始校准”就是试图获取一个初始的R0。你必须将传感器放在通风良好的室外或已知空气新鲜的地方进行这一步。Rs/R0比值这个比值直接反映了空气的污染程度。比值越小说明Rs越小导电性越好空气污染越严重。从比值到ppm最不精确的一步getPPM函数中的公式ppm A * (Rs/R0)^B是一个经验公式。参数A和B代码中的PARA和PARB是从传感器数据手册中针对特定气体如CO2拟合出来的。这意味着用它算出的ppm值是一个基于CO2响应的“估算值”并非真实CO2浓度。它更准确的叫法应该是“CO2当量浓度”或“空气质量指数”。温湿度补偿MQ-135的灵敏度受环境温湿度影响很大。代码中的CORACORBCORCCORD就是尝试进行线性补偿的参数。这些参数需要你自己通过实验校准在相同气体浓度下记录不同温湿度时的传感器读数然后拟合出补偿系数。对于要求不高的应用可以暂时使用示例值或忽略此步骤但要知道这会是误差的主要来源之一。所以整个流程可以总结为读取电压 - 计算电阻Rs - 与基准R0比较得到比值 - 通过经验公式和补偿将比值转换为一个具有参考意义的“空气质量数值”。5. 系统调试、数据解读与优化建议代码上传成功后打开串口监视器波特率设置为9600你应该能看到数据开始滚动输出。但这只是开始如何让这些数据变得有意义才是项目的价值所在。5.1 串口数据解读与初步判断你会看到类似这样的输出温度: 25.30 °C 湿度: 45.00 % MQ-135原始值: 320 Rs: 22.50 Kohm Rs/R0: 0.85 估算CO2浓度: 650 ppm温度/湿度一目了然判断环境舒适度。MQ-135原始值范围0-1023。这个值越低表示传感器导电性越强空气污染可能性越高。在洁净空气中这个值通常较高例如400以上。Rs/R0比值这是核心指标。比值接近1表示空气质量接近你校准时的洁净空气状态。比值显著小于1例如降到0.5以下说明空气质量变差。你可以为这个比值设定一个阈值比如0.7当低于阈值时触发报警点亮LED或蜂鸣器。估算CO2浓度如前所述这是一个估算值。但它提供了一个相对量纲。通常室外新鲜空气的CO2浓度在400-450 ppm左右。室内如果浓度持续超过1000 ppm就可能有人感到困倦、注意力不集中超过2000 ppm则明显需要通风。你可以用这个估算值作为是否需要开窗的参考。5.2 常见问题排查踩坑记录DHT11读取失败返回NaN检查接线确保VCC GND DATA连接正确且牢固。DATA引脚是否接了上拉电阻模块内置或外接。检查引脚号代码中的DHTPIN定义是否与实际连接的数字引脚一致。检查延时loop()函数中的delay是否足够长建议至少2秒。DHT11两次读取之间需要时间恢复。电源问题尝试单独给DHT11模块供电或换一个5V电源。MQ-135数值不变或变化极小预热不足这是最常见的原因。确保传感器已预热24小时以上。环境空气流动差传感器需要暴露在流动的空气中将其放在封闭空间或靠近死角响应会非常迟钝。引脚接错确认连接的是模拟输出AO引脚而不是数字输出DO引脚。估算的CO2 ppm值明显不合理如始终为几万ppmR0校准错误setup()中的初始校准必须在真正洁净的空气中进行。在室内校准R0会偏小导致计算出的ppm值异常偏高。经验参数不适用代码中的PARA和PARB是示例值。不同批次、不同厂商的MQ-135传感器特性有差异。你需要根据数据手册或通过与其他可靠传感器对比来校准这两个参数。忽略温湿度补偿在高湿或高温环境下如果不补偿读数会严重偏离。5.3 项目优化与功能扩展思路基础功能实现后你可以考虑以下方向进行升级让项目变得更实用增加本地显示连接一个I2C接口的OLED屏幕如0.96寸 SSD1306实时显示温度、湿度和空气质量状态如“优”、“良”、“差”摆脱对电脑串口的依赖。设定阈值报警连接一个蜂鸣器和一个LED。当估算的CO2浓度超过设定阈值如1000ppm时蜂鸣器响LED亮起实现物理报警。数据记录与可视化SD卡模块将数据以CSV格式保存到SD卡中便于长期分析和导出。无线传输将主控换成ESP8266或ESP32通过Wi-Fi将数据发送到私有服务器如用Python Flask搭建、开源物联网平台如ThingsBoard或直接推送至手机App如Blynk。这是将项目从“玩具”升级为“实用工具”的关键一步。多传感器融合与算法优化引入一个真正的CO2传感器如SCD30或MH-Z19B与MQ-135进行数据对比可以更准确地标定你的MQ-135甚至尝试用机器学习算法建立更精确的估算模型。结合PM2.5传感器如GP2Y1014AU0F或更先进的激光传感器打造一个更全面的室内空气质量监测站。构建自动化联动系统通过继电器模块控制风扇或新风系统的开关当空气质量差时自动开启通风设备实现简单的智能家居控制。这个基于MQ-135和DHT11的室内气候监测项目就像一把打开硬件世界和物联网大门的钥匙。它涉及了传感器原理、模拟数字信号、微控制器编程、数据校准等多个基础知识点。通过动手实践、调试问题、优化改进你获得的远不止一个能显示数字的小设备而是一整套解决实际问题的工程化思维方法。