// Dự án IOT: Mô phỏng cảm biến khí gas MQ2 trên ESP32
//
// Mã này mô phỏng cảm biến khí gas MQ2 và điều khiển màn hình LCD, các đèn LED,
// còi báo động và servo dựa trên mức khí gas được mô phỏng.
// Nó sử dụng một biến trở để thay thế cho cảm biến MQ2.
// Định danh cho dự án Blynk của bạn
#define BLYNK_TEMPLATE_ID "TMPL6d1d_YO1X"
#define BLYNK_TEMPLATE_NAME "ProjectGas"
#define BLYNK_AUTH_TOKEN "k3l0RL_9OiXOGJlLESeQvWkm94sB8YE2"
// Thư viện cần thiết cho WiFi và Blynk
#include <WiFi.h>
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
#include <BlynkSimpleEsp32.h>
// ----- Định nghĩa chân cắm -----
#define GAS_SENSOR_PIN 34 // Chân analog cho cảm biến (mô phỏng bằng biến trở)
#define BUZZER_PIN 2 // Chân cho còi báo động
#define RED_LED_PIN 23 // Chân cho đèn LED đỏ (Khí gas nguy hiểm)
#define GREEN_LED_PIN 19 // Chân cho đèn LED xanh (An toàn)
#define YELLOW_LED_PIN 18 // Chân cho đèn LED vàng (Cảnh báo)
#define SERVO_PIN 5 // Chân cho động cơ servo (GPIO5)
// ----- Cấu hình LCD -----
LiquidCrystal_I2C lcd(0x27, 16, 2); // Địa chỉ I2C, số cột, số hàng
// ----- Cấu hình Servo -----
Servo servo;
// ----- Tham số hệ thống -----
#define ADC_REF 3.3 // Điện áp tham chiếu ADC (Vôn)
#define ADC_RESOLUTION 4095 // Độ phân giải ADC 12-bit của ESP32
const float PPM_THRESHOLD_WARNING = 900.0; // Mức PPM để kích hoạt cảnh báo
const float PPM_THRESHOLD_DANGER = 1800.0; // Mức PPM để kích hoạt báo động nguy hiểm
// ----- Thông tin WiFi -----
char ssid[] = "Wokwi-GUEST"; // Tên WiFi của bạn
char password[] = ""; // Mật khẩu WiFi của bạn
// ----- Biến trạng thái -----
unsigned long previousMillis = 0;
const long interval = 500; // Kiểm tra cảm biến mỗi 500 mili giây
// ----- Khai báo hàm -----
float convertAnalogToPPM(int sensorValue);
void updateGasStatus(float ppm);
// ----- Hàm Setup -----
void setup() {
Serial.begin(115200);
delay(1000);
// Khởi tạo LCD
lcd.init();
lcd.backlight();
// Cấu hình các chân I/O
pinMode(BUZZER_PIN, OUTPUT);
pinMode(RED_LED_PIN, OUTPUT);
pinMode(GREEN_LED_PIN, OUTPUT);
pinMode(YELLOW_LED_PIN, OUTPUT);
// Gắn servo vào chân và đặt vị trí ban đầu
servo.attach(SERVO_PIN);
servo.write(0); // Đặt servo về 0 độ ban đầu
// Kết nối với Blynk (bao gồm cả kết nối WiFi)
Blynk.begin(BLYNK_AUTH_TOKEN, ssid, password);
// Hiển thị thông báo khởi động trên LCD
lcd.setCursor(1, 0);
lcd.print("May do khi gas");
for (int a = 0; a <= 15; a++) {
lcd.setCursor(a, 1);
lcd.print(".");
delay(100);
}
lcd.clear();
}
// ----- Vòng lặp chính -----
void loop() {
Blynk.run(); // Chạy Blynk để xử lý các sự kiện
unsigned long currentMillis = millis();
// Sử dụng khoảng thời gian không chặn để kiểm tra cảm biến định kỳ
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Đọc cảm biến và chuyển đổi sang PPM
int sensorValue = analogRead(GAS_SENSOR_PIN);
float ppm = convertAnalogToPPM(sensorValue);
// Cập nhật tất cả các đầu ra dựa trên giá trị PPM
updateGasStatus(ppm);
}
}
// ----- Các hàm tùy chỉnh -----
/**
* @brief Chuyển đổi giá trị đọc analog từ cảm biến sang giá trị PPM.
* @param sensorValue Giá trị analog thô (0-4095).
* @return Giá trị PPM đã tính toán.
* @note Đây là một chuyển đổi tuyến tính đơn giản cho mục đích mô phỏng.
* Cảm biến MQ2 thực tế cần một đường cong hiệu chỉnh phức tạp hơn.
*/
float convertAnalogToPPM(int sensorValue) {
float voltage = sensorValue * (ADC_REF / ADC_RESOLUTION);
// Giả định mối quan hệ tuyến tính để mô phỏng.
// Hệ số 1000 là một giá trị tùy ý để minh họa.
float ppm = voltage * 1000;
return ppm;
}
/**
* @brief Cập nhật LCD, đèn LED, còi và servo dựa trên mức PPM của khí gas.
* @param ppm Nồng độ khí gas hiện tại tính bằng phần triệu.
*/
void updateGasStatus(float ppm) {
// Gửi giá trị PPM đến virtual pin V0 của Blynk
Blynk.virtualWrite(V0, ppm);
// Xóa và hiển thị PPM trên LCD
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Gas: ");
lcd.print((int)ppm); // Chuyển sang int để hiển thị gọn hơn
lcd.print("ppm");
// Đặt lại tất cả các đầu ra về trạng thái ban đầu
digitalWrite(RED_LED_PIN, LOW);
digitalWrite(GREEN_LED_PIN, LOW);
digitalWrite(YELLOW_LED_PIN, LOW);
noTone(BUZZER_PIN);
// Logic điều khiển đầu ra dựa trên ngưỡng PPM
if (ppm >= PPM_THRESHOLD_DANGER) {
// NGUY HIỂM: Nồng độ khí gas cao
lcd.setCursor(0, 1);
lcd.print("Nguy hiem!!!");
digitalWrite(RED_LED_PIN, HIGH);
tone(BUZZER_PIN, 200); // Kích hoạt còi báo động
servo.write(90); // Mở van đến 90 độ
Serial.println("Nguy hiem!");
Blynk.virtualWrite(V1, "Nguy hiem!");
} else if (ppm >= PPM_THRESHOLD_WARNING) {
// CẢNH BÁO: Nồng độ khí gas trung bình
lcd.setCursor(0, 1);
lcd.print("Canh bao ! ! !");
digitalWrite(YELLOW_LED_PIN, HIGH);
servo.write(45); // Mở van một phần
Serial.println("Canh bao!");
Blynk.virtualWrite(V1, "Canh bao!");
} else {
// AN TOÀN: Nồng độ khí gas thấp
lcd.setCursor(0, 1);
lcd.print("An toan");
digitalWrite(GREEN_LED_PIN, HIGH);
servo.write(0); // Đóng van
Serial.println("An toan");
Blynk.virtualWrite(V1, "An toan");
}
}