From e38b6286c75f090362a2c0638aaa148ec6dd6c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Wed, 3 Sep 2025 23:25:30 +0300 Subject: [PATCH] Add initial support for 50/75kWh vans --- Software/src/battery/ECMP-BATTERY.cpp | 155 +++++++++++++++++++++----- Software/src/battery/ECMP-BATTERY.h | 5 +- 2 files changed, 130 insertions(+), 30 deletions(-) diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index 536f4b90..456eb7b0 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -12,44 +12,86 @@ This integration is still ongoing. Here is what still needs to be done in order /* Do not change code below unless you are sure what you are doing */ void EcmpBattery::update_values() { - datalayer.battery.status.real_soc = battery_soc * 10; + if (!MysteryVan) { //Normal eCMP platform + datalayer.battery.status.real_soc = battery_soc * 10; - datalayer.battery.status.soh_pptt; + datalayer.battery.status.soh_pptt; - datalayer.battery.status.voltage_dV = battery_voltage * 10; + datalayer.battery.status.voltage_dV = battery_voltage * 10; - datalayer.battery.status.current_dA = -(battery_current * 10); + datalayer.battery.status.current_dA = -(battery_current * 10); - datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt - ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); + datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt + ((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100); - datalayer.battery.status.max_charge_power_W = battery_AllowedMaxChargeCurrent * battery_voltage; + datalayer.battery.status.max_charge_power_W = battery_AllowedMaxChargeCurrent * battery_voltage; - datalayer.battery.status.max_discharge_power_W = battery_AllowedMaxDischargeCurrent * battery_voltage; + datalayer.battery.status.max_discharge_power_W = battery_AllowedMaxDischargeCurrent * battery_voltage; - datalayer.battery.status.temperature_min_dC = battery_lowestTemperature * 10; + datalayer.battery.status.temperature_min_dC = battery_lowestTemperature * 10; - datalayer.battery.status.temperature_max_dC = battery_highestTemperature * 10; + datalayer.battery.status.temperature_max_dC = battery_highestTemperature * 10; - // Initialize min and max, lets find which cells are min and max! - uint16_t min_cell_mv_value = std::numeric_limits::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 < datalayer.battery.info.number_of_cells; ++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); + // Initialize min and max, lets find which cells are min and max! + uint16_t min_cell_mv_value = std::numeric_limits::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 < datalayer.battery.info.number_of_cells; ++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::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; + } else { //Some variant of the 50/75kWh battery that is not using the eCMP CAN mappings. + // For these batteries we need to use the OBD2 PID polled values + datalayer.battery.status.real_soc = battery_soc * 10; //TTOD, calculate based on cap remaining? + + datalayer.battery.status.soh_pptt; + + if (pid_pack_voltage != NOT_SAMPLED_YET) { + datalayer.battery.status.voltage_dV = pid_pack_voltage; + } + + if (pid_current != NOT_SAMPLED_YET) { + datalayer.battery.status.current_dA = (int16_t)(pid_current / 100); + + datalayer.battery.status.active_power_W = + (uint16_t)((pid_current / 1000.0f) * (datalayer.battery.status.voltage_dV / 10.0f)); + } + + if (pid_max_charge_10s != NOT_SAMPLED_YET) { + datalayer.battery.status.max_charge_power_W = pid_max_charge_10s; + } + + if (pid_max_discharge_10s != NOT_SAMPLED_YET) { + datalayer.battery.status.max_discharge_power_W = pid_max_discharge_10s; + } + + if (pid_lowest_temperature != NOT_SAMPLED_YET) { + datalayer.battery.status.temperature_min_dC = pid_lowest_temperature * 10; + } + + if (pid_highest_temperature != NOT_SAMPLED_YET) { + datalayer.battery.status.temperature_max_dC = pid_highest_temperature * 10; + } + + if (pid_high_cell_voltage != NOT_SAMPLED_YET) { + datalayer.battery.status.cell_max_voltage_mV = pid_high_cell_voltage; + } + + if (pid_low_cell_voltage != NOT_SAMPLED_YET) { + datalayer.battery.status.cell_min_voltage_mV = pid_low_cell_voltage; } } - // If all array values are 0, reset min/max to 3700 - if (min_cell_mv_value == std::numeric_limits::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; // Update extended datalayer (More Battery Info page) datalayer_extended.stellantisECMP.MainConnectorState = battery_MainConnectorState; @@ -145,9 +187,58 @@ void EcmpBattery::update_values() { } void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; switch (rx_frame.ID) { + case 0x2D4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + MysteryVan = true; + break; + case 0x3B4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x2F4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x3F4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x554: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x373: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4F4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x414: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x353: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x474: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x574: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x583: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x314: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x254: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x2B4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; + case 0x4D4: //MysteryVan 50/75kWh platform + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + break; case 0x125: //Common + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_soc = (rx_frame.data.u8[0] << 2) | (rx_frame.data.u8[1] >> 6); // Byte1, bit 7 length 10 (0x3FE when abnormal) (0-1000 ppt) battery_MainConnectorState = ((rx_frame.data.u8[2] & 0x18) >> @@ -157,6 +248,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { battery_current = (((rx_frame.data.u8[4] & 0x0F) << 8) | rx_frame.data.u8[5]) - 600; // TODO: Test break; case 0x127: //DFM specific + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_AllowedMaxChargeCurrent = (rx_frame.data.u8[0] << 2) | ((rx_frame.data.u8[1] & 0xC0) >> 6); //Byte 1, bit 7, length 10 (0-600A) [0x3FF if invalid] @@ -165,12 +257,15 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { (rx_frame.data.u8[3] >> 4); //Byte 2, bit 5, length 10 (0-600A) [0x3FF if invalid] break; case 0x129: //PSA specific + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; case 0x31B: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_InterlockOpen = ((rx_frame.data.u8[1] & 0x10) >> 4); //Best guess, seems to work? //TODO: frame7 contains checksum, we can use this to check for CAN message corruption break; case 0x358: //Common + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_highestTemperature = rx_frame.data.u8[6] - 40; battery_lowestTemperature = rx_frame.data.u8[7] - 40; break; @@ -185,11 +280,13 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { case 0x494: break; case 0x594: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_insulation_failure_diag = ((rx_frame.data.u8[6] & 0xE0) >> 5); //Unsure if this is right position //byte pos 6, bit pos 7, signal lenth 3 //0 = no failure, 1 = symmetric failure, 4 = invalid value , forbidden value 5-7 break; case 0x6D0: //Common + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_insulationResistanceKOhm = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; //Byte 2, bit 7, length 16 (0-60000 kOhm) break; @@ -198,6 +295,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { case 0x6D2: break; case 0x6D3: + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; cellvoltages[0] = (rx_frame.data.u8[0] << 8) | rx_frame.data.u8[1]; cellvoltages[1] = (rx_frame.data.u8[2] << 8) | rx_frame.data.u8[3]; cellvoltages[2] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; @@ -374,6 +472,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) { memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages, 108 * sizeof(uint16_t)); break; case 0x694: // Poll reply + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; // Handle user requested functionality first if ongoing if (datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring) { diff --git a/Software/src/battery/ECMP-BATTERY.h b/Software/src/battery/ECMP-BATTERY.h index fe4aa58a..201f59d8 100644 --- a/Software/src/battery/ECMP-BATTERY.h +++ b/Software/src/battery/ECMP-BATTERY.h @@ -38,6 +38,7 @@ class EcmpBattery : public CanBattery { static const int COMPLETED_STATE = 0; bool battery_RelayOpenRequest = false; bool battery_InterlockOpen = false; + bool MysteryVan = false; uint8_t ContactorResetStatemachine = 0; uint8_t CollisionResetStatemachine = 0; uint8_t IsolationResetStatemachine = 0; @@ -80,8 +81,8 @@ class EcmpBattery : public CanBattery { uint8_t pid_coldest_module = NOT_SAMPLED_YET; uint8_t pid_lowest_temperature = NOT_SAMPLED_YET; uint8_t pid_average_temperature = NOT_SAMPLED_YET; - uint8_t pid_highest_temperature = NOT_SAMPLED_YET; - uint8_t pid_hottest_module = NOT_SAMPLED_YET; + int8_t pid_highest_temperature = NOT_SAMPLED_YET; + int8_t pid_hottest_module = NOT_SAMPLED_YET; uint16_t pid_avg_cell_voltage = NOT_SAMPLED_YET; int32_t pid_current = NOT_SAMPLED_YET; uint32_t pid_insulation_res_neg = NOT_SAMPLED_YET;