MEB: Improve MEB status output. (#983)

* Add 4 missing status outputs from battery to MEB more battery page.
BMS_fault_performance
BMS_fault_emergency_shutdown_crash
BMS_error_shutdown_request
BMS_error_shutdown
* Adds SOH calculation.
* Fixes: (hopefully) the capacity calculation from Ah to kWh.
* Only update capacity and soh if number of cells is set. Remove incorrect scaling on Wh_max.

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Marijn van Galen 2025-03-20 16:34:33 +01:00 committed by GitHub
parent f4613ae2d4
commit e6a6ce9925
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 50 additions and 13 deletions

View file

@ -540,14 +540,27 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100 datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100
datalayer.battery.status.soh_pptt;
datalayer.battery.status.voltage_dV = BMS_voltage * 2.5; // *0.25*10 datalayer.battery.status.voltage_dV = BMS_voltage * 2.5; // *0.25*10
datalayer.battery.status.current_dA = (BMS_current - 16300); // 0.1 * 10 datalayer.battery.status.current_dA = (BMS_current - 16300); // 0.1 * 10
if (nof_cells_determined) {
datalayer.battery.info.total_capacity_Wh = datalayer.battery.info.total_capacity_Wh =
((float)datalayer.battery.info.number_of_cells) * 3.6458 * ((float)BMS_capacity_ah) * 0.2 * 1.13; ((float)datalayer.battery.info.number_of_cells) * 3.67 * ((float)BMS_capacity_ah) * 0.2 * 1.02564;
// The factor 1.02564 = 1/0.975 is to correct for bottom 2.5% which is reported by the remaining_capacity_Wh,
// but which is not actually usable, but if we do not include it, the remaining_capacity_Wh can be larger than
// the total_capacity_Wh.
// 0.935 and 0.9025 are the different conversions for different battery sizes to go from design capacity to
// total_capacity_Wh calculated above.
int Wh_max = 61832 * 0.935; // 108 cells
if (datalayer.battery.info.number_of_cells <= 84)
Wh_max = 48091 * 0.9025;
else if (datalayer.battery.info.number_of_cells <= 96)
Wh_max = 82442 * 0.9025;
if (BMS_capacity_ah > 0)
datalayer.battery.status.soh_pptt = 10000 * datalayer.battery.info.total_capacity_Wh / Wh_max;
}
datalayer.battery.status.remaining_capacity_Wh = usable_energy_amount_Wh * 5; datalayer.battery.status.remaining_capacity_Wh = usable_energy_amount_Wh * 5;
@ -589,6 +602,11 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer_extended.meb.BMS_mode = BMS_mode; datalayer_extended.meb.BMS_mode = BMS_mode;
datalayer_extended.meb.battery_diagnostic = battery_diagnostic; datalayer_extended.meb.battery_diagnostic = battery_diagnostic;
datalayer_extended.meb.status_HV_line = status_HV_line; datalayer_extended.meb.status_HV_line = status_HV_line;
datalayer_extended.meb.BMS_fault_performance = BMS_fault_performance;
datalayer_extended.meb.BMS_fault_emergency_shutdown_crash = BMS_fault_emergency_shutdown_crash;
datalayer_extended.meb.BMS_error_shutdown_request = BMS_error_shutdown_request;
datalayer_extended.meb.BMS_error_shutdown = BMS_error_shutdown;
datalayer_extended.meb.warning_support = warning_support; datalayer_extended.meb.warning_support = warning_support;
datalayer_extended.meb.BMS_status_voltage_free = BMS_status_voltage_free; datalayer_extended.meb.BMS_status_voltage_free = BMS_status_voltage_free;
datalayer_extended.meb.BMS_OBD_MIL = BMS_OBD_MIL; datalayer_extended.meb.BMS_OBD_MIL = BMS_OBD_MIL;

View file

@ -585,6 +585,14 @@ typedef struct {
uint8_t status_HV_line = 0; uint8_t status_HV_line = 0;
/** uint8_t */ /** uint8_t */
/** 0 = OK, 1 = Not OK, 0x06 = init, 0x07 = fault */ /** 0 = OK, 1 = Not OK, 0x06 = init, 0x07 = fault */
bool BMS_fault_performance = false; //Error: Battery performance is limited (e.g. due to sensor or fan failure)
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
bool BMS_error_shutdown_request =
false; // Fault: Fault condition, requires battery contactors to be opened internal battery error; Advance notification of an impending opening of the battery contactors by the BMS
bool BMS_error_shutdown =
false; // Fault: Fault condition, requires battery contactors to be opened Internal battery error, battery contactors opened without notice by the BMS
uint8_t warning_support = 0; uint8_t warning_support = 0;
/** uint32_t */ /** uint32_t */
/** Isolation resistance in kOhm */ /** Isolation resistance in kOhm */

View file

@ -1062,7 +1062,18 @@ String advanced_battery_processor(const String& var) {
default: default:
content += String("? ") + String(datalayer_extended.meb.status_HV_line); content += String("? ") + String(datalayer_extended.meb.status_HV_line);
} }
content += "</h4><h4>Warning support: "; content += "</h4>";
content += datalayer_extended.meb.BMS_fault_performance ? "<h4>BMS fault performance: Active!</h4>"
: "<h4>BMS fault performance: Off</h4>";
content += datalayer_extended.meb.BMS_fault_emergency_shutdown_crash
? "<h4>BMS fault emergency shutdown crash: Active!</h4>"
: "<h4>BMS fault emergency shutdown crash: Off</h4>";
content += datalayer_extended.meb.BMS_error_shutdown_request ? "<h4>BMS error shutdown request: Active!</h4>"
: "<h4>BMS error shutdown request: Inactive</h4>";
content += datalayer_extended.meb.BMS_error_shutdown ? "<h4>BMS error shutdown: Active!</h4>"
: "<h4>BMS error shutdown: Off</h4>";
content += "<h4>Warning support: ";
switch (datalayer_extended.meb.warning_support) { switch (datalayer_extended.meb.warning_support) {
case 0: case 0:
content += String("OK"); content += String("OK");

View file

@ -1054,13 +1054,13 @@ String processor(const String& var) {
uint16_t cell_delta_mv = uint16_t cell_delta_mv =
datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV; datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV;
content += "<h4 style='color: white;'>Real SOC: " + String(socRealFloat, 2) + "</h4>"; content += "<h4 style='color: white;'>Real SOC: " + String(socRealFloat, 2) + "&percnt;</h4>";
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) + "</h4>"; content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) + "&percnt;</h4>";
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "</h4>"; content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "&percnt;</h4>";
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) + " V</h4>"; content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) + " V</h4>";
content += "<h4 style='color: white;'>Current: " + String(currentFloat, 1) + " A</h4>"; content += "<h4 style='color: white;'>Current: " + String(currentFloat, 1) + " A</h4>";
content += formatPowerValue("Power", powerFloat, "", 1); content += formatPowerValue("Power", powerFloat, "", 1);
content += formatPowerValue("Total capacity", datalayer.battery.info.total_capacity_Wh, "h", 0); content += formatPowerValue("Total capacity", datalayer.battery.info.total_capacity_Wh, "h", 1);
content += formatPowerValue("Real Remaining capacity", datalayer.battery.status.remaining_capacity_Wh, "h", 1); content += formatPowerValue("Real Remaining capacity", datalayer.battery.status.remaining_capacity_Wh, "h", 1);
content += content +=
formatPowerValue("Scaled Remaining capacity", datalayer.battery.status.reported_remaining_capacity_Wh, "h", 1); formatPowerValue("Scaled Remaining capacity", datalayer.battery.status.reported_remaining_capacity_Wh, "h", 1);
@ -1261,13 +1261,13 @@ String processor(const String& var) {
tempMinFloat = static_cast<float>(datalayer.battery2.status.temperature_min_dC) / 10.0; // Convert to float tempMinFloat = static_cast<float>(datalayer.battery2.status.temperature_min_dC) / 10.0; // Convert to float
cell_delta_mv = datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV; cell_delta_mv = datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV;
content += "<h4 style='color: white;'>Real SOC: " + String(socRealFloat, 2) + "</h4>"; content += "<h4 style='color: white;'>Real SOC: " + String(socRealFloat, 2) + "&percnt;</h4>";
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) + "</h4>"; content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) + "&percnt;</h4>";
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "</h4>"; content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "&percnt;</h4>";
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) + " V</h4>"; content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) + " V</h4>";
content += "<h4 style='color: white;'>Current: " + String(currentFloat, 1) + " A</h4>"; content += "<h4 style='color: white;'>Current: " + String(currentFloat, 1) + " A</h4>";
content += formatPowerValue("Power", powerFloat, "", 1); content += formatPowerValue("Power", powerFloat, "", 1);
content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 0); content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 1);
content += formatPowerValue("Real Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1); content += formatPowerValue("Real Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1);
content += content +=
formatPowerValue("Scaled Remaining capacity", datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1); formatPowerValue("Scaled Remaining capacity", datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1);