From 79ee6c896ab6513ec3b07cdf2dce260033243693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 30 Oct 2024 18:37:45 +0200 Subject: [PATCH 1/4] Add cellpower BMS support --- Software/USER_SETTINGS.h | 3 +- Software/src/battery/BATTERIES.h | 4 + Software/src/battery/CELLPOWER-BMS.cpp | 298 ++++++++++++++++++ Software/src/battery/CELLPOWER-BMS.h | 19 ++ Software/src/datalayer/datalayer_extended.h | 67 ++++ .../webserver/advanced_battery_html.cpp | 137 +++++++- Software/src/devboard/webserver/webserver.cpp | 3 + 7 files changed, 529 insertions(+), 2 deletions(-) create mode 100644 Software/src/battery/CELLPOWER-BMS.cpp create mode 100644 Software/src/battery/CELLPOWER-BMS.h diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 4fb50c77..3cd15f88 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -11,6 +11,7 @@ /* Select battery used */ //#define BMW_I3_BATTERY //#define BYD_ATTO_3_BATTERY +//#define CELLPOWER_BMS //#define CHADEMO_BATTERY //NOTE: inherently enables CONTACTOR_CONTROL below //#define IMIEV_CZERO_ION_BATTERY //#define JAGUAR_IPACE_BATTERY @@ -45,7 +46,7 @@ //#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus /* Select hardware used for Battery-Emulator */ -#define HW_LILYGO +//#define HW_LILYGO //#define HW_STARK /* Other options */ diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 8539c551..268386a0 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -10,6 +10,10 @@ #include "BYD-ATTO-3-BATTERY.h" #endif +#ifdef CELLPOWER_BMS +#include "CELLPOWER-BMS.h" +#endif + #ifdef CHADEMO_BATTERY #include "CHADEMO-BATTERY.h" #endif diff --git a/Software/src/battery/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp new file mode 100644 index 00000000..a337ec1d --- /dev/null +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -0,0 +1,298 @@ +#include "../include.h" +#ifdef CELLPOWER_BMS +#include "../datalayer/datalayer.h" +#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage +#include "../devboard/utils/events.h" +#include "CELLPOWER-BMS.h" + +/* Do not change code below unless you are sure what you are doing */ +static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was sent + +//Actual content messages +// Optional add-on charger module. Might not be needed to send these towards the BMS to keep it happy. +CAN_frame CELLPOWER_18FF50E9 = {.FD = false, + .ext_ID = true, + .DLC = 5, + .ID = 0x18FF50E9, + .data = {0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame CELLPOWER_18FF50E8 = {.FD = false, + .ext_ID = true, + .DLC = 5, + .ID = 0x18FF50E8, + .data = {0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame CELLPOWER_18FF50E7 = {.FD = false, + .ext_ID = true, + .DLC = 5, + .ID = 0x18FF50E7, + .data = {0x00, 0x00, 0x00, 0x00, 0x00}}; +CAN_frame CELLPOWER_18FF50E5 = {.FD = false, + .ext_ID = true, + .DLC = 5, + .ID = 0x18FF50E5, + .data = {0x00, 0x00, 0x00, 0x00, 0x00}}; + +static bool system_state_discharge = false; +static bool system_state_charge = false; +static bool system_state_cellbalancing = false; +static bool system_state_tricklecharge = false; +static bool system_state_idle = false; +static bool system_state_chargecompleted = false; +static bool system_state_maintenancecharge = false; +static bool IO_state_main_positive_relay = false; +static bool IO_state_main_negative_relay = false; +static bool IO_state_charge_enable = false; +static bool IO_state_precharge_relay = false; +static bool IO_state_discharge_enable = false; +static bool IO_state_IO_6 = false; +static bool IO_state_IO_7 = false; +static bool IO_state_IO_8 = false; +static bool error_Cell_overvoltage = false; +static bool error_Cell_undervoltage = false; +static bool error_Cell_end_of_life_voltage = false; +static bool error_Cell_voltage_misread = false; +static bool error_Cell_over_temperature = false; +static bool error_Cell_under_temperature = false; +static bool error_Cell_unmanaged = false; +static bool error_LMU_over_temperature = false; +static bool error_LMU_under_temperature = false; +static bool error_Temp_sensor_open_circuit = false; +static bool error_Temp_sensor_short_circuit = false; +static bool error_SUB_communication = false; +static bool error_LMU_communication = false; +static bool error_Over_current_IN = false; +static bool error_Over_current_OUT = false; +static bool error_Short_circuit = false; +static bool error_Leak_detected = false; +static bool error_Leak_detection_failed = false; +static bool error_Voltage_difference = false; +static bool error_BMCU_supply_over_voltage = false; +static bool error_BMCU_supply_under_voltage = false; +static bool error_Main_positive_contactor = false; +static bool error_Main_negative_contactor = false; +static bool error_Precharge_contactor = false; +static bool error_Midpack_contactor = false; +static bool error_Precharge_timeout = false; +static bool error_Emergency_connector_override = false; +static bool warning_High_cell_voltage = false; +static bool warning_Low_cell_voltage = false; +static bool warning_High_cell_temperature = false; +static bool warning_Low_cell_temperature = false; +static bool warning_High_LMU_temperature = false; +static bool warning_Low_LMU_temperature = false; +static bool warning_SUB_communication_interfered = false; +static bool warning_LMU_communication_interfered = false; +static bool warning_High_current_IN = false; +static bool warning_High_current_OUT = false; +static bool warning_Pack_resistance_difference = false; +static bool warning_High_pack_resistance = false; +static bool warning_Cell_resistance_difference = false; +static bool warning_High_cell_resistance = false; +static bool warning_High_BMCU_supply_voltage = false; +static bool warning_Low_BMCU_supply_voltage = false; +static bool warning_Low_SOC = false; +static bool warning_Balancing_required_OCV_model = false; +static bool warning_Charger_not_responding = false; +static uint16_t cell_voltage_max_mV = 3700; +static uint16_t cell_voltage_min_mV = 3700; +static int8_t pack_temperature_high_C = 0; +static int8_t pack_temperature_low_C = 0; +static uint16_t battery_pack_voltage_dV = 3700; +static int16_t battery_pack_current_dA = 0; +static uint8_t battery_SOH_percentage = 99; +static uint8_t battery_SOC_percentage = 50; +static uint16_t battery_remaining_dAh = 0; +static uint8_t cell_with_highest_voltage = 0; +static uint8_t cell_with_lowest_voltage = 0; +static uint16_t requested_charge_current_dA = 0; +static uint16_t average_charge_current_dA = 0; +static uint16_t actual_charge_current_dA = 0; +static bool requested_exceeding_average_current = 0; +static bool error_state = false; + +void update_values_battery() { + + /* Update values from CAN */ + + datalayer.battery.status.real_soc = battery_SOC_percentage * 100; + + datalayer.battery.status.remaining_capacity_Wh = static_cast( + (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); + + datalayer.battery.status.soh_pptt = battery_SOH_percentage * 100; + + datalayer.battery.status.voltage_dV = battery_pack_voltage_dV; + + datalayer.battery.status.current_dA = battery_pack_current_dA; + + datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt + ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); + + datalayer.battery.status.max_charge_power_W = ((requested_charge_current_dA * battery_pack_voltage_dV) / 100); + + datalayer.battery.status.max_discharge_power_W = 5000; //TODO, is this available via CAN? + + datalayer.battery.status.temperature_min_dC = (int16_t)(pack_temperature_low_C * 10); + + datalayer.battery.status.temperature_max_dC = (int16_t)(pack_temperature_high_C * 10); + + datalayer.battery.status.cell_max_voltage_mV = cell_voltage_max_mV; + + datalayer.battery.status.cell_min_voltage_mV = cell_voltage_min_mV; + + /* Peform safety checks */ + if (system_state_chargecompleted) { + //TODO, shall we set max_charge_power_W to 0 incase this is true? + } + if (IO_state_charge_enable) { + //TODO, shall we react on this? + } + if (IO_state_discharge_enable) { + //TODO, shall we react on this? + } +} + +void receive_can_battery(CAN_frame rx_frame) { + + /* + // All CAN messages recieved will be logged via serial + Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D + Serial.print(" "); + Serial.print(rx_frame.ID, HEX); + Serial.print(" "); + Serial.print(rx_frame.DLC); + Serial.print(" "); + for (int i = 0; i < rx_frame.DLC; ++i) { + Serial.print(rx_frame.data.u8[i], HEX); + Serial.print(" "); + } + Serial.println(""); + */ + switch (rx_frame.ID) { + case 0x1A4: //PDO1_TX - 200ms + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + cell_voltage_max_mV = (uint16_t)((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); + cell_voltage_min_mV = (uint16_t)((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); + pack_temperature_high_C = (int8_t)rx_frame.data.u8[4]; + pack_temperature_low_C = (int8_t)rx_frame.data.u8[5]; + system_state_discharge = (rx_frame.data.u8[6] & 0x01); + system_state_charge = ((rx_frame.data.u8[6] & 0x02) >> 1); + system_state_cellbalancing = ((rx_frame.data.u8[6] & 0x04) >> 2); + system_state_tricklecharge = ((rx_frame.data.u8[6] & 0x08) >> 3); + system_state_idle = ((rx_frame.data.u8[6] & 0x10) >> 4); + system_state_chargecompleted = ((rx_frame.data.u8[6] & 0x20) >> 5); + system_state_maintenancecharge = ((rx_frame.data.u8[6] & 0x40) >> 6); + IO_state_main_positive_relay = (rx_frame.data.u8[7] & 0x01); + IO_state_main_negative_relay = ((rx_frame.data.u8[7] & 0x02) >> 1); + IO_state_charge_enable = ((rx_frame.data.u8[7] & 0x04) >> 2); + IO_state_precharge_relay = ((rx_frame.data.u8[7] & 0x08) >> 3); + IO_state_discharge_enable = ((rx_frame.data.u8[7] & 0x10) >> 4); + IO_state_IO_6 = ((rx_frame.data.u8[7] & 0x20) >> 5); + IO_state_IO_7 = ((rx_frame.data.u8[7] & 0x40) >> 6); + IO_state_IO_8 = ((rx_frame.data.u8[7] & 0x80) >> 7); + break; + case 0x2A4: //PDO2_TX - 200ms + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + battery_pack_voltage_dV = (uint16_t)((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); + battery_pack_current_dA = (int16_t)((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); + battery_SOH_percentage = (uint8_t)rx_frame.data.u8[4]; + battery_SOC_percentage = (uint8_t)rx_frame.data.u8[5]; + battery_remaining_dAh = (uint16_t)((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]); + break; + case 0x3A4: //PDO3_TX - 200ms + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + cell_with_highest_voltage = (uint8_t)rx_frame.data.u8[0]; + cell_with_lowest_voltage = (uint8_t)rx_frame.data.u8[1]; + break; + case 0x4A4: //PDO4_TX - 200ms + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + error_Cell_overvoltage = (rx_frame.data.u8[0] & 0x01); + error_Cell_undervoltage = ((rx_frame.data.u8[0] & 0x02) >> 1); + error_Cell_end_of_life_voltage = ((rx_frame.data.u8[0] & 0x04) >> 2); + error_Cell_voltage_misread = ((rx_frame.data.u8[0] & 0x08) >> 3); + error_Cell_over_temperature = ((rx_frame.data.u8[0] & 0x10) >> 4); + error_Cell_under_temperature = ((rx_frame.data.u8[0] & 0x20) >> 5); + error_Cell_unmanaged = ((rx_frame.data.u8[0] & 0x40) >> 6); + error_LMU_over_temperature = ((rx_frame.data.u8[0] & 0x80) >> 7); + error_LMU_under_temperature = (rx_frame.data.u8[1] & 0x01); + error_Temp_sensor_open_circuit = ((rx_frame.data.u8[1] & 0x02) >> 1); + error_Temp_sensor_short_circuit = ((rx_frame.data.u8[1] & 0x04) >> 2); + error_SUB_communication = ((rx_frame.data.u8[1] & 0x08) >> 3); + error_LMU_communication = ((rx_frame.data.u8[1] & 0x10) >> 4); + error_Over_current_IN = ((rx_frame.data.u8[1] & 0x20) >> 5); + error_Over_current_OUT = ((rx_frame.data.u8[1] & 0x40) >> 6); + error_Short_circuit = ((rx_frame.data.u8[1] & 0x80) >> 7); + error_Leak_detected = (rx_frame.data.u8[2] & 0x01); + error_Leak_detection_failed = ((rx_frame.data.u8[2] & 0x02) >> 1); + error_Voltage_difference = ((rx_frame.data.u8[2] & 0x04) >> 2); + error_BMCU_supply_over_voltage = ((rx_frame.data.u8[2] & 0x08) >> 3); + error_BMCU_supply_under_voltage = ((rx_frame.data.u8[2] & 0x10) >> 4); + error_Main_positive_contactor = ((rx_frame.data.u8[2] & 0x20) >> 5); + error_Main_negative_contactor = ((rx_frame.data.u8[2] & 0x40) >> 6); + error_Precharge_contactor = ((rx_frame.data.u8[2] & 0x80) >> 7); + error_Midpack_contactor = (rx_frame.data.u8[3] & 0x01); + error_Precharge_timeout = ((rx_frame.data.u8[3] & 0x02) >> 1); + error_Emergency_connector_override = ((rx_frame.data.u8[3] & 0x04) >> 2); + warning_High_cell_voltage = (rx_frame.data.u8[4] & 0x01); + warning_Low_cell_voltage = ((rx_frame.data.u8[4] & 0x02) >> 1); + warning_High_cell_temperature = ((rx_frame.data.u8[4] & 0x04) >> 2); + warning_Low_cell_temperature = ((rx_frame.data.u8[4] & 0x08) >> 3); + warning_High_LMU_temperature = ((rx_frame.data.u8[4] & 0x10) >> 4); + warning_Low_LMU_temperature = ((rx_frame.data.u8[4] & 0x20) >> 5); + warning_SUB_communication_interfered = ((rx_frame.data.u8[4] & 0x40) >> 6); + warning_LMU_communication_interfered = ((rx_frame.data.u8[4] & 0x80) >> 7); + warning_High_current_IN = (rx_frame.data.u8[5] & 0x01); + warning_High_current_OUT = ((rx_frame.data.u8[5] & 0x02) >> 1); + warning_Pack_resistance_difference = ((rx_frame.data.u8[5] & 0x04) >> 2); + warning_High_pack_resistance = ((rx_frame.data.u8[5] & 0x08) >> 3); + warning_Cell_resistance_difference = ((rx_frame.data.u8[5] & 0x10) >> 4); + warning_High_cell_resistance = ((rx_frame.data.u8[5] & 0x20) >> 5); + warning_High_BMCU_supply_voltage = ((rx_frame.data.u8[5] & 0x40) >> 6); + warning_Low_BMCU_supply_voltage = ((rx_frame.data.u8[5] & 0x80) >> 7); + warning_Low_SOC = (rx_frame.data.u8[6] & 0x01); + warning_Balancing_required_OCV_model = ((rx_frame.data.u8[6] & 0x02) >> 1); + warning_Charger_not_responding = ((rx_frame.data.u8[6] & 0x04) >> 2); + break; + case 0x7A4: //PDO7_TX - 200ms + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + requested_charge_current_dA = (uint16_t)((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); + average_charge_current_dA = (uint16_t)((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); + actual_charge_current_dA = (uint16_t)((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]); + requested_exceeding_average_current = (rx_frame.data.u8[6] & 0x01); + break; + case 0x7A5: //PDO7.1_TX - 200ms + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + error_state = (rx_frame.data.u8[0] & 0x01); + break; + default: + break; + } +} + +void send_can_battery() { + unsigned long currentMillis = millis(); + // Send 1s CAN Message + if (currentMillis - previousMillis1s >= INTERVAL_1_S) { + + previousMillis1s = currentMillis; + + /* + transmit_can(&CELLPOWER_18FF50E9, can_config.battery); + transmit_can(&CELLPOWER_18FF50E8, can_config.battery); + transmit_can(&CELLPOWER_18FF50E7, can_config.battery); + transmit_can(&CELLPOWER_18FF50E5, can_config.battery); + */ + } +} + +void setup_battery(void) { // Performs one time setup at startup +#ifdef DEBUG_VIA_USB + Serial.println("Cellpower BMS selected"); +#endif + + datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; + datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; + datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; + datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; +} + +#endif // CELLPOWER_BMS diff --git a/Software/src/battery/CELLPOWER-BMS.h b/Software/src/battery/CELLPOWER-BMS.h new file mode 100644 index 00000000..e1956b74 --- /dev/null +++ b/Software/src/battery/CELLPOWER-BMS.h @@ -0,0 +1,19 @@ +#ifndef CELLPOWER_BMS_H +#define CELLPOWER_BMS_H +#include +#include "../include.h" + +/* Tweak these according to your battery build */ +#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V +#define MIN_PACK_VOLTAGE_DV 1500 +#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 + +/* Do not modify any rows below*/ +#define BATTERY_SELECTED +#define NATIVECAN_250KBPS + +void setup_battery(void); +void transmit_can(CAN_frame* tx_frame, int interface); + +#endif diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 9cae0eb6..e1ed206c 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -64,6 +64,72 @@ typedef struct { } DATALAYER_INFO_BYDATTO3; +typedef struct { + /** bool */ + /** All values either True or false */ + bool system_state_discharge = false; + bool system_state_charge = false; + bool system_state_cellbalancing = false; + bool system_state_tricklecharge = false; + bool system_state_idle = false; + bool system_state_chargecompleted = false; + bool system_state_maintenancecharge = false; + bool IO_state_main_positive_relay = false; + bool IO_state_main_negative_relay = false; + bool IO_state_charge_enable = false; + bool IO_state_precharge_relay = false; + bool IO_state_discharge_enable = false; + bool IO_state_IO_6 = false; + bool IO_state_IO_7 = false; + bool IO_state_IO_8 = false; + bool error_Cell_overvoltage = false; + bool error_Cell_undervoltage = false; + bool error_Cell_end_of_life_voltage = false; + bool error_Cell_voltage_misread = false; + bool error_Cell_over_temperature = false; + bool error_Cell_under_temperature = false; + bool error_Cell_unmanaged = false; + bool error_LMU_over_temperature = false; + bool error_LMU_under_temperature = false; + bool error_Temp_sensor_open_circuit = false; + bool error_Temp_sensor_short_circuit = false; + bool error_SUB_communication = false; + bool error_LMU_communication = false; + bool error_Over_current_IN = false; + bool error_Over_current_OUT = false; + bool error_Short_circuit = false; + bool error_Leak_detected = false; + bool error_Leak_detection_failed = false; + bool error_Voltage_difference = false; + bool error_BMCU_supply_over_voltage = false; + bool error_BMCU_supply_under_voltage = false; + bool error_Main_positive_contactor = false; + bool error_Main_negative_contactor = false; + bool error_Precharge_contactor = false; + bool error_Midpack_contactor = false; + bool error_Precharge_timeout = false; + bool error_Emergency_connector_override = false; + bool warning_High_cell_voltage = false; + bool warning_Low_cell_voltage = false; + bool warning_High_cell_temperature = false; + bool warning_Low_cell_temperature = false; + bool warning_High_LMU_temperature = false; + bool warning_Low_LMU_temperature = false; + bool warning_SUB_communication_interfered = false; + bool warning_LMU_communication_interfered = false; + bool warning_High_current_IN = false; + bool warning_High_current_OUT = false; + bool warning_Pack_resistance_difference = false; + bool warning_High_pack_resistance = false; + bool warning_Cell_resistance_difference = false; + bool warning_High_cell_resistance = false; + bool warning_High_BMCU_supply_voltage = false; + bool warning_Low_BMCU_supply_voltage = false; + bool warning_Low_SOC = false; + bool warning_Balancing_required_OCV_model = false; + bool warning_Charger_not_responding = false; +} DATALAYER_INFO_CELLPOWER; + typedef struct { /** uint8_t */ /** Contactor status */ @@ -139,6 +205,7 @@ class DataLayerExtended { public: DATALAYER_INFO_BMWI3 bmwi3; DATALAYER_INFO_BYDATTO3 bydAtto3; + DATALAYER_INFO_CELLPOWER cellpower; DATALAYER_INFO_TESLA tesla; DATALAYER_INFO_NISSAN_LEAF nissanleaf; }; diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index 223b67a4..9718f61e 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -100,6 +100,141 @@ String advanced_battery_processor(const String& var) { #endif //BMW_I3_BATTERY +#ifdef CELLPOWER_BMS + static const char* falseTrue[2] = {"False", "True"}; + content += "

