mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
MEB: improvements (#848)
- Don't regulate precharge voltage with external voltage 0. - Improve log output - Wait before starting precharge. - MEB: Improve handling of data from battery BMS - Use actual_Cellvoltage_lowest / highest instead of calculating min/max ourselves. - Correct conversion of temperature_min/max _dC - Correct total_capacity_Wh calculation - Set overall battery_temperature_dC in extended datalayer - Set celltemperature values in extended datalayer - Set temp_points temperature values in extended datalayer - Fix TODO comments that are already solved - Add temp point PIDs and store in extended datalayer. - Display temp points and cell temperatures in advanced battery page - MEB: Add events start/end balancing - Use actual precharge status for precharge bit send to BMS - Only set lowest/highest cell voltage in datalayer when they have actually been received.
This commit is contained in:
parent
21bcf3cf00
commit
566039be5d
10 changed files with 273 additions and 90 deletions
|
@ -185,4 +185,8 @@ extern IPAddress subnet;
|
|||
#define DEBUG_LOG
|
||||
#endif
|
||||
|
||||
#if defined(MEB_BATTERY)
|
||||
#define PRECHARGE_CONTROL
|
||||
#endif
|
||||
|
||||
#endif // __USER_SETTINGS_H__
|
||||
|
|
|
@ -10,15 +10,9 @@
|
|||
|
||||
/*
|
||||
TODO list
|
||||
- Check value mappings on the PID polls
|
||||
- Check all TODO:s in the code
|
||||
- 0x1B000044 & 1B00008F seems to be missing from logs? (Classic CAN)
|
||||
- Scaled remaining capacity, should take already scaled total capacity into account, or we
|
||||
should undo the scaling on the total capacity (which is calculated from the ah value now,
|
||||
which is scaled already).
|
||||
- Investigate why opening and then closing contactors from webpage does not always work
|
||||
- Invertigate why contactors don't close when lilygo and battery are powered on simultaneously -> timeout on can msgs triggers to late, reset when open contactors is executed
|
||||
- Find out how to get the battery in balancing mode
|
||||
- remaining_capacity_Wh is based on a lower limit of 5% soc. This means that at 5% soc, remaining_capacity_Wh returns 0.
|
||||
*/
|
||||
|
||||
/* Do not change code below unless you are sure what you are doing */
|
||||
|
@ -144,7 +138,6 @@ static uint8_t target_flow_temperature_C = 0; //*0,5 -40
|
|||
static uint8_t return_temperature_C = 0; //*0,5 -40
|
||||
static uint8_t status_valve_1 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
|
||||
static uint8_t status_valve_2 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
|
||||
static uint8_t battery_temperature = 0;
|
||||
static uint8_t temperature_request =
|
||||
0; //0 high cooling, 1 medium cooling, 2 low cooling, 3 no temp requirement init, 4 low heating , 5 medium heating, 6 high heating, 7 circulation
|
||||
static uint16_t performance_index_discharge_peak_temperature_percentage = 0;
|
||||
|
@ -164,7 +157,6 @@ static uint16_t actual_cellvoltage_lowest_mV = 0; //bias 1000
|
|||
static uint16_t predicted_power_dyn_standard_watt = 0;
|
||||
static uint8_t predicted_time_dyn_standard_minutes = 0;
|
||||
static uint8_t mux = 0;
|
||||
static int8_t celltemperature[56] = {0}; //Temperatures 1-56. Value is 0xFD if sensor not present
|
||||
static uint16_t cellvoltages[160] = {0};
|
||||
static uint16_t duration_discharge_power_watt = 0;
|
||||
static uint16_t duration_charge_power_watt = 0;
|
||||
|
@ -197,6 +189,7 @@ static bool instrumentation_cluster_request = false;
|
|||
static uint8_t seconds = 0;
|
||||
static uint32_t first_can_msg = 0;
|
||||
static uint32_t last_can_msg_timestamp = 0;
|
||||
static bool hv_requested = false;
|
||||
|
||||
#define TIME_YEAR 2024
|
||||
#define TIME_MONTH 8
|
||||
|
@ -538,8 +531,7 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint16_t address) {
|
|||
|
||||
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||
|
||||
datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100 battery_soc_polled * 10;
|
||||
//Alternatively use battery_SOC for more precision
|
||||
datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100
|
||||
|
||||
datalayer.battery.status.soh_pptt;
|
||||
|
||||
|
@ -548,10 +540,9 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer.battery.status.current_dA = (BMS_current - 16300); // 0.1 * 10
|
||||
|
||||
datalayer.battery.info.total_capacity_Wh =
|
||||
((float)datalayer.battery.info.number_of_cells) * 3.6458 * ((float)BMS_capacity_ah) * 0.2;
|
||||
((float)datalayer.battery.info.number_of_cells) * 3.6458 * ((float)BMS_capacity_ah) * 0.2 * 1.13;
|
||||
|
||||
datalayer.battery.status.remaining_capacity_Wh = usable_energy_amount_Wh * 5;
|
||||
//Alternatively use battery_Wh_left
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = (max_charge_power_watt * 100);
|
||||
|
||||
|
@ -561,34 +552,15 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer.battery.status.active_power_W =
|
||||
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = (battery_min_temp - 350) / 2;
|
||||
// datalayer.battery.status.temperature_min_dC = actual_temperature_lowest_C*5 -400; // We use the value below, because it has better accuracy
|
||||
datalayer.battery.status.temperature_min_dC = (battery_min_temp * 10) / 64;
|
||||
|
||||
datalayer.battery.status.temperature_max_dC = (battery_max_temp - 350) / 2;
|
||||
// datalayer.battery.status.temperature_max_dC = actual_temperature_highest_C*5 -400; // We use the value below, because it has better accuracy
|
||||
datalayer.battery.status.temperature_max_dC = (battery_max_temp * 10) / 64;
|
||||
|
||||
//Map all cell voltages to the global array
|
||||
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t));
|
||||
|
||||
// Initialize min and max, lets find which cells are min and max!
|
||||
uint16_t min_cell_mv_value = std::numeric_limits<uint16_t>::max();
|
||||
uint16_t max_cell_mv_value = 0;
|
||||
// Loop to find the min and max while ignoring zero values
|
||||
for (uint8_t i = 0; i < 108; ++i) {
|
||||
uint16_t voltage_mV = datalayer.battery.status.cell_voltages_mV[i];
|
||||
if (voltage_mV != 0) { // Skip unread values (0)
|
||||
min_cell_mv_value = std::min(min_cell_mv_value, voltage_mV);
|
||||
max_cell_mv_value = std::max(max_cell_mv_value, voltage_mV);
|
||||
}
|
||||
}
|
||||
// If all array values are 0, reset min/max to 3700
|
||||
if (min_cell_mv_value == std::numeric_limits<uint16_t>::max()) {
|
||||
min_cell_mv_value = 3700;
|
||||
max_cell_mv_value = 3700;
|
||||
}
|
||||
|
||||
datalayer.battery.status.cell_min_voltage_mV = min_cell_mv_value;
|
||||
datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value;
|
||||
//TODO, use actual_cellvoltage_lowest_mV instead to save performance
|
||||
|
||||
if (service_disconnect_switch_missing) {
|
||||
set_event(EVENT_HVIL_FAILURE, 1);
|
||||
} else {
|
||||
|
@ -636,6 +608,13 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer_extended.meb.rt_cell_undervol = realtime_cell_undervoltage_warning;
|
||||
datalayer_extended.meb.rt_cell_imbalance = realtime_cell_imbalance_warning;
|
||||
datalayer_extended.meb.rt_battery_unathorized = realtime_warning_battery_unathorized;
|
||||
if (balancing_active == 1 && datalayer_extended.meb.balancing_active != 1)
|
||||
set_event_latched(EVENT_BALANCING_START, 0);
|
||||
if (balancing_active == 2 && datalayer_extended.meb.balancing_active == 1)
|
||||
set_event(EVENT_BALANCING_END, 0);
|
||||
datalayer_extended.meb.balancing_active = balancing_active;
|
||||
datalayer_extended.meb.balancing_request = balancing_request;
|
||||
datalayer_extended.meb.charging_active = charging_active;
|
||||
}
|
||||
|
||||
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||
|
@ -713,7 +692,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
status_valve_1 = (rx_frame.data.u8[3] & 0x1C) >> 2;
|
||||
status_valve_2 = (rx_frame.data.u8[3] & 0xE0) >> 5;
|
||||
temperature_request = (((rx_frame.data.u8[2] & 0x03) << 1) | rx_frame.data.u8[1] >> 7);
|
||||
battery_temperature = rx_frame.data.u8[5]; //*0,5 -40
|
||||
datalayer_extended.meb.battery_temperature_dC = rx_frame.data.u8[5] * 5 - 400; //*0,5 -40
|
||||
target_flow_temperature_C = rx_frame.data.u8[6]; //*0,5 -40
|
||||
return_temperature_C = rx_frame.data.u8[7]; //*0,5 -40
|
||||
break;
|
||||
|
@ -737,6 +716,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
actual_temperature_lowest_C = rx_frame.data.u8[4]; //*0,5 -40
|
||||
actual_cellvoltage_highest_mV = (((rx_frame.data.u8[6] & 0x0F) << 8) | rx_frame.data.u8[5]);
|
||||
actual_cellvoltage_lowest_mV = ((rx_frame.data.u8[7] << 4) | rx_frame.data.u8[6] >> 4);
|
||||
datalayer.battery.status.cell_min_voltage_mV = actual_cellvoltage_lowest_mV + 1000;
|
||||
datalayer.battery.status.cell_max_voltage_mV = actual_cellvoltage_highest_mV + 1000;
|
||||
}
|
||||
break;
|
||||
case 0x16A954F8: // BMS
|
||||
|
@ -748,7 +729,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
switch (mux) {
|
||||
case 0: // Temperatures 1-56. Value is 0xFD if sensor not present
|
||||
for (uint8_t i = 0; i < 56; i++) {
|
||||
celltemperature[i] = (rx_frame.data.u8[i + 1] / 2) - 40;
|
||||
datalayer_extended.meb.celltemperature_dC[i] = (rx_frame.data.u8[i + 1] * 5) - 400;
|
||||
}
|
||||
break;
|
||||
case 1: // Cellvoltages 1-42
|
||||
|
@ -990,7 +971,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
can_msg_received |= RX_0x5CA;
|
||||
BMS_5CA_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
|
||||
BMS_5CA_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
|
||||
balancing_request = (rx_frame.data.u8[5] & 0x08) >> 3; //True/False
|
||||
balancing_request = (rx_frame.data.u8[5] & 0x08) >>
|
||||
3; // BMS requests a low current end charge to support balancing, maybe unused.
|
||||
battery_diagnostic = (rx_frame.data.u8[3] & 0x07);
|
||||
battery_Wh_left =
|
||||
(rx_frame.data.u8[2] << 4) | (rx_frame.data.u8[1] >> 4); //*50 ! Not usable, seems to always contain 0x7F0
|
||||
|
@ -1015,34 +997,37 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
case 6: // DC_CHARGING
|
||||
#ifdef DEBUG_LOG
|
||||
if (!datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB Contactors closed\n");
|
||||
logging.printf("MEB: Contactors closed\n");
|
||||
#endif
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||
datalayer.battery.status.real_bms_status = BMS_ACTIVE;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
hv_requested = false;
|
||||
break;
|
||||
case 5: // Error
|
||||
#ifdef DEBUG_LOG
|
||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB Contactors opened\n");
|
||||
logging.printf("MEB: Contactors opened\n");
|
||||
#endif
|
||||
datalayer.battery.status.real_bms_status = BMS_FAULT;
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
hv_requested = false;
|
||||
break;
|
||||
case 7: // Init
|
||||
#ifdef DEBUG_LOG
|
||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB Contactors opened\n");
|
||||
logging.printf("MEB: Contactors opened\n");
|
||||
#endif
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||
hv_requested = false;
|
||||
break;
|
||||
case 2: // BALANCING
|
||||
default:
|
||||
#ifdef DEBUG_LOG
|
||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||
logging.printf("MEB Contactors opened\n");
|
||||
logging.printf("MEB: Contactors opened\n");
|
||||
#endif
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
||||
|
@ -1084,6 +1069,60 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
case PID_MIN_TEMP:
|
||||
battery_min_temp = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
break;
|
||||
case PID_TEMP_POINT_1:
|
||||
datalayer_extended.meb.temp_points[0] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_2:
|
||||
datalayer_extended.meb.temp_points[1] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_3:
|
||||
datalayer_extended.meb.temp_points[2] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_4:
|
||||
datalayer_extended.meb.temp_points[3] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_5:
|
||||
datalayer_extended.meb.temp_points[4] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_6:
|
||||
datalayer_extended.meb.temp_points[5] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_7:
|
||||
datalayer_extended.meb.temp_points[6] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_8:
|
||||
datalayer_extended.meb.temp_points[7] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_9:
|
||||
datalayer_extended.meb.temp_points[8] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_10:
|
||||
datalayer_extended.meb.temp_points[9] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_11:
|
||||
datalayer_extended.meb.temp_points[10] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_12:
|
||||
datalayer_extended.meb.temp_points[11] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_13:
|
||||
datalayer_extended.meb.temp_points[12] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_14:
|
||||
datalayer_extended.meb.temp_points[13] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_15:
|
||||
datalayer_extended.meb.temp_points[14] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_16:
|
||||
datalayer_extended.meb.temp_points[15] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_17:
|
||||
datalayer_extended.meb.temp_points[16] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_TEMP_POINT_18:
|
||||
datalayer_extended.meb.temp_points[17] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||
break;
|
||||
case PID_MAX_CHARGE_VOLTAGE:
|
||||
battery_max_charge_voltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||
break;
|
||||
|
@ -1535,7 +1574,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
void transmit_can_battery() {
|
||||
unsigned long currentMillis = millis();
|
||||
// Send 10ms CAN Message
|
||||
if (datalayer.system.settings.equipment_stop_active || currentMillis > last_can_msg_timestamp + 500) {
|
||||
if (currentMillis > last_can_msg_timestamp + 500) {
|
||||
#ifdef DEBUG_LOG
|
||||
if (first_can_msg)
|
||||
logging.printf("MEB: No CAN msg received for 500ms\n");
|
||||
|
@ -1615,31 +1654,63 @@ void transmit_can_battery() {
|
|||
|
||||
//HV request and DC/DC control lies in 0x503
|
||||
|
||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT &&
|
||||
(datalayer.battery.status.real_bms_status == BMS_STANDBY ||
|
||||
datalayer.battery.status.real_bms_status == BMS_ACTIVE) &&
|
||||
(labs(((int32_t)datalayer.battery.status.voltage_dV) -
|
||||
((int32_t)datalayer_extended.meb.BMS_voltage_intermediate_dV)) < 200)) {
|
||||
if ((!datalayer.system.settings.equipment_stop_active) && datalayer.battery.status.real_bms_status != BMS_FAULT &&
|
||||
(datalayer.battery.status.real_bms_status == BMS_ACTIVE ||
|
||||
(datalayer.battery.status.real_bms_status == BMS_STANDBY &&
|
||||
(hv_requested ||
|
||||
(datalayer.battery.status.voltage_dV > 200 && datalayer_extended.meb.BMS_voltage_intermediate_dV > 0 &&
|
||||
labs(((int32_t)datalayer.battery.status.voltage_dV) -
|
||||
((int32_t)datalayer_extended.meb.BMS_voltage_intermediate_dV)) < 200))))) {
|
||||
hv_requested = true;
|
||||
datalayer.system.settings.start_precharging = false;
|
||||
#ifdef DEBUG_LOG
|
||||
if (MEB_503.data.u8[3] == BMS_TARGET_HV_OFF) {
|
||||
logging.printf("MEB Requesting HV\n");
|
||||
logging.printf("MEB: Requesting HV\n");
|
||||
}
|
||||
if ((MEB_503.data.u8[1] & 0x80) !=
|
||||
(datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00)) {
|
||||
if (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING) {
|
||||
logging.printf("MEB: Precharge bit set to active\n");
|
||||
} else {
|
||||
logging.printf("MEB: Precharge bit set to inactive\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MEB_503.data.u8[1] =
|
||||
0x30 |
|
||||
(datalayer.battery.status.real_bms_status == BMS_ACTIVE ? 0x00 : 0x80); // Disable precharing if ACTIVE
|
||||
MEB_503.data.u8[3] = BMS_TARGET_HV_ON; //TODO, should we try AC_2 or DC charging?
|
||||
0x30 | (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00);
|
||||
MEB_503.data.u8[3] = BMS_TARGET_AC_CHARGING;
|
||||
MEB_503.data.u8[5] = 0x82; // Bordnetz Active
|
||||
MEB_503.data.u8[6] = 0xE0; // Request emergency shutdown HV system == 0, false
|
||||
} else if (first_can_msg > 0 && currentMillis > first_can_msg + 2000 && BMS_mode != 0 &&
|
||||
BMS_mode != 7) { //FAULT STATE, open contactors
|
||||
MEB_503.data.u8[1] = 0x90;
|
||||
} else if ((first_can_msg > 0 && currentMillis > first_can_msg + 1000 && BMS_mode != 7) ||
|
||||
datalayer.system.settings.equipment_stop_active) { //FAULT STATE, open contactors
|
||||
|
||||
if (datalayer.battery.status.bms_status != FAULT && datalayer.battery.status.real_bms_status == BMS_STANDBY &&
|
||||
!datalayer.system.settings.equipment_stop_active) {
|
||||
datalayer.system.settings.start_precharging = true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG
|
||||
if (MEB_503.data.u8[3] != BMS_TARGET_HV_OFF) {
|
||||
logging.printf("MEB: Requesting HV_OFF\n");
|
||||
}
|
||||
if ((MEB_503.data.u8[1] & 0x80) !=
|
||||
(datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00)) {
|
||||
if (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING) {
|
||||
logging.printf("MEB: Precharge bit set to active\n");
|
||||
} else {
|
||||
logging.printf("MEB: Precharge bit set to inactive\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MEB_503.data.u8[1] =
|
||||
0x10 | (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00);
|
||||
MEB_503.data.u8[3] = BMS_TARGET_HV_OFF;
|
||||
MEB_503.data.u8[5] = 0x80; // Bordnetz Inactive
|
||||
MEB_503.data.u8[6] =
|
||||
0xE3; // Request emergency shutdown HV system == init (3) (not sure if we dare activate this, this is done with 0xE1)
|
||||
} else {
|
||||
MEB_503.data.u8[3] = 0;
|
||||
MEB_503.data.u8[5] = 0x80; // Bordnetz Inactive
|
||||
}
|
||||
MEB_503.data.u8[1] = ((MEB_503.data.u8[1] & 0xF0) | counter_100ms);
|
||||
MEB_503.data.u8[0] = vw_crc_calc(MEB_503.data.u8, MEB_503.DLC, MEB_503.ID);
|
||||
|
@ -1673,9 +1744,9 @@ void transmit_can_battery() {
|
|||
if (currentMillis - previousMillis200ms >= INTERVAL_200_MS) {
|
||||
previousMillis200ms = currentMillis;
|
||||
|
||||
//TODO: 153 does not seem to need CRC even though it has it? Empty in some logs and still works
|
||||
// MEB_153 does not need CRC even though it has it. Empty in some logs as well.
|
||||
|
||||
//TODO: MEB_1B0000B9 & MEB_1B000010 & MEB_1B000046 has CAN sleep commands, static OK?
|
||||
//TODO: MEB_1B0000B9 & MEB_1B000010 & MEB_1B000046 has CAN sleep commands. May be removed?
|
||||
|
||||
transmit_can_frame(&MEB_5E1, can_config.battery);
|
||||
transmit_can_frame(&MEB_153, can_config.battery);
|
||||
|
@ -1707,6 +1778,32 @@ void transmit_can_battery() {
|
|||
case PID_MIN_TEMP:
|
||||
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_MIN_TEMP >> 8);
|
||||
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_MIN_TEMP;
|
||||
poll_pid = PID_TEMP_POINT_1;
|
||||
break;
|
||||
case PID_TEMP_POINT_1:
|
||||
case PID_TEMP_POINT_2:
|
||||
case PID_TEMP_POINT_3:
|
||||
case PID_TEMP_POINT_4:
|
||||
case PID_TEMP_POINT_5:
|
||||
case PID_TEMP_POINT_6:
|
||||
case PID_TEMP_POINT_7:
|
||||
case PID_TEMP_POINT_8:
|
||||
case PID_TEMP_POINT_9:
|
||||
case PID_TEMP_POINT_10:
|
||||
case PID_TEMP_POINT_11:
|
||||
case PID_TEMP_POINT_12:
|
||||
case PID_TEMP_POINT_13:
|
||||
case PID_TEMP_POINT_14:
|
||||
case PID_TEMP_POINT_15:
|
||||
case PID_TEMP_POINT_16:
|
||||
case PID_TEMP_POINT_17:
|
||||
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(poll_pid >> 8);
|
||||
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)poll_pid;
|
||||
poll_pid = poll_pid + 1;
|
||||
break;
|
||||
case PID_TEMP_POINT_18:
|
||||
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(poll_pid >> 8);
|
||||
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)poll_pid;
|
||||
poll_pid = PID_MAX_CHARGE_VOLTAGE;
|
||||
break;
|
||||
case PID_MAX_CHARGE_VOLTAGE:
|
||||
|
|
|
@ -131,6 +131,24 @@
|
|||
#define PID_CELLVOLTAGE_CELL_106 0x1EA9
|
||||
#define PID_CELLVOLTAGE_CELL_107 0x1EAA
|
||||
#define PID_CELLVOLTAGE_CELL_108 0x1EAB
|
||||
#define PID_TEMP_POINT_1 0x1EAE
|
||||
#define PID_TEMP_POINT_2 0x1EAF
|
||||
#define PID_TEMP_POINT_3 0x1EB0
|
||||
#define PID_TEMP_POINT_4 0x1EB1
|
||||
#define PID_TEMP_POINT_5 0x1EB2
|
||||
#define PID_TEMP_POINT_6 0x1EB3
|
||||
#define PID_TEMP_POINT_7 0x1EB4
|
||||
#define PID_TEMP_POINT_8 0x1EB5
|
||||
#define PID_TEMP_POINT_9 0x1EB6
|
||||
#define PID_TEMP_POINT_10 0x1EB7
|
||||
#define PID_TEMP_POINT_11 0x1EB8
|
||||
#define PID_TEMP_POINT_12 0x1EB9
|
||||
#define PID_TEMP_POINT_13 0x1EBA
|
||||
#define PID_TEMP_POINT_14 0x1EBB
|
||||
#define PID_TEMP_POINT_15 0x1EBC
|
||||
#define PID_TEMP_POINT_16 0x1EBD
|
||||
#define PID_TEMP_POINT_17 0x1EBE
|
||||
#define PID_TEMP_POINT_18 0x1EBF
|
||||
|
||||
void setup_battery(void);
|
||||
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
// Parameters
|
||||
|
||||
#ifdef PRECHARGE_CONTROL
|
||||
enum State { PRECHARGE_IDLE, START_PRECHARGE, PRECHARGE, PRECHARGE_OFF, COMPLETED };
|
||||
State prechargeStatus = PRECHARGE_IDLE;
|
||||
|
||||
#define MAX_PRECHARGE_TIME_MS 15000 // Maximum time precharge may be enabled
|
||||
|
||||
|
@ -32,6 +30,7 @@ void init_precharge_control() {
|
|||
#endif
|
||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||
digitalWrite(PRECHARGE_PIN, LOW);
|
||||
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
|
||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
||||
}
|
||||
|
||||
|
@ -44,22 +43,28 @@ void handle_precharge_control() {
|
|||
#endif
|
||||
|
||||
// Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge
|
||||
switch (prechargeStatus) {
|
||||
case PRECHARGE_IDLE:
|
||||
switch (datalayer.system.status.precharge_status) {
|
||||
case AUTO_PRECHARGE_IDLE:
|
||||
|
||||
#if 0
|
||||
if (datalayer.battery.status.bms_status != FAULT && datalayer.battery.status.real_bms_status == BMS_STANDBY &&
|
||||
datalayer.system.status.inverter_allows_contactor_closing &&
|
||||
/*datalayer.system.status.inverter_allows_contactor_closing &&*/
|
||||
!datalayer.system.settings.equipment_stop_active) {
|
||||
prechargeStatus = START_PRECHARGE;
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_START;
|
||||
}
|
||||
#else
|
||||
if (datalayer.system.settings.start_precharging) {
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_START;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case START_PRECHARGE:
|
||||
case AUTO_PRECHARGE_START:
|
||||
freq = Precharge_default_PWM_Freq;
|
||||
ledcAttachChannel(PRECHARGE_PIN, freq, PWM_Res, PWM_Precharge_Channel);
|
||||
ledcWriteTone(PRECHARGE_PIN, freq); // Set frequency and set dutycycle to 50%
|
||||
prechargeStartTime = currentTime;
|
||||
prechargeStatus = PRECHARGE;
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_PRECHARGING;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Starting sequence\n");
|
||||
#endif
|
||||
|
@ -67,9 +72,9 @@ void handle_precharge_control() {
|
|||
|
||||
break;
|
||||
|
||||
case PRECHARGE:
|
||||
case AUTO_PRECHARGE_PRECHARGING:
|
||||
// Check if external voltage measurement changed, for instance with the MEB batteries, the external voltage is only updated every 100ms.
|
||||
if (prev_external_voltage != external_voltage) {
|
||||
if (prev_external_voltage != external_voltage && external_voltage != 0) {
|
||||
prev_external_voltage = external_voltage;
|
||||
|
||||
if (labs(target_voltage - external_voltage) > 150) {
|
||||
|
@ -101,17 +106,18 @@ void handle_precharge_control() {
|
|||
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||
digitalWrite(PRECHARGE_PIN, LOW);
|
||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
||||
prechargeStatus = PRECHARGE_IDLE;
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Disabling Precharge bms not standby/active or equipment stop\n");
|
||||
#endif
|
||||
} else if (currentTime - prechargeStartTime >= MAX_PRECHARGE_TIME_MS) {
|
||||
} else if (currentTime - prechargeStartTime >= MAX_PRECHARGE_TIME_MS ||
|
||||
datalayer.battery.status.real_bms_status == BMS_FAULT) {
|
||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||
digitalWrite(PRECHARGE_PIN, LOW);
|
||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
||||
prechargeStatus = PRECHARGE_OFF;
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_OFF;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Disabled (timeout reached) -> PRECHARGE_OFF\n");
|
||||
logging.printf("Precharge: Disabled (timeout reached / BMS fault) -> AUTO_PRECHARGE_OFF\n");
|
||||
#endif
|
||||
set_event(EVENT_AUTOMATIC_PRECHARGE_FAILURE, 0);
|
||||
|
||||
|
@ -120,27 +126,27 @@ void handle_precharge_control() {
|
|||
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||
digitalWrite(PRECHARGE_PIN, LOW);
|
||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
||||
prechargeStatus = COMPLETED;
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_COMPLETED;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: Disabled (contacts closed) -> COMPLETED\n");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case COMPLETED:
|
||||
case AUTO_PRECHARGE_COMPLETED:
|
||||
if (datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != ACTIVE) {
|
||||
prechargeStatus = PRECHARGE_IDLE;
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Precharge: equipment stop activated -> IDLE\n");
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case PRECHARGE_OFF:
|
||||
case AUTO_PRECHARGE_OFF:
|
||||
if (!datalayer.system.status.battery_allows_contactor_closing ||
|
||||
!datalayer.system.status.inverter_allows_contactor_closing ||
|
||||
datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != FAULT) {
|
||||
prechargeStatus = PRECHARGE_IDLE;
|
||||
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||
digitalWrite(PRECHARGE_PIN, LOW);
|
||||
#ifdef DEBUG_LOG
|
||||
|
@ -153,4 +159,4 @@ void handle_precharge_control() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
#endif // PRECHARGE_CONTROL
|
||||
#endif // AUTO_PRECHARGE_CONTROL
|
||||
|
|
|
@ -280,10 +280,17 @@ typedef struct {
|
|||
#endif
|
||||
/** True if the BMS is being reset, by cutting power towards it */
|
||||
bool BMS_reset_in_progress = false;
|
||||
#ifdef PRECHARGE_CONTROL
|
||||
/** State of automatic precharge sequence */
|
||||
PrechargeState precharge_status = AUTO_PRECHARGE_IDLE;
|
||||
#endif
|
||||
} DATALAYER_SYSTEM_STATUS_TYPE;
|
||||
|
||||
typedef struct {
|
||||
bool equipment_stop_active = false;
|
||||
#ifdef PRECHARGE_CONTROL
|
||||
bool start_precharging = false;
|
||||
#endif
|
||||
} DATALAYER_SYSTEM_SETTINGS_TYPE;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -603,6 +603,12 @@ typedef struct {
|
|||
bool BMS_warning_lamp_req = 0;
|
||||
int32_t BMS_voltage_intermediate_dV = 0;
|
||||
int32_t BMS_voltage_dV = 0;
|
||||
uint8_t balancing_active = 0;
|
||||
bool balancing_request = 0;
|
||||
bool charging_active = 0;
|
||||
float temp_points[18] = {0};
|
||||
uint16_t celltemperature_dC[56] = {0};
|
||||
uint16_t battery_temperature_dC = 0;
|
||||
} DATALAYER_INFO_MEB;
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -354,14 +354,14 @@ void emulator_pause_state_transmit_can_battery() {
|
|||
|
||||
if (previous_allowed_to_send_CAN && !allowed_to_send_CAN) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Safety: Pausing CAN sending");
|
||||
logging.printf("Safety: Pausing CAN sending\n");
|
||||
#endif
|
||||
//completely force stop the CAN communication
|
||||
ESP32Can.CANStop();
|
||||
} else if (!previous_allowed_to_send_CAN && allowed_to_send_CAN) {
|
||||
//resume CAN communication
|
||||
#ifdef DEBUG_LOG
|
||||
logging.printf("Safety: Resuming CAN sending");
|
||||
logging.printf("Safety: Resuming CAN sending\n");
|
||||
#endif
|
||||
ESP32Can.CANInit();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,13 @@ enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAU
|
|||
enum real_bms_status_enum { BMS_DISCONNECTED = 0, BMS_STANDBY = 1, BMS_ACTIVE = 2, BMS_FAULT = 3 };
|
||||
enum battery_chemistry_enum { NCA, NMC, LFP };
|
||||
enum led_color { GREEN, YELLOW, RED, BLUE };
|
||||
enum PrechargeState {
|
||||
AUTO_PRECHARGE_IDLE,
|
||||
AUTO_PRECHARGE_START,
|
||||
AUTO_PRECHARGE_PRECHARGING,
|
||||
AUTO_PRECHARGE_OFF,
|
||||
AUTO_PRECHARGE_COMPLETED
|
||||
};
|
||||
|
||||
#define DISCHARGING 1
|
||||
#define CHARGING 2
|
||||
|
|
|
@ -998,6 +998,23 @@ String advanced_battery_processor(const String& var) {
|
|||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content += String("</h4><h4>Charging: ") + (datalayer_extended.meb.charging_active ? "active" : "not active");
|
||||
content += String("</h4><h4>Balancing: ");
|
||||
switch (datalayer_extended.meb.balancing_active) {
|
||||
case 0:
|
||||
content += String("init");
|
||||
break;
|
||||
case 1:
|
||||
content += String("active");
|
||||
break;
|
||||
case 2:
|
||||
content += String("inactive");
|
||||
break;
|
||||
default:
|
||||
content += String("?");
|
||||
}
|
||||
content +=
|
||||
String("</h4><h4>Slow charging: ") + (datalayer_extended.meb.balancing_request ? "requested" : "not requested");
|
||||
content += "</h4><h4>Diagnostic: ";
|
||||
switch (datalayer_extended.meb.battery_diagnostic) {
|
||||
case 0:
|
||||
|
@ -1127,6 +1144,27 @@ String advanced_battery_processor(const String& var) {
|
|||
content += "<h4>Cell imbalance: " + String(rt_enum[datalayer_extended.meb.rt_cell_imbalance & 0x03]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Battery unathorized: " + String(rt_enum[datalayer_extended.meb.rt_battery_unathorized & 0x03]) + "</h4>";
|
||||
content +=
|
||||
"<h4>Battery temperature: " + String(datalayer_extended.meb.battery_temperature_dC / 10.f, 1) + " °C</h4>";
|
||||
for (int i = 0; i < 3; i++) {
|
||||
content += "<h4>Temperature points " + String(i * 6 + 1) + "-" + String(i * 6 + 6) + " :";
|
||||
for (int j = 0; j < 6; j++)
|
||||
content += " " + String(datalayer_extended.meb.temp_points[i * 6 + j], 1);
|
||||
content += " °C</h4>";
|
||||
}
|
||||
bool temps_done = false;
|
||||
for (int i = 0; i < 7 && !temps_done; i++) {
|
||||
content += "<h4>Cell temperatures " + String(i * 8 + 1) + "-" + String(i * 8 + 8) + " :";
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (datalayer_extended.meb.celltemperature_dC[i * 8 + j] == 865) {
|
||||
temps_done = true;
|
||||
break;
|
||||
} else {
|
||||
content += " " + String(datalayer_extended.meb.celltemperature_dC[i * 8 + j] / 10.f, 1);
|
||||
}
|
||||
}
|
||||
content += " °C</h4>";
|
||||
}
|
||||
#endif //MEB_BATTERY
|
||||
|
||||
#ifdef RENAULT_ZOE_GEN2_BATTERY
|
||||
|
|
|
@ -896,8 +896,8 @@ String processor(const String& var) {
|
|||
} else {
|
||||
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
}
|
||||
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " C</h4>";
|
||||
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " C</h4>";
|
||||
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " °C</h4>";
|
||||
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " °C</h4>";
|
||||
|
||||
content += "<h4>System status: ";
|
||||
switch (datalayer.battery.status.bms_status) {
|
||||
|
@ -1074,8 +1074,8 @@ String processor(const String& var) {
|
|||
} else {
|
||||
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
}
|
||||
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " C</h4>";
|
||||
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " C</h4>";
|
||||
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " °C</h4>";
|
||||
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " °C</h4>";
|
||||
if (datalayer.battery.status.bms_status == ACTIVE) {
|
||||
content += "<h4>System status: OK </h4>";
|
||||
} else if (datalayer.battery.status.bms_status == UPDATING) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue