#include <WiFi.h>
#include <WebServer.h>
#include <LiquidCrystal_I2C.h>
#define ANALOG_PIN 34
#define HISTORY 60
#define INPUTS 5
// ---- Wi-Fi ----
const char* SSID = "Wokwi-GUEST";
const char* PASSWORD = "";
WebServer server(80);
// ---- LCD ----
LiquidCrystal_I2C LCD(0x27, 16, 2);
// ---- Data ----
float powerHist[HISTORY];
int histIdx = 0;
// ---- Mini-Perceptron ----
// 5 inputs → 1 output (sigmoid)
float W[INPUTS];
float b = 0;
float lr = 0.01;
float sigmoid(float x) { return 1.0 / (1.0 + exp(-x)); }
float forward(float x[INPUTS]) {
float s = b;
for (int i = 0; i < INPUTS; i++) s += W[i] * x[i];
return sigmoid(s);
}
void trainPerceptron(int epochs = 300) {
// Small synthetic training set: simple pattern recognition
float X[8][INPUTS] = {
{0,0,0,0,0}, {0.1,0.2,0.1,0.2,0.1}, {0.5,0.6,0.5,0.6,0.5}, {0.8,0.9,0.8,0.9,0.8},
{0.2,0.9,0.1,0.8,0.2}, {0.9,0.2,0.8,0.1,0.9}, {0.4,0.8,0.3,0.9,0.4}, {0.9,0.9,0.9,0.9,0.9}
};
int y[8] = {0,0,0,1,1,1,1,1}; // 1=anomaly
for (int i = 0; i < INPUTS; i++) W[i] = random(-100,100)/500.0;
b = 0;
for (int e=0; e<epochs; e++){
for (int n=0;n<8;n++){
float out = forward(X[n]);
float err = y[n]-out;
for(int i=0;i<INPUTS;i++) W[i]+=lr*err*X[n][i];
b += lr*err;
}
}
}
// ---- Utility ----
float norm(float v, float minv, float maxv){ return (v-minv)/(maxv-minv); }
bool detectAnomaly(){
float inputs[INPUTS];
int j = histIdx-INPUTS;
if (j<0) j+=HISTORY;
for(int i=0;i<INPUTS;i++){
inputs[i]=norm(powerHist[(j+i)%HISTORY],0,4095); // normalize ADC range
}
float out = forward(inputs);
return out>0.6;
}
// ---- HTML page ----
String makePage(bool anomaly, float power){
String html = R"rawliteral(
<html>
<head>
<meta http-equiv='refresh' content='5'>
<title>Smart Energy Monitor AI</title>
<style>
body{margin:0;font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background:#0d1117; color:#ffffff; text-align:center;}
h1{margin-top:20px; color:#00ffff; text-shadow: 0 0 10px #00ffff;}
h3{color:#ffdd00; margin-bottom:20px;}
canvas{width:80%; height:200px; background:#111; border-radius:12px; margin-bottom:20px;}
.status{padding:15px;margin:15px auto;width:320px;border-radius:12px;font-weight:bold; box-shadow:0 0 20px rgba(0,255,255,0.5);}
.ok{background:#0a1f2e; color:#00ffcc;}
.bad{background:#440000; color:#ff4444;}
footer{color:#888; margin-top:10px; font-size:0.8em;}
</style>
</head>
<body>
<h1>Smart Energy Monitor</h1>
<h3>Current Power: %POWER% (ADC)</h3>
<div class='status %CLS%'>%MSG%</div>
<canvas id="chart"></canvas>
<footer>Auto-refresh every 5 seconds | AI-powered anomaly detection</footer>
<script>
let data=[%DATA%];
let c=document.getElementById('chart');
let ctx=c.getContext('2d');
ctx.clearRect(0,0,c.width,c.height);
ctx.strokeStyle="#00ffff";
ctx.lineWidth=2;
ctx.beginPath();
ctx.moveTo(0, c.height - data[0]/20);
for(let i=1;i<data.length;i++){
ctx.lineTo(i*(c.width/data.length), c.height - data[i]/20);
}
ctx.stroke();
ctx.strokeStyle="#ffdd00";
ctx.setLineDash([5,5]);
ctx.beginPath();
ctx.moveTo(0, c.height-200);
ctx.lineTo(c.width, c.height-200);
ctx.stroke();
</script>
</body>
</html>
)rawliteral";
html.replace("%POWER%", String(power,1));
html.replace("%CLS%", anomaly?"bad":"ok");
html.replace("%MSG%", anomaly?"Anomaly detected!":"Normal consumption");
String d="";
for(int i=0;i<HISTORY;i++){
d += String(powerHist[(histIdx+i)%HISTORY]);
if(i<HISTORY-1) d+=",";
}
html.replace("%DATA%",d);
return html;
}
// ---- Setup ----
void setup(){
Serial.begin(115200);
LCD.init(); LCD.backlight();
WiFi.begin(SSID, PASSWORD);
while(WiFi.status()!=WL_CONNECTED){delay(500);Serial.print(".");}
Serial.println("\nWiFi connected: "+WiFi.localIP().toString());
LCD.clear(); LCD.setCursor(0,0); LCD.print("AI Training...");
trainPerceptron();
LCD.setCursor(0,1); LCD.print("Done!");
delay(1000);
for(int i=0;i<HISTORY;i++) powerHist[i]=analogRead(ANALOG_PIN);
server.on("/", [](){
bool anom = detectAnomaly();
String page = makePage(anom, powerHist[(histIdx-1+HISTORY)%HISTORY]);
server.send(200,"text/html",page);
});
server.begin();
LCD.clear(); LCD.print("Server Ready");
}
// ---- Loop ----
void loop(){
server.handleClient();
float val = analogRead(ANALOG_PIN);
powerHist[histIdx] = val;
histIdx = (histIdx+1)%HISTORY;
bool anomaly = detectAnomaly();
Serial.printf("Power=%.1f | %s\n", val, anomaly?"Anomaly!":"Normal");
LCD.clear();
LCD.setCursor(0,0);
LCD.print("Pwr:" + String(val,0));
LCD.setCursor(0,1);
LCD.print(anomaly?"Anomaly!":"Normal");
delay(1000);
}