#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ========== WIFI ==========
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASS = "";
// ========== PIN CONFIG ==========
#define LED_WIFI 16
#define SW_RESET 32
#define OLED_SDA 21
#define OLED_SCL 22
// ========== OLED ==========
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(
SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1
);
WebServer server(80);
bool oledOK = false;
bool ledState = false;
bool webStarted = false;
bool wasConnected = false;
unsigned long lastBlink = 0;
unsigned long lastOLED = 0;
unsigned long lastRetry = 0;
const unsigned long BLINK_MS = 250;
const unsigned long OLED_MS = 500;
const unsigned long RETRY_MS = 10000;
// Button debounce
bool lastReading = HIGH;
bool stableButton = HIGH;
unsigned long debounceStart = 0;
const unsigned long DEBOUNCE_MS = 50;
// Delayed reconnect for web request
bool reconnectPending = false;
unsigned long reconnectAt = 0;
// ========== HTML DASHBOARD ==========
const char WEB_PAGE[] PROGMEM = R"HTML(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width,initial-scale=1">
<title>ESP32 WiFi Dashboard</title>
<style>
* { box-sizing:border-box; }
body {
margin:0; padding:20px;
font-family:Arial,sans-serif;
background:#101827; color:#e5edf8;
}
.container { max-width:780px; margin:auto; }
h1 { text-align:center; font-size:27px; }
.subtitle { text-align:center; color:#9ca3af; }
.card {
background:#1e293b;
padding:20px; border-radius:15px;
margin:15px 0;
}
.grid {
display:grid;
grid-template-columns:repeat(2,minmax(0,1fr));
gap:12px;
}
.label { color:#94a3b8; font-size:13px; }
.value {
font-size:22px; font-weight:bold;
margin-top:8px; overflow-wrap:anywhere;
}
.status { text-align:center; font-size:23px; }
.online { color:#4ade80; }
.offline { color:#f87171; }
.track {
width:100%; height:15px;
background:#334155; border-radius:20px;
overflow:hidden;
}
.bar {
background:#22c55e;
height:100%; width:0%;
transition:width .3s;
}
button {
background:#2563eb; color:white;
padding:14px; border:0; width:100%;
border-radius:9px; font-size:16px;
cursor:pointer;
}
button:hover { background:#1d4ed8; }
.note { color:#94a3b8; text-align:center; }
@media(max-width:450px) {
.grid { grid-template-columns:1fr; }
}
</style>
</head>
<body>
<div class="container">
<h1>ESP32 WiFi Dashboard</h1>
<p class="subtitle">Real-Time Network Monitoring</p>
<div class="card status">
<div id="status" class="offline">Loading...</div>
</div>
<div class="grid">
<div class="card">
<div class="label">Network SSID</div>
<div class="value" id="ssid">--</div>
</div>
<div class="card">
<div class="label">IP Address</div>
<div class="value" id="ip">--</div>
</div>
<div class="card">
<div class="label">Signal Strength</div>
<div class="value" id="rssi">--</div>
</div>
<div class="card">
<div class="label">Device Uptime</div>
<div class="value" id="uptime">--</div>
</div>
</div>
<div class="card">
<h3>WiFi Signal Quality</h3>
<div class="track">
<div class="bar" id="bar"></div>
</div>
<p id="quality">--</p>
</div>
<div class="card">
<button onclick="resetWifi()">
Reconnect WiFi
</button>
<p class="note" id="message">
Automatic update every 2 seconds
</p>
</div>
</div>
<script>
const $ = id => document.getElementById(id);
async function updateData() {
try {
const response = await fetch('/api/status',
{cache:'no-store'});
if (!response.ok) throw new Error('HTTP error');
const d = await response.json();
$('status').textContent =
d.connected ? 'CONNECTED' : 'DISCONNECTED';
$('status').className =
d.connected ? 'online' : 'offline';
$('ssid').textContent = d.ssid || '--';
$('ip').textContent = d.ip || '--';
$('rssi').textContent =
d.connected ? d.rssi + ' dBm' : '--';
$('uptime').textContent = d.uptime + ' sec';
$('bar').style.width = d.quality + '%';
$('quality').textContent =
d.quality + '% signal quality';
} catch(e) {
$('status').textContent = 'DEVICE OFFLINE';
$('status').className = 'offline';
$('message').textContent =
'Cannot reach ESP32. Waiting for reconnect...';
}
}
async function resetWifi() {
$('message').textContent = 'Reconnecting WiFi...';
try {
const response = await fetch('/api/reconnect',
{method:'POST'});
if (!response.ok) throw new Error('Request failed');
$('message').textContent =
'Reconnect requested. Please wait...';
} catch(e) {
$('message').textContent =
'Connection lost. Try refreshing later.';
}
}
updateData();
setInterval(updateData, 2000);
</script>
</body>
</html>
)HTML";
// Escape JSON string data
String jsonEscape(const String &value) {
String out = "";
for (size_t i = 0; i < value.length(); i++) {
char c = value[i];
if (c == '"' || c == '\\') {
out += '\\';
out += c;
} else if (c == '\n') {
out += "\\n";
} else if (c == '\r') {
out += "\\r";
} else if ((unsigned char)c < 32) {
out += ' ';
} else {
out += c;
}
}
return out;
}
// ========== WIFI CONNECTION ==========
void connectWiFi() {
Serial.println("Connecting to WiFi...");
wasConnected = false;
digitalWrite(LED_WIFI, LOW);
ledState = false;
WiFi.disconnect();
WiFi.begin(WIFI_SSID, WIFI_PASS);
lastRetry = millis();
}
// ========== API STATUS ==========
void handleStatus() {
bool connected = WiFi.status() == WL_CONNECTED;
int rssi = connected ? WiFi.RSSI() : 0;
int quality = 0;
if (connected) {
quality = constrain(2 * (rssi + 100), 0, 100);
}
String json = "{";
json += "\"connected\":";
json += connected ? "true" : "false";
json += ",\"ssid\":\"";
json += jsonEscape(connected ? WiFi.SSID() : "");
json += "\"";
json += ",\"ip\":\"";
json += connected ? WiFi.localIP().toString() : "";
json += "\"";
json += ",\"rssi\":" + String(rssi);
json += ",\"quality\":" + String(quality);
json += ",\"uptime\":" + String(millis() / 1000);
json += "}";
server.sendHeader("Cache-Control", "no-store");
server.send(200, "application/json", json);
}
// ========== WEB RECONNECT ==========
void handleReconnect() {
server.send(200, "application/json",
"{\"message\":\"Reconnecting\"}");
reconnectPending = true;
reconnectAt = millis() + 300;
}
// ========== WEB SERVER ==========
void setupWebServer() {
server.on("/", HTTP_GET, []() {
server.send_P(200, "text/html", WEB_PAGE);
});
server.on("/api/status", HTTP_GET, handleStatus);
server.on("/api/reconnect", HTTP_POST, handleReconnect);
server.onNotFound([]() {
server.send(404, "text/plain", "Not Found");
});
server.begin();
webStarted = true;
Serial.println("Web Server Started");
}
// ========== BUTTON ==========
void checkButton() {
bool reading = digitalRead(SW_RESET);
if (reading != lastReading) {
debounceStart = millis();
}
if (millis() - debounceStart >= DEBOUNCE_MS) {
if (reading != stableButton) {
stableButton = reading;
if (stableButton == LOW) {
Serial.println("SW Pressed: Reconnect WiFi");
connectWiFi();
}
}
}
lastReading = reading;
}
// ========== LED ==========
void updateLED() {
unsigned long now = millis();
if (WiFi.status() == WL_CONNECTED) {
digitalWrite(LED_WIFI, HIGH);
if (!wasConnected) {
wasConnected = true;
Serial.println("WiFi CONNECTED!");
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
Serial.print("IP: ");
Serial.println(WiFi.localIP());
Serial.print("RSSI: ");
Serial.println(WiFi.RSSI());
}
} else {
if (wasConnected) {
wasConnected = false;
Serial.println("WiFi Disconnected");
}
if (now - lastBlink >= BLINK_MS) {
lastBlink = now;
ledState = !ledState;
digitalWrite(LED_WIFI, ledState);
}
}
}
// ========== OLED ==========
void updateOLED() {
if (!oledOK) return;
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.println("ESP32 WIFI MONITOR");
display.drawLine(0, 12, 127, 12, SSD1306_WHITE);
if (WiFi.status() == WL_CONNECTED) {
display.setCursor(0, 17);
display.println("Status: CONNECTED");
display.setCursor(0, 28);
display.print("SSID:");
display.println(WiFi.SSID().substring(0, 15));
display.setCursor(0, 39);
display.print("IP:");
display.println(WiFi.localIP());
display.setCursor(0, 50);
display.print("RSSI:");
display.print(WiFi.RSSI());
display.println(" dBm");
} else {
display.setCursor(0, 20);
display.println("CONNECTING WIFI...");
display.setCursor(0, 38);
display.println("Please wait...");
}
display.display();
}
// ========== SETUP ==========
void setup() {
Serial.begin(115200);
pinMode(LED_WIFI, OUTPUT);
pinMode(SW_RESET, INPUT_PULLUP);
digitalWrite(LED_WIFI, LOW);
Wire.begin(OLED_SDA, OLED_SCL);
oledOK = display.begin(
SSD1306_SWITCHCAPVCC, OLED_ADDR
);
if (!oledOK) {
Serial.println("OLED not found");
} else {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 22);
display.println("ESP32 WIFI SYSTEM");
display.display();
}
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
setupWebServer();
connectWiFi();
}
// ========== LOOP ==========
void loop() {
unsigned long now = millis();
// Respond to browser requests
if (WiFi.status() == WL_CONNECTED && webStarted) {
server.handleClient();
}
// Physical reset switch
checkButton();
// Delayed reconnect from web
if (reconnectPending &&
(long)(millis() - reconnectAt) >= 0) {
reconnectPending = false;
connectWiFi();
}
// WiFi LED indicator
updateLED();
// Auto reconnect
if (WiFi.status() != WL_CONNECTED &&
millis() - lastRetry >= RETRY_MS) {
connectWiFi();
}
// OLED update
if (millis() - lastOLED >= OLED_MS) {
lastOLED = millis();
updateOLED();
}
}