diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 0baa8ae27..55228f27d 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -40,6 +40,7 @@ //#define TESLA_MODEL_3Y_BATTERY //#define TESLA_MODEL_SX_BATTERY //#define VOLVO_SPA_BATTERY +//#define VOLVO_SPA_HYBRID_BATTERY //#define TEST_FAKE_BATTERY //#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires CAN_ADDON setup) //#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery) diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index 81a3ab406..01134f910 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -135,6 +135,10 @@ void setup_can_shunt(); #include "VOLVO-SPA-BATTERY.h" #endif +#ifdef VOLVO_SPA_HYBRID_BATTERY +#include "VOLVO-SPA-HYBRID-BATTERY.h" +#endif + #ifdef SERIAL_LINK_RECEIVER #include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h" #endif diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp new file mode 100644 index 000000000..233af5ca0 --- /dev/null +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp @@ -0,0 +1,667 @@ +#include "../include.h" +#ifdef VOLVO_SPA_HYBRID_BATTERY +#include "../datalayer/datalayer.h" +#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage +#include "../devboard/utils/events.h" +#include "VOLVO-SPA-HYBRID-BATTERY.h" + +/* Do not change code below unless you are sure what you are doing */ +static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send +static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send +static unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send + +static float BATT_U = 0; //0x3A +static float MAX_U = 0; //0x3A +static float MIN_U = 0; //0x3A +static float BATT_I = 0; //0x3A +static int32_t CHARGE_ENERGY = 0; //0x1A1 +static uint8_t BATT_ERR_INDICATION = 0; //0x413 +static float BATT_T_MAX = 0; //0x413 +static float BATT_T_MIN = 0; //0x413 +static float BATT_T_AVG = 0; //0x413 +static uint16_t SOC_BMS = 0; //0X37D +static uint16_t SOC_CALC = 0; +static uint16_t CELL_U_MAX = 3700; //0x37D +static uint16_t CELL_U_MIN = 3700; //0x37D +static uint8_t CELL_ID_U_MAX = 0; //0x37D +static uint16_t HvBattPwrLimDchaSoft = 0; //0x369 +static uint16_t HvBattPwrLimDcha1 = 0; //0x175 +//static uint16_t HvBattPwrLimDchaSlowAgi = 0; //0x177 +//static uint16_t HvBattPwrLimChrgSlowAgi = 0; //0x177 +//static uint8_t batteryModuleNumber = 0x10; // First battery module +static uint8_t battery_request_idx = 0; +static uint8_t rxConsecutiveFrames = 0; +static uint16_t min_max_voltage[2]; //contains cell min[0] and max[1] values in mV +static uint8_t cellcounter = 0; +static uint32_t remaining_capacity = 0; +static uint16_t cell_voltages[102]; //array with all the cellvoltages +static bool startedUp = false; +static uint8_t DTC_reset_counter = 0; + +CAN_frame VOLVO_536 = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x536, + //.data = {0x00, 0x40, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame + .data = {0x00, 0x40, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00}}; //Network manage frame + +CAN_frame VOLVO_140_CLOSE = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x140, + .data = {0x00, 0x02, 0x00, 0xB7, 0xFF, 0x03, 0xFF, 0x82}}; //Close contactors message + +CAN_frame VOLVO_140_OPEN = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x140, + .data = {0x00, 0x02, 0x00, 0x9E, 0xFF, 0x03, 0xFF, 0x82}}; //Open contactor message + +CAN_frame VOLVO_372 = { + .FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x372, + .data = {0x00, 0xA6, 0x07, 0x14, 0x04, 0x00, 0x80, 0x00}}; //Ambient Temp -->>VERIFY this data content!!!<<-- +CAN_frame VOLVO_CELL_U_Req = { + .FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x735, + .data = {0x03, 0x22, 0x48, 0x06, 0x00, 0x00, 0x00, 0x00}}; //Cell voltage request frame // changed +CAN_frame VOLVO_FlowControl = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x735, + .data = {0x30, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Flowcontrol +CAN_frame VOLVO_SOH_Req = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x735, + .data = {0x03, 0x22, 0x49, 0x6D, 0x00, 0x00, 0x00, 0x00}}; //Battery SOH request frame +CAN_frame VOLVO_BECMsupplyVoltage_Req = { + .FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x735, + .data = {0x03, 0x22, 0xF4, 0x42, 0x00, 0x00, 0x00, 0x00}}; //BECM supply voltage request frame +CAN_frame VOLVO_DTC_Erase = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x7FF, + .data = {0x04, 0x14, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00}}; //Global DTC erase +CAN_frame VOLVO_BECM_ECUreset = { + .FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x735, + .data = {0x02, 0x11, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00}}; //BECM ECU reset command (reboot/powercycle BECM) +CAN_frame VOLVO_DTCreadout = {.FD = false, + .ext_ID = false, + .DLC = 8, + .ID = 0x7FF, + .data = {0x02, 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Global DTC readout + +void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter + uint8_t cnt = 0; + + // Update webserver datalayer + datalayer_extended.VolvoHybrid.soc_bms = SOC_BMS; + datalayer_extended.VolvoHybrid.soc_calc = SOC_CALC; + datalayer_extended.VolvoHybrid.soc_rescaled = datalayer.battery.status.reported_soc; + datalayer_extended.VolvoHybrid.soh_bms = datalayer.battery.status.soh_pptt; + + datalayer_extended.VolvoHybrid.BECMBatteryVoltage = BATT_U; + datalayer_extended.VolvoHybrid.BECMBatteryCurrent = BATT_I; + datalayer_extended.VolvoHybrid.BECMUDynMaxLim = MAX_U; + datalayer_extended.VolvoHybrid.BECMUDynMinLim = MIN_U; + + datalayer_extended.VolvoHybrid.HvBattPwrLimDcha1 = HvBattPwrLimDcha1; + datalayer_extended.VolvoHybrid.HvBattPwrLimDchaSoft = HvBattPwrLimDchaSoft; + //datalayer_extended.VolvoHybrid.HvBattPwrLimDchaSlowAgi = HvBattPwrLimDchaSlowAgi; + //datalayer_extended.VolvoHybrid.HvBattPwrLimChrgSlowAgi = HvBattPwrLimChrgSlowAgi; + + // Update requests from webserver datalayer + if (datalayer_extended.VolvoHybrid.UserRequestDTCreset) { + transmit_can_frame(&VOLVO_DTC_Erase, can_config.battery); //Send global DTC erase command + datalayer_extended.VolvoHybrid.UserRequestDTCreset = false; + } + if (datalayer_extended.VolvoHybrid.UserRequestBECMecuReset) { + transmit_can_frame(&VOLVO_BECM_ECUreset, can_config.battery); //Send BECM ecu reset command + datalayer_extended.VolvoHybrid.UserRequestBECMecuReset = false; + } + if (datalayer_extended.VolvoHybrid.UserRequestDTCreadout) { + transmit_can_frame(&VOLVO_DTCreadout, can_config.battery); //Send DTC readout command + datalayer_extended.VolvoHybrid.UserRequestDTCreadout = false; + } + + remaining_capacity = (18830 - CHARGE_ENERGY); + + //datalayer.battery.status.real_soc = SOC_BMS; // Use BMS reported SOC, havent figured out how to get the BMS to calibrate empty/full yet + SOC_CALC = remaining_capacity / 19; // Use calculated SOC based on remaining_capacity + + datalayer.battery.status.real_soc = SOC_CALC * 10; + + if (BATT_U > MAX_U) // Protect if overcharged + { + datalayer.battery.status.real_soc = 10000; + } else if (BATT_U < MIN_U) //Protect if undercharged + { + datalayer.battery.status.real_soc = 0; + } + + datalayer.battery.status.voltage_dV = BATT_U * 10; + datalayer.battery.status.current_dA = BATT_I * 10; + datalayer.battery.status.remaining_capacity_Wh = remaining_capacity; + + datalayer.battery.status.max_discharge_power_W = 6600; //default power on charge connector + datalayer.battery.status.max_charge_power_W = 6600; //default power on charge connector + datalayer.battery.status.temperature_min_dC = BATT_T_MIN; + datalayer.battery.status.temperature_max_dC = BATT_T_MAX; + + datalayer.battery.status.cell_max_voltage_mV = CELL_U_MAX; // Use min/max reported from BMS + datalayer.battery.status.cell_min_voltage_mV = CELL_U_MIN; + + //Map all cell voltages to the global array + for (int i = 0; i < 102; ++i) { + datalayer.battery.status.cell_voltages_mV[i] = cell_voltages[i]; + } + +#ifdef DEBUG_LOG + logging.print("BMS reported SOC%: "); + logging.println(SOC_BMS); + logging.print("Calculated SOC%: "); + logging.println(SOC_CALC); + logging.print("Rescaled SOC%: "); + logging.println(datalayer.battery.status.reported_soc / 100); + logging.print("Battery current: "); + logging.println(BATT_I); + logging.print("Battery voltage: "); + logging.println(BATT_U); + logging.print("Battery maximum voltage limit: "); + logging.println(MAX_U); + logging.print("Battery minimum voltage limit: "); + logging.println(MIN_U); + logging.print("Remaining Energy: "); + logging.println(remaining_capacity); + logging.print("Discharge limit: "); + logging.println(HvBattPwrLimDchaSoft); + logging.print("Battery Error Indication: "); + logging.println(BATT_ERR_INDICATION); + logging.print("Maximum battery temperature: "); + logging.println(BATT_T_MAX / 10); + logging.print("Minimum battery temperature: "); + logging.println(BATT_T_MIN / 10); + logging.print("Average battery temperature: "); + logging.println(BATT_T_AVG / 10); + logging.print("BMS Highest cell voltage: "); + logging.println(CELL_U_MAX); + logging.print("BMS Lowest cell voltage: "); + logging.println(CELL_U_MIN); + logging.print("BMS Highest cell nr: "); + logging.println(CELL_ID_U_MAX); + logging.print("Highest cell voltage: "); + logging.println(min_max_voltage[1]); + logging.print("Lowest cell voltage: "); + logging.println(min_max_voltage[0]); + logging.print("Cell voltage,"); + while (cnt < 102) { + logging.print(cell_voltages[cnt++]); + logging.print(","); + } + logging.println(";"); +#endif +} + +void handle_incoming_can_frame_battery(CAN_frame rx_frame) { + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + switch (rx_frame.ID) { + case 0x3A: + if ((rx_frame.data.u8[6] & 0x80) == 0x80) + BATT_I = (0 - ((((rx_frame.data.u8[6] & 0x7F) * 256.0 + rx_frame.data.u8[7]) * 0.1) - 1638)); + else { + BATT_I = 0; +#ifdef DEBUG_LOG + logging.println("BATT_I not valid"); +#endif + } + + if ((rx_frame.data.u8[2] & 0x08) == 0x08) + MAX_U = (((rx_frame.data.u8[2] & 0x07) * 256.0 + rx_frame.data.u8[3]) * 0.25); + else { + //MAX_U = 0; + //logging.println("MAX_U not valid"); // Value toggles between true/false from BMS + } + + if ((rx_frame.data.u8[4] & 0x08) == 0x08) + MIN_U = (((rx_frame.data.u8[4] & 0x07) * 256.0 + rx_frame.data.u8[5]) * 0.25); + else { + //MIN_U = 0; + //logging.println("MIN_U not valid"); // Value toggles between true/false from BMS + } + + if ((rx_frame.data.u8[0] & 0x08) == 0x08) + BATT_U = (((rx_frame.data.u8[0] & 0x07) * 256.0 + rx_frame.data.u8[1]) * 0.25); + else { + BATT_U = 0; +#ifdef DEBUG_LOG + logging.println("BATT_U not valid"); +#endif + } + + if ((rx_frame.data.u8[0] & 0x40) == 0x40) + datalayer_extended.VolvoHybrid.HVSysRlySts = ((rx_frame.data.u8[0] & 0x30) >> 4); + else + datalayer_extended.VolvoHybrid.HVSysRlySts = 0xFF; + + if ((rx_frame.data.u8[2] & 0x40) == 0x40) + datalayer_extended.VolvoHybrid.HVSysDCRlySts1 = ((rx_frame.data.u8[2] & 0x30) >> 4); + else + datalayer_extended.VolvoHybrid.HVSysDCRlySts1 = 0xFF; + if ((rx_frame.data.u8[2] & 0x80) == 0x80) + datalayer_extended.VolvoHybrid.HVSysDCRlySts2 = ((rx_frame.data.u8[4] & 0x30) >> 4); + else + datalayer_extended.VolvoHybrid.HVSysDCRlySts2 = 0xFF; + if ((rx_frame.data.u8[0] & 0x80) == 0x80) + datalayer_extended.VolvoHybrid.HVSysIsoRMonrSts = ((rx_frame.data.u8[4] & 0xC0) >> 6); + else + datalayer_extended.VolvoHybrid.HVSysIsoRMonrSts = 0xFF; + + break; + case 0x1A1: + if ((rx_frame.data.u8[4] & 0x10) == 0x10) + CHARGE_ENERGY = ((((rx_frame.data.u8[4] & 0x0F) * 256.0 + rx_frame.data.u8[5]) * 50) - 500); + else { + CHARGE_ENERGY = 0; + set_event(EVENT_KWH_PLAUSIBILITY_ERROR, CHARGE_ENERGY); + } + break; + case 0x413: + if ((rx_frame.data.u8[0] & 0x80) == 0x80) + BATT_ERR_INDICATION = ((rx_frame.data.u8[0] & 0x40) >> 6); + else { + BATT_ERR_INDICATION = 0; +#ifdef DEBUG_LOG + logging.println("BATT_ERR_INDICATION not valid"); +#endif + } + if ((rx_frame.data.u8[0] & 0x20) == 0x20) { + BATT_T_MAX = ((rx_frame.data.u8[2] & 0x1F) * 256.0 + rx_frame.data.u8[3]); + BATT_T_MIN = ((rx_frame.data.u8[4] & 0x1F) * 256.0 + rx_frame.data.u8[5]); + BATT_T_AVG = ((rx_frame.data.u8[0] & 0x1F) * 256.0 + rx_frame.data.u8[1]); + } else { + BATT_T_MAX = 0; + BATT_T_MIN = 0; + BATT_T_AVG = 0; +#ifdef DEBUG_LOG + logging.println("BATT_T not valid"); +#endif + } + break; + case 0x369: + if ((rx_frame.data.u8[0] & 0x80) == 0x80) { + HvBattPwrLimDchaSoft = (((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[6]) >> 2); + } else { + HvBattPwrLimDchaSoft = 0; +#ifdef DEBUG_LOG + logging.println("HvBattPwrLimDchaSoft not valid"); +#endif + } + break; + case 0x175: + if ((rx_frame.data.u8[4] & 0x80) == 0x80) { + HvBattPwrLimDcha1 = (((rx_frame.data.u8[2] & 0x07) * 256 + rx_frame.data.u8[3]) >> 2); + } else { + HvBattPwrLimDcha1 = 0; + } + break; + case 0x177: + if ((rx_frame.data.u8[4] & 0x08) == 0x08) { + //HvBattPwrLimDchaSlowAgi = (((rx_frame.data.u8[4] & 0x07) * 256 + rx_frame.data.u8[5]) >> 2); + ; + } else { + //HvBattPwrLimDchaSlowAgi = 0; + ; + } + if ((rx_frame.data.u8[2] & 0x08) == 0x08) { + //HvBattPwrLimChrgSlowAgi = (((rx_frame.data.u8[2] & 0x07) * 256 + rx_frame.data.u8[3]) >> 2); + ; + } else { + //HvBattPwrLimChrgSlowAgi = 0; + ; + } + break; + case 0x37D: + if ((rx_frame.data.u8[0] & 0x40) == 0x40) { + SOC_BMS = ((rx_frame.data.u8[6] & 0x03) * 256 + rx_frame.data.u8[7]); + } else { + SOC_BMS = 0; +#ifdef DEBUG_LOG + logging.println("SOC_BMS not valid"); +#endif + } + + if ((rx_frame.data.u8[0] & 0x04) == 0x04) + //CELL_U_MAX = ((rx_frame.data.u8[2] & 0x01) * 256 + rx_frame.data.u8[3]); + ; + else { + //CELL_U_MAX = 0; + ; +#ifdef DEBUG_LOG + logging.println("CELL_U_MAX not valid"); +#endif + } + + if ((rx_frame.data.u8[0] & 0x02) == 0x02) + //CELL_U_MIN = ((rx_frame.data.u8[0] & 0x01) * 256.0 + rx_frame.data.u8[1]); + ; + else { + //CELL_U_MIN = 0; + ; +#ifdef DEBUG_LOG + logging.println("CELL_U_MIN not valid"); +#endif + } + + if ((rx_frame.data.u8[0] & 0x08) == 0x08) + //CELL_ID_U_MAX = ((rx_frame.data.u8[4] & 0x01) * 256.0 + rx_frame.data.u8[5]); + ; + else { + //CELL_ID_U_MAX = 0; + ; +#ifdef DEBUG_LOG + logging.println("CELL_ID_U_MAX not valid"); +#endif + } + break; + case 0x635: // Diag request response + if ((rx_frame.data.u8[0] == 0x07) && (rx_frame.data.u8[1] == 0x62) && (rx_frame.data.u8[2] == 0x49) && + (rx_frame.data.u8[3] == 0x6D)) // SOH response frame + { + datalayer.battery.status.soh_pptt = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]); + transmit_can_frame(&VOLVO_BECMsupplyVoltage_Req, can_config.battery); //Send BECM supply voltage req + } else if ((rx_frame.data.u8[0] == 0x05) && (rx_frame.data.u8[1] == 0x62) && (rx_frame.data.u8[2] == 0xF4) && + (rx_frame.data.u8[3] == 0x42)) // BECM module voltage supply + { + datalayer_extended.VolvoHybrid.BECMsupplyVoltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); + } else if ((rx_frame.data.u8[0] == 0x10) && (rx_frame.data.u8[2] == 0x62) && (rx_frame.data.u8[3] == 0x48) && + (rx_frame.data.u8[4] == 0x06)) // First response frame of cell voltages //changed + { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]); + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + rxConsecutiveFrames = 1; + } else if ((rx_frame.data.u8[0] == 0x10) && (rx_frame.data.u8[2] == 0x59) && + (rx_frame.data.u8[3] == 0x03)) // First response frame for DTC with more than one code + { + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x21) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x22) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x23) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x24) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x25) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x26) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x27) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x28) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x29) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x2A) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x2B) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x2C) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x2D) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x2E) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x2F) && (rxConsecutiveFrames == 1)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + rxConsecutiveFrames = 2; + } else if ((rx_frame.data.u8[0] == 0x20) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x21) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x22) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x23) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x24) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x25) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x26) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x27) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x28) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x29) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x2A) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x2B) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x2C) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[2]); + cell_voltages[battery_request_idx++] = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]); + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]; + cell_voltages[battery_request_idx] = (rx_frame.data.u8[7] << 8); + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + } else if ((rx_frame.data.u8[0] == 0x2D) && (rxConsecutiveFrames == 2)) { + cell_voltages[battery_request_idx++] = cell_voltages[battery_request_idx] | rx_frame.data.u8[1]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; + cell_voltages[battery_request_idx++] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + //cell_voltages[battery_request_idx++] = (rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]; + //transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + transmit_can_frame(&VOLVO_FlowControl, can_config.battery); // Send flow control + + if (false) // Run until last pack is read + { + //VOLVO_CELL_U_Req.data.u8[3] = batteryModuleNumber++; + //transmit_can_frame(&VOLVO_CELL_U_Req, can_config.battery); //Send cell voltage read request for next module + ; + } else { + min_max_voltage[0] = 9999; + min_max_voltage[1] = 0; + for (cellcounter = 0; cellcounter < 102; cellcounter++) { + if (min_max_voltage[0] > cell_voltages[cellcounter]) + min_max_voltage[0] = cell_voltages[cellcounter]; + if (min_max_voltage[1] < cell_voltages[cellcounter]) { + min_max_voltage[1] = cell_voltages[cellcounter]; + CELL_ID_U_MAX = cellcounter; + } + } + CELL_U_MAX = min_max_voltage[1]; + CELL_U_MIN = min_max_voltage[0]; + + transmit_can_frame(&VOLVO_SOH_Req, can_config.battery); //Send SOH read request + } + rxConsecutiveFrames = 0; + } + break; + default: + break; + } +} + +void readCellVoltages() { + battery_request_idx = 0; + //batteryModuleNumber = 0x10; + rxConsecutiveFrames = 0; + //VOLVO_CELL_U_Req.data.u8[3] = batteryModuleNumber++; + transmit_can_frame(&VOLVO_CELL_U_Req, can_config.battery); //Send cell voltage read request for first module +} + +void transmit_can_battery() { + unsigned long currentMillis = millis(); + // Send 100ms CAN Message + if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { + // Check if sending of CAN messages has been delayed too much. + if ((currentMillis - previousMillis100 >= INTERVAL_100_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) { + set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis100)); + } else { + clear_event(EVENT_CAN_OVERRUN); + } + previousMillis100 = currentMillis; + + transmit_can_frame(&VOLVO_536, can_config.battery); //Send 0x536 Network managing frame to keep BMS alive + transmit_can_frame(&VOLVO_372, can_config.battery); //Send 0x372 ECMAmbientTempCalculated + + if ((datalayer.battery.status.bms_status == ACTIVE) && startedUp) { + datalayer.system.status.battery_allows_contactor_closing = true; + //transmit_can_frame(&VOLVO_140_CLOSE, can_config.battery); //Send 0x140 Close contactors message + } else { //datalayer.battery.status.bms_status == FAULT , OR inverter requested opening contactors, OR system not started yet + datalayer.system.status.battery_allows_contactor_closing = false; + //transmit_can_frame(&VOLVO_140_OPEN, can_config.battery); //Send 0x140 Open contactors message + } + } + if (currentMillis - previousMillis1s >= INTERVAL_1_S) { + previousMillis1s = currentMillis; + + if (!startedUp) { + transmit_can_frame(&VOLVO_DTC_Erase, can_config.battery); //Erase any DTCs preventing startup + DTC_reset_counter++; + if (DTC_reset_counter > 1) { // Performed twice before starting + startedUp = true; + } + } + } + if (currentMillis - previousMillis60s >= INTERVAL_60_S) { + previousMillis60s = currentMillis; + if (true) { + readCellVoltages(); +#ifdef DEBUG_LOG + logging.println("Requesting cell voltages"); +#endif + } + } +} + +void setup_battery(void) { // Performs one time setup at startup + strncpy(datalayer.system.info.battery_protocol, "Volvo PHEV battery", 63); //changed + datalayer.system.info.battery_protocol[63] = '\0'; + datalayer.battery.info.number_of_cells = 102; //was 108, changed + 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; + datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; +} +#endif diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h new file mode 100644 index 000000000..1e3927054 --- /dev/null +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h @@ -0,0 +1,16 @@ +#ifndef VOLVO_SPA_HYBRID_BATTERY_H +#define VOLVO_SPA_HYBRID_BATTERY_H +#include +#include "../include.h" + +#define BATTERY_SELECTED +#define MAX_PACK_VOLTAGE_DV 4294 //5000 = 500.0V +#define MIN_PACK_VOLTAGE_DV 2754 +#define MAX_CELL_DEVIATION_MV 250 +#define MAX_CELL_VOLTAGE_MV 4210 //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 + +void setup_battery(void); +void transmit_can_frame(CAN_frame* tx_frame, int interface); + +#endif diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index ee2da57eb..fcb4b04ac 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -643,6 +643,36 @@ typedef struct { } DATALAYER_INFO_VOLVO_POLESTAR; +typedef struct { + uint16_t soc_bms = 0; + uint16_t soc_calc = 0; + uint16_t soc_rescaled = 0; + uint16_t soh_bms = 0; + uint16_t BECMsupplyVoltage = 0; + + uint16_t BECMBatteryVoltage = 0; + uint16_t BECMBatteryCurrent = 0; + uint16_t BECMUDynMaxLim = 0; + uint16_t BECMUDynMinLim = 0; + + uint16_t HvBattPwrLimDcha1 = 0; + uint16_t HvBattPwrLimDchaSoft = 0; + //uint16_t HvBattPwrLimDchaSlowAgi = 0; + //uint16_t HvBattPwrLimChrgSlowAgi = 0; + + uint8_t HVSysRlySts = 0; + uint8_t HVSysDCRlySts1 = 0; + uint8_t HVSysDCRlySts2 = 0; + uint8_t HVSysIsoRMonrSts = 0; + /** User requesting DTC reset via WebUI*/ + bool UserRequestDTCreset = false; + /** User requesting DTC readout via WebUI*/ + bool UserRequestDTCreadout = false; + /** User requesting BECM reset via WebUI*/ + bool UserRequestBECMecuReset = false; + +} DATALAYER_INFO_VOLVO_HYBRID; + typedef struct { /** uint16_t */ /** Values WIP*/ @@ -702,6 +732,7 @@ class DataLayerExtended { DATALAYER_INFO_NISSAN_LEAF nissanleaf; DATALAYER_INFO_MEB meb; DATALAYER_INFO_VOLVO_POLESTAR VolvoPolestar; + DATALAYER_INFO_VOLVO_HYBRID VolvoHybrid; DATALAYER_INFO_ZOE_PH2 zoePH2; }; diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index 19baf62c9..10421fefe 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -1321,10 +1321,100 @@ String advanced_battery_processor(const String& var) { content += ""; #endif // VOLVO_SPA_BATTERY +#ifdef VOLVO_SPA_HYBRID_BATTERY + content += "

