From 7aaaa7486f5b226f7d767504c730f7c3a484bcde Mon Sep 17 00:00:00 2001 From: mvgalen Date: Sun, 1 Dec 2024 20:49:01 +0100 Subject: [PATCH] Fixes for MEB battery support (#648) * Improve MEB support - Add FD CAN overrun checking (incl event) - Process multiple (max 16) CAN RX frames back-to-back to prevent CAN RX overruns - Make MEB extended battery page string handling robust - Add BMS_voltage and BMS_voltage intermediate to extended battery page - Fix temperature calculation - Fix isolation_resistance value - Fix kwh capacity bits - Fix no return value and never reached return statement. - Fix a few bit values - Fix bit position error in 0x1A55555B1 msg. * Change CAN printing format to CANdump format --- Software/Software.ino | 37 +++++--- Software/src/battery/MEB-BATTERY.cpp | 12 ++- Software/src/datalayer/datalayer_extended.h | 2 + Software/src/devboard/utils/events.cpp | 3 + Software/src/devboard/utils/events.h | 1 + .../webserver/advanced_battery_html.cpp | 95 +++++++++++++++---- .../src/devboard/webserver/events_html.cpp | 2 +- 7 files changed, 114 insertions(+), 38 deletions(-) diff --git a/Software/Software.ino b/Software/Software.ino index aa09175f..b7cf02bd 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -58,6 +58,7 @@ const char* version_number = "7.8.dev"; // Interval settings uint16_t intervalUpdateValues = INTERVAL_1_S; // Interval at which to update inverter values / Modbus registers unsigned long previousMillis10ms = 50; +unsigned long previousMillis1s = 0; unsigned long previousMillisUpdateVal = 0; // CAN parameters @@ -257,6 +258,20 @@ void core_loop(void* task_time_us) { receive_can_native(); // Receive CAN messages from native CAN port #ifdef CAN_FD receive_canfd(); // Receive CAN-FD messages. + if (millis() - previousMillis1s >= INTERVAL_1_S) { + previousMillis10ms = millis(); + int overflow = canfd.hardwareReceiveBufferOverflowCount(); + if (overflow > 0){ + set_event(EVENT_CANFD_RX_OVERRUN, overflow); + canfd.resetHardwareReceiveBufferOverflowCount(); + #ifdef DEBUG_CAN_DATA + Serial.print(millis()); + Serial.print(" RX overrun, lost "); + Serial.print(overflow); + Serial.println(" messages during 1s."); + #endif + } + } #endif #ifdef DUAL_CAN receive_can_addonMCP2515(); // Receive CAN messages on add-on MCP2515 chip @@ -592,35 +607,35 @@ enum frameDirection { MSG_RX, MSG_TX }; //RX = 0, TX = 1 void print_can_frame(CAN_frame frame, frameDirection msgDir); void print_can_frame(CAN_frame frame, frameDirection msgDir) { uint8_t i = 0; - Serial.print(millis()); - Serial.print(" "); - (msgDir == 0) ? Serial.print("RX ") : Serial.print("TX "); + Serial.print("("); + Serial.print(millis()/1000.0); + (msgDir == MSG_RX) ? Serial.print(") RX0 ") : Serial.print(") TX1 "); Serial.print(frame.ID, HEX); - Serial.print(" "); + Serial.print(" ["); Serial.print(frame.DLC); - Serial.print(" "); + Serial.print("] "); for (i = 0; i < frame.DLC; i++) { Serial.print(frame.data.u8[i] < 16 ? "0" : ""); Serial.print(frame.data.u8[i], HEX); - Serial.print(" "); + if (i < frame.DLC-1) + Serial.print(" "); } - Serial.println(" "); + Serial.println(""); } #ifdef CAN_FD // Functions void receive_canfd() { // This section checks if we have a complete CAN-FD message incoming CANFDMessage frame; - if (canfd.available()) { + int count = 0; + while (canfd.available() && count++ < 16) { canfd.receive(frame); CAN_frame rx_frame; rx_frame.ID = frame.id; rx_frame.ext_ID = frame.ext; rx_frame.DLC = frame.len; - for (uint8_t i = 0; i < rx_frame.DLC && i < 64; i++) { - rx_frame.data.u8[i] = frame.data[i]; - } + memcpy(rx_frame.data.u8, frame.data, MIN(rx_frame.DLC, 64)); //message incoming, pass it on to the handler receive_can(&rx_frame, CAN_ADDON_FD_MCP2518); receive_can(&rx_frame, CANFD_NATIVE); diff --git a/Software/src/battery/MEB-BATTERY.cpp b/Software/src/battery/MEB-BATTERY.cpp index 0e5aec9f..8fe7c492 100644 --- a/Software/src/battery/MEB-BATTERY.cpp +++ b/Software/src/battery/MEB-BATTERY.cpp @@ -533,9 +533,9 @@ 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 / 2) - 350); + datalayer.battery.status.temperature_min_dC = (battery_min_temp - 350) / 2; - datalayer.battery.status.temperature_max_dC = ((battery_max_temp / 2) - 350); + datalayer.battery.status.temperature_max_dC = (battery_max_temp - 350) / 2; //Map all cell voltages to the global array memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t)); @@ -589,7 +589,9 @@ void update_values_battery() { //This function maps all the values fetched via datalayer_extended.meb.BMS_error_lamp_req = BMS_error_lamp_req; datalayer_extended.meb.BMS_warning_lamp_req = BMS_warning_lamp_req; datalayer_extended.meb.BMS_Kl30c_Status = BMS_Kl30c_Status; - datalayer_extended.meb.isolation_resistance == isolation_resistance_kOhm * 5; + datalayer_extended.meb.BMS_voltage_intermediate_dV = (BMS_voltage_intermediate - 2000) * 10 / 2 ; + datalayer_extended.meb.BMS_voltage_dV = BMS_voltage * 10 / 4; + datalayer_extended.meb.isolation_resistance = isolation_resistance_kOhm * 5; datalayer_extended.meb.battery_heating = battery_heating_active; datalayer_extended.meb.rt_overcurrent = realtime_overcurrent_monitor; datalayer_extended.meb.rt_CAN_fault = realtime_CAN_communication_fault; @@ -890,7 +892,7 @@ void receive_can_battery(CAN_frame rx_frame) { case 0x1A5555B1: // BMS 1000ms cyclic // 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] & 0x0F) >> 1; + realtime_CAN_communication_fault = (rx_frame.data.u8[3] & 0x0E) >> 1; realtime_overcharge_warning = (rx_frame.data.u8[3] & 0x70) >> 4; realtime_SOC_too_high = ((rx_frame.data.u8[4] & 0x03) << 1) | rx_frame.data.u8[3] >> 7; realtime_SOC_too_low = (rx_frame.data.u8[4] & 0x1C) >> 2; @@ -929,7 +931,7 @@ void receive_can_battery(CAN_frame rx_frame) { BMS_status_voltage_free = (rx_frame.data.u8[1] & 0xC0) >> 6; BMS_OBD_MIL = (rx_frame.data.u8[2] & 0x01); BMS_error_status = (rx_frame.data.u8[2] & 0x70) >> 4; - BMS_capacity_ah = (rx_frame.data.u8[4] << 9) | (rx_frame.data.u8[3] << 1) | (rx_frame.data.u8[2] >> 7); + BMS_capacity_ah = (rx_frame.data.u8[4] & 0x03 << 9) | (rx_frame.data.u8[3] << 1) | (rx_frame.data.u8[2] >> 7); BMS_error_lamp_req = (rx_frame.data.u8[4] & 0x04) >> 2; BMS_warning_lamp_req = (rx_frame.data.u8[4] & 0x08) >> 3; BMS_Kl30c_Status = (rx_frame.data.u8[4] & 0x30) >> 4; diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index da123176..53b22483 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -312,6 +312,8 @@ typedef struct { bool BMS_OBD_MIL = 0; bool BMS_error_lamp_req = 0; bool BMS_warning_lamp_req = 0; + int32_t BMS_voltage_intermediate_dV = 0; + int32_t BMS_voltage_dV = 0; } DATALAYER_INFO_MEB; typedef struct { diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index 79584885..b0c3e285 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -144,6 +144,7 @@ void init_events(void) { events.entries[EVENT_CANMCP_INIT_FAILURE].level = EVENT_LEVEL_WARNING; events.entries[EVENT_CANFD_BUFFER_FULL].level = EVENT_LEVEL_WARNING; events.entries[EVENT_CAN_OVERRUN].level = EVENT_LEVEL_INFO; + events.entries[EVENT_CANFD_RX_OVERRUN].level = EVENT_LEVEL_WARNING; events.entries[EVENT_CAN_RX_FAILURE].level = EVENT_LEVEL_ERROR; events.entries[EVENT_CAN2_RX_FAILURE].level = EVENT_LEVEL_WARNING; events.entries[EVENT_CANFD_RX_FAILURE].level = EVENT_LEVEL_ERROR; @@ -270,6 +271,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) { return "CAN-FD buffer overflowed. Some CAN messages were not sent. Contact developers."; case EVENT_CAN_OVERRUN: return "CAN message failed to send within defined time. Contact developers, CPU load might be too high."; + case EVENT_CANFD_RX_OVERRUN: + return "CAN-FD failed to receive all messages from CAN bus. Contact developers, CPU load might be too high."; case EVENT_CAN_RX_FAILURE: return "No CAN communication detected for 60s. Shutting down battery control."; case EVENT_CAN2_RX_FAILURE: diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h index dcdcfd6a..b50d7de4 100644 --- a/Software/src/devboard/utils/events.h +++ b/Software/src/devboard/utils/events.h @@ -30,6 +30,7 @@ XX(EVENT_CANMCP_INIT_FAILURE) \ XX(EVENT_CANFD_BUFFER_FULL) \ XX(EVENT_CAN_OVERRUN) \ + XX(EVENT_CANFD_RX_OVERRUN) \ XX(EVENT_CAN_RX_FAILURE) \ XX(EVENT_CAN2_RX_FAILURE) \ XX(EVENT_CANFD_RX_FAILURE) \ diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index 9f83c34d..f3bba24c 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -348,27 +348,80 @@ String advanced_battery_processor(const String& var) { content += datalayer_extended.meb.shutdown_active ? "

Shutdown: Active!

" : "

Shutdown: No

"; content += datalayer_extended.meb.componentprotection ? "

Component protection: Active!

" : "

Component protection: No

"; - const char* HVIL_status[] = {"Init", "Closed", "Open!", "Fault"}; - content += "

HVIL status: " + String(HVIL_status[datalayer_extended.meb.HVIL]) + "

"; - content += "

KL30C status: " + String(HVIL_status[datalayer_extended.meb.BMS_Kl30c_Status]) + "

"; - const char* BMS_modes[] = {"HV inactive", "HV active", "Balancing", "Extern charging", - "AC charging", "Battery error", "DC charging", "Init"}; - content += "

BMS mode: " + String(BMS_modes[datalayer_extended.meb.BMS_mode]) + "

"; - const char* diagnostic_modes[] = {"Init", "Battery display", "", "", "Battery display OK", - "", "Display battery check", "Fault"}; - content += "

Diagnostic: " + String(diagnostic_modes[datalayer_extended.meb.battery_diagnostic]) + "

"; - const char* HV_line_status[] = {"Init", "No open HV line detected", "Open HV line", "Fault"}; - content += "

HV line status: " + String(HV_line_status[datalayer_extended.meb.status_HV_line]) + "

"; - const char* warning_support_status[] = {"OK", "Not OK", "", "", "", "", "Init", "Fault"}; - content += - "

Warning support: " + String(warning_support_status[datalayer_extended.meb.warning_support]) + "

"; - const char* voltage_free_status[] = {"Init", "BMS interm circuit voltage free (U<20V)", - "BMS interm circuit not voltage free (U >= 25V)", "Error"}; - content += - "

Voltage status: " + String(voltage_free_status[datalayer_extended.meb.BMS_status_voltage_free]) + "

"; - const char* bms_error_status[] = {"Component IO", "Iso Error 1", "Iso Error 2", "Interlock", - "SD", "Performance red", "No component function", "Init"}; - content += "

BMS error status: " + String(bms_error_status[datalayer_extended.meb.BMS_error_status]) + "

"; + content += "

HVIL status: "; + switch (datalayer_extended.meb.HVIL){ + case 0: content+= String("Init"); break; + case 1: content+= String("Closed"); break; + case 2: content+= String("Open!"); break; + case 3: content+= String("Fault"); break; + default: content += String("?"); + } + content += "

KL30C status: "; + switch (datalayer_extended.meb.BMS_Kl30c_Status){ + case 0: content+= String("Init"); break; + case 1: content+= String("Closed"); break; + case 2: content+= String("Open!"); break; + case 3: content+= String("Fault"); break; + default: content += String("?"); + } + content += "

BMS mode: "; + switch (datalayer_extended.meb.BMS_mode){ + case 0: content+= String("HV inactive"); break; + case 1: content+= String("HV active"); break; + case 2: content+= String("Balancing"); break; + case 3: content+= String("Extern charging"); break; + case 4: content+= String("AC charging"); break; + case 5: content+= String("Battery error"); break; + case 6: content+= String("DC charging"); break; + case 7: content+= String("Init"); break; + default: content += String("?"); + } + content += "

Diagnostic: "; + switch (datalayer_extended.meb.battery_diagnostic){ + case 0: content+= String("Init"); break; + case 1: content+= String("Battery display"); break; + case 4: content+= String("Battery display OK"); break; + case 6: content+= String("Battery display check"); break; + case 7: content+= String("Fault"); break; + default: content += String("?"); + } + content += "

HV line status: "; + switch (datalayer_extended.meb.status_HV_line){ + case 0: content+= String("Init"); break; + case 1: content+= String("No open HV line detected"); break; + case 2: content+= String("Open HV line"); break; + case 3: content+= String("Fault"); break; + default: content += String("? ") + String(datalayer_extended.meb.status_HV_line); + } + content += "

Warning support: "; + switch (datalayer_extended.meb.warning_support){ + case 0: content+= String("OK"); break; + case 1: content+= String("Not OK"); break; + case 6: content+= String("Init"); break; + case 7: content+= String("Fault"); break; + default: content += String("?"); + } + content += "

Interm. Voltage ("+ String(datalayer_extended.meb.BMS_voltage_intermediate_dV/10.0, 1)+"V) status: "; + switch (datalayer_extended.meb.BMS_status_voltage_free){ + case 0: content+= String("Init"); break; + case 1: content+= String("BMS interm circuit voltage free (U<20V)"); break; + case 2: content+= String("BMS interm circuit not voltage free (U >= 25V)"); break; + case 3: content+= String("Error"); break; + default: content += String("?"); + } + content += "

BMS error status: "; + switch (datalayer_extended.meb.BMS_error_status){ + case 0: content+= String("Component IO"); break; + case 1: content+= String("Iso Error 1"); break; + case 2: content+= String("Iso Error 2"); break; + case 3: content+= String("Interlock"); break; + case 4: content+= String("SD"); break; + case 5: content+= String("Performance red"); break; + case 6: content+= String("No component function"); break; + case 7: content+= String("Init"); break; + default: content += String("?"); + } + content += "

BMS voltage: " + String(datalayer_extended.meb.BMS_voltage_dV/10.0, 1) + "

"; content += datalayer_extended.meb.BMS_OBD_MIL ? "

OBD MIL: ON!

" : "

OBD MIL: Off

"; content += datalayer_extended.meb.BMS_error_lamp_req ? "

Red error lamp: ON!

" : "

Red error lamp: Off

"; diff --git a/Software/src/devboard/webserver/events_html.cpp b/Software/src/devboard/webserver/events_html.cpp index 11ff6a7f..df2c056a 100644 --- a/Software/src/devboard/webserver/events_html.cpp +++ b/Software/src/devboard/webserver/events_html.cpp @@ -58,8 +58,8 @@ String events_processor(const String& var) { order_events.clear(); content.concat(FPSTR(EVENTS_HTML_END)); return content; - return String(); } + return String(); } /* Script for displaying event log before it gets minified