MQ135 Arduino: Hava Kalitesi Sensörü, Kalibrasyon ve ppm Hesabı
MQ135 Arduino rehberi: algıladığı gazlar, 24–48 saat ön ısıtma, R0 kalibrasyonu ve ppm formülü, CO2 ölçer mi, fan otomasyonu, ESP32 kaydı ve CCS811 farkı.
MQ135 Arduino projeleri, odadaki havanın ne kadar temiz olduğunu ucuz ve basit bir yolla izlemek isteyenlerin ilk durağıdır. MQ-135, amonyak, azot oksitler, benzen, alkol ve duman gibi gazlara tepki veren, ısıtıcılı bir metal oksit gaz sensörüdür. Hobi kitlerinde genellikle üzerinde bir karşılaştırıcı entegre ve ayar potansiyometresi bulunan küçük bir modül olarak gelir; Arduino'ya analog çıkışından bağlanır ve tek bir analogRead() ile okunur. Fakat bu sensörün verdiği sayıyı anlamlı bir ölçüme çevirmek, çoğu rehberin anlattığından biraz daha fazla bilgi ister.
Bu rehberde sensörün içinde ne olup bittiğini, hangi gazlara ne kadar duyarlı olduğunu ve neden ilk kullanımda 24–48 saat ısıtılması gerektiğini öğreneceksin. Ardından yük direnci (RL), sensör direnci (Rs) ve referans direnç (R0) kavramlarıyla ppm hesabını adım adım kuracak, temiz havada R0 kalibrasyonu yapacak ve en çok sorulan soruya dürüst bir cevap vereceğiz: MQ-135 gerçekten CO2 ölçer mi? Son olarak bir havalandırma fanını otomatik çalıştıracak, ESP32 ile ölçümleri zaman damgalı bir dosyaya kaydedecek, Raspberry Pi Pico ile MicroPython kodu yazacak ve MQ-135'i CCS811 ile NDIR CO2 sensörleriyle karşılaştıracağız.

