#include <WiFi.h>
#include <WebServer.h>
#include "DHTesp.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ------------------- WiFi -------------------
const char* SSID = "Wokwi-GUEST"; // your WiFi SSID
const char* PASSWORD = ""; // your WiFi password
WebServer server(80);
// ------------------- DHT22, Gas -------------------
DHTesp dhtSensor;
const int DHT_PIN = 15;
const int GAS_PIN = 34;
// ------------------- OLED -------------------
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
TwoWire I2COLED = TwoWire(1);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &I2COLED);
// ------------------- AI Model -------------------
#define INPUTS 3
#define CLASSES 4
float W[INPUTS][CLASSES];
float B[CLASSES];
// Training data (temperature, humidity, gas)
float X[6][3] = {
{22, 55, 100},
{27, 45, 250},
{30, 60, 500},
{18, 40, 120},
{25, 70, 350},
{31, 80, 700}
};
// Classes: 0=ideal, 1=normal, 2=polluted, 3=gas leak
int y[6] = {0, 1, 2, 0, 1, 3};
// ------------------- Class Labels -------------------
const char* classes[CLASSES] = {
"Ideal environment",
"Normal conditions",
"Polluted environment",
"Gas leak detected!"
};
// ------------------- Variables -------------------
float temp = 0, hum = 0, gas = 0;
String aiPrediction = "Training...";
// ------------------- AI Functions -------------------
int predictClass(float inputs[INPUTS]) {
float out[CLASSES];
for (int j = 0; j < CLASSES; j++) {
out[j] = B[j];
for (int i = 0; i < INPUTS; i++) {
out[j] += inputs[i] * W[i][j];
}
}
int best = 0;
for (int j = 1; j < CLASSES; j++) {
if (out[j] > out[best]) best = j;
}
return best;
}
void trainPerceptron(int epochs = 50, float lr = 0.001) {
for (int e = 0; e < epochs; e++) {
for (int s = 0; s < 6; s++) {
float inputs[INPUTS] = {X[s][0], X[s][1], X[s][2]};
int target = y[s];
int pred = predictClass(inputs);
if (pred != target) {
for (int j = 0; j < CLASSES; j++) {
float error = (j == target ? 1.0 : 0.0) - (j == pred ? 1.0 : 0.0);
for (int i = 0; i < INPUTS; i++) {
W[i][j] += lr * error * inputs[i];
}
B[j] += lr * error;
}
}
}
}
}
// ------------------- Gas Sensor -------------------
const float VCC = 3.3;
const float RL = 10000.0;
float getGasPPM() {
int adc = analogRead(GAS_PIN);
float voltage = adc * (VCC / 4095.0);
if (voltage <= 0.1) voltage = 0.1;
float Rs = (VCC - voltage) * RL / voltage;
float R0 = 8500.0;
float ratio = Rs / R0;
float a = -2.3, b = 0.72;
float ppm_log = a * log10(ratio) + b;
float ppm = pow(10, ppm_log);
return (ppm < 0) ? 0 : ppm;
}
// ------------------- Web Page -------------------
void sendHtml() {
String html = R"rawliteral(
<html>
<head>
<meta http-equiv="refresh" content="5">
<title>AI Air Quality Dashboard</title>
<style>
body {
font-family: 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #e2ecff, #ffffff);
margin: 0;
padding: 0;
text-align: center;
color: #222;
}
h1 {
margin-top: 25px;
font-size: 2em;
color: #3b3b98;
text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
}
.container {
margin-top: 25px;
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 15px;
}
.card {
background: #ffffff;
border-radius: 18px;
padding: 20px;
width: 260px;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
}
.card:hover {
transform: scale(1.04);
}
.label {
color: #666;
font-size: 0.9em;
margin-bottom: 5px;
}
.value {
font-size: 1.8em;
font-weight: bold;
color: #3b3b98;
}
.prediction {
background: #3b3b98;
color: #fff;
padding: 15px;
margin-top: 20px;
border-radius: 14px;
font-size: 1.2em;
font-weight: bold;
width: 80%;
margin-left: auto;
margin-right: auto;
box-shadow: 0 6px 12px rgba(59,59,152,0.3);
}
footer {
margin-top: 20px;
color: #777;
font-size: 0.8em;
}
</style>
</head>
<body>
<h1>AI Environmental Monitor</h1>
<div class="container">
<div class="card">
<div class="label">Temperature (°C)</div>
<div class="value">%TEMP%</div>
</div>
<div class="card">
<div class="label">Humidity (%)</div>
<div class="value">%HUM%</div>
</div>
<div class="card">
<div class="label">Gas (ppm)</div>
<div class="value">%GAS%</div>
</div>
</div>
<div class="prediction">
AI Prediction: %PRED%
</div>
<footer>Auto-refresh every 5 seconds</footer>
</body>
</html>
)rawliteral";
html.replace("%TEMP%", String(temp, 1));
html.replace("%HUM%", String(hum, 1));
html.replace("%GAS%", String(gas, 1));
html.replace("%PRED%", aiPrediction);
server.send(200, "text/html", html);
}
// ------------------- SETUP -------------------
void setup() {
Serial.begin(115200);
dhtSensor.setup(DHT_PIN, DHTesp::DHT22);
I2COLED.begin(14, 12);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
// Initialize random weights
for (int i = 0; i < INPUTS; i++)
for (int j = 0; j < CLASSES; j++)
W[i][j] = random(-100, 100) / 1000.0;
for (int j = 0; j < CLASSES; j++) B[j] = 0.0;
Serial.println("=== Starting Onboard AI Training ===");
trainPerceptron(200, 0.0001);
Serial.println("=== Training Completed ===");
// WiFi setup
WiFi.begin(SSID, PASSWORD);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected!");
Serial.println(WiFi.localIP());
// Web server
server.on("/", sendHtml);
server.begin();
Serial.println("Web server started.");
}
// ------------------- LOOP -------------------
void loop() {
server.handleClient();
TempAndHumidity th = dhtSensor.getTempAndHumidity();
temp = th.temperature;
hum = th.humidity;
gas = getGasPPM();
float inputs[INPUTS] = {temp, hum, gas};
int cls = predictClass(inputs);
aiPrediction = classes[cls];
// OLED Display
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("Env. Monitoring AI");
display.printf("T: %.1fC\nH: %.1f%%\nG: %.1fppm\n", temp, hum, gas);
display.println(aiPrediction);
display.display();
Serial.printf("Temp: %.1f | Hum: %.1f | Gas: %.1f | AI: %s\n", temp, hum, gas, aiPrediction.c_str());
delay(5000);
}