BECM reported SOC: " + String(datalayer_extended.VolvoHybrid.soc_bms) + "

"; + content += "

Calculated SOC: " + String(datalayer_extended.VolvoHybrid.soc_calc) + "

"; + content += "

Rescaled SOC: " + String(datalayer_extended.VolvoHybrid.soc_rescaled / 10) + "

"; + content += "

BECM reported SOH: " + String(datalayer_extended.VolvoHybrid.soh_bms) + "

"; + content += "

BECM supply voltage: " + String(datalayer_extended.VolvoHybrid.BECMsupplyVoltage) + " mV

"; + + content += "

HV voltage: " + String(datalayer_extended.VolvoHybrid.BECMBatteryVoltage) + " V

"; + content += "

HV current: " + String(datalayer_extended.VolvoHybrid.BECMBatteryCurrent) + " A

"; + content += "

Dynamic max voltage: " + String(datalayer_extended.VolvoHybrid.BECMUDynMaxLim) + " V

"; + content += "

Dynamic min voltage: " + String(datalayer_extended.VolvoHybrid.BECMUDynMinLim) + " V

"; + + content += "

Discharge power limit 1: " + String(datalayer_extended.VolvoHybrid.HvBattPwrLimDcha1) + " kW

"; + content += + "

Discharge soft power limit: " + String(datalayer_extended.VolvoHybrid.HvBattPwrLimDchaSoft) + " kW