States:

"; + content += "

Discharge: " + String(falseTrue[datalayer_extended.cellpower.system_state_discharge]) + "

"; + content += "

Charge: " + String(falseTrue[datalayer_extended.cellpower.system_state_charge]) + "

"; + content += + "

Cellbalancing: " + String(falseTrue[datalayer_extended.cellpower.system_state_cellbalancing]) + "

"; + content += + "

Tricklecharging: " + String(falseTrue[datalayer_extended.cellpower.system_state_tricklecharge]) + "

"; + content += "

Idle: " + String(falseTrue[datalayer_extended.cellpower.system_state_idle]) + "

"; + content += "

Charge completed: " + String(falseTrue[datalayer_extended.cellpower.system_state_chargecompleted]) + + "

"; + content += + "

Maintenance charge: " + String(falseTrue[datalayer_extended.cellpower.system_state_maintenancecharge]) + + "

"; + content += "

IO:

"; + content += + "

Main positive relay: " + String(falseTrue[datalayer_extended.cellpower.IO_state_main_positive_relay]) + + "

"; + content += + "

Main negative relay: " + String(falseTrue[datalayer_extended.cellpower.IO_state_main_negative_relay]) + + "

"; + content += + "

Charge enabled: " + String(falseTrue[datalayer_extended.cellpower.IO_state_charge_enable]) + "

