MQ135 Arduino: sensore qualità aria, calibrazione e calcolo ppm
MQ135 con Arduino: gas rilevati, preriscaldamento di 24–48 ore, calibrazione di R0 e formula dei ppm, misura la CO2?, ventola automatica, registro con ESP32.
I progetti con MQ135 e Arduino sono il primo passo per chi vuole tenere d'occhio in modo semplice ed economico quanto è pulita l'aria di una stanza. L'MQ-135 è un sensore di gas a ossido metallico riscaldato che reagisce a gas come ammoniaca, ossidi di azoto, benzene, alcol e fumo. Nei kit arriva di solito come un piccolo modulo con un comparatore e un trimmer di regolazione; si collega ad Arduino tramite l'uscita analogica e si legge con un semplice analogRead(). Ma trasformare il numero che restituisce in una misura sensata richiede qualche conoscenza in più di quanto spieghino la maggior parte dei tutorial.
In questa guida scoprirai cosa succede dentro il sensore, a quali gas è più sensibile e perché va riscaldato 24–48 ore prima del primo uso serio. Poi costruiremo passo dopo passo il calcolo dei ppm con la resistenza di carico (RL), la resistenza del sensore (Rs) e la resistenza di riferimento (R0), faremo la calibrazione di R0 in aria pulita e daremo una risposta onesta alla domanda più frequente: l'MQ-135 misura davvero la CO2? Infine comanderemo una ventola in automatico, registreremo le misure con un ESP32 in un file con data e ora, scriveremo codice MicroPython per il Raspberry Pi Pico e confronteremo l'MQ-135 con il CCS811 e con i sensori di CO2 NDIR.