"; + + content += "

HV system relay status: "; + switch (datalayer_extended.VolvoHybrid.HVSysRlySts) { + case 0: + content += String("Open"); + break; + case 1: + content += String("Closed"); + break; + case 2: + content += String("KeepStatus"); + break; + case 3: + content += String("OpenAndRequestActiveDischarge"); + break; + default: + content += String("Not valid"); + } + content += "

HV system relay status 1: "; + switch (datalayer_extended.VolvoHybrid.HVSysDCRlySts1) { + case 0: + content += String("Open"); + break; + case 1: + content += String("Closed"); + break; + case 2: + content += String("KeepStatus"); + break; + case 3: + content += String("Fault"); + break; + default: + content += String("Not valid"); + } + content += "

HV system relay status 2: "; + switch (datalayer_extended.VolvoHybrid.HVSysDCRlySts2) { + case 0: + content += String("Open"); + break; + case 1: + content += String("Closed"); + break; + case 2: + content += String("KeepStatus"); + break; + case 3: + content += String("Fault"); + break; + default: + content += String("Not valid"); + } + content += "

HV system isolation resistance monitoring status: "; + switch (datalayer_extended.VolvoHybrid.HVSysIsoRMonrSts) { + case 0: + content += String("Not valid 1"); + break; + case 1: + content += String("False"); + break; + case 2: + content += String("True"); + break; + case 3: + content += String("Not valid 2"); + break; + default: + content += String("Not valid"); + } + + content += "