MQ-135 nedir, nasıl çalışır?
Paslanmaz çelik ağın arkasındaki küçük seramik tüpün yüzeyi kalay dioksit (SnO2) ile kaplıdır ve içinden geçen bir ısıtıcı teli bu tabakayı birkaç yüz santigrat dereceye kadar ısıtır. Temiz havada oksijen molekülleri SnO2 yüzeyine tutunur, malzemedeki serbest elektronları yakalar ve tabakanın direnci yüksek kalır. Havaya amonyak, alkol ya da benzen gibi indirgen bir gaz karıştığında bu gaz yüzeydeki oksijenle tepkimeye girer, elektronlar serbest kalır ve direnç düşer. Gaz ne kadar yoğunsa direnç o kadar azalır. Modül bu direnci bir yük direnciyle gerilim bölücüye bağlar; böylece gaz arttıkça AO pinindeki gerilim yükselir.
| Özellik | Değer |
|---|---|
| Algılayıcı malzeme | Kalay dioksit (SnO2), ısıtıcılı seramik tüp |
| Isıtıcı gerilimi | 5 V ± 0,1 V |
| Isıtıcı direnci ve akımı | 33 Ω ± %5, yaklaşık 150 mA |
| Isıtıcı gücü | En fazla 800 mW |
| Algılama aralığı | NH3 10–300 ppm, benzen 10–1000 ppm, alkol 10–300 ppm |
| İlk ön ısıtma | 24 saatten fazla |
| Çalışma ortamı | −10 ile 45 °C arası, bağıl nem %95'in altında |
| Modül çıkışları | AO (analog) ve DO (LM393 ile dijital eşik) |
MQ-135 hangi gazları algılar?
MQ-135 tek bir gazı ölçen seçici bir sensör değildir; ısıtılmış SnO2 yüzeyiyle tepkimeye giren pek çok gaza aynı anda tepki verir ve bunları birbirinden ayıramaz. Üretici sensörü özellikle amonyak, kükürtlü bileşikler ve benzen türevi buharlara karşı duyarlı olarak tanımlar; alkole, dumana ve azot oksitlere de belirgin tepki verir. Bu yüzden MQ-135'i bir "hava kirliliği göstergesi" gibi düşünmek en doğrusudur: değer yükseldiğinde havada bir şeylerin değiştiğini söyler, ama neyin değiştiğini söylemez. Doğalgaz ve LPG kaçağı için ise bu iş için tasarlanmış MQ-2 gaz sensörü çok daha uygundur.
| Gaz | Evde nereden gelir? | MQ-135 tepkisi |
|---|---|---|
| Amonyak (NH3) | Temizlik ürünleri, kedi kumu, gübre | Yüksek |
| Benzen, toluen ve çözücüler | Boya, tiner, yapıştırıcı, sigara dumanı | Yüksek |
| Alkol buharı | El dezenfektanı, kolonya, parfüm | Yüksek |
| Duman | Yemek pişirme, sigara, mum | Orta |
| Azot oksitler (NOx) | Gaz ocağı, egzoz | Orta |
| Karbondioksit (CO2) | İnsan nefesi, kapalı ve kalabalık oda | Zayıf, dolaylı |
MQ-135 CO2 ölçer mi?
Kısa cevap: hayır, gerçek anlamda ölçmez, tahmin eder. MQ-135'in veri sayfasında bir CO2 eğrisi vardır ve internetteki kütüphanelerin çoğu "CO2 ppm" değerini bu eğriden hesaplar. Ancak CO2 kimyasal olarak kararlı bir gazdır ve SnO2 yüzeyiyle çok zayıf tepkimeye girer. Kapalı bir odada insanlar nefes aldıkça CO2 ile birlikte nem ve vücuttan çıkan uçucu organik bileşikler de artar; MQ-135 asıl bunlara tepki verir. Yani sensörün gösterdiği "CO2" aslında odadaki genel hava kirliliğinden yapılmış bir tahmindir. Bir şişe kolonyanın kapağını açtığında "CO2" değerinin 5000 ppm'e fırlaması bunun en kolay kanıtıdır.
Bu, MQ-135'in işe yaramadığı anlamına gelmez. "Oda havasızlaştı, pencereyi aç" ya da "mutfakta duman var, aspiratörü çalıştır" gibi göreli kararlar için gayet iyi çalışır. Okul, ofis ya da yatak odasında gerçek CO2 değerini bilmen gerekiyorsa ise yazının sonundaki karşılaştırmada anlattığımız NDIR CO2 sensörlerinden birini kullanmalısın. Aşağıdaki tablo, gerçek bir CO2 ölçerin gösterdiği değerlerin ne anlama geldiğini özetliyor; MQ-135'in tahmini değerlerini de bu aralıklarla yorumlayabilirsin.
| CO2 (ppm) | Ne anlama gelir? |
|---|---|
| 400–450 | Dış ortamdaki temiz hava |
| 450–800 | İyi havalandırılmış iç ortam |
| 800–1000 | Kabul edilebilir, havalandırmayı düşün |
| 1000–1500 | Hava ağırlaşır, dikkat dağılabilir; havalandır |
| 1500–2000 | Baş ağrısı ve uyku hali görülebilir |
| 5000 | İş yerlerinde 8 saatlik maruziyet sınırı |
MQ-135 Arduino bağlantısı
| Modül pini | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| VCC (ısıtıcı dahil) | 5V | VIN (USB'den 5 V) | VBUS (USB'den 5 V) |
| GND | GND | GND | GND |
| AO (analog çıkış) | A0 | GPIO 34, 10 kΩ / 20 kΩ bölücü üzerinden | GP26 (ADC0), 10 kΩ / 20 kΩ bölücü üzerinden |
| DO (dijital eşik, isteğe bağlı) | D2 | Bölücü ile herhangi bir GPIO | Bölücü ile herhangi bir GP pini |
Ön ısıtma: 24–48 saatlik ilk ısınma ve günlük ısınma
MQ serisi sensörlerde iki ayrı ısınma vardır ve ikisi sık karıştırılır. Birincisi ilk ön ısıtmadır (burn-in): kutudan yeni çıkan ya da uzun süre kullanılmadan depoda kalmış bir sensörün yüzeyinde nem ve kirlilik birikir. Üretici, standart ölçümlerden önce sensörün 24 saatten uzun süre beslenmesini ister; pratikte 24–48 saat boyunca sürekli çalıştırmak, değerlerin yavaş yavaş kayıp sonunda sabitlendiğini görmeni sağlar. R0 kalibrasyonunu bu süre dolmadan yaparsan birkaç gün içinde bütün ölçümlerin kayar. İkincisi günlük ısınmadır: ön ısıtması tamamlanmış bir sensör her açıldığında ısıtıcının dengeye gelmesi için birkaç dakika bekler; ilk dakikadaki değerler belirgin biçimde yüksek ya da düşük gelir. Kodlarda bu yüzden en az 2–3 dakikalık bir bekleme vardır, hassas ölçüm için 10–20 dakika beklemek daha iyidir.
Rs, RL ve R0: MQ-135 ile ppm nasıl hesaplanır?
Modülün içinde sensör direnci Rs ile karttaki yük direnci RL seri bağlıdır ve 5 V'u paylaşır. AO pinindeki gerilim Vout = 5 × RL / (Rs + RL) olur. Bu formülü ters çevirirsen ölçtüğün gerilimden sensörün o anki direncini bulursun: Rs = RL × (5 − Vout) / Vout. Arduino Uno'da ADC de aynı 5 V'u referans aldığı için gerilime çevirmeye bile gerek kalmaz: Rs = RL × (1023 − ham değer) / ham değer. Tek başına Rs anlamlı değildir, çünkü her sensörün direnci üretimden üretime çok farklıdır. Önemli olan, Rs'nin bilinen bir havadaki değerine, yani R0'a oranıdır.
Veri sayfasındaki eğriler, Rs/R0 oranını gaz yoğunluğuna bağlar ve logaritmik eksende neredeyse düz bir çizgidir. Bu yüzden bir üs fonksiyonuyla ifade edilebilir: ppm = A × (Rs/R0)^−B. Yaygın MQ-135 kütüphanelerinin CO2 eğrisi için kullandığı katsayılar A = 116,602 ve B = 2,769'dur. R0'ı bulmak için sensörü temiz dış havaya koyarsın; dış havadaki CO2 bugün yaklaşık 420 ppm olduğu için formülü ters çevirip R0 = Rs × (420 / A)^(1/B) ≈ Rs × 1,59 hesaplarsın. Bu katsayıların başka gazlar için farklı olduğunu ve sonucun yalnızca bir tahmin olduğunu unutma.
Adım adım MQ-135 R0 kalibrasyonu
- Modülün VCC'sini 5V'a, GND'sini GND'ye, AO'yu A0'a bağla ve sensörü 24–48 saat kesintisiz çalıştırarak ilk ön ısıtmayı tamamla.
- Kartın arkasındaki yük direncinin kodunu oku ve aşağıdaki kodda RL_KOHM değerini buna göre ayarla.
- Devreyi bir balkona ya da açık bir pencerenin önüne, egzoz, sigara ve mutfaktan uzak, temiz bir dış havaya götür.
- Kalibrasyon kodunu yükle, Seri Monitör'ü 9600 baud hızında aç ve en az 10 dakika bekle.
- Ekrandaki ortalama R0 değeri birkaç dakika boyunca neredeyse hiç değişmiyorsa not al; bu senin sensörünün R0 değeridir.
1const byte MQ_PIN = A0; // MQ-135 AO
2const float RL_KOHM = 1.0; // karttaki yük direnci: 102 = 1, 103 = 10 / load resistor on the board
3const float PARA = 116.6020682; // CO2 eğrisi katsayıları / CO2 curve coefficients
4const float PARB = 2.769034857;
5const float CLEAN_AIR_PPM = 420.0; // dış havadaki CO2 / CO2 in outdoor air
6
7float r0Average = 0;
8
9float readRs() {
10 long sum = 0;
11 for (int i = 0; i < 50; i++) { // 50 ölçüm, yaklaşık 1 saniye / 50 readings, about 1 second
12 sum += analogRead(MQ_PIN);
13 delay(20);
14 }
15 float raw = sum / 50.0;
16 if (raw < 1) raw = 1; // sıfıra bölmeyi önle / avoid dividing by zero
17 return RL_KOHM * (1023.0 - raw) / raw; // Rs = RL × (Vc − Vout) / Vout
18}
19
20void setup() {
21 Serial.begin(9600);
22 Serial.println(F("Temiz havada en az 10 dk bekle / wait at least 10 min in clean air"));
23}
24
25void loop() {
26 float rs = readRs();
27 float r0 = rs * pow(CLEAN_AIR_PPM / PARA, 1.0 / PARB); // R0 = Rs × (420 / A)^(1/B)
28 if (r0Average == 0) r0Average = r0;
29 r0Average = r0Average * 0.95 + r0 * 0.05; // yavaş ortalama / slow running average
30
31 Serial.print(F("Rs = "));
32 Serial.print(rs, 2);
33 Serial.print(F(" kOhm R0 = "));
34 Serial.print(r0, 2);
35 Serial.print(F(" ortalama / average R0 = "));
36 Serial.println(r0Average, 2);
37}Kod her saniye 50 ölçümün ortalamasını alır, bundan Rs'yi ve o anki R0'ı hesaplar, sonra bu değeri yavaş bir hareketli ortalamaya ekler. Hareketli ortalama, rüzgârla gelen kısa süreli bir egzoz kokusunun sonucu bozmasını engeller. Bulduğun R0 yalnızca o sensöre ve o RL değerine aittir; sensörü değiştirirsen kalibrasyonu yeniden yap. Aylar içinde sensör yaşlandıkça R0 yavaşça kayar, bu yüzden uzun süre çalışacak projelerde kalibrasyonu birkaç ayda bir tekrarlamak iyi bir alışkanlıktır.
Ölçüm kodu: tahmini ppm ve hava kalitesi seviyesi
R0'ı bulduktan sonra onu ölçüm koduna yazarsın. Kod önce 3 dakika ısınmayı bekler, sonra her 2 saniyede Rs/R0 oranını, tahmini ppm değerini ve yukarıdaki tabloya göre bir hava kalitesi etiketini Seri Monitör'e yazar. Oran temiz havada 0,6 civarındadır; değer küçüldükçe havada daha fazla gaz var demektir. Denemek için sensörün yakınında bir pamuğa biraz kolonya damlat: oran birkaç saniye içinde düşer, ppm yükselir, pamuğu uzaklaştırınca değerler birkaç dakikada eski hâline döner.
1const byte MQ_PIN = A0;
2const float RL_KOHM = 1.0; // kalibrasyondaki RL ile aynı / same RL as in calibration
3const float R0_KOHM = 3.6; // kendi kalibrasyon sonucunla değiştir / replace with your calibration result
4const float PARA = 116.6020682;
5const float PARB = 2.769034857;
6const unsigned long WARMUP_MS = 180000; // 3 dk ısınma / 3 min warm-up
7
8float readRs() {
9 long sum = 0;
10 for (int i = 0; i < 20; i++) {
11 sum += analogRead(MQ_PIN);
12 delay(10);
13 }
14 float raw = sum / 20.0;
15 if (raw < 1) raw = 1;
16 return RL_KOHM * (1023.0 - raw) / raw;
17}
18
19const __FlashStringHelper* airLabel(float ppm) {
20 if (ppm < 800) return F("Iyi / good");
21 if (ppm < 1000) return F("Orta / moderate");
22 if (ppm < 1500) return F("Havalandir / ventilate");
23 return F("Kotu / poor");
24}
25
26void setup() {
27 Serial.begin(9600);
28 Serial.println(F("Isiniyor / warming up..."));
29 while (millis() < WARMUP_MS) delay(100);
30}
31
32void loop() {
33 float ratio = readRs() / R0_KOHM; // gaz arttıkça küçülür / falls as gas rises
34 float ppm = PARA * pow(ratio, -PARB); // tahmini CO2 eşdeğeri / estimated CO2 equivalent
35
36 Serial.print(F("Rs/R0 = "));
37 Serial.print(ratio, 2);
38 Serial.print(F(" ~"));
39 Serial.print(ppm, 0);
40 Serial.print(F(" ppm "));
41 Serial.println(airLabel(ppm));
42 delay(2000);
43}Sıcaklık ve nem MQ-135'i nasıl etkiler?
Metal oksit sensörlerin direnci yalnızca gaza değil, ortam sıcaklığına ve neme de bağlıdır. Veri sayfasındaki eğriye göre aynı havada sıcaklık 20 °C'den 40 °C'ye çıktığında Rs/R0 oranı belirgin biçimde düşer, nem arttıkça da yine düşer; sensör bunu "daha fazla gaz" olarak yorumlar. Sonuç, öğleden sonra güneş alan bir odada ya da duştan sonra banyoda kendiliğinden yükselen ppm değerleridir. Bunu azaltmanın iki yolu vardır: kalibrasyonu sensörü kullanacağın ortama yakın sıcaklık ve nemde yapmak, ya da bir DHT22 ya da DHT11 ile sıcaklığı ve nemi ölçüp kütüphanelerdeki düzeltme formülünü uygulamak. Hazır MQ135 kütüphanelerinin getCorrectedPPM(sıcaklık, nem) gibi fonksiyonları tam olarak bunu yapar. Sıcaklığı ve nemi DHT11'den daha doğru ölçmek istersen BME280 sensörünü kullanabilirsin.
Havalandırma fanı otomasyonu
MQ-135'in en kullanışlı işi, hava ağırlaştığında bir fanı ya da aspiratörü kendiliğinden çalıştırmaktır. Burada iki ayrıntı önemlidir. Birincisi histerezis: fan 1000 ppm'in üzerinde açılır ama ancak değer 800 ppm'in altına inince kapanır; tek bir eşik kullanırsan değer eşiğin çevresinde dalgalanırken röle saniyede bir tıklar. İkincisi en kısa çalışma süresi: fan bir kez açıldığında en az 2 dakika çalışır, böylece odadaki hava gerçekten değişir. Kod ayrıca ölçümleri yumuşatarak bir anlık sıçramaların fanı tetiklemesini önler.
1const byte MQ_PIN = A0;
2const byte FAN_PIN = 7; // röle modülü IN / relay module IN
3const bool RELAY_ACTIVE_LOW = true; // çoğu modül LOW ile çeker / most modules switch on with LOW
4const float RL_KOHM = 1.0;
5const float R0_KOHM = 3.6; // kendi kalibrasyon sonucun / your calibration result
6const float PARA = 116.6020682;
7const float PARB = 2.769034857;
8const float FAN_ON_PPM = 1000; // bunun üstünde fanı aç / fan on above this
9const float FAN_OFF_PPM = 800; // bunun altında fanı kapat / fan off below this
10const unsigned long MIN_RUN_MS = 120000; // fan en az 2 dk çalışır / fan runs at least 2 min
11const unsigned long WARMUP_MS = 180000; // 3 dk ısınma / 3 min warm-up
12
13bool fanOn = false;
14unsigned long fanStart = 0;
15float smoothPpm = 0;
16
17void setFan(bool on) {
18 fanOn = on;
19 if (on) fanStart = millis();
20 bool level = RELAY_ACTIVE_LOW ? !on : on;
21 digitalWrite(FAN_PIN, level ? HIGH : LOW);
22}
23
24float readPpm() {
25 long sum = 0;
26 for (int i = 0; i < 20; i++) {
27 sum += analogRead(MQ_PIN);
28 delay(10);
29 }
30 float raw = sum / 20.0;
31 if (raw < 1) raw = 1;
32 float rs = RL_KOHM * (1023.0 - raw) / raw;
33 return PARA * pow(rs / R0_KOHM, -PARB);
34}
35
36void setup() {
37 digitalWrite(FAN_PIN, RELAY_ACTIVE_LOW ? HIGH : LOW); // önce kapalı seviye, sonra çıkış / off level first, then output
38 pinMode(FAN_PIN, OUTPUT);
39 Serial.begin(9600);
40 while (millis() < WARMUP_MS) delay(100);
41 smoothPpm = readPpm();
42}
43
44void loop() {
45 smoothPpm = smoothPpm * 0.8 + readPpm() * 0.2; // ani sıçramaları yumuşat / smooth out spikes
46
47 if (!fanOn && smoothPpm > FAN_ON_PPM) {
48 setFan(true);
49 } else if (fanOn && smoothPpm < FAN_OFF_PPM && millis() - fanStart > MIN_RUN_MS) {
50 setFan(false);
51 }
52
53 Serial.print(F("~"));
54 Serial.print(smoothPpm, 0);
55 Serial.print(F(" ppm fan: "));
56 Serial.println(fanOn ? F("ACIK / ON") : F("KAPALI / OFF"));
57 delay(1000);
58}Röle modülünün IN pini D7'ye, VCC ve GND'si Arduino'ya bağlanır; fan rölenin COM ve NO uçları üzerinden kendi beslemesine bağlanır. 12 V bir bilgisayar fanı kullanıyorsan röle yerine bir MOSFET daha sessiz ve uzun ömürlüdür; bağlantısı mini su pompası devresiyle aynıdır. Banyo aspiratörü gibi 220 V'luk bir cihazı anahtarlayacaksan röle modülü yazısındaki güvenlik kurallarına mutlaka uy: şebeke tarafını kapalı bir kutuya al ve devreyi enerji varken asla elleme. setup() içinde pinin önce kapalı seviyeye çekilip sonra çıkış yapılması, aktif LOW rölelerin açılışta bir anlık çekmesini önler.
ESP32 ile hava kalitesi kaydı
Hava kalitesinin gün içinde nasıl değiştiğini görmek, anlık değerden çok daha öğreticidir: sabah yatak odasında biriken havayı, yemek pişerken mutfaktaki sıçramayı ya da havalandırmanın etkisini ancak bir kayıtla fark edersin. Aşağıdaki ESP32 kodu dakikada bir ölçüm alır, saati internetten (NTP) çekerek her satıra tarih ve saat yazar ve sonucu ESP32'nin kendi flaş belleğindeki LittleFS dosya sistemine CSV olarak kaydeder. Tarayıcıdan ESP32'nin IP adresine girdiğinde son ölçümü görür, tek tıkla bütün kaydı Excel'de açılabilen bir CSV dosyası olarak indirirsin. Ek kütüphane gerekmez.
1#include <WiFi.h>
2#include <WebServer.h>
3#include <LittleFS.h>
4#include <time.h>
5
6const char* WIFI_SSID = "AgAdi"; // Wi-Fi adın / your Wi-Fi name
7const char* WIFI_PASS = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
8const int MQ_PIN = 34; // AO, 10k/20k bölücü üzerinden (ADC1) / through a 10k/20k divider (ADC1)
9const float DIVIDER = 1.5; // (10k + 20k) / 20k
10const float RL_KOHM = 1.0;
11const float R0_KOHM = 3.6; // ESP32 ile yeniden kalibre et / recalibrate with the ESP32
12const float PARA = 116.6020682;
13const float PARB = 2.769034857;
14const unsigned long LOG_INTERVAL = 60000; // dakikada bir kayıt / one entry per minute
15
16WebServer server(80);
17unsigned long lastLog = 0;
18float lastPpm = 0;
19
20float readPpm() {
21 uint32_t mv = 0;
22 for (int i = 0; i < 20; i++) {
23 mv += analogReadMilliVolts(MQ_PIN); // kalibre edilmiş mV / calibrated mV
24 delay(5);
25 }
26 float vout = (mv / 20.0) / 1000.0 * DIVIDER; // sensör çıkışı (V) / sensor output (V)
27 if (vout < 0.05) vout = 0.05;
28 float rs = RL_KOHM * (5.0 - vout) / vout;
29 return PARA * pow(rs / R0_KOHM, -PARB);
30}
31
32void handleRoot() {
33 String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>"
34 "<meta name='viewport' content='width=device-width,initial-scale=1'></head>"
35 "<body style='font-family:sans-serif'><h2>MQ-135</h2><p>Son ölçüm / last reading: ~";
36 html += String(lastPpm, 0);
37 html += " ppm</p><p><a href='/log.csv'>CSV indir / download</a> | "
38 "<a href='/clear'>Kaydı sil / clear log</a></p></body></html>";
39 server.send(200, "text/html", html);
40}
41
42void handleCsv() {
43 File f = LittleFS.open("/log.csv", "r");
44 if (!f) {
45 server.send(404, "text/plain", "Kayit yok / no log yet");
46 return;
47 }
48 server.streamFile(f, "text/csv");
49 f.close();
50}
51
52void handleClear() {
53 LittleFS.remove("/log.csv");
54 server.sendHeader("Location", "/");
55 server.send(303);
56}
57
58void setup() {
59 Serial.begin(115200);
60 analogSetPinAttenuation(MQ_PIN, ADC_11db); // yaklaşık 0-3,1 V ölçüm aralığı / about 0-3.1 V range
61 if (!LittleFS.begin(true)) { // true: ilk açılışta biçimlendir / format on first use
62 Serial.println("LittleFS hatasi / error");
63 }
64 WiFi.mode(WIFI_STA);
65 WiFi.begin(WIFI_SSID, WIFI_PASS);
66 while (WiFi.status() != WL_CONNECTED) {
67 delay(500);
68 Serial.print(".");
69 }
70 Serial.println();
71 Serial.println(WiFi.localIP());
72 configTime(3 * 3600, 0, "pool.ntp.org"); // UTC+3; kendi saat dilimine göre değiştir / change to your time zone
73
74 server.on("/", handleRoot);
75 server.on("/log.csv", handleCsv);
76 server.on("/clear", handleClear);
77 server.begin();
78 lastLog = millis(); // ilk kayıt 1 dk sonra, sensör ısınırken / first entry after 1 min
79}
80
81void loop() {
82 server.handleClient();
83 if (millis() - lastLog >= LOG_INTERVAL) {
84 lastLog = millis();
85 lastPpm = readPpm();
86 char stamp[20] = "saat-yok";
87 struct tm now;
88 if (getLocalTime(&now, 100)) {
89 strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M", &now);
90 }
91 File f = LittleFS.open("/log.csv", "a"); // dosyanın sonuna ekle / append to the file
92 if (f) {
93 f.printf("%s,%.0f\n", stamp, lastPpm);
94 f.close();
95 }
96 Serial.printf("%s ~%.0f ppm\n", stamp, lastPpm);
97 }
98}ESP32'nin ADC'si Uno'dan farklı ölçtüğü için R0 kalibrasyonunu bu devreyle yeniden yapman en doğrusudur; kalibrasyon kodundaki readRs() fonksiyonunu buradaki readPpm() mantığıyla uyarlaman yeterli. Sensörü GPIO 32–39 arasındaki ADC1 pinlerinden birine bağla: ADC2 pinleri Wi-Fi açıkken kullanılamaz. Her satır yaklaşık 22 bayttır; dakikada bir kayıtla günde yaklaşık 32 KB veri birikir ve varsayılan bölüm şemasındaki 1,5 MB'lık alan bir buçuk ay kadar yeter. Daha uzun kayıtlar için bir SD kart modülü kullanabilir, verileri doğrudan bir tabloya göndermek istersen ESP32 ile Google Sheets yöntemine bakabilirsin.
Raspberry Pi Pico ile MQ-135 (MicroPython)
Pico'da modülü VBUS pininden 5 V ile besler, AO çıkışını yine 10 kΩ / 20 kΩ bölücü üzerinden GP26'ya bağlarsın. Pico'nun ADC'si 16 bitlik bir değer (0–65535) döndürür ve 3,3 V'u referans alır; kod bu değeri önce gerilime, sonra bölücü oranıyla sensör çıkışına çevirir. Formül Arduino'dakiyle aynıdır. VBUS gerilimi USB kaynağına göre 4,8–5,1 V arasında değişebileceği için hassas sonuç istiyorsan VBUS'ı multimetreyle ölçüp koddaki 5,0 değerini düzeltebilirsin.
1from machine import ADC
2import time
3
4adc = ADC(26) # GP26 = ADC0, AO bölücü üzerinden / AO through a divider
5DIVIDER = 1.5 # (10k + 20k) / 20k
6RL = 1.0 # kΩ, karttaki yük direnci / load resistor on the board
7R0 = 3.6 # kΩ, kendi kalibrasyon sonucun / your calibration result
8PARA, PARB = 116.6020682, 2.769034857
9VCC = 5.0 # VBUS gerilimi / VBUS voltage
10
11def read_rs():
12 total = 0
13 for _ in range(20):
14 total += adc.read_u16()
15 time.sleep_ms(5)
16 v = total / 20 / 65535 * 3.3 * DIVIDER # sensör çıkışı (V) / sensor output (V)
17 v = max(v, 0.05) # sıfıra bölmeyi önle / avoid dividing by zero
18 return RL * (VCC - v) / v
19
20print("Isiniyor / warming up...")
21time.sleep(180) # 3 dk ısınma / 3 min warm-up
22while True:
23 ratio = read_rs() / R0
24 ppm = PARA * ratio ** -PARB # tahmini CO2 eşdeğeri / estimated CO2 equivalent
25 print("Rs/R0 = %.2f ~%d ppm" % (ratio, ppm))
26 time.sleep(2)MQ-135, CCS811 ve NDIR CO2 sensörü karşılaştırması
Hava kalitesi için üç farklı sensör sınıfı vardır ve her biri farklı bir soruya cevap verir. MQ-135 ucuz, analog ve basittir; havadaki genel değişimi gösterir. CCS811 da bir metal oksit sensördür, ama içinde kendi mikrodenetleyicisi vardır, ısıtıcıyı kendisi yönetir ve I2C üzerinden doğrudan TVOC (toplam uçucu organik bileşik) ve eCO2 (eşdeğer CO2) değeri verir; ayrıntılarını CCS811 hava kalitesi sensörü yazısında anlatıyoruz. Gerçek CO2 ölçen tek grup ise NDIR (dağıtıcı olmayan kızılötesi) sensörlerdir: MH-Z19B ya da SenseAir S8 gibi modüller, CO2 molekülünün 4,26 µm dalga boyundaki kızılötesi ışığı soğurmasını ölçer ve başka gazlardan neredeyse hiç etkilenmez.
| Özellik | MQ-135 | CCS811 | NDIR (MH-Z19B, SenseAir S8) |
|---|---|---|---|
| Ölçüm ilkesi | Isıtılmış SnO2'nin direnci | Metal oksit + dahili mikrodenetleyici | Kızılötesi ışık soğurması |
| Gerçek CO2 ölçer mi? | Hayır, tahmin eder | Hayır, VOC'den eCO2 hesaplar | Evet |
| Çıkış | Analog (ve dijital eşik) | I2C | UART ya da PWM |
| Besleme | 5 V, yaklaşık 150 mA | 3,3 V, ortalama yaklaşık 30 mA | 5 V, ortalama 60 mA'den az |
| Isınma | İlk kullanımda 24–48 saat, sonra birkaç dakika | İlk kullanımda 48 saat, sonra 20 dakika | Yaklaşık 3 dakika |
| Doğruluk | Düşük, göreli değişim için | Orta, göreli VOC takibi için | Yaklaşık ±50 ppm + okumanın %3–5'i |
| Fiyat | En ucuz | Orta | En pahalı |
Kısaca: öğrenmek, bir alarm ya da fanı tetiklemek ve havadaki değişimi izlemek için MQ-135 yeterlidir. Boya, yapıştırıcı ya da temizlik ürünü buharlarını takip etmek istiyorsan CCS811 daha düzgün ve kararlı veri verir. Sınıf, ofis ya da yatak odası için "pencereyi açma zamanı geldi mi?" sorusuna güvenilir bir sayı lazımsa NDIR bir CO2 sensörüne yatırım yapmak gerekir. Birçok kişi ikisini birlikte kullanır: NDIR sensör CO2'yi, MQ-135 ise mutfaktaki duman ya da çözücü kokusu gibi ani olayları yakalar.
MQ-135 ile proje fikirleri
- Oda havası göstergesi: Hava kalitesine göre yeşil, sarı ve kırmızı yanan bir RGB LED ya da bir OLED ekranda canlı grafik.
- Akıllı mutfak aspiratörü: Yemek pişerken duman ve buhar artınca aspiratörü açan, hava temizlenince kapatan sistem.
- Garaj ve boya atölyesi uyarıcısı: Çözücü buharı yükselince buzzer ile uyaran ve havalandırmayı çalıştıran devre.
- Kedi kumu takibi: Amonyak arttığında kumun değiştirilme zamanının geldiğini telefona bildiren ESP32 projesi.
- Sınıf havası panosu: Ölçümleri ESP32 ile kaydedip ders saatleriyle karşılaştıran, havalandırma alışkanlığını gösteren bir grafik.
- Ev otomasyonu: Değerleri MQTT ile Home Assistant'a gönder, hava kötüleşince hava temizleyiciyi akıllı prizle aç.
Sık yapılan hatalar ve çözümleri
| Belirti | Olası neden | Çözüm |
|---|---|---|
| Değerler saatlerce sürekli kayıyor | İlk ön ısıtma tamamlanmamış | Sensörü 24–48 saat kesintisiz çalıştır, sonra kalibre et |
| Açılışta değerler çok yüksek ya da çok düşük | Günlük ısınma beklenmemiş | Ölçmeden önce en az 3 dakika, tercihen 10–20 dakika bekle |
| ppm temiz havada bile binlerce | R0 yanlış ya da kalibrasyon kapalı ortamda yapılmış | Kalibrasyonu temiz dış havada tekrarla |
| ESP32'de değerler hep tavan yapıyor | AO 5 V'a çıkıyor ya da sensör 3,3 V'tan besleniyor | Sensörü 5 V'tan besle, AO'ya 10k/20k bölücü koy |
| Arduino ısınıyor ya da USB kopuyor | 150 mA'lik ısıtıcı ve diğer yükler USB'yi zorluyor | Harici 5 V besleme kullan, GND'leri birleştir |
| Güneş gelince ya da duştan sonra ppm artıyor | Sıcaklık ve nem etkisi | Kalibrasyonu benzer koşulda yap ya da DHT22 ile düzeltme uygula |
MQ-135, sınırlarını bildiğin sürece hava kalitesini öğrenmek ve havalandırmayı otomatikleştirmek için mükemmel bir başlangıç sensörüdür: 5 V ile besle, 24–48 saat ısıt, R0'ı temiz havada bul, sonuçları göreli yorumla ve gerçek CO2 gerektiğinde NDIR bir sensöre geç. BlueGrays uygulamasında Gaz Sensörü Kullanımı, Alev ve Gaz Sensörü Alarm ve ESP32 MQ-2 Gaz Dedektörü projelerini, DHT11 ve DHT22 ile sıcaklık–nem ölçümü ve ESP32 sıcaklık kaydedici projeleriyle birlikte adım adım devre şemaları ve kodlarıyla bulabilirsin.
Sıkça sorulan sorular
MQ135 CO2 ölçer mi?
MQ-135 gerçek anlamda CO2 ölçmez, tahmin eder. Sensör CO2'ye çok zayıf tepki verir; asıl tepkisi alkol, amonyak ve benzen gibi gazlara ve insanların yaydığı uçucu bileşiklere karşıdır. Kütüphanelerin gösterdiği CO2 ppm değeri bu genel kirlilikten yapılan bir hesaptır; gerçek CO2 için MH-Z19B gibi bir NDIR sensör gerekir.
MQ135 kaç dakikada ısınır?
Yeni bir MQ-135'in ilk ön ısıtması 24–48 saat sürer ve kalibrasyondan önce mutlaka tamamlanmalıdır. Bu süre geçtikten sonra sensör her açılışta yaklaşık 2–3 dakikada kullanılabilir hâle gelir; en kararlı değerler için 10–20 dakika beklemek daha iyidir.
MQ135 R0 değeri nasıl bulunur?
Ön ısıtması tamamlanmış sensörü temiz dış havaya götür, Rs = RL × (1023 − ham) / ham formülüyle direncini hesapla ve dış havadaki CO2'yi yaklaşık 420 ppm kabul ederek R0 = Rs × (420 / 116,6)^(1 / 2,769) hesabını yap. Birkaç dakikalık ölçümün ortalamasını al. Bulduğun R0 yalnızca o sensöre ve o yük direncine aittir.
MQ135 ile MQ2 arasındaki fark nedir?
İkisi de ısıtıcılı metal oksit sensördür ama farklı gazlara ayarlanmıştır. MQ-2 LPG, propan, metan, hidrojen ve duman için, yani gaz kaçağı alarmı için uygundur. MQ-135 ise amonyak, benzen, alkol, NOx ve duman gibi hava kirleticilerine duyarlıdır ve hava kalitesi izlemek için kullanılır.
MQ135 ESP32 ile kullanılır mı?
Evet, ama iki noktaya dikkat etmelisin. Sensörün ısıtıcısı 5 V ister, bu yüzden modülü ESP32'nin VIN pininden beslemelisin. AO çıkışı da 5 V'a kadar çıkabildiği için 10 kΩ ve 20 kΩ'luk bir gerilim bölücüyle 3,3 V'a indirip GPIO 32–39 arasındaki bir ADC1 pinine bağlamalısın.
MQ135 hangi gazları algılar?
MQ-135 en çok amonyak, kükürtlü bileşikler ve benzen gibi aromatik buharlara duyarlıdır. Alkol, duman ve azot oksitlere de belirgin, CO2 ve karbonmonoksite ise zayıf tepki verir. Bu gazları birbirinden ayıramaz; yalnızca havadaki toplam değişimi gösterir.
Bu yazıdan sonra yapabileceğin projeler
Tüm projeleri gör
DHT11 İle Sıcaklık Ve Nem
Odan kaç derece, hava nemli mi, kuru mu? DHT11 sensörü sıcaklığı ve nemi ölçer, Arduino da bu değerleri iki satırlık LCD ekrana yazar ve birkaç saniyede bir yeniler. Sensöre bir şey olursa ekranda hat