Che cos'è l'MQ-135 e come funziona?
Dietro la rete in acciaio inox, la superficie di un piccolo tubo ceramico è rivestita di biossido di stagno (SnO2), e un filo riscaldatore che lo attraversa porta questo strato a diverse centinaia di gradi. In aria pulita le molecole di ossigeno si legano alla superficie di SnO2, catturano gli elettroni liberi del materiale e la resistenza dello strato resta alta. Quando nell'aria arriva un gas riducente come ammoniaca, alcol o benzene, reagisce con l'ossigeno in superficie, gli elettroni si liberano e la resistenza scende. Più il gas è concentrato, più la resistenza cala. Il modulo mette questa resistenza in un partitore di tensione con una resistenza di carico, quindi la tensione sul pin AO sale all'aumentare del gas.
| Caratteristica | Valore |
|---|---|
| Materiale sensibile | Biossido di stagno (SnO2), tubo ceramico riscaldato |
| Tensione del riscaldatore | 5 V ± 0,1 V |
| Resistenza e corrente del riscaldatore | 33 Ω ± 5 %, circa 150 mA |
| Potenza del riscaldatore | Al massimo 800 mW |
| Campo di rilevamento | NH3 10–300 ppm, benzene 10–1000 ppm, alcol 10–300 ppm |
| Preriscaldamento iniziale | Più di 24 ore |
| Condizioni di lavoro | Da −10 a 45 °C, umidità relativa sotto il 95 % |
| Uscite del modulo | AO (analogica) e DO (soglia digitale con LM393) |
Quali gas rileva l'MQ-135?
L'MQ-135 non è un sensore selettivo che misura un solo gas; reagisce contemporaneamente a molti gas che reagiscono con la superficie calda di SnO2 e non sa distinguerli. Il produttore lo descrive come particolarmente sensibile ad ammoniaca, composti solforati e vapori di tipo benzene; risponde chiaramente anche ad alcol, fumo e ossidi di azoto. Per questo è meglio considerare l'MQ-135 un «indicatore di inquinamento dell'aria»: quando il valore sale ti dice che qualcosa nell'aria è cambiato, ma non cosa. Per le fughe di metano o GPL è molto più adatto il sensore di gas MQ-2, progettato proprio per questo.
| Gas | Da dove arriva in casa? | Risposta dell'MQ-135 |
|---|---|---|
| Ammoniaca (NH3) | Detersivi, lettiera del gatto, concime | Alta |
| Benzene, toluene e solventi | Vernici, diluenti, colla, fumo di sigaretta | Alta |
| Vapori di alcol | Gel igienizzante, acqua di colonia, profumo | Alta |
| Fumo | Cucina, sigarette, candele | Media |
| Ossidi di azoto (NOx) | Fornello a gas, gas di scarico | Media |
| Anidride carbonica (CO2) | Respiro umano, stanze chiuse e affollate | Debole, indiretta |
L'MQ-135 misura la CO2?
Risposta breve: no, non davvero; la stima. Il datasheet dell'MQ-135 contiene una curva per la CO2 e la maggior parte delle librerie online ne ricava un valore di «CO2 in ppm». Ma la CO2 è un gas chimicamente stabile e reagisce pochissimo con la superficie di SnO2. In una stanza chiusa, mentre le persone respirano, insieme alla CO2 aumentano l'umidità e i composti organici volatili emessi dal corpo, ed è soprattutto a questi che reagisce l'MQ-135. Quindi la «CO2» mostrata dal sensore è in realtà una stima ricavata dall'inquinamento generale dell'aria della stanza. La prova più semplice: apri accanto una boccetta di acqua di colonia e vedrai la «CO2» schizzare a 5000 ppm.
Questo non vuol dire che l'MQ-135 sia inutile. Funziona benissimo per decisioni relative come «la stanza è diventata viziata, apri la finestra» o «c'è fumo in cucina, accendi la cappa». Se però ti serve il valore reale di CO2 in un'aula, un ufficio o una camera da letto, dovresti usare uno dei sensori di CO2 NDIR del confronto alla fine di questa guida. La tabella riassume cosa significano le letture di un vero misuratore di CO2; puoi interpretare le stime dell'MQ-135 con gli stessi intervalli.
| CO2 (ppm) | Che cosa significa? |
|---|---|
| 400–450 | Aria esterna pulita |
| 450–800 | Ambiente interno ben ventilato |
| 800–1000 | Accettabile, pensa a ventilare |
| 1000–1500 | Aria viziata, cala la concentrazione; arieggia |
| 1500–2000 | Possibili mal di testa e sonnolenza |
| 5000 | Limite di esposizione professionale su 8 ore |
Collegamento dell'MQ-135 ad Arduino
| Pin del modulo | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| VCC (riscaldatore incluso) | 5V | VIN (5 V dall'USB) | VBUS (5 V dall'USB) |
| GND | GND | GND | GND |
| AO (uscita analogica) | A0 | GPIO 34, tramite partitore 10 kΩ / 20 kΩ | GP26 (ADC0), tramite partitore 10 kΩ / 20 kΩ |
| DO (soglia digitale, facoltativa) | D2 | Qualsiasi GPIO tramite partitore | Qualsiasi pin GP tramite partitore |
Preriscaldamento: il rodaggio di 24–48 ore e il riscaldamento quotidiano
I sensori della serie MQ hanno due riscaldamenti distinti, spesso confusi. Il primo è il preriscaldamento iniziale (rodaggio o burn-in): sulla superficie di un sensore nuovo, o rimasto a lungo in magazzino senza uso, si accumulano umidità e impurità. Il produttore chiede di alimentarlo per più di 24 ore prima delle misure standard; in pratica, lasciarlo acceso di continuo per 24–48 ore ti permette di vedere i valori spostarsi lentamente e alla fine stabilizzarsi. Se calibri R0 prima che sia trascorso questo tempo, tutte le misure deriveranno nel giro di pochi giorni. Il secondo è il riscaldamento quotidiano: anche un sensore rodato ha bisogno di qualche minuto a ogni accensione perché il riscaldatore arrivi all'equilibrio; i valori del primo minuto sono chiaramente troppo alti o troppo bassi. Per questo il codice aspetta almeno 2–3 minuti; per misure precise è meglio aspettare 10–20 minuti.
Rs, RL e R0: come si calcolano i ppm con l'MQ-135?
Nel modulo la resistenza del sensore Rs e la resistenza di carico RL della scheda sono in serie e si dividono i 5 V. La tensione sul pin AO è Vout = 5 × RL / (Rs + RL). Invertendo la formula ricavi la resistenza attuale del sensore dalla tensione misurata: Rs = RL × (5 − Vout) / Vout. Su Arduino Uno l'ADC usa gli stessi 5 V come riferimento, quindi non serve nemmeno convertire in volt: Rs = RL × (1023 − valore grezzo) / valore grezzo. Da sola Rs dice poco, perché la resistenza cambia moltissimo da un sensore all'altro. Conta il suo rapporto con il valore che ha in un'aria nota, chiamato R0.
Le curve del datasheet legano il rapporto Rs/R0 alla concentrazione del gas e in scala log-log sono quasi rette. Per questo si possono scrivere come funzione di potenza: ppm = A × (Rs/R0)^−B. I coefficienti che le librerie MQ-135 più diffuse usano per la curva della CO2 sono A = 116,602 e B = 2,769. Per trovare R0 metti il sensore in aria esterna pulita; poiché oggi l'aria esterna contiene circa 420 ppm di CO2, inverti la formula e calcoli R0 = Rs × (420 / A)^(1/B) ≈ Rs × 1,59. Ricorda che questi coefficienti sono diversi per altri gas e che il risultato è solo una stima.
Calibrazione di R0 dell'MQ-135 passo dopo passo
- Collega VCC del modulo a 5V, GND a GND e AO ad A0, e completa il preriscaldamento iniziale lasciando acceso il sensore senza interruzioni per 24–48 ore.
- Leggi il codice della resistenza di carico sul retro della scheda e imposta di conseguenza RL_KOHM nel codice qui sotto.
- Porta il circuito in aria esterna pulita, per esempio su un balcone o davanti a una finestra aperta, lontano da traffico, sigarette e cucina.
- Carica il codice di calibrazione, apri il monitor seriale a 9600 baud e aspetta almeno 10 minuti.
- Quando la media di R0 sullo schermo non cambia quasi più per diversi minuti, annotala: è l'R0 del tuo sensore.
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}Ogni secondo il codice fa la media di 50 letture, ne ricava Rs e l'R0 del momento e aggiunge questo valore a una media mobile lenta. La media mobile evita che uno sbuffo di gas di scarico portato dal vento rovini il risultato. L'R0 che trovi vale solo per quel sensore e quel valore di RL; se cambi sensore, ripeti la calibrazione. Nel corso dei mesi il sensore invecchia e R0 si sposta lentamente, quindi nei progetti di lunga durata è una buona abitudine ricalibrare ogni qualche mese.
Codice di misura: ppm stimati e livello di qualità dell'aria
Trovato R0, lo inserisci nel codice di misura. Il codice aspetta prima 3 minuti di riscaldamento e poi ogni 2 secondi scrive nel monitor seriale il rapporto Rs/R0, i ppm stimati e un'etichetta di qualità dell'aria secondo la tabella qui sopra. In aria pulita il rapporto è intorno a 0,6; più scende, più gas c'è nell'aria. Per provarlo, metti qualche goccia di acqua di colonia su un batuffolo di cotone vicino al sensore: in pochi secondi il rapporto cala e i ppm salgono; togliendo il cotone, i valori tornano normali in pochi minuti.
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}Come influiscono temperatura e umidità sull'MQ-135?
La resistenza dei sensori a ossido metallico non dipende solo dal gas, ma anche da temperatura e umidità ambiente. Secondo la curva del datasheet, nella stessa aria il rapporto Rs/R0 cala in modo evidente quando la temperatura passa da 20 °C a 40 °C, e cala anche quando l'umidità aumenta; il sensore lo interpreta come «più gas». Il risultato sono ppm che salgono da soli in una stanza col sole del pomeriggio o in bagno dopo la doccia. Ci sono due modi per ridurre l'effetto: calibrare a temperatura e umidità simili a quelle del luogo d'uso, oppure misurare temperatura e umidità con un DHT22 o DHT11 e applicare la formula di correzione delle librerie. Funzioni come getCorrectedPPM(temperatura, umidità) delle librerie MQ135 fanno proprio questo. Se vuoi misurare temperatura e umidità con più precisione di un DHT11, puoi usare un sensore BME280.
Automatizzare una ventola di aerazione
Il compito più utile dell'MQ-135 è accendere da solo una ventola o una cappa quando l'aria diventa pesante. Due dettagli sono importanti. Il primo è l'isteresi: la ventola si accende sopra 1000 ppm ma si spegne solo quando il valore scende sotto 800 ppm; con una sola soglia il relè scatterebbe ogni secondo mentre il valore oscilla attorno a essa. Il secondo è un tempo minimo di funzionamento: una volta accesa, la ventola gira almeno 2 minuti, così l'aria della stanza si rinnova davvero. Il codice inoltre smussa le letture perché i picchi momentanei non facciano partire la ventola.
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}Il pin IN del modulo relè va a D7, VCC e GND ad Arduino; la ventola si collega alla sua alimentazione attraverso i morsetti COM e NO del relè. Con una ventola da PC a 12 V, un MOSFET è più silenzioso e dura più di un relè; il collegamento è lo stesso del circuito della mini pompa acqua. Se devi commutare un apparecchio a 230 V come l'aspiratore del bagno, segui alla lettera le regole di sicurezza della guida al modulo relè: chiudi la parte di rete in una scatola e non toccare mai il circuito sotto tensione. Portare prima il pin al livello di spento e solo dopo impostarlo come uscita in setup() evita che i relè attivi bassi scattino per un attimo all'accensione.
Registrare la qualità dell'aria con un ESP32
Vedere come cambia la qualità dell'aria durante la giornata insegna molto più di una lettura singola: solo un registro mostra l'aria viziata che si accumula in camera al mattino, il picco in cucina mentre cucini o l'effetto di aprire la finestra. Il codice ESP32 qui sotto misura una volta al minuto, prende l'ora da internet (NTP) per mettere data e ora su ogni riga e salva il risultato in CSV nel file system LittleFS della memoria flash dell'ESP32. Aprendo l'IP dell'ESP32 nel browser vedi l'ultima misura e scarichi con un clic tutto il registro come file CSV che si apre in Excel. Non serve nessuna libreria aggiuntiva.
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}Poiché l'ADC dell'ESP32 misura in modo diverso da quello dell'Uno, è meglio ripetere la calibrazione di R0 con questo circuito; basta adattare la funzione readRs() del codice di calibrazione con la logica di readPpm(). Collega il sensore a uno dei pin ADC1, tra GPIO 32 e 39: i pin ADC2 non si possono usare con il Wi-Fi acceso. Ogni riga occupa circa 22 byte; con una registrazione al minuto si accumulano circa 32 KB al giorno, e l'area da 1,5 MB dello schema di partizioni predefinito basta per circa un mese e mezzo. Per registri più lunghi puoi usare un modulo scheda SD, e se vuoi mandare i dati direttamente in un foglio di calcolo guarda il metodo ESP32 con Google Sheets.
MQ-135 con Raspberry Pi Pico (MicroPython)
Sul Pico alimenti il modulo a 5 V dal pin VBUS e colleghi l'uscita AO a GP26, sempre tramite un partitore da 10 kΩ / 20 kΩ. L'ADC del Pico restituisce un valore a 16 bit (0–65535) riferito a 3,3 V; il codice lo converte prima in tensione e poi, con il rapporto del partitore, nell'uscita del sensore. La formula è la stessa di Arduino. Poiché VBUS può variare tra 4,8 e 5,1 V a seconda della sorgente USB, per risultati precisi misura VBUS con un multimetro e correggi il 5.0 nel codice.
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)Confronto: MQ-135, CCS811 e sensore di CO2 NDIR
Esistono tre classi di sensori di qualità dell'aria e ognuna risponde a una domanda diversa. L'MQ-135 è economico, analogico e semplice; mostra le variazioni generali dell'aria. Il CCS811 è anch'esso un sensore a ossido metallico, ma ha un proprio microcontrollore, gestisce da solo il riscaldatore e fornisce direttamente via I2C il TVOC (composti organici volatili totali) e l'eCO2 (CO2 equivalente); ne parliamo nella guida al sensore di qualità dell'aria CCS811. Gli unici a misurare la CO2 reale sono i sensori NDIR (infrarosso non dispersivo): moduli come l'MH-Z19B o il SenseAir S8 misurano quanta luce infrarossa a 4,26 µm viene assorbita dalle molecole di CO2 e risentono pochissimo degli altri gas.
| Caratteristica | MQ-135 | CCS811 | NDIR (MH-Z19B, SenseAir S8) |
|---|---|---|---|
| Principio di misura | Resistenza dell'SnO2 riscaldato | Ossido metallico + microcontrollore integrato | Assorbimento di luce infrarossa |
| Misura la CO2 reale? | No, la stima | No, calcola l'eCO2 dai VOC | Sì |
| Uscita | Analogica (più soglia digitale) | I2C | UART o PWM |
| Alimentazione | 5 V, circa 150 mA | 3,3 V, circa 30 mA in media | 5 V, meno di 60 mA in media |
| Riscaldamento | 24–48 ore la prima volta, poi qualche minuto | 48 ore la prima volta, poi 20 minuti | Circa 3 minuti |
| Precisione | Bassa, per variazioni relative | Media, per seguire i VOC in modo relativo | Circa ±50 ppm + 3–5 % della lettura |
| Prezzo | Il più economico | Medio | Il più caro |
In breve: per imparare, far scattare un allarme o una ventola e seguire le variazioni dell'aria, l'MQ-135 basta. Se vuoi tenere d'occhio i vapori di vernici, colle o detersivi, il CCS811 dà dati più regolari e stabili. Se in un'aula, un ufficio o una camera ti serve un numero affidabile per capire se è ora di aprire la finestra, devi investire in un sensore di CO2 NDIR. Molti usano entrambi: il sensore NDIR misura la CO2 e l'MQ-135 coglie eventi improvvisi come fumo o odore di solventi in cucina.
Idee di progetti con l'MQ-135
- Indicatore dell'aria della stanza: Un LED RGB che diventa verde, giallo o rosso in base alla qualità dell'aria, o un grafico in tempo reale su un display OLED.
- Cappa da cucina intelligente: Un sistema che accende la cappa quando fumo e vapore aumentano durante la cottura e la spegne quando l'aria è pulita.
- Avviso per garage e laboratorio di verniciatura: Un circuito che avvisa con un buzzer e avvia la ventilazione quando salgono i vapori di solvente.
- Controllo della lettiera: Un progetto ESP32 che avvisa il telefono quando l'ammoniaca sale ed è ora di cambiare la lettiera.
- Pannello dell'aria in classe: Un grafico che registra le misure con un ESP32 e le confronta con l'orario delle lezioni per mostrare le abitudini di ricambio d'aria.
- Domotica: Invia i valori a Home Assistant via MQTT e accendi un purificatore con una presa smart quando l'aria peggiora.
Errori comuni e come risolverli
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
| I valori derivano per ore | Preriscaldamento iniziale non completato | Tieni acceso il sensore 24–48 ore di fila, poi calibra |
| Valori troppo alti o bassi all'accensione | Non si è atteso il riscaldamento quotidiano | Aspetta almeno 3 minuti, meglio 10–20, prima di misurare |
| Migliaia di ppm anche in aria pulita | R0 sbagliato o calibrazione fatta al chiuso | Ripeti la calibrazione in aria esterna pulita |
| Sull'ESP32 i valori sono sempre al massimo | AO arriva a 5 V o il sensore è alimentato a 3,3 V | Alimenta il sensore a 5 V e metti un partitore 10k/20k su AO |
| Arduino si scalda o l'USB si disconnette | Il riscaldatore da 150 mA e altri carichi affaticano l'USB | Usa un'alimentazione esterna a 5 V e unisci le masse |
| I ppm salgono col sole o dopo la doccia | Effetto di temperatura e umidità | Calibra in condizioni simili o applica la correzione con un DHT22 |
Se ne conosci i limiti, l'MQ-135 è un ottimo sensore per iniziare a capire la qualità dell'aria e automatizzare la ventilazione: alimentalo a 5 V, preriscaldalo 24–48 ore, trova R0 in aria pulita, leggi i risultati in modo relativo e passa a un sensore NDIR quando ti serve la CO2 reale. Nell'app BlueGrays trovi i progetti Sensore di gas, Allarme con sensore di fiamma e gas e Rilevatore di gas MQ-2 con ESP32, insieme a progetti di temperatura e umidità con DHT11 e DHT22 e un registratore di temperatura con ESP32, con schemi e codice passo dopo passo.
Domande frequenti
L'MQ135 misura la CO2?
L'MQ-135 non misura davvero la CO2, la stima. Il sensore risponde poco alla CO2; reagisce soprattutto a gas come alcol, ammoniaca e benzene e ai composti volatili emessi dalle persone. Il valore di CO2 in ppm mostrato dalle librerie è calcolato da questo inquinamento generale; per la CO2 reale serve un sensore NDIR come l'MH-Z19B.
Quanto tempo impiega l'MQ135 a scaldarsi?
Un MQ-135 nuovo richiede un preriscaldamento iniziale di 24–48 ore, da completare prima della calibrazione. Dopo, a ogni accensione il sensore è utilizzabile in circa 2–3 minuti; per i valori più stabili è meglio aspettare 10–20 minuti.
Come si trova il valore R0 dell'MQ135?
Porta il sensore già preriscaldato in aria esterna pulita, calcola la sua resistenza con Rs = RL × (1023 − grezzo) / grezzo e, considerando circa 420 ppm di CO2 all'esterno, calcola R0 = Rs × (420 / 116,6)^(1 / 2,769). Fai la media di alcuni minuti di letture. L'R0 trovato vale solo per quel sensore e quella resistenza di carico.
Che differenza c'è tra MQ135 e MQ2?
Sono entrambi sensori a ossido metallico riscaldati, ma tarati su gas diversi. L'MQ-2 è adatto a GPL, propano, metano, idrogeno e fumo, quindi agli allarmi per fughe di gas. L'MQ-135 è sensibile agli inquinanti dell'aria come ammoniaca, benzene, alcol, NOx e fumo e si usa per monitorare la qualità dell'aria.
Si può usare l'MQ135 con un ESP32?
Sì, ma attenzione a due cose. Il riscaldatore vuole 5 V, quindi alimenta il modulo dal pin VIN dell'ESP32. Anche l'uscita AO può arrivare a 5 V: abbassala a 3,3 V con un partitore da 10 kΩ e 20 kΩ e collegala a un pin ADC1 tra GPIO 32 e 39.
Quali gas rileva l'MQ135?
L'MQ-135 è più sensibile ad ammoniaca, composti solforati e vapori aromatici come il benzene. Risponde chiaramente anche ad alcol, fumo e ossidi di azoto, e debolmente a CO2 e monossido di carbonio. Non sa distinguere questi gas tra loro; mostra solo la variazione complessiva dell'aria.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
Temperatura e umidità con DHT11
Quanti gradi ci sono nella tua stanza, e l'aria è umida o secca? Il sensore DHT11 misura temperatura e umidità, e l'Arduino scrive i valori su uno schermo LCD a due righe, aggiornandoli ogni paio di s

