#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <ESP32Servo.h>
#include <DHT.h>
// =========================================
// PIN DEFINITIONS
// =========================================
// --- SENSORS ---
#define DHTPIN 16 // DHT22 Data
#define DHTTYPE DHT22
#define PIR_PIN 35 // Motion Sensor (Input Only pin)
#define LDR_PIN 34 // Photoresistor (Analog Input)
#define TRIG_PIN 18 // Ultrasonic Trigger
#define ECHO_PIN 19 // Ultrasonic Echo
// --- ACTUATORS ---
#define RELAY_FAN_PIN 17 // Fan/AC Relay
#define RELAY_LIGHT_PIN 5 // Main Light Relay
#define SERVO_PIN 15 // Door Lock Servo
#define BUZZER_PIN 23 // Active Buzzer
// --- RGB LED ---
#define RED_PIN 25
#define GREEN_PIN 26
#define BLUE_PIN 27
// --- LCD ---
// SDA = 21, SCL = 22 (Standard ESP32 I2C)
// =========================================
// OBJECT SETUP
// =========================================
// LCD Address is usually 0x27 for 20x4 or 16x2
LiquidCrystal_I2C lcd(0x27, 20, 4);
DHT dht(DHTPIN, DHTTYPE);
Servo doorServo;
// Keypad Configuration
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'}
};
// Pin Map matches the diagram provided
byte rowPins[ROWS] = {13, 12, 14, 32};
byte colPins[COLS] = {33, 2, 0, 4};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// =========================================
// GLOBAL VARIABLES
// =========================================
// Security
String inputPassword = "";
String correctPassword = "1234"; // CHANGE THIS IF YOU WANT
bool isArmed = true;
// Timing (Non-blocking)
unsigned long lastClimateCheck = 0;
unsigned long lastGarageCheck = 0;
unsigned long buzzerTimer = 0;
bool buzzerState = false;
// Garage
long duration;
int distance;
// =========================================
// SETUP
// =========================================
void setup() {
Serial.begin(115200);
// 1. Initialize LCD
lcd.init();
lcd.backlight();
lcd.setCursor(0,0);
lcd.print("System Booting...");
// 2. Initialize Sensors
dht.begin();
// 3. Initialize Servo (Reset to 0 then detach immediately)
doorServo.attach(SERVO_PIN);
doorServo.write(0);
delay(500);
doorServo.detach(); // <--- CRITICAL FIX: Disconnects servo so Buzzer can't kill it
// 4. Pin Modes
pinMode(RELAY_FAN_PIN, OUTPUT);
pinMode(RELAY_LIGHT_PIN, OUTPUT);
pinMode(PIR_PIN, INPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
pinMode(LDR_PIN, INPUT);
delay(1000);
lcd.clear();
// Default State: Armed (Red LED)
updateStatusRGB(1, 0, 0);
lcd.print("System: ARMED");
delay(1000);
lcd.clear();
}
// =========================================
// MAIN LOOP
// =========================================
void loop() {
unsigned long currentMillis = millis();
// --- TASK 1: Security (High Priority - Runs every loop) ---
handleKeypad();
// --- TASK 2: Climate Control (Runs every 2 seconds) ---
if (currentMillis - lastClimateCheck > 2000) {
handleClimate();
lastClimateCheck = currentMillis;
}
// --- TASK 3: Smart Lighting (Runs constantly/fast) ---
handleLighting();
// --- TASK 4: Garage Parking (Runs every 100ms) ---
if (currentMillis - lastGarageCheck > 100) {
handleGarage();
lastGarageCheck = currentMillis;
}
// Handle Buzzer Non-Blocking Logic (for Garage Beeps)
handleBuzzer(currentMillis);
delay(10); // Small stability delay
}
// =========================================
// FUNCTION LOGIC
// =========================================
// --- ZONE 1: SECURITY ---
void handleKeypad() {
char key = keypad.getKey();
if (key) {
// Show * for security
lcd.setCursor(0, 3);
lcd.print("Pin: " + maskPassword(inputPassword) + key);
if (key == '#') { // Enter Key
lcd.clear();
if (inputPassword == correctPassword) {
unlockDoor();
} else {
triggerAlarm();
}
inputPassword = ""; // Reset
}
else if (key == '*') { // Lock/Reset Key
lockDoor();
inputPassword = "";
}
else {
inputPassword += key;
}
}
}
String maskPassword(String pass) {
String masked = "";
for(int i=0; i<pass.length(); i++) masked += "*";
return masked;
}
void unlockDoor() {
lcd.setCursor(0,0);
lcd.print("WELCOME HOME");
doorServo.attach(SERVO_PIN); // Ensure it's connected
// Slow Motion Open (0 to 90 degrees)
for (int pos = 0; pos <= 90; pos += 2) {
doorServo.write(pos);
delay(15); // Wait 15ms for the servo to reach the position
}
updateStatusRGB(0, 1, 0); // Green
isArmed = false;
// Note: We REMOVED doorServo.detach() to keep the door holding its position.
noTone(BUZZER_PIN);
}
void lockDoor() {
lcd.setCursor(0,0);
lcd.print("System ARMED");
doorServo.attach(SERVO_PIN);
// Slow Motion Close (90 to 0 degrees)
for (int pos = 90; pos >= 0; pos -= 2) {
doorServo.write(pos);
delay(15);
}
updateStatusRGB(1, 0, 0); // Red
isArmed = true;
// We can detach here to save power since 0 is the "rest" state
delay(500);
doorServo.detach();
}
void triggerAlarm() {
lcd.setCursor(0,0);
lcd.print("ACCESS DENIED!");
updateStatusRGB(1, 0, 0); // Red
// Quick Alarm Burst
for(int i=0; i<3; i++) {
tone(BUZZER_PIN, 1000);
delay(200);
noTone(BUZZER_PIN);
delay(200);
}
}
// --- ZONE 2: CLIMATE ---
void handleClimate() {
float t = dht.readTemperature();
float h = dht.readHumidity();
// Error handling
if (isnan(t) || isnan(h)) {
lcd.setCursor(0, 1);
lcd.print("DHT Error");
return;
}
lcd.setCursor(0, 1);
lcd.print("T:" + String(t,1) + "C H:" + String(h,0) + "%");
if (t > 30.0) {
digitalWrite(RELAY_FAN_PIN, HIGH); // Turn Fan ON
lcd.setCursor(15, 1);
lcd.print("FAN+"); // Indicator on LCD
} else {
digitalWrite(RELAY_FAN_PIN, LOW); // Turn Fan OFF
lcd.setCursor(15, 1);
lcd.print(" ");
}
}
// --- ZONE 3: LIGHTING ---
void handleLighting() {
int lightLevel = analogRead(LDR_PIN); // 0-4095
int motion = digitalRead(PIR_PIN);
// Debug logic: < 2000 usually means 'dark' in simulation
bool isDark = (lightLevel < 2000);
if (isDark && motion == HIGH) {
digitalWrite(RELAY_LIGHT_PIN, HIGH); // Lights ON
} else {
digitalWrite(RELAY_LIGHT_PIN, LOW); // Lights OFF
}
}
// --- ZONE 4: GARAGE ---
int beepFrequency = 0; // 0 = off, 1 = fast, 2 = solid
void handleGarage() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
duration = pulseIn(ECHO_PIN, HIGH, 25000); // 25ms timeout
distance = duration * 0.034 / 2;
// Determine Beep Logic based on distance
if (distance > 0 && distance < 10) {
beepFrequency = 2; // Continuous (STOP)
} else if (distance >= 10 && distance < 50) {
beepFrequency = 1; // Beeping (Warning)
} else {
beepFrequency = 0; // Safe
}
}
void handleBuzzer(unsigned long currentMillis) {
// Priority 1: Garage Stop (Critical)
if (beepFrequency == 2) {
tone(BUZZER_PIN, 2000); // Continuous Tone
}
// Priority 2: Garage Warning (Beeping)
else if (beepFrequency == 1) {
if (currentMillis - buzzerTimer > (distance * 10)) {
buzzerTimer = currentMillis;
if (buzzerState) {
noTone(BUZZER_PIN);
buzzerState = false;
} else {
tone(BUZZER_PIN, 1000);
buzzerState = true;
}
}
}
// Priority 3: Safe / Idle
else {
// FIX: Force buzzer off if distance is safe, even if Armed.
// The Security Alarm uses a blocking function, so this won't interrupt it.
noTone(BUZZER_PIN);
buzzerState = false;
}
}
// Helper to set RGB Color
void updateStatusRGB(bool r, bool g, bool b) {
digitalWrite(RED_PIN, r);
digitalWrite(GREEN_PIN, g);
digitalWrite(BLUE_PIN, b);
}