ESP32 Air Quality Monitor

Environmental Monitoring Beginner Intermediate Advanced

Monitor indoor air quality with an ESP32, gas sensor, and fan or relay control — from a simple threshold alarm to a connected dashboard.

Overview

This beginner stage of ESP32 Air Quality Monitor focuses on the essentials: read MQ135 gas sensor data, compare it to a simple threshold, and switch the fan relay on or off. No Wi-Fi required — perfect for your first working prototype on a breadboard.

Components
  • ESP32 DevKit (30-pin)
  • ESP32 DevKit (30-pin)
  • Breadboard
  • Breadboard
  • Jumper wires
  • Jumper wires
  • USB cable (5 V power)
  • USB cable (5 V power)
  • Mq135 Gas Sensor
  • Mq135 Gas Sensor
  • Fan Relay
  • Fan Relay
  • 1-channel relay module (if driving AC/high current)
  • 1-channel relay module (if driving AC/high current)
Wiring
Component PinESP32 PinNotes
Mq135 Gas Sensor GPIO34 Analog input
Fan Relay GPIO26 Digital I/O
Air Quality Led SDA GPIO21 I2C bus
SCL GPIO22 I2C bus
VCC 3.3V/5V Power rail
GND GND Common ground
Arduino Code
esp32-air-quality-monitor_beginner.ino
// ESP32 Air Quality Monitor — Beginner
const int SENSOR_PIN = 34;
const int OUTPUT_PIN = 26;
const int THRESHOLD = 1800;
const int SAMPLE_DELAY_MS = 500;

void setup() {
  Serial.begin(115200);
  pinMode(OUTPUT_PIN, OUTPUT);
  pinMode(SENSOR_PIN, INPUT);
  Serial.println("Beginner stage ready");
}

void loop() {
  int reading = analogRead(SENSOR_PIN);
  Serial.print("MQ135 gas sensor: ");
  Serial.println(reading);
  bool active = reading > THRESHOLD;
  digitalWrite(OUTPUT_PIN, active ? HIGH : LOW);
  delay(SAMPLE_DELAY_MS);
}
How It Works
01

The ESP32 reads the MQ135 gas sensor on GPIO34.

02

Firmware compares the reading against THRESHOLD in the sketch.

03

When the condition is met, GPIO26 drives the fan relay HIGH.

04

Serial Monitor at 115200 baud shows live values for calibration.

Applications
  • First environmental prototype on a breadboard
  • Classroom demo of sense → decide → act
  • Bench testing before permanent wiring
Troubleshooting

Serial Monitor shows no output

Check USB cable, correct COM port, and baud rate 115200. Press EN/RST on the board after upload.

Sensor readings stuck at 0 or 4095

Verify GPIO34 wiring, sensor VCC at 3.3 V, and that the module GND ties to ESP32 GND.

Output never turns on

Lower THRESHOLD after logging normal vs trigger readings in Serial Monitor.

Upgrades
  • Move to intermediate stage for OLED and manual/auto mode
  • Add deep sleep between samples to save power
  • Enclose in project box with labeled terminals
FAQ

You need an ESP32 DevKit, MQ135 gas sensor, fan relay, a breadboard, jumper wires, and a USB cable for power and programming.

Only the Advanced stage uses Wi-Fi. Beginner and Intermediate builds run offline on the ESP32 with USB power.

Start with Beginner if you are new to Environmental. Use Intermediate for OLED feedback and Advanced for dashboards or connected monitoring.

Overview

The intermediate stage adds calibration, an OLED status screen, and manual/auto mode. You will tune thresholds from real readings, show live values on the display, and harden the sketch with clearer error handling before adding connectivity.

