Giới thiệu series “Lập trình ESP32 38 Pin & NRF” – Phần 7
Đến Phần 6, bạn đã xây dựng được mạng cảm biến nhiều node → 1 gateway ESP32 dùng NRF24L01, với mỗi node gửi struct dữ liệu gồm nodeId, temperature, humidity, uptime.
Trong Phần 7, chúng ta sẽ tiến gần hơn đến ứng dụng thực tế bằng cách:
- Kết hợp thêm các cảm biến vật lý như DHT11/DHT22 (nhiệt độ/độ ẩm) và MQ-2 (cảm biến khí gas, khói).
- Mở rộng struct dữ liệu để chứa thêm giá trị khí gas.
- Gửi đầy đủ thông tin nhiệt độ – độ ẩm – nồng độ khí gas từ node ESP32 + NRF về gateway.
Bạn có thể áp dụng ngay cho các dự án:
- Giám sát môi trường trong phòng, lớp học, nhà bếp.
- Hệ thống cảnh báo rò rỉ gas đơn giản.
- Bước nền cho IoTLabs Alert Device / IoTLabs Gas Detector.
1. Mục tiêu bài học
Sau bài này, bạn sẽ:
- Hiểu sơ lược về DHT11/DHT22 và MQ-2 (nguyên lý, cách đọc dữ liệu).
- Biết cách kết nối DHT + MQ-2 với ESP32 38 pin.
- Mở rộng
SensorPacketđể kèm thêm trườnggasValue. - Gửi dữ liệu cảm biến đầy đủ từ node lên gateway qua NRF24L01.
2. Chuẩn bị phần cứng
2.1. Danh sách linh kiện cho 1 node cảm biến
- 1 x ESP32 DevKit 38 pin.
- 1 x Module NRF24L01 (8 chân).
- 1 x Cảm biến DHT11 hoặc DHT22 (nên dùng DHT22 cho độ chính xác cao hơn).
- 1 x Cảm biến MQ-2 (module gas có mạch so sánh LM393 hoặc module analog đơn).
- 1 x Breadboard + dây Dupont đực–cái.
- 1 x Tụ 10µF – 47µF cho NRF24L01 (lọc nguồn).
- Điện trở kéo lên 10kΩ cho chân DATA của DHT nếu module bạn dùng chưa tích hợp.
2.2. Gateway
- 1 ESP32 + NRF24L01 (code gần như giữ nguyên từ Phần 6, chỉ sửa phần in log để hiển thị thêm gas).
3. Tổng quan nhanh về DHT11/DHT22 và MQ-2
3.1. Cảm biến DHT11/DHT22
- Đo nhiệt độ và độ ẩm.
- Giao tiếp kiểu 1-wire, dùng duy nhất 1 chân DATA.
- DHT11:
- Rẻ, đo tương đối, độ phân giải kém hơn.
- Tầm đo hẹp hơn.
- DHT22:
- Chính xác hơn, tầm đo rộng hơn.
- Thích hợp cho ứng dụng cần số liệu tốt hơn.
3.2. Cảm biến MQ-2
- Cảm biến khí gas, LPG, propane, hydrogen, khói.
- Module thường có:
- Đầu ra analog (AO): giá trị điện áp tỷ lệ với nồng độ khí.
- Đầu ra digital (DO): mức HIGH/LOW phụ thuộc ngưỡng chỉnh bằng chiết áp.
- Trong bài này, ta ưu tiên đọc ngõ analog AO để có giá trị “mượt” hơn (sau đó có thể chuẩn hóa về % hoặc mức cảnh báo).
4. Sơ đồ kết nối ESP32 38 pin với DHT & MQ-2 & NRF24L01
4.1. Kết nối NRF24L01 (nhắc lại)
| NRF24L01 | ESP32 DevKit 38-pin | Ghi chú |
|---|---|---|
| GND | GND | GND chung |
| VCC | 3V3 | Nguồn 3.3V, không dùng 5V |
| CE | GPIO 4 | Chân điều khiển CE |
| CSN (CS) | GPIO 5 | Chip Select SPI |
| SCK | GPIO 18 | VSPI SCK |
| MOSI | GPIO 23 | VSPI MOSI |
| MISO | GPIO 19 | VSPI MISO |
| IRQ | (Không nối) | Bỏ trống ở mức cơ bản |
4.2. Kết nối DHT11/DHT22
| DHT | ESP32 | Ghi chú |
|---|---|---|
| VCC | 3V3 | Có thể dùng 5V với một số module |
| GND | GND | GND chung |
| DATA | GPIO 15 (ví dụ) | Cần trở kéo lên 10kΩ nếu chưa có |
4.3. Kết nối MQ-2
| MQ-2 | ESP32 | Ghi chú |
|---|---|---|
| VCC | 5V | Module MQ-2 thường dùng 5V |
| GND | GND | GND chung với ESP32 |
| AO | GPIO 34 (ADC1_CH6) | Chân ADC1, chỉ đọc analog, không xuất PWM |
⚠️ Lưu ý: ESP32 chỉ đọc analog trên các chân ADC (ADC1, ADC2). Ở đây ta dùng GPIO 34 (ADC1), an toàn.
5. Thiết kế lại struct dữ liệu cảm biến (thêm gas)
Từ Phần 5–6, ta có struct:
struct SensorPacket {
uint8_t nodeId;
float temperature;
float humidity;
uint32_t uptime;
};
Trong bài này, ta bổ sung thêm trường gas (giá trị ADC đã chuẩn hóa về 0.0–100.0% hoặc giữ raw):
struct SensorPacket {
uint8_t nodeId; // ID node
float temperature; // Nhiet do (°C)
float humidity; // Do am (%)
float gas; // Gia tri khi gas (0–100 hoac raw ADC)
uint32_t uptime; // Thoi gian chay (giay)
};
Kích thước vẫn < 32 byte nên hoàn toàn phù hợp với NRF24L01.
6. Code node ESP32 + NRF + DHT + MQ-2 (TX)
Node này sẽ:
- Định kỳ đọc nhiệt độ/độ ẩm từ DHT.
- Đọc giá trị analog từ MQ-2.
- Chuẩn hóa gas về thang 0–100 (tương đối, sử dụng giá trị 0–4095 của ADC ESP32).
- Gửi struct
SensorPacketqua NRF24L01.
6.1. Khai báo & setup
#include <Arduino.h>
#include <SPI.h>
#include <RF24.h>
#include <DHT.h>
// Cau hinh node
#define NODE_ID 1
// NRF24L01 pins
#define PIN_NRF_CE 4
#define PIN_NRF_CSN 5
// DHT pins
#define DHT_PIN 15
#define DHT_TYPE DHT22 // hoac DHT11
// MQ-2 pin (analog)
#define MQ2_PIN 34 // ADC1
RF24 radio(PIN_NRF_CE, PIN_NRF_CSN);
DHT dht(DHT_PIN, DHT_TYPE);
const byte address[6] = "00001";
struct SensorPacket {
uint8_t nodeId;
float temperature;
float humidity;
float gas;
uint32_t uptime;
};
SensorPacket packet;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println(F("=== Node ESP32 + DHT + MQ2 + NRF - Khoi dong ==="));
dht.begin();
// Cau hinh ADC cho MQ-2
analogReadResolution(12); // 0-4095
if (!radio.begin()) {
Serial.println(F("Loi: Khong khoi tao duoc NRF24L01"));
while (1) {
delay(1000);
}
}
radio.setChannel(108);
radio.setPALevel(RF24_PA_LOW);
radio.setDataRate(RF24_1MBPS);
radio.openWritingPipe(address);
radio.stopListening();
packet.nodeId = NODE_ID;
Serial.println(F("Node san sang gui du lieu cam bien..."));
}
6.2. Hàm loop() – đọc dữ liệu & gửi struct
void loop() {
// Doc DHT
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println(F("Loi: Khong doc duoc DHT"));
}
// Doc MQ-2 (analog)
int rawGas = analogRead(MQ2_PIN); // 0-4095
// Chuan hoa sang 0-100 (tuong doi)
float gasPercent = (rawGas / 4095.0) * 100.0;
packet.temperature = t;
packet.humidity = h;
packet.gas = gasPercent;
packet.uptime = millis() / 1000;
bool ok = radio.write(&packet, sizeof(packet));
if (ok) {
Serial.print(F("["));
Serial.print(packet.nodeId);
Serial.print(F("] T="));
Serial.print(packet.temperature, 1);
Serial.print(F(" °C, H="));
Serial.print(packet.humidity, 1);
Serial.print(F(" %, Gas="));
Serial.print(packet.gas, 1);
Serial.print(F(" %, Uptime="));
Serial.print(packet.uptime);
Serial.println(F(" s"));
} else {
Serial.println(F("Gui du lieu that bai!"));
}
delay(3000);
}
Ghi chú: Việc quy đổi MQ-2 sang % ở đây chỉ mang tính tương đối (0–100%) dựa trên thang ADC. Nếu muốn đo nồng độ chính xác (ppm) bạn cần quy trình hiệu chuẩn riêng.
7. Code gateway ESP32 – nhận thêm dữ liệu gas
Gateway gần giống Phần 6, chỉ mở rộng struct để có thêm gas và in thêm cột log.
7.1. Khai báo và setup
#include <Arduino.h>
#include <SPI.h>
#include <RF24.h>
#define PIN_NRF_CE 4
#define PIN_NRF_CSN 5
RF24 radio(PIN_NRF_CE, PIN_NRF_CSN);
const byte address[6] = "00001";
struct SensorPacket {
uint8_t nodeId;
float temperature;
float humidity;
float gas;
uint32_t uptime;
};
SensorPacket packet;
void printHeader() {
Serial.println(F("Node | T(°C) | H(%) | Gas(%) | Uptime(s)"));
Serial.println(F("----------------------------------------------"));
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println();
Serial.println(F("=== ESP32 Gateway - DHT + MQ2 + NRF ==="));
if (!radio.begin()) {
Serial.println(F("Loi: Khong khoi tao duoc NRF24L01 (Gateway)"));
while (1) {
delay(1000);
}
}
radio.setChannel(108);
radio.setPALevel(RF24_PA_LOW);
radio.setDataRate(RF24_1MBPS);
radio.openReadingPipe(1, address);
radio.startListening();
printHeader();
}
unsigned long lastHeaderTime = 0;
7.2. Hàm loop() – nhận & hiển thị
void loop() {
if (radio.available()) {
radio.read(&packet, sizeof(packet));
Serial.print(" ");
Serial.print(packet.nodeId);
Serial.print(F(" | "));
Serial.print(packet.temperature, 1);
Serial.print(F(" | "));
Serial.print(packet.humidity, 1);
Serial.print(F(" | "));
Serial.print(packet.gas, 1);
Serial.print(F(" | "));
Serial.println(packet.uptime);
// Canh bao don gian neu gas cao
if (packet.gas > 60.0) {
Serial.print(F("*** CANH BAO: Node "));
Serial.print(packet.nodeId);
Serial.print(F(" - muc gas cao ("));
Serial.print(packet.gas, 1);
Serial.println(F(" %) ***"));
}
lastHeaderTime = millis();
}
if (millis() - lastHeaderTime > 20000) {
printHeader();
lastHeaderTime = millis();
}
delay(10);
}
Giờ đây gateway đã có thể hiển thị đồng thời nhiệt độ, độ ẩm, gas của node cảm biến và đưa ra thông báo cảnh báo đơn giản.
8. Bài tập mở rộng với cảm biến thực tế
8.1. Tinh chỉnh ngưỡng cảnh báo gas
- Thử thay đổi ngưỡng
packet.gas > 60.0sang các giá trị khác. - Quan sát điều kiện thực tế:
- Bình thường trong phòng: gas ~ 0–20%.
- Khi bật bếp gas hoặc đốt nhang: gas tăng cao.
8.2. Thêm cảm biến khác
- Bạn có thể thay MQ-2 bằng:
- MQ-135 (chất lượng không khí).
- Cảm biến lửa, khói.
- Cảm biến ánh sáng (LDR hoặc BH1750).
- Chỉ cần thêm các trường tương ứng vào
SensorPacket(miễn < 32 byte) và đọc giá trị ở node.
8.3. Hiển thị dữ liệu lên màn hình OLED
- Gắn thêm OLED SSD1306 vào gateway.
- Mỗi vài giây hiển thị thông tin node quan trọng nhất (hoặc node có gas cao nhất).
9. Kết luận & hướng tới MQTT/IoTLabs Cloud
Trong Phần 7 của series “Lập trình ESP32 38 Pin & NRF”, bạn đã:
- Kết hợp cảm biến thực tế (DHT11/DHT22, MQ-2) với node ESP32.
- Mở rộng struct dữ liệu để truyền nhiệt độ, độ ẩm, gas qua NRF24L01.
- Xây dựng gateway ESP32 có khả năng nhận, hiển thị và cảnh báo dựa trên dữ liệu thực tế.
Đây chính là bước chuyển từ demo lý thuyết sang ứng dụng IoT gần gũi.
Ở Phần 8 (gợi ý nội dung tiếp theo), chúng ta sẽ:
Kết nối ESP32 Gateway với WiFi & MQTT/IoTLabs Cloud, chuyển dữ liệu cảm biến lên server để theo dõi bằng dashboard web hoặc mobile app.
Hẹn gặp bạn ở các phần tiếp theo trên iotlabs.vn!