"; + content += + "

Precharge relay: " + String(falseTrue[datalayer_extended.cellpower.IO_state_precharge_relay]) + "

"; + content += + "

Discharge enable: " + String(falseTrue[datalayer_extended.cellpower.IO_state_discharge_enable]) + "

"; + content += "

IO 6: " + String(falseTrue[datalayer_extended.cellpower.IO_state_IO_6]) + "

"; + content += "

IO 7: " + String(falseTrue[datalayer_extended.cellpower.IO_state_IO_7]) + "

"; + content += "

IO 8: " + String(falseTrue[datalayer_extended.cellpower.IO_state_IO_8]) + "

"; + content += "

Errors:

"; + content += + "

Cell overvoltage: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_overvoltage]) + "

"; + content += + "

Cell undervoltage: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_undervoltage]) + "

"; + content += "

Cell end of life voltage: " + + String(falseTrue[datalayer_extended.cellpower.error_Cell_end_of_life_voltage]) + "

"; + content += + "

Cell voltage misread: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_voltage_misread]) + + "

"; + content += + "

Cell over temperature: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_over_temperature]) + + "

"; + content += + "

Cell under temperature: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_under_temperature]) + + "

"; + content += "

Cell unmanaged: " + String(falseTrue[datalayer_extended.cellpower.error_Cell_unmanaged]) + "