Gaz Sensörü Kullanımı
Mutfakta gaz kaçağını senden önce fark eden küçük bir bekçi. Gaz sensörü havayı sürekli koklar; her şey yolundayken yeşil LED yanar, havadaki gaz belli bir seviyeyi aşınca kırmızı LED yanar ve buzzer

Alev ve Gaz Sensörü Alarm
Gaz kaçtığında ya da bir yerde alev çıktığında seni hemen uyaran küçük bir bekçi. Gaz sensörü havadaki gazı, alev sensörü ateşin ışığını izler; her şey yolundayken yeşil LED yanar, biri tehlike sezinc

ESP32 MQ-2 Gaz/Duman Dedektörü Alarmı
Bu projede MQ-2 gaz sensörü modülü ile yanıcı gaz (LPG, metan, propan) ve duman tespiti yapılır; tespit anında pasif buzzer üzerinden siren benzeri bir alarm sesi çalınır. Sensörün dijital eşik çıkışı (DO) ESP32'nin GPIO 4 pinine bağlanır; gaz ya da duman eşik değeri aştığında DO LOW olur ve alarm tetiklenir. Hassasiyet modül üzerindeki potansiyometre ile ayarlanır. Pasif buzzer tone() fonksiyonu ile sürülerek iki frekans arasında geçişli siren efekti üretilir.

