Merge branch 'main' into improvement/can-overrun-refactoring

This commit is contained in:
Daniel Öster 2025-05-03 21:36:37 +03:00
commit cd5facf774
13 changed files with 110 additions and 35 deletions

View file

@ -3,14 +3,17 @@
// These functions adapt the old C-style global functions battery-API to the // These functions adapt the old C-style global functions battery-API to the
// object-oriented battery API. // object-oriented battery API.
#ifdef OO_BATTERY_SELECTED // The instantiated class is defined by the pre-compiler define
// to support battery class selection at compile-time
#ifdef SELECTED_BATTERY_CLASS
static CanBattery* battery; static CanBattery* battery = nullptr;
void setup_battery() { void setup_battery() {
// Currently only one battery is implemented as a class. // Instantiate the battery only once just in case this function gets called multiple times.
// TODO: Extend based on build-time or run-time selected battery. if (battery == nullptr) {
battery = new RenaultZoeGen1Battery(); battery = new SELECTED_BATTERY_CLASS();
}
battery->setup(); battery->setup();
} }

View file

@ -2,19 +2,6 @@
#define BATTERIES_H #define BATTERIES_H
#include "../../USER_SETTINGS.h" #include "../../USER_SETTINGS.h"
#include "src/devboard/utils/types.h"
// Abstract base class for next-generation battery implementations.
// Defines the interface to call battery specific functionality.
// No support for double battery yet.
class CanBattery {
public:
virtual void setup(void) = 0;
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
virtual void update_values() = 0;
virtual void transmit_can(unsigned long currentMillis) = 0;
};
#ifdef BMW_SBOX #ifdef BMW_SBOX
#include "BMW-SBOX.h" #include "BMW-SBOX.h"
void handle_incoming_can_frame_shunt(CAN_frame rx_frame); void handle_incoming_can_frame_shunt(CAN_frame rx_frame);

View file

