Auteur
CETECH | Dernière modification 20/12/2025 par CETECH
ESP32 Real_Time_System_Telemetry_Dashboard_with_FireBeetle_ESP32P4_a2a.JPG Technique
Display / UI
Software on PC
pip install pyserial psutil)Arduino IDE
Network
You must check out PCBWAY for ordering PCBs online for cheap!
You get 10 good-quality PCBs manufactured and shipped to your doorstep for cheap. You will also get a discount on shipping on your first order. Upload your Gerber files onto PCBWAY to get them manufactured with good quality and quick turnaround time. PCBWay now could provide a complete product solution, from design to enclosure production. Check out their online Gerber viewer function. With reward points, you can get free stuff from their gift shop. Also, check out this useful blog on PCBWay Plugin for KiCad from here. Using this plugin, you can directly order PCBs in just one click after completing your design in KiCad.
LED
Notes
What it does
http://<ESP32-IP>/./data JSON endpoint used by the page’s AJAX to update UI every 5 seconds.Paste and upload this sketch (replace YOUR_SSID and YOUR_PASSWORD):
#include <WiFi.h>
#include <WebServer.h>
#include <Arduino_JSON.h>
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
WebServer server(80);
String host, os, boot, ramUsed, ramTotal, ip, mac;
double cpu = 0, ramPct = 0;
#define LED_PIN 3 // onboard LED pin
// Serve the main HTML page
void handleRoot() {
String html = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ESP32-P4 Telemetry Dashboard</title>
<style>
body{background:#121212;color:#eee;font-family:Arial;text-align:center;}
h1{color:#4cafef;}
.card{background:#1e1e1e;padding:20px;margin:20px;border-radius:10px;box-shadow:0 0 10px rgba(0,0,0,0.5);}
.bar{height:20px;background:#333;border-radius:10px;overflow:hidden;margin-top:10px;}
.fill{height:100%;background:#4caf50;text-align:right;color:#fff;padding-right:5px;}
.gauge{width:120px;height:60px;background:#333;border-radius:120px 120px 0 0;overflow:hidden;margin:20px auto;position:relative;}
.needle{width:4px;height:60px;background:#4cafef;position:absolute;bottom:0;left:58px;transform-origin:bottom center;}
</style>
<script>
async function fetchData(){
const res = await fetch('/data');
const data = await res.json();
document.getElementById('host').innerText = data.host;
document.getElementById('os').innerText = data.os;
document.getElementById('boot').innerText = data.boot_time;
document.getElementById('cpu').innerText = data.cpu_percent + '%';
document.getElementById('cpuFill').style.width = data.cpu_percent + '%';
document.getElementById('cpuNeedle').style.transform = 'rotate('+(data.cpu_percent/100*180)+'deg)';
document.getElementById('ram').innerText = data.ram.used + ' / ' + data.ram.total + ' ('+data.ram.percent+'%)';
document.getElementById('ramFill').style.width = data.ram.percent + '%';
document.getElementById('ramNeedle').style.transform = 'rotate('+(data.ram.percent/100*180)+'deg)';
document.getElementById('ip').innerText = data.network.ip;
document.getElementById('mac').innerText = data.network.mac;
// Show raw JSON in console widget
document.getElementById('console').innerText = JSON.stringify(data, null, 2);
}
setInterval(fetchData, 5000); // auto refresh every 5s
window.onload = fetchData;
</script>
</head>
<body>
<h1>ESP32-P4 Telemetry Dashboard</h1>
<div class="card">
<h2>System Info</h2>
<p><b>Host:</b> <span id="host"></span></p>
<p><b>OS:</b> <span id="os"></span></p>
<p><b>Boot Time:</b> <span id="boot"></span></p></div>
<div class="card">
<h2>CPU Usage</h2>
<div class="gauge"><div id="cpuNeedle" class="needle"></div></div>
<div class="bar"><div id="cpuFill" class="fill">0%</div></div>
<p id="cpu"></p>
</div>
<div class="card">
<h2>RAM Usage</h2>
<div class="gauge"><div id="ramNeedle" class="needle"></div></div>
<div class="bar"><div id="ramFill" class="fill">0%</div></div>
<p id="ram"></p>
</div>
<div class="card">
<h2>Network</h2>
<p><b>IP:</b> <span id="ip"></span></p>
<p><b>MAC:</b> <span id="mac"></span></p>
</div>
<div class="card" style="margin:20px;">
<h2>Raw data console</h2>
<pre id="console" style="background:#000;color:#0f0;padding:10px;border-radius:6px;height:140px;overflow:auto;">{}</pre>
</div>
</body>
</html>
)rawliteral";
server.send(200, "text/html", html);
}
// Serve JSON data for AJAX
void handleData() {
String json = "{";
json += "\"host\":\"" + host + "\",";
json += "\"os\":\"" + os + "\",";
json += "\"boot_time\":\"" + boot + "\",";
json += "\"cpu_percent\":" + String(cpu) + ",";
json += "\"ram\":{\"used\":\"" + ramUsed + "\",\"total\":\"" + ramTotal + "\",\"percent\":" + String(ramPct) + "},";
json += "\"network\":{\"ip\":\"" + ip + "\",\"mac\":\"" + mac + "\"}";
json += "}";
server.send(200, "application/json", json);
}
void setup() {
Serial.begin(9600);
pinMode(LED_PIN, OUTPUT);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected!");
Serial.print("ESP32 IP: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/data", handleData);
server.begin();
}
void loop() {
server.handleClient();
if (Serial.available()) {
String msg = Serial.readStringUntil('\n');
JSONVar data = JSON.parse(msg);
if (JSON.typeof(data) != "undefined") {
host = (const char*)data["host"];
os = (const char*)data["os"];
boot = (const char*)data["boot_time"];
cpu = double(data["cpu_percent"]);
ramUsed = (const char*)data["ram"]["used"];
ramTotal = (const char*)data["ram"]["total"];
ramPct = double(data["ram"]["percent"]);
ip = (const char*)data["network"]["ip"];
mac = (const char*)data["network"]["mac"];
// Blink LED when new data arrives
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
}
}
}
Notes
YOUR_SSID and YOUR_PASSWORD.
What it does
psutil.\n is required so the ESP32 can use readStringUntil('\n').pip install pyserial psutil
Python script (run on your PC, update PORT)import psutil
import platform
import socket
import time
import serial
import json
from datetime import datetime
PORT = "COM8" # Change to your ESP32 port
BAUD = 9600
def get_size(bytes, suffix="B"):
factor = 1024.0
for unit in ["", "K", "M", "G", "T"]:
if bytes < factor:
return f"{bytes:.2f}{unit}{suffix}"
bytes /= factor
def get_ip_mac():
ip, mac = "N/A", "N/A"
for iface, addrs in psutil.net_if_addrs().items():
for addr in addrs:
if addr.family == socket.AF_INET and not addr.address.startswith("127."):
ip = addr.address
if hasattr(addr.family, "name") and addr.family.name in ("AF_LINK", "AF_PACKET"):
mac = addr.address
return ip, mac
try:
ser = serial.Serial(PORT, BAUD, timeout=1)
time.sleep(2)
while True:
uname = platform.uname()
boot_time = datetime.fromtimestamp(psutil.boot_time()).strftime("%Y-%m-%d %H:%M:%S")
cpu = psutil.cpu_percent(interval=None)
ram = psutil.virtual_memory()
disk = psutil.disk_usage('/')
ip, mac = get_ip_mac()
data = {
"host": uname.node,
"os": f"{uname.system} {uname.release}",
"boot_time": boot_time,
"cpu_percent": cpu,
"ram": {
"used": get_size(ram.used),
"total": get_size(ram.total),
"percent": ram.percent
},
"disk": {
"used": get_size(disk.used),
"total": get_size(disk.total),
"percent": disk.percent
},
"network": {
"ip": ip,
"mac": mac
}
}
ser.write((json.dumps(data) + "\n").encode("utf-8"))
print("Sent:", json.dumps(data))
time.sleep(5)
except serial.SerialException as e:
print("Serial error:", e)
except KeyboardInterrupt:
print("Stopped by user.")
finally:
try:
ser.close()
except:
pass
Prepare hardware
Upload Arduino sketch
Start Python sender
PORT to the ESP32 COM port (Windows: COMx, Linux: /dev/ttyUSB0), then run the Python script.Open dashboard
http://<ESP32-IP>/ (use the IP printed in Serial Monitor)./data every 5 seconds and update the UI.Verify LED
GPUtil), disk per‑partition details.
en none 0 Published
Vous avez entré un nom de page invalide, avec un ou plusieurs caractères suivants :
< > @ ~ : * € £ ` + = / \ | [ ] { } ; ? #