Components
  • ESP32 DevKit (30-pin)
  • ESP32 DevKit (30-pin)
  • Breadboard
  • Breadboard
  • Jumper wires
  • Jumper wires
  • USB cable (5 V power)
  • USB cable (5 V power)
  • Mq135 Gas Sensor
  • Mq135 Gas Sensor
  • Fan Relay
  • Fan Relay
  • Relay module
  • Relay module
  • 0.96" I2C OLED display
  • 0.96" I2C OLED display
  • Tactile push button
  • Tactile push button
  • 10 kΩ resistor (pull-up if needed)
  • 10 kΩ resistor (pull-up if needed)
Wiring
Component PinESP32 PinNotes
Mq135 Gas Sensor GPIO34 Analog input
Fan Relay GPIO26 Digital I/O
Air Quality Led SDA GPIO21 I2C bus
SCL GPIO22 I2C bus
VCC 3.3V/5V Power rail
GND GND Common ground
Mode button GPIO0 Boot button — manual/auto toggle
Arduino Code
esp32-air-quality-monitor_intermediate.ino
// ESP32 Air Quality Monitor — Intermediate
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const int SENSOR_PIN = 34;
const int OUTPUT_PIN = 26;
const int BUTTON_PIN = 0;
const int THRESHOLD = 1800;
const int HYSTERESIS = 80;
bool autoMode = true;

Adafruit_SSD1306 display(128, 64, &Wire, -1);