ESP32 DHT22 Sıcaklık ve Nem (LCD Ekranlı)
DHT22 sensörü ile ortam sıcaklığını ve nem oranını ölçüp 16x2 I2C LCD ekranda gerçek zamanlı olarak gösteren bir projedir. Adafruit DHT kütüphanesi kullanılarak tek kablo (one-wire) protokolü ile sensörden veri okunur. Bu proje, IoT hava istasyonu, akıllı ev otomasyonu veya seralarda ortam takip sistemlerinin temelini oluşturur. ESP32'nin GPIO 4 pini üzerinden DHT22 ile haberleşme sağlanır.

ESP32 Sıcaklık Loglayıcı (DHT22 + DS1307 RTC + microSD + OLED)
ESP32 ile zamanlı sıcaklık ve nem kayıt sistemi kur! Bu projede DHT22 sensörü ile dakikada bir sıcaklık/nem okuyup DS1307 RTC modülünden aldığın gerçek zaman etiketiyle microSD karta CSV formatında kaydediyoruz. OLED ekranda büyük font ile son sıcaklık değeri, nem yüzdesi, timestamp ve toplam kayıt sayısı canlı gösterilir. SD kart üzerindeki /temp_log.csv dosyası Excel/Google Sheets/Python pandas ile açılıp analiz edilebilir — sera monitörü, ev/oda sıcaklık takibi, laboratuvar veri toplama,

