
1. 项目概述1.1 核心需求解析MQ-135气体传感器模块这个模块在Arduino玩家圈子里几乎是必玩的一个元件各大开发板套装里基本都能看到它的身影。它的定位是空气质量检测官方手册里写的是检测氨气、硫化物、苯系蒸汽、烟雾等有害气体说白了就是给室内空气健康状况把个脉。这个实验属于Arduino Uno入门序列里的第十五课难度适中卡在传感器入门和实际应用之间。你不需要懂复杂的电路分析也不用会化学只要照着接线、烧录代码、看串口输出就能直观地感受“空气里到底有什么”。它能解决的实际问题很直接新装修的房子甲醛有没有超标、厨房油烟浓度高不高、车里空气质量行不行这些都可以用MQ-135搭建一个简易监控装置来量化感知。适合谁来参考电子爱好者、创客教育场景下的学生和老师、以及想自己动手做空气监测小项目但缺乏经验的朋友。这篇文章我会从原理一路讲到代码、校准和坑点尽量把每一步的“为什么”也说清楚方便你不仅能复现还能举一反三。1.2 模块特性速览MQ-135本质上是一个模拟输出的半导体气体传感器它内部有一个加热丝和一个二氧化锡SnO₂半导体层。加热丝负责把传感器加热到工作温度半导体层在洁净空气中的电导率较低当还原性气体比如氨气、酒精蒸汽、烟雾接触到传感器表面时电导率会显著上升表现出来就是模块的模拟输出电压改变。常见参数我整理在下面参数典型值说明工作电压5V DC需要稳定供电纹波太大会影响读数加热功耗约800mW发热明显连续工作模块会温升模拟输出范围0~5V实际经模块分压后输出可直接接单片机ADC数字输出阈值可调板载电位器调节比较器触发电平预热时间24小时初次长期未用建议通电预热12小时以上检测浓度范围10~1000ppm依气体而定不同气体的响应曲线差异很大需要特别注意的是模块上除了模拟输出A0引脚还带了一个数字输出DO引脚板载的LM393比较器会把模拟信号和电位器设定的阈值做比较超过阈值就输出低电平。这个功能可以用来直接触发报警或者接继电器免去单片机参与。2. 硬件原理深度拆解2.1 传感器内部结构与工作机制MQ-135的核心部件是一个微型陶瓷管管壁覆盖着二氧化锡敏感层管内穿过一根镍铬合金加热丝。整个结构封装在带不锈钢网罩的塑料底座里网罩的作用是让气体自由进出同时隔离粉尘和机械损伤。工作流程可以这么理解加热丝通电后敏感层温度升到200℃~300℃之间此时空气中的氧分子会吸附在二氧化锡表面夺取半导体中的电子形成氧负离子导致表面电子浓度下降电导率降低。当还原性气体出现时这些气体会与表面的氧负离子发生反应把电子释放回半导体电导率升高。传感器就是通过测量电导率的变化来间接推算气体浓度的。用生活化的类比来说二氧化锡敏感层就像一个门卫氧气分子把门堵上了电子出不去电流就小还原性气体过来“敲门”把氧气拉走门开了电子涌出去电流就大了。Arduino读到的电压变化本质上就是这扇“门”被推开程度的外在表现。2.2 模块上为什么还要加一个比较器MQ-135也有很多不带模块的裸传感器版本只有四个引脚输出的是一个几kΩ到几百kΩ之间变化的电阻值需要自己搭分压电路才能接入ADC。而我们常用的模块版已经把外围电路全部做好了一路是分压后直接给到A0引脚另一路是经过LM393电压比较器转成数字开关信号从DO输出。这个设计很贴心也反映了模块设计者对不同使用场景的考量。A0模拟输出适合做定量分析比如你在串口监视器里看到数值随烟雾浓度明显上升这是“量”的概念DO数字输出则适合做阈值报警电位器一拧设好阈值浓度到了就翻电平完全不需要单片机做任何判断。实际项目里我经常两个都接模拟量用来记录趋势数字量用来触发蜂鸣器或者继电器。2.3 敏感度与交叉干扰的天然矛盾这里必须说一个很多人会踩的坑MQ-135并不是一个选择性很好的传感器。它对氨气、酒精、甲苯、烟雾都有响应这意味着你在厨房测到的浓度飙升可能是油烟也可能是料酒挥发甚至可能是燃气泄漏单凭MQ-135无法区分具体是哪种气体。网上有些教程信誓旦旦地说“MQ-135可以测甲醛”严格来说这是不严谨的。它的数据手册里确实提到了甲醛但响应曲线和灵敏度都比较有限而且交叉干扰严重。如果要精确测量某种特定气体的浓度需要的是电化学传感器或者更专业的PID光离子传感器成本要高出几个数量级。MQ-135合理的定位是“空气质量恶化趋势的指示器”而不是“某某气体浓度计”。理解这一点你在解读数据时就不会被误导。3. 硬件接线与准备工作3.1 物料清单开始接线之前先确认手头的材料是否齐全Arduino Uno开发板一块MQ-135气体传感器模块一个面包板和跳线若干蜂鸣器模块一个可选做报警实验用USB数据线一根电脑上的Arduino IDE建议1.8.19或2.x版本如果你手头是ESP32、Nano、Mega等其他开发板接线思路完全一样只是要注意部分板子的ADC参考电压不同比如ESP32的ADC是12位的0~3.3V直接接5V输出的MQ-135模块就需要分压这个后面会提到。3.2 接线步骤与引脚对应接线非常简单四根线搞定VCC接Arduino的5V引脚GND接GNDA0接Arduino的A0模拟输入引脚DO接Arduino的D2数字引脚不接也行先留着备用接线前务必断电操作所有跳线插好后再次核对确认没有插反或者短接然后再接USB线通电。这个习惯比任何技巧都重要我在实验室见过太多因为带电接插导致开发板烧毁的案例。面包板接线看起来是一根线插一个孔好像没什么技术含量但其实有很多细节。跳线颜色最好功能分明红色电源、黑色地线、黄色信号线养成这个习惯之后排查问题会省很多时间。另外接线时不要为了图省事把模块直接插在面包板最边缘传感器发热时间长了可能会让面包板变形。3.3 供电稳定性对读数的影响这里要特别强调供电问题。MQ-135的加热丝工作电流大约150mA单独看不算大但如果你的Arduino已经挂了LCD屏幕、多个传感器模块USB口供电就会吃紧5V电压被拉低传感器读数的基准值会发生变化你记录到的“正常值”和“报警值”都会漂移。我实测过用USB供电和用9V适配器供电的差异同样是洁净空气环境USB供电下A0读数是 90 左右换成适配器供电后稳定在 98。原因就是USB供电电压偏低导致模块的基准电压也偏低。如果你想做长期监测项目建议给Arduino用7~12V DC适配器供电或者干脆用独立的5V电源给传感器供电让两者电气隔离。如果只能USB供电那么至少确保基线校准和实际测量在相同的供电条件下进行不然数据对比的意义会大打折扣。4. 实验代码设计与解读4.1 基础读取代码——先让数据“看得见”第一步不是写复杂的校准算法而是先把传感器的原始电压值读出来确认硬件工作正常。下面是常用的基础代码int mq135AnalogPin A0; // 模拟输出接A0 int mq135DigitalPin 2; // 数字输出接D2 void setup() { Serial.begin(9600); pinMode(mq135DigitalPin, INPUT); } void loop() { int sensorValue analogRead(mq135AnalogPin); float voltage sensorValue * (5.0 / 1023.0); int digitalValue digitalRead(mq135DigitalPin); Serial.print(ADC原始值: ); Serial.print(sensorValue); Serial.print( | 电压值: ); Serial.print(voltage, 3); Serial.print( V | 数字输出: ); Serial.println(digitalValue LOW ? 触发 : 正常); delay(1000); }这段代码的逻辑很简单每一次循环读取一次模拟值换算成电压同时读取数字引脚的逻辑电平最后通过串口打印出来。analogRead返回的是一个0~1023的整数对应0~5V的电压区间这就是Arduino Uno的10位ADC分辨率。注意我用的分度是5.0/1023.0而不是1024.0这是为了和Arduino ADC的实际转换特性保持一致虽然差异很小但在做精细测量时这个习惯很重要。4.2 输出电压与PPM换算的理解很多教程到这里就让你直接用ADC值或者电压值来判定空气质量这属于“能用但粗糙”的水平。要稍微专业一点就需要理解气体浓度ppm和传感器输出电压之间的关系。MQ-135的数据手册给出了在不同气体中传感器电阻值Rs与洁净空气中基准电阻R0的比值曲线纵轴是Rs/R0横轴是浓度单位是ppm。这个关系在双对数坐标系里近似一条直线所以可以用数学公式拟合浓度。公式的基本形式如下ppm A * (Rs/R0)^B这里的A和B是两个常数取决于具体目标气体而且不同厂家的MQ-135特性曲线会略有差异手册上给出的值只能作为参考。Rs可以通过分压电路反推Rs (Vc - Vout) * RL / Vout其中Vc是模块供电电压5VVout是模拟输出引脚电压RL是负载电阻模块上一般取的是1kΩ。你可以从模块背面的丝印或者原理图确认具体阻值。不过说实话对于入门实验来说我建议先不急着做ppm换算。原因有两个第一没有标准气体标定算出来的数值误差可能达到±40%以上第二重点应该放在理解“浓度变化导致电压变化”这个物理过程上先把数据趋势看懂再去追求绝对浓度的准确性。4.3 阈值报警增强版代码在基础读取没问题之后可以加一个阈值报警的功能。利用数字输出口读取比较器的结果或者直接在代码里判断模拟值是否超过某个阈值。下面的代码用的是第二种方式因为这种方式更灵活你可以在串口监视器里实时调整阈值int threshold 150; // 阈值可根据环境调整 void setup() { Serial.begin(9600); pinMode(8, OUTPUT); // 接蜂鸣器或者LED } void loop() { int sensorValue analogRead(A0); if (sensorValue threshold) { digitalWrite(8, HIGH); Serial.println(警告空气质量异常); } else { digitalWrite(8, LOW); } Serial.print(当前ADC值: ); Serial.println(sensorValue); delay(500); }这里选150作为阈值只是一个示例。你在实际使用时应该先让传感器在目标环境里通电稳定一段时间记录洁净空气中的基线数值然后以基线值加上一定的裕量作为报警阈值。比如你在室内测到基线是90那就把阈值设在120~150之间比较合理既能避免误报又能及时捕捉到明显的浓度变化。4.4 为什么首次上电数据会“跳来跳去”新买的MQ-135模块第一次上电或者模块很长时间没用后重新通电前几分钟的读数会很不稳定数值持续下降或者上下跳动。这不是模块坏了而是敏感层表面的状态正在平衡。新传感器的敏感层表面可能吸附了水汽或者其他杂质加热丝需要时间把它们赶走另外表面氧吸附也需要时间达到动态平衡。所以拿到新模块后建议先通电老化24小时再用于正式测量如果等不了那么久至少也要预热10分钟以上等读数基本平稳后再记录数据。我自己的习惯是拿到新模块先通电放一晚上第二天再看数据就非常稳定。如果你在项目里用MQ-135一定把这个“预热”需求写进设计文档不然用户第一次开机看到跳变的数值会以为设备坏了。5. 数据校准与实际应用扩展5.1 单点校准与两点校准MQ-135出厂时并没有经过精确标定所以每块模块的绝对读数都可能不同——两块模块放在同样的环境里ADC值可能相差20%以上。这就要求在实际使用前进行校准。最简单的校准方法是单点校准在已知洁净空气的环境中比如通风良好的室外或者开着空气净化器的房间记录当前的ADC值把这个值作为基线记为R0对应值。之后测量就以此为参照读数明显高于基线时判定为异常。更可靠一点的是两点校准如果你能拿到已知浓度的标准气体在一个浓度点测量并记录数据然后改变浓度再测一次两点确定一条直线在双对数坐标下。这样计算出的换算公式会比直接套用数据手册准确很多。但标准气体对个人玩家来说不容易获得所以至少做单点校准并在记录数据时注明“相对值”而不是“绝对值”。5.2 看门狗逻辑——如何避免传感器失效导致的误判气敏传感器有一个通病长期使用后灵敏度会逐渐衰减。这意味着同一浓度的气体新模块的电压变化是100mV用了半年后可能只有60mV了。如果阈值是固定的就会出现“灵敏度下降→报警阈值形同虚设”的情况。一个实用的解决办法是在代码里加入“看门狗”机制持续监测传感器在无事件时的基线值如果发现基线值持续向报警阈值方向漂移超出了正常范围就提示用户“传感器可能需要校准或更换”。这个操作不复杂只需在代码里加一段定期采样求平均的逻辑和预设的初始基线做对比偏移超过一定比例就置一个标志位。比如新模块在洁净空气里基线是90使用一段时间后基线变成了120那你基本可以确定传感器衰减了或者被测环境已经不再洁净。在项目文档里把这个逻辑写清楚设备的使用寿命和可靠性都会有实质提升。5.3 扩展玩法联动OLED显示屏与继电器MQ-135模块只是整个监测系统的一个感知单元真正发挥价值需要和其他执行设备联动。这里我分享一个我实际做过的组合方案用于厨房油烟报警场景MQ-135模块负责检测烟雾/油烟浓度A0接Arduino的A0一块0.96寸OLED显示屏I2C接口显示实时ADC值和一个空气质量状态图标一个5V继电器模块串在厨房排风扇的电源线上Arduino根据MQ-135读数判断油烟浓度超过阈值AOLED显示“通风中”并开启继电器回落到阈值B以下一段时间后自动关闭这个项目里用到了“迟滞区间”的设计即开启阈值高于关闭阈值避免排风扇在临界点反复开关。具体来说我设的是开启阈值150关闭阈值90中间有60的差值空间。如果不加这个迟滞设计燃气灶火力稍微波动一下排风扇就会疯了一样来回切换机械寿命会急剧缩短。5.4 多节点组网的可能单个Arduino MQ-135能做的只是单点监测。如果你想把监测范围扩大——比如办公室不同工位、家里不同房间可以考虑给每套监测节点加上ESP8266或者ESP32模块通过WiFi把数据传到MQTT broker在Node-RED或者Home Assistant里做可视化大屏。这个方案我帮朋友做过一次五个节点分别放在客厅、厨房、卧室、书房和阳台每30秒上报一次数据。实际运行下来有两个意外收获一是能发现某些房间在特定时间段的异常波动比如阳台午后的数据明显偏高排查后发现是楼下装修的粉尘飘上来了二是多个节点的数据对比比单个节点的绝对值更有参考意义——同一时间几个房间的数据同时上升基本可以判定是外部空气输入而不是某个房间内的局部污染源。6. 常见问题与排查技巧实录6.1 问题速查表我在MQ-135的实验和项目开发中整理了一份问题排查表遇到问题可以对照着查现象可能原因排查方法串口无输出波特率不匹配、代码未上传成功检查Serial.begin(9600)是否与监视器一致读数一直为0A0接线错误、ADC引脚损坏换一个模拟引脚测试用万用表量模块A0电压读数一直为1023A0接错到5V或GND、模块未供电检查VCC和GND接线上电数值剧烈跳变传感器未预热通电老化24小时或至少预热10分钟基线数值逐渐升高传感器老化、环境存在持续污染源对比初始基线必要时更换传感器对气体没反应加热丝断、敏感层失效用万用表量加热丝两端电阻正常约30~40Ω数字输出一直不变电位器阈值设置不合适用螺丝刀微调电位器观察DO电平变化6.2 加热丝检查法——最容易忽略的硬件诊断MQ-135最常见的一个硬件故障就是加热丝烧断。判断方法非常简单通电后用万用表测量加热丝两端的直流电阻正常值在30Ω到40Ω之间如果测量结果是无穷大说明加热丝已经断了这个模块基本可以报废。另外模块正常工作时网罩位置会微微发热温度大约在40~60℃之间用手背靠近能明显感觉到热量。如果通电一段时间后网罩还是冷的那大概率是供电问题或者加热丝断路。这个触感检查法虽然不太精确但用来快速排除故障非常有效。6.3 温湿度漂移的修正思路MQ-135的敏感层对环境的温湿度也敏感同一个浓度的气体在夏天高湿环境下和冬天干燥环境下输出值会有差异。这意味着你在12月份校准好的基线到了7月份可能就不准了。要处理这个问题最简单的办法是给系统加一个DHT22温湿度传感器在代码里根据温湿度对MQ-135读数做修正。修正系数怎么来可以通过实验记录不同温湿度下传感器在洁净空气中的基线值然后用多项式拟合出修正函数。这个方法不算特别精确但对家用级别的空气监测场景完全够用。如果你做的是实验演示不追求长期数据的可比性那这个修正也可以省掉只要在记录数据时把温湿度信息一并记下来就行。6.4 电源纹波干扰的排查经验我遇到过一种比较隐蔽的问题传感器的读数每隔几秒就会出现一次无规律的尖峰持续时间很短。一开始我怀疑是环境里真有什么气体间歇性出现后来用示波器一测发现Arduino的5V电源线上有周期性的纹波尖峰时间点和那些异常读数完全吻合。原因是我当时用了一个质量不太好的USB电源适配器输出的纹波比较大。换了一个正规品牌的适配器之后所有异常读数都消失了。这个案例给我的教训是当传感器数据出现不明原因的小幅波动时不要急着怀疑传感器本身先检查供电是否干净。给传感器供电的电源线上并联一个100μF的电解电容和一个0.1μF的瓷片电容是一个简单有效的滤波方案。7. 写在最后的实操心得MQ-135这个模块你说它精度高、选择性好那是昧着良心夸它但你说它没用那也冤枉了它。它最大的价值在于用极低的成本让你直观地理解“气体浓度→敏感层电导率变化→电压信号→数字量化”这一整条传感链路是怎么走通的。几个我可以直接分享的心得第一凡是上了MQ-135的项目一定在代码里加上数据记录功能哪怕只是往SD卡里写个CSV。很多看似异常的数据波动放在更长的时间跨度里看反而是有价值的信息。第二用MQ-135做报警类项目宁可把阈值调高一点也要减少误报。误报几次之后用户就会对报警失去信任甚至把设备拆了这是我在实际项目中用教训换来的结论。第三传感器模块买到手之后第一件事不是接线是看丝印确认引脚定义。市面上的MQ-135模块出自不同厂家VCC和GND的位置甚至模拟输出的引脚编号排列可能有差异直接把红黑跳线随便插接反了可能直接把传感器烧掉。第四如果要做严谨一点的实验不要在代码里直接用5.0作为电压参考值。先用万用表实测自己Arduino板子的5V输出电压很多USB口实际输出只有4.7V左右把这个实测值代进代码数据会更接近真实情况。MQ-135的实验到这里就完整做完了。从最初点亮串口的第一行数据到最终搭出一个能自动联动排风扇的烟雾监控装置整个过程并不难但每一步都值得你亲手验证一遍。传感器这东西看再多的教程不如实际闻一次酒精棉球看数值变化的反应来得深刻。接下来你可以做的扩展方向有很多多传感器融合加上粉尘传感器和温湿度、无线数据上报、数据历史曲线记录——路子很宽慢慢玩。