Cảm biến khí MQ-2 và MQ-135 với ESP32: làm nóng, hiệu chuẩn R0, ước tính ppm và cảnh báo khí gas

Cảm biến khí MQ hoạt động thế nào, cầu phân áp cho chân AO, tính Rs, hiệu chuẩn R0 trong không khí sạch, đổi ra ppm gần đúng, cảnh báo có ngưỡng trễ và giới hạn của MQ-135.

Module cảm biến khí MQ-2 (khí gas, khói) và MQ-135 (chất lượng không khí) rẻ và dễ mua, nhưng hay bị dùng sai: nối thẳng chân AO vào ESP32, đọc ngay khi vừa cấp điện, rồi tin con số "ppm" như máy đo thật. Bài này giải thích cảm biến hoạt động thế nào, vì sao phải làm nóng, cách tính điện trở Rs và hiệu chuẩn R0 trong không khí sạch, cách đổi ra nồng độ gần đúng và cách làm cảnh báo có ngưỡng trễ, cùng những giới hạn cần biết.

Lưu ý an toàn: mạch trong bài dùng để học và thử nghiệm. Nơi có bếp gas, bình gas cần đầu báo rò rỉ gas được chứng nhận, lắp đúng vị trí theo hướng dẫn của nhà sản xuất.

1. Cảm biến khí MQ hoạt động thế nào

Hình cắt ngang cảm biến MQ: lưới thép bên ngoài, ống gốm có lớp thiếc oxit, dây nung bên trong, điện cực đo Rs; hai khung giải thích không khí sạch thì điện trở cao, có khí gas thì điện trở giảm
Lớp thiếc oxit được nung nóng; điện trở của nó giảm khi gặp khí cháy hoặc khí lạ (hình minh họa).

Bên trong vỏ lưới thép là một ống gốm phủ lớp thiếc oxit (SnO2), có dây nung ở giữa giữ lớp này ở vài trăm độ C. Ở nhiệt độ đó, oxy trong không khí bám lên bề mặt và giữ electron, nên điện trở Rs của lớp SnO2 cao. Khi có khí khử (gas LPG, khí metan, khói, hơi cồn, CO, H2), các khí này phản ứng với oxy trên bề mặt, trả electron lại, Rs giảm xuống. Càng nhiều khí, Rs càng nhỏ.

Hai loại khác nhau ở công thức lớp nhạy và độ nhạy với từng khí:

  • MQ-2: nhạy với LPG, propan, metan, H2, khói. Hợp để thử nghiệm phát hiện rò rỉ gas, khói.
  • MQ-135: nhạy với NH3, hơi cồn, benzen, khói và nhiều khí khác. Hay được gọi là cảm biến "chất lượng không khí", nhưng nó không cho biết đó là khí gì.

Dây nung cần khoảng 150 mA ở 5 V. Datasheet khuyên cấp điện liên tục 24–48 giờ trong lần dùng đầu tiên để cảm biến ổn định; những lần sau vẫn phải chờ vài phút sau khi cấp điện mới đọc.

2. Mạch đo trên module và cầu phân áp

Sơ đồ nguyên lý: VCC 5 V qua cảm biến Rs tới điểm AO, AO qua điện trở tải 1 kΩ xuống GND trên module; từ AO qua điện trở 10 kΩ tới chân D34, từ D34 qua 20 kΩ xuống GND; kèm công thức AO, Rs
Cảm biến và điện trở tải tạo cầu phân áp trên module; thêm cầu 10 kΩ / 20 kΩ trước khi vào ESP32 (hình minh họa).

Trên module, cảm biến Rs và điện trở tải (thường 1 kΩ, ghi chữ 102) nối thành cầu phân áp từ 5 V xuống GND. Điện áp ở giữa là chân AO:

  • AO = 5 V × R tải / (Rs + R tải): ít khí, Rs lớn, AO thấp; nhiều khí, Rs nhỏ, AO lên gần 5 V.
  • Đảo lại: Rs = R tải × (5 V − AO) / AO. Chương trình đo AO rồi tính Rs theo công thức này.

Vì AO có thể lên gần 5 V, không nối thẳng AO vào ESP32. Dùng cầu phân áp 10 kΩ / 20 kΩ: chân ESP32 chỉ nhận 2/3 điện áp AO, tối đa khoảng 3,33 V. Chương trình nhân lại với 1,5 để ra điện áp AO thật. Cách đọc ADC và cầu phân áp được giải thích kỹ hơn trong bài đọc ADC trên ESP32 bằng cầu phân áp.

