Various additions, still WIP

This commit is contained in:
Cabooman 2024-02-09 22:27:13 +01:00
parent cfc2c6a439
commit c839646e58
7 changed files with 228 additions and 112 deletions

View file

@ -259,7 +259,9 @@ void update_values_leaf_battery() { /* This function maps all the values fetched
if (battery_voltage > if (battery_voltage >
(ABSOLUTE_MAX_VOLTAGE - 100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT (ABSOLUTE_MAX_VOLTAGE - 100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
if (LB_SOC < 650) { if (LB_SOC < 650) {
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, LB_SOC / 10); set_event(EVENT_SOC_PLAUSIBILITY_ERROR, LB_SOC / 10); // Set event with the SOC as data
} else {
clear_event(EVENT_SOC_PLAUSIBILITY_ERROR);
} }
} }

View file

@ -2,157 +2,254 @@
#include "../../../USER_SETTINGS.h" #include "../../../USER_SETTINGS.h"
#include "../config.h" #include "../config.h"
#include "timer.h"
typedef enum {
EVENT_STATE_INIT = 0,
EVENT_STATE_INACTIVE,
EVENT_STATE_ACTIVE,
EVENT_STATE_ACTIVE_LATCHED
} EVENT_STATE_TYPE;
typedef struct { typedef struct {
uint32_t timestamp; // Time in seconds since startup when the event occurred 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 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 occurences; // Number of occurrences since startup
uint8_t led_color; // Weirdly indented comment uint8_t led_color; // LED indication
EVENT_STATE_TYPE state; // Event state
} EVENTS_STRUCT_TYPE; } EVENTS_STRUCT_TYPE;
static EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS]; typedef struct {
static unsigned long previous_millis = 0; EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
static uint32_t time_seconds = 0; uint32_t time_seconds;
static uint8_t total_led_color = GREEN; char message[256];
static char event_message[256]; MyTimer second_timer;
uint8_t nof_yellow_events;
uint8_t nof_blue_events;
uint8_t nof_red_events;
} EVENT_TYPE;
/* Local variables */
static EVENT_TYPE events;
/* Local function prototypes */ /* Local function prototypes */
static void update_event_time(void); static void update_event_time(void);
static void set_event_message(EVENTS_ENUM_TYPE event); static void set_message(EVENTS_ENUM_TYPE event);
static void update_led_color(EVENTS_ENUM_TYPE event); static void update_led_color(void);
static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched);
static void update_event_numbers(void);
static void update_bms_status(void);
/* Exported functions */ /* Exported functions */
/* Main execution function, should handle various continuous functionality */
void run_event_handling(void) { void run_event_handling(void) {
update_event_time(); update_event_time();
} }
/* Initialization function */
void init_events(void) { void init_events(void) {
for (uint8_t i = 0; i < EVENT_NOF_EVENTS; i++) { for (uint8_t i = 0; i < EVENT_NOF_EVENTS; i++) {
entries[i].timestamp = 0; events.entries[i].timestamp = 0;
entries[i].data = 0; events.entries[i].data = 0;
entries[i].occurences = 0; events.entries[i].occurences = 0;
entries[i].led_color = RED; // Most events are RED events.entries[i].led_color = RED; // Most events are RED
events.entries[i].state = EVENT_STATE_INACTIVE;
} }
// YELLOW events below // YELLOW events below
entries[EVENT_12V_LOW].led_color = YELLOW; events.entries[EVENT_12V_LOW].led_color = YELLOW;
entries[EVENT_CAN_WARNING].led_color = YELLOW; events.entries[EVENT_CAN_WARNING].led_color = YELLOW;
entries[EVENT_CELL_DEVIATION_HIGH].led_color = YELLOW; events.entries[EVENT_CELL_DEVIATION_HIGH].led_color = YELLOW;
entries[EVENT_KWH_PLAUSIBILITY_ERROR].led_color = YELLOW; events.entries[EVENT_KWH_PLAUSIBILITY_ERROR].led_color = YELLOW;
// BLUE... // BLUE...
entries[EVENT_OTA_UPDATE].led_color = BLUE; events.entries[EVENT_OTA_UPDATE].led_color = BLUE;
events.second_timer.interval = 1000;
events.nof_blue_events = 0;
events.nof_red_events = 0;
events.nof_yellow_events = 0;
} }
void set_event(EVENTS_ENUM_TYPE event, uint8_t data) { void set_event(EVENTS_ENUM_TYPE event, uint8_t data) {
if (event >= EVENT_NOF_EVENTS) { set_event(event, data, false);
event = EVENT_UNKNOWN_EVENT_SET;
}
entries[event].timestamp = time_seconds;
entries[event].data = data;
entries[event].occurences++;
update_led_color(event);
if (total_led_color == RED) {
bms_status = FAULT;
} else if (total_led_color) {
bms_status = UPDATING;
} }
set_event_message(event); void set_event_latched(EVENTS_ENUM_TYPE event, uint8_t data) {
#ifdef DEBUG_VIA_USB set_event(event, data, true);
Serial.println(event_message);
#endif
} }
uint8_t get_event_ledcolor(void) { void clear_event(EVENTS_ENUM_TYPE event) {
return total_led_color; if (events.entries[event].state == EVENT_STATE_ACTIVE) {
events.entries[event].state = EVENT_STATE_INACTIVE;
update_event_numbers();
update_led_color();
update_bms_status();
}
} }
/* Local functions */ /* Local functions */
static void update_event_time(void) {
unsigned long new_millis = millis(); static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) {
if (new_millis - previous_millis >= 1000) { // Just some defensive stuff if someone sets an unknown event
time_seconds++; if (event >= EVENT_NOF_EVENTS) {
previous_millis = new_millis; event = EVENT_UNKNOWN_EVENT_SET;
}
// If the event is already set, no reason to continue
if ((events.entries[event].state == EVENT_STATE_ACTIVE) ||
(events.entries[event].state == EVENT_STATE_ACTIVE_LATCHED)) {
return;
}
// We should set the event, update event info
events.entries[event].timestamp = events.time_seconds;
events.entries[event].data = data;
events.entries[event].occurences++;
// Check if the event is latching
events.entries[event].state = latched ? EVENT_STATE_ACTIVE_LATCHED : EVENT_STATE_ACTIVE;
update_event_numbers();
update_led_color();
update_bms_status();
// Set the associated event message, even if debug is disabled
set_message(event);
#ifdef DEBUG_VIA_USB
Serial.println(events.message);
#endif
}
static void update_bms_status(void) {
if (events.nof_red_events > 0) {
bms_status = FAULT;
} else if (events.nof_blue_events > 0) {
bms_status = UPDATING;
} else if (events.nof_yellow_events > 0) {
// No bms_status update
} }
} }
static void update_led_color(EVENTS_ENUM_TYPE event) { static void update_event_numbers(void) {
total_led_color = max(total_led_color, entries[event].led_color); events.nof_red_events = 0;
} events.nof_blue_events = 0;
events.nof_yellow_events = 0;
static void set_event_message(EVENTS_ENUM_TYPE event) { for (uint8_t i = 0u; i < EVENT_NOF_EVENTS; i++) {
switch (event) { if ((events.entries[i].state == EVENT_STATE_ACTIVE) || (events.entries[i].state == EVENT_STATE_ACTIVE_LATCHED)) {
case EVENT_CAN_FAILURE: switch (events.entries[i].led_color) {
snprintf(event_message, sizeof(event_message), case GREEN:
"No CAN communication detected for 60s. Shutting down battery control."); // Just informative
break; break;
case EVENT_CAN_WARNING: case YELLOW:
snprintf(event_message, sizeof(event_message), events.nof_yellow_events++;
"ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!");
break; break;
case EVENT_WATER_INGRESS: case BLUE:
snprintf(event_message, sizeof(event_message), events.nof_blue_events++;
"Water leakage inside battery detected. Operation halted. Inspect battery!");
break; break;
case EVENT_12V_LOW: case RED:
snprintf(event_message, sizeof(event_message), events.nof_red_events++;
"12V battery source below required voltage to safely close contactors. Inspect the supply/battery!"); break;
break; default:
case EVENT_SOC_PLAUSIBILITY_ERROR: break;
snprintf(event_message, sizeof(event_message), "ERROR: SOC reported by battery not plausible. Restart battery!"); }
break; }
case EVENT_KWH_PLAUSIBILITY_ERROR: }
snprintf(event_message, sizeof(event_message), }
"Warning: kWh remaining reported by battery not plausible. Battery needs cycling.");
break; static void update_event_time(void) {
case EVENT_BATTERY_CHG_STOP_REQ: unsigned long new_millis = millis();
snprintf(event_message, sizeof(event_message), if (events.second_timer.elapsed() == true) {
"ERROR: Battery raised caution indicator AND requested charge stop. Inspect battery status!"); events.time_seconds++;
break; }
case EVENT_BATTERY_DISCHG_STOP_REQ: }
snprintf(event_message, sizeof(event_message),
"ERROR: Battery raised caution indicator AND requested discharge stop. Inspect battery status!"); static void update_led_color(void) {
break; if (events.nof_red_events > 0) {
case EVENT_BATTERY_CHG_DISCHG_STOP_REQ: LEDcolor = RED;
snprintf(event_message, sizeof(event_message), } else if (events.nof_blue_events > 0) {
"ERROR: Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!"); LEDcolor = BLUE;
break; } else if (events.nof_yellow_events > 0) {
case EVENT_LOW_SOH: LEDcolor = YELLOW;
snprintf( } else {
event_message, sizeof(event_message), LEDcolor = GREEN;
"ERROR: State of health critically low. Battery internal resistance too high to continue. Recycle battery."); }
break;
case EVENT_HVIL_FAILURE: // events.total_led_color = max(events.total_led_color, events.entries[event].led_color);
snprintf(event_message, sizeof(event_message), }
"ERROR: Battery interlock loop broken. Check that high voltage connectors are seated. Battery will be "
"disabled!"); static void set_message(EVENTS_ENUM_TYPE event) {
break; switch (event) {
case EVENT_INTERNAL_OPEN_FAULT: case EVENT_CAN_FAILURE:
snprintf(event_message, sizeof(event_message), snprintf(events.message, sizeof(events.message),
"ERROR: High voltage cable removed while battery running. Opening contactors!"); "No CAN communication detected for 60s. Shutting down battery control.");
break; break;
case EVENT_CELL_UNDER_VOLTAGE: case EVENT_CAN_WARNING:
snprintf(event_message, sizeof(event_message), snprintf(events.message, sizeof(events.message),
"ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!"); "ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!");
break; break;
case EVENT_CELL_OVER_VOLTAGE: case EVENT_WATER_INGRESS:
snprintf(event_message, sizeof(event_message), snprintf(events.message, sizeof(events.message),
"ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!"); "Water leakage inside battery detected. Operation halted. Inspect battery!");
break; break;
case EVENT_CELL_DEVIATION_HIGH: case EVENT_12V_LOW:
snprintf(event_message, sizeof(event_message), "ERROR: HIGH CELL DEVIATION!!! Inspect battery!"); snprintf(events.message, sizeof(events.message),
break; "12V battery source below required voltage to safely close contactors. Inspect the supply/battery!");
case EVENT_UNKNOWN_EVENT_SET: break;
snprintf(event_message, sizeof(event_message), "An unknown event was set! Review your code!"); case EVENT_SOC_PLAUSIBILITY_ERROR:
break; snprintf(events.message, sizeof(events.message),
case EVENT_OTA_UPDATE: "ERROR: SOC reported by battery not plausible. Restart battery!");
snprintf(event_message, sizeof(event_message), "OTA update started!"); break;
break; case EVENT_KWH_PLAUSIBILITY_ERROR:
case EVENT_DUMMY: snprintf(events.message, sizeof(events.message),
snprintf(event_message, sizeof(event_message), "The dummy event was set!"); // Don't change this event message! "Warning: kWh remaining reported by battery not plausible. Battery needs cycling.");
break;
case EVENT_BATTERY_CHG_STOP_REQ:
snprintf(events.message, sizeof(events.message),
"ERROR: Battery raised caution indicator AND requested charge stop. Inspect battery status!");
break;
case EVENT_BATTERY_DISCHG_STOP_REQ:
snprintf(events.message, sizeof(events.message),
"ERROR: Battery raised caution indicator AND requested discharge stop. Inspect battery status!");
break;
case EVENT_BATTERY_CHG_DISCHG_STOP_REQ:
snprintf(events.message, sizeof(events.message),
"ERROR: Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!");
break;
case EVENT_LOW_SOH:
snprintf(
events.message, sizeof(events.message),
"ERROR: State of health critically low. Battery internal resistance too high to continue. Recycle battery.");
break;
case EVENT_HVIL_FAILURE:
snprintf(events.message, sizeof(events.message),
"ERROR: Battery interlock loop broken. Check that high voltage connectors are seated. Battery will be "
"disabled!");
break;
case EVENT_INTERNAL_OPEN_FAULT:
snprintf(events.message, sizeof(events.message),
"ERROR: High voltage cable removed while battery running. Opening contactors!");
break;
case EVENT_CELL_UNDER_VOLTAGE:
snprintf(events.message, sizeof(events.message),
"ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
break;
case EVENT_CELL_OVER_VOLTAGE:
snprintf(events.message, sizeof(events.message),
"ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
break;
case EVENT_CELL_DEVIATION_HIGH:
snprintf(events.message, sizeof(events.message), "ERROR: HIGH CELL DEVIATION!!! Inspect battery!");
break;
case EVENT_UNKNOWN_EVENT_SET:
snprintf(events.message, sizeof(events.message), "An unknown event was set! Review your code!");
break;
case EVENT_OTA_UPDATE:
snprintf(events.message, sizeof(events.message), "OTA update started!");
break;
case EVENT_DUMMY:
snprintf(events.message, sizeof(events.message), "The dummy event was set!"); // Don't change this event message!
break; break;
default: default:
break; break;

View file

@ -8,32 +8,35 @@
#include <stdint.h> #include <stdint.h>
typedef enum { typedef enum {
EVENT_CAN_FAILURE = 0u, EVENT_CAN_FAILURE = 0u, // RED event
EVENT_CAN_WARNING, EVENT_CAN_WARNING, // YELLOW event
EVENT_WATER_INGRESS, EVENT_WATER_INGRESS, // RED event
EVENT_12V_LOW, EVENT_12V_LOW, // YELLOW event
EVENT_SOC_PLAUSIBILITY_ERROR, EVENT_SOC_PLAUSIBILITY_ERROR, // RED event
EVENT_KWH_PLAUSIBILITY_ERROR, EVENT_KWH_PLAUSIBILITY_ERROR, // YELLOW event
EVENT_BATTERY_CHG_STOP_REQ, EVENT_BATTERY_CHG_STOP_REQ, // RED event
EVENT_BATTERY_DISCHG_STOP_REQ, EVENT_BATTERY_DISCHG_STOP_REQ, // RED event
EVENT_BATTERY_CHG_DISCHG_STOP_REQ, EVENT_BATTERY_CHG_DISCHG_STOP_REQ, // RED event
EVENT_LOW_SOH, EVENT_LOW_SOH, // RED event
EVENT_HVIL_FAILURE, EVENT_HVIL_FAILURE, // RED event
EVENT_INTERNAL_OPEN_FAULT, EVENT_INTERNAL_OPEN_FAULT, // RED event
EVENT_CELL_UNDER_VOLTAGE, EVENT_CELL_UNDER_VOLTAGE, // RED event
EVENT_CELL_OVER_VOLTAGE, EVENT_CELL_OVER_VOLTAGE, // RED event
EVENT_CELL_DEVIATION_HIGH, EVENT_CELL_DEVIATION_HIGH, // YELLOW event
EVENT_UNKNOWN_EVENT_SET, EVENT_UNKNOWN_EVENT_SET, // RED event
EVENT_OTA_UPDATE, EVENT_OTA_UPDATE, // BLUE event
EVENT_DUMMY, EVENT_DUMMY, // RED event
EVENT_NOF_EVENTS EVENT_NOF_EVENTS // RED event
} EVENTS_ENUM_TYPE; } EVENTS_ENUM_TYPE;
void init_events(void); void init_events(void);
void set_event_latched(EVENTS_ENUM_TYPE event, uint8_t data);
void set_event(EVENTS_ENUM_TYPE event, uint8_t data); void set_event(EVENTS_ENUM_TYPE event, uint8_t data);
void clear_event(EVENTS_ENUM_TYPE event);
void run_event_handling(void); void run_event_handling(void);
uint8_t get_event_ledcolor(void);
extern uint8_t bms_status; //Enum, 0-5 extern uint8_t bms_status; //Enum, 0-5
extern uint8_t LEDcolor;
#endif // __MYTIMER_H__ #endif // __MYTIMER_H__

View file

@ -7,13 +7,16 @@
class MyTimer { class MyTimer {
public: public:
/** Default constructor */
MyTimer() : interval(0), previous_millis(0) {}
/** interval in ms */ /** interval in ms */
MyTimer(unsigned long interval); MyTimer(unsigned long interval);
/** Returns true and resets the timer if it has elapsed */ /** Returns true and resets the timer if it has elapsed */
bool elapsed(); bool elapsed();
unsigned long interval;
private: private:
unsigned long interval;
unsigned long previous_millis; unsigned long previous_millis;
}; };

View file

@ -2,10 +2,12 @@
#include <cstdint> #include <cstdint>
#include "config.h" #include "../Software/src/devboard/config.h"
MySerial Serial; MySerial Serial;
unsigned long testlib_millis = 0; unsigned long testlib_millis = 0;
uint8_t bms_status = ACTIVE; uint8_t bms_status = ACTIVE;
uint8_t LEDcolor = GREEN;

View file

@ -16,6 +16,7 @@ class MySerial;
extern unsigned long testlib_millis; extern unsigned long testlib_millis;
extern MySerial Serial; extern MySerial Serial;
extern uint8_t bms_status; extern uint8_t bms_status;
extern uint8_t LEDcolor;
/* Mock millis() */ /* Mock millis() */
static inline unsigned long millis(void) { static inline unsigned long millis(void) {

View file

@ -1,79 +1,87 @@
// The test library must be included first! // The test library must be included first!
#include "../test_lib.h" #include "../test_lib.h"
#include "../../Software/src/devboard/config.h"
#include "events.cpp" #include "events.cpp"
#include "timer.cpp"
/* Local rest variables */
bool elapsed = false;
/* Helper functions */ /* Helper functions */
// bool MyTimer::elapsed(void) { return true; }
static void reset_event_msg(void) { static void reset_event_msg(void) {
snprintf(event_message, sizeof(event_message), ""); snprintf(events.message, sizeof(events.message), ".");
} }
TEST(init_events_test) { TEST(init_events_test) {
init_events(); init_events();
for (uint8_t i = 0; i < EVENT_NOF_EVENTS; i++) { for (uint8_t i = 0; i < EVENT_NOF_EVENTS; i++) {
ASSERT_EQ(entries[i].occurences, 0); ASSERT_EQ(events.entries[i].occurences, 0);
} }
} }
TEST(update_event_time_test) { TEST(update_event_time_test) {
// Reset // Reset
init_events(); init_events();
time_seconds = 0; events.time_seconds = 0;
// No delta, so time shouldn't increase // No delta, so time shouldn't increase
testlib_millis = 0; testlib_millis = 0;
update_event_time(); update_event_time();
ASSERT_EQ(time_seconds, 0); ASSERT_EQ(events.time_seconds, 0);
// Almost time to bump the seconds // Almost time to bump the seconds
testlib_millis = 999; testlib_millis = 999;
update_event_time(); update_event_time();
ASSERT_EQ(time_seconds, 0); ASSERT_EQ(events.time_seconds, 0);
ASSERT_EQ(previous_millis, 0);
// millis == 1000, so we should add a second // millis == 1000, so we should add a second
testlib_millis = 1000; testlib_millis = 1000;
update_event_time(); update_event_time();
ASSERT_EQ(time_seconds, 1); ASSERT_EQ(events.time_seconds, 1);
ASSERT_EQ(previous_millis, 1000);
// We shouldn't add more seconds until 2000 now // We shouldn't add more seconds until 2000 now
testlib_millis = 1999; testlib_millis = 1999;
update_event_time(); update_event_time();
ASSERT_EQ(time_seconds, 1); ASSERT_EQ(events.time_seconds, 1);
ASSERT_EQ(previous_millis, 1000);
testlib_millis = 2000; testlib_millis = 2000;
update_event_time(); update_event_time();
ASSERT_EQ(time_seconds, 2); ASSERT_EQ(events.time_seconds, 2);
ASSERT_EQ(previous_millis, 2000);
} }
TEST(set_event_test) { TEST(set_event_test) {
// Reset // Reset
init_events(); init_events();
time_seconds = 0; events.time_seconds = 0;
// Initially, the event should not have any data or occurences // Initially, the event should not have any data or occurences
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].data, 0); ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].data, 0);
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].occurences, 0); ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].occurences, 0);
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].timestamp, 0); ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].timestamp, 0);
// Set current time and overvoltage event for cell 23 (RED color, bms_status == FAULT) // Set current time and overvoltage event for cell 23 (RED color, bms_status == FAULT)
time_seconds = 345; events.time_seconds = 345;
set_event(EVENT_CELL_OVER_VOLTAGE, 123); set_event(EVENT_CELL_OVER_VOLTAGE, 123);
// Ensure proper event data // Ensure proper event data
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].data, 123); ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].data, 123);
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].occurences, 1); ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].occurences, 1);
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].timestamp, 345); ASSERT_EQ(events.entries[EVENT_CELL_OVER_VOLTAGE].timestamp, 345);
ASSERT_EQ(bms_status, FAULT); ASSERT_EQ(bms_status, FAULT);
} }
TEST(event_message_test) { TEST(events_message_test) {
reset_event_msg(); reset_event_msg();
set_event(EVENT_DUMMY, 0); // Set dummy event with no data set_event(EVENT_DUMMY, 0); // Set dummy event with no data
ASSERT_STREQ("The dummy event was set!", event_message); ASSERT_STREQ("The dummy event was set!", events.message);
}
TEST(event_priority_test) {
ASSERT_TRUE(RED > BLUE);
ASSERT_TRUE(BLUE > YELLOW);
} }
TEST_MAIN(); TEST_MAIN();