/**
* ============================================================================
* DỰ ÁN SMART HOME (NHÀ THÔNG MINH) MÔ PHỎNG TRÊN WOKWI - ARDUINO UNO
* ============================================================================
* TÍNH NĂNG ĐÃ NÂNG CẤP:
* 1. Công tắc vật lý: D0 điều khiển Đèn Gara, D1 điều khiển Đèn Phòng ngủ.
* 2. Cảm biến siêu âm HC-SR04: Tự động bật Đèn Gara khi người/xe đến gần (<30cm).
* 3. Cảm biến khí Gas MQ-2 & Thoát hiểm PCCC:
* - Khi phát hiện rò rỉ khí gas (>= 600 ppm) -> CỬA TỰ ĐỘNG MỞ TOANG để thoát hiểm,
* còi hú liên tục và LCD cảnh báo.
* - Khi nồng độ gas hạ về an toàn (< 520 ppm) -> CỬA TỰ ĐỘNG ĐÓNG LẠI và CÒI TỰ NGẮT!
* 4. Khóa cửa thông minh: Nhập mã PIN 1234# qua Keypad 4x4 mở chốt Servo 4 giây.
* 5. Hệ thống an ninh: Cảm biến PIR chống trộm (Phím 'D' để ARM/DISARM).
* ============================================================================
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Servo.h>
#include <EEPROM.h>
// ------------------- KHAI BÁO CHÂN (PIN MAPPING) -------------------
#define PIN_BTN_GARAGE 0 // Công tắc Đèn Gara (D0)
#define PIN_BTN_BEDROOM 1 // Công tắc Đèn Phòng ngủ (D1)
#define PIN_TRIG 2 // HC-SR04 Trigger (D2)
#define PIN_ECHO 3 // HC-SR04 Echo (D3)
#define PIN_BUZZER 4 // Còi báo động Buzzer & LED Đỏ cảnh báo (D4)
#define PIN_SERVO 5 // Động cơ Servo chốt cửa (D5)
#define PIN_LED_LIGHT 6 // Đèn Phòng ngủ (LED Vàng - D6)
#define PIN_LED_GARAGE 7 // Đèn Gara (LED Xanh dương - D7)
#define PIN_PIR 13 // Cảm biến chuyển động PIR báo trộm (D13)
#define PIN_MQ2 A0 // Cảm biến khí Gas / Khói MQ-2 (A0)
// ------------------- CẤU HÌNH KEYPAD 4x4 ---------------------------
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {8, 9, 10, 11}; // Chân R1, R2, R3, R4
byte colPins[COLS] = {12, A1, A2, A3}; // Chân C1, C2, C3, C4
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// ------------------- KHỞI TẠO NGOẠI VI -----------------------------
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo doorServo;
// ------------------- BIẾN TRẠNG THÁI HỆ THỐNG ----------------------
String currentPassword = "1234";
String inputPassword = "";
int wrongAttempts = 0;
// Tính năng 1: Đổi mật khẩu
bool isChangingPin = false;
int changePinStep = 0; // 1: Nhập PIN cũ, 2: Nhập PIN mới
// Tính năng 2: Khóa bàn phím khi nhập sai nhiều lần
unsigned long lockoutUntil = 0;
const unsigned long LOCKOUT_DURATION = 30000; // Khóa 30 giây
// Tính năng 5: Chế độ giữ cửa luôn mở (Hold Door Open)
bool isDoorHoldOpen = false;
// Tính năng 6: Màn hình LCD đa chế độ (Dashboard)
int lcdScreenPage = 0; // 0: Màn hình chính, 1: Thống kê an ninh & môi trường
unsigned int countDoorOpens = 0;
unsigned int countAlerts = 0;
int maxGasPeak = 400;
// Trạng thái cửa
bool isDoorOpen = false;
unsigned long doorOpenedTime = 0;
const unsigned long DOOR_OPEN_TIME = 6000; // Mở 6 giây khi nhập đúng pass (đếm ngược 6s, 5s, 4s, 3s, 2s, 1s)
// Trạng thái đèn
bool isBedroomLightOn = false; // Đèn phòng ngủ (LED Vàng)
bool manualGarageSwitch = false; // Trạng thái bật/tắt thủ công của công tắc Đèn Gara
bool isCarInGarage = false; // Phát hiện có vật thể/người trong phạm vi <= 50cm
bool isGarageLightOn = false; // Trạng thái thực tế xuất ra đèn Gara (LED Xanh)
const int SENSOR_DISTANCE_THRESHOLD = 50; // Ngưỡng khoảng cách siêu âm 50cm
int distNearCount = 0; // Bộ đếm xác nhận gần để triệt tiêu nhiễu
int distFarCount = 0; // Bộ đếm xác nhận xa để triệt tiêu nhiễu
// Tính năng 4: Tự động tắt đèn Gara sau thời gian chờ (Courtesy Light)
bool garageTimerActive = false;
unsigned long garageTimerStart = 0;
const unsigned long GARAGE_OFF_DELAY = 6000; // Giữ sáng thêm 6 giây khi rời đi rồi mới tắt
// Trạng thái công tắc nút bấm (Chống dội phím)
bool lastBtnGarage = HIGH;
bool lastBtnBedroom = HIGH;
unsigned long lastDebounceGarage = 0;
unsigned long lastDebounceBedroom = 0;
// Trạng thái an ninh & Cảnh báo
bool isArmed = false; // false: DISARMED, true: ARMED
bool isBurglarAlert = false; // Cảnh báo có trộm (PIR)
bool isGasAlert = false; // Cảnh báo rò rỉ khí gas (MQ-2)
// Giá trị cảm biến
int distanceCm = 0;
int gasValue = 400; // Nồng độ gas hiển thị (PPM), mặc định 400 PPM
int gasBaseline = 500; // Mức nền ADC không khí sạch hiệu chuẩn tự động
const int GAS_ALARM_PPM = 1000; // Ngưỡng báo động rò rỉ gas: >= 1000 PPM (bình thường 400 PPM an toàn)
int gasHighCount = 0; // Xác nhận 2 lần liên tiếp để chống nhiễu
// Quản lý thời gian
unsigned long lastSensorRead = 0;
unsigned long lastLcdUpdate = 0;
unsigned long lastAlarmBeep = 0;
bool alarmBeepState = false;
unsigned long keyInputTimeout = 0;
// ------------------- HÀM ĐO KHOẢNG CÁCH SIÊU ÂM (HC-SR04) ----------
int readUltrasonicDistance() {
digitalWrite(PIN_TRIG, LOW);
delayMicroseconds(2);
digitalWrite(PIN_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(PIN_TRIG, LOW);
long duration = pulseIn(PIN_ECHO, HIGH, 25000);
if (duration == 0) return 400;
return duration * 0.034 / 2;
}
// ------------------- HÀM PHÁT ÂM THANH -----------------------------
void beepShort(int freq = 1200, int duration = 50) {
tone(PIN_BUZZER, freq, duration);
}
void beepSuccess() {
tone(PIN_BUZZER, 1200, 100);
delay(120);
tone(PIN_BUZZER, 1600, 180);
}
void beepError() {
tone(PIN_BUZZER, 400, 350);
}
// ------------------- ĐIỀU KHIỂN CỬA (SERVO) ------------------------
void openDoor() {
doorServo.write(90); // Mở chốt cửa 90 độ
if (!isDoorOpen) {
countDoorOpens++;
}
isDoorOpen = true;
doorOpenedTime = millis();
}
void closeDoor() {
doorServo.write(0); // Đóng chốt cửa về 0 độ
isDoorOpen = false;
isDoorHoldOpen = false;
}
// ------------------- EEPROM QUẢN LÝ MẬT KHẨU -----------------------
void loadPasswordFromEEPROM() {
byte marker = EEPROM.read(0);
if (marker == 0xAA) {
String pass = "";
for (int i = 0; i < 4; i++) {
char c = (char)EEPROM.read(1 + i);
if (c >= '0' && c <= '9') {
pass += c;
}
}
if (pass.length() == 4) {
currentPassword = pass;
}
}
}
void savePasswordToEEPROM(String newPass) {
EEPROM.write(0, 0xAA);
for (int i = 0; i < 4; i++) {
EEPROM.write(1 + i, newPass[i]);
}
currentPassword = newPass;
}
// ------------------- HÀM ĐỌC NỒNG ĐỘ KHÍ GAS CHUẨN PPM ------------
int readGasPPM() {
long sum = 0;
for (int i = 0; i < 4; i++) {
sum += analogRead(PIN_MQ2);
delayMicroseconds(50);
}
int raw = sum / 4;
// Nếu ở mức nền hoặc thấp hơn mức chuẩn -> Trả về 400 PPM khớp thanh trượt
if (raw <= gasBaseline) {
return 400;
}
// Quy đổi từ [gasBaseline ... 1015] sang [400 ... 10000] PPM khớp thanh trượt Wokwi
int maxAdc = 1015;
if (gasBaseline >= maxAdc) gasBaseline = maxAdc - 50;
long ppm = 400L + ((long)(raw - gasBaseline) * 9600L) / (maxAdc - gasBaseline);
if (ppm > 10000) ppm = 10000;
return (int)ppm;
}
// ------------------- SETUP -----------------------------------------
void setup() {
// Đọc mật khẩu đã lưu từ EEPROM (nếu có)
loadPasswordFromEEPROM();
// Cấu hình 2 công tắc nút bấm trên D0 và D1
pinMode(PIN_BTN_GARAGE, INPUT_PULLUP);
pinMode(PIN_BTN_BEDROOM, INPUT_PULLUP);
// Cấu hình ngoại vi
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
pinMode(PIN_PIR, INPUT);
pinMode(PIN_BUZZER, OUTPUT);
pinMode(PIN_LED_LIGHT, OUTPUT);
pinMode(PIN_LED_GARAGE, OUTPUT);
digitalWrite(PIN_LED_LIGHT, LOW);
digitalWrite(PIN_LED_GARAGE, LOW);
// Gắn Servo và đóng cửa ban đầu
doorServo.attach(PIN_SERVO);
closeDoor();
// Khởi động màn hình LCD
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" SMART HOME DIY ");
lcd.setCursor(0, 1);
lcd.print(" CALIBRATING... ");
// Đọc mức nền không khí sạch ban đầu (MQ-2 clean air baseline)
long sum = 0;
for (int i = 0; i < 20; i++) {
sum += analogRead(PIN_MQ2);
delay(15);
}
gasBaseline = sum / 20;
gasValue = 400; // Mức ban đầu luôn chuẩn 400 PPM khớp thanh trượt
beepSuccess();
delay(500);
lcd.clear();
}
// ------------------- VÒNG LẶP CHÍNH (LOOP) ------------------------
void loop() {
unsigned long currentMillis = millis();
// 1. Đọc công tắc vật lý Đèn Gara (D0) - Bật/tắt thủ công khi ngoài khoảng cách
int readGarageBtn = digitalRead(PIN_BTN_GARAGE);
if (readGarageBtn == LOW && lastBtnGarage == HIGH && (currentMillis - lastDebounceGarage > 200)) {
lastDebounceGarage = currentMillis;
manualGarageSwitch = !manualGarageSwitch;
garageTimerActive = false; // Tắt timer tự động nếu người dùng bấm nút thủ công
isGarageLightOn = isCarInGarage || manualGarageSwitch;
digitalWrite(PIN_LED_GARAGE, isGarageLightOn ? HIGH : LOW);
beepShort(1500, 35);
}
lastBtnGarage = readGarageBtn;
// 2. Đọc công tắc vật lý Đèn Phòng ngủ (D1)
int readBedBtn = digitalRead(PIN_BTN_BEDROOM);
if (readBedBtn == LOW && lastBtnBedroom == HIGH && (currentMillis - lastDebounceBedroom > 200)) {
lastDebounceBedroom = currentMillis;
isBedroomLightOn = !isBedroomLightOn;
beepShort(1500, 35);
}
lastBtnBedroom = readBedBtn;
// 3. Đọc dữ liệu từ cảm biến chu kỳ mỗi 150ms
if (currentMillis - lastSensorRead >= 150) {
lastSensorRead = currentMillis;
// Đọc khoảng cách từ HC-SR04
int d = readUltrasonicDistance();
if (d > 0 && d <= SENSOR_DISTANCE_THRESHOLD) {
distNearCount++;
distFarCount = 0;
if (distNearCount >= 2) {
isCarInGarage = true; // Trong khoảng cách <= 50cm
garageTimerActive = false; // Đang ở trong gara -> hủy timer
}
} else {
distFarCount++;
distNearCount = 0;
if (distFarCount >= 2) {
if (isCarInGarage) {
// Vừa từ trong khoảng cách bước ra ngoài -> Kích hoạt giữ sáng thêm 6s!
isCarInGarage = false;
garageTimerActive = true;
garageTimerStart = currentMillis;
}
}
}
distanceCm = d;
// Kiểm tra thời gian chờ tự tắt đèn Gara (Courtesy Delay)
if (garageTimerActive) {
if (currentMillis - garageTimerStart >= GARAGE_OFF_DELAY) {
garageTimerActive = false; // Hết 6 giây -> tắt đèn
}
}
// QUY TẮC ĐÈN GARA:
// - Trong khoảng cách cảm biến (<= 50cm): LUÔN SÁNG!
// - Khi vừa rời đi: GIỮ SÁNG THÊM 6 GIÂY!
// - Ngoài khoảng cách cảm biến: BẬT/TẮT TỰ DO BẰNG NÚT BẤM!
isGarageLightOn = isCarInGarage || garageTimerActive || manualGarageSwitch;
// Đọc nồng độ Gas từ MQ-2 quy đổi sang PPM chuẩn khớp thanh trượt Wokwi
gasValue = readGasPPM();
if (gasValue > maxGasPeak) {
maxGasPeak = gasValue;
}
// Xử lý báo rò rỉ khí Gas & Thoát hiểm PCCC (>= 1000 PPM)
if (gasValue >= GAS_ALARM_PPM) {
gasHighCount++;
if (gasHighCount >= 2 && !isGasAlert) {
isGasAlert = true;
countAlerts++;
// BÁO KHÍ GAS: CỬA TỰ ĐỘNG MỞ TOANG ĐỂ THOÁT HIỂM!
openDoor();
}
} else if (gasValue < (GAS_ALARM_PPM - 200)) {
gasHighCount = 0;
if (isGasAlert) {
// HẾT KHÍ GAS: CỬA TỰ ĐỘNG ĐÓNG LẠI & CÒI NGỪNG KÊU!
isGasAlert = false;
noTone(PIN_BUZZER);
closeDoor();
beepShort(800, 150);
}
}
}
// Cập nhật trạng thái đèn ra chân thực tế
digitalWrite(PIN_LED_LIGHT, isBedroomLightOn ? HIGH : LOW);
digitalWrite(PIN_LED_GARAGE, isGarageLightOn ? HIGH : LOW);
// 4. Quản lý đóng cửa tự động khi mở bằng mật khẩu (không có gas)
if (!isGasAlert) {
if (isDoorOpen && !isDoorHoldOpen && (currentMillis - doorOpenedTime >= DOOR_OPEN_TIME)) {
closeDoor();
beepShort(900, 100);
}
} else {
// Nếu đang có sự cố khí gas, liên tục giữ mở cửa
if (!isDoorOpen) {
openDoor();
}
}
// 5. Hệ thống an ninh PIR (Chống trộm khi ARMED)
if (isArmed && !isDoorOpen && !isGasAlert) {
if (digitalRead(PIN_PIR) == HIGH) {
if (!isBurglarAlert) {
isBurglarAlert = true;
countAlerts++;
}
}
}
// 6. Xử lý còi và đèn cảnh báo (Gas Alert hoặc Trộm)
if (isGasAlert || isBurglarAlert) {
if (currentMillis - lastAlarmBeep >= 120) {
lastAlarmBeep = currentMillis;
alarmBeepState = !alarmBeepState;
if (alarmBeepState) {
tone(PIN_BUZZER, isGasAlert ? 2400 : 1800);
} else {
tone(PIN_BUZZER, 600);
}
}
}
// 7. Xử lý phím bấm Keypad
char key = keypad.getKey();
if (key) {
keyInputTimeout = currentMillis;
if (lockoutUntil > 0 && currentMillis < lockoutUntil) {
// Khi đang bị khóa bàn phím:
// Cho phép phím 'B' chuyển trang màn hình để người dùng xem thông tin
if (key == 'B') {
lcdScreenPage = (lcdScreenPage == 0) ? 1 : 0;
lcd.clear();
beepShort(1500, 40);
} else {
beepError();
}
} else {
if (lockoutUntil > 0 && currentMillis >= lockoutUntil) {
lockoutUntil = 0; // Hết thời gian khóa
wrongAttempts = 0;
}
beepShort(1400, 35);
handleKeypad(key);
}
}
// 8. Cập nhật màn hình LCD
updateDisplay(currentMillis);
}
// ------------------- XỬ LÝ PHÍM BẤM -------------------------------
void handleKeypad(char key) {
// 1. Phím B:
// - Nếu không đang gõ mật khẩu -> Chuyển đổi qua lại giữa Màn hình chính & Dashboard thống kê
if (key == 'B') {
if (inputPassword.length() == 0 && !isChangingPin) {
lcdScreenPage = (lcdScreenPage == 0) ? 1 : 0;
lcd.clear();
beepShort(1500, 40);
return;
}
}
// 2. Phím A:
// - Nếu cửa đang mở (bằng pass) -> Bật/Tắt chế độ GIỮ CỬA LUÔN MỞ (HOLD DOOR)
// - Nếu cửa đang đóng -> Bật/Tắt đèn phòng ngủ
if (key == 'A') {
if (isDoorOpen && !isGasAlert) {
isDoorHoldOpen = !isDoorHoldOpen;
lcd.clear();
lcd.setCursor(0, 0);
if (isDoorHoldOpen) {
lcd.print(" DOOR HOLD ON! ");
lcd.setCursor(0, 1);
lcd.print(" Press # to Lock");
beepSuccess();
} else {
lcd.print(" DOOR HOLD OFF! ");
lcd.setCursor(0, 1);
lcd.print(" Auto Lock in 4s");
doorOpenedTime = millis();
beepShort(1000, 100);
}
delay(800);
lcd.clear();
return;
} else {
isBedroomLightOn = !isBedroomLightOn;
return;
}
}
// 3. Phím C: Bật / Tắt đèn Gara
if (key == 'C') {
manualGarageSwitch = !manualGarageSwitch;
garageTimerActive = false;
isGarageLightOn = isCarInGarage || manualGarageSwitch;
return;
}
// 4. Phím D: Bật / Tắt chế độ an ninh (ARM / DISARM) hoặc ngắt báo trộm
if (key == 'D') {
if (isBurglarAlert) {
isBurglarAlert = false;
noTone(PIN_BUZZER);
isArmed = false;
beepShort(1000, 150);
} else {
isArmed = !isArmed;
if (isArmed) {
beepSuccess();
} else {
beepShort(800, 200);
}
}
return;
}
// 5. Phím '*':
// - Nếu không nhập gì -> Kích hoạt / Hủy chế độ ĐỔI MẬT KHẨU (CHANGE PIN)
// - Nếu đang gõ -> Xóa chuỗi mật khẩu
if (key == '*') {
if (inputPassword.length() == 0) {
if (!isChangingPin) {
isChangingPin = true;
changePinStep = 1; // Bước 1: Yêu cầu nhập PIN hiện tại
lcd.clear();
beepShort(1600, 60);
} else {
isChangingPin = false;
changePinStep = 0;
lcd.clear();
beepShort(800, 80);
}
} else {
inputPassword = "";
}
return;
}
// 6. Nhập số (0-9)
if (key >= '0' && key <= '9') {
if (inputPassword.length() < 4) {
inputPassword += key;
}
return;
}
// 7. Phím '#': Xác nhận (Mở cửa, khóa cửa giữ mở, hoặc đổi mã PIN)
if (key == '#') {
// Nếu cửa đang giữ mở (Door Hold), nhấn # sẽ đóng và khóa cửa ngay lập tức
if (isDoorOpen && isDoorHoldOpen && inputPassword.length() == 0 && !isChangingPin) {
closeDoor();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" DOOR LOCKED! ");
beepShort(900, 120);
delay(800);
lcd.clear();
return;
}
// Xử lý chế độ Đổi mật khẩu
if (isChangingPin) {
if (changePinStep == 1) {
// Kiểm tra mã PIN cũ
if (inputPassword == currentPassword) {
changePinStep = 2; // Đúng PIN cũ -> chuyển sang nhập PIN mới
inputPassword = "";
beepSuccess();
lcd.clear();
} else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" WRONG OLD PIN! ");
beepError();
delay(1000);
inputPassword = "";
isChangingPin = false;
changePinStep = 0;
lcd.clear();
}
} else if (changePinStep == 2) {
// Lưu mã PIN mới 4 số
if (inputPassword.length() == 4) {
savePasswordToEEPROM(inputPassword);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" PIN CHANGED! ");
lcd.setCursor(0, 1);
lcd.print(" New PIN: ");
lcd.print(currentPassword);
beepSuccess();
delay(1200);
inputPassword = "";
isChangingPin = false;
changePinStep = 0;
lcd.clear();
} else {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" MUST BE 4 DIGIT");
beepError();
delay(1000);
inputPassword = "";
}
}
return;
}
// Xử lý mở cửa thông thường
if (inputPassword == currentPassword) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" PASSWORD OK! ");
lcd.setCursor(0, 1);
lcd.print(" WELCOME HOME! ");
beepSuccess();
openDoor();
wrongAttempts = 0;
if (isBurglarAlert) {
isBurglarAlert = false;
noTone(PIN_BUZZER);
}
delay(700);
doorOpenedTime = millis(); // Reset mốc thời gian để đếm ngược trọn vẹn 6 giây!
lcdScreenPage = 0; // Đảm bảo ở Trang 0 để người dùng thấy ngay đếm ngược
lastLcdUpdate = 0; // Cập nhật LCD ngay tức thì
lcd.clear();
} else {
wrongAttempts++;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" WRONG PASSWORD ");
lcd.setCursor(0, 1);
lcd.print(" Tries: ");
lcd.print(wrongAttempts);
lcd.print("/3");
beepError();
delay(1000);
if (wrongAttempts >= 3) {
isBurglarAlert = true;
countAlerts++;
lockoutUntil = millis() + LOCKOUT_DURATION; // Khóa bàn phím 30 giây
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" SECURITY LOCK! ");
lcd.setCursor(0, 1);
lcd.print(" Wait 30 seconds");
delay(1200);
lcd.clear();
}
}
inputPassword = "";
}
}
// ------------------- CẬP NHẬT MÀN HÌNH LCD ------------------------
void updateDisplay(unsigned long currentMillis) {
// Ưu tiên 1: Cảnh báo khí gas & Cửa mở thoát hiểm PCCC (Cao nhất)
if (isGasAlert) {
if (currentMillis - lastLcdUpdate >= 300) {
lastLcdUpdate = currentMillis;
lcd.setCursor(0, 0);
lcd.print("!GAS LEAK ALERT!");
lcd.setCursor(0, 1);
lcd.print("G:");
lcd.print(gasValue);
lcd.print("ppm ESCAPE");
}
return;
}
// Ưu tiên 2: Thông báo Khóa bàn phím đếm ngược (Hiển thị ngay cả khi còi hú do sai 3 lần)
if (lockoutUntil > 0 && currentMillis < lockoutUntil) {
if (currentMillis - lastLcdUpdate >= 300) {
lastLcdUpdate = currentMillis;
int secLeft = (lockoutUntil - currentMillis + 999) / 1000;
lcd.setCursor(0, 0);
lcd.print(" KEYPAD LOCKED! ");
lcd.setCursor(0, 1);
lcd.print(" Wait: ");
lcd.print(secLeft);
lcd.print("s (B:Pg)");
}
return;
}
// Ưu tiên 3: Cảnh báo phát hiện trộm (PIR khi ARMED)
if (isBurglarAlert) {
if (currentMillis - lastLcdUpdate >= 300) {
lastLcdUpdate = currentMillis;
lcd.setCursor(0, 0);
lcd.print("!INTRUDER ALERT!");
lcd.setCursor(0, 1);
lcd.print("PRESS D TO STOP ");
}
return;
}
// Ưu tiên 4: Giao diện Đổi mã PIN
if (isChangingPin) {
if (currentMillis - lastLcdUpdate >= 200) {
lastLcdUpdate = currentMillis;
lcd.setCursor(0, 0);
if (changePinStep == 1) {
lcd.print("Old PIN: [");
} else {
lcd.print("New PIN: [");
}
for (int i = 0; i < inputPassword.length(); i++) {
lcd.print("*");
}
for (int i = inputPassword.length(); i < 4; i++) {
lcd.print(" ");
}
lcd.print("] ");
lcd.setCursor(0, 1);
lcd.print("#=OK *=Cancel ");
}
return;
}
// Ưu tiên 5: Giao diện nhập mật khẩu mở cửa
if (inputPassword.length() > 0 && (currentMillis - keyInputTimeout < 3500)) {
lcd.setCursor(0, 0);
lcd.print("Enter PIN: ");
lcd.setCursor(0, 1);
lcd.print("[");
for (int i = 0; i < inputPassword.length(); i++) {
lcd.print("*");
}
for (int i = inputPassword.length(); i < 4; i++) {
lcd.print(" ");
}
lcd.print("] #=OK *=Chg");
return;
} else if (inputPassword.length() > 0 && (currentMillis - keyInputTimeout >= 3500)) {
inputPassword = "";
}
// Ưu tiên 6: Màn hình chính (Bấm 'B' để chuyển đổi qua lại giữa Trang 0 và Trang 1)
if (currentMillis - lastLcdUpdate >= 300) {
lastLcdUpdate = currentMillis;
if (lcdScreenPage == 0) {
// -------------------------------------------------------------
// TRANG 0: GIÁM SÁT KHOẢNG CÁCH, AN NINH & ĐẾM NGƯỢC CỬA
// Dòng 1: Dist: <x>cm [ARM] / [DIS]
// Dòng 2: DOOR: OPEN (5s) / DOOR: LOCKED / DOOR: HOLD OPEN
// -------------------------------------------------------------
lcd.setCursor(0, 0);
lcd.print("Dist: ");
if (distanceCm < 100) {
lcd.print(distanceCm);
lcd.print("cm ");
if (distanceCm < 10) lcd.print(" ");
} else {
lcd.print(">1m ");
}
lcd.setCursor(11, 0);
if (isArmed) {
lcd.print("[ARM]");
} else {
lcd.print("[DIS]");
}
lcd.setCursor(0, 1);
if (isDoorOpen) {
if (isGasAlert) {
lcd.print("DOOR: ESCAPE! ");
} else if (isDoorHoldOpen) {
lcd.print("DOOR: HOLD OPEN ");
} else {
long msLeft = (long)DOOR_OPEN_TIME - (long)(currentMillis - doorOpenedTime);
int secLeft = (msLeft > 0) ? (int)((msLeft + 999) / 1000) : 0;
lcd.print("DOOR: OPEN (");
lcd.print(secLeft);
lcd.print("s) ");
}
} else {
lcd.print("DOOR: LOCKED ");
}
} else {
// -------------------------------------------------------------
// TRANG 1: KHÍ GAS (AN TOÀN / NGUY HIỂM) & TRẠNG THÁI 2 ĐÈN
// Dòng 1: GAS: SAFE / DANGER (B:Trang)
// Dòng 2: BED: ON GAR: OFF
// -------------------------------------------------------------
lcd.setCursor(0, 0);
lcd.print("GAS: ");
if (gasValue >= GAS_ALARM_PPM) {
lcd.print("DANGER! ");
} else {
lcd.print("SAFE ");
}
lcd.setCursor(0, 1);
lcd.print("BED: ");
lcd.print(isBedroomLightOn ? "ON ":"OFF");
lcd.print(" GAR: ");
lcd.print(isGarageLightOn ? "ON ":"OFF");
}
}
}