"; + content += + "

LMU over temperature: " + String(falseTrue[datalayer_extended.cellpower.error_LMU_over_temperature]) + + "

"; + content += + "

LMU under temperature: " + String(falseTrue[datalayer_extended.cellpower.error_LMU_under_temperature]) + + "

"; + content += "

Temp sensor open circuit: " + + String(falseTrue[datalayer_extended.cellpower.error_Temp_sensor_open_circuit]) + "

"; + content += "

Temp sensor short circuit: " + + String(falseTrue[datalayer_extended.cellpower.error_Temp_sensor_short_circuit]) + "

"; + content += "

SUB comm: " + String(falseTrue[datalayer_extended.cellpower.error_SUB_communication]) + "

"; + content += "

LMU comm: " + String(falseTrue[datalayer_extended.cellpower.error_LMU_communication]) + "

"; + content += + "

Over current In: " + String(falseTrue[datalayer_extended.cellpower.error_Over_current_IN]) + "

"; + content += + "

Over current Out: " + String(falseTrue[datalayer_extended.cellpower.error_Over_current_OUT]) + "

"; + content += "

Short circuit: " + String(falseTrue[datalayer_extended.cellpower.error_Short_circuit]) + "

"; + content += "

Leak detected: " + String(falseTrue[datalayer_extended.cellpower.error_Leak_detected]) + "

