This project is an air quality monitoring system built using an ESP32 microcontroller, an MQ-135 gas sensor, and a buzzer for audible alerts. The main goal of the system is to continuously monitor the air quality, detect harmful gas levels (such as NH3, NOx, benzene, CO2, and smoke), and:
The following schematic illustrates the core components and their connections in the air quality monitoring system:
This schematic includes:
ESP32 Development Board
Description: The ESP32 is a low-cost, low-power microcontroller with integrated Wi-Fi and Bluetooth. It is ideal for IoT projects due to its wireless capabilities and sufficient computing power.
Key Features:
Use in Project:
Important Pins Used:
MQ-135 Gas Sensor
Description: The MQ-135 is a chemical gas sensor capable of detecting a wide range of gases, including ammonia, nitrogen oxides, benzene, smoke, and carbon dioxide. It is commonly used in air quality monitoring.
Key Features:
Use in Project: Provides an analog voltage proportional to air pollution levels. Connected to the ESP32’s analog input (GPIO34) through a voltage divider or level shifter, since it outputs 5V and ESP32 is 3.3V-tolerant.
Pinout:
| Pin | Function | Description |
|---|---|---|
| VCC | Power Input | Connect to 5V |
| GND | Ground | Connect to GND |
| AOUT | Analog Output | Analog signal to ESP32 (via voltage divider) |
| DOUT | Digital Output | Not used in this project |
Active Buzzer Module
Description: An active buzzer is a simple sound-emitting component that generates a tone when voltage is applied. Unlike passive buzzers, it does not require PWM control — just a HIGH/LOW signal.
Key Features:
Use in Project:
Pinout:
| Pin | Function | Description |
|---|---|---|
| + | VCC | Connect to 3.3V or 5V |
| - | GND | Connect to Ground |
| SIG | Signal Input | Connect to ESP32 digital pin (e.g., GPIO25) |
Voltage Divider Resistors
Description:
Use in Project:
Breadboard and Jumper Wires
Description:
Use in Project:
Power Supply
Description:
Use in Project:
The Air Quality is a web application created to monitor and display air quality data in real time. It retrieves sensor readings such as PPM values from an air quality sensor—and presents them through a clean, user-friendly interface. The application is designed for development and educational use, offering a simple way to test, view, and analyze environmental data either locally or through connection with cloud-based platforms. Its structure is modular and flexible, making it easy to adapt to different use cases and hardware setups.
This part connects to Wi-Fi and initializes Firebase for anonymous sign-in and Realtime Database communication.
#include <WiFi.h>
#include <Firebase_ESP_Client.h>
#include "addons/TokenHelper.h"
#include "addons/RTDBHelper.h"
#define WIFI_SSID "********"
#define WIFI_PASSWORD "**********"
#define API_KEY "********"
#define DATABASE_URL "**********"
FirebaseData fbdo;
FirebaseAuth auth;
FirebaseConfig config;
void setup() {
Serial.begin(115200);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(300);
}
config.api_key = API_KEY;
config.database_url = DATABASE_URL;
if (Firebase.signUp(&config, &auth, "", "")) {
Serial.println("Firebase signUp succeeded");
}
config.token_status_callback = tokenStatusCallback;
Firebase.begin(&config, &auth);
Firebase.reconnectWiFi(true);
}
Reads the analog value from the MQ-135 sensor, calculates Rs, and then converts that to a PPM (parts per million) estimate using a logarithmic formula.
#define MQ135_PIN 34
#define RZERO 116404.0
#define RL_VALUE 10000.0
#define PARA 116.6020682
#define PARB -2.769034857
void loop() {
int adcValue = analogRead(MQ135_PIN);
float sensor_voltage = adcValue * (3.3 / 4095.0);
float rs = (3.3 - sensor_voltage) * RL_VALUE / sensor_voltage;
float ratio = rs / RZERO;
float ppm = PARA * pow(ratio, PARB);
}
Activates a buzzer if the air quality degrades beyond a certain threshold (800 PPM in this case).
#define BUZZER_PIN 25
void setup() {
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, HIGH); // buzzer off
}
void loop() {
// ...
if (ppm > 800) {
digitalWrite(BUZZER_PIN, LOW); // buzzer on
} else {
digitalWrite(BUZZER_PIN, HIGH); // buzzer off
}
}
Uploads the latest calculated PPM value to Firebase Realtime Database under the path /air_quality/ppm.
if (Firebase.RTDB.setFloat(&fbdo, "/air_quality/ppm", ppm)) {
Serial.println("Data sent to Firebase");
} else {
Serial.print("Failed to send data: ");
Serial.println(fbdo.errorReason());
}
}