#include <WiFi.h>
#include <HTTPClient.h>
#include <DHT.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <LiquidCrystal_I2C.h>
#include <PubSubClient.h>
// ---------------- PIN DEFINITIONS ----------------
#define DHTPIN 4
#define DHTTYPE DHT22
#define GAS_PIN 34
#define BUZZER 19
#define RELAY 15
#define LED_RED 18
#define LED_YELLOW 5
#define LED_GREEN 23
#define BUTTON_PIN 13
// ---------------- THRESHOLDS ----------------
const int GAS_SAFE_MAX = 1500;
const int GAS_WARNING_MAX = 2500;
const float TEMP_WARNING = 35.0;
const float TEMP_UNSAFE = 50.0;
const float HUM_WARNING = 70.0;
const float HUM_UNSAFE = 85.0;
// ---------------- WIFI ----------------
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// ---------------- FIREBASE & TELEGRAM ----------------
const char* firebaseURL = "https://smart-air-quality-system-97e90-default-rtdb.firebaseio.com/device_1.json";
const char* BOT_TOKEN = "8342604590:AAHd8syzDi7qS-PTyd8LTp120-nhcHYKg6k";
const char* CHAT_ID = "1761625405";
// ---------------- THINGSBOARD ----------------
const char* TB_SERVER = "thingsboard.cloud";
const int TB_PORT = 1883;
const char* TB_TOKEN = "eoC2r1jb5HSRgaEwoapL";
// ---------------- OBJECTS ----------------
WiFiClient espClient;
PubSubClient tbClient(espClient);
DHT dht(DHTPIN, DHTTYPE);
Adafruit_SSD1306 oled(128, 64, &Wire, -1);
LiquidCrystal_I2C lcd(0x27, 20, 4);
// ---------------- STATES ----------------
enum SystemState { SAFE, WARNING, UNSAFE };
SystemState currentState = SAFE;
SystemState lastState = SAFE;
bool buzzerSilenced = false;
// ---------------- GAS SMOOTHING ----------------
#define GAS_WINDOW_SIZE 5
int gasBuffer[GAS_WINDOW_SIZE];
int gasIndex = 0;
bool gasBufferFilled = false;
int getSmoothedGas(int newGas) {
gasBuffer[gasIndex++] = newGas;
if (gasIndex >= GAS_WINDOW_SIZE) { gasIndex = 0; gasBufferFilled = true; }
int sum = 0;
int count = gasBufferFilled ? GAS_WINDOW_SIZE : gasIndex;
for (int i = 0; i < count; i++) sum += gasBuffer[i];
return sum / count;
}
// ---------------- LOGIC ----------------
SystemState evaluateAirQuality(float t, float h, int g) {
if (g >= GAS_WARNING_MAX || t >= TEMP_UNSAFE || h >= HUM_UNSAFE) return UNSAFE;
if (g >= GAS_SAFE_MAX || t >= TEMP_WARNING || h >= HUM_WARNING) return WARNING;
return SAFE;
}
void applyState(SystemState s, float t, float h, int g) {
bool fanOn = (s != SAFE);
digitalWrite(RELAY, fanOn ? HIGH : LOW);
if (s != UNSAFE) {
buzzerSilenced = false;
noTone(BUZZER);
if (s == SAFE) { digitalWrite(LED_GREEN, HIGH); digitalWrite(LED_YELLOW, LOW); digitalWrite(LED_RED, LOW); }
else { digitalWrite(LED_GREEN, LOW); digitalWrite(LED_YELLOW, HIGH); digitalWrite(LED_RED, LOW); }
} else {
digitalWrite(LED_GREEN, LOW); digitalWrite(LED_YELLOW, LOW); digitalWrite(LED_RED, HIGH);
if (!buzzerSilenced) {
bool isTotalUnsafe = (g >= GAS_WARNING_MAX && (t >= TEMP_UNSAFE || h >= HUM_UNSAFE));
bool isGasOnly = (g >= GAS_WARNING_MAX);
// Continuous sound for Total Danger or Gas Leak
if (isTotalUnsafe || isGasOnly) {
tone(BUZZER, 1000);
}
// 3-second interval for Temp/Hum only
else {
static unsigned long lastToggle = 0;
static bool bState = false;
if (millis() - lastToggle >= 3000) {
bState = !bState;
if (bState) tone(BUZZER, 1000); else noTone(BUZZER);
lastToggle = millis();
}
}
}
}
}
// ---------------- EXTERNAL SERVICES ----------------
void sendTelegramAlert(String msg) {
if (WiFi.status() != WL_CONNECTED) return;
HTTPClient http;
String url = "https://api.telegram.org/bot" + String(BOT_TOKEN) + "/sendMessage?chat_id=" + String(CHAT_ID) + "&text=" + msg;
http.begin(url); http.GET(); http.end();
}
void sendToFirebase(float t, float h, int g, bool fanOn) {
if (WiFi.status() != WL_CONNECTED) return;
HTTPClient http;
http.begin(firebaseURL);
http.addHeader("Content-Type", "application/json");
String airQ = (currentState == SAFE) ? "SAFE" : (currentState == WARNING) ? "WARNING" : "UNSAFE";
String payload = "{\"sensors\":{\"temp\":" + String(t,1) + ",\"hum\":" + String(h,1) + ",\"gas\":" + String(g) + "},";
payload += "\"status\":{\"air_quality\":\"" + airQ + "\",\"fan\":\"" + String(fanOn ? "ON" : "OFF") + "\"}}";
http.PUT(payload); http.end();
}
void sendToThingsBoard(float t, float h, int g, bool fanOn) {
if (!tbClient.connected()) return;
String stateStr = (currentState == SAFE) ? "SAFE" : (currentState == WARNING) ? "WARNING" : "UNSAFE";
String payload = "{\"temperature\":" + String(t,1) + ",\"humidity\":" + String(h,1) + ",\"gas\":" + String(g) + ",\"fan\":\"" + String(fanOn ? "ON" : "OFF") + "\",\"air_quality\":\"" + stateStr + "\"}";
tbClient.publish("v1/devices/me/telemetry", payload.c_str());
}
// ---------------- OLED & LCD COMPLEX UPDATES ----------------
void updateDisplays(float t, float h, int g, bool fanOn) {
lcd.clear();
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(SSD1306_WHITE);
// OLED Standard Readings
oled.setCursor(0,0); oled.print("T: "); oled.print(t,1); oled.print(" C");
oled.setCursor(0,13); oled.print("H: "); oled.print(h,1); oled.print(" %");
oled.setCursor(0,26); oled.print("G: "); oled.print(g); oled.print(" PPM");
oled.setCursor(0,39); oled.print("F: "); oled.print(fanOn ? "ON" : "OFF");
oled.setCursor(0,54); oled.print(currentState == SAFE ? "STATUS: SAFE" : currentState == WARNING ? "STATUS: WARN" : "STATUS: DANGER");
// LCD Complex Logic
lcd.setCursor(0, 0);
if (currentState == SAFE) {
lcd.print("Status: SAFE");
} else if (currentState == WARNING) {
lcd.print("Status: WARNING");
} else {
if (g >= GAS_WARNING_MAX && (t >= TEMP_UNSAFE || h >= HUM_UNSAFE)) {
lcd.print("Unsafe Air Quality"); lcd.setCursor(0,1); lcd.print("Please evacuate!");
} else if (g >= GAS_WARNING_MAX) {
lcd.print("GAS UNSAFE,Please"); lcd.setCursor(0,1); lcd.print("leave the house!");
} else if (t >= TEMP_UNSAFE && h >= HUM_UNSAFE) {
lcd.print("!!! WARNING !!!"); lcd.setCursor(0,1); lcd.print("HIGH Temp & Hum");
} else if (t >= TEMP_UNSAFE) {
lcd.print("!!! WARNING !!!"); lcd.setCursor(0,1); lcd.print("HIGH Temperature");
} else if (h >= HUM_UNSAFE) {
lcd.print("!!! WARNING !!!"); lcd.setCursor(0,1); lcd.print("HIGH Humidity");
}
}
oled.display();
}
// ---------------- SETUP & LOOP ----------------
void setup() {
Serial.begin(115200);
Serial.println("--- IoT System Booting ---");
WiFi.begin(ssid, password);
unsigned long startT = millis();
while (WiFi.status() != WL_CONNECTED && millis() - startT < 8000) delay(500);
tbClient.setServer(TB_SERVER, TB_PORT);
pinMode(BUZZER, OUTPUT); pinMode(RELAY, OUTPUT); pinMode(LED_RED, OUTPUT);
pinMode(LED_YELLOW, OUTPUT); pinMode(LED_GREEN, OUTPUT); pinMode(BUTTON_PIN, INPUT_PULLUP);
dht.begin(); oled.begin(SSD1306_SWITCHCAPVCC, 0x3C); lcd.init(); lcd.backlight();
}
void loop() {
if (!tbClient.connected() && WiFi.status() == WL_CONNECTED) tbClient.connect("IoTDevice", TB_TOKEN, NULL);
tbClient.loop();
float t = dht.readTemperature(); float h = dht.readHumidity();
int g = getSmoothedGas(analogRead(GAS_PIN));
currentState = evaluateAirQuality(t, h, g);
bool fanOn = (currentState != SAFE);
applyState(currentState, t, h, g);
updateDisplays(t, h, g, fanOn);
sendToFirebase(t, h, g, fanOn);
sendToThingsBoard(t, h, g, fanOn);
// Telegram Specific Messages
if (currentState == UNSAFE && lastState != UNSAFE) {
String tMsg = "";
if (g >= GAS_WARNING_MAX && (t >= TEMP_UNSAFE || h >= HUM_UNSAFE)) tMsg = "Unsafe Air Quality detected Please evacuate immediately";
else if (g >= GAS_WARNING_MAX) tMsg = "GAS UNSAFE, Please leave the house or fix the leak immediately";
else if (t >= TEMP_UNSAFE && h >= HUM_UNSAFE) tMsg = "!!! WARNING !!! HIGH Temperature and Humidity detected";
else if (t >= TEMP_UNSAFE) tMsg = "!!! WARNING !!! HIGH Temperature";
else if (h >= HUM_UNSAFE) tMsg = "!!! WARNING !!! HIGH Humidity";
sendTelegramAlert(tMsg);
}
// Serial Monitor Output
// Serial Monitor Output with Humidity added
Serial.print("T: "); Serial.print(t, 1);
Serial.print(" | H: "); Serial.print(h, 1); // Added Humidity here
Serial.print(" | G: "); Serial.print(g);
Serial.print(" | Fan: "); Serial.print(fanOn ? "ON" : "OFF");
Serial.print(" | Status: ");
Serial.println(currentState == SAFE ? "SAFE" : currentState == WARNING ? "WARN" : "DANGER");
lastState = currentState;
delay(2000);
}SAFE
UNSAFE
WARNING
FAN