"; + content += + "

Leak detection failed: " + String(falseTrue[datalayer_extended.cellpower.error_Leak_detection_failed]) + + "

"; + content += + "

Voltage diff: " + String(falseTrue[datalayer_extended.cellpower.error_Voltage_difference]) + "

"; + content += "

BMCU supply overvoltage: " + + String(falseTrue[datalayer_extended.cellpower.error_BMCU_supply_over_voltage]) + "

"; + content += "

BMCU supply undervoltage: " + + String(falseTrue[datalayer_extended.cellpower.error_BMCU_supply_under_voltage]) + "

"; + content += "

Main positive contactor: " + + String(falseTrue[datalayer_extended.cellpower.error_Main_positive_contactor]) + "

"; + content += "

Main negative contactor: " + + String(falseTrue[datalayer_extended.cellpower.error_Main_negative_contactor]) + "

"; + content += "

Precharge contactor: " + String(falseTrue[datalayer_extended.cellpower.error_Precharge_contactor]) + + "

"; + content += + "

Midpack contactor: " + String(falseTrue[datalayer_extended.cellpower.error_Midpack_contactor]) + "

"; + content += + "

Precharge timeout: " + String(falseTrue[datalayer_extended.cellpower.error_Precharge_timeout]) + "

"; + content += "

