#include <WiFi.h> //Library to handle functions for ESP32 to connect with wifi
#include <WiFiClientSecure.h> //Library to handle functions for ESP32 to connect with wifi
#include <PubSubClient.h> //Library to handle communication using MQTT to send data to HiveMQ
#include <Wire.h> //Library to control LCD using I2C
#include <math.h> //Library to handle "is not a number " function in DHT22
#include <LiquidCrystal_I2C.h> //Library to control LCD using I2C
#include <DHT.h> //Library to get temperature and humidity data
#include <ESP32Servo.h> //Library to control Esp32 servo motors
// WIFI configuaration - for MQTT connectivity
//for WOKWI simulations we use the WOKWI GUEST wifi without any password
const char* ssid = "Wokwi-GUEST"; //Wifi name
const char* password = ""; //wifi password
//MQTT configuration - In this Smart room safety and comfort project I am using the HiveMQ as my MQTT broker
const char* mqtt_server = "9474a89328bf49e78de2c2bc2c93676d.s1.eu.hivemq.cloud"; //HIVEMQ cluster URL
const int mqtt_port = 8883;
const char* mqtt_user = "Smartroom123"; //HIVEMQ Mqtt user name
const char* mqtt_password = "Rk123456"; //HIVEMQ Mqtt password
const char* mqtt_topic = "Smartroom123"; //HiveMQ webclient topic
const char* mqtt_client_id = "ESP32_Final_Working_Code"; //Mqtt unique id
//Initializing the object and the clients
WiFiClientSecure espClient; //Creates a secure wifi client to connect with internet
PubSubClient client(espClient); //Main object to understand the MQTT protocol
LiquidCrystal_I2C lcd(0x27, 16, 2); //Object to control the LCD screen using I2C protocol
//Defining the PINs (inputs and outputs)
//Inputs
#define DHT_PIN 15 //DHT22 temperature sensor connected to GPIO15
#define PIR_PIN 14 //PIR sensor connected to GPIO14
#define MQ2_AO_PIN 35 //MQ2 air quality sensor connected to GPIO35
#define PUSHBUTTON_PIN 4 //ush button connected to GPIO4
//Outputs
#define FAN_LED_PIN 12 //fan indicater RGB LED to GPIO12
#define MOTION_LED_PIN 25 //motion inidactor RGB LED to GPIO25
#define BUZZER_PIN 27 //motion detect security alarm buzzer to GPIO27
#define AQ_LED_PIN 26 //Air quality indication RGB LED to GPIO26
#define DOOR_LOCK_SERVO_PIN 18 //door lock servo motor to GPIO18
#define DHTTYPE DHT22 //defining the DHT sensor type
//-------------------------------
DHT dht(DHT_PIN, DHTTYPE); //software object creation to communicate with DHT sensor
Servo doorLockServo; //software object creation to communicate with Servo motor
// Thresholds & Angles
const float TEMP_THRESHOLD_FAN_ON = 28.0; // Define a temperature level to react
const int MQ2_AIR_QUALITY_POOR_THRESHOLD = 2000; // Define a air quality level to react (Analog value in MQ2 sensor)
unsigned int alertBuzzerFrequency = 2800; // Define the frequency of the buzzer
const int DOOR_LOCK_UNLOCKED_ANGLE = 0; // Define the "unlock" angle of the servo
const int DOOR_LOCK_LOCKED_ANGLE = 90; // Define the "lock" angle of the servo
bool securitySystemArmed = false; //declare a boolean to variable to hold true or false
int debouncedButtonState = HIGH; //Create debouncing for pushbutton
int lastRawButtonState = HIGH; //Create debouncing for pushbutton
unsigned long lastDebounceTime = 0; //Create debouncing for pushbutton
unsigned long debounceDelay = 50; //Create debouncing for pushbutton
//Above 4 lines work together to create debouncing mechanism for security alarm pushbutton
unsigned long lastMqttPublishTime = 0; //Holding time stamp when the programe starts
const long mqttPublishInterval = 5000; //Define the MQTT interval 5000 milliseconds (5sec)
// Setting up the WiFi connection
void setup_wifi() { //defines the setup wifi function
delay(10); Serial.println();
Serial.print("Connecting to WiFi..."); //Indicating the connecting status
WiFi.begin(ssid, password); //Command to connect to the wifi
int retries = 0;
while (WiFi.status() != WL_CONNECTED) { // Until connect to successfully the above lines will re-run
delay(500); Serial.print(".");
retries++;
if (retries > 30) { Serial.println("\nFailed to connect to WiFi."); return; } // after trying 30 times it will print "failed to connect" - each round is 0.5s
}
Serial.println("\nWiFi connected!"); //WiFi connection successful message to serial monito
Serial.print("IP address: "); Serial.println(WiFi.localIP()); //Printinng the IP adress of the wifi router connected
}
// Reconnecting to MQTT broker
void reconnect_mqtt() { //defines the reconnect mqtt funtion
while (!client.connected()) {
Serial.print("Attempting MQTT connection..."); //Printimg a status message
if (client.connect(mqtt_client_id, mqtt_user, mqtt_password)) { //Establish a connection with MQTT broker - HiveMQ credentials
Serial.println("connected!"); //Printing success message
} else {
Serial.print("failed, rc="); Serial.print(client.state()); //Printomg the failure message ( rc = -4 = connection timed out), (rc = 2 = Invalid client ID), (rc = 4 = invalid user name and password)
Serial.println(" try again in 5 seconds");
delay(5000); //delay for reconnect
}
}
}
// Initialize components and communication
void setup() {
Serial.begin(115200); //Initialize serial communication
Wire.begin(); //Initilize I2C communication
lcd.init(); //Initialize LCD component
lcd.backlight();
lcd.clear();
lcd.print("Smart Room"); //LCD startup commands
lcd.setCursor(0, 1);
lcd.print("System"); //LCD startup commands
dht.begin(); //Initialize DHT sensor
doorLockServo.attach(DOOR_LOCK_SERVO_PIN); //Initialize Doorlock servo
doorLockServo.write(DOOR_LOCK_UNLOCKED_ANGLE);
// Pin mode setup
pinMode(PIR_PIN, INPUT); //Define PIR pin as input
pinMode(PUSHBUTTON_PIN, INPUT_PULLUP); //Define Pushbutton as input
pinMode(FAN_LED_PIN, OUTPUT); //Define Fan LED pin as output
pinMode(MOTION_LED_PIN, OUTPUT); //Define Motion indicate LED as output
pinMode(BUZZER_PIN, OUTPUT); //Define buzzer as output
pinMode(AQ_LED_PIN, OUTPUT); //Define air quality indication LED as output
// Setting up initial output status
digitalWrite(FAN_LED_PIN, LOW); //Fan LED starts on "OFF" status
digitalWrite(MOTION_LED_PIN, LOW); //Motion LED starts on "OFF" status
digitalWrite(AQ_LED_PIN, LOW); //Air quality LED starts on "OFF" status
noTone(BUZZER_PIN); //Buzzer is on "SILENT" when starts
// Connect to wifi network and MQTT
setup_wifi(); //Calling the setup wifi function
if (WiFi.status() == WL_CONNECTED) { //This runs only when ESP32 is online
espClient.setInsecure(); //Verify the security status of the wifi client
client.setServer(mqtt_server, mqtt_port); //Guiding to the MQTT server
}
delay(1500); //Delay for the initial startup message on LCD
lcd.clear(); //Clear the screen after initialization
Serial.println("System Initialized. Security: DISARMED."); //Confirmation message to serial monitor
}
// Check network connections
void loop() {
if (WiFi.status() != WL_CONNECTED) { //Checking the wifi status
Serial.println("WiFi disconnected. Reconnecting..."); //Printing the status
setup_wifi(); //Retrying to connect
return;
}
if (!client.connected()) { //After connecting to wifi, retrying to connect the mqtt broker
reconnect_mqtt();
}
client.loop(); //Handle the MQTT to keep it on wakeup (calling the PubSubClient library frequently)
// PUSH BUTTON HANDLING - SECURITY FEATURE
int rawButtonReading = digitalRead(PUSHBUTTON_PIN); //Reading the push button pin
if (rawButtonReading != lastRawButtonState) { //Check the button status
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) { //Check the button signal stability
if (rawButtonReading != debouncedButtonState) { //Ensure the button reading
debouncedButtonState = rawButtonReading;
if (debouncedButtonState == LOW) { //Check the button press
securitySystemArmed = !securitySystemArmed; //Toggle switch to check the button press "true" or "false"
if (securitySystemArmed) {
Serial.println("#BUTTON PRESSED -> SYS ARMING #"); //If security system is armed, it prints button press and armed and locked
doorLockServo.write(DOOR_LOCK_LOCKED_ANGLE);
} else {
Serial.println("#BUTTON PRESSED -> SYS DISARMING #"); //If security system is disarmed, it prints button press and disarmed and unlocked
// Following lines are used to fix the servo and TONE conflict between servo motor and tone funtion in Buzzer
noTone(BUZZER_PIN); // Immediate stop to the Buzzer tone function
doorLockServo.detach(); // Detach the servo temperorily
delay(10); // Delay to process the temporary detach
doorLockServo.attach(DOOR_LOCK_SERVO_PIN); // Re-attach the servo
delay(10); // Delay to re-attach
doorLockServo.write(DOOR_LOCK_UNLOCKED_ANGLE); // Command the servo to move back to unlock position
}
}
}
}
lastRawButtonState = rawButtonReading; // Remember the current status of the button
// Reading sensor data
float temperature = dht.readTemperature(); // Read temperature value from DHT sensor
float humidity = dht.readHumidity(); // Read humidity value from DHT sensor
int motionDetected = digitalRead(PIR_PIN); // Detect motion from PIR sensor
int mq2Value = analogRead(MQ2_AO_PIN); // Reading Analog output values from MQ2 sensor
bool fanLedIsOn = (!isnan(temperature) && temperature > TEMP_THRESHOLD_FAN_ON);
digitalWrite(FAN_LED_PIN, fanLedIsOn); //Check the condition of the temperature data and On or OFF the Fan LED
bool triggerSecurityAlarm = (motionDetected && securitySystemArmed);
digitalWrite(MOTION_LED_PIN, triggerSecurityAlarm); //check the motion and trigger buzzer and the motion LED
/// TONE / NOTONE Logic to control the buzzer
if (triggerSecurityAlarm) {
tone(BUZZER_PIN, alertBuzzerFrequency);
} else {
noTone(BUZZER_PIN);
}
bool poorAirQuality = (mq2Value >= MQ2_AIR_QUALITY_POOR_THRESHOLD);
digitalWrite(AQ_LED_PIN, poorAirQuality); //Check the air quality and trigger the air quality LED
//LCD SCREEN control
String lcdLine1, lcdLine2;
if (isnan(temperature)) { lcdLine1 = "Temp: Error"; } //Validating temperature values
else { lcdLine1 = "Temp: " + String(temperature, 1) + (char)223 + "C " + (fanLedIsOn ? "FON" : "FOFF"); }
bool doorIsLocked = (doorLockServo.read() == DOOR_LOCK_LOCKED_ANGLE); //Validating the security status
if (triggerSecurityAlarm) {
lcdLine2 = "SECURITY ALERT!!"; //print on LCD line 2 (if alarm triggered)
} else if (poorAirQuality) {
lcdLine2 = "AQ BAD!"; //Print AQ bad if the air quality threshold passes
} else if (securitySystemArmed) {
lcdLine2 = "Sys ARMED"; //Print SYS ARMED when push button pressed
} else {
lcdLine2 = "Sys DISARM"; //Print SYS DISARMED when push button pressed again
}
lcd.setCursor(0, 0); lcd.print(" "); lcd.setCursor(0, 0); lcd.print(lcdLine1); // Prepare the LCD line 01 for printing
lcd.setCursor(0, 1); lcd.print(" "); lcd.setCursor(0, 1); lcd.print(lcdLine2); // Prepare the LCD line 02 for printing
// PUBLISH TO MQTT
if (client.connected() && (millis() - lastMqttPublishTime >= mqttPublishInterval)) {
lastMqttPublishTime = millis();
bool isDoorLockedNow = securitySystemArmed; //use reliable state variable to lock status
String payload = "{";
payload += "\"temperature\":" + (isnan(temperature) ? "\"NaN\"" : String(temperature, 1)) + ",";
payload += "\"humidity\":" + (isnan(humidity) ? "\"NaN\"" : String(humidity, 1)) + ",";
payload += "\"motion\":" + String(motionDetected) + ",";
payload += "\"mq2_gas_raw\":" + String(mq2Value) + ",";
payload += "\"security_armed\":" + String(securitySystemArmed ? "true" : "false") + ",";
payload += "\"door_locked\":" + String(isDoorLockedNow ? "true" : "false") + ",";
payload += "\"fan_led_on\":" + String(fanLedIsOn ? "true" : "false") + ",";
payload += "\"alarm_active\":" + String(triggerSecurityAlarm ? "true" : "false");
payload += "}";
client.publish(mqtt_topic, payload.c_str());
Serial.println("MQTT Pub: " + payload);
}
// Configuring Serial monitor outputs
bool currentDoorLockState = securitySystemArmed; // Use reliable state variable
Serial.print("T:" + (isnan(temperature) ? "N/A" : String(temperature,1) + "C"));
Serial.print(" Fan:" + String(fanLedIsOn ? "ON" : "OFF"));
Serial.print(" | Sec:"+ String(securitySystemArmed ? "ARMED" : "DISARMED"));
Serial.print(" Lock:" + String(currentDoorLockState ? "LOCKED" : "UNLOCKED"));
Serial.print(" |Mot:" + String(motionDetected));
Serial.print(" Alarm:" + String(triggerSecurityAlarm));
Serial.print(" |MQ2:" + String(mq2Value));
Serial.print(" AQled:" + String(poorAirQuality));
Serial.println();
delay(100);
}