Chân DO là ngõ ra so sánh (IC LM393): lên hoặc xuống mức khi AO vượt ngưỡng chỉnh bằng biến trở trên module. DO cũng kéo lên 5 V, và chương trình đã tự so ngưỡng nên bài không dùng chân này.

3. Linh kiện cần chuẩn bị

4. Nối MQ-2 và MQ-135 với ESP32

Sơ đồ nối hai module MQ-2 và MQ-135 với ESP32 DevKit V1: VCC vào VIN, GND vào GND, chân DO để trống, AO của MQ-2 vào D34, AO của MQ-135 vào D35 qua cầu phân áp
VCC vào VIN (5 V), AO qua cầu phân áp vào D34 và D35; chân DO không dùng (hình minh họa).
ModuleChânESP32 DevKit V1
MQ-2, MQ-135VCCVIN (5 V khi cắm USB)
MQ-2, MQ-135GNDGND
MQ-2AO10 kΩ → D34; D34 → 20 kΩ → GND
MQ-135AO10 kΩ → D35; D35 → 20 kΩ → GND
MQ-2, MQ-135DOĐể trống

D34, D35 là chân chỉ nhận vào, thuộc khối ADC1 nên vẫn đọc được khi bật Wi-Fi. Đèn xanh trên bo (GPIO2) dùng làm đèn cảnh báo.

5. Hiệu chuẩn R0 và đổi ra nồng độ

Đồ thị trục log: nồng độ LPG theo tỉ số Rs trên R0 của MQ-2 là một đường thẳng đi xuống, phần ngoài 200 đến 10000 ppm vẽ nét đứt; chấm đỏ ở 1000 ppm ứng với Rs/R0 bằng 0,78; đường dọc ở 9,83 là không khí sạch
Đường LPG của MQ-2 dùng trong chương trình, trục log (đường khớp gần đúng từ đồ thị datasheet).

Mỗi cảm biến có Rs khác nhau dù cùng loại, nên datasheet không cho Rs mà cho tỉ số Rs/R0, trong đó R0 là Rs của chính cảm biến đó ở một nồng độ chuẩn. Đồ thị datasheet còn cho biết tỉ số này trong không khí sạch: khoảng 9,83 với MQ-2 và 3,6 với MQ-135. Từ đó:

  1. Làm nóng cảm biến, đặt ở nơi thoáng, không khí sạch (không gần bếp, không có cồn, nước hoa).
  2. Đo Rs, lấy trung bình 50 lần: đó là Rs trong không khí sạch.
  3. R0 = Rs không khí sạch / 9,83 (MQ-2), hoặc / 3,6 (MQ-135).

Chương trình lưu R0 vào bộ nhớ flash bằng thư viện Preferences (xem bài lưu dữ liệu khi mất điện bằng Preferences), nên không phải hiệu chuẩn lại mỗi lần cấp điện. Điện trở tải R tải có mặt ở cả Rs đo được lẫn R0, nên giá trị của nó không làm sai tỉ số Rs/R0, chỉ làm sai số Rs in ra.

Trên đồ thị log-log, đường của mỗi khí gần như thẳng, nên viết được thành ppm ≈ a × (Rs/R0)b. Với LPG của MQ-2, chương trình dùng a = 574,25, b = −2,222 (đường khớp dùng phổ biến, lấy từ đồ thị datasheet). Đồ thị gốc chỉ có từ 200 đến 10 000 ppm, nên dưới 200 ppm chương trình chỉ in "duoi 200 ppm" thay vì một con số đoán.

MQ-135 thì chương trình không đổi ra ppm, chỉ in Rs so với lúc không khí sạch: 1,00 là như lúc hiệu chuẩn, càng nhỏ càng có nhiều khí lạ. Nhiều bài trên mạng dùng MQ-135 để "đo CO2", nhưng cảm biến này không chọn lọc CO2 và gần như không phản ứng với CO2 ở nồng độ trong nhà; muốn đo CO2 cần cảm biến hồng ngoại (NDIR).

6. Chương trình

Chương trình chỉ dùng thư viện có sẵn trong gói esp32. Ba phút đầu chỉ in điện áp AO để thấy cảm biến đang nóng lên; sau đó nếu chưa có R0 thì nhắc hiệu chuẩn. Gõ C trên Serial Monitor khi cảm biến ở không khí sạch.

