#pragma once
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <time.h>
#include <esp_system.h>
#define BUTTON_PIN 15
#define LED1_PIN 19
#define LED2_PIN 18
#define DHTPIN 26
#define DHTTYPE DHT22
#define BUZZER_PIN 4
#define GAS_PIN 34
#define PIR_PIN 27
const float TEMP_THRESHOLD_C = 20.0;
const float TEMP_THRESHOLD_F = 68.0;
const float HUMIDITY_THRESHOLD = 70.0;
const int GAS_THRESHOLD = 4000;
const unsigned long MOTION_DELAY = 5000;
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* mqtt_server = "broker.hivemq.com";
const char* mqtt_topic_pub = "IoT/Warehouse room/sensor";
const char* mqtt_topic_time = "IoT/Warehouse room/time";
const char* mqtt_topic_sub = "IoT/Warehouse room/command";
const char* mqtt_topic_alarm = "IoT/Warehouse room/alarm";
const char* mqtt_topic_lwt = "IoT/Warehouse room/status";
const char* mqtt_topic_motion = "IoT/Warehouse room/motion";
WiFiClient espClient;
PubSubClient client(espClient);
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 20, 4);
bool led1State = false, led2State = false;
unsigned long lastMsg = 0, lastDisplayChange = 0,
alarmStartTime = 0, lastBuzzerToggle = 0;
const long publishInterval = 3000, displayInterval = 3000;
int displayState = 0;
char msg[100];
bool welcomeDisplayed = false, isAlarm = false,
motionDetected = false, motionStateChanged = false;
unsigned long lastMotionTime = 0;
int buttonState, lastButtonState = HIGH;
unsigned long lastDebounceTime = 0, debounceDelay = 50;
bool restartTriggered = false;
uint8_t dot[] = {0x0E, 0x0A, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00};
void setup_wifi() {
Serial.println("Connecting to WiFi...");
WiFi.begin(ssid, password);
int wifiRetry = 0;
while (WiFi.status() != WL_CONNECTED && wifiRetry < 10) {
Serial.print(".");
delay(500);
wifiRetry++;
}
if (WiFi.status() == WL_CONNECTED) Serial.println("\nWiFi connected!");
else {
Serial.println("\nWiFi failed! Restarting...");
esp_restart();
}
}
int readGasLevel() { return analogRead(GAS_PIN); }
void handlePIR() {
bool currentMotion = digitalRead(PIR_PIN) == HIGH;
if (currentMotion) {
lastMotionTime = millis();
if (!motionDetected) {
motionDetected = true;
motionStateChanged = true;
Serial.println("Motion detected!");
client.publish(mqtt_topic_motion, "detected");
}
} else if (motionDetected && millis() - lastMotionTime > MOTION_DELAY) {
motionDetected = false;
motionStateChanged = true;
Serial.println("Motion stopped");
client.publish(mqtt_topic_motion, "stopped");
}
}
void displayWelcome() {
if (!welcomeDisplayed) {
lcd.clear();
lcd.print("Hi,Welcome!");
lcd.setCursor(0, 1);
lcd.print("Warehouse1,");
lcd.setCursor(0, 2);
lcd.print("By Group3!");
welcomeDisplayed = true;
delay(2000);
}
}
void displayDateTime() {
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
char dtbuf[21];
strftime(dtbuf, sizeof(dtbuf), "%Y-%m-%d %H:%M:%S", &timeinfo);
lcd.clear();
lcd.print("DateTime:");
lcd.setCursor(0, 1);
lcd.print(dtbuf);
if (millis() - lastMsg >= publishInterval) {
lastMsg = millis();
client.publish(mqtt_topic_time, dtbuf);
}
} else {
lcd.clear();
lcd.print("Time sync lost!");
}
}
void displaySensorData() {
unsigned long start = millis();
float h = dht.readHumidity();
float c = dht.readTemperature();
float f = dht.readTemperature(true);
int gas = readGasLevel();
if (millis() - start > 1000) {
lcd.clear();
lcd.print("Sensor Timeout!");
return;
}
if (isnan(h) || isnan(c) || isnan(f)) {
Serial.println(F("DHT read failed!"));
lcd.clear();
lcd.print("Sensor Error!");
return;
}
Serial.printf("Hum: %.1f%% Temp: %.1f°C / %.1f°F Gas: %d Motion: %s\n",
h, c, f, gas, motionDetected ? "Yes" : "No");
bool tempAlarm = c > TEMP_THRESHOLD_C;
bool humAlarm = h > HUMIDITY_THRESHOLD;
bool gasAlarm = gas > GAS_THRESHOLD;
if (tempAlarm || humAlarm || gasAlarm) {
isAlarm = true;
alarmStartTime = millis();
lcd.clear();
lcd.print("ALARM!");
lcd.setCursor(0, 1);
if (tempAlarm) lcd.print("Temp Over!");
else if (humAlarm) lcd.print("Humidity Over!");
else lcd.print("Gas Over!");
lcd.setCursor(0, 2);
lcd.print(tempAlarm ? String(c) + "C > " + String(TEMP_THRESHOLD_C) + "C"
: humAlarm ? String(h) + "% > " + String(HUMIDITY_THRESHOLD) + "%"
: String(gas) + " > " + String(GAS_THRESHOLD));
lcd.setCursor(0, 3);
lcd.print("Motion: ");
lcd.print(motionDetected ? "Detected" : "None");
char alarmMsg[100];
snprintf(alarmMsg, sizeof(alarmMsg), "Alarm: %s %.1f%s",
tempAlarm ? "Temp" : humAlarm ? "Humidity" : "Gas",
tempAlarm ? c : humAlarm ? h : (float)gas,
tempAlarm ? "C" : humAlarm ? "%" : "");
client.publish(mqtt_topic_alarm, alarmMsg);
} else {
isAlarm = false;
digitalWrite(BUZZER_PIN, LOW);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Humidity: "); lcd.print(h, 1); lcd.print("%");
lcd.setCursor(0, 1);
lcd.print("Temp: "); lcd.print(c, 1); lcd.write(0); lcd.print("C / ");
lcd.print(f, 1); lcd.print("F");
lcd.setCursor(0, 2);
lcd.print("Gas Level: "); lcd.print(gas);
lcd.setCursor(0, 3);
lcd.print("Motion: "); lcd.print(motionDetected ? "Detected" : "None");
if (millis() - lastMsg >= publishInterval) {
lastMsg = millis();
snprintf(msg, sizeof(msg),
"{\"temp_c\": %.1f, \"temp_f\": %.1f, \"hum\": %.1f, \"gas\": %d, \"motion\": %s}",
c, f, h, gas, motionDetected ? "true" : "false");
client.publish(mqtt_topic_pub, msg);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
payload[length] = 0;
String command = String((char*)payload);
command.toUpperCase();
Serial.print("MQTT: "); Serial.println(command);
if (command == "RESTART") {
Serial.println("Restart command");
blinkAndRestart();
}
}
void controlBuzzer(bool enable) {
static int buzzerPattern = 0;
unsigned long now = millis();
if (enable && now - lastBuzzerToggle >= 500) {
lastBuzzerToggle = now;
buzzerPattern = (buzzerPattern + 1) % 4;
switch (buzzerPattern) {
case 0: tone(BUZZER_PIN, 1000); break;
case 1: tone(BUZZER_PIN, 1500); break;
case 2: tone(BUZZER_PIN, 2000); break;
case 3: noTone(BUZZER_PIN); break;
}
} else if (!enable) noTone(BUZZER_PIN);
}
void blinkAndRestart() {
lcd.clear();
lcd.print("Restarting...");
for (int i = 0; i < 3; i++) {
digitalWrite(LED2_PIN, LOW);
delay(300);
digitalWrite(LED2_PIN, HIGH);
delay(300);
}
client.publish(mqtt_topic_lwt, "restarting", true);
delay(250);
esp_restart();
}
void handleButton() {
int reading = digitalRead(BUTTON_PIN);
if (reading != lastButtonState) lastDebounceTime = millis();
if (millis() - lastDebounceTime > debounceDelay && reading != buttonState) {
buttonState = reading;
if (buttonState == LOW && !restartTriggered) {
restartTriggered = true;
Serial.println("Button: Restart");
blinkAndRestart();
}
}
lastButtonState = reading;
}
void reconnect() {
if (client.connected()) return;
Serial.println("Connecting to MQTT...");
if (client.connect("ESP32ClientDevice", mqtt_topic_lwt, 0, true, "offline")) {
Serial.println("MQTT connected!");
client.publish(mqtt_topic_lwt, "online", true);
client.subscribe(mqtt_topic_sub);
delay(500);
} else {
Serial.print("MQTT failed (");
Serial.print(client.state());
Serial.println("), retry in 5s...");
delay(5000);
}
}
void setup() {
Serial.begin(115200);
Serial.println("Starting...");
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(GAS_PIN, INPUT);
pinMode(PIR_PIN, INPUT);
digitalWrite(LED1_PIN, LOW);
digitalWrite(LED2_PIN, HIGH);
digitalWrite(BUZZER_PIN, LOW);
lcd.init();
lcd.backlight();
lcd.createChar(0, dot);
lcd.clear();
lcd.print("Initializing...");
delay(1000);
dht.begin();
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
client.setKeepAlive(120);
client.setSocketTimeout(5000);
configTime(8 * 3600, 0, "pool.ntp.org");
struct tm timeinfo;
int timeRetry = 0;
while (!getLocalTime(&timeinfo) && timeRetry < 5) {
Serial.println("Waiting for time...");
delay(1000);
timeRetry++;
}
if (timeRetry < 5) {
Serial.println("Time synced");
displayWelcome();
lastDisplayChange = millis();
lastMsg = 0;
} else Serial.println("Time sync failed!");
Serial.println("Entering loop");
}
void loop() {
client.loop();
if (!client.connected()) reconnect();
handleButton();
handlePIR();
unsigned long now = millis();
if (isAlarm) {
controlBuzzer(true);
if (now - lastBuzzerToggle >= 250) {
lastBuzzerToggle = now;
digitalWrite(LED1_PIN, !digitalRead(LED1_PIN));
}
if (now - alarmStartTime > 30000) {
isAlarm = false;
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(LED1_PIN, led1State);
}
}
if (motionStateChanged && !isAlarm && !restartTriggered) {
motionStateChanged = false;
if (displayState == 1) displayDateTime();
else if (displayState == 2) displaySensorData();
}
if (now - lastDisplayChange >= displayInterval && !isAlarm && !restartTriggered) {
lastDisplayChange = now;
displayState = (displayState + 1) % 3;
switch (displayState) {
case 0: displayWelcome(); break;
case 1: displayDateTime(); break;
case 2: displaySensorData(); break;
}
}
}