Air Quality Indicator
Beginner30 minAn indicator that shows a room's air like a traffic light: the MQ-135 sensor reacts to pollutants such as smoke, ammonia, benzene and the CO₂ that builds up from breathing; green lights while the air is clean, yellow as it starts to get stale and red when it is bad, and the Serial Monitor tells you to air the room. At start the sensor warms up and the clean-air value is measured; every reading after that is judged against it, and if the air gets even cleaner the value is updated. It teaches reading an analog sensor against its own clean-air value, reducing jitter by averaging several readings and leaving a margin at the thresholds so the LEDs do not flicker at the edge. No extra library is needed. The MQ-135 does not tell gases apart and does not give a calibrated CO₂ reading; it shows roughly how polluted the air is.
Build it step by step in the appWhat you need
1 × Arduino Uno R3
1 × Breadboard 830 Points (MB-102)
1 × MQ-135 Air Quality Sensor
1 × LED - Green (5mm)
1 × LED - Yellow (5mm)
1 × LED - Red (5mm)
3 × Resistor 220 Ω (1/4W)
9 × Jumper Wire - Male/Male
Wiring steps
- 1
Connect the power rails (5V and GND)

Connect one of the GND pins on the Arduino's power header to the breadboard's bottom blue (−) rail with a black male-male wire, and the 5V pin on the same header to the bottom red (+) rail with a red wire. At the right end of the breadboard, join the bottom (−) rail to the top (−) rail with a black wire: the LEDs' resistors take their ground from the top (−) rail, and the breadboard's top and bottom rails are not connected to each other. The top (+) rail is not used in this project.
- Arduino GND → the bottom blue (−) rail (black wire)
- Arduino 5V → the bottom red (+) rail (red wire)
- the bottom blue (−) rail → the top blue (−) rail (black wire)
- 2
Add the three LEDs with their resistors

Plug the green LED's long leg (+) into column 18 in row F of the top half and its short leg (−) into column 19. Put one end of a 220 Ω resistor in row J of column 19 and the other end in the top blue (−) rail. Connect the Arduino's pin 10 to column 18 with a green wire (GREEN_LED 10). Plug the yellow LED's long leg (+) into column 24 in row F of the top half and its short leg (−) into column 25. Put one end of a 220 Ω resistor in row J of column 25 and the other end in the top blue (−) rail. Connect the Arduino's pin 11 to column 24 with a orange wire (YELLOW_LED 11). Plug the red LED's long leg (+) into column 30 in row F of the top half and its short leg (−) into column 31. Put one end of a 220 Ω resistor in row J of column 31 and the other end in the top blue (−) rail. Connect the Arduino's pin 12 to column 30 with a white wire (RED_LED 12). The resistors limit the current through the LEDs. Green means good, yellow fair and red poor air; only one of them is on at a time.
- green LED long leg (+) → column 18, top half
- green LED short leg (−) → column 19, top half
- yellow LED long leg (+) → column 24, top half
- yellow LED short leg (−) → column 25, top half
- red LED long leg (+) → column 30, top half
- red LED short leg (−) → column 31, top half
- green LED's resistor (leg 1) → column 19, top half
- green LED's resistor (leg 2) → the top blue (−) rail
- yellow LED's resistor (leg 1) → column 25, top half
- yellow LED's resistor (leg 2) → the top blue (−) rail
- red LED's resistor (leg 1) → column 31, top half
- red LED's resistor (leg 2) → the top blue (−) rail
- Arduino pin 10 → column 18, top half (green wire)
- Arduino pin 11 → column 24, top half (orange wire)
- Arduino pin 12 → column 30, top half (white wire)
- 3
Plug in and wire the MQ-135 sensor

