688 lines
18 KiB
C++
688 lines
18 KiB
C++
// web_server.cpp - Web Server Implementation with WebServer and WebSocketsServer
|
|
|
|
#include <Arduino.h>
|
|
#include <WiFi.h>
|
|
#include <WebServer.h>
|
|
#include <WebSocketsServer.h>
|
|
#include <ESPmDNS.h>
|
|
#include <SD_MMC.h>
|
|
#include <ArduinoJson.h>
|
|
|
|
#include "web_server.h"
|
|
#include "task_config.h"
|
|
#include "can_handler.h"
|
|
#include "sd_logger.h"
|
|
#include "rtc_manager.h"
|
|
#include "signal_manager.h"
|
|
#include "dbc_parser.h"
|
|
#include "auto_trigger.h"
|
|
#include "psram_buffer.h"
|
|
#include "test_handler.h"
|
|
|
|
#include "data/web_index.h"
|
|
#include "data/web_settings.h"
|
|
#include "data/web_files.h"
|
|
#include "data/web_can.h"
|
|
#include "data/web_graph.h"
|
|
#include "data/web_test.h"
|
|
|
|
WebServer server(WEB_SERVER_PORT);
|
|
WebSocketsServer webSocket(81);
|
|
|
|
bool wifiInitialized = false;
|
|
bool apModeActive = false;
|
|
bool staModeActive = false;
|
|
|
|
WiFiConfig wifiConfig;
|
|
|
|
static char wsBuffer[2048];
|
|
|
|
bool initWiFi() {
|
|
Serial.println("Initializing WiFi...");
|
|
|
|
loadWiFiConfig();
|
|
|
|
if (!startAPMode()) {
|
|
Serial.println("Failed to start AP mode!");
|
|
return false;
|
|
}
|
|
|
|
if (wifiConfig.useSTA && strlen(wifiConfig.staSSID) > 0) {
|
|
startSTAMode(wifiConfig.staSSID, wifiConfig.staPassword);
|
|
}
|
|
|
|
wifiInitialized = true;
|
|
return true;
|
|
}
|
|
|
|
bool startAPMode() {
|
|
Serial.println("Starting WiFi AP mode...");
|
|
|
|
WiFi.mode(WIFI_AP_STA);
|
|
|
|
bool result = WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASSWORD, WIFI_AP_CHANNEL, 0, WIFI_AP_MAX_CLIENTS);
|
|
|
|
if (result) {
|
|
apModeActive = true;
|
|
Serial.printf("AP Started: %s\n", WIFI_AP_SSID);
|
|
Serial.printf("AP IP: %s\n", WiFi.softAPIP().toString().c_str());
|
|
initMDNS();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool startSTAMode(const char* ssid, const char* password) {
|
|
Serial.printf("Connecting to WiFi: %s\n", ssid);
|
|
|
|
WiFi.begin(ssid, password);
|
|
|
|
int attempts = 0;
|
|
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
|
|
delay(500);
|
|
Serial.print(".");
|
|
attempts++;
|
|
}
|
|
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
staModeActive = true;
|
|
Serial.println("\nWiFi Connected!");
|
|
Serial.printf("STA IP: %s\n", WiFi.localIP().toString().c_str());
|
|
return true;
|
|
} else {
|
|
Serial.println("\nWiFi connection failed!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void stopWiFi() {
|
|
WiFi.disconnect(true);
|
|
WiFi.mode(WIFI_OFF);
|
|
apModeActive = false;
|
|
staModeActive = false;
|
|
}
|
|
|
|
bool initMDNS() {
|
|
if (!MDNS.begin("esp32-can")) {
|
|
Serial.println("mDNS failed to start!");
|
|
return false;
|
|
}
|
|
|
|
MDNS.addService("http", "tcp", WEB_SERVER_PORT);
|
|
MDNS.addService("ws", "tcp", 81);
|
|
Serial.println("mDNS started: esp32-can.local");
|
|
return true;
|
|
}
|
|
|
|
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
|
|
switch(type) {
|
|
case WStype_DISCONNECTED:
|
|
Serial.printf("[%u] Disconnected!\n", num);
|
|
break;
|
|
case WStype_CONNECTED:
|
|
Serial.printf("[%u] Connected from %s\n", num, webSocket.remoteIP(num).toString().c_str());
|
|
break;
|
|
case WStype_TEXT:
|
|
Serial.printf("[%u] Message: %s\n", num, payload);
|
|
break;
|
|
case WStype_BIN:
|
|
case WStype_ERROR:
|
|
case WStype_FRAGMENT_TEXT_START:
|
|
case WStype_FRAGMENT_BIN_START:
|
|
case WStype_FRAGMENT:
|
|
case WStype_FRAGMENT_FIN:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void broadcastToClients(const char* message) {
|
|
webSocket.broadcastTXT(message);
|
|
}
|
|
|
|
void broadcastSignalData(const GraphSignal* signals, uint8_t count) {
|
|
StaticJsonDocument<2048> doc;
|
|
doc["type"] = "signal";
|
|
doc["timestamp"] = millis();
|
|
|
|
JsonArray sigArray = doc.createNestedArray("signals");
|
|
for (uint8_t i = 0; i < count; i++) {
|
|
JsonObject sig = sigArray.createNestedObject();
|
|
sig["id"] = signals[i].signal_id;
|
|
sig["value"] = signals[i].value;
|
|
}
|
|
|
|
serializeJson(doc, wsBuffer, sizeof(wsBuffer));
|
|
webSocket.broadcastTXT(wsBuffer);
|
|
}
|
|
|
|
void handleRoot() {
|
|
server.send(200, "text/html", HTML_INDEX);
|
|
}
|
|
|
|
void handleSettings() {
|
|
server.send(200, "text/html", HTML_SETTINGS);
|
|
}
|
|
|
|
void handleFiles() {
|
|
server.send(200, "text/html", HTML_FILES);
|
|
}
|
|
|
|
void handleCAN() {
|
|
server.send(200, "text/html", HTML_CAN);
|
|
}
|
|
|
|
void handleGraph() {
|
|
server.send(200, "text/html", HTML_GRAPH);
|
|
}
|
|
|
|
void handleTest() {
|
|
server.send(200, "text/html", HTML_TEST);
|
|
}
|
|
|
|
void handleAPIStatus() {
|
|
StaticJsonDocument<1024> doc;
|
|
|
|
doc["wifi"] = wifiInitialized;
|
|
doc["ap"] = apModeActive;
|
|
doc["sta"] = staModeActive;
|
|
doc["ap_ip"] = WiFi.softAPIP().toString();
|
|
|
|
if (staModeActive) {
|
|
doc["sta_ip"] = WiFi.localIP().toString();
|
|
}
|
|
|
|
doc["can"]["initialized"] = canInitialized;
|
|
uint32_t rx, tx, err;
|
|
getCANStats(rx, tx, err);
|
|
doc["can"]["rx_count"] = rx;
|
|
doc["can"]["tx_count"] = tx;
|
|
doc["can"]["error_count"] = err;
|
|
doc["can"]["mode"] = getCANMode();
|
|
doc["can"]["buffer_used"] = canFrameBuffer.available();
|
|
doc["can"]["buffer_capacity"] = canFrameBuffer.capacity();
|
|
|
|
doc["sd"]["initialized"] = sdInitialized;
|
|
doc["sd"]["total_mb"] = getSDCardSize() / (1024 * 1024);
|
|
doc["sd"]["free_mb"] = getFreeSpace() / (1024 * 1024);
|
|
|
|
doc["rtc"]["initialized"] = rtcInitialized;
|
|
|
|
doc["log"]["filename"] = getCurrentLogFilename();
|
|
|
|
doc["memory"]["heap_free"] = ESP.getFreeHeap();
|
|
doc["memory"]["heap_total"] = ESP.getHeapSize();
|
|
if (psramFound()) {
|
|
doc["memory"]["psram_free"] = ESP.getFreePsram();
|
|
doc["memory"]["psram_total"] = ESP.getPsramSize();
|
|
doc["memory"]["psram_used_mb"] = (ESP.getPsramSize() - ESP.getFreePsram()) / (1024 * 1024);
|
|
}
|
|
|
|
String output;
|
|
serializeJson(doc, output);
|
|
server.send(200, "application/json", output);
|
|
}
|
|
|
|
void handleAPIMemory() {
|
|
StaticJsonDocument<512> doc;
|
|
|
|
doc["heap"]["free"] = ESP.getFreeHeap();
|
|
doc["heap"]["total"] = ESP.getHeapSize();
|
|
doc["heap"]["used"] = ESP.getHeapSize() - ESP.getFreeHeap();
|
|
|
|
if (psramFound()) {
|
|
doc["psram"]["found"] = true;
|
|
doc["psram"]["free"] = ESP.getFreePsram();
|
|
doc["psram"]["total"] = ESP.getPsramSize();
|
|
doc["psram"]["used"] = ESP.getPsramSize() - ESP.getFreePsram();
|
|
doc["psram"]["free_mb"] = ESP.getFreePsram() / (1024 * 1024);
|
|
doc["psram"]["total_mb"] = ESP.getPsramSize() / (1024 * 1024);
|
|
} else {
|
|
doc["psram"]["found"] = false;
|
|
}
|
|
|
|
doc["can_buffer"]["used"] = canFrameBuffer.available();
|
|
doc["can_buffer"]["capacity"] = canFrameBuffer.capacity();
|
|
doc["can_buffer"]["free"] = canFrameBuffer.freeSpace();
|
|
|
|
String output;
|
|
serializeJson(doc, output);
|
|
server.send(200, "application/json", output);
|
|
}
|
|
|
|
void handleAPIFileList() {
|
|
StaticJsonDocument<4096> doc;
|
|
JsonArray files = doc.to<JsonArray>();
|
|
|
|
if (sdInitialized) {
|
|
File root = SD_MMC.open(LOGS_DIR);
|
|
if (root && root.isDirectory()) {
|
|
File file = root.openNextFile();
|
|
while (file) {
|
|
if (!file.isDirectory() && String(file.name()).endsWith(".pcap")) {
|
|
JsonObject f = files.createNestedObject();
|
|
f["name"] = file.name();
|
|
f["size"] = file.size();
|
|
f["time"] = file.getLastWrite();
|
|
}
|
|
file = root.openNextFile();
|
|
}
|
|
}
|
|
}
|
|
|
|
String output;
|
|
serializeJson(doc, output);
|
|
server.send(200, "application/json", output);
|
|
}
|
|
|
|
void handleAPIFileDownload() {
|
|
if (!server.hasArg("name")) {
|
|
server.send(400, "application/json", "{\"error\":\"Missing name parameter\"}");
|
|
return;
|
|
}
|
|
|
|
String filename = server.arg("name");
|
|
String path = String(LOGS_DIR) + "/" + filename;
|
|
|
|
if (!SD_MMC.exists(path)) {
|
|
server.send(404, "application/json", "{\"error\":\"File not found\"}");
|
|
return;
|
|
}
|
|
|
|
File file = SD_MMC.open(path, "r");
|
|
if (!file) {
|
|
server.send(500, "application/json", "{\"error\":\"Cannot open file\"}");
|
|
return;
|
|
}
|
|
|
|
server.streamFile(file, "application/octet-stream");
|
|
file.close();
|
|
}
|
|
|
|
void handleAPIFileDelete() {
|
|
if (!server.hasArg("name")) {
|
|
server.send(400, "application/json", "{\"error\":\"Missing name parameter\"}");
|
|
return;
|
|
}
|
|
|
|
String filename = server.arg("name");
|
|
|
|
if (deleteLogFile(filename.c_str())) {
|
|
server.send(200, "application/json", "{\"status\":\"deleted\"}");
|
|
} else {
|
|
server.send(500, "application/json", "{\"error\":\"Delete failed\"}");
|
|
}
|
|
}
|
|
|
|
void handleAPICANSend() {
|
|
if (!server.hasArg("plain")) {
|
|
server.send(400, "application/json", "{\"error\":\"Missing body\"}");
|
|
return;
|
|
}
|
|
|
|
String body = server.arg("plain");
|
|
StaticJsonDocument<512> doc;
|
|
DeserializationError error = deserializeJson(doc, body);
|
|
|
|
if (error) {
|
|
server.send(400, "application/json", "{\"error\":\"Invalid JSON\"}");
|
|
return;
|
|
}
|
|
|
|
String idStr = doc["id"] | "0x100";
|
|
uint32_t id = 0;
|
|
if (idStr.startsWith("0x") || idStr.startsWith("0X")) {
|
|
id = strtol(idStr.c_str(), NULL, 16);
|
|
} else {
|
|
id = idStr.toInt();
|
|
}
|
|
|
|
String frameType = doc["type"] | "standard";
|
|
bool ext = (frameType == "extended");
|
|
bool fd = (frameType == "fd") || (doc["isFD"] | false);
|
|
|
|
JsonArray dataArr = doc["data"];
|
|
uint8_t data[64] = {0};
|
|
uint8_t len = doc["length"] | 8;
|
|
|
|
int i = 0;
|
|
for (JsonVariant v : dataArr) {
|
|
if (i < 64) {
|
|
data[i++] = v.as<uint8_t>();
|
|
}
|
|
}
|
|
|
|
if (ext) id |= 0x80000000;
|
|
|
|
if (sendCANFrame(id, data, len, fd)) {
|
|
server.send(200, "application/json", "{\"status\":\"sent\"}");
|
|
} else {
|
|
server.send(500, "application/json", "{\"error\":\"Send failed\"}");
|
|
}
|
|
}
|
|
|
|
void handleAPIWiFiConfig() {
|
|
if (!server.hasArg("plain")) {
|
|
server.send(400, "application/json", "{\"error\":\"Missing body\"}");
|
|
return;
|
|
}
|
|
|
|
String body = server.arg("plain");
|
|
StaticJsonDocument<256> doc;
|
|
DeserializationError error = deserializeJson(doc, body);
|
|
|
|
if (error) {
|
|
server.send(400, "application/json", "{\"error\":\"Invalid JSON\"}");
|
|
return;
|
|
}
|
|
|
|
if (doc.containsKey("ssid") && doc.containsKey("password")) {
|
|
strlcpy(wifiConfig.staSSID, doc["ssid"], sizeof(wifiConfig.staSSID));
|
|
strlcpy(wifiConfig.staPassword, doc["password"], sizeof(wifiConfig.staPassword));
|
|
wifiConfig.useSTA = true;
|
|
saveWiFiConfig();
|
|
|
|
server.send(200, "application/json", "{\"status\":\"saved\",\"reconnect\":true}");
|
|
} else {
|
|
server.send(400, "application/json", "{\"error\":\"Missing ssid or password\"}");
|
|
}
|
|
}
|
|
|
|
void handleAPILoggingStart() {
|
|
if (startLogFile()) {
|
|
server.send(200, "application/json", "{\"status\":\"started\"}");
|
|
} else {
|
|
server.send(500, "application/json", "{\"error\":\"Start failed\"}");
|
|
}
|
|
}
|
|
|
|
void handleAPILoggingStop() {
|
|
closeLogFile();
|
|
server.send(200, "application/json", "{\"status\":\"stopped\"}");
|
|
}
|
|
|
|
void handleAPIDBCUpload() {
|
|
if (!server.hasArg("plain")) {
|
|
server.send(400, "application/json", "{\"error\":\"Missing body\"}");
|
|
return;
|
|
}
|
|
|
|
String content = server.arg("plain");
|
|
|
|
if (parseDBC(content.c_str())) {
|
|
server.send(200, "application/json", "{\"status\":\"loaded\"}");
|
|
} else {
|
|
server.send(400, "application/json", "{\"error\":\"Parse failed\"}");
|
|
}
|
|
}
|
|
|
|
void handleAPITimeSync() {
|
|
if (!server.hasArg("plain")) {
|
|
server.send(400, "application/json", "{\"error\":\"Missing body\"}");
|
|
return;
|
|
}
|
|
|
|
String body = server.arg("plain");
|
|
StaticJsonDocument<128> doc;
|
|
DeserializationError error = deserializeJson(doc, body);
|
|
|
|
if (error) {
|
|
server.send(400, "application/json", "{\"error\":\"Invalid JSON\"}");
|
|
return;
|
|
}
|
|
|
|
if (doc.containsKey("timestamp")) {
|
|
uint32_t timestamp = doc["timestamp"];
|
|
setRTCTime(timestamp);
|
|
server.send(200, "application/json", "{\"status\":\"synced\"}");
|
|
} else {
|
|
server.send(400, "application/json", "{\"error\":\"Missing timestamp\"}");
|
|
}
|
|
}
|
|
|
|
void handleAPIRestart() {
|
|
server.send(200, "application/json", "{\"status\":\"restarting\"}");
|
|
delay(100);
|
|
ESP.restart();
|
|
}
|
|
|
|
void handleAPICANConfig() {
|
|
if (!server.hasArg("plain")) {
|
|
server.send(400, "application/json", "{\"error\":\"Missing body\"}");
|
|
return;
|
|
}
|
|
|
|
String body = server.arg("plain");
|
|
StaticJsonDocument<256> doc;
|
|
DeserializationError error = deserializeJson(doc, body);
|
|
|
|
if (error) {
|
|
server.send(400, "application/json", "{\"error\":\"Invalid JSON\"}");
|
|
return;
|
|
}
|
|
|
|
uint32_t arbBaud = doc["arbBaud"] | CAN_DEFAULT_ARBITRATION_BAUDRATE;
|
|
uint32_t dataBaud = doc["dataBaud"] | CAN_DEFAULT_DATA_BAUDRATE;
|
|
uint8_t mode = doc["mode"] | 0;
|
|
bool enableFD = doc["enableFD"] | true;
|
|
|
|
if (setCANBaudrateAndMode(arbBaud, dataBaud, mode, enableFD)) {
|
|
server.send(200, "application/json", "{\"status\":\"configured\"}");
|
|
} else {
|
|
server.send(500, "application/json", "{\"error\":\"Configuration failed\"}");
|
|
}
|
|
}
|
|
|
|
void handleAPITriggerConfig() {
|
|
if (!server.hasArg("plain")) {
|
|
server.send(200, "application/json", "{\"error\":\"Missing body\"}");
|
|
return;
|
|
}
|
|
|
|
String body = server.arg("plain");
|
|
StaticJsonDocument<512> doc;
|
|
deserializeJson(doc, body);
|
|
|
|
if (doc.containsKey("enabled")) {
|
|
enableTrigger(doc["enabled"]);
|
|
}
|
|
if (doc.containsKey("logic")) {
|
|
setLogicalOperator(doc["logic"] == "AND" ? LOGIC_AND : LOGIC_OR);
|
|
}
|
|
|
|
char buffer[256];
|
|
getTriggerStatusJSON(buffer, sizeof(buffer));
|
|
server.send(200, "application/json", buffer);
|
|
}
|
|
|
|
void handleAPISignalAdd() {
|
|
if (!server.hasArg("plain")) {
|
|
server.send(400, "application/json", "{\"error\":\"Missing body\"}");
|
|
return;
|
|
}
|
|
|
|
String body = server.arg("plain");
|
|
StaticJsonDocument<256> doc;
|
|
deserializeJson(doc, body);
|
|
|
|
const char* name = doc["name"];
|
|
uint32_t canId = doc["canId"];
|
|
uint32_t startBit = doc["startBit"];
|
|
uint32_t length = doc["length"];
|
|
bool littleEndian = doc["littleEndian"] | true;
|
|
bool isSigned = doc["signed"] | false;
|
|
float factor = doc["factor"] | 1.0;
|
|
float offset = doc["offset"] | 0.0;
|
|
|
|
if (addManualSignal(name, canId, startBit, length, littleEndian, isSigned, factor, offset)) {
|
|
server.send(200, "application/json", "{\"status\":\"added\"}");
|
|
} else {
|
|
server.send(500, "application/json", "{\"error\":\"Failed to add signal\"}");
|
|
}
|
|
}
|
|
|
|
void handleAPISignalList() {
|
|
char buffer[2048];
|
|
getSignalsJSON(buffer, sizeof(buffer));
|
|
server.send(200, "application/json", buffer);
|
|
}
|
|
|
|
void handleAPITestStart() {
|
|
if (isTestRunning()) {
|
|
server.send(400, "application/json", "{\"error\":\"Test already running\"}");
|
|
return;
|
|
}
|
|
|
|
if (!server.hasArg("plain")) {
|
|
server.send(400, "application/json", "{\"error\":\"Missing body\"}");
|
|
return;
|
|
}
|
|
|
|
String body = server.arg("plain");
|
|
StaticJsonDocument<256> doc;
|
|
deserializeJson(doc, body);
|
|
|
|
String testType = doc["type"] | "loopback";
|
|
uint32_t frameCount = doc["frames"] | 1000;
|
|
uint32_t interval = doc["interval"] | 1000;
|
|
uint8_t dataLen = doc["dataLen"] | 64;
|
|
bool useFD = doc["useFD"] | true;
|
|
uint32_t canId = doc["canId"] | 0x100;
|
|
|
|
bool started = false;
|
|
|
|
if (testType == "loopback") {
|
|
started = startLoopbackTest(frameCount, interval);
|
|
} else if (testType == "stress") {
|
|
started = startStressTest(frameCount, dataLen, useFD);
|
|
} else if (testType == "sequence") {
|
|
started = startSequenceTest(frameCount, canId);
|
|
}
|
|
|
|
if (started) {
|
|
server.send(200, "application/json", "{\"status\":\"started\"}");
|
|
} else {
|
|
server.send(500, "application/json", "{\"error\":\"Failed to start test\"}");
|
|
}
|
|
}
|
|
|
|
void handleAPITestStop() {
|
|
stopTest();
|
|
server.send(200, "application/json", "{\"status\":\"stopped\"}");
|
|
}
|
|
|
|
void handleAPITestStatus() {
|
|
char buffer[512];
|
|
getTestResultJSON(buffer, sizeof(buffer));
|
|
server.send(200, "application/json", buffer);
|
|
}
|
|
|
|
void handleNotFound() {
|
|
server.send(404, "text/plain", "Not Found");
|
|
}
|
|
|
|
bool initWebServer() {
|
|
Serial.println("Initializing Web Server...");
|
|
|
|
server.on("/", handleRoot);
|
|
server.on("/settings", handleSettings);
|
|
server.on("/files", handleFiles);
|
|
server.on("/can", handleCAN);
|
|
server.on("/graph", handleGraph);
|
|
server.on("/test", handleTest);
|
|
|
|
server.on("/api/status", handleAPIStatus);
|
|
server.on("/api/memory", handleAPIMemory);
|
|
server.on("/api/files", handleAPIFileList);
|
|
server.on("/api/files/download", handleAPIFileDownload);
|
|
server.on("/api/files/delete", handleAPIFileDelete);
|
|
server.on("/api/can/send", HTTP_POST, handleAPICANSend);
|
|
server.on("/api/wifi", HTTP_POST, handleAPIWiFiConfig);
|
|
server.on("/api/logging/start", handleAPILoggingStart);
|
|
server.on("/api/logging/stop", handleAPILoggingStop);
|
|
server.on("/api/dbc/upload", HTTP_POST, handleAPIDBCUpload);
|
|
|
|
server.on("/api/time", HTTP_POST, handleAPITimeSync);
|
|
server.on("/api/restart", HTTP_POST, handleAPIRestart);
|
|
server.on("/api/can/config", HTTP_POST, handleAPICANConfig);
|
|
server.on("/api/trigger", handleAPITriggerConfig);
|
|
server.on("/api/signal/add", HTTP_POST, handleAPISignalAdd);
|
|
server.on("/api/signal/list", handleAPISignalList);
|
|
|
|
server.on("/api/test/start", HTTP_POST, handleAPITestStart);
|
|
server.on("/api/test/stop", handleAPITestStop);
|
|
server.on("/api/test/status", handleAPITestStatus);
|
|
|
|
server.onNotFound(handleNotFound);
|
|
|
|
server.begin();
|
|
|
|
webSocket.begin();
|
|
webSocket.onEvent(webSocketEvent);
|
|
|
|
Serial.println("Web Server started on port 80");
|
|
Serial.println("WebSocket started on port 81");
|
|
return true;
|
|
}
|
|
|
|
void webServerTask(void *pvParameters) {
|
|
Serial.println("Web Server Task started on Core 1");
|
|
|
|
if (!initWiFi()) {
|
|
Serial.println("WiFi initialization failed!");
|
|
}
|
|
|
|
if (!initWebServer()) {
|
|
Serial.println("Web Server initialization failed!");
|
|
}
|
|
|
|
while (1) {
|
|
server.handleClient();
|
|
webSocket.loop();
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
}
|
|
}
|
|
|
|
void wsTxTask(void *pvParameters) {
|
|
Serial.println("WebSocket TX Task started on Core 1");
|
|
|
|
CanFrame frame;
|
|
SignalValue signals[10];
|
|
uint16_t signalCount = 0;
|
|
uint32_t lastUpdate = 0;
|
|
|
|
while (1) {
|
|
if (xQueueReceive(graphQueue, &frame, pdMS_TO_TICKS(50)) == pdTRUE) {
|
|
updateAllSignals(&frame);
|
|
}
|
|
|
|
uint32_t now = millis();
|
|
if (now - lastUpdate >= 100) {
|
|
signalCount = getEnabledSignals(signals, 10);
|
|
if (signalCount > 0) {
|
|
GraphSignal graphSignals[10];
|
|
for (uint16_t i = 0; i < signalCount; i++) {
|
|
strncpy(graphSignals[i].signal_id, signals[i].name, 32);
|
|
graphSignals[i].value = signals[i].value;
|
|
graphSignals[i].timestamp = signals[i].timestamp;
|
|
}
|
|
broadcastSignalData(graphSignals, signalCount);
|
|
}
|
|
lastUpdate = now;
|
|
}
|
|
|
|
updateTrigger();
|
|
}
|
|
}
|
|
|
|
bool loadWiFiConfig() {
|
|
wifiConfig.useSTA = false;
|
|
wifiConfig.staSSID[0] = '\0';
|
|
wifiConfig.staPassword[0] = '\0';
|
|
return true;
|
|
}
|
|
|
|
bool saveWiFiConfig() {
|
|
return true;
|
|
}
|