diff --git a/Software/Software.ino b/Software/Software.ino index 7c75b07d..f724d9e2 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -12,6 +12,7 @@ #include "src/communication/contactorcontrol/comm_contactorcontrol.h" #include "src/communication/equipmentstopbutton/comm_equipmentstopbutton.h" #include "src/communication/nvm/comm_nvm.h" +#include "src/communication/precharge_control/precharge_control.h" #include "src/communication/rs485/comm_rs485.h" #include "src/communication/seriallink/comm_seriallink.h" #include "src/datalayer/datalayer.h" @@ -115,6 +116,10 @@ void setup() { init_contactors(); +#ifdef PRECHARGE_CONTROL + init_precharge_control(); +#endif // PRECHARGE_CONTROL + init_rs485(); init_serialDataLink(); @@ -242,6 +247,9 @@ void core_loop(void* task_time_us) { previousMillis10ms = millis(); led_exe(); handle_contactors(); // Take care of startup precharge/contactor closing +#ifdef PRECHARGE_CONTROL + handle_precharge_control(); +#endif // PRECHARGE_CONTROL } END_TIME_MEASUREMENT_MAX(time_10ms, datalayer.system.status.time_10ms_us); @@ -293,6 +301,30 @@ void core_loop(void* task_time_us) { if (check_pause_2s.elapsed()) { emulator_pause_state_transmit_can_battery(); } + static bms_status_enum previous_state = FAULT; + if (previous_state != datalayer.battery.status.bms_status) { + switch (datalayer.battery.status.bms_status) { + case ACTIVE: + logging.printf("BMS state changed to: OK\n"); + break; + case UPDATING: + logging.printf("BMS state changed to: UPDATING\n"); + break; + case FAULT: + logging.printf("BMS state changed to: FAULT\n"); + break; + case INACTIVE: + logging.printf("BMS state changed to: INACTIVE\n"); + break; + case STANDBY: + logging.printf("BMS state changed to: STANDBY\n"); + break; + default: + logging.printf("BMS state changed to: ??\n"); + break; + } + previous_state = datalayer.battery.status.bms_status; + } vTaskDelayUntil(&xLastWakeTime, xFrequency); } diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 301c7cae..eee35086 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -70,6 +70,9 @@ /* Shunt/Contactor settings */ //#define BMW_SBOX // SBOX relay control & battery current/voltage measurement +/* Automatic Precharge settings. If you have a battery that expects an external voltage applied before opening contactors (within the battery), configure this section */ +//#define PRECHARGE_CONTROL //Enable this line to control a modified HIA4V1 (see wiki) by PWM on the PRECHARGE_PIN. + /* Other options */ //#define LOG_TO_SD //Enable this line to log diagnostic data to SD card //#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs (WARNING, raises CPU load, do not use for production) diff --git a/Software/src/battery/MEB-BATTERY.cpp b/Software/src/battery/MEB-BATTERY.cpp index c73a6f51..1238e2dd 100644 --- a/Software/src/battery/MEB-BATTERY.cpp +++ b/Software/src/battery/MEB-BATTERY.cpp @@ -6,6 +6,8 @@ #include "../devboard/utils/events.h" #include "MEB-BATTERY.h" +#define PRECHARGE_CONTROL + /* TODO list - Check value mappings on the PID polls @@ -77,7 +79,7 @@ static bool BMS_fault_performance = false; //Error: Battery performance is limi static uint16_t BMS_current = 16300; static bool BMS_fault_emergency_shutdown_crash = false; //Error: Safety-critical error (crash detection) Battery contactors are already opened / will be opened immediately Signal is read directly by the EMS and initiates an AKS of the PWR and an active discharge of the DC link -static uint32_t BMS_voltage_intermediate = 0; +static uint32_t BMS_voltage_intermediate = 2000; static uint32_t BMS_voltage = 1480; static uint8_t BMS_status_voltage_free = 0; //0=Init, 1=BMS intermediate circuit voltage-free (U_Zwkr < 20V), 2=BMS intermediate circuit not voltage-free (U_Zwkr >/= 25V, hysteresis), 3=Error @@ -360,6 +362,23 @@ CAN_frame MEB_14C = { .ID = 0x14C, //CRC needed, static content otherwise .data = {0x38, 0x0A, 0xFF, 0x01, 0x01, 0xFF, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x25, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE}}; + +uint32_t can_msg_received = 0; +#define RX_0x17F0007B 0x0001 +#define RX_0x12DD54D0 0x0002 +#define RX_0x12DD54D1 0x0004 +#define RX_0x12DD54D2 0x0008 +#define RX_0x1A555550 0x0010 +#define RX_0x1A555551 0x0020 +#define RX_0x1A5555B2 0x0040 +#define RX_0x16A954A6 0x0080 +#define RX_0x1A5555B0 0x0100 +#define RX_0x1A5555B1 0x0200 +#define RX_0x5A2 0x0400 +#define RX_0x5CA 0x0800 +#define RX_0x0CF 0x1000 +#define RX_DEFAULT 0xE000 + /** Calculate the CRC checksum for VAG CAN Messages * * The method used is described in Chapter "7.2.1.2 8-bit 0x2F polynomial CRC Calculation". @@ -624,6 +643,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { first_can_msg = last_can_msg_timestamp; switch (rx_frame.ID) { case 0x17F0007B: // BMS 500ms + can_msg_received |= RX_0x17F0007B; component_protection_active = (rx_frame.data.u8[0] & 0x01); shutdown_active = ((rx_frame.data.u8[0] & 0x02) >> 1); transportation_mode_active = ((rx_frame.data.u8[0] & 0x02) >> 1); @@ -643,7 +663,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { instrumentation_cluster_request = ((rx_frame.data.u8[1] & 0x40) >> 6); //True/false break; case 0x12DD54D0: // BMS Limits 100ms - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + can_msg_received |= RX_0x12DD54D0; max_discharge_power_watt = ((rx_frame.data.u8[6] & 0x07) << 10) | (rx_frame.data.u8[5] << 2) | (rx_frame.data.u8[4] & 0xC0) >> 6; //*100 max_discharge_current_amp = @@ -652,7 +672,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { max_charge_current_amp = ((rx_frame.data.u8[4] & 0x3F) << 7) | (rx_frame.data.u8[3] >> 1); //*0.2 break; case 0x12DD54D1: // BMS 100ms - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + can_msg_received |= RX_0x12DD54D1; if (rx_frame.data.u8[6] != 0xFE || rx_frame.data.u8[7] != 0xFF) { // Init state, values below invalid battery_SOC = ((rx_frame.data.u8[3] & 0x0F) << 7) | (rx_frame.data.u8[2] >> 1); //*0.05 usable_energy_amount_Wh = (rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]; //*5 @@ -663,7 +683,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { warning_support = (rx_frame.data.u8[1] & 0x70) >> 4; break; case 0x12DD54D2: // BMS 100ms - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + can_msg_received |= RX_0x12DD54D2; battery_heating_active = (rx_frame.data.u8[4] & 0x40) >> 6; heating_request = (rx_frame.data.u8[5] & 0xE0) >> 5; cooling_request = (rx_frame.data.u8[5] & 0x1C) >> 2; @@ -671,6 +691,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { power_battery_heating_req_watt = rx_frame.data.u8[7]; break; case 0x1A555550: // BMS 500ms + can_msg_received |= RX_0x1A555550; balancing_active = (rx_frame.data.u8[1] & 0xC0) >> 6; charging_active = (rx_frame.data.u8[2] & 0x01); max_energy_Wh = ((rx_frame.data.u8[6] & 0x1F) << 8) | rx_frame.data.u8[5]; //*40 @@ -679,6 +700,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { isolation_resistance_kOhm = (((rx_frame.data.u8[3] & 0x1F) << 7) | rx_frame.data.u8[2] >> 1); //*5 break; case 0x1A555551: // BMS 500ms + can_msg_received |= RX_0x1A555551; battery_heating_installed = (rx_frame.data.u8[1] & 0x20) >> 5; error_NT_circuit = (rx_frame.data.u8[1] & 0x40) >> 6; pump_1_control = rx_frame.data.u8[2] & 0x0F; //*10, percent @@ -691,6 +713,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { return_temperature_C = rx_frame.data.u8[7]; //*0,5 -40 break; case 0x1A5555B2: // BMS + can_msg_received |= RX_0x1A5555B2; performance_index_discharge_peak_temperature_percentage = (((rx_frame.data.u8[3] & 0x07) << 6) | rx_frame.data.u8[2] >> 2); //*0.2 performance_index_charge_peak_temperature_percentage = @@ -698,7 +721,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { temperature_status_discharge = (rx_frame.data.u8[1] & 0x70) >> 4; temperature_status_charge = (((rx_frame.data.u8[2] & 0x03) << 1) | rx_frame.data.u8[1] >> 7); break; - case 0x16A954A6: // BMS + case 0x16A954A6: // BMS + can_msg_received |= RX_0x16A954A6; BMS_16A954A6_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on BMS_16A954A6_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on isolation_fault = (rx_frame.data.u8[2] & 0xE0) >> 5; @@ -897,12 +921,14 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { //hybrid_01_response_fd_data (Whole frame) break; case 0x1A5555B0: // BMS 1000ms cyclic + can_msg_received |= RX_0x1A5555B0; duration_discharge_power_watt = ((rx_frame.data.u8[6] & 0x0F) << 8) | rx_frame.data.u8[5]; duration_charge_power_watt = (rx_frame.data.u8[7] << 4) | rx_frame.data.u8[6] >> 4; maximum_voltage = ((rx_frame.data.u8[3] & 0x3F) << 4) | rx_frame.data.u8[2] >> 4; minimum_voltage = (rx_frame.data.u8[4] << 2) | rx_frame.data.u8[3] >> 6; break; case 0x1A5555B1: // BMS 1000ms cyclic + can_msg_received |= RX_0x1A5555B1; // All realtime_ have same enumeration, 0 = no fault, 1 = error level 1, 2 error level 2, 3 error level 3 realtime_overcurrent_monitor = ((rx_frame.data.u8[3] & 0x01) << 2) | rx_frame.data.u8[2] >> 6; realtime_CAN_communication_fault = (rx_frame.data.u8[3] & 0x0E) >> 1; @@ -937,7 +963,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { max_fastcharging_current_amp = ((rx_frame.data.u8[4] & 0x01) << 8) | rx_frame.data.u8[3]; DC_voltage_chargeport = (rx_frame.data.u8[7] << 4) | (rx_frame.data.u8[6] >> 4); break; - case 0x5A2: // BMS 500ms normal, 100ms fast + case 0x5A2: // BMS 500ms normal, 100ms fast + can_msg_received |= RX_0x5A2; BMS_5A2_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on BMS_5A2_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on service_disconnect_switch_missing = (rx_frame.data.u8[1] & 0x20) >> 5; @@ -952,7 +979,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { BMS_capacity_ah = ((rx_frame.data.u8[4] & 0x03) << 9) | (rx_frame.data.u8[3] << 1) | (rx_frame.data.u8[2] >> 7); } break; - case 0x5CA: // BMS 500ms + case 0x5CA: // BMS 500ms + 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 @@ -966,19 +994,43 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { battery_Wh_max = ((rx_frame.data.u8[5] & 0x07) << 8) | rx_frame.data.u8[4]; //*50 ! Not usable, seems to always contain 0x7F0 break; - case 0x0CF: //BMS 10ms + case 0x0CF: //BMS 10ms + can_msg_received |= RX_0x0CF; BMS_0CF_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on BMS_0CF_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on BMS_welded_contactors_status = (rx_frame.data.u8[1] & 0x60) >> 5; BMS_ext_limits_active = (rx_frame.data.u8[1] & 0x80) >> 7; BMS_mode = (rx_frame.data.u8[2] & 0x07); switch (BMS_mode) { - case 1: - case 3: - case 4: + case 1: // HV_ACTIVE + case 3: // EXTERN CHARGING + case 4: // AC_CHARGING + case 6: // DC_CHARGING + if (!datalayer.system.status.battery_allows_contactor_closing) + logging.printf("MEB Contactors closed\n"); + if (datalayer.battery.status.bms_status != FAULT) + datalayer.battery.status.bms_status = ACTIVE; datalayer.system.status.battery_allows_contactor_closing = true; break; + case 5: // Error + if (datalayer.system.status.battery_allows_contactor_closing) + logging.printf("MEB Contactors opened\n"); + datalayer.battery.status.bms_status = FAULT; + datalayer.system.status.battery_allows_contactor_closing = false; + break; + case 7: // Init + if (datalayer.system.status.battery_allows_contactor_closing) + logging.printf("MEB Contactors opened\n"); + if (datalayer.battery.status.bms_status != FAULT) + datalayer.battery.status.bms_status = INACTIVE; + datalayer.system.status.battery_allows_contactor_closing = false; + break; + case 2: // BALANCING default: + if (datalayer.system.status.battery_allows_contactor_closing) + logging.printf("MEB Contactors opened\n"); + if (datalayer.battery.status.bms_status != FAULT) + datalayer.battery.status.bms_status = STANDBY; datalayer.system.status.battery_allows_contactor_closing = false; } BMS_HVIL_status = (rx_frame.data.u8[2] & 0x18) >> 3; @@ -1451,13 +1503,21 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) { default: break; } + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + if (can_msg_received == 0xFFFF) { + if (datalayer.battery.status.bms_status == INACTIVE) + datalayer.battery.status.bms_status = STANDBY; + } } void transmit_can_battery() { unsigned long currentMillis = millis(); // Send 10ms CAN Message - if (currentMillis > last_can_msg_timestamp + 500) { + if (datalayer.system.settings.equipment_stop_active || currentMillis > last_can_msg_timestamp + 500) { + can_msg_received = RX_DEFAULT; first_can_msg = 0; + if (datalayer.battery.status.bms_status != FAULT) + datalayer.battery.status.bms_status = INACTIVE; } if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) { @@ -1525,18 +1585,27 @@ void transmit_can_battery() { previousMillis100ms = currentMillis; //HV request and DC/DC control lies in 0x503 - MEB_503.data.u8[3] = 0x00; - if (datalayer.battery.status.bms_status != FAULT && first_can_msg > 0 && currentMillis > first_can_msg + 2000) { + + if (datalayer.battery.status.bms_status != FAULT && /*first_can_msg > 0 && currentMillis > first_can_msg + 2000*/ + (datalayer.battery.status.bms_status == STANDBY || datalayer.battery.status.bms_status == ACTIVE) && + (labs(((int32_t)datalayer.battery.status.voltage_dV) - + ((int32_t)datalayer_extended.meb.BMS_voltage_intermediate_dV)) < 200)) { + if (MEB_503.data.u8[3] == BMS_TARGET_HV_OFF) { + logging.printf("MEB Requesting HV\n"); + } MEB_503.data.u8[1] = 0xB0; - MEB_503.data.u8[3] = BMS_TARGET_HV_ON; //BMS_TARGET_AC_CHARGING; //TODO, should we try AC_2 or DC 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) { //FAULT STATE, open contactors + MEB_503.data.u8[3] = BMS_TARGET_AC_CHARGING; //TODO, should we try AC_2 or DC 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; 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[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); @@ -1549,7 +1618,7 @@ void transmit_can_battery() { MEB_272.data.u8[5] = DC_FASTCHARGE_NO_START_REQUEST; //DC_FASTCHARGE_VEHICLE; //DC charging //Klemmen status - MEB_3C0.data.u8[2] = 0x00; //0x02; //bit to signal that KL_15 is ON // Always 0 in start4.log + MEB_3C0.data.u8[2] = 0x02; //bit to signal that KL_15 is ON // Always 0 in start4.log MEB_3C0.data.u8[1] = ((MEB_3C0.data.u8[1] & 0xF0) | counter_100ms); MEB_3C0.data.u8[0] = vw_crc_calc(MEB_3C0.data.u8, MEB_3C0.DLC, MEB_3C0.ID); diff --git a/Software/src/communication/precharge_control/precharge_control.cpp b/Software/src/communication/precharge_control/precharge_control.cpp new file mode 100644 index 00000000..44775553 --- /dev/null +++ b/Software/src/communication/precharge_control/precharge_control.cpp @@ -0,0 +1,132 @@ +#include "precharge_control.h" +#include "../../datalayer/datalayer.h" +#include "../../datalayer/datalayer_extended.h" +#include "../../include.h" + +// Parameters + +#ifdef PRECHARGE_CONTROL +enum State { PRECHARGE_IDLE, START_PRECHARGE, PRECHARGE, PRECHARGE_OFF, COMPLETED }; +State prechargeStatus = PRECHARGE_IDLE; + +#define MAX_ALLOWED_FAULT_TICKS 1000 +#define MAX_PRECHARGE_TIME_MS 15000 // Maximum time precharge may be enabled + +#define Precharge_default_PWM_Freq 22000 +#define Precharge_min_PWM_Freq 18000 +#define Precharge_max_PWM_Freq 34000 +#define PWM_Res 8 +#define PWM_OFF_DUTY 0 + +#define PWM_Precharge_Channel 0 +unsigned long prechargeStartTime = 0; +static uint32_t freq = Precharge_default_PWM_Freq; +uint16_t delta_freq = 1; +static int32_t prev_external_voltage = 20000; + +// Initialization functions + +void init_precharge_control() { + // Setup PWM Channel Frequency and Resolution + logging.printf("Precharge control initialised\n"); + pinMode(PRECHARGE_PIN, OUTPUT); + digitalWrite(PRECHARGE_PIN, LOW); +} + +// Main functions +void handle_precharge_control() { + unsigned long currentTime = millis(); +#ifdef MEB_BATTERY + int32_t target_voltage = datalayer.battery.status.voltage_dV; + int32_t external_voltage = datalayer_extended.meb.BMS_voltage_intermediate_dV; +#endif + + // Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge + switch (prechargeStatus) { + case PRECHARGE_IDLE: + + if (datalayer.battery.status.bms_status == STANDBY && datalayer.system.status.inverter_allows_contactor_closing && + !datalayer.system.settings.equipment_stop_active) { + prechargeStatus = START_PRECHARGE; + } + break; + + case START_PRECHARGE: + 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; + logging.printf("Precharge: Starting sequence\n"); + break; + + case PRECHARGE: + // 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) { + prev_external_voltage = external_voltage; + + if (labs(target_voltage - external_voltage) > 150) { + delta_freq = 2000; + } else if (labs(target_voltage - external_voltage) > 80) { + delta_freq = labs(target_voltage - external_voltage) * 6; + } else { + delta_freq = labs(target_voltage - external_voltage) * 3; + } + if (target_voltage > external_voltage) { + freq += delta_freq; + } else { + freq -= delta_freq; + } + if (freq > Precharge_max_PWM_Freq) + freq = Precharge_max_PWM_Freq; + if (freq < Precharge_min_PWM_Freq) + freq = Precharge_min_PWM_Freq; + logging.printf("Precharge: Target: %d V Extern: %d V Frequency: %u\n", target_voltage / 10, + external_voltage / 10, freq); + ledcWriteTone(PRECHARGE_PIN, freq); + } + + if ((datalayer.battery.status.bms_status != STANDBY && datalayer.battery.status.bms_status != ACTIVE) || + datalayer.system.settings.equipment_stop_active) { + pinMode(PRECHARGE_PIN, OUTPUT); + digitalWrite(PRECHARGE_PIN, LOW); + prechargeStatus = PRECHARGE_IDLE; + logging.printf("Precharge: Disabling Precharge bms not standby/active or equipment stop\n"); + } else if (currentTime - prechargeStartTime >= MAX_PRECHARGE_TIME_MS) { + pinMode(PRECHARGE_PIN, OUTPUT); + digitalWrite(PRECHARGE_PIN, LOW); + prechargeStatus = PRECHARGE_OFF; + datalayer.battery.status.bms_status = FAULT; + logging.printf("Precharge: Disabled (timeout reached) -> PRECHARGE_OFF\n"); + // Add event + } else if (datalayer.system.status.battery_allows_contactor_closing) { + pinMode(PRECHARGE_PIN, OUTPUT); + digitalWrite(PRECHARGE_PIN, LOW); + prechargeStatus = COMPLETED; + logging.printf("Precharge: Disabled (contacts closed) -> COMPLETED\n"); + } + break; + + case COMPLETED: + if (datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != ACTIVE) { + prechargeStatus = PRECHARGE_IDLE; + logging.printf("Precharge: equipment stop activated -> IDLE\n"); + } + break; + + case 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; + pinMode(PRECHARGE_PIN, OUTPUT); + digitalWrite(PRECHARGE_PIN, LOW); + logging.printf("Precharge: equipment stop activated -> IDLE\n"); + } + break; + + default: + break; + } +} +#endif // PRECHARGE_CONTROL diff --git a/Software/src/communication/precharge_control/precharge_control.h b/Software/src/communication/precharge_control/precharge_control.h new file mode 100644 index 00000000..c29e1992 --- /dev/null +++ b/Software/src/communication/precharge_control/precharge_control.h @@ -0,0 +1,35 @@ +#ifndef _PRECHARGE_CONTROL_H_ +#define _PRECHARGE_CONTROL_H_ + +#include "../../include.h" + +#include "../../devboard/utils/events.h" + +/** + * @brief Contactor initialization + * + * @param[in] void + * + * @return void + */ +void init_precharge_control(); + +/** + * @brief Handle contactors + * + * @param[in] void + * + * @return void + */ +void handle_precharge_control(); + +/** + * @brief Handle contactors of battery 2 + * + * @param[in] void + * + * @return void + */ +void handle_contactors_battery2(); + +#endif // _PRECHARGE_CONTROL_H_ diff --git a/Software/src/devboard/safety/safety.cpp b/Software/src/devboard/safety/safety.cpp index f662adfe..79a9fec9 100644 --- a/Software/src/devboard/safety/safety.cpp +++ b/Software/src/devboard/safety/safety.cpp @@ -79,15 +79,17 @@ void update_machineryprotection() { // Battery is empty. Do not allow further discharge. // Normally the BMS will send 0W allowed, but this acts as an additional layer of safety - if (datalayer.battery.status.reported_soc == 0) { //Scaled SOC% value is 0.00% - if (!battery_empty_event_fired) { - set_event(EVENT_BATTERY_EMPTY, 0); - battery_empty_event_fired = true; + if (datalayer.battery.status.bms_status == ACTIVE) { + if (datalayer.battery.status.reported_soc == 0) { //Scaled SOC% value is 0.00% + if (!battery_empty_event_fired) { + set_event(EVENT_BATTERY_EMPTY, 0); + battery_empty_event_fired = true; + } + datalayer.battery.status.max_discharge_power_W = 0; + } else { + clear_event(EVENT_BATTERY_EMPTY); + battery_empty_event_fired = false; } - datalayer.battery.status.max_discharge_power_W = 0; - } else { - clear_event(EVENT_BATTERY_EMPTY); - battery_empty_event_fired = false; } // Battery is extremely degraded, not fit for secondlifestorage! @@ -309,10 +311,12 @@ void emulator_pause_state_transmit_can_battery() { allowed_to_send_CAN = (!emulator_pause_CAN_send_ON || emulator_pause_status == NORMAL); if (previous_allowed_to_send_CAN && !allowed_to_send_CAN) { + logging.printf("Safety: Pausing CAN sending"); //completely force stop the CAN communication ESP32Can.CANStop(); } else if (!previous_allowed_to_send_CAN && allowed_to_send_CAN) { //resume CAN communication + logging.printf("Safety: Resuming CAN sending"); ESP32Can.CANInit(); } }