Embedded Development Gas Sensor Practice: MQ Series for Alcohol, Smoke, and Carbon Monoxide Detection - Safety Monitoring DIY Solution
Why Are MQ Series Sensors Still in Use?
MQ series gas sensors have been around for over twenty years - cheap, easy to buy, and simple to use. Although their accuracy can’t match industrial-grade equipment, they’re perfectly adequate for home smoke alarms, alcohol detection, and carbon monoxide monitoring.
Today I’ll walk through the three most commonly used sensors with Arduino: MQ-2 (smoke/combustible gas), MQ-3 (alcohol), and MQ-7 (carbon monoxide), covering everything from wiring to calibration to alarm systems.
Hardware List
| Module | Model | Price (approx.) | Description |
|---|---|---|---|
| Gas sensor | MQ-2 | ¥8-15 | Smoke, LPG, methane detection |
| Gas sensor | MQ-3 | ¥6-12 | Alcohol/ethanol detection |
| Gas sensor | MQ-7 | ¥10-18 | Carbon monoxide detection |
| Main board | Arduino Uno R3 | ¥15-25 | ESP32 also works |
| Breadboard | 830 holes | ¥5-10 | For wiring |
| Buzzer | Active 5V | ¥2-5 | Alarm sound |
| LED | 1 each of red/yellow/green | ¥0.5 | Status indication |
| Resistors | 220Ω + 10kΩ | ¥1 | Current limiting and voltage divider |
| Jumper wires | Male-to-male/Male-to-female | ¥3 | Connection wires |
Total cost: about ¥50-90, cheaper than buying a finished smoke alarm.
Sensor Principle Quick Overview
The core of MQ series is a SnO₂ (tin dioxide) sensitive material whose resistance changes with surrounding gas concentration when heated:
-
In clean air, oxygen adsorbs on the SnO₂ surface, resistance is higher
-
When encountering reducing gases (smoke, alcohol, CO), oxygen is consumed, resistance drops
-
The higher the gas concentration, the lower the resistance, the higher the output voltage
Key parameter: heating time. Each MQ sensor needs to preheat for 24-48 hours after power-on to reach stable state (some models can be shortened to a few minutes for quick testing, but accuracy will be compromised). In actual projects, it’s recommended to preheat for at least 30 minutes before starting to read values.
Wiring Scheme
The wiring for all three sensors is basically the same, all have 4 pins: VCC, GND, AOUT (analog output), DOUT (digital output, some modules have it).
Arduino Uno
├── 5V ────→ Sensor VCC
├── GND ────→ Sensor GND
├── A0 ────→ Sensor AOUT (analog signal)
├── D2 ────→ Buzzer positive (through 220Ω resistor)
└── GND ────→ Buzzer negative
If you’re using a module with a comparator (most on Taobao have it), there will be an additional DOUT pin, which can adjust the digital threshold through the onboard potentiometer, directly outputting high/low levels. However, I recommend using AOUT analog output for higher precision and more flexible threshold adjustment in code.
Complete Code
// MQ Series Gas Sensors - Multi-channel Detection + Alarm System
// Supports MQ-2 (smoke), MQ-3 (alcohol), MQ-7 (carbon monoxide)
#define MQ2_PIN A0 // MQ-2 smoke sensor
#define MQ3_PIN A1 // MQ-3 alcohol sensor
#define MQ7_PIN A2 // MQ-7 carbon monoxide sensor
#define BUZZER_PIN 2 // Buzzer
#define LED_RED 3 // Red LED - danger
#define LED_YELLOW 4 // Yellow LED - warning
#define LED_GREEN 5 // Green LED - normal
// Alarm thresholds (need to be adjusted based on actual calibration)
#define MQ2_SMOKE_THRESHOLD 300 // MQ-2 smoke alarm value
#define MQ3_ALCOHOL_THRESHOLD 250 // MQ-3 alcohol alarm value
#define MQ7_CO_THRESHOLD 200 // MQ-7 CO alarm value
// Preheat time (milliseconds) - sensors need time to stabilize
#define WARMUP_TIME 180000 // 3 minutes minimum preheat
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);
// All LEDs off
digitalWrite(LED_RED, LOW);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_GREEN, LOW);
digitalWrite(BUZZER_PIN, LOW);
warmupStart = millis();
Serial.println("=== MQ Gas Sensor System Starting ===");
Serial.print("Preheating... Estimated ");
Serial.print(WARMUP_TIME / 60000);
Serial.println(" minutes");
}
void loop() {
// Preheat check
if (!sensorReady) {
unsigned long elapsed = millis() - warmupStart;
if (elapsed >= WARMUP_TIME) {
sensorReady = true;
Serial.println("✅ Preheat complete, sensors ready!");
digitalWrite(LED_GREEN, HIGH);
} else {
Serial.print("Preheat progress: ");
Serial.print(elapsed / 1000);
Serial.print(" / ");
Serial.print(WARMUP_TIME / 1000);
Serial.println(" seconds");
delay(5000);
return;
}
}
// Read sensor values
int mq2Value = analogRead(MQ2_PIN);
int mq3Value = analogRead(MQ3_PIN);
int mq7Value = analogRead(MQ7_PIN);
// Print data
Serial.print("MQ-2(Smoke): ");
Serial.print(mq2Value);
Serial.print(" | MQ-3(Alcohol): ");
Serial.print(mq3Value);
Serial.print(" | MQ-7(CO): ");
Serial.println(mq7Value);
// Determine status and control output
bool alarm = false;
bool warning = false;
// Smoke detection
if (mq2Value > MQ2_SMOKE_THRESHOLD) {
Serial.println("⚠️ Smoke detected!");
alarm = true;
} else if (mq2Value > MQ2_SMOKE_THRESHOLD * 0.7) {
warning = true;
}
// Alcohol detection
if (mq3Value > MQ3_ALCOHOL_THRESHOLD) {
Serial.println("⚠️ Alcohol vapor detected!");
alarm = true;
} else if (mq3Value > MQ3_ALCOHOL_THRESHOLD * 0.7) {
warning = true;
}
// Carbon monoxide detection
if (mq7Value > MQ7_CO_THRESHOLD) {
Serial.println("🚨 Carbon monoxide detected! Danger!");
alarm = true;
} else if (mq7Value > MQ7_CO_THRESHOLD * 0.7) {
warning = true;
}
// Control LEDs and buzzer
if (alarm) {
digitalWrite(LED_RED, HIGH);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_GREEN, LOW);
digitalWrite(BUZZER_PIN, HIGH); // Buzzer sounds
} 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); // Read once every 2 seconds
}
Calibration Method: This Step Cannot Be Skipped
MQ sensor values are not absolute, greatly affected by temperature, humidity, and individual sensor differences. Calibration is mandatory.
Method 1: Clean Air Baseline Method (Recommended for Beginners)
// Read 100 times in known clean air, take average as baseline
float getBaseline(int pin) {
long sum = 0;
for (int i = 0; i < 100; i++) {
sum += analogRead(pin);
delay(10);
}
return sum / 100.0;
}
void calibrate() {
Serial.println("Calibrating... Please ensure clean air environment");
delay(5000);
float mq2Baseline = getBaseline(MQ2_PIN);
float mq3Baseline = getBaseline(MQ3_PIN);
float mq7Baseline = getBaseline(MQ7_PIN);
Serial.print("MQ-2 Baseline: "); Serial.println(mq2Baseline);
Serial.print("MQ-3 Baseline: "); Serial.println(mq3Baseline);
Serial.print("MQ-7 Baseline: "); Serial.println(mq7Baseline);
}
Method 2: Standard Gas Calibration (Professional)
If you have access to standard gas (like 50ppm CO standard gas):
// Calculate sensitivity coefficient
float calculateSensitivity(int pin, float knownPPM) {
float sensorValue = analogRead(pin);
float baseline = getBaseline(pin);
// Rs/R0 ratio
float rs_r0 = (1023.0 - sensorValue) / (1023.0 - baseline);
// Calculate sensitivity coefficient (specific formula varies by sensor model)
// Take MQ-7 as example: lg(Rs/R0) = -0.35 * lg(ppm) + 0.55
float sensitivity = pow(10, (-0.35 * log10(knownPPM) + 0.55));
return sensitivity;
}
Threshold Setting Strategy
Don’t just copy threshold values from datasheets! Adjust based on actual environment:
// Dynamic threshold adjustment
void adjustThresholds() {
// Read current environment baseline
float currentBaseline = getBaseline(MQ2_PIN);
// Set threshold as 2x baseline (adjustable)
#define THRESHOLD_MULTIPLIER 2.0
int newThreshold = currentBaseline * THRESHOLD_MULTIPLIER;
Serial.print("Adjusted MQ-2 threshold: ");
Serial.println(newThreshold);
}
Recommended threshold ranges:
| Sensor | Clean air baseline | Warning threshold | Alarm threshold |
|---|---|---|---|
| MQ-2 | 100-150 | 250-300 | 400+ |
| MQ-3 | 80-120 | 200-250 | 350+ |
| MQ-7 | 50-100 | 150-200 | 300+ |
Power Supply Considerations
MQ sensor heating element consumes considerable power:
- Single sensor heating current: 80-120mA
- Three sensors simultaneously: 240-360mA
Power supply recommendations:
- Use independent 5V/2A power supply
- Don’t power from USB (current insufficient)
- Add 100μF electrolytic capacitor at power input for filtering
// Power management: heat sensors sequentially
void powerManagement() {
// Control sensor power via MOSFET
digitalWrite(MQ2_POWER, HIGH);
delay(5000); // MQ-2 heats for 5 seconds
int mq2Value = analogRead(MQ2_PIN);
digitalWrite(MQ2_POWER, LOW);
digitalWrite(MQ3_POWER, HIGH);
delay(5000);
int mq3Value = analogRead(MQ3_PIN);
digitalWrite(MQ3_POWER, LOW);
// ...
}
Home Assistant Integration
Upload data to MQTT server for Home Assistant monitoring:
#include <WiFi.h>
#include <PubSubClient.h>
// WiFi and MQTT configuration
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);
}
Add in Home Assistant’s configuration.yaml:
sensor:
- platform: mqtt
name: "Kitchen Smoke Sensor"
state_topic: "sensor/gas/mq2"
unit_of_measurement: "ppm"
device_class: "gas"
Summary
Although MQ series sensors are old, they excel in being cheap and easy to use. Perfectly adequate for home safety monitoring, laboratory gas detection, or simple DIY projects.
Core points review:
-
Preheat first, at least 30 minutes
-
Must calibrate, clean air baseline is the simplest method
-
Power supply must be stable, heating element consumes significant power
-
Thresholds need adjustment based on actual environment, don’t just copy datasheets
-
For safety-related applications (especially CO detection), recommend dual protection with commercial equipment
Hope this blog post is helpful to you!