Skip to content

Commit

Permalink
Fixes for MEB battery support (#648)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mvgalen authored Dec 1, 2024
1 parent 5b2320b commit 7aaaa74
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 38 deletions.
37 changes: 26 additions & 11 deletions Software/Software.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
12 changes: 7 additions & 5 deletions Software/src/battery/MEB-BATTERY.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions Software/src/datalayer/datalayer_extended.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions Software/src/devboard/utils/events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions Software/src/devboard/utils/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
95 changes: 74 additions & 21 deletions Software/src/devboard/webserver/advanced_battery_html.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,27 +348,80 @@ String advanced_battery_processor(const String& var) {
content += datalayer_extended.meb.shutdown_active ? "<h4>Shutdown: Active!</h4>" : "<h4>Shutdown: No</h4>";
content += datalayer_extended.meb.componentprotection ? "<h4>Component protection: Active!</h4>"
: "<h4>Component protection: No</h4>";
const char* HVIL_status[] = {"Init", "Closed", "Open!", "Fault"};
content += "<h4>HVIL status: " + String(HVIL_status[datalayer_extended.meb.HVIL]) + "</h4>";
content += "<h4>KL30C status: " + String(HVIL_status[datalayer_extended.meb.BMS_Kl30c_Status]) + "</h4>";
const char* BMS_modes[] = {"HV inactive", "HV active", "Balancing", "Extern charging",
"AC charging", "Battery error", "DC charging", "Init"};
content += "<h4>BMS mode: " + String(BMS_modes[datalayer_extended.meb.BMS_mode]) + "</h4>";
const char* diagnostic_modes[] = {"Init", "Battery display", "", "", "Battery display OK",
"", "Display battery check", "Fault"};
content += "<h4>Diagnostic: " + String(diagnostic_modes[datalayer_extended.meb.battery_diagnostic]) + "</h4>";
const char* HV_line_status[] = {"Init", "No open HV line detected", "Open HV line", "Fault"};
content += "<h4>HV line status: " + String(HV_line_status[datalayer_extended.meb.status_HV_line]) + "</h4>";
const char* warning_support_status[] = {"OK", "Not OK", "", "", "", "", "Init", "Fault"};
content +=
"<h4>Warning support: " + String(warning_support_status[datalayer_extended.meb.warning_support]) + "</h4>";
const char* voltage_free_status[] = {"Init", "BMS interm circuit voltage free (U<20V)",
"BMS interm circuit not voltage free (U >= 25V)", "Error"};
content +=
"<h4>Voltage status: " + String(voltage_free_status[datalayer_extended.meb.BMS_status_voltage_free]) + "</h4>";
const char* bms_error_status[] = {"Component IO", "Iso Error 1", "Iso Error 2", "Interlock",
"SD", "Performance red", "No component function", "Init"};
content += "<h4>BMS error status: " + String(bms_error_status[datalayer_extended.meb.BMS_error_status]) + "</h4>";
content += "<h4>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 += "</h4><h4>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 += "</h4><h4>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 += "</h4><h4>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 += "</h4><h4>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 += "</h4><h4>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 += "</h4><h4>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 += "</h4><h4>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 += "</h4><h4>BMS voltage: " + String(datalayer_extended.meb.BMS_voltage_dV/10.0, 1) + "</h4>";
content += datalayer_extended.meb.BMS_OBD_MIL ? "<h4>OBD MIL: ON!</h4>" : "<h4>OBD MIL: Off</h4>";
content +=
datalayer_extended.meb.BMS_error_lamp_req ? "<h4>Red error lamp: ON!</h4>" : "<h4>Red error lamp: Off</h4>";
Expand Down
2 changes: 1 addition & 1 deletion Software/src/devboard/webserver/events_html.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7aaaa74

Please sign in to comment.