add event log to webserver

This commit is contained in:
Brett Christensen 2024-02-07 07:52:44 +11:00
parent f0f1fddb15
commit 3503b8586a
4 changed files with 126 additions and 77 deletions

View file

@ -3,14 +3,6 @@
#include "../../../USER_SETTINGS.h"
#include "../config.h"
typedef struct {
uint32_t timestamp; // Time in seconds since startup when the event occurred
uint8_t data; // Custom data passed when setting the event, for example cell number for under voltage
uint8_t occurences; // Number of occurrences since startup
uint8_t led_color; // Weirdly indented comment
} EVENTS_STRUCT_TYPE;
static EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
static unsigned long previous_millis = 0;
static uint32_t time_seconds = 0;
static uint8_t total_led_color = GREEN;
@ -62,75 +54,52 @@ static void update_led_color(EVENTS_ENUM_TYPE event) {
total_led_color = (total_led_color == RED) ? RED : entries[event].led_color;
}
static void set_event_message(EVENTS_ENUM_TYPE event) {
const char* get_event_message(EVENTS_ENUM_TYPE event) {
switch (event) {
case EVENT_CAN_FAILURE:
snprintf(event_message, sizeof(event_message),
"No CAN communication detected for 60s. Shutting down battery control.");
break;
return "No CAN communication detected for 60s. Shutting down battery control.";
case EVENT_CAN_WARNING:
snprintf(event_message, sizeof(event_message),
"ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!");
break;
return "ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!";
case EVENT_WATER_INGRESS:
snprintf(event_message, sizeof(event_message),
"Water leakage inside battery detected. Operation halted. Inspect battery!");
break;
return "Water leakage inside battery detected. Operation halted. Inspect battery!";
case EVENT_12V_LOW:
snprintf(event_message, sizeof(event_message),
"12V battery source below required voltage to safely close contactors. Inspect the supply/battery!");
break;
return "12V battery source below required voltage to safely close contactors. Inspect the supply/battery!";
case EVENT_SOC_PLAUSIBILITY_ERROR:
snprintf(event_message, sizeof(event_message), "ERROR: SOC% reported by battery not plausible. Restart battery!");
break;
return "ERROR: SOC% reported by battery not plausible. Restart battery!";
case EVENT_KWH_PLAUSIBILITY_ERROR:
snprintf(event_message, sizeof(event_message),
"Warning: kWh remaining reported by battery not plausible. Battery needs cycling.");
break;
return "Warning: kWh remaining reported by battery not plausible. Battery needs cycling.";
case EVENT_BATTERY_CHG_STOP_REQ:
snprintf(event_message, sizeof(event_message),
"ERROR: Battery raised caution indicator AND requested charge stop. Inspect battery status!");
break;
return "ERROR: Battery raised caution indicator AND requested charge stop. Inspect battery status!";
case EVENT_BATTERY_DISCHG_STOP_REQ:
snprintf(event_message, sizeof(event_message),
"ERROR: Battery raised caution indicator AND requested discharge stop. Inspect battery status!");
break;
return "ERROR: Battery raised caution indicator AND requested discharge stop. Inspect battery status!";
case EVENT_BATTERY_CHG_DISCHG_STOP_REQ:
snprintf(event_message, sizeof(event_message),
"ERROR: Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!");
break;
return "ERROR: Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!";
case EVENT_LOW_SOH:
snprintf(
event_message, sizeof(event_message),
"ERROR: State of health critically low. Battery internal resistance too high to continue. Recycle battery.");
break;
return "ERROR: State of health critically low. Battery internal resistance too high to continue. Recycle battery.";
case EVENT_HVIL_FAILURE:
snprintf(event_message, sizeof(event_message),
"ERROR: Battery interlock loop broken. Check that high voltage connectors are seated. Battery will be "
"disabled!");
break;
return "ERROR: Battery interlock loop broken. Check that high voltage connectors are seated. Battery will be disabled!";
case EVENT_INTERNAL_OPEN_FAULT:
snprintf(event_message, sizeof(event_message),
"ERROR: High voltage cable removed while battery running. Opening contactors!");
break;
return "ERROR: High voltage cable removed while battery running. Opening contactors!";
case EVENT_CELL_UNDER_VOLTAGE:
snprintf(event_message, sizeof(event_message),
"ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
break;
return "ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!";
case EVENT_CELL_OVER_VOLTAGE:
snprintf(event_message, sizeof(event_message),
"ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
break;
return "ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!";
case EVENT_CELL_DEVIATION_HIGH:
snprintf(event_message, sizeof(event_message), "ERROR: HIGH CELL DEVIATION!!! Inspect battery!");
break;
return "ERROR: HIGH CELL DEVIATION!!! Inspect battery!";
case EVENT_UNKNOWN_EVENT_SET:
snprintf(event_message, sizeof(event_message), "An unknown event was set! Review your code!");
break;
return "An unknown event was set! Review your code!";
case EVENT_DUMMY:
snprintf(event_message, sizeof(event_message), "The dummy event was set!"); // Don't change this event message!
break;
return "The dummy event was set!"; // Don't change this event message!
default:
break;
return "";
}
}
const char* get_event_enum_string(EVENTS_ENUM_TYPE event) {
return EVENTS_ENUM_TYPE_STRING[event];
}
static void set_event_message(EVENTS_ENUM_TYPE event) {
const char* message = get_event_message(event);
snprintf(event_message, sizeof(event_message), "%s", message);
}

View file

@ -7,29 +7,50 @@
#include <stdint.h>
#define EVENTS_ENUM_TYPE(XX) \
XX(EVENT_CAN_FAILURE) \
XX(EVENT_CAN_WARNING) \
XX(EVENT_WATER_INGRESS) \
XX(EVENT_12V_LOW) \
XX(EVENT_SOC_PLAUSIBILITY_ERROR) \
XX(EVENT_KWH_PLAUSIBILITY_ERROR) \
XX(EVENT_BATTERY_CHG_STOP_REQ) \
XX(EVENT_BATTERY_DISCHG_STOP_REQ) \
XX(EVENT_BATTERY_CHG_DISCHG_STOP_REQ) \
XX(EVENT_LOW_SOH) \
XX(EVENT_HVIL_FAILURE) \
XX(EVENT_INTERNAL_OPEN_FAULT) \
XX(EVENT_CELL_UNDER_VOLTAGE) \
XX(EVENT_CELL_OVER_VOLTAGE) \
XX(EVENT_CELL_DEVIATION_HIGH) \
XX(EVENT_UNKNOWN_EVENT_SET) \
XX(EVENT_DUMMY) \
XX(EVENT_NOF_EVENTS)
#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,
typedef enum {
EVENT_CAN_FAILURE = 0u,
EVENT_CAN_WARNING,
EVENT_WATER_INGRESS,
EVENT_12V_LOW,
EVENT_SOC_PLAUSIBILITY_ERROR,
EVENT_KWH_PLAUSIBILITY_ERROR,
EVENT_BATTERY_CHG_STOP_REQ,
EVENT_BATTERY_DISCHG_STOP_REQ,
EVENT_BATTERY_CHG_DISCHG_STOP_REQ,
EVENT_LOW_SOH,
EVENT_HVIL_FAILURE,
EVENT_INTERNAL_OPEN_FAULT,
EVENT_CELL_UNDER_VOLTAGE,
EVENT_CELL_OVER_VOLTAGE,
EVENT_CELL_DEVIATION_HIGH,
EVENT_UNKNOWN_EVENT_SET,
EVENT_DUMMY,
EVENT_NOF_EVENTS
EVENTS_ENUM_TYPE(GENERATE_ENUM)
} EVENTS_ENUM_TYPE;
static const char *EVENTS_ENUM_TYPE_STRING[] = {
EVENTS_ENUM_TYPE(GENERATE_STRING)
};
const char* get_event_enum_string(EVENTS_ENUM_TYPE event);
const char* get_event_message(EVENTS_ENUM_TYPE event);
void init_events(void);
void set_event(EVENTS_ENUM_TYPE event, uint8_t data);
void update_event_timestamps(void);
typedef struct {
uint32_t timestamp; // Time in seconds since startup when the event occurred
uint8_t data; // Custom data passed when setting the event, for example cell number for under voltage
uint8_t occurences; // Number of occurrences since startup
uint8_t led_color; // Weirdly indented comment
} EVENTS_STRUCT_TYPE;
static EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
#endif // __MYTIMER_H__

View file

@ -68,6 +68,10 @@ void init_webserver() {
request->send_P(200, "text/html", index_html, cellmonitor_processor);
});
server.on("events", HTTP_GET, [](AsyncWebServerRequest* request) {
request->send_P(200, "text/html", index_html, events_processor);
});
// Route for editing Wh
server.on("/updateBatterySize", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
@ -581,11 +585,14 @@ String processor(const String& var) {
content += " ";
content += "<button onclick='goToCellmonitorPage()'>Cellmonitor</button>";
content += " ";
content += "<button onclick='goToEventsPage()'>Events</button>";
content += " ";
content += "<button onclick='promptToReboot()'>Reboot Emulator</button>";
content += "<script>";
content += "function goToUpdatePage() { window.location.href = '/update'; }";
content += "function goToCellmonitorPage() { window.location.href = '/cellmonitor'; }";
content += "function goToSettingsPage() { window.location.href = '/settings'; }";
content += "function goToEventsPage() { window.location.href = '/events'; }";
content +=
"function promptToReboot() { if (window.confirm('Are you sure you want to reboot the emulator? NOTE: If "
"emulator is handling contactors, they will open during reboot!')) { "
@ -897,6 +904,47 @@ String cellmonitor_processor(const String& var) {
return String();
}
String events_processor(const String& var) {
if (var == "PLACEHOLDER") {
String content = "";
// Page format
content += "<style>";
content += "body { background-color: black; color: white; }";
content += "table { width: 100%; border-collapse: collapse; }";
content += "th, td { border: 1px solid white; padding: 10px; text-align: left; }";
content += "</style>";
// Start a new block with a specific background color
content += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
//iterate through entries and display count, time and event
content += "<h4 style='color: white;'>Event log:</h4>";
content += "<table>";
content += "<tr><th>Event Type</th><th>LED Color</th><th>Last Event (seconds ago)</th><th>Count</th><th>Data</th><th>Message</th></tr>";
for(int i = 0; i < EVENT_NOF_EVENTS; i++) {
content += "<tr>";
content += "<td>" + String(get_event_enum_string(static_cast<EVENTS_ENUM_TYPE>(i))) + "</td>";
content += "<td>" + String(entries[i].led_color) + "</td>";
content += "<td>" + String((millis() / 1000) - entries[i].timestamp) + "</td>";
content += "<td>" + String(entries[i].occurences) + "</td>";
content += "<td>" + String(entries[i].data) + "</td>";
content += "<td>" + String(get_event_message(static_cast<EVENTS_ENUM_TYPE>(i))) + "</td>";
content += "</tr>";
}
content += "</table>";
// Close the block
content += "</div>";
content += "<button onclick='goToMainPage()'>Back to main page</button>";
content += "<script>";
content += "function goToMainPage() { window.location.href = '/'; }";
content += "</script>";
return content;
}
return String();
}
void onOTAStart() {
// Log when OTA has started
Serial.println("OTA update started!");

View file

@ -12,6 +12,7 @@
#include "../../lib/me-no-dev-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "../config.h" // Needed for LED defines
#include "../utils/events.h"
#ifdef MQTT
#include "../mqtt/mqtt.h"
#endif
@ -35,6 +36,7 @@ extern uint16_t cellvoltages[120]; //mV 0-4350 per cell
extern uint8_t LEDcolor; //Enum, 0-10
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
extern EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
extern const char* ssid;
extern const char* password;
@ -117,6 +119,15 @@ String settings_processor(const String& var);
*/
String cellmonitor_processor(const String& var);
/**
* @brief Replaces placeholder with content section in web page
*
* @param[in] var
*
* @return String
*/
String events_processor(const String& var);
/**
* @brief Executes on OTA start
*