Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

MQ-135 air quality sensor with Arduino Uno — Rs/R0 and honest ppm

Wire an MQ-135 to an Arduino Uno R3 (AO on A0, DO on D2), warm it up, calibrate R0 in clean air and track air quality as Rs/R0 with a rough ppm estimate.

Arduino C++Verified — core avr 1.8.8, 3 Nov 2026Compile-only

Community draft — report a mistake

The MQ-135 is the cheap (about ₹150) “air quality” gas sensor in many school and college projects. A heated tin-oxide element changes its resistance when it meets pollutant gases, so the module can show whether a room is getting stuffy or smoky.

On the Uno it runs from 5V — its heater needs 5 V and about 150 mA. The analog output goes to A0 and the threshold output to D2. The Uno’s 5 V ADC range matches the module’s output exactly, so no divider is needed, unlike on ESP32.

Instead of printing raw numbers, the sketch works out the sensor’s resistance Rs and compares it with R0, its value in clean air. The ratio Rs/R0 falls as the air gets worse. It also prints a rough CO₂-equivalent from the datasheet curve. Calibrate R0 once outdoors and the trend becomes meaningful.

Wiring

Arduino Uno R3Only the pins used are shown · 5 V logicMQ-135 gas sensor module5VGNDA0D2VCCGNDDOAO
Every connection
Arduino Uno R3MQ-135 gas sensor moduleWireNotes
5VVCCredThe heater draws about 150 mA. Fine from USB with little else connected; with a relay or servo too, use an external 5 V supply (common GND).
GNDGNDblack
A0AOyellow
D2DOgreenOptional threshold alarm (LOW past the pot setting on most boards).

5 V (heater: about 150 mA) part, 5 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

  • The MQ-135 reacts to many gases at once — CO₂, ammonia, alcohol, smoke, benzene — and can’t tell them apart. The “ppm” here is a rough CO₂-equivalent from the datasheet curve. Use it to see the air getting worse or better, not as a calibrated meter.
  • A new sensor needs a 24-hour burn-in powered on before the readings settle, and about 2 minutes of heating at every start. The sketch marks those readings as warming.
  • Check the load resistor on your board: 102 means 1 kΩ, 103 means 10 kΩ. Put it in RL — a wrong RL scales every result.
  • Calibrate R0 outdoors or by an open window, not in a closed room in the evening, when CO₂ is already high. Humidity and temperature also shift the reading; monsoon days read differently from dry winter days.
  • The sensor body gets hot (around 50 °C). Keep it away from plastic and from the DHT or BME you might use for temperature.

Code

mq135-uno.inoArduino C++Open in PlaygroundOpen in PlaygroundFlash
// MQ-135 air-quality sensor module on an Arduino Uno R3.
// VCC → 5V, GND → GND, AO → A0, DO → D2.
// Reads the sensor resistance Rs and compares it with R0, your clean-air value.
// Calibrate: run it outdoors (or by an open window) for 30 min, copy the printed Rs into R0.
const uint8_t AO_PIN = A0, DO_PIN = 2;
const float RL = 1000.0;  // load resistor on the module: "102" = 1 kΩ (some boards: "103" = 10 kΩ)
float R0 = 3600.0;        // clean-air Rs - replace with your own reading
const unsigned long WARMUP_MS = 120000UL;

void setup() {
  pinMode(DO_PIN, INPUT);
  Serial.begin(9600);
  Serial.println(F("MQ-135 heating up - readings settle after 2 minutes"));
}

void loop() {
  long sum = 0;
  for (uint8_t i = 0; i < 32; i++) sum += analogRead(AO_PIN);
  float adc = sum / 32.0;
  if (adc < 1) adc = 1;
  float rs = RL * (1023.0 - adc) / adc;  // from Vout = 5 V x RL / (Rs + RL)
  float ratio = rs / R0;
  // Rough CO2-equivalent from the datasheet curve. Treat it as a trend, not a measurement.
  float ppm = 116.602 * pow(ratio, -2.769);

  Serial.print(millis() < WARMUP_MS ? F("[warming] ") : F(""));
  Serial.print(F("AO "));
  Serial.print(adc, 0);
  Serial.print(F("  Rs "));
  Serial.print(rs, 0);
  Serial.print(F(" ohm  Rs/R0 "));
  Serial.print(ratio, 2);
  Serial.print(F("  ~"));
  Serial.print(ppm, 0);
  Serial.print(F(" ppm  "));
  if (ratio > 0.8) Serial.print(F("Air OK"));
  else if (ratio > 0.5) Serial.print(F("Stuffy - ventilate"));
  else Serial.print(F("Poor - smoke/fumes?"));
  Serial.println(digitalRead(DO_PIN) == LOW ? F("  [DO alarm]") : F(""));
  delay(2000);
}

Expected serial output

Serial Monitor · 9600Output
MQ-135 heating up - readings settle after 2 minutes
[warming] AO 412  Rs 1483 ohm  Rs/R0 0.41  ~1360 ppm  Poor - smoke/fumes?
AO 225  Rs 3547 ohm  Rs/R0 0.99  ~119 ppm  Air OK
AO 290  Rs 2528 ohm  Rs/R0 0.70  ~313 ppm  Stuffy - ventilate

Common errors

Readings keep falling for the first hour

Normal for a new or long-unused sensor. Leave it powered for a day before calibrating R0.

AO stuck near 1023 or 0

Heater not powered (sensor stays cold) or AO not on A0. The sensor should feel warm after a minute.

Huge ppm numbers

R0 is still the default. Calibrate it in clean air with your own sensor and board.

↑↓ to move · Enter to open