@ -0,0 +1,17 @@
#ifndef CAN_BATTERY_H
#define CAN_BATTERY_H
#include "src/devboard/utils/types.h"
// Abstract base class for next-generation battery implementations.
// Defines the interface to call battery specific functionality.
// No support for double battery yet.
class CanBattery {
public:
virtual void setup(void) = 0;
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
virtual void update_values() = 0;
virtual void transmit_can() = 0;
};
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h" #include "../include.h"
#ifdef JAGUAR_IPACE_BATTERY #ifdef JAGUAR_IPACE_BATTERY
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "JAGUAR-IPACE-BATTERY.h" #include "JAGUAR-IPACE-BATTERY.h"
@ -62,7 +63,7 @@ void print_units(char* header, int value, char* units) {
logging.print(units); logging.print(units);
} }
void update_values_battery() { void JaguarIpaceBattery::update_values() {
datalayer.battery.status.real_soc = HVBattAvgSOC * 100; //Add two decimals datalayer.battery.status.real_soc = HVBattAvgSOC * 100; //Add two decimals
@ -119,7 +120,8 @@ void update_values_battery() {
#endif #endif
} }
void handle_incoming_can_frame_battery(CAN_frame rx_frame) { void JaguarIpaceBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) { // These messages are periodically transmitted by the battery switch (rx_frame.ID) { // These messages are periodically transmitted by the battery
case 0x080: case 0x080:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
@ -216,7 +218,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
} }
} }
void transmit_can_battery(unsigned long currentMillis) { void JaguarIpaceBattery::transmit_can(unsigned long currentMillis) {
/* Send keep-alive every 200ms */ /* Send keep-alive every 200ms */
if (currentMillis - previousMillisKeepAlive >= INTERVAL_200_MS) { if (currentMillis - previousMillisKeepAlive >= INTERVAL_200_MS) {
previousMillisKeepAlive = currentMillis; previousMillisKeepAlive = currentMillis;
@ -224,7 +226,7 @@ void transmit_can_battery(unsigned long currentMillis) {
} }
} }
void setup_battery(void) { // Performs one time setup at startup void JaguarIpaceBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Jaguar I-PACE", 63); strncpy(datalayer.system.info.battery_protocol, "Jaguar I-PACE", 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 108; datalayer.battery.info.number_of_cells = 108;

View file

@ -1,15 +1,23 @@
#ifndef JAGUAR_IPACE_BATTERY_H #ifndef JAGUAR_IPACE_BATTERY_H
#define JAGUAR_IPACE_BATTERY_H #define JAGUAR_IPACE_BATTERY_H
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED #define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS JaguarIpaceBattery
#define MAX_PACK_VOLTAGE_DV 4546 //5000 = 500.0V #define MAX_PACK_VOLTAGE_DV 4546 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3370 #define MIN_PACK_VOLTAGE_DV 3370
#define MAX_CELL_DEVIATION_MV 250 #define MAX_CELL_DEVIATION_MV 250
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value #define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value #define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value
void setup_battery(void); class JaguarIpaceBattery : public CanBattery {
void transmit_can_frame(CAN_frame* tx_frame, int interface); public:
virtual void setup(void);
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can();
};
#endif #endif

View file

@ -1,11 +1,10 @@
#ifndef RENAULT_ZOE_GEN1_BATTERY_H #ifndef RENAULT_ZOE_GEN1_BATTERY_H
#define RENAULT_ZOE_GEN1_BATTERY_H #define RENAULT_ZOE_GEN1_BATTERY_H
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED #define BATTERY_SELECTED
#define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery
// Indicates that the object-oriented battery interface is to be activated.
#define OO_BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4200 //5000 = 500.0V #define MAX_PACK_VOLTAGE_DV 4200 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3000 #define MIN_PACK_VOLTAGE_DV 3000

View file

@ -16,6 +16,7 @@
#endif //CANFD_ADDON #endif //CANFD_ADDON
void dump_can_frame(CAN_frame& frame, frameDirection msgDir); void dump_can_frame(CAN_frame& frame, frameDirection msgDir);
void transmit_can_frame(CAN_frame* tx_frame, int interface);
/** /**
* @brief Initialization function for CAN. * @brief Initialization function for CAN.

View file

@ -19,11 +19,19 @@ battery_pause_status emulator_pause_status = NORMAL;
//battery pause status end //battery pause status end
void update_machineryprotection() { void update_machineryprotection() {
// Check if the CPU is too hot /* Check if the ESP32 CPU running the Battery-Emulator is too hot.
We start with a warning, you can start to see Wifi issues if it becomes too hot
If the chip starts to approach the design limit, we perform a graceful shutdown */
if (datalayer.system.info.CPU_temperature > 80.0f) { if (datalayer.system.info.CPU_temperature > 80.0f) {
set_event(EVENT_CPU_OVERHEAT, 0); set_event(EVENT_CPU_OVERHEATING, 0);
} else { } else {
clear_event(EVENT_CPU_OVERHEAT); clear_event(EVENT_CPU_OVERHEATING);
}
if (datalayer.system.info.CPU_temperature > 110.0f) {
set_event(EVENT_CPU_OVERHEATED, 0);
}
if (datalayer.system.info.CPU_temperature < 105.0f) {
clear_event(EVENT_CPU_OVERHEATED); //Hysteresis on the clearing
} }
// Check health status of CAN interfaces // Check health status of CAN interfaces

View file

@ -47,7 +47,8 @@ void init_events(void) {
events.entries[EVENT_CAN_CHARGER_MISSING].level = EVENT_LEVEL_INFO; events.entries[EVENT_CAN_CHARGER_MISSING].level = EVENT_LEVEL_INFO;
events.entries[EVENT_CAN_INVERTER_MISSING].level = EVENT_LEVEL_WARNING; events.entries[EVENT_CAN_INVERTER_MISSING].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_CONTACTOR_WELDED].level = EVENT_LEVEL_WARNING; events.entries[EVENT_CONTACTOR_WELDED].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_CPU_OVERHEAT].level = EVENT_LEVEL_WARNING; events.entries[EVENT_CPU_OVERHEATING].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_CPU_OVERHEATED].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_WATER_INGRESS].level = EVENT_LEVEL_ERROR; events.entries[EVENT_WATER_INGRESS].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO; events.entries[EVENT_CHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO;
events.entries[EVENT_DISCHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO; events.entries[EVENT_DISCHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO;
@ -81,6 +82,7 @@ void init_events(void) {
events.entries[EVENT_INVERTER_OPEN_CONTACTOR].level = EVENT_LEVEL_INFO; events.entries[EVENT_INVERTER_OPEN_CONTACTOR].level = EVENT_LEVEL_INFO;
events.entries[EVENT_INTERFACE_MISSING].level = EVENT_LEVEL_INFO; events.entries[EVENT_INTERFACE_MISSING].level = EVENT_LEVEL_INFO;
events.entries[EVENT_MODBUS_INVERTER_MISSING].level = EVENT_LEVEL_INFO; events.entries[EVENT_MODBUS_INVERTER_MISSING].level = EVENT_LEVEL_INFO;
events.entries[EVENT_NO_ENABLE_DETECTED].level = EVENT_LEVEL_INFO;
events.entries[EVENT_ERROR_OPEN_CONTACTOR].level = EVENT_LEVEL_INFO; events.entries[EVENT_ERROR_OPEN_CONTACTOR].level = EVENT_LEVEL_INFO;
events.entries[EVENT_CELL_CRITICAL_UNDER_VOLTAGE].level = EVENT_LEVEL_ERROR; events.entries[EVENT_CELL_CRITICAL_UNDER_VOLTAGE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CELL_CRITICAL_OVER_VOLTAGE].level = EVENT_LEVEL_ERROR; events.entries[EVENT_CELL_CRITICAL_OVER_VOLTAGE].level = EVENT_LEVEL_ERROR;
@ -192,8 +194,10 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
return "Inverter not sending messages via CAN for the last 60 seconds. Check wiring!"; return "Inverter not sending messages via CAN for the last 60 seconds. Check wiring!";
case EVENT_CONTACTOR_WELDED: case EVENT_CONTACTOR_WELDED:
return "Contactors sticking/welded. Inspect battery with caution!"; return "Contactors sticking/welded. Inspect battery with caution!";
case EVENT_CPU_OVERHEAT: case EVENT_CPU_OVERHEATING:
return "Battery-Emulator CPU overheating! Increase airflow/cooling to increase hardware lifespan!"; return "Battery-Emulator CPU overheating! Increase airflow/cooling to increase hardware lifespan!";
case EVENT_CPU_OVERHEATED:
return "Battery-Emulator CPU melting! Performing controlled shutdown until temperature drops!";
case EVENT_CHARGE_LIMIT_EXCEEDED: case EVENT_CHARGE_LIMIT_EXCEEDED:
return "Inverter is charging faster than battery is allowing."; return "Inverter is charging faster than battery is allowing.";
case EVENT_DISCHARGE_LIMIT_EXCEEDED: case EVENT_DISCHARGE_LIMIT_EXCEEDED:
@ -269,6 +273,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
"Check other error code for reason!"; "Check other error code for reason!";
case EVENT_MODBUS_INVERTER_MISSING: case EVENT_MODBUS_INVERTER_MISSING:
return "Modbus inverter has not sent any data. Inspect communication wiring!"; return "Modbus inverter has not sent any data. Inspect communication wiring!";
case EVENT_NO_ENABLE_DETECTED:
return "Inverter Enable line has not been active for a long time. Check Wiring!";
case EVENT_CELL_CRITICAL_UNDER_VOLTAGE: case EVENT_CELL_CRITICAL_UNDER_VOLTAGE:
return "CELL VOLTAGE CRITICALLY LOW! Not possible to continue. Inspect battery!"; return "CELL VOLTAGE CRITICALLY LOW! Not possible to continue. Inspect battery!";
case EVENT_CELL_UNDER_VOLTAGE: case EVENT_CELL_UNDER_VOLTAGE:

View file

@ -20,7 +20,8 @@
XX(EVENT_CAN_NATIVE_TX_FAILURE) \ XX(EVENT_CAN_NATIVE_TX_FAILURE) \
XX(EVENT_CHARGE_LIMIT_EXCEEDED) \ XX(EVENT_CHARGE_LIMIT_EXCEEDED) \
XX(EVENT_CONTACTOR_WELDED) \ XX(EVENT_CONTACTOR_WELDED) \
XX(EVENT_CPU_OVERHEAT) \ XX(EVENT_CPU_OVERHEATING) \
XX(EVENT_CPU_OVERHEATED) \
XX(EVENT_DISCHARGE_LIMIT_EXCEEDED) \ XX(EVENT_DISCHARGE_LIMIT_EXCEEDED) \
XX(EVENT_WATER_INGRESS) \ XX(EVENT_WATER_INGRESS) \
XX(EVENT_12V_LOW) \ XX(EVENT_12V_LOW) \
@ -54,6 +55,7 @@
XX(EVENT_INVERTER_OPEN_CONTACTOR) \ XX(EVENT_INVERTER_OPEN_CONTACTOR) \
XX(EVENT_INTERFACE_MISSING) \ XX(EVENT_INTERFACE_MISSING) \
XX(EVENT_MODBUS_INVERTER_MISSING) \ XX(EVENT_MODBUS_INVERTER_MISSING) \
XX(EVENT_NO_ENABLE_DETECTED) \
XX(EVENT_ERROR_OPEN_CONTACTOR) \ XX(EVENT_ERROR_OPEN_CONTACTOR) \
XX(EVENT_CELL_CRITICAL_UNDER_VOLTAGE) \ XX(EVENT_CELL_CRITICAL_UNDER_VOLTAGE) \
XX(EVENT_CELL_CRITICAL_OVER_VOLTAGE) \ XX(EVENT_CELL_CRITICAL_OVER_VOLTAGE) \

View file

@ -1,6 +1,7 @@
#include "../include.h" #include "../include.h"
#ifdef SMA_BYD_H_CAN #ifdef SMA_BYD_H_CAN
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SMA-BYD-H-CAN.h" #include "SMA-BYD-H-CAN.h"
/* TODO: Map error bits in 0x158 */ /* TODO: Map error bits in 0x158 */
@ -11,6 +12,8 @@ static unsigned long previousMillis100ms = 0;
static uint32_t inverter_time = 0; static uint32_t inverter_time = 0;
static uint16_t inverter_voltage = 0; static uint16_t inverter_voltage = 0;
static int16_t inverter_current = 0; static int16_t inverter_current = 0;
static uint16_t timeWithoutInverterAllowsContactorClosing = 0;
#define THIRTY_MINUTES 1200
//Actual content messages //Actual content messages
CAN_frame SMA_158 = {.FD = false, CAN_frame SMA_158 = {.FD = false,
@ -145,6 +148,17 @@ void update_values_can_inverter() { //This function maps all the values fetched
#endif // INVERTER_CONTACTOR_ENABLE_LED_PIN #endif // INVERTER_CONTACTOR_ENABLE_LED_PIN
} }
// Check if Enable line is working. If we go too long without any input, raise an event
if (!datalayer.system.status.inverter_allows_contactor_closing) {
timeWithoutInverterAllowsContactorClosing++;
if (timeWithoutInverterAllowsContactorClosing > THIRTY_MINUTES) {
set_event(EVENT_NO_ENABLE_DETECTED, 0);
}
} else {
timeWithoutInverterAllowsContactorClosing = 0;
}
/* /*
//SMA_158.data.u8[0] = //bit12 Fault high temperature, bit34Battery cellundervoltage, bit56 Battery cell overvoltage, bit78 batterysystemdefect //SMA_158.data.u8[0] = //bit12 Fault high temperature, bit34Battery cellundervoltage, bit56 Battery cell overvoltage, bit78 batterysystemdefect
//TODO: add all error bits. Sending message with all 0xAA until that. //TODO: add all error bits. Sending message with all 0xAA until that.

View file

@ -1,6 +1,7 @@
#include "../include.h" #include "../include.h"
#ifdef SMA_BYD_HVS_CAN #ifdef SMA_BYD_HVS_CAN
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SMA-BYD-HVS-CAN.h" #include "SMA-BYD-HVS-CAN.h"
/* TODO: Map error bits in 0x158 */ /* TODO: Map error bits in 0x158 */
@ -11,6 +12,8 @@ static unsigned long previousMillis100ms = 0;
static uint32_t inverter_time = 0; static uint32_t inverter_time = 0;
static uint16_t inverter_voltage = 0; static uint16_t inverter_voltage = 0;
static int16_t inverter_current = 0; static int16_t inverter_current = 0;
static uint16_t timeWithoutInverterAllowsContactorClosing = 0;
#define THIRTY_MINUTES 1200
//Actual content messages //Actual content messages
CAN_frame SMA_158 = {.FD = false, CAN_frame SMA_158 = {.FD = false,
@ -149,6 +152,17 @@ void update_values_can_inverter() { //This function maps all the values fetched
#endif // INVERTER_CONTACTOR_ENABLE_LED_PIN #endif // INVERTER_CONTACTOR_ENABLE_LED_PIN
} }
// Check if Enable line is working. If we go too long without any input, raise an event
if (!datalayer.system.status.inverter_allows_contactor_closing) {
timeWithoutInverterAllowsContactorClosing++;
if (timeWithoutInverterAllowsContactorClosing > THIRTY_MINUTES) {
set_event(EVENT_NO_ENABLE_DETECTED, 0);
}
} else {
timeWithoutInverterAllowsContactorClosing = 0;
}
/* /*
//SMA_158.data.u8[0] = //bit12 Fault high temperature, bit34Battery cellundervoltage, bit56 Battery cell overvoltage, bit78 batterysystemdefect //SMA_158.data.u8[0] = //bit12 Fault high temperature, bit34Battery cellundervoltage, bit56 Battery cell overvoltage, bit78 batterysystemdefect
//TODO: add all error bits. Sending message with all 0xAA until that. //TODO: add all error bits. Sending message with all 0xAA until that.

View file

@ -1,6 +1,7 @@
#include "../include.h" #include "../include.h"
#ifdef SMA_TRIPOWER_CAN #ifdef SMA_TRIPOWER_CAN
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SMA-TRIPOWER-CAN.h" #include "SMA-TRIPOWER-CAN.h"
/* TODO: /* TODO:
@ -32,6 +33,8 @@ static int16_t inverter_current = 0;
static bool pairing_completed = false; static bool pairing_completed = false;
static int16_t temperature_average = 0; static int16_t temperature_average = 0;
static uint16_t ampere_hours_remaining = 0; static uint16_t ampere_hours_remaining = 0;
static uint16_t timeWithoutInverterAllowsContactorClosing = 0;
#define THIRTY_MINUTES 1200
//Actual content messages //Actual content messages
CAN_frame SMA_358 = {.FD = false, CAN_frame SMA_358 = {.FD = false,
@ -146,6 +149,17 @@ void update_values_can_inverter() { //This function maps all the values fetched
} else { } else {
SMA_4D8.data.u8[6] = READY_STATE; SMA_4D8.data.u8[6] = READY_STATE;
} }
// Check if Enable line is working. If we go too long without any input, raise an event
if (!datalayer.system.status.inverter_allows_contactor_closing) {
timeWithoutInverterAllowsContactorClosing++;
if (timeWithoutInverterAllowsContactorClosing > THIRTY_MINUTES) {
set_event(EVENT_NO_ENABLE_DETECTED, 0);
}
} else {
timeWithoutInverterAllowsContactorClosing = 0;
}
} }
void map_can_frame_to_variable_inverter(CAN_frame rx_frame) { void map_can_frame_to_variable_inverter(CAN_frame rx_frame) {