#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <HTTPClient.h>
// === WiFi Credentials ===
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// === MQTT Broker Settings ===
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
const char* client_id = "ESP32_MachineMonitor";
// === Google Sheets Script URL ===
const char* googleScriptURL = "https://script.google.com/macros/s/AKfycbxRQ5gG6HEDah3MEy9QeOtf1SXjB_3Fu1kokQ3UFd6cnBG40gBqMn0cvwe-sX9QoxPH/exec";
// === MQTT Topics ===
const char* temp_topic = "machine/temperature";
const char* hum_topic = "machine/humidity";
const char* smoke_topic = "machine/smoke";
const char* alert_topic = "machine/alert";
const char* temp_led_topic = "machine/led/temp";
const char* hum_led_topic = "machine/led/hum";
const char* smoke_led_topic = "machine/led/smoke";
// === Hardware Pins ===
#define DHTPIN 4
#define DHTTYPE DHT22
#define RELAY_PIN 16
#define TEMP_LED_PIN 17
#define HUM_LED_PIN 19
#define BUZZER_PIN 18
#define SMOKE_PIN 35
// === Thresholds ===
#define MAX_TEMP 60.0
#define MAX_HUM 40.0
#define SMOKE_THRESHOLD 4000
// === Objects ===
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 20, 4);
WiFiClient espClient;
PubSubClient client(espClient);
// === Tracking Previous Errors ===
bool prevOverheat = false, prevHumidity = false, prevSmoke = false;
float prevTemp = 0.0, prevHum = 0.0;
int prevSmokeValue = 0;
// === New: Logging Throttle ===
unsigned long lastLogTime = 0;
const unsigned long logInterval = 10000; // 10 seconds
// === New: Non-blocking loop timing ===
unsigned long lastLoopTime = 0;
const unsigned long loopDelay = 2000;
void connectToWiFi() {
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected.");
}
void connectToMQTT() {
while (!client.connected()) {
Serial.print("Connecting to MQTT...");
if (client.connect(client_id)) {
Serial.println("Connected.");
} else {
Serial.print("Failed, rc=");
Serial.print(client.state());
delay(2000);
}
}
}
void logToGoogleSheets(String type, String value, String status) {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(googleScriptURL);
http.addHeader("Content-Type", "application/json");
String json = "{\"type\":\"" + type + "\",\"value\":\"" + value + "\",\"status\":\"" + status + "\"}";
int code = http.POST(json);
if (code > 0) {
Serial.println("Logged to Sheets: " + json);
} else {
Serial.println("Failed to log: " + String(code));
}
http.end();
}
}
void setup() {
Serial.begin(115200);
dht.begin();
lcd.init();
lcd.backlight();
pinMode(RELAY_PIN, OUTPUT);
pinMode(TEMP_LED_PIN, OUTPUT);
pinMode(HUM_LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(SMOKE_PIN, INPUT);
connectToWiFi();
client.setServer(mqtt_server, mqtt_port);
connectToMQTT();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Machine Health");
lcd.setCursor(0, 1);
lcd.print("Monitor Started");
delay(2000);
}
void loop() {
// === Non-blocking loop delay ===
if (millis() - lastLoopTime < loopDelay) return;
lastLoopTime = millis();
if (!client.connected()) connectToMQTT();
client.loop();
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
int smokeValue = analogRead(SMOKE_PIN);
if (isnan(temperature) || isnan(humidity)) {
Serial.println("DHT sensor error");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("DHT sensor error");
return;
}
bool overheat = temperature > MAX_TEMP;
bool humidityIssue = humidity > MAX_HUM;
bool smokeDetected = smokeValue > SMOKE_THRESHOLD;
bool motorOn = !(overheat || humidityIssue || smokeDetected);
// === Logging condition ===
bool shouldLog = false;
int errorCount = overheat + humidityIssue + smokeDetected;
int prevErrorCount = prevOverheat + prevHumidity + prevSmoke;
if (millis() - lastLogTime >= logInterval) {
if (errorCount > 1 || errorCount != prevErrorCount ||
(overheat && temperature != prevTemp) ||
(humidityIssue && humidity != prevHum) ||
(smokeDetected && smokeValue != prevSmokeValue)) {
shouldLog = true;
lastLogTime = millis(); // Update log timer
}
}
if (shouldLog) {
String types = "", values = "", statuses = "";
if (overheat) {
types += "Temperature ";
values += String(temperature, 1) + "C ";
statuses += "Overheated ";
}
if (humidityIssue) {
types += "Humidity ";
values += String(humidity, 1) + "% ";
statuses += "Humidity Detected ";
}
if (smokeDetected) {
types += "Smoke ";
values += String(smokeValue) + " ppm";
statuses += "Smoke Detected ";
}
types.trim();
values.trim();
statuses.trim();
logToGoogleSheets(types, values, statuses);
}
// Save previous state
prevOverheat = overheat;
prevHumidity = humidityIssue;
prevSmoke = smokeDetected;
prevTemp = temperature;
prevHum = humidity;
prevSmokeValue = smokeValue;
// === Actuator Control ===
digitalWrite(RELAY_PIN, motorOn ? HIGH : LOW);
digitalWrite(TEMP_LED_PIN, overheat ? HIGH : LOW);
digitalWrite(HUM_LED_PIN, humidityIssue ? HIGH : LOW);
// === Updated Buzzer Logic ===
static unsigned long smokeClearTime = 0;
if (smokeDetected) {
tone(BUZZER_PIN, 1000); // Start buzzer
smokeClearTime = millis(); // Reset clear timer
} else if (millis() - smokeClearTime > 3000) {
noTone(BUZZER_PIN); // Stop buzzer after 3 seconds of safe level
}
client.publish(temp_led_topic, overheat ? "ON" : "OFF");
client.publish(hum_led_topic, humidityIssue ? "ON" : "OFF");
client.publish(smoke_led_topic, smokeDetected ? "ON" : "OFF");
// === LCD Display ===
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temp:");
lcd.print(temperature, 1);
lcd.print("C");
lcd.setCursor(11, 0);
lcd.print("Hum:");
lcd.print(humidity, 1);
lcd.print("%");
lcd.setCursor(0, 1);
lcd.print("Smoke Level:");
lcd.print(smokeValue, 1);
lcd.print("ppm");
lcd.setCursor(0, 2);
lcd.print("Motor: ");
lcd.print(motorOn ? "ON - Normal" : "OFF - Alert ");
lcd.setCursor(0, 3);
if (!motorOn) {
if (overheat && humidityIssue && smokeDetected) {
lcd.print("All: Temp+Hum+Smoke");
client.publish(alert_topic, "ALERT: Temp + Humidity + Smoke!");
} else if (overheat && humidityIssue) {
lcd.print("Overheat + Humidity");
client.publish(alert_topic, "ALERT: Temp + Humidity Error");
} else if (overheat && smokeDetected) {
lcd.print("Overheat + Smoke");
client.publish(alert_topic, "ALERT: Overheat + Smoke");
} else if (humidityIssue && smokeDetected) {
lcd.print("Humidity + Smoke");
client.publish(alert_topic, "ALERT: Humidity + Smoke Detected");
} else if (overheat) {
lcd.print("ALERT: Overheat!");
client.publish(alert_topic, "ALERT: Overheat detected!");
} else if (humidityIssue) {
lcd.print("ALERT: Humidity!");
client.publish(alert_topic, "ALERT: Humidity issue detected!");
} else if (smokeDetected) {
lcd.print("ALERT: Smoke Detected");
client.publish(alert_topic, "ALERT: Smoke Level High!");
}
} else {
lcd.print("System NORMAL: OK");
client.publish(alert_topic, "System Normal: Motor ON");
}
// === MQTT Publish ===
char tempBuf[8], humBuf[8], smokeBuf[8];
dtostrf(temperature, 1, 2, tempBuf);
dtostrf(humidity, 1, 2, humBuf);
itoa(smokeValue, smokeBuf, 10);
client.publish(temp_topic, tempBuf);
client.publish(hum_topic, humBuf);
client.publish(smoke_topic, smokeBuf);
}