#if defined(ESP32)
#include <WiFi.h>
#elif defined(ESP8266)
#include <ESP8266WiFi.h>
#endif
#include <ESP_Mail_Client.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Fonts/FreeSansBold18pt7b.h>
#include <DHT.h>
// --- Pin Definitions ---
#define I2C_SDA 21
#define I2C_SCL 22
#define FLAME_SENSOR_PIN 15
#define GAS_SENSOR_PIN 2
#define DHT_PIN 12
#define DHT_TYPE DHT22
// --- OLED Configuration ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDRESS 0x3C // Common I2C address for SSD1306 (0x3C or 0x3D)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
DHT dht(DHT_PIN, DHT_TYPE);
// --- Network & Mail Configuration ---
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASSWORD ""
#define SMTP_HOST "smtp.gmail.com"
#define SMTP_PORT 465
#define AUTHOR_EMAIL "[email protected]"
#define AUTHOR_PASSWORD "viom mfli mpkm fwol"
#define RECIPIENT_EMAIL "[email protected]"
SMTPSession smtp;
// --- Timing Variables ---
unsigned long lastDisplayUpdate = 0;
const unsigned long displayInterval = 2000; // Switch display pages every 2s
unsigned long lastEmailSent = 0;
const unsigned long emailCooldown = 60000; // Minimum 60s between alert emails
bool displayToggle = false;
// Function Prototypes
void smtpCallback(SMTP_Status status);
void send_mail(float temp, float hum, int flame, int gas);
void updateDisplay(float temp, float hum, int flame, int gas);
void setup() {
Serial.begin(115200);
// Initialize I2C explicitly with specified pins
Wire.begin(I2C_SDA, I2C_SCL);
// Initialize OLED Display
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 10);
display.println(F("System Initializing..."));
display.display();
dht.begin();
pinMode(FLAME_SENSOR_PIN, INPUT_PULLUP);
pinMode(GAS_SENSOR_PIN, INPUT_PULLUP);
// Connect to Wi-Fi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(300);
}
Serial.println("\nConnected with IP: " + WiFi.localIP().toString());
display.clearDisplay();
display.setCursor(0, 10);
display.println(F("WiFi Connected!"));
display.display();
delay(1000);
}
void loop() {
int flameValue = digitalRead(FLAME_SENSOR_PIN);
int gasValue = digitalRead(GAS_SENSOR_PIN);
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
// Validate DHT readings
if (isnan(humidity) || isnan(temperature)) {
Serial.println(F("Failed to read from DHT sensor!"));
return;
}
// Alarm Condition Assessment (Active Low for Flame/Gas Digital Outputs)
bool isAlarm = (temperature > 40.0) || (flameValue == LOW) || (gasValue == LOW);
if (isAlarm && (millis() - lastEmailSent > emailCooldown)) {
Serial.println("Alarm condition detected! Sending email...");
send_mail(temperature, humidity, flameValue, gasValue);
lastEmailSent = millis();
}
// Update Display periodically
if (millis() - lastDisplayUpdate >= displayInterval) {
lastDisplayUpdate = millis();
displayToggle = !displayToggle;
updateDisplay(temperature, humidity, flameValue, gasValue);
}
}
void updateDisplay(float temp, float hum, int flame, int gas) {
display.clearDisplay();
if (displayToggle) {
// Page 1: Large Temperature Display using TrueType Font
display.setFont(); // Default font for header label
display.setTextSize(1);
display.setCursor(0, 0);
display.print(F("TEMPERATURE"));
// Render Big Temp Value
display.setFont(&FreeSansBold18pt7b);
display.setCursor(5, 42);
display.printf("%.1f", temp);
// Celsius Symbol with default font
display.setFont();
display.setTextSize(1);
display.setCursor(95, 20);
display.print((char)247); // Degree symbol
display.print("C");
// Bottom Status Line
display.setCursor(0, 54);
display.printf("Humidity: %.1f%%", hum);
} else {
// Page 2: System & Sensor Status Overview
display.setFont();
display.setTextSize(1);
display.setCursor(0, 0);
display.println(F("--- SENSOR STATUS ---"));
display.setCursor(0, 18);
display.printf("Flame Sensor : %s\n", (flame == LOW) ? "ALARM!" : "OK");
display.setCursor(0, 32);
display.printf("Gas Sensor : %s\n", (gas == LOW) ? "ALARM!" : "OK");
display.setCursor(0, 48);
display.printf("System Temp : %.1f C", temp);
}
display.display();
}
/* Callback function to log Email sending status */
void smtpCallback(SMTP_Status status) {
Serial.println(status.info());
if (status.success()) {
Serial.println("----------------");
ESP_MAIL_PRINTF("Message sent success: %d\n", status.completedCount());
ESP_MAIL_PRINTF("Message sent failed: %d\n", status.failedCount());
Serial.println("----------------\n");
for (size_t i = 0; i < smtp.sendingResult.size(); i++) {
SMTP_Result result = smtp.sendingResult.getItem(i);
ESP_MAIL_PRINTF("Message No: %d\n", i + 1);
ESP_MAIL_PRINTF("Status: %s\n", result.completed ? "success" : "failed");
ESP_MAIL_PRINTF("Date/Time: %s\n", MailClient.Time.getDateTimeString(result.timestamp, "%B %d, %Y %H:%M:%S").c_str());
}
smtp.sendingResult.clear();
}
}
void send_mail(float temp, float hum, int flame, int gas) {
MailClient.networkReconnect(true);
smtp.debug(0); // Set to 1 for verbose SMTP debugging
smtp.callback(smtpCallback);
Session_Config config;
config.server.host_name = SMTP_HOST;
config.server.port = SMTP_PORT;
config.login.email = AUTHOR_EMAIL;
config.login.password = AUTHOR_PASSWORD;
config.login.user_domain = "";
config.time.ntp_server = F("pool.ntp.org,time.nist.gov");
config.time.gmt_offset = 3;
config.time.day_light_offset = 0;
SMTP_Message message;
message.sender.name = F("ESP32 Safety System");
message.sender.email = AUTHOR_EMAIL;
message.subject = F("CRITICAL ALARM: Hazard Detected!");
message.addRecipient(F("Recipient"), RECIPIENT_EMAIL);
// Dynamic Alert Message
String textMsg = "CRITICAL ALARM DETECTED!\n\n";
textMsg += "Temperature: " + String(temp, 1) + " C\n";
textMsg += "Humidity: " + String(hum, 1) + " %\n";
textMsg += "Flame Status: " + String((flame == LOW) ? "FIRE DETECTED" : "Normal") + "\n";
textMsg += "Gas Status: " + String((gas == LOW) ? "GAS LEAK DETECTED" : "Normal") + "\n";
message.text.content = textMsg.c_str();
message.text.charSet = "us-ascii";
message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit;
message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_high;
if (!smtp.connect(&config)) {
ESP_MAIL_PRINTF("Connection error, Status Code: %d, Error Code: %d, Reason: %s\n", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
return;
}
if (!MailClient.sendMail(&smtp, &message)) {
ESP_MAIL_PRINTF("Error sending Email, Status Code: %d, Error Code: %d, Reason: %s\n", smtp.statusCode(), smtp.errorCode(), smtp.errorReason().c_str());
}
}