为什么 MQ 系列传感器还在用?
MQ 系列气体传感器诞生已经二十多年了,便宜、好买、容易上手。虽然精度比不上工业级设备,但做个家庭烟雾报警、酒精检测、一氧化碳监控,完全够用。
今天我用 Arduino 把 MQ-2(烟雾/可燃气体)、MQ-3(酒精)、MQ-7(一氧化碳)三个最常用的传感器跑一遍,从接线到校准到报警系统,一次讲清楚。
硬件清单
| 模块 | 型号 | 价格(约) | 说明 |
|---|---|---|---|
| 气体传感器 | MQ-2 | ¥8-15 | 烟雾、液化气、甲烷检测 |
| 气体传感器 | MQ-3 | ¥6-12 | 酒精/乙醇检测 |
| 气体传感器 | MQ-7 | ¥10-18 | 一氧化碳检测 |
| 主控板 | Arduino Uno R3 | ¥15-25 | 也可用 ESP32 |
| 面包板 | 830 孔 | ¥5-10 | 接线用 |
| 蜂鸣器 | 有源 5V | ¥2-5 | 报警声音 |
| LED | 红/黄/绿各 1 | ¥0.5 | 状态指示 |
| 电阻 | 220Ω + 10kΩ | ¥1 | 限流和分压 |
| 杜邦线 | 公对公/公对母 | ¥3 | 连接线 |
总成本:约 ¥50-90,比买一个成品烟雾报警器还便宜。
传感器原理速讲
MQ 系列的核心是一个 SnO₂(二氧化锡)敏感材料,加热后电阻会随周围气体浓度变化:
- 清洁空气中,SnO₂ 表面吸附氧气,电阻较高
- 遇到还原性气体(烟雾、酒精、CO),氧气被消耗,电阻下降
- 气体浓度越高,电阻越低,输出电压越高
关键参数:加热时间。 每个 MQ 传感器上电后需要预热 24-48 小时 才能达到稳定状态(有些型号可以缩短到几分钟用于快速测试,但精度会打折扣)。实际项目中建议至少预热 30 分钟再开始读数。
接线方案
三个传感器的接线方式基本一样,都是 4 个引脚:VCC、GND、AOUT(模拟输出)、DOUT(数字输出,部分模块带)。
Arduino Uno
├── 5V ────→ 传感器 VCC
├── GND ────→ 传感器 GND
├── A0 ────→ 传感器 AOUT(模拟信号)
├── D2 ────→ 蜂鸣器正极(通过 220Ω 电阻)
└── GND ────→ 蜂鸣器负极
如果你用的是带比较器的模块(淘宝上大部分都带),还会多一个 DOUT 引脚,可以通过板载电位器调节数字阈值,直接输出高/低电平。不过我建议用 AOUT 模拟输出,精度更高,可以在代码里灵活调整阈值。
完整代码
// MQ 系列气体传感器 - 多通道检测 + 报警系统
// 支持 MQ-2(烟雾)、MQ-3(酒精)、MQ-7(一氧化碳)
#define MQ2_PIN A0 // MQ-2 烟雾传感器
#define MQ3_PIN A1 // MQ-3 酒精传感器
#define MQ7_PIN A2 // MQ-7 一氧化碳传感器
#define BUZZER_PIN 2 // 蜂鸣器
#define LED_RED 3 // 红色 LED - 危险
#define LED_YELLOW 4 // 黄色 LED - 警告
#define LED_GREEN 5 // 绿色 LED - 正常
// 报警阈值(需要根据实际校准调整)
#define MQ2_SMOKE_THRESHOLD 300 // MQ-2 烟雾报警值
#define MQ3_ALCOHOL_THRESHOLD 250 // MQ-3 酒精报警值
#define MQ7_CO_THRESHOLD 200 // MQ-7 CO 报警值
// 预热时间(毫秒)- 传感器需要时间稳定
#define WARMUP_TIME 180000 // 3 分钟最小预热
unsigned long warmupStart;
bool sensorReady = false;
void setup() {
Serial.begin(115200);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
// 所有 LED 关闭
digitalWrite(LED_RED, LOW);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_GREEN, LOW);
digitalWrite(BUZZER_PIN, LOW);
warmupStart = millis();
Serial.println("=== MQ 气体传感器系统启动 ===");
Serial.print("预热中...预计 ");
Serial.print(WARMUP_TIME / 60000);
Serial.println(" 分钟");
}
void loop() {
// 预热检查
if (!sensorReady) {
unsigned long elapsed = millis() - warmupStart;
if (elapsed >= WARMUP_TIME) {
sensorReady = true;
Serial.println("✅ 预热完成,传感器就绪!");
digitalWrite(LED_GREEN, HIGH);
} else {
Serial.print("预热进度: ");
Serial.print(elapsed / 1000);
Serial.print(" / ");
Serial.print(WARMUP_TIME / 1000);
Serial.println(" 秒");
delay(5000);
return;
}
}
// 读取传感器值
int mq2Value = analogRead(MQ2_PIN);
int mq3Value = analogRead(MQ3_PIN);
int mq7Value = analogRead(MQ7_PIN);
// 打印数据
Serial.print("MQ-2(烟雾): ");
Serial.print(mq2Value);
Serial.print(" | MQ-3(酒精): ");
Serial.print(mq3Value);
Serial.print(" | MQ-7(CO): ");
Serial.println(mq7Value);
// 判断状态并控制输出
bool alarm = false;
bool warning = false;
// 烟雾检测
if (mq2Value > MQ2_SMOKE_THRESHOLD) {
Serial.println("⚠️ 检测到烟雾!");
alarm = true;
} else if (mq2Value > MQ2_SMOKE_THRESHOLD * 0.7) {
warning = true;
}
// 酒精检测
if (mq3Value > MQ3_ALCOHOL_THRESHOLD) {
Serial.println("⚠️ 检测到酒精蒸气!");
alarm = true;
} else if (mq3Value > MQ3_ALCOHOL_THRESHOLD * 0.7) {
warning = true;
}
// 一氧化碳检测
if (mq7Value > MQ7_CO_THRESHOLD) {
Serial.println("🚨 检测到一氧化碳!危险!");
alarm = true;
} else if (mq7Value > MQ7_CO_THRESHOLD * 0.7) {
warning = true;
}
// 控制 LED 和蜂鸣器
if (alarm) {
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_GREEN, LOW);
digitalWrite(BUZZER_PIN, HIGH); // 蜂鸣器响
} else if (warning) {
digitalWrite(LED_RED, LOW);
digitalWrite(LED_YELLOW, HIGH);
digitalWrite(LED_GREEN, LOW);
digitalWrite(BUZZER_PIN, LOW);
} else {
digitalWrite(LED_RED, LOW);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_GREEN, HIGH);
digitalWrite(BUZZER_PIN, LOW);
}
delay(2000); // 每 2 秒读取一次
}
校准方法:这一步不能省
MQ 传感器的数值不是绝对的,受温度、湿度、传感器个体差异影响很大。校准是必须的。
方法一:清洁空气基线法(推荐入门)
// 在已知清洁空气中读取 100 次,取平均值作为基线
float getBaseline(int pin) {
long sum = 0;
for (int i = 0; i < 100; i++) {
sum += analogRead(pin);
delay(10);
}
return sum / 100.0;
}
// 使用:基线值 × 1.5 作为警告阈值,× 2.0 作为报警阈值
float baseline = getBaseline(MQ2_PIN);
float warningThreshold = baseline * 1.5;
float alarmThreshold = baseline * 2.0;
方法二:已知浓度对比法(更精确)
如果你有标准气体或者已知浓度的测试源(比如打火机气体对于 MQ-2),可以建立浓度-电压曲线:
- 在清洁空气中记录读数(0 ppm 基准点)
- 用已知浓度的气体靠近传感器,记录读数
- 两点连线,估算中间浓度
浓度(ppm) = (当前读数 - 基线) × 系数
系数需要根据你的具体传感器和气体类型标定。MQ 传感器的数据手册里通常会给出 Rs/R₀ 比值与浓度的关系曲线,可以参考。
各传感器注意事项
MQ-2(烟雾/可燃气体)
- 检测对象:烟雾、液化气、甲烷、氢气
- 灵敏度:对丁烷和丙烷最敏感
- 典型应用:厨房烟雾报警、燃气泄漏检测
- 注意:对酒精也有反应,厨房和酒精测试环境要分开考虑
MQ-3(酒精)
- 检测对象:乙醇蒸气
- 灵敏度:对酒精非常敏感,低浓度即可检测
- 典型应用:酒驾检测模拟、酒精泄漏监控
- 注意:呼出气体中的酒精浓度较高,做"酒精测试仪"时需要加采样腔
MQ-7(一氧化碳)
- 检测对象:CO 气体
- 特殊要求:需要交替加热(高温 5V 和低电压 1.5V)来区分气体
- 典型应用:车库 CO 检测、取暖设备安全监控
- 注意:CO 无色无味,危险性高,建议配合商业 CO 报警器使用
常见问题排查
1. 读数一直很高,不变化
- 预热不够:传感器刚上电时读数会很高,等待至少 30 分钟
- 环境有干扰:远离香水、清洁剂、烹饪油烟
- 传感器老化:MQ 系列寿命约 1-2 年,长期不用的传感器可能失效
2. 读数波动很大
- 电源不稳定:MQ 传感器加热丝需要较大电流(约 150-170mA),用 Arduino 的 5V 引脚可能供电不足,建议用外部 5V 电源
- 模拟参考电压:默认参考 5V,如果供电电压有波动,可以用内部 1.1V 参考提高精度:
analogReference(INTERNAL); // 使用 1.1V 内部参考
3. 蜂鸣器一直响
- 阈值设置太低,用上面的基线法重新校准
- 环境中有持续的气体干扰源(比如附近有厨房或车库)
4. 数字输出 DOUT 不工作
- 检查板载电位器是否调节到位
- DOUT 的阈值是固定的(由电位器决定),如果电位器拧到底了可能永远输出高或低
进阶:接入 Home Assistant
如果你在做智能家居,可以把传感器数据通过 ESP32 发送到 Home Assistant:
#include
#include
// WiFi 和 MQTT 配置
const char* wifi_ssid = "YOUR_WIFI";
const char* mqtt_server = "192.168.1.100";
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
WiFi.begin(wifi_ssid, "YOUR_PASSWORD");
while (WiFi.status() != WL_CONNECTED) delay(500);
client.setServer(mqtt_server, 1883);
client.connect("gas-sensor");
}
void loop() {
if (client.connected()) {
int mq2Value = analogRead(MQ2_PIN);
client.publish("sensor/gas/mq2", String(mq2Value).c_str());
}
delay(5000);
}
在 Home Assistant 的 configuration.yaml 中添加:
sensor:
- platform: mqtt
name: "厨房烟雾传感器"
state_topic: "sensor/gas/mq2"
unit_of_measurement: "ppm"
device_class: "gas"
总结
MQ 系列传感器虽然古老,但胜在便宜好用。做家庭安全监控、实验室气体检测、或者简单的 DIY 项目,完全够用。
核心要点回顾:
- 上电先预热,至少 30 分钟
- 一定要校准,清洁空气基线是最简单的方法
- 电源要稳定,加热丝耗电不小
- 阈值要根据实际环境调整,不要照搬数据手册
- 安全相关的应用(尤其 CO 检测),建议配合商业设备双重保障
希望这篇博客文章对您有所帮助!