6 Commits

9 changed files with 324 additions and 68 deletions
+6 -4
View File
@@ -12,9 +12,11 @@
platform = espressif32
board = esp32dev
framework = arduino
;lib_deps =
;; esp32async/ESPAsyncWebServer@^3.12.0
lib_deps =
WebSockets
;board_build.f_cpu = 160000000L
;build_type = debug
; upload via OTA
upload_protocol = espota
upload_port = 192.168.4.1
;upload_protocol = espota
;upload_port = 192.168.4.1
+1 -1
View File
@@ -1,3 +1,3 @@
// Contains configuration constants:
#define FIRMWARE_VERSION "1.0.2"
#define FIRMWARE_VERSION "1.0.6"
+37
View File
@@ -0,0 +1,37 @@
#include "diagnostics_state.h"
void DiagnosticsState::update()
{
current.timestamp = millis();
current.freeHeap =
ESP.getFreeHeap();
current.minimumFreeHeap =
ESP.getMinFreeHeap();
current.cpuFrequency =
ESP.getCpuFreqMHz();
Serial.print("#update: ");
Serial.println(current.freeHeap);
Serial.println("Calling current from update");
getCurrent();
}
DiagnosticSample DiagnosticsState::getCurrent()
{
DiagnosticSample sample;
sample.timestamp = current.timestamp;
sample.freeHeap = current.freeHeap;
sample.minimumFreeHeap = current.minimumFreeHeap;
sample.cpuFrequency = current.cpuFrequency;
Serial.print("#getCurrent: ");
Serial.println(sample.freeHeap);
Serial.println(sample.timestamp);
return sample;
}
+34
View File
@@ -0,0 +1,34 @@
#pragma once
#include <Arduino.h>
#define DIAGNOSTIC_HISTORY_SIZE 60
struct DiagnosticSample
{
uint32_t timestamp;
uint32_t freeHeap;
uint32_t minimumFreeHeap;
uint32_t cpuFrequency;
};
class DiagnosticsState
{
public:
void update();
DiagnosticSample getCurrent();
//DiagnosticSample getHistory(uint8_t index);
private:
volatile DiagnosticSample current;
//DiagnosticSample history[DIAGNOSTIC_HISTORY_SIZE];
//uint8_t historyIndex = 0;
};
+8 -4
View File
@@ -4,6 +4,7 @@
#include <Arduino.h>
#include "core/dashboard_state.h"
#include "core/diagnostics_state.h"
#include "services/service_manager.h"
#include "services/wifi_service.h"
@@ -11,22 +12,24 @@
#include "services/web_service.h"
#include "tasks/system_task.h"
#include "tasks/diagnostics_task.h"
DashboardState dashboardState;
DiagnosticsState diagnosticsState;
// Simple service scheduler nice for grouping tasks
ServiceManager services;
WiFiService wifi;
OTAService ota;
WebService web(dashboardState);
WebService web(dashboardState, diagnosticsState);
// Actual FreeRTOS tasks that are scheduled
SystemTask systemTask(services);
DiagnosticsTask diagnosticsTask(diagnosticsState);
void setup()
{
@@ -38,6 +41,7 @@ void setup()
systemTask.start();
diagnosticsTask.start();
}
+143 -55
View File
@@ -1,10 +1,12 @@
#include "web_service.h"
WebService::WebService(DashboardState& state)
WebService::WebService(DashboardState& state, DiagnosticsState& diag_state)
:
Service("Web", 10),
dashboardState(state),
server(80)
diagnosticsState(diag_state),
server(80),
webSocket(81)
{
}
@@ -65,31 +67,38 @@ h1 {
</style>
<script>
function updateUptime() {
fetch('/uptime')
.then(response => response.text())
.then(data => {
document.getElementById("uptime").innerHTML = data;
});
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);
};
}
setInterval(updateUptime, 1000);
window.onload = updateUptime;
window.onload = connectWebSocket;
</script>
<script>
function getVersion() {
fetch('/version')
.then(response => response.text())
.then(data => {
document.getElementById("firmware_version").innerHTML = data;
});
}
window.onload = getVersion;
</script>
</head>
<body>
@@ -98,25 +107,69 @@ window.onload = getVersion;
<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>
</body>
</html>
)rawliteral";
// server.send(200, "text/html", html);
// }
//
// void handleUptime() {
// server.send(200, "text/plain", formatUptime());
// }
//
// void handleVersion() {
// server.send(200, "text/plain", FIRMWARE_VERSION);
// }
//
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
@@ -128,34 +181,69 @@ void WebService::begin() {
);
});
// Version path
server.on("/version", [this](){
server.send(
200,
"text/plain",
dashboardState.firmwareVersion
);
#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");
});
// Uptime path
server.on("/uptime", [this]() {
server.send(
200,
"text/plain",
dashboardState.uptime
);
});
#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();
}
}
+13 -4
View File
@@ -1,15 +1,18 @@
#pragma once
#include "service.h"
#include "../core/dashboard_state.h"
#include <WebServer.h>
#include <WebSocketsServer.h>
#include "service.h"
#include "../core/dashboard_state.h"
#include "../core/diagnostics_state.h"
class WebService : public Service {
public:
WebService(DashboardState& state);
WebService(DashboardState& state, DiagnosticsState& diag_state);
void begin() override;
void update() override;
@@ -17,5 +20,11 @@ public:
private:
WebServer server;
DashboardState dashboardState;
WebSocketsServer webSocket;
DashboardState& dashboardState;
DiagnosticsState& diagnosticsState;
void handleWebSocketMessage(uint8_t clientNum, uint8_t *payload, size_t length);
void broadcastState();
};
+54
View File
@@ -0,0 +1,54 @@
#include "diagnostics_task.h"
DiagnosticsTask::DiagnosticsTask(
DiagnosticsState& state
)
:
diagnostics(state)
{
}
void DiagnosticsTask::start()
{
xTaskCreatePinnedToCore(
taskEntry,
"DiagnosticsTask",
4096,
this,
1,
&taskHandle,
1
);
}
void DiagnosticsTask::taskEntry(void* parameter)
{
DiagnosticsTask* task =
static_cast<DiagnosticsTask*>(parameter);
task->run();
}
void DiagnosticsTask::run()
{
TickType_t lastWake =
xTaskGetTickCount();
while(true)
{
diagnostics.update();
vTaskDelayUntil(
&lastWake,
pdMS_TO_TICKS(1000)
);
}
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <Arduino.h>
#include "../core/diagnostics_state.h"
class DiagnosticsTask
{
public:
DiagnosticsTask(
DiagnosticsState& state
);
void start();
private:
static void taskEntry(void* parameter);
void run();
DiagnosticsState& diagnostics;
TaskHandle_t taskHandle = nullptr;
};