void setup() {
  Serial.begin(115200);
  pinMode(OUTPUT_PIN, OUTPUT);
  pinMode(SENSOR_PIN, INPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  Wire.begin(21, 22);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.display();
}

void loop() {
  if (!digitalRead(BUTTON_PIN)) {
    autoMode = !autoMode;
    delay(250);
  }
  int reading = analogRead(SENSOR_PIN);
  bool active = autoMode ? (reading > THRESHOLD) : false;
  if (autoMode && reading < THRESHOLD - HYSTERESIS) active = false;
  digitalWrite(OUTPUT_PIN, active ? HIGH : LOW);
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print(autoMode ? "AUTO" : "MANUAL");
  display.print("  Val:");
  display.println(reading);
  display.display();
  delay(300);
}
How It Works
01

On boot, the sketch loads saved calibration offsets and shows status on the OLED.

02

Sensor samples on GPIO34; display updates every loop with current value and mode.

03

Press the mode button to toggle manual override vs automatic threshold control.

04

Watchdog-style checks prevent relay chatter when readings hover near the cutoff.

Applications
  • Field trial with visible OLED feedback
  • Manual override for maintenance or testing
  • Calibrated setup for daily use
Troubleshooting

Serial Monitor shows no output

Check USB cable, correct COM port, and baud rate 115200. Press EN/RST on the board after upload.

Sensor readings stuck at 0 or 4095

Verify GPIO34 wiring, sensor VCC at 3.3 V, and that the module GND ties to ESP32 GND.

OLED stays blank

Confirm I2C address (usually 0x3C), SDA on GPIO21, SCL on GPIO22, and 3.3 V power.

Relay chatters near threshold

Add hysteresis in code or increase SAMPLE_DELAY_MS slightly.

Upgrades
  • Jump to advanced stage for Wi-Fi dashboard and alerts
  • Store calibration in EEPROM or Preferences library
  • Add physical override switch on a dedicated GPIO
FAQ

You need an ESP32 DevKit, MQ135 gas sensor, fan relay, a breadboard, jumper wires, and a USB cable for power and programming.

Only the Advanced stage uses Wi-Fi. Beginner and Intermediate builds run offline on the ESP32 with USB power.

Start with Beginner if you are new to Environmental. Use Intermediate for OLED feedback and Advanced for dashboards or connected monitoring.

Overview

The advanced stage connects ESP32 Air Quality Monitor to your network: live dashboard or mobile-friendly monitoring, alert notifications, CSV-style logging, and automation rules you can adjust remotely.

Components
  • ESP32 DevKit (30-pin)
  • ESP32 DevKit (30-pin)
  • Breadboard
  • Breadboard
  • Jumper wires
  • Jumper wires
  • USB cable (5 V power)
  • USB cable (5 V power)
  • Mq135 Gas Sensor
  • Mq135 Gas Sensor
  • Fan Relay
  • Fan Relay
  • Relay module
  • Relay module
  • I2C OLED display
  • I2C OLED display
  • microSD module (optional logging)
  • microSD module (optional logging)
  • Stable 5 V supply for field deployment
  • Stable 5 V supply for field deployment
Wiring
Component PinESP32 PinNotes
Mq135 Gas Sensor GPIO34 Analog input
Fan Relay GPIO26 Digital I/O
Air Quality Led SDA GPIO21 I2C bus
SCL GPIO22 I2C bus
VCC 3.3V/5V Power rail
GND GND Common ground
Alert LED GPIO2 Notification indicator
Wi-Fi Built-in 2.4 GHz — no extra wiring
Arduino Code
esp32-air-quality-monitor_advanced.ino
// ESP32 Air Quality Monitor — Advanced
#include <WiFi.h>
#include <WebServer.h>

const char* WIFI_SSID = "YOUR_SSID";
const char* WIFI_PASS = "YOUR_PASSWORD";
const int SENSOR_PIN = 34;
const int OUTPUT_PIN = 26;
const int THRESHOLD = 1800;
WebServer server(80);
unsigned long lastAlert = 0;

void handleRoot() {
  int v = analogRead(SENSOR_PIN);
  String body = "<h1>ESP32 Air Quality Monitor</h1><p>Reading: " + String(v) + "</p>";
  body += "<p>Output: " + String(digitalRead(OUTPUT_PIN) ? "ON" : "OFF") + "</p>";
  server.send(200, "text/html", body);
}

void setup() {
  Serial.begin(115200);
  pinMode(OUTPUT_PIN, OUTPUT);
  pinMode(SENSOR_PIN, INPUT);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(300);
  server.on("/", handleRoot);
  server.begin();
  Serial.println(WiFi.localIP());
}

void loop() {
  server.handleClient();
  int reading = analogRead(SENSOR_PIN);
  bool active = reading > THRESHOLD;
  digitalWrite(OUTPUT_PIN, active ? HIGH : LOW);
  if (active && millis() - lastAlert > 60000UL) {
    Serial.println("ALERT: threshold crossed");
    lastAlert = millis();
  }
  delay(200);
}
How It Works
01

ESP32 joins Wi-Fi and exposes readings through a lightweight web page or MQTT topic.

02

Local GPIO34 sensing still runs on-device — cloud is optional for resilience.

03

Rules engine evaluates thresholds, quiet hours, and repeat-alert intervals.

04

Logs append timestamped events you can download or forward to Home Assistant.

Applications
  • Remote monitoring from phone or laptop
  • Automated alerts when limits are crossed
  • Long-term trend logging for optimization
Troubleshooting

Serial Monitor shows no output

Check USB cable, correct COM port, and baud rate 115200. Press EN/RST on the board after upload.

Sensor readings stuck at 0 or 4095

Verify GPIO34 wiring, sensor VCC at 3.3 V, and that the module GND ties to ESP32 GND.

Wi-Fi connection fails

Use 2.4 GHz SSID, check password in sketch, and ensure router allows new devices.

Dashboard loads but values frozen

Confirm loop still reads sensor; avoid blocking HTTP handlers — use async or periodic refresh.

Upgrades
  • Integrate with Home Assistant or MQTT broker
  • Add OTA firmware updates for remote fixes
  • Ship on custom PCB with regulated power supply
FAQ

You need an ESP32 DevKit, MQ135 gas sensor, fan relay, a breadboard, jumper wires, and a USB cable for power and programming.

Only the Advanced stage uses Wi-Fi. Beginner and Intermediate builds run offline on the ESP32 with USB power.

Start with Beginner if you are new to Environmental. Use Intermediate for OLED feedback and Advanced for dashboards or connected monitoring.