"; + content += "
"; + content += ""; +#endif // VOLVO_SPA_HYBRID_BATTERY + #if !defined(BMW_PHEV_BATTERY) && !defined(BMW_IX_BATTERY) && !defined(BOLT_AMPERA_BATTERY) && \ !defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \ !defined(BYD_ATTO_3_BATTERY) && !defined(RENAULT_ZOE_GEN2_BATTERY) && !defined(CELLPOWER_BMS) && \ - !defined(MEB_BATTERY) && !defined(VOLVO_SPA_BATTERY) && \ + !defined(MEB_BATTERY) && !defined(VOLVO_SPA_BATTERY) && !defined(VOLVO_SPA_HYBRID_BATTERY) && \ !defined(KIA_HYUNDAI_64_BATTERY) //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 7bdb686c1..64efbdd50 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -430,6 +430,33 @@ void init_webserver() { request->send(200, "text/plain", "Updated successfully"); }); + // Route for erasing DTC on Volvo hybrid batteries + server.on("/volvoEraseDTC", HTTP_GET, [](AsyncWebServerRequest* request) { + if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) { + return request->requestAuthentication(); + } + datalayer_extended.VolvoHybrid.UserRequestDTCreset = true; + request->send(200, "text/plain", "Updated successfully"); + }); + + // Route for reading DTC on Volvo hybrid batteries + server.on("/volvoReadDTC", HTTP_GET, [](AsyncWebServerRequest* request) { + if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) { + return request->requestAuthentication(); + } + datalayer_extended.VolvoHybrid.UserRequestDTCreadout = true; + request->send(200, "text/plain", "Updated successfully"); + }); + + // Route for performing ECU reset on Volvo hybrid batteries + server.on("/volvoBECMecuReset", HTTP_GET, [](AsyncWebServerRequest* request) { + if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) { + return request->requestAuthentication(); + } + datalayer_extended.VolvoHybrid.UserRequestBECMecuReset = true; + request->send(200, "text/plain", "Updated successfully"); + }); + #ifdef TEST_FAKE_BATTERY // Route for editing FakeBatteryVoltage server.on("/updateFakeBatteryVoltage", HTTP_GET, [](AsyncWebServerRequest* request) {