Utilizzo del sensore di gas
Un piccolo guardiano che si accorge di una fuga di gas in cucina prima di te. Il sensore di gas annusa l'aria di continuo; finché va tutto bene resta acceso il LED verde, e quando il gas nell'aria sup

Allarme del sensore di fiamma e gas
Un piccolo guardiano che ti avvisa subito se c'è una fuga di gas o compare una fiamma da qualche parte. Il sensore di gas controlla l'aria e il sensore di fiamma cerca la luce del fuoco; finché va tut

Allarme rilevatore di gas/fumo ESP32 MQ-2
In questo progetto, il rilevamento di gas combustibili (GPL, metano, propano) e fumo viene effettuato tramite un modulo sensore di gas MQ-2; al rilevamento viene emesso un segnale acustico simile a una sirena tramite un cicalino passivo. L'uscita digitale di soglia (DO) del sensore viene collegata al GPIO 4 dell'ESP32; quando gas o fumo superano la soglia, DO passa a LOW e l'allarme si attiva. La sensibilità si regola tramite il potenziometro del modulo. Il cicalino è pilotato con tone().

ESP32 DHT22 Temperatura e umidità (con display LCD)
Si tratta di un progetto che misura la temperatura e l'umidità dell'ambiente tramite il sensore DHT22 e le visualizza in tempo reale su un display LCD I2C 16x2. I dati vengono letti dal sensore utilizzando il protocollo one-wire tramite la libreria Adafruit DHT. Questo progetto costituisce la base per stazioni meteorologiche IoT, sistemi di automazione domestica intelligente o sistemi di monitoraggio ambientale nelle serre. La comunicazione con il DHT22 avviene tramite il pin GPIO 4 dell'ESP32.

Data Logger di Temperatura ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
Costruisci un data logger di temperatura con timestamp usando ESP32! Leggi temperatura/umidità dal DHT22 ogni minuto, ottieni l'ora reale dal DS1307 RTC e registra su scheda microSD in formato CSV. L'OLED mostra dal vivo temperatura (font grande), percentuale di umidità, timestamp e contatore di log. Il file /temp_log.csv sulla scheda SD si apre in Excel, Google Sheets o Python pandas per l'analisi — perfetto per il monitoraggio di serre, tracciamento della temperatura domestica, raccolta

Schermo LCD I2C per ESP32 (16x2)
In questo progetto visualizziamo del testo collegando un display LCD a 16x2 caratteri all'ESP32 tramite il protocollo I2C. Grazie all'I2C possiamo controllare il display utilizzando solo 2 pin di dati (SDA e SCL); si elimina così lo spreco dei 6+ pin tipici del collegamento parallelo classico. Il codice stampa un messaggio fisso sulla prima riga e un contatore che aumenta ogni secondo sulla seconda riga. Inoltre, all'inizio, eseguendo lo scanner I2C, stampiamo l'indirizzo del display sul Serial

Lampadina 220V con pulsante (Push)
Finché tieni premuto il pulsante, una vera lampadina da 220 volt resta accesa, e si spegne appena lo lasci. Il piccolo segnale dell'Arduino non può accendere la lampadina direttamente, quindi in mezzo
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 200 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