Plug the MQ-135 board in with its four pins in row E of the breadboard's bottom half: VCC in column 48, GND in 49, DO in 50 and AO in 51; the pin names are printed on the board. The board stands upright with the sensor's metal cap facing up. Connect column 48 to the bottom (+) rail with a short red wire and column 49 to the bottom (−) rail with a short black wire. Connect the Arduino's A0 pin to column 51 (AO, the analog output) with a yellow wire (AIR_PIN A0). The DO pin stays empty: it only gives on/off against the threshold set by the screw on the board, and here the code sets the thresholds. The sensor has a heater inside that draws about 150 mA, so it is powered from the 5V rail rather than an Arduino pin. It gets warm while it runs: that is normal, do not touch the metal cap. A new sensor may smell slightly burnt for its first few runs; leaving it on for a day the first time is recommended so its readings settle.
- MQ-135 VCC → column 48, bottom half
- MQ-135 GND → column 49, bottom half
- MQ-135 DO → column 50, bottom half
- MQ-135 AO → column 51, bottom half
- Arduino A0 → column 51, bottom half (yellow wire)
- column 48, bottom half → the bottom red (+) rail (red wire)
- column 49, bottom half → the bottom blue (−) rail (black wire)
Arduino sketch
// The MQ-135's analog output (AO)
const int AIR_PIN = A0;
// The three LEDs' pins
const int GREEN_LED = 10;
const int YELLOW_LED = 11;
const int RED_LED = 12;
// The heater's warm-up time (ms)
const unsigned long WARM_UP = 30000;
// This far above the clean-air value
// yellow lights, this far red (0-1023)
const int FAIR_RISE = 60;
const int POOR_RISE = 150;
// Going down it must fall this far below
// a threshold: the LEDs do not flicker
// at the edge
const int MARGIN = 15;
// Time between readings (ms)
const unsigned long MEASURE_EVERY = 1000;
int baseline = 0;
// 0 good, 1 fair, 2 poor
int level = 0;
unsigned long lastMeasure = 0;
// The average of 10 readings: less jitter
int readAir() {
long total = 0;
for (int i = 0; i < 10; i++) {
total += analogRead(AIR_PIN);
delay(10);
}
return total / 10;
}
void showLevel() {
digitalWrite(GREEN_LED, level == 0 ? HIGH : LOW);
digitalWrite(YELLOW_LED, level == 1 ? HIGH : LOW);
digitalWrite(RED_LED, level == 2 ? HIGH : LOW);
}
void setup() {
pinMode(GREEN_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
Serial.begin(9600);
// Yellow blinks while it warms up
Serial.println("Sensor warming up, wait 30 s...");
unsigned long start = millis();
while (millis() - start < WARM_UP) {
digitalWrite(YELLOW_LED, (millis() / 500) % 2 ? HIGH : LOW);
}
// Clean air: the average of 10 readings
long total = 0;
for (int i = 0; i < 10; i++) {
total += readAir();
delay(100);
}
baseline = total / 10;
Serial.print("Clean-air value: ");
Serial.println(baseline);
// Almost 0: the sensor has no power
if (baseline < 10) {
Serial.println("Cannot read the sensor, check the wires.");
while (true) {
digitalWrite(RED_LED, (millis() / 250) % 2 ? HIGH : LOW);
}
}
showLevel();
Serial.println("Measuring.");
}
void loop() {
if (millis() - lastMeasure < MEASURE_EVERY) return;
lastMeasure = millis();
int air = readAir();
// If the air gets even cleaner, the
// clean-air value comes down too
if (air < baseline) baseline = air;
int rise = air - baseline;
// Going up, the threshold itself; going
// down, MARGIN below it
int newLevel = 0;
if (rise >= FAIR_RISE - (level >= 1 ? MARGIN : 0)) newLevel = 1;
if (rise >= POOR_RISE - (level >= 2 ? MARGIN : 0)) newLevel = 2;
Serial.print("Air: ");
Serial.print(air);
Serial.print(" | Rise: ");
Serial.print(rise);
if (newLevel == 0) Serial.println(" | Good");
if (newLevel == 1) Serial.println(" | Fair");
if (newLevel == 2) Serial.println(" | Poor, open a window!");
if (newLevel != level) {
level = newLevel;
showLevel();
}
}
What the Serial Monitor shows
Sensor warming up, wait 30 s... Clean-air value: 205 Measuring. Air: 206 | Rise: 1 | Good Air: 204 | Rise: 0 | Good Air: 268 | Rise: 64 | Fair Air: 371 | Rise: 167 | Poor, open a window! Air: 322 | Rise: 118 | Fair Air: 231 | Rise: 27 | Good
If a wire is loose
Sensor warming up, wait 30 s... Clean-air value: 0 Cannot read the sensor, check the wires.
What you will see
At start the yellow LED blinks for 30 seconds while the sensor's heater warms up; the sensor should be in clean air. Then the clean-air value appears in the Serial Monitor, the green LED lights, and every second the reading and its rise over clean air are printed. To test it, breathe on the sensor from close up a few times, or bring something with a strong smell near it, such as cologne, hand sanitiser or a permanent marker: once the rise passes 60 the yellow LED lights, past 150 the red one, and "Poor, open a window!" appears in the Serial Monitor. Take the smell away and air the sensor, and the LEDs go back to green step by step; going down, the reading has to fall a little below each threshold so they do not flicker at the edge. If "Cannot read the sensor" appears, check the MQ-135's VCC and GND wires. If the reading never changes, check that the yellow wire is on A0. If the air was stale at start, that stale air counts as clean and the indicator warns late; as the air clears, the clean-air value comes down by itself, or press the Arduino's reset button in clean air. For a more or less sensitive indicator, change FAIR_RISE and POOR_RISE in the code. Safety: cologne and sanitiser are flammable, so do not spray them onto the sensor's hot cap; do not test it with a lighter or an open flame.
In the app, tick off every wire, match the parts to your inventory and pick up where you left off.
Build it step by step in the app