// PYWORLD.VN - Cảm biến khí MQ-2 và MQ-135 với ESP32: làm nóng, hiệu chuẩn R0 trong không khí sạch, ước tính nồng độ và cảnh báo
// Module cấp 5 V. AO của MQ-2 qua cầu phân áp 10 kΩ / 20 kΩ vào D34, AO của MQ-135 qua cầu phân áp tương tự vào D35. Đèn báo GPIO2
// Gõ C trên Serial Monitor khi cả hai cảm biến đang ở không khí sạch để hiệu chuẩn; R0 được lưu lại, mất điện không mất

#include <Preferences.h>

const float VCC_MODULE = 5.0;          // điện áp cấp cho module (đo lại chân VCC nếu muốn chính xác hơn)
const float HE_SO_PHAN_AP = 1.5;       // (10 kΩ + 20 kΩ) / 20 kΩ: điện áp AO thật = điện áp đo được × 1,5
const float R_TAI_KOHM = 1.0;          // điện trở tải trên module (chữ 102 = 1 kΩ, 103 = 10 kΩ); chỉ ảnh hưởng số Rs in ra
const uint32_t THOI_GIAN_NONG_MS = 180000;   // chờ 3 phút cho dây nung ổn định trước khi đo
const int CHAN_DEN = 2;

struct CamBien {
  const char *ten;
  int chan;
  float tiSoKhongKhi;                  // Rs/R0 trong không khí sạch, đọc từ đồ thị datasheet
  float a, b;                          // ppm ≈ a × (Rs/R0)^b, đường thẳng trên đồ thị log-log (chỉ dùng cho MQ-2)
  float r0;                            // kΩ, có sau khi hiệu chuẩn
};

CamBien mq[2] = {
  {"MQ-2", 34, 9.83, 574.25, -2.222, 0},          // đường LPG
  {"MQ-135", 35, 3.6, 0, 0, 0},                    // không đổi ra ppm: chỉ so với lúc không khí sạch
};

const float NGUONG_BAO_LPG = 1000;     // ppm: bật cảnh báo (khoảng 5% giới hạn cháy nổ dưới của LPG)
const float NGUONG_TAT_LPG = 800;      // ppm: tắt cảnh báo (có khoảng trễ để đèn không nhấp nháy ở ngưỡng)
Preferences boNho;
bool canhBao = false;

float docAoV(int chan) {               // trung bình 64 lần đọc, trả về điện áp thật ở chân AO
  uint32_t tong = 0;
  for (int i = 0; i < 64; i++) tong += analogReadMilliVolts(chan);
  return tong / 64.0f / 1000.0f * HE_SO_PHAN_AP;
}

// Module là cầu phân áp: VCC → Rs (cảm biến) → AO → R_tải → GND, nên Rs = R_tải × (VCC − AO) / AO.
float docRs(const CamBien &c) {
  float v = docAoV(c.chan);
  if (v < 0.01f) v = 0.01f;
  return R_TAI_KOHM * (VCC_MODULE - v) / v;
}