EMG connector override: " + + String(falseTrue[datalayer_extended.cellpower.error_Emergency_connector_override]) + "

"; + content += "

Warnings:

"; + content += + "

High cell voltage: " + String(falseTrue[datalayer_extended.cellpower.warning_High_cell_voltage]) + "

"; + content += + "

Low cell voltage: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_cell_voltage]) + "

"; + content += + "

High cell temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_High_cell_temperature]) + + "

"; + content += + "

Low cell temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_cell_temperature]) + + "

"; + content += + "

High LMU temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_High_LMU_temperature]) + + "

"; + content += + "

Low LMU temperature: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_LMU_temperature]) + + "

"; + content += + "

SUB comm interf: " + String(falseTrue[datalayer_extended.cellpower.warning_SUB_communication_interfered]) + + "

"; + content += + "

LMU comm interf: " + String(falseTrue[datalayer_extended.cellpower.warning_LMU_communication_interfered]) + + "

"; + content += + "

High current In: " + String(falseTrue[datalayer_extended.cellpower.warning_High_current_IN]) + "

"; + content += + "

High current Out: " + String(falseTrue[datalayer_extended.cellpower.warning_High_current_OUT]) + "

"; + content += "

Pack resistance diff: " + + String(falseTrue[datalayer_extended.cellpower.warning_Pack_resistance_difference]) + "

"; + content += + "

High pack resistance: " + String(falseTrue[datalayer_extended.cellpower.warning_High_pack_resistance]) + + "

"; + content += "

Cell resistance diff: " + + String(falseTrue[datalayer_extended.cellpower.warning_Cell_resistance_difference]) + "

"; + content += + "

High cell resistance: " + String(falseTrue[datalayer_extended.cellpower.warning_High_cell_resistance]) + + "

"; + content += "

High BMCU supply voltage: " + + String(falseTrue[datalayer_extended.cellpower.warning_High_BMCU_supply_voltage]) + "

"; + content += "

Low BMCU supply voltage: " + + String(falseTrue[datalayer_extended.cellpower.warning_Low_BMCU_supply_voltage]) + "

"; + content += "

Low SOC: " + String(falseTrue[datalayer_extended.cellpower.warning_Low_SOC]) + "

"; + content += "

Balancing required: " + + String(falseTrue[datalayer_extended.cellpower.warning_Balancing_required_OCV_model]) + "

"; + content += "

Charger not responding: " + + String(falseTrue[datalayer_extended.cellpower.warning_Charger_not_responding]) + "

"; +#endif //CELLPOWER_BMS + #ifdef BYD_ATTO_3_BATTERY content += "

SOC estimated: " + String(datalayer_extended.bydAtto3.SOC_estimated) + "

"; content += "

SOC highprec: " + String(datalayer_extended.bydAtto3.SOC_highprec) + "