ESP32 I2C LCD Ekran (16x2)
Bu projede 16x2 karakter LCD ekranı I2C protokolü üzerinden ESP32'ye bağlayarak metin görüntülüyoruz. I2C sayesinde sadece 2 data pini (SDA ve SCL) ile ekranı kontrol edebiliyoruz; klasik paralel bağlantıdaki 6+ pin israfı ortadan kalkıyor. Kod ilk satıra sabit bir mesaj, ikinci satıra ise her saniye artan bir sayaç yazdırıyor. Ayrıca başlangıçta I2C tarayıcı çalıştırarak ekranın adresini Serial Monitor'a yazdırıyoruz (genelde 0x27 veya 0x3F).

220V Ampülü Buton(Push) İle Yakma
Butona bastığın sürece 220 voltluk gerçek bir ampul yanar, bıraktığın anda söner. Arduino'nun küçük sinyali ampulü doğrudan yakamaz; araya röle denen elektrikli bir anahtar girer ve ampulün devresini
Diğer yazılar
BlueGrays uygulaması
Bu devreyi telefonunda adım adım kur
Devre şeması, kod ve parça listesi tek ekranda. 200'den fazla Arduino, ESP32 ve Pico projesi seni bekliyor.
Telefonunla tara
Kamerayı koda tut; telefonunun uygulama mağazası açılır.
ya da mağazada aç

