The MQ-2 detects LPG, smoke and other combustible gases with a heated tin-oxide element. Its output voltage rises as gas reaches it, which makes it the usual sensor for kitchen gas-leak and smoke projects.
The ESP32 DevKit V1 needs two changes compared with an Uno: the heater runs from VIN (5 V), and the analog output goes through a 10 kΩ / 20 kΩ divider into D34, an ADC1 pin that still works once Wi-Fi is on. The sketch warms the heater for a minute, records a clean-air baseline, and lights the blue LED when the reading rises well above it.
Use it to learn and to experiment. For a real cylinder in a real kitchen, fit a certified LPG detector as well.
Wiring
| ESP32 DevKit V1 | MQ-2 gas sensor module | Wire | Notes |
|---|---|---|---|
| VIN | VCC | red | The heater needs 5 V and about 150 mA — VIN carries USB 5 V. 3V3 won’t heat it properly. |
| GND | GND | black | |
| D34 | AO | yellow | Via 10 kΩ / 20 kΩ divider — 10 kΩ from AO to D34, 20 kΩ from D34 to GND: 5 V at AO becomes 3.33 V at the pin. The sketch multiplies back by 1.5. |
5 V (heater: about 150 mA) part, 3.3 V board. The heater needs 5 V. AO can reach about 5 V, so on ESP32 and Pico feed AO through a 10 kΩ / 20 kΩ divider (or similar) to stay under 3.3 V. DO is driven by the comparator from VCC — divide it too on 3.3 V boards.
Board quirks
- AO can reach about 5 V in heavy gas, which is above the ESP32’s 3.3 V limit. The divider is not optional on this board. Use 10 kΩ / 20 kΩ rather than 1 kΩ / 2 kΩ: the module’s load resistor is only a few kΩ, and a low-value divider would pull its reading down.
- D34 is on ADC1, so readings keep working when you later add Wi-Fi alerts. ADC2 pins (D4, D13, D25–D27…) return nothing while Wi-Fi is on. D34 is input-only, which is all an analog input needs.
analogReadMilliVolts()uses the chip’s factory calibration (core 3.x). With the default 11 dB attenuation the useful range is roughly 0.15–3.1 V; above that the reading flattens. The divider keeps a 4.5 V AO at 3.0 V, inside the good part.- DO is the comparator output and swings to 5 V. The sketch doesn’t use it; if you want it, it needs its own divider.
- A new sensor needs 24–48 hours of powered burn-in before readings settle, and a few minutes of warm-up at every power-on. Readings are relative — the MQ-2 reacts to LPG, smoke, alcohol, methane and hydrogen alike, so treat it as “something changed”, never as calibrated ppm or a certified LPG alarm.
Code
// MQ-2 gas/smoke module on an ESP32 DevKit V1.
// VCC → VIN (5 V heater), GND → GND, AO → 10 kΩ → D34 (GPIO34), 20 kΩ from D34 to GND.
// The divider scales AO (0–~5 V) to 2/3 so the ESP32 pin stays under 3.3 V.
// Indicative only — this is not a certified gas or smoke alarm.
constexpr uint8_t MQ_PIN = 34; // ADC1_CH6: works with Wi-Fi on, input-only
constexpr uint8_t LED_PIN = 2; // on-board blue LED
constexpr float DIVIDER = 1.5f; // (10 kΩ + 20 kΩ) / 20 kΩ
constexpr float RISE_ALARM_V = 0.6f; // alarm when AO rises this far above clean-air baseline
float baselineV = 0;
float readAoVolts() {
uint32_t sum = 0;
for (int i = 0; i < 16; i++) sum += analogReadMilliVolts(MQ_PIN); // calibrated mV at the pin
return (sum / 16.0f) / 1000.0f * DIVIDER; // volts at the module's AO
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
Serial.println("MQ-2 on GPIO34 — heater warming up (60 s)");
for (int s = 60; s > 0; s -= 10) {
Serial.printf(" %d s…\n", s);
delay(10000);
}
float sum = 0;
for (int i = 0; i < 10; i++) {
sum += readAoVolts();
delay(500);
}
baselineV = sum / 10;
Serial.printf("Clean-air baseline: AO %.2f V\n", baselineV);
}
void loop() {
float v = readAoVolts();
bool alarm = v > baselineV + RISE_ALARM_V;
digitalWrite(LED_PIN, alarm);
Serial.printf("AO %.2f V (+%.2f V over baseline)%s\n", v, v - baselineV, alarm ? " GAS/SMOKE DETECTED" : "");
delay(1000);
}// MQ-2 gas/smoke module on an ESP32 DevKit V1.
// VCC → VIN (5 V heater), GND → GND, AO → 10 kΩ → D34 (GPIO34), 20 kΩ from D34 to GND.
// The divider scales AO (0–~5 V) to 2/3 so the ESP32 pin stays under 3.3 V.
// Indicative only — this is not a certified gas or smoke alarm.
constexpr uint8_t MQ_PIN = 34; // ADC1_CH6: works with Wi-Fi on, input-only
constexpr uint8_t LED_PIN = 2; // on-board blue LED
constexpr float DIVIDER = 1.5f; // (10 kΩ + 20 kΩ) / 20 kΩ
constexpr float RISE_ALARM_V = 0.6f; // alarm when AO rises this far above clean-air baseline
float baselineV = 0;
float readAoVolts() {
uint32_t sum = 0;
for (int i = 0; i < 16; i++) sum += analogReadMilliVolts(MQ_PIN); // calibrated mV at the pin
return (sum / 16.0f) / 1000.0f * DIVIDER; // volts at the module's AO
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
Serial.println("MQ-2 on GPIO34 — heater warming up (60 s)");
for (int s = 60; s > 0; s -= 10) {
Serial.printf(" %d s…\n", s);
delay(10000);
}
float sum = 0;
for (int i = 0; i < 10; i++) {
sum += readAoVolts();
delay(500);
}
baselineV = sum / 10;
Serial.printf("Clean-air baseline: AO %.2f V\n", baselineV);
}
void loop() {
float v = readAoVolts();
bool alarm = v > baselineV + RISE_ALARM_V;
digitalWrite(LED_PIN, alarm);
Serial.printf("AO %.2f V (+%.2f V over baseline)%s\n", v, v - baselineV, alarm ? " GAS/SMOKE DETECTED" : "");
delay(1000);
}Expected serial output
MQ-2 on GPIO34 — heater warming up (60 s)
60 s…
50 s…
Clean-air baseline: AO 0.74 V
AO 0.75 V (+0.01 V over baseline)
AO 1.92 V (+1.18 V over baseline) GAS/SMOKE DETECTEDMQ-2 on GPIO34 — heater warming up (60 s) 60 s… 50 s… Clean-air baseline: AO 0.74 V AO 0.75 V (+0.01 V over baseline) AO 1.92 V (+1.18 V over baseline) GAS/SMOKE DETECTED
Common errors
AO reads 0 V all the time
The heater isn’t powered (the module’s power LED is off), or the divider is wired the wrong way round: 10 kΩ goes between AO and D34, 20 kΩ between D34 and GND.
Reading keeps climbing for hours
Normal during burn-in of a new sensor. Leave it powered for a day, then take the baseline again (press EN to restart the sketch).
ESP32 resets when the MQ-2 is plugged in
The cold heater draws a surge on top of the ESP32’s own current. Use a good USB cable and port, or feed the MQ-2 from a separate 5 V supply with its GND joined to the ESP32’s GND.