"; @@ -160,7 +295,7 @@ String advanced_battery_processor(const String& var) { #endif #if !defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \ - !defined(BYD_ATTO_3_BATTERY) //Only the listed types have extra info + !defined(BYD_ATTO_3_BATTERY) && !defined(CELLPOWER_BMS) //Only the listed types have extra info content += "No extra information available for this battery type"; #endif diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index f1b3230a..038c5d02 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -516,6 +516,9 @@ String processor(const String& var) { #ifdef BYD_ATTO_3_BATTERY content += "BYD Atto 3"; #endif // BYD_ATTO_3_BATTERY +#ifdef CELLPOWER_BMS + content += "Cellpower BMS"; +#endif // CELLPOWER_BMS #ifdef CHADEMO_BATTERY content += "Chademo V2X mode"; #endif // CHADEMO_BATTERY From 40bba6d2a3d829e06506361aad67d78ebb0397f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 30 Oct 2024 18:38:11 +0200 Subject: [PATCH 2/4] Add missing HW def --- Software/USER_SETTINGS.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 3cd15f88..6e2d03d2 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -46,7 +46,7 @@ //#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus /* Select hardware used for Battery-Emulator */ -//#define HW_LILYGO +#define HW_LILYGO //#define HW_STARK /* Other options */ From f7839a0bb9e085e04a80091a82646dddda0a5c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 30 Oct 2024 21:34:59 +0200 Subject: [PATCH 3/4] Add extended datalayer mappings --- Software/src/battery/CELLPOWER-BMS.cpp | 63 ++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/Software/src/battery/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp index a337ec1d..0020d082 100644 --- a/Software/src/battery/CELLPOWER-BMS.cpp +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -139,6 +139,69 @@ void update_values_battery() { datalayer.battery.status.cell_min_voltage_mV = cell_voltage_min_mV; + /* Update webserver datalayer */ + datalayer_extended.cellpower.system_state_discharge = system_state_discharge; + datalayer_extended.cellpower.system_state_charge = system_state_charge; + datalayer_extended.cellpower.system_state_cellbalancing = system_state_cellbalancing; + datalayer_extended.cellpower.system_state_tricklecharge = system_state_tricklecharge; + datalayer_extended.cellpower.system_state_idle = system_state_idle; + datalayer_extended.cellpower.system_state_chargecompleted = system_state_chargecompleted; + datalayer_extended.cellpower.system_state_maintenancecharge = system_state_maintenancecharge; + datalayer_extended.cellpower.IO_state_main_positive_relay = IO_state_main_positive_relay; + datalayer_extended.cellpower.IO_state_main_negative_relay = IO_state_main_negative_relay; + datalayer_extended.cellpower.IO_state_charge_enable = IO_state_charge_enable; + datalayer_extended.cellpower.IO_state_precharge_relay = IO_state_precharge_relay; + datalayer_extended.cellpower.IO_state_discharge_enable = IO_state_discharge_enable; + datalayer_extended.cellpower.IO_state_IO_6 = IO_state_IO_6; + datalayer_extended.cellpower.IO_state_IO_7 = IO_state_IO_7; + datalayer_extended.cellpower.IO_state_IO_8 = IO_state_IO_8; + datalayer_extended.cellpower.error_Cell_overvoltage = error_Cell_overvoltage; + datalayer_extended.cellpower.error_Cell_undervoltage = error_Cell_undervoltage; + datalayer_extended.cellpower.error_Cell_end_of_life_voltage = error_Cell_end_of_life_voltage; + datalayer_extended.cellpower.error_Cell_voltage_misread = error_Cell_voltage_misread; + datalayer_extended.cellpower.error_Cell_over_temperature = error_Cell_over_temperature; + datalayer_extended.cellpower.error_Cell_under_temperature = error_Cell_under_temperature; + datalayer_extended.cellpower.error_Cell_unmanaged = error_Cell_unmanaged; + datalayer_extended.cellpower.error_LMU_over_temperature = error_LMU_over_temperature; + datalayer_extended.cellpower.error_LMU_under_temperature = error_LMU_under_temperature; + datalayer_extended.cellpower.error_Temp_sensor_open_circuit = error_Temp_sensor_open_circuit; + datalayer_extended.cellpower.error_Temp_sensor_short_circuit = error_Temp_sensor_short_circuit; + datalayer_extended.cellpower.error_SUB_communication = error_SUB_communication; + datalayer_extended.cellpower.error_LMU_communication = error_LMU_communication; + datalayer_extended.cellpower.error_Over_current_IN = error_Over_current_IN; + datalayer_extended.cellpower.error_Over_current_OUT = error_Over_current_OUT; + datalayer_extended.cellpower.error_Short_circuit = error_Short_circuit; + datalayer_extended.cellpower.error_Leak_detected = error_Leak_detected; + datalayer_extended.cellpower.error_Leak_detection_failed = error_Leak_detection_failed; + datalayer_extended.cellpower.error_Voltage_difference = error_Voltage_difference; + datalayer_extended.cellpower.error_BMCU_supply_over_voltage = error_BMCU_supply_over_voltage; + datalayer_extended.cellpower.error_BMCU_supply_under_voltage = error_BMCU_supply_under_voltage; + datalayer_extended.cellpower.error_Main_positive_contactor = error_Main_positive_contactor; + datalayer_extended.cellpower.error_Main_negative_contactor = error_Main_negative_contactor; + datalayer_extended.cellpower.error_Precharge_contactor = error_Precharge_contactor; + datalayer_extended.cellpower.error_Midpack_contactor = error_Midpack_contactor; + datalayer_extended.cellpower.error_Precharge_timeout = error_Precharge_timeout; + datalayer_extended.cellpower.error_Emergency_connector_override = error_Emergency_connector_override; + datalayer_extended.cellpower.warning_High_cell_voltage = warning_High_cell_voltage; + datalayer_extended.cellpower.warning_Low_cell_voltage = warning_Low_cell_voltage; + datalayer_extended.cellpower.warning_High_cell_temperature = warning_High_cell_temperature; + datalayer_extended.cellpower.warning_Low_cell_temperature = warning_Low_cell_temperature; + datalayer_extended.cellpower.warning_High_LMU_temperature = warning_High_LMU_temperature; + datalayer_extended.cellpower.warning_Low_LMU_temperature = warning_Low_LMU_temperature; + datalayer_extended.cellpower.warning_SUB_communication_interfered = warning_SUB_communication_interfered; + datalayer_extended.cellpower.warning_LMU_communication_interfered = warning_LMU_communication_interfered; + datalayer_extended.cellpower.warning_High_current_IN = warning_High_current_IN; + datalayer_extended.cellpower.warning_High_current_OUT = warning_High_current_OUT; + datalayer_extended.cellpower.warning_Pack_resistance_difference = warning_Pack_resistance_difference; + datalayer_extended.cellpower.warning_High_pack_resistance = warning_High_pack_resistance; + datalayer_extended.cellpower.warning_Cell_resistance_difference = warning_Cell_resistance_difference; + datalayer_extended.cellpower.warning_High_cell_resistance = warning_High_cell_resistance; + datalayer_extended.cellpower.warning_High_BMCU_supply_voltage = warning_High_BMCU_supply_voltage; + datalayer_extended.cellpower.warning_Low_BMCU_supply_voltage = warning_Low_BMCU_supply_voltage; + datalayer_extended.cellpower.warning_Low_SOC = warning_Low_SOC; + datalayer_extended.cellpower.warning_Balancing_required_OCV_model = warning_Balancing_required_OCV_model; + datalayer_extended.cellpower.warning_Charger_not_responding = warning_Charger_not_responding; + /* Peform safety checks */ if (system_state_chargecompleted) { //TODO, shall we set max_charge_power_W to 0 incase this is true? From eb40deee69da74224146072aa20931c69ec59754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 31 Oct 2024 16:43:31 +0200 Subject: [PATCH 4/4] Add battery to workflow --- .github/workflows/compile-all-batteries.yml | 1 + Software/src/battery/CELLPOWER-BMS.cpp | 22 +++++---------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/.github/workflows/compile-all-batteries.yml b/.github/workflows/compile-all-batteries.yml index b2306708..1cd8955e 100644 --- a/.github/workflows/compile-all-batteries.yml +++ b/.github/workflows/compile-all-batteries.yml @@ -35,6 +35,7 @@ jobs: battery: - BMW_I3_BATTERY - BYD_ATTO_3_BATTERY + - CELLPOWER_BMS - CHADEMO_BATTERY - IMIEV_CZERO_ION_BATTERY - JAGUAR_IPACE_BATTERY diff --git a/Software/src/battery/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp index 0020d082..8bd5a1b5 100644 --- a/Software/src/battery/CELLPOWER-BMS.cpp +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -127,7 +127,7 @@ void update_values_battery() { datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.max_charge_power_W = ((requested_charge_current_dA * battery_pack_voltage_dV) / 100); + datalayer.battery.status.max_charge_power_W = 5000; //TODO, is this available via CAN? datalayer.battery.status.max_discharge_power_W = 5000; //TODO, is this available via CAN? @@ -212,24 +212,12 @@ void update_values_battery() { if (IO_state_discharge_enable) { //TODO, shall we react on this? } + if (error_state) { + //TODO, shall we react on this? + } } - void receive_can_battery(CAN_frame rx_frame) { - /* - // All CAN messages recieved will be logged via serial - Serial.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D - Serial.print(" "); - Serial.print(rx_frame.ID, HEX); - Serial.print(" "); - Serial.print(rx_frame.DLC); - Serial.print(" "); - for (int i = 0; i < rx_frame.DLC; ++i) { - Serial.print(rx_frame.data.u8[i], HEX); - Serial.print(" "); - } - Serial.println(""); - */ switch (rx_frame.ID) { case 0x1A4: //PDO1_TX - 200ms datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; @@ -351,7 +339,7 @@ void setup_battery(void) { // Performs one time setup at startup #ifdef DEBUG_VIA_USB Serial.println("Cellpower BMS selected"); #endif - + datalayer.system.status.battery_allows_contactor_closing = true; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;