250 lines
5.2 KiB
C++
250 lines
5.2 KiB
C++
#include "web_service.h"
|
|
|
|
WebService::WebService(DashboardState& state, DiagnosticsState& diag_state)
|
|
:
|
|
Service("Web", 10),
|
|
dashboardState(state),
|
|
diagnosticsState(diag_state),
|
|
server(80),
|
|
webSocket(81)
|
|
{
|
|
}
|
|
|
|
|
|
String formatUptime() {
|
|
unsigned long seconds = millis() / 1000;
|
|
|
|
unsigned long days = seconds / 86400;
|
|
seconds %= 86400;
|
|
|
|
unsigned long hours = seconds / 3600;
|
|
seconds %= 3600;
|
|
|
|
unsigned long minutes = seconds / 60;
|
|
seconds %= 60;
|
|
|
|
char buffer[64];
|
|
snprintf(buffer, sizeof(buffer),
|
|
"%lu days %02lu:%02lu:%02lu",
|
|
days, hours, minutes, seconds);
|
|
|
|
return String(buffer);
|
|
}
|
|
|
|
String ROOT_HTML = R"rawliteral(
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>ESP32 Dashboard</title>
|
|
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
text-align: center;
|
|
margin-top: 50px;
|
|
background: #111;
|
|
color: white;
|
|
}
|
|
|
|
.card {
|
|
background: #222;
|
|
padding: 25px;
|
|
margin: auto;
|
|
width: 80%;
|
|
max-width: 400px;
|
|
border-radius: 15px;
|
|
}
|
|
|
|
h1 {
|
|
color: #00ff99;
|
|
}
|
|
|
|
#uptime {
|
|
font-size: 28px;
|
|
margin-top: 20px;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
|
|
let socket;
|
|
|
|
function connectWebSocket() {
|
|
socket = new WebSocket(
|
|
"ws://" + window.location.hostname + ":81/"
|
|
);
|
|
|
|
socket.onopen = function() {
|
|
console.log("WebSocket connected");
|
|
};
|
|
|
|
socket.onmessage = function(event) {
|
|
const data = JSON.parse(event.data);
|
|
|
|
document.getElementById("uptime").innerHTML = data.system.uptime;
|
|
document.getElementById("firmware_version").innerHTML = data.system.version;
|
|
document.getElementById("free_heap").innerHTML = data.diagnostics.free_heap;
|
|
document.getElementById("minimum_free_heap").innerHTML = data.diagnostics.minimum_free_heap;
|
|
document.getElementById("cpu_frequency").innerHTML = data.diagnostics.cpu_frequency;
|
|
};
|
|
|
|
socket.onclose = function() {
|
|
console.log("WebSocket disconnected");
|
|
setTimeout(connectWebSocket, 2000);
|
|
};
|
|
}
|
|
|
|
window.onload = connectWebSocket;
|
|
|
|
</script>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
<div class="card">
|
|
<h1>ESP32 Dashboard OTA</h1>
|
|
<p>Device uptime:</p>
|
|
<div id="uptime">Loading...</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h1>Hub Diagnostics:</h1>
|
|
<p>Free heap:</p>
|
|
<div id=free_heap>Loading...</div>
|
|
<p>Minimum free heap:</p>
|
|
<div id=minimum_free_heap>Loading...</div>
|
|
<p>CPU Frequency:</p>
|
|
<div id=cpu_frequency>Loading...</div>
|
|
</div>
|
|
|
|
</body>
|
|
<footer>
|
|
Firmware Version: <div id="firmware_version">Loading...</div>
|
|
</footer>
|
|
</html>
|
|
)rawliteral";
|
|
|
|
void WebService::broadcastState()
|
|
{
|
|
DiagnosticSample diagnostics = diagnosticsState.getCurrent();
|
|
Serial.println(diagnostics.freeHeap);
|
|
String json = "{";
|
|
|
|
// Opening system tag:
|
|
json += "\"system\":{";
|
|
|
|
json += "\"uptime\":\"";
|
|
json += dashboardState.uptime;
|
|
json += "\",";
|
|
|
|
json += "\"version\":\"";
|
|
json += dashboardState.firmwareVersion;
|
|
json += "\"";
|
|
|
|
json += "},"; // Close system tag
|
|
|
|
// Opening diagnostic tag:
|
|
json += "\"diagnostics\":{";
|
|
|
|
json += "\"free_heap\":\"";
|
|
json += diagnostics.freeHeap;
|
|
//json += ESP.getFreeHeap();
|
|
json += "\",";
|
|
|
|
json += "\"minimum_free_heap\":\"";
|
|
json += diagnostics.minimumFreeHeap;
|
|
json += "\",";
|
|
|
|
json += "\"cpu_frequency\":\"";
|
|
json += diagnostics.cpuFrequency;
|
|
json += "\"";
|
|
|
|
json += "}"; // Clost diagnostic tag
|
|
|
|
|
|
// Final close bracket
|
|
json += "}";
|
|
|
|
|
|
//Serial.println(json);
|
|
webSocket.broadcastTXT(json);
|
|
}
|
|
|
|
void WebService::begin() {
|
|
// Root path
|
|
server.on("/", [this](){
|
|
server.send(
|
|
200,
|
|
"text/html",
|
|
ROOT_HTML
|
|
);
|
|
});
|
|
|
|
#ifdef DEBUGGING
|
|
// Prints out paths that are requested but not found.
|
|
server.onNotFound([this]() {
|
|
Serial.print("HTTP not found: ");
|
|
Serial.println(server.uri());
|
|
|
|
server.send(404, "text/plain", "Not found");
|
|
});
|
|
#endif
|
|
|
|
server.begin();
|
|
|
|
webSocket.begin();
|
|
|
|
// Manage web socket connections
|
|
webSocket.onEvent(
|
|
[this](uint8_t clientNum,
|
|
WStype_t type,
|
|
uint8_t *payload,
|
|
size_t length)
|
|
{
|
|
if (type == WStype_TEXT) {
|
|
handleWebSocketMessage(
|
|
clientNum,
|
|
payload,
|
|
length
|
|
);
|
|
}
|
|
|
|
if (type == WStype_CONNECTED) {
|
|
Serial.println("WebSocket client connected");
|
|
broadcastState();
|
|
}
|
|
}
|
|
);
|
|
|
|
Serial.println("Web server started");
|
|
}
|
|
|
|
void WebService::handleWebSocketMessage(uint8_t clientNum, uint8_t *payload, size_t length) {
|
|
Serial.println("Client number: " + clientNum);
|
|
Serial.println("Sent a message of length: " + length);
|
|
Serial.print("Saying: ");
|
|
for (int ii = 0; ii < length; ii ++) {
|
|
Serial.print(payload[ii]);
|
|
}
|
|
Serial.print("\n\n\n");
|
|
}
|
|
|
|
|
|
void WebService::update()
|
|
{
|
|
dashboardState.update();
|
|
|
|
server.handleClient();
|
|
webSocket.loop();
|
|
|
|
// Brodcast updates once per second
|
|
static unsigned long lastUpdate = 0;
|
|
|
|
if (millis() - lastUpdate >= 1000)
|
|
{
|
|
lastUpdate = millis();
|
|
broadcastState();
|
|
}
|
|
}
|