void hieuChuan() {
  for (int k = 0; k < 2; k++) {
    float tong = 0;
    for (int i = 0; i < 50; i++) {
      tong += docRs(mq[k]);
      delay(20);
    }
    mq[k].r0 = tong / 50 / mq[k].tiSoKhongKhi;          // R0 = Rs trong không khí sạch / tỉ số của datasheet
    boNho.putFloat(mq[k].ten, mq[k].r0);
    Serial.printf("Hieu chuan %s: R0 = %.2f kOhm\n", mq[k].ten, mq[k].r0);
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(CHAN_DEN, OUTPUT);
  analogSetAttenuation(ADC_11db);                         // đọc được tới khoảng 3,1 V
  boNho.begin("mq", false);
  for (int k = 0; k < 2; k++) mq[k].r0 = boNho.getFloat(mq[k].ten, 0);
  Serial.println("Dang lam nong day nung, cho 3 phut...");
}

void loop() {
  if (Serial.available()) {
    char c = Serial.read();
    if ((c == 'C' || c == 'c') && millis() >= THOI_GIAN_NONG_MS) hieuChuan();
  }
  static uint32_t lanIn = 0;
  if (millis() - lanIn < 2000) return;
  lanIn = millis();

  if (millis() < THOI_GIAN_NONG_MS) {
    Serial.printf("Lam nong: con %lu giay | MQ-2 AO %.2f V | MQ-135 AO %.2f V\n",
                  (unsigned long)((THOI_GIAN_NONG_MS - millis()) / 1000), docAoV(mq[0].chan), docAoV(mq[1].chan));
    return;
  }
  if (mq[0].r0 <= 0 || mq[1].r0 <= 0) {
    Serial.println("Chua hieu chuan: dat cam bien o khong khi sach roi go C");
    return;
  }

  float rs2 = docRs(mq[0]), rs135 = docRs(mq[1]);
  float tiSo2 = rs2 / mq[0].r0, tiSo135 = rs135 / mq[1].r0;
  float lpg = mq[0].a * powf(tiSo2, mq[0].b);
  float sachTuongDoi = tiSo135 / mq[1].tiSoKhongKhi;     // 1,00 = như lúc hiệu chuẩn; càng nhỏ càng nhiều khí lạ

  if (!canhBao && lpg >= NGUONG_BAO_LPG) canhBao = true;
  if (canhBao && lpg <= NGUONG_TAT_LPG) canhBao = false;
  digitalWrite(CHAN_DEN, canhBao);

  char chuLpg[24];                                       // đường cong chỉ có từ 200 ppm: dưới đó không ghi số
  if (lpg < 200) snprintf(chuLpg, sizeof(chuLpg), "duoi 200 ppm");
  else snprintf(chuLpg, sizeof(chuLpg), "~%.0f ppm", lpg);
  Serial.printf("MQ-2: Rs %.1f kOhm, Rs/R0 %.2f, LPG %s | MQ-135: Rs/Rs(sach) %.2f%s\n", rs2, tiSo2, chuLpg,
                sachTuongDoi, canhBao ? " | CANH BAO KHI GAS" : "");
}
  • docAoV() lấy trung bình 64 lần analogReadMilliVolts() (đã bù sai lệch ADC theo dữ liệu hiệu chuẩn trong chip) rồi nhân 1,5.
  • docRs() tính Rs theo công thức cầu phân áp; hieuChuan() lấy trung bình 50 lần, chia cho tỉ số không khí sạch và lưu vào flash.
  • Cảnh báo bật khi LPG ước tính từ 1 000 ppm (khoảng 5% giới hạn cháy nổ dưới của LPG), tắt khi xuống 800 ppm. Hai ngưỡng khác nhau tạo khoảng trễ, để đèn không bật tắt liên tục khi số đo dao động quanh ngưỡng.

7. Kết quả mong đợi

Serial Monitor: các dòng làm nóng với điện áp AO giảm dần, dòng nhắc hiệu chuẩn, dòng R0 của MQ-2 và MQ-135, các dòng đo trong không khí sạch dưới 200 ppm, khi đưa bật lửa lại gần LPG lên khoảng 1333 và 2094 ppm kèm CANH BAO KHI GAS, rồi giảm dần
Làm nóng, hiệu chuẩn rồi đưa bật lửa (chưa đánh lửa) lại gần cảm biến (số liệu ví dụ).

Lúc mới cấp điện, AO thường cao rồi giảm dần trong vài phút: dây nung đang đốt sạch hơi ẩm, khí bám trên bề mặt. Để thử, nhấn nhẹ van bật lửa gas cho xì một chút gas (không đánh lửa) cách cảm biến vài cm: Rs giảm mạnh, đèn cảnh báo sáng, rồi tự tắt khi gas tan. Làm thử ở nơi thoáng, xa lửa.

Rs còn thay đổi theo nhiệt độ và độ ẩm không khí: trời nồm, độ ẩm cao làm Rs thấp hơn cùng một nồng độ khí. Số ppm vì vậy chỉ nên xem là mức tương đối "ít, nhiều, tăng, giảm".

8. Xử lý lỗi thường gặp

Hiện tượngNguyên nhân thường gặpCách xử lý
AO luôn gần 0 VDây nung không nóng: thiếu nguồn 5 V, cổng USB yếuĐo 5 V ở VCC module; dùng nguồn 5 V riêng, GND chung
Số đo trôi mãi không ổn địnhCảm biến mới, chưa qua thời gian cấp điện ban đầuCấp điện liên tục 24–48 giờ rồi hiệu chuẩn lại
Vừa hiệu chuẩn đã báo nhiều khíHiệu chuẩn khi chưa nóng đủ hoặc ở nơi có khíChờ hết 3 phút, ra chỗ thoáng, gõ C lại
AO trong không khí sạch dưới 0,2 V, số đo nhảyRs rất lớn so với R tải; ADC kém chính xác ở mức thấpChấp nhận độ phân giải thấp ở vùng sạch, hoặc thay R tải lớn hơn (10 kΩ) rồi hiệu chuẩn lại
Đèn cảnh báo sáng khi xịt nước hoa, lau cồnCảm biến nhạy với mọi khí khửBình thường với cảm biến loại này; đặt xa nguồn hơi cồn
ESP32 khởi động lại khi cắm moduleDây nung kéo dòng lớn lúc đầu, sụt áp USBNguồn 5 V riêng đủ dòng; thêm tụ 470 µF ở 5 V

9. Xem thêm


Tin tức liên quan

ESP32 điều khiển máy CNC, máy laser với FluidNC: Wi-Fi, file cấu hình và giao diện web
ESP32 điều khiển máy CNC, máy laser với FluidNC: Wi-Fi, file cấu hình và giao diện web

Dùng ESP32 DevKit V1 chạy FluidNC điều khiển máy khắc laser 2 trục: sơ đồ nối hai A4988, cài firmware từ trình duyệt, file config.yaml mẫu giải thích từng dòng, lệnh hay dùng, Wi-Fi.

Cánh tay robot 4 servo với ESP32: điều khiển bằng biến trở, ghi nhớ và phát lại động tác
Cánh tay robot 4 servo với ESP32: điều khiển bằng biến trở, ghi nhớ và phát lại động tác

Làm cánh tay robot 4 servo với ESP32: điều khiển từng khớp bằng biến trở, nhấn nút ghi tư thế, phát lại chuỗi động tác mượt, lưu vào flash khi mất điện và gợi ý STEM.

Phát hiện chuyển động bằng cảm biến PIR HC-SR501 với ESP32: tự bật đèn qua relay
Phát hiện chuyển động bằng cảm biến PIR HC-SR501 với ESP32: tự bật đèn qua relay

Hướng dẫn dùng cảm biến chuyển động PIR HC-SR501 với ESP32: nguyên lý, cách chỉnh module, đọc tín hiệu bằng ngắt và ghép relay làm đèn tự bật khi có người, tự tắt sau 60 giây.

Ghi dữ liệu ESP32 lên Google Sheets bằng Apps Script: điện áp pin, Wi-Fi, không cần máy chủ riêng
Ghi dữ liệu ESP32 lên Google Sheets bằng Apps Script: điện áp pin, Wi-Fi, không cần máy chủ riêng

Tạo bảng tính, triển khai Apps Script thành web app, đo pin 18650 qua cầu phân áp và chương trình ESP32 gửi JSON lên Google Sheets mỗi phút, kèm bảng xử lý lỗi.

Web điều khiển ESP32 cập nhật tức thời trên nhiều điện thoại: hỏi định kỳ bằng fetch hay WebSocket
Web điều khiển ESP32 cập nhật tức thời trên nhiều điện thoại: hỏi định kỳ bằng fetch hay WebSocket

Làm trang web trên ESP32 tự cập nhật trạng thái relay trên mọi điện thoại đang mở bằng fetch() và JSON, chỉ dùng WebServer có sẵn, so sánh với WebSocket.

Micro I2S INMP441 với ESP32: đo mức âm thanh dBFS, dB SPL và vỗ tay hai lần bật tắt đèn
Micro I2S INMP441 với ESP32: đo mức âm thanh dBFS, dB SPL và vỗ tay hai lần bật tắt đèn

Khung I2S của micro INMP441, nối với ESP32, đổi số đo 24 bit ra dBFS và ước tính dB SPL, nhận tiếng vỗ tay bằng mức nền và độ vọt, bật tắt đèn khi vỗ tay hai lần.

Hướng dẫn bắt đầu với Arduino Nano CH340: sơ đồ chân, driver và nạp code
Hướng dẫn bắt đầu với Arduino Nano CH340: sơ đồ chân, driver và nạp code

Hướng dẫn bắt đầu với Arduino Nano CH340: sơ đồ chân, cài driver, chọn Board và Processor, nạp Blink, in ra Serial Monitor, quét I2C trên A4/A5, cấp nguồn và xử lý lỗi thường gặp.

Hướng dẫn dùng màn hình OLED SSD1306 với ESP8266
Hướng dẫn dùng màn hình OLED SSD1306 với ESP8266

Hướng dẫn nối OLED SSD1306 với ESP8266, quét địa chỉ I2C, cài thư viện và hiển thị thời gian bằng mã Arduino mẫu.


Bình luận
Đã thêm vào giỏ hàng