#include "TESLA-BATTERY.h" #include //For unit test #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For Advanced Battery Insights webpage #include "../devboard/utils/events.h" #include "../devboard/utils/logging.h" /* Credits: */ /* Some of the original CAN frame parsing code below comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */ /* Most of the additional CAN frame parsing/information/display comes from Josiah Higgs (https://github.com/josiahhiggs/) */ inline const char* getContactorText(int index) { switch (index) { case 0: return "UNKNOWN(0)"; case 1: return "OPEN"; case 2: return "CLOSING"; case 3: return "BLOCKED"; case 4: return "OPENING"; case 5: return "CLOSED"; case 6: return "UNKNOWN(6)"; case 7: return "WELDED"; case 8: return "POS_CL"; case 9: return "NEG_CL"; case 10: return "UNKNOWN(10)"; case 11: return "UNKNOWN(11)"; case 12: return "UNKNOWN(12)"; default: return "UNKNOWN"; } } inline const char* getContactorState(int index) { switch (index) { case 0: return "SNA"; case 1: return "OPEN"; case 2: return "PRECHARGE"; case 3: return "BLOCKED"; case 4: return "PULLED_IN"; case 5: return "OPENING"; case 6: return "ECONOMIZED"; case 7: return "WELDED"; case 8: return "UNKNOWN(8)"; case 9: return "UNKNOWN(9)"; case 10: return "UNKNOWN(10)"; case 11: return "UNKNOWN(11)"; default: return "UNKNOWN"; } } inline const char* getHvilStatusState(int index) { switch (index) { case 0: return "NOT OK"; case 1: return "STATUS_OK"; case 2: return "CURRENT_SOURCE_FAULT"; case 3: return "INTERNAL_OPEN_FAULT"; case 4: return "VEHICLE_OPEN_FAULT"; case 5: return "PENTHOUSE_LID_OPEN_FAULT"; case 6: return "UNKNOWN_LOCATION_OPEN_FAULT"; case 7: return "VEHICLE_NODE_FAULT"; case 8: return "NO_12V_SUPPLY"; case 9: return "VEHICLE_OR_PENTHOUSE_LID_OPENFAULT"; case 10: return "UNKNOWN(10)"; case 11: return "UNKNOWN(11)"; case 12: return "UNKNOWN(12)"; case 13: return "UNKNOWN(13)"; case 14: return "UNKNOWN(14)"; case 15: return "UNKNOWN(15)"; default: return "UNKNOWN"; } } inline const char* getBMSState(int index) { switch (index) { case 0: return "STANDBY"; case 1: return "DRIVE"; case 2: return "SUPPORT"; case 3: return "CHARGE"; case 4: return "FEIM"; case 5: return "CLEAR_FAULT"; case 6: return "FAULT"; case 7: return "WELD"; case 8: return "TEST"; case 9: return "SNA"; default: return "UNKNOWN"; } } inline const char* getBMSContactorState(int index) { switch (index) { case 0: return "SNA"; case 1: return "OPEN"; case 2: return "OPENING"; case 3: return "CLOSING"; case 4: return "CLOSED"; case 5: return "WELDED"; case 6: return "BLOCKED"; default: return "UNKNOWN"; } } inline const char* getBMSHvState(int index) { switch (index) { case 0: return "DOWN"; case 1: return "COMING_UP"; case 2: return "GOING_DOWN"; case 3: return "UP_FOR_DRIVE"; case 4: return "UP_FOR_CHARGE"; case 5: return "UP_FOR_DC_CHARGE"; case 6: return "UP"; default: return "UNKNOWN"; } } inline const char* getBMSUiChargeStatus(int index) { switch (index) { case 0: return "DISCONNECTED"; case 1: return "NO_POWER"; case 2: return "ABOUT_TO_CHARGE"; case 3: return "CHARGING"; case 4: return "CHARGE_COMPLETE"; case 5: return "CHARGE_STOPPED"; default: return "UNKNOWN"; } } inline const char* getPCS_DcdcStatus(int index) { switch (index) { case 0: return "IDLE"; case 1: return "ACTIVE"; case 2: return "FAULTED"; default: return "UNKNOWN"; } } inline const char* getPCS_DcdcMainState(int index) { switch (index) { case 0: return "STANDBY"; case 1: return "12V_SUPPORT_ACTIVE"; case 2: return "PRECHARGE_STARTUP"; case 3: return "PRECHARGE_ACTIVE"; case 4: return "DIS_HVBUS_ACTIVE"; case 5: return "SHUTDOWN"; case 6: return "FAULTED"; default: return "UNKNOWN"; } } inline const char* getPCS_DcdcSubState(int index) { switch (index) { case 0: return "PWR_UP_INIT"; case 1: return "STANDBY"; case 2: return "12V_SUPPORT_ACTIVE"; case 3: return "DIS_HVBUS"; case 4: return "PCHG_FAST_DIS_HVBUS"; case 5: return "PCHG_SLOW_DIS_HVBUS"; case 6: return "PCHG_DWELL_CHARGE"; case 7: return "PCHG_DWELL_WAIT"; case 8: return "PCHG_DI_RECOVERY_WAIT"; case 9: return "PCHG_ACTIVE"; case 10: return "PCHG_FLT_FAST_DIS_HVBUS"; case 11: return "SHUTDOWN"; case 12: return "12V_SUPPORT_FAULTED"; case 13: return "DIS_HVBUS_FAULTED"; case 14: return "PCHG_FAULTED"; case 15: return "CLEAR_FAULTS"; case 16: return "FAULTED"; case 17: return "NUM"; default: return "UNKNOWN"; } } inline const char* getBMSPowerLimitState(int index) { switch (index) { case 0: return "NOT_CALCULATED_FOR_DRIVE"; case 1: return "CALCULATED_FOR_DRIVE"; default: return "UNKNOWN"; } } inline const char* getHVPStatus(int index) { switch (index) { case 0: return "INVALID"; case 1: return "NOT_AVAILABLE"; case 2: return "STALE"; case 3: return "VALID"; default: return "UNKNOWN"; } } inline const char* getHVPContactor(int index) { switch (index) { case 0: return "NOT_ACTIVE"; case 1: return "ACTIVE"; case 2: return "COMPLETED"; default: return "UNKNOWN"; } } inline const char* getFalseTrue(bool value) { return value ? "True" : "False"; } inline const char* getNoYes(bool value) { return value ? "Yes" : "No"; } inline const char* getFault(bool value) { return value ? "ACTIVE" : "NOT_ACTIVE"; } // Clamp DLC to 0–8 bytes for classic CAN inline int getDataLen(uint8_t dlc) { return std::min(dlc, 8); } // Fast bit‐field writer: writes 'bitLen' bits of 'value' starting at 'startBit' inline void setBitField(uint8_t* data, int bytes, int startBit, int bitLen, uint64_t value) { int bit = startBit; for (int i = 0; i < bitLen && bit < bytes * 8; ++i, ++bit) { uint8_t* p = data + (bit >> 3); uint8_t m = uint8_t(1u << (bit & 7)); *p = (*p & ~m) | (uint8_t((value >> i) & 1) << (bit & 7)); } } /// Increment a counter field and recompute an 8‑bit checksum as part of a mux void generateMuxFrameCounterChecksum(CAN_frame& f, uint8_t frameCounter, // counter value int ctrStartBit, // bit index of counter LSB int ctrBitLength, // width of counter in bits int csumStartBit, // bit index of checksum LSB int csumBitLength // width of checksum in bits ) { int bytes = getDataLen(f.DLC); auto data = f.data.u8; // Pack payload into a 64‑bit word uint64_t w = 0; for (uint8_t i = 0; i < bytes; ++i) { w |= uint64_t(data[i]) << (8 * i); } // Increment the counter { uint64_t mask = (uint64_t(1) << ctrBitLength) - 1; uint64_t ctr = frameCounter & mask; // External counter 0-15 w = (w & ~(mask << ctrStartBit)) | (ctr << ctrStartBit); } // Unpack back into the frame bytes for (uint8_t i = 0; i < bytes; ++i) { data[i] = uint8_t((w >> (8 * i)) & 0xFF); } // Build a small buffer and zero out the checksum bits uint8_t buf[8]; for (uint8_t i = 0; i < bytes; ++i) { buf[i] = data[i]; } for (int bit = csumStartBit; bit < csumStartBit + csumBitLength; ++bit) { int b = bit >> 3; if (b >= bytes) break; buf[b] &= uint8_t(~(1u << (bit & 7))); } // Compute checksum offset from most significant hex digit of CAN ID uint8_t checksum_offset = uint8_t((f.ID >> 8) & 0xF); // high nibble of top byte // Sum the low byte of ID + buf[] uint8_t sum = uint8_t(f.ID & 0xFF); for (int i = 0; i < bytes; ++i) { sum = uint8_t(sum + buf[i]); } uint8_t checksum = uint8_t(sum + checksum_offset); // Write the checksum back into the frame setBitField(data, bytes, csumStartBit, csumBitLength, checksum); } // Increment a counter field and recompute an 8‑bit checksum void generateFrameCounterChecksum(CAN_frame& f, int ctrStartBit, // bit index of counter LSB int ctrBitLength, // width of counter in bits int csumStartBit, // bit index of checksum LSB int csumBitLength // width of checksum in bits ) { int bytes = getDataLen(f.DLC); auto data = f.data.u8; // Pack payload into a 64‑bit word uint64_t w = 0; for (int i = 0; i < bytes; ++i) { w |= uint64_t(data[i]) << (8 * i); } // Increment the counter by +1 modulo its width { uint64_t mask = (uint64_t(1) << ctrBitLength) - 1; uint64_t ctr = ((w >> ctrStartBit) & mask) + 1; ctr &= mask; w = (w & ~(mask << ctrStartBit)) | (ctr << ctrStartBit); } // Unpack back into the frame bytes for (int i = 0; i < bytes; ++i) { data[i] = uint8_t((w >> (8 * i)) & 0xFF); } // Build a small buffer and zero out the checksum bits uint8_t buf[8]; for (int i = 0; i < bytes; ++i) { buf[i] = data[i]; } for (int bit = csumStartBit; bit < csumStartBit + csumBitLength; ++bit) { int b = bit >> 3; if (b >= bytes) break; buf[b] &= uint8_t(~(1u << (bit & 7))); } // Compute checksum offset from most significant hex digit of CAN ID uint8_t checksum_offset = uint8_t((f.ID >> 8) & 0xF); // high nibble of top byte // Sum the low byte of ID + buf[] uint8_t sum = uint8_t(f.ID & 0xFF); for (int i = 0; i < bytes; ++i) { sum = uint8_t(sum + buf[i]); } uint8_t checksum = uint8_t(sum + checksum_offset); // Write the checksum back into the frame setBitField(data, bytes, csumStartBit, csumBitLength, checksum); } // Function to extract raw bits/values from a given CAN frame signal inline uint64_t extract_signal_value(const uint8_t* data, uint32_t start_bit, uint32_t bit_length) { // // Usage: uint8_t bms_state = static_cast(extract_signal_value(rx_frame.data.u8, 31, 4)); // // Calculate the starting byte and bit offset uint32_t byte_index = start_bit / 8; uint32_t bit_offset = start_bit % 8; // Read up to 8 bytes starting from byte_index (need enough to cover bit_length + bit_offset) uint64_t raw = 0; for (int i = 0; i < 8 && (byte_index + i) < 64; ++i) { raw |= (uint64_t)data[byte_index + i] << (8 * i); } // Shift and mask raw >>= bit_offset; if (bit_length == 64) return raw; return raw & ((1ULL << bit_length) - 1); } // Function to write a value to a given CAN frame signal void write_signal_value(CAN_frame* frame, uint16_t start_bit, uint8_t bit_length, int64_t value, bool is_signed) { if (bit_length == 0 || bit_length > 64 || frame == nullptr) return; uint64_t uvalue; if (is_signed) { int64_t min_val = -(1LL << (bit_length - 1)); int64_t max_val = (1LL << (bit_length - 1)) - 1; // Clamp to valid range if (value < min_val) value = min_val; if (value > max_val) value = max_val; // Two's complement encoding uvalue = static_cast(value) & ((1ULL << bit_length) - 1); } else { uvalue = static_cast(value) & ((1ULL << bit_length) - 1); } // Write value into frame->data.u8 using little-endian bit layout for (uint8_t i = 0; i < bit_length; ++i) { uint8_t bit_val = (uvalue >> i) & 1; uint16_t bit_pos = start_bit + i; uint8_t byte_index = bit_pos / 8; uint8_t bit_index = bit_pos % 8; if (byte_index >= frame->DLC) continue; // Prevent overrun if (bit_val) frame->data.u8[byte_index] |= (1 << bit_index); else frame->data.u8[byte_index] &= ~(1 << bit_index); } } void generateTESLA_229(CAN_frame& f) { static const uint8_t checksumLookup[16] = {0x46, 0x44, 0x52, 0x6D, 0x43, 0x41, 0xDD, 0xF9, 0x4C, 0xA5, 0xF6, 0x8C, 0x49, 0x2F, 0x31, 0x3B}; // Safety, only run if this is the right ID if (f.ID != 0x229) return; const int ctrStartBit = 8; const int ctrBitLength = 4; const int csumStartBit = 0; const int csumBitLength = 8; int bytes = getDataLen(f.DLC); auto data = f.data.u8; // Pack the first few bytes into a word uint64_t w = 0; for (int i = 0; i < bytes; ++i) { w |= uint64_t(data[i]) << (8 * i); } // Extract current counter uint64_t mask = (uint64_t(1) << ctrBitLength) - 1; uint8_t ctr = (w >> ctrStartBit) & mask; // Increment counter mod 16 ctr = (ctr + 1) & 0xF; // Write updated counter back w = (w & ~(mask << ctrStartBit)) | (uint64_t(ctr) << ctrStartBit); for (int i = 0; i < bytes; ++i) { data[i] = uint8_t((w >> (8 * i)) & 0xFF); } // Look up and insert checksum uint8_t checksum = checksumLookup[ctr]; setBitField(data, bytes, csumStartBit, csumBitLength, checksum); } void generateTESLA_213(CAN_frame& f) { static uint8_t counter = 0; // Increment counter (wrap at 16) counter = (counter + 1) & 0xF; // Safety, only modify if ID is 0x213 and DLC is at least 2 if (f.ID != 0x213 || f.DLC < 2) return; // Byte 0: counter in high nibble uint8_t value = counter << 4; // Byte 1: checksum = value + 0x15 uint8_t checksum = (value + 0x15) & 0xFF; f.data.u8[0] = value; f.data.u8[1] = checksum; } // Function to check if a year is a leap year bool isLeapYear(int year) { if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { return true; } return false; } // Function to convert year and day of year (i.e. Julian date) into human readable date char* dayOfYearToDate(int year, int dayOfYear) { // Arrays to hold the number of days in each month for standard/leap years int daysInMonthStandard[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int daysInMonthLeap[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // Select the appropriate array for the given year int* daysInMonth = isLeapYear(year) ? daysInMonthLeap : daysInMonthStandard; int month = 0; // Find the month and the day within the month while (dayOfYear > daysInMonth[month]) { dayOfYear -= daysInMonth[month]; month++; } static char dateString[11]; // For "YYYY-MM-DD\0" // Format the date string in "YYYY-MM-DD" format snprintf(dateString, sizeof(dateString), "%d-%02d-%02d", year, month + 1, dayOfYear); return dateString; } void TeslaBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus //After values are mapped, we perform some safety checks, and do some serial printouts datalayer.battery.status.soh_pptt = 9900; //Tesla batteries do not send a SOH% value on bus. Hardcode to 99% datalayer.battery.status.real_soc = (battery_soc_ui * 10); //increase SOC range from 0-100.0 -> 100.00 datalayer.battery.status.voltage_dV = battery_volts; datalayer.battery.status.current_dA = battery_amps; //13.0A //Calculate the remaining Wh amount from SOC% and max Wh value. datalayer.battery.status.remaining_capacity_Wh = static_cast( (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); // Define the allowed discharge power datalayer.battery.status.max_discharge_power_W = (battery_max_discharge_current * (battery_volts / 10)); // Cap the allowed discharge power if higher than the maximum discharge power allowed if (datalayer.battery.status.max_discharge_power_W > datalayer.battery.status.override_discharge_power_W) { datalayer.battery.status.max_discharge_power_W = datalayer.battery.status.override_discharge_power_W; } //The allowed charge power behaves strangely. We instead estimate this value if (battery_soc_ui > 990) { datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W; } else if (battery_soc_ui > RAMPDOWN_SOC) { // When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0 datalayer.battery.status.max_charge_power_W = RAMPDOWNPOWERALLOWED * (1 - (battery_soc_ui - RAMPDOWN_SOC) / (1000.0 - RAMPDOWN_SOC)); //If the cellvoltages start to reach overvoltage, only allow a small amount of power in if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { if (battery_cell_max_v > (MAX_CELL_VOLTAGE_LFP - FLOAT_START_MV)) { datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W; } } else { //NCM/A if (battery_cell_max_v > (MAX_CELL_VOLTAGE_NCA_NCM - FLOAT_START_MV)) { datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W; } } } else { // No limits, max charging power allowed datalayer.battery.status.max_charge_power_W = datalayer.battery.status.override_charge_power_W; } datalayer.battery.status.temperature_min_dC = battery_min_temp; datalayer.battery.status.temperature_max_dC = battery_max_temp; datalayer.battery.status.cell_max_voltage_mV = battery_cell_max_v; datalayer.battery.status.cell_min_voltage_mV = battery_cell_min_v; battery_cell_deviation_mV = (battery_cell_max_v - battery_cell_min_v); /* Value mapping is completed. Start to check all safeties */ //INTERNAL_OPEN_FAULT - Someone disconnected a high voltage cable while battery was in use if (battery_hvil_status == 3) { set_event(EVENT_INTERNAL_OPEN_FAULT, 0); } else { clear_event(EVENT_INTERNAL_OPEN_FAULT); } //Voltage between 0.5-5.0V, pyrofuse most likely blown if (datalayer.battery.status.voltage_dV >= 5 && datalayer.battery.status.voltage_dV <= 50) { set_event(EVENT_BATTERY_FUSE, 0); } else { clear_event(EVENT_BATTERY_FUSE); } if (user_selected_tesla_GTW_chassisType > 1) { //{{0, "Model S"}, {1, "Model X"}, {2, "Model 3"}, {3, "Model Y"}}; // Autodetect algoritm for chemistry on 3/Y packs. // NCM/A batteries have 96s, LFP has 102-108s // Drawback with this check is that it takes 3-5minutes before all cells have been counted! if (datalayer.battery.info.number_of_cells > 101) { datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; } //Once cell chemistry is determined, set maximum and minimum total pack voltage safety limits if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_LFP; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_LFP; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_LFP; } else { // NCM/A chemistry datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM; } // During forced balancing request via webserver, we allow the battery to exceed normal safety parameters if (datalayer.battery.settings.user_requests_balancing) { datalayer.battery.status.real_soc = 9900; //Force battery to show up as 99% when balancing datalayer.battery.info.max_design_voltage_dV = datalayer.battery.settings.balancing_max_pack_voltage_dV; datalayer.battery.info.max_cell_voltage_mV = datalayer.battery.settings.balancing_max_cell_voltage_mV; datalayer.battery.info.max_cell_voltage_deviation_mV = datalayer.battery.settings.balancing_max_deviation_cell_voltage_mV; datalayer.battery.status.max_charge_power_W = datalayer.battery.settings.balancing_float_power_W; } } // Check if user requests some action if (datalayer.battery.settings.user_requests_tesla_isolation_clear) { stateMachineClearIsolationFault = 0; //Start the isolation fault statemachine datalayer.battery.settings.user_requests_tesla_isolation_clear = false; } if (datalayer.battery.settings.user_requests_tesla_bms_reset) { if (battery_contactor == 1 && BMS_a180_SW_ECU_reset_blocked == false) { //Start the BMS ECU reset statemachine, only if contactors are OPEN and BMS ECU allows it stateMachineBMSReset = 0; datalayer.battery.settings.user_requests_tesla_bms_reset = false; logging.println("BMS reset requested"); } else { logging.println("ERROR: BMS reset failed due to contactors not being open, or BMS ECU not allowing it"); stateMachineBMSReset = 0xFF; datalayer.battery.settings.user_requests_tesla_bms_reset = false; } } if (datalayer.battery.settings.user_requests_tesla_soc_reset) { if (datalayer.battery.status.real_soc < 1500 || datalayer.battery.status.real_soc > 9000) { //Start the SOC reset statemachine, only if SOC < 15% or > 90% stateMachineSOCReset = 0; datalayer.battery.settings.user_requests_tesla_soc_reset = false; logging.println("SOC reset requested"); } else { logging.println("ERROR: SOC reset failed due to SOC not being less than 15 or greater than 90"); stateMachineSOCReset = 0xFF; datalayer.battery.settings.user_requests_tesla_soc_reset = false; } } //Update 0x333 UI_chargeTerminationPct (bit 16, width 10) value to SOC max value - expose via UI? //One firmware version this was seen at bit 17 width 11 write_signal_value(&TESLA_333, 16, 10, static_cast(datalayer.battery.settings.max_percentage / 10), false); // Update webserver datalayer //datalayer_extended.tesla.BMS_hvilFault = BMS_a036_SW_HvpHvilFault; datalayer_extended.tesla.hvil_status = battery_hvil_status; //0x20A datalayer_extended.tesla.packContNegativeState = battery_packContNegativeState; datalayer_extended.tesla.packContPositiveState = battery_packContPositiveState; datalayer_extended.tesla.packContactorSetState = battery_packContactorSetState; datalayer_extended.tesla.packCtrsClosingBlocked = battery_packCtrsClosingBlocked; datalayer_extended.tesla.pyroTestInProgress = battery_pyroTestInProgress; datalayer_extended.tesla.battery_packCtrsOpenNowRequested = battery_packCtrsOpenNowRequested; datalayer_extended.tesla.battery_packCtrsOpenRequested = battery_packCtrsOpenRequested; datalayer_extended.tesla.battery_packCtrsRequestStatus = battery_packCtrsRequestStatus; datalayer_extended.tesla.battery_packCtrsResetRequestRequired = battery_packCtrsResetRequestRequired; datalayer_extended.tesla.battery_dcLinkAllowedToEnergize = battery_dcLinkAllowedToEnergize; //0x72A if (parsed_battery_serialNumber && battery_serialNumber[13] != 0) { memcpy(datalayer_extended.tesla.battery_serialNumber, battery_serialNumber, sizeof(battery_serialNumber)); datalayer_extended.tesla.battery_manufactureDate = battery_manufactureDate; //We have valid data and comms with the battery, attempt to query part number if (!parsed_battery_partNumber && stateMachineBMSQuery == 0xFF) { stateMachineBMSQuery = 0; } } //Via UDS if (parsed_battery_partNumber && battery_partNumber[11] != 0) { memcpy(datalayer_extended.tesla.battery_partNumber, battery_partNumber, sizeof(battery_partNumber)); } //0x3C4 if (parsed_PCS_partNumber && PCS_partNumber[11] != 0) { memcpy(datalayer_extended.tesla.PCS_partNumber, PCS_partNumber, sizeof(PCS_partNumber)); } //0x2B4 datalayer_extended.tesla.battery_dcdcLvBusVolt = battery_dcdcLvBusVolt; datalayer_extended.tesla.battery_dcdcHvBusVolt = battery_dcdcHvBusVolt; datalayer_extended.tesla.battery_dcdcLvOutputCurrent = battery_dcdcLvOutputCurrent; //0x352 datalayer_extended.tesla.BMS352_mux = BMS352_mux; datalayer_extended.tesla.battery_nominal_full_pack_energy = battery_nominal_full_pack_energy; datalayer_extended.tesla.battery_nominal_full_pack_energy_m0 = battery_nominal_full_pack_energy_m0; datalayer_extended.tesla.battery_nominal_energy_remaining = battery_nominal_energy_remaining; datalayer_extended.tesla.battery_nominal_energy_remaining_m0 = battery_nominal_energy_remaining_m0; datalayer_extended.tesla.battery_ideal_energy_remaining = battery_ideal_energy_remaining; datalayer_extended.tesla.battery_ideal_energy_remaining_m0 = battery_ideal_energy_remaining_m0; datalayer_extended.tesla.battery_energy_to_charge_complete = battery_energy_to_charge_complete; datalayer_extended.tesla.battery_energy_to_charge_complete_m1 = battery_energy_to_charge_complete_m1; datalayer_extended.tesla.battery_energy_buffer = battery_energy_buffer; datalayer_extended.tesla.battery_energy_buffer_m1 = battery_energy_buffer_m1; datalayer_extended.tesla.battery_expected_energy_remaining_m1 = battery_expected_energy_remaining_m1; datalayer_extended.tesla.battery_full_charge_complete = battery_full_charge_complete; datalayer_extended.tesla.battery_fully_charged = battery_fully_charged; //0x3D2 datalayer.battery.status.total_discharged_battery_Wh = battery_total_discharge; datalayer.battery.status.total_charged_battery_Wh = battery_total_charge; //0x392 datalayer_extended.tesla.battery_moduleType = battery_moduleType; datalayer_extended.tesla.battery_packMass = battery_packMass; datalayer_extended.tesla.battery_platformMaxBusVoltage = battery_platformMaxBusVoltage; //0x2D2 datalayer_extended.tesla.BMS_min_voltage = BMS_min_voltage; datalayer_extended.tesla.BMS_max_voltage = BMS_max_voltage; datalayer_extended.tesla.battery_max_charge_current = battery_max_charge_current; datalayer_extended.tesla.battery_max_discharge_current = battery_max_discharge_current; //0x292 datalayer_extended.tesla.battery_beginning_of_life = battery_beginning_of_life; datalayer_extended.tesla.battery_battTempPct = battery_battTempPct; datalayer_extended.tesla.battery_soc_ave = battery_soc_ave; datalayer_extended.tesla.battery_soc_max = battery_soc_max; datalayer_extended.tesla.battery_soc_min = battery_soc_min; datalayer_extended.tesla.battery_soc_ui = battery_soc_ui; //0x332 datalayer_extended.tesla.battery_BrickVoltageMax = battery_BrickVoltageMax; datalayer_extended.tesla.battery_BrickVoltageMin = battery_BrickVoltageMin; datalayer_extended.tesla.battery_BrickTempMaxNum = battery_BrickTempMaxNum; datalayer_extended.tesla.battery_BrickTempMinNum = battery_BrickTempMinNum; datalayer_extended.tesla.battery_BrickModelTMax = battery_BrickModelTMax; datalayer_extended.tesla.battery_BrickModelTMin = battery_BrickModelTMin; //0x212 datalayer_extended.tesla.BMS_isolationResistance = BMS_isolationResistance; datalayer_extended.tesla.BMS_contactorState = BMS_contactorState; datalayer_extended.tesla.BMS_state = BMS_state; datalayer_extended.tesla.BMS_hvState = BMS_hvState; datalayer_extended.tesla.BMS_uiChargeStatus = BMS_uiChargeStatus; datalayer_extended.tesla.BMS_diLimpRequest = BMS_diLimpRequest; datalayer_extended.tesla.BMS_chgPowerAvailable = BMS_chgPowerAvailable; datalayer_extended.tesla.BMS_pcsPwmEnabled = BMS_pcsPwmEnabled; //0x224 datalayer_extended.tesla.PCS_dcdcPrechargeStatus = PCS_dcdcPrechargeStatus; datalayer_extended.tesla.PCS_dcdc12VSupportStatus = PCS_dcdc12VSupportStatus; datalayer_extended.tesla.PCS_dcdcHvBusDischargeStatus = PCS_dcdcHvBusDischargeStatus; datalayer_extended.tesla.PCS_dcdcMainState = PCS_dcdcMainState; datalayer_extended.tesla.PCS_dcdcSubState = PCS_dcdcSubState; datalayer_extended.tesla.PCS_dcdcFaulted = PCS_dcdcFaulted; datalayer_extended.tesla.PCS_dcdcOutputIsLimited = PCS_dcdcOutputIsLimited; datalayer_extended.tesla.PCS_dcdcMaxOutputCurrentAllowed = PCS_dcdcMaxOutputCurrentAllowed; datalayer_extended.tesla.PCS_dcdcPrechargeRtyCnt = PCS_dcdcPrechargeRtyCnt; datalayer_extended.tesla.PCS_dcdc12VSupportRtyCnt = PCS_dcdc12VSupportRtyCnt; datalayer_extended.tesla.PCS_dcdcDischargeRtyCnt = PCS_dcdcDischargeRtyCnt; datalayer_extended.tesla.PCS_dcdcPwmEnableLine = PCS_dcdcPwmEnableLine; datalayer_extended.tesla.PCS_dcdcSupportingFixedLvTarget = PCS_dcdcSupportingFixedLvTarget; datalayer_extended.tesla.PCS_dcdcPrechargeRestartCnt = PCS_dcdcPrechargeRestartCnt; datalayer_extended.tesla.PCS_dcdcInitialPrechargeSubState = PCS_dcdcInitialPrechargeSubState; //0x252 datalayer_extended.tesla.BMS_maxRegenPower = BMS_maxRegenPower; datalayer_extended.tesla.BMS_maxDischargePower = BMS_maxDischargePower; datalayer_extended.tesla.BMS_maxStationaryHeatPower = BMS_maxStationaryHeatPower; datalayer_extended.tesla.BMS_hvacPowerBudget = BMS_hvacPowerBudget; datalayer_extended.tesla.BMS_notEnoughPowerForHeatPump = BMS_notEnoughPowerForHeatPump; datalayer_extended.tesla.BMS_powerLimitState = BMS_powerLimitState; datalayer_extended.tesla.BMS_inverterTQF = BMS_inverterTQF; //Via UDS //datalayer_extended.tesla.BMS_info_buildConfigId = BMS_info_buildConfigId; //0x300 datalayer_extended.tesla.BMS_info_buildConfigId = BMS_info_buildConfigId; datalayer_extended.tesla.BMS_info_hardwareId = BMS_info_hardwareId; datalayer_extended.tesla.BMS_info_componentId = BMS_info_componentId; datalayer_extended.tesla.BMS_info_pcbaId = BMS_info_pcbaId; datalayer_extended.tesla.BMS_info_assemblyId = BMS_info_assemblyId; datalayer_extended.tesla.BMS_info_usageId = BMS_info_usageId; datalayer_extended.tesla.BMS_info_subUsageId = BMS_info_subUsageId; datalayer_extended.tesla.BMS_info_platformType = BMS_info_platformType; datalayer_extended.tesla.BMS_info_appCrc = BMS_info_appCrc; datalayer_extended.tesla.BMS_info_bootGitHash = BMS_info_bootGitHash; datalayer_extended.tesla.BMS_info_bootUdsProtoVersion = BMS_info_bootUdsProtoVersion; datalayer_extended.tesla.BMS_info_bootCrc = BMS_info_bootCrc; //0x312 datalayer_extended.tesla.BMS_powerDissipation = BMS_powerDissipation; datalayer_extended.tesla.BMS_flowRequest = BMS_flowRequest; datalayer_extended.tesla.BMS_inletActiveCoolTargetT = BMS_inletActiveCoolTargetT; datalayer_extended.tesla.BMS_inletPassiveTargetT = BMS_inletPassiveTargetT; datalayer_extended.tesla.BMS_inletActiveHeatTargetT = BMS_inletActiveHeatTargetT; datalayer_extended.tesla.BMS_packTMin = BMS_packTMin; datalayer_extended.tesla.BMS_packTMax = BMS_packTMax; datalayer_extended.tesla.BMS_pcsNoFlowRequest = BMS_pcsNoFlowRequest; datalayer_extended.tesla.BMS_noFlowRequest = BMS_noFlowRequest; //0x3C4 datalayer_extended.tesla.PCS_info_buildConfigId = PCS_info_buildConfigId; datalayer_extended.tesla.PCS_info_hardwareId = PCS_info_hardwareId; datalayer_extended.tesla.PCS_info_componentId = PCS_info_componentId; //0x2A4 datalayer_extended.tesla.PCS_dcdcTemp = PCS_dcdcTemp; datalayer_extended.tesla.PCS_ambientTemp = PCS_ambientTemp; datalayer_extended.tesla.PCS_chgPhATemp = PCS_chgPhATemp; datalayer_extended.tesla.PCS_chgPhBTemp = PCS_chgPhBTemp; datalayer_extended.tesla.PCS_chgPhCTemp = PCS_chgPhCTemp; //0x2C4 datalayer_extended.tesla.PCS_dcdcMaxLvOutputCurrent = PCS_dcdcMaxLvOutputCurrent; datalayer_extended.tesla.PCS_dcdcCurrentLimit = PCS_dcdcCurrentLimit; datalayer_extended.tesla.PCS_dcdcLvOutputCurrentTempLimit = PCS_dcdcLvOutputCurrentTempLimit; datalayer_extended.tesla.PCS_dcdcUnifiedCommand = PCS_dcdcUnifiedCommand; datalayer_extended.tesla.PCS_dcdcCLAControllerOutput = PCS_dcdcCLAControllerOutput; datalayer_extended.tesla.PCS_dcdcTankVoltage = PCS_dcdcTankVoltage; datalayer_extended.tesla.PCS_dcdcTankVoltageTarget = PCS_dcdcTankVoltageTarget; datalayer_extended.tesla.PCS_dcdcClaCurrentFreq = PCS_dcdcClaCurrentFreq; datalayer_extended.tesla.PCS_dcdcTCommMeasured = PCS_dcdcTCommMeasured; datalayer_extended.tesla.PCS_dcdcShortTimeUs = PCS_dcdcShortTimeUs; datalayer_extended.tesla.PCS_dcdcHalfPeriodUs = PCS_dcdcHalfPeriodUs; datalayer_extended.tesla.PCS_dcdcIntervalMaxFrequency = PCS_dcdcIntervalMaxFrequency; datalayer_extended.tesla.PCS_dcdcIntervalMaxHvBusVolt = PCS_dcdcIntervalMaxHvBusVolt; datalayer_extended.tesla.PCS_dcdcIntervalMaxLvBusVolt = PCS_dcdcIntervalMaxLvBusVolt; datalayer_extended.tesla.PCS_dcdcIntervalMaxLvOutputCurr = PCS_dcdcIntervalMaxLvOutputCurr; datalayer_extended.tesla.PCS_dcdcIntervalMinFrequency = PCS_dcdcIntervalMinFrequency; datalayer_extended.tesla.PCS_dcdcIntervalMinHvBusVolt = PCS_dcdcIntervalMinHvBusVolt; datalayer_extended.tesla.PCS_dcdcIntervalMinLvBusVolt = PCS_dcdcIntervalMinLvBusVolt; datalayer_extended.tesla.PCS_dcdcIntervalMinLvOutputCurr = PCS_dcdcIntervalMinLvOutputCurr; datalayer_extended.tesla.PCS_dcdc12vSupportLifetimekWh = PCS_dcdc12vSupportLifetimekWh; //0x310 datalayer_extended.tesla.HVP_info_buildConfigId = HVP_info_buildConfigId; datalayer_extended.tesla.HVP_info_hardwareId = HVP_info_hardwareId; datalayer_extended.tesla.HVP_info_componentId = HVP_info_componentId; //0x7AA datalayer_extended.tesla.HVP_gpioPassivePyroDepl = HVP_gpioPassivePyroDepl; datalayer_extended.tesla.HVP_gpioPyroIsoEn = HVP_gpioPyroIsoEn; datalayer_extended.tesla.HVP_gpioCpFaultIn = HVP_gpioCpFaultIn; datalayer_extended.tesla.HVP_gpioPackContPowerEn = HVP_gpioPackContPowerEn; datalayer_extended.tesla.HVP_gpioHvCablesOk = HVP_gpioHvCablesOk; datalayer_extended.tesla.HVP_gpioHvpSelfEnable = HVP_gpioHvpSelfEnable; datalayer_extended.tesla.HVP_gpioLed = HVP_gpioLed; datalayer_extended.tesla.HVP_gpioCrashSignal = HVP_gpioCrashSignal; datalayer_extended.tesla.HVP_gpioShuntDataReady = HVP_gpioShuntDataReady; datalayer_extended.tesla.HVP_gpioFcContPosAux = HVP_gpioFcContPosAux; datalayer_extended.tesla.HVP_gpioFcContNegAux = HVP_gpioFcContNegAux; datalayer_extended.tesla.HVP_gpioBmsEout = HVP_gpioBmsEout; datalayer_extended.tesla.HVP_gpioCpFaultOut = HVP_gpioCpFaultOut; datalayer_extended.tesla.HVP_gpioPyroPor = HVP_gpioPyroPor; datalayer_extended.tesla.HVP_gpioShuntEn = HVP_gpioShuntEn; datalayer_extended.tesla.HVP_gpioHvpVerEn = HVP_gpioHvpVerEn; datalayer_extended.tesla.HVP_gpioPackCoontPosFlywheel = HVP_gpioPackCoontPosFlywheel; datalayer_extended.tesla.HVP_gpioCpLatchEnable = HVP_gpioCpLatchEnable; datalayer_extended.tesla.HVP_gpioPcsEnable = HVP_gpioPcsEnable; datalayer_extended.tesla.HVP_gpioPcsDcdcPwmEnable = HVP_gpioPcsDcdcPwmEnable; datalayer_extended.tesla.HVP_gpioPcsChargePwmEnable = HVP_gpioPcsChargePwmEnable; datalayer_extended.tesla.HVP_gpioFcContPowerEnable = HVP_gpioFcContPowerEnable; datalayer_extended.tesla.HVP_gpioHvilEnable = HVP_gpioHvilEnable; datalayer_extended.tesla.HVP_gpioSecDrdy = HVP_gpioSecDrdy; datalayer_extended.tesla.HVP_hvp1v5Ref = HVP_hvp1v5Ref; datalayer_extended.tesla.HVP_shuntCurrentDebug = HVP_shuntCurrentDebug; datalayer_extended.tesla.HVP_packCurrentMia = HVP_packCurrentMia; datalayer_extended.tesla.HVP_auxCurrentMia = HVP_auxCurrentMia; datalayer_extended.tesla.HVP_currentSenseMia = HVP_currentSenseMia; datalayer_extended.tesla.HVP_shuntRefVoltageMismatch = HVP_shuntRefVoltageMismatch; datalayer_extended.tesla.HVP_shuntThermistorMia = HVP_shuntThermistorMia; datalayer_extended.tesla.HVP_shuntHwMia = HVP_shuntHwMia; datalayer_extended.tesla.HVP_dcLinkVoltage = HVP_dcLinkVoltage; datalayer_extended.tesla.HVP_packVoltage = HVP_packVoltage; datalayer_extended.tesla.HVP_fcLinkVoltage = HVP_fcLinkVoltage; datalayer_extended.tesla.HVP_packContVoltage = HVP_packContVoltage; datalayer_extended.tesla.HVP_packNegativeV = HVP_packNegativeV; datalayer_extended.tesla.HVP_packPositiveV = HVP_packPositiveV; datalayer_extended.tesla.HVP_pyroAnalog = HVP_pyroAnalog; datalayer_extended.tesla.HVP_dcLinkNegativeV = HVP_dcLinkNegativeV; datalayer_extended.tesla.HVP_dcLinkPositiveV = HVP_dcLinkPositiveV; datalayer_extended.tesla.HVP_fcLinkNegativeV = HVP_fcLinkNegativeV; datalayer_extended.tesla.HVP_fcContCoilCurrent = HVP_fcContCoilCurrent; datalayer_extended.tesla.HVP_fcContVoltage = HVP_fcContVoltage; datalayer_extended.tesla.HVP_hvilInVoltage = HVP_hvilInVoltage; datalayer_extended.tesla.HVP_hvilOutVoltage = HVP_hvilOutVoltage; datalayer_extended.tesla.HVP_fcLinkPositiveV = HVP_fcLinkPositiveV; datalayer_extended.tesla.HVP_packContCoilCurrent = HVP_packContCoilCurrent; datalayer_extended.tesla.HVP_battery12V = HVP_battery12V; datalayer_extended.tesla.HVP_shuntRefVoltageDbg = HVP_shuntRefVoltageDbg; datalayer_extended.tesla.HVP_shuntAuxCurrentDbg = HVP_shuntAuxCurrentDbg; datalayer_extended.tesla.HVP_shuntBarTempDbg = HVP_shuntBarTempDbg; datalayer_extended.tesla.HVP_shuntAsicTempDbg = HVP_shuntAsicTempDbg; datalayer_extended.tesla.HVP_shuntAuxCurrentStatus = HVP_shuntAuxCurrentStatus; datalayer_extended.tesla.HVP_shuntBarTempStatus = HVP_shuntBarTempStatus; datalayer_extended.tesla.HVP_shuntAsicTempStatus = HVP_shuntAsicTempStatus; //Safety checks for CAN message sesnding if ((datalayer.system.status.inverter_allows_contactor_closing == true) && (datalayer.battery.status.bms_status != FAULT) && (!datalayer.system.settings.equipment_stop_active)) { // Carry on: 0x221 DRIVE state & reset power down timer vehicleState = 1; powerDownTimer = 180; //0x221 50ms cyclic, 20 calls/second } else { // Faulted state, or inverter blocks contactor closing // Shut down: 0x221 ACCESSORY state for 3 seconds, followed by GOING_DOWN, then OFF if (powerDownTimer <= 180 && powerDownTimer > 120) { vehicleState = 2; //ACCESSORY powerDownTimer--; } if (powerDownTimer <= 120 && powerDownTimer > 60) { vehicleState = 3; //GOING_DOWN powerDownTimer--; } if (powerDownTimer <= 60 && powerDownTimer > 0) { vehicleState = 0; //OFF powerDownTimer--; } } printFaultCodesIfActive(); logging.printf("BMS Contactors State: "); logging.printf(getBMSContactorState(battery_contactor)); // Display what state the BMS thinks the contactors are in logging.printf(", HVIL: "); logging.printf(getHvilStatusState(battery_hvil_status)); logging.printf(", NegativeState: "); logging.printf(getContactorState(battery_packContNegativeState)); logging.printf(", PositiveState: "); logging.println(getContactorState(battery_packContPositiveState)); logging.printf("HVP Contactors setState: "); logging.printf( getContactorText(battery_packContactorSetState)); // Display what state the HVP has set the contactors to be in logging.printf(", Closing blocked: "); logging.printf(getNoYes(battery_packCtrsClosingBlocked)); if (battery_packContactorSetState == 5) { logging.printf(" (already CLOSED)"); } logging.printf(", Pyrotest: "); logging.println(getNoYes(battery_pyroTestInProgress)); logging.printf("Battery values: "); logging.printf("Real SOC: "); logging.print(battery_soc_ui / 10.0, 1); logging.printf(", Battery voltage: "); logging.print(battery_volts / 10.0, 1); logging.printf("V"); logging.printf(", Battery HV current: "); logging.print(battery_amps / 10.0, 1); logging.printf("A"); logging.printf(", Fully charged?: "); if (battery_full_charge_complete) logging.printf("YES, "); else logging.printf("NO, "); if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { logging.printf("LFP chemistry detected!"); } logging.println(""); logging.printf("Cellstats, Max: "); logging.print(battery_cell_max_v); logging.printf("mV (cell "); logging.print(battery_BrickVoltageMaxNum); logging.printf("), Min: "); logging.print(battery_cell_min_v); logging.printf("mV (cell "); logging.print(battery_BrickVoltageMinNum); logging.printf("), Imbalance: "); logging.print(battery_cell_deviation_mV); logging.println("mV."); logging.printf("High Voltage Output Pins: %.2f V, Low Voltage: %.2f V, DC/DC 12V current: %.2f A.\n", (battery_dcdcHvBusVolt * 0.146484), (battery_dcdcLvBusVolt * 0.0390625), (battery_dcdcLvOutputCurrent * 0.1)); logging.printf("PCS_ambientTemp: %.2f°C, DCDC_Temp: %.2f°C, ChgPhA: %.2f°C, ChgPhB: %.2f°C, ChgPhC: %.2f°C.\n", PCS_ambientTemp * 0.1 + 40, PCS_dcdcTemp * 0.1 + 40, PCS_chgPhATemp * 0.1 + 40, PCS_chgPhBTemp * 0.1 + 40, PCS_chgPhCTemp * 0.1 + 40); } void TeslaBattery::handle_incoming_can_frame(CAN_frame rx_frame) { static uint8_t mux = 0; static uint16_t temp = 0; static bool mux0_read = false; static bool mux1_read = false; switch (rx_frame.ID) { case 0x352: // 850 BMS_energyStatus newer BMS datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; mux = ((rx_frame.data.u8[0]) & 0x03); //BMS_energyStatusIndex M : 0|2@1+ (1,0) [0|0] "" X if (mux == 0) { battery_nominal_full_pack_energy_m0 = (((rx_frame.data.u8[3]) << 8) | rx_frame.data.u8[2]); //16|16@1+ (0.02,0) [0|0] "kWh"//to datalayer_extended battery_nominal_energy_remaining_m0 = (((rx_frame.data.u8[5]) << 8) | rx_frame.data.u8[4]); //32|16@1+ (0.02,0) [0|0] "kWh"//to datalayer_extended battery_ideal_energy_remaining_m0 = (((rx_frame.data.u8[7]) << 8) | rx_frame.data.u8[6]); //48|16@1+ (0.02,0) [0|0] "kWh"//to datalayer_extended mux0_read = true; //Set flag to true } if (mux == 1) { battery_fully_charged = (rx_frame.data.u8[1] & 0x01); //BMS_fullChargeComplete : 15|1@1+ (1,0) [0|1]//noYes battery_energy_buffer_m1 = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); //16|16@1+ (0.01,0) [0|0] "kWh"//to datalayer_extended battery_expected_energy_remaining_m1 = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]); //32|16@1+ (0.02,0) [0|0] "kWh"//to datalayer_extended battery_energy_to_charge_complete_m1 = ((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]); //48|16@1+ (0.02,0) [0|0] "kWh"//to datalayer_extended mux1_read = true; //Set flag to true } if (mux == 2) { } // Additional information needed on this mux 2, example frame: 02 26 02 20 02 80 00 00 doesn't change if (mux0_read && mux1_read) { mux0_read = false; mux1_read = false; BMS352_mux = true; //Set flag to true break; } if (mux0_read || mux1_read || BMS352_mux) { //Skip older BMS frame parsing break; } // Older BMS <2021 without mux battery_nominal_full_pack_energy = //BMS_nominalFullPackEnergy : 0|11@1+ (0.1,0) [0|204.6] "kWh" //((_d[1] & (0x07U)) << 8) | (_d[0] & (0xFFU)); (((rx_frame.data.u8[1] & 0x07) << 8) | (rx_frame.data.u8[0])); //Example 752 (75.2kWh) battery_nominal_energy_remaining = //BMS_nominalEnergyRemaining : 11|11@1+ (0.1,0) [0|204.6] "kWh" //((_d[2] & (0x3FU)) << 5) | ((_d[1] >> 3) & (0x1FU)); (((rx_frame.data.u8[2] & 0x3F) << 5) | ((rx_frame.data.u8[1] & 0x1F) >> 3)); //Example 1247 * 0.1 = 124.7kWh battery_expected_energy_remaining = //BMS_expectedEnergyRemaining : 22|11@1+ (0.1,0) [0|204.6] "kWh"// ((_d[4] & (0x01U)) << 10) | ((_d[3] & (0xFFU)) << 2) | ((_d[2] >> 6) & (0x03U)); (((rx_frame.data.u8[4] & 0x01) << 10) | (rx_frame.data.u8[3] << 2) | ((rx_frame.data.u8[2] & 0x03) >> 6)); //Example 622 (62.2kWh) battery_ideal_energy_remaining = //BMS_idealEnergyRemaining : 33|11@1+ (0.1,0) [0|204.6] "kWh" //((_d[5] & (0x0FU)) << 7) | ((_d[4] >> 1) & (0x7FU)); (((rx_frame.data.u8[5] & 0x0F) << 7) | ((rx_frame.data.u8[4] & 0x7F) >> 1)); //Example 311 * 0.1 = 31.1kWh battery_energy_to_charge_complete = // BMS_energyToChargeComplete : 44|11@1+ (0.1,0) [0|204.6] "kWh"// ((_d[6] & (0x7FU)) << 4) | ((_d[5] >> 4) & (0x0FU)); (((rx_frame.data.u8[6] & 0x7F) << 4) | ((rx_frame.data.u8[5] & 0x0F) << 4)); //Example 147 * 0.1 = 14.7kWh battery_energy_buffer = //BMS_energyBuffer : 55|8@1+ (0.1,0) [0|25.4] "kWh"// ((_d[7] & (0x7FU)) << 1) | ((_d[6] >> 7) & (0x01U)); (((rx_frame.data.u8[7] & 0xFE) >> 1) | ((rx_frame.data.u8[6] & 0x80) >> 7)); //Example 1 * 0.1 = 0 battery_fully_charged = //BMS_fullChargeComplete : 63|1@1+ (1,0) [0|1] ""//((_d[7] >> 7) & (0x01U)); ((rx_frame.data.u8[7] >> 7) & 0x01); //noYes break; case 0x20A: //522 HVP_contactorState: datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_packContNegativeState = (rx_frame.data.u8[0] & 0x07); //(_d[0] & (0x07U)); 0|3@1+ (1,0) [0|7] //contactorState battery_packContPositiveState = (rx_frame.data.u8[0] & 0x38) >> 3; //((_d[0] >> 3) & (0x07U)); 3|3@1+ (1,0) [0|7] //contactorState //battery_contactor = (rx_frame.data.u8[1] & 0x0F); // 8|4@1+ (1,0) [0|9] //contactorText battery_packContactorSetState = (rx_frame.data.u8[1] & 0x0F); //(_d[1] & (0x0FU)); 8|4@1+ (1,0) [0|9] //contactorState battery_packCtrsClosingBlocked = (rx_frame.data.u8[4] & 0x08) >> 3; //((_d[4] >> 3) & (0x01U)); 35|1@1+ (1,0) [0|1] //noYes battery_pyroTestInProgress = (rx_frame.data.u8[4] & 0x20) >> 5; //((_d[4] >> 5) & (0x01U));//37|1@1+ (1,0) [0|1] //noYes battery_hvil_status = (rx_frame.data.u8[5] & 0x0F); //(_d[5] & (0x0FU)); //40|4@1+ (1,0) [0|9] //hvilStatusState battery_packCtrsOpenNowRequested = ((rx_frame.data.u8[4] >> 1) & (0x01U)); //33|1@1+ (1,0) [0|1] //noYes battery_packCtrsOpenRequested = ((rx_frame.data.u8[4] >> 2) & (0x01U)); //34|1@1+ (1,0) [0|1] //noYes battery_packCtrsRequestStatus = ((rx_frame.data.u8[3] >> 6) & (0x03U)); //30|2@1+ (1,0) [0|2] //HVP_contactor battery_packCtrsResetRequestRequired = (rx_frame.data.u8[4] & (0x01U)); //32|1@1+ (1,0) [0|1] //noYes battery_dcLinkAllowedToEnergize = ((rx_frame.data.u8[4] >> 4) & (0x01U)); //36|1@1+ (1,0) [0|1] //noYes battery_fcContNegativeAuxOpen = ((rx_frame.data.u8[0] >> 7) & (0x01U)); //7|1@1+ (1,0) [0|1] "" Receiver battery_fcContNegativeState = ((rx_frame.data.u8[1] >> 4) & (0x07U)); //12|3@1+ (1,0) [0|7] "" Receiver battery_fcContPositiveAuxOpen = ((rx_frame.data.u8[0] >> 6) & (0x01U)); //6|1@1+ (1,0) [0|1] "" Receiver battery_fcContPositiveState = (rx_frame.data.u8[2] & (0x07U)); //16|3@1+ (1,0) [0|7] "" Receiver battery_fcContactorSetState = ((rx_frame.data.u8[2] >> 3) & (0x0FU)); //19|4@1+ (1,0) [0|9] "" Receiver battery_fcCtrsClosingAllowed = ((rx_frame.data.u8[3] >> 5) & (0x01U)); //29|1@1+ (1,0) [0|1] "" Receiver battery_fcCtrsOpenNowRequested = ((rx_frame.data.u8[3] >> 3) & (0x01U)); //27|1@1+ (1,0) [0|1] "" Receiver battery_fcCtrsOpenRequested = ((rx_frame.data.u8[3] >> 4) & (0x01U)); //28|1@1+ (1,0) [0|1] "" Receiver battery_fcCtrsRequestStatus = (rx_frame.data.u8[3] & (0x03U)); //24|2@1+ (1,0) [0|2] "" Receiver battery_fcCtrsResetRequestRequired = ((rx_frame.data.u8[3] >> 2) & (0x01U)); //26|1@1+ (1,0) [0|1] "" Receiver battery_fcLinkAllowedToEnergize = ((rx_frame.data.u8[5] >> 4) & (0x03U)); //44|2@1+ (1,0) [0|2] "" Receiver case 0x212: //530 BMS_status: 8 datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; BMS_hvacPowerRequest = (rx_frame.data.u8[0] & (0x01U)); BMS_notEnoughPowerForDrive = ((rx_frame.data.u8[0] >> 1) & (0x01U)); BMS_notEnoughPowerForSupport = ((rx_frame.data.u8[0] >> 2) & (0x01U)); BMS_preconditionAllowed = ((rx_frame.data.u8[0] >> 3) & (0x01U)); BMS_updateAllowed = ((rx_frame.data.u8[0] >> 4) & (0x01U)); BMS_activeHeatingWorthwhile = ((rx_frame.data.u8[0] >> 5) & (0x01U)); BMS_cpMiaOnHvs = ((rx_frame.data.u8[0] >> 6) & (0x01U)); BMS_contactorState = (rx_frame.data.u8[1] & (0x07U)); //0 "SNA" 1 "OPEN" 2 "OPENING" 3 "CLOSING" 4 "CLOSED" 5 "WELDED" 6 "BLOCKED" ; battery_contactor = BMS_contactorState; //BMS_state = // Original code from older DBCs //((rx_frame.data.u8[1] >> 3) & //(0x0FU)); //0 "STANDBY" 1 "DRIVE" 2 "SUPPORT" 3 "CHARGE" 4 "FEIM" 5 "CLEAR_FAULT" 6 "FAULT" 7 "WELD" 8 "TEST" 9 "SNA" ; BMS_state = static_cast(extract_signal_value(rx_frame.data.u8, 31, 4)); //0 "STANDBY" 1 "DRIVE" 2 "SUPPORT" 3 "CHARGE" 4 "FEIM" 5 "CLEAR_FAULT" 6 "FAULT" 7 "WELD" 8 "TEST" 9 "SNA" 10 "BMS_DIAG"; BMS_hvState = (rx_frame.data.u8[2] & (0x07U)); //0 "DOWN" 1 "COMING_UP" 2 "GOING_DOWN" 3 "UP_FOR_DRIVE" 4 "UP_FOR_CHARGE" 5 "UP_FOR_DC_CHARGE" 6 "UP" ; BMS_isolationResistance = ((rx_frame.data.u8[3] & (0x1FU)) << 5) | ((rx_frame.data.u8[2] >> 3) & (0x1FU)); //19|10@1+ (10,0) [0|0] "kOhm"/to datalayer_extended //BMS_chargeRequest = ((rx_frame.data.u8[3] >> 5) & (0x01U)); BMS_chargeRequest = static_cast(extract_signal_value(rx_frame.data.u8, 29, 1)); BMS_keepWarmRequest = ((rx_frame.data.u8[3] >> 6) & (0x01U)); BMS_uiChargeStatus = static_cast(extract_signal_value(rx_frame.data.u8, 32, 3)); //BMS_uiChargeStatus = //(rx_frame.data.u8[4] & //(0x07U)); // 0 "DISCONNECTED" 1 "NO_POWER" 2 "ABOUT_TO_CHARGE" 3 "CHARGING" 4 "CHARGE_COMPLETE" 5 "CHARGE_STOPPED" ; BMS_diLimpRequest = ((rx_frame.data.u8[4] >> 3) & (0x01U)); BMS_okToShipByAir = ((rx_frame.data.u8[4] >> 4) & (0x01U)); BMS_okToShipByLand = ((rx_frame.data.u8[4] >> 5) & (0x01U)); BMS_chgPowerAvailable = ((rx_frame.data.u8[6] & (0x01U)) << 10) | ((rx_frame.data.u8[5] & (0xFFU)) << 2) | ((rx_frame.data.u8[4] >> 6) & (0x03U)); //38|11@1+ (0.125,0) [0|0] "kW" BMS_chargeRetryCount = ((rx_frame.data.u8[6] >> 1) & (0x0FU)); BMS_pcsPwmEnabled = ((rx_frame.data.u8[6] >> 5) & (0x01U)); BMS_ecuLogUploadRequest = ((rx_frame.data.u8[6] >> 6) & (0x03U)); BMS_minPackTemperature = (rx_frame.data.u8[7] & (0xFFU)); //56|8@1+ (0.5,-40) [0|0] "DegC break; case 0x224: //548 PCS_dcdcStatus: datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; PCS_dcdcPrechargeStatus = (rx_frame.data.u8[0] & (0x03U)); //0 "IDLE" 1 "ACTIVE" 2 "FAULTED" ; PCS_dcdc12VSupportStatus = ((rx_frame.data.u8[0] >> 2) & (0x03U)); //0 "IDLE" 1 "ACTIVE" 2 "FAULTED" PCS_dcdcHvBusDischargeStatus = ((rx_frame.data.u8[0] >> 4) & (0x03U)); //0 "IDLE" 1 "ACTIVE" 2 "FAULTED" PCS_dcdcMainState = ((rx_frame.data.u8[1] & (0x03U)) << 2) | ((rx_frame.data.u8[0] >> 6) & (0x03U)); //0 "STANDBY" 1 "12V_SUPPORT_ACTIVE" 2 "PRECHARGE_STARTUP" 3 "PRECHARGE_ACTIVE" 4 "DIS_HVBUS_ACTIVE" 5 "SHUTDOWN" 6 "FAULTED" ; PCS_dcdcSubState = ((rx_frame.data.u8[1] >> 2) & (0x1FU)); //0 "PWR_UP_INIT" 1 "STANDBY" 2 "12V_SUPPORT_ACTIVE" 3 "DIS_HVBUS" 4 "PCHG_FAST_DIS_HVBUS" 5 "PCHG_SLOW_DIS_HVBUS" 6 "PCHG_DWELL_CHARGE" 7 "PCHG_DWELL_WAIT" 8 "PCHG_DI_RECOVERY_WAIT" 9 "PCHG_ACTIVE" 10 "PCHG_FLT_FAST_DIS_HVBUS" 11 "SHUTDOWN" 12 "12V_SUPPORT_FAULTED" 13 "DIS_HVBUS_FAULTED" 14 "PCHG_FAULTED" 15 "CLEAR_FAULTS" 16 "FAULTED" 17 "NUM" ; PCS_dcdcFaulted = ((rx_frame.data.u8[1] >> 7) & (0x01U)); PCS_dcdcOutputIsLimited = ((rx_frame.data.u8[3] >> 4) & (0x01U)); PCS_dcdcMaxOutputCurrentAllowed = ((rx_frame.data.u8[5] & (0x01U)) << 11) | ((rx_frame.data.u8[4] & (0xFFU)) << 3) | ((rx_frame.data.u8[3] >> 5) & (0x07U)); //29|12@1+ (0.1,0) [0|0] "A" PCS_dcdcPrechargeRtyCnt = ((rx_frame.data.u8[5] >> 1) & (0x07U)); //Retry Count PCS_dcdc12VSupportRtyCnt = ((rx_frame.data.u8[5] >> 4) & (0x0FU)); //Retry Count PCS_dcdcDischargeRtyCnt = (rx_frame.data.u8[6] & (0x0FU)); //Retry Count PCS_dcdcPwmEnableLine = ((rx_frame.data.u8[6] >> 4) & (0x01U)); PCS_dcdcSupportingFixedLvTarget = ((rx_frame.data.u8[6] >> 5) & (0x01U)); PCS_ecuLogUploadRequest = ((rx_frame.data.u8[6] >> 6) & (0x03U)); PCS_dcdcPrechargeRestartCnt = (rx_frame.data.u8[7] & (0x07U)); PCS_dcdcInitialPrechargeSubState = ((rx_frame.data.u8[7] >> 3) & (0x1FU)); //0 "PWR_UP_INIT" 1 "STANDBY" 2 "12V_SUPPORT_ACTIVE" 3 "DIS_HVBUS" 4 "PCHG_FAST_DIS_HVBUS" 5 "PCHG_SLOW_DIS_HVBUS" 6 "PCHG_DWELL_CHARGE" 7 "PCHG_DWELL_WAIT" 8 "PCHG_DI_RECOVERY_WAIT" 9 "PCHG_ACTIVE" 10 "PCHG_FLT_FAST_DIS_HVBUS" 11 "SHUTDOWN" 12 "12V_SUPPORT_FAULTED" 13 "DIS_HVBUS_FAULTED" 14 "PCHG_FAULTED" 15 "CLEAR_FAULTS" 16 "FAULTED" 17 "NUM" ; break; case 0x252: //Limit //594 BMS_powerAvailable: datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; BMS_maxRegenPower = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); //0|16@1+ (0.01,0) [0|655.35] "kW" //Example 4715 * 0.01 = 47.15kW BMS_maxDischargePower = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); //16|16@1+ (0.013,0) [0|655.35] "kW" //Example 2009 * 0.013 = 26.117??? BMS_maxStationaryHeatPower = (((rx_frame.data.u8[5] & 0x03) << 8) | rx_frame.data.u8[4]); //32|10@1+ (0.01,0) [0|10.23] "kW" //Example 500 * 0.01 = 5kW BMS_hvacPowerBudget = (((rx_frame.data.u8[7] << 6) | ((rx_frame.data.u8[6] & 0xFC) >> 2))); //50|10@1+ (0.02,0) [0|20.46] "kW" //Example 1000 * 0.02 = 20kW? BMS_notEnoughPowerForHeatPump = ((rx_frame.data.u8[5] >> 2) & (0x01U)); //BMS_notEnoughPowerForHeatPump : 42|1@1+ (1,0) [0|1] "" Receiver BMS_powerLimitState = (rx_frame.data.u8[6] & (0x01U)); //BMS_powerLimitsState : 48|1@1+ (1,0) [0|1] 0 "NOT_CALCULATED_FOR_DRIVE" 1 "CALCULATED_FOR_DRIVE" BMS_inverterTQF = ((rx_frame.data.u8[7] >> 4) & (0x03U)); break; case 0x132: //battery amps/volts //HVBattAmpVolt datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_volts = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]) * 0.1; //0|16@1+ (0.01,0) [0|655.35] "V" //Example 37030mv * 0.01 = 3703dV battery_amps = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8 [2]); //SmoothBattCurrent : 16|16@1- (-0.1,0) [-3276.7|3276.7] "A"//Example 65492 (-4.3A) OR 225 (22.5A) battery_raw_amps = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) * -0.05 + 822; //RawBattCurrent : 32|16@1- (-0.05,822) [-1138.35|2138.4] "A" //Example 10425 * -0.05 = ? battery_charge_time_remaining = (((rx_frame.data.u8[7] & 0x0F) << 8) | rx_frame.data.u8[6]); //ChargeHoursRemaining : 48|12@1+ (1,0) [0|4095] "Min" //Example 228 * 0.1 = 22.8min if (battery_charge_time_remaining == 4095) { battery_charge_time_remaining = 0; } break; case 0x3D2: //TotalChargeDischarge: datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_total_discharge = ((rx_frame.data.u8[3] << 24) | (rx_frame.data.u8[2] << 16) | (rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); //0|32@1+ (0.001,0) [0|4294970] "kWh" battery_total_charge = ((rx_frame.data.u8[7] << 24) | (rx_frame.data.u8[6] << 16) | (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]); //32|32@1+ (0.001,0) [0|4294970] "kWh" break; case 0x332: //min/max hist values //BattBrickMinMax: datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; mux = (rx_frame.data.u8[0] & 0x03); //BattBrickMultiplexer M : 0|2@1+ (1,0) [0|0] "" if (mux == 1) //Cell voltages { temp = ((rx_frame.data.u8[1] << 6) | (rx_frame.data.u8[0] >> 2)); temp = (temp & 0xFFF); battery_cell_max_v = temp * 2; temp = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); temp = (temp & 0xFFF); battery_cell_min_v = temp * 2; cellvoltagesRead = true; //BattBrickVoltageMax m1 : 2|12@1+ (0.002,0) [0|0] "V" Receiver ((_d[1] & (0x3FU)) << 6) | ((_d[0] >> 2) & (0x3FU)); battery_BrickVoltageMax = ((rx_frame.data.u8[1] & (0x3F)) << 6) | ((rx_frame.data.u8[0] >> 2) & (0x3F)); //to datalayer_extended //BattBrickVoltageMin m1 : 16|12@1+ (0.002,0) [0|0] "V" Receiver ((_d[3] & (0x0FU)) << 8) | (_d[2] & (0xFFU)); battery_BrickVoltageMin = ((rx_frame.data.u8[3] & (0x0F)) << 8) | (rx_frame.data.u8[2] & (0xFF)); //to datalayer_extended //BattBrickVoltageMaxNum m1 : 32|7@1+ (1,1) [0|0] "" Receiver battery_BrickVoltageMaxNum = 1 + (rx_frame.data.u8[4] & 0x7F); //(_d[4] & (0x7FU)); //This cell has highest voltage //BattBrickVoltageMinNum m1 : 40|7@1+ (1,1) [0|0] "" Receiver battery_BrickVoltageMinNum = 1 + (rx_frame.data.u8[5] & 0x7F); //(_d[5] & (0x7FU)); //This cell has lowest voltage } if (mux == 0) //Temperature sensors { //BattBrickTempMax m0 : 16|8@1+ (0.5,-40) [0|0] "C" (_d[2] & (0xFFU)); battery_max_temp = (rx_frame.data.u8[2] * 5) - 400; //Temperature values have 40.0*C offset, 0.5*C /bit //BattBrickTempMin m0 : 24|8@1+ (0.5,-40) [0|0] "C" (_d[3] & (0xFFU)); battery_min_temp = (rx_frame.data.u8[3] * 5) - 400; //Multiply by 5 and remove offset to get C+1 (0x61*5=485-400=8.5*C) //BattBrickTempMaxNum m0 : 2|4@1+ (1,0) [0|0] "" ((_d[0] >> 2) & (0x0FU)); battery_BrickTempMaxNum = ((rx_frame.data.u8[0] >> 2) & (0x0F)); //to datalayer_extended //BattBrickTempMinNum m0 : 8|4@1+ (1,0) [0|0] "" (_d[1] & (0x0FU)); battery_BrickTempMinNum = (rx_frame.data.u8[1] & (0x0F)); //to datalayer_extended //BattBrickModelTMax m0 : 32|8@1+ (0.5,-40) [0|0] "C" (_d[4] & (0xFFU)); battery_BrickModelTMax = (rx_frame.data.u8[4] & (0xFFU)); //to datalayer_extended //BattBrickModelTMin m0 : 40|8@1+ (0.5,-40) [0|0] "C" (_d[5] & (0xFFU)); battery_BrickModelTMin = (rx_frame.data.u8[5] & (0xFFU)); //to datalayer_extended } break; case 0x312: // 786 BMS_thermalStatus datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; BMS_powerDissipation = ((rx_frame.data.u8[1] & (0x03U)) << 8) | (rx_frame.data.u8[0] & (0xFFU)); //0|10@1+ (0.02,0) [0|0] "kW" BMS_flowRequest = ((rx_frame.data.u8[2] & (0x01U)) << 6) | ((rx_frame.data.u8[1] >> 2) & (0x3FU)); //10|7@1+ (0.3,0) [0|0] "LPM" BMS_inletActiveCoolTargetT = ((rx_frame.data.u8[3] & (0x03U)) << 7) | ((rx_frame.data.u8[2] >> 1) & (0x7FU)); //17|9@1+ (0.25,-25) [0|0] "DegC" BMS_inletPassiveTargetT = ((rx_frame.data.u8[4] & (0x07U)) << 6) | ((rx_frame.data.u8[3] >> 2) & (0x3FU)); //26|9@1+ (0.25,-25) [0|0] "DegC" X BMS_inletActiveHeatTargetT = ((rx_frame.data.u8[5] & (0x0FU)) << 5) | ((rx_frame.data.u8[4] >> 3) & (0x1FU)); //35|9@1+ (0.25,-25) [0|0] "DegC" BMS_packTMin = ((rx_frame.data.u8[6] & (0x1FU)) << 4) | ((rx_frame.data.u8[5] >> 4) & (0x0FU)); //44|9@1+ (0.25,-25) [-25|100] "DegC" BMS_packTMax = ((rx_frame.data.u8[7] & (0x3FU)) << 3) | ((rx_frame.data.u8[6] >> 5) & (0x07U)); //53|9@1+ (0.25,-25) [-25|100] "DegC" BMS_pcsNoFlowRequest = ((rx_frame.data.u8[7] >> 6) & (0x01U)); // 62|1@1+ (1,0) [0|0] "" BMS_noFlowRequest = ((rx_frame.data.u8[7] >> 7) & (0x01U)); //63|1@1+ (1,0) [0|0] "" break; case 0x2A4: //676 PCS_thermalStatus datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; PCS_chgPhATemp = (rx_frame.data.u8[0] & 0xFF) | ((rx_frame.data.u8[1] & 0x07) << 8); //0|11@1- (0.1,40) [0|0] "C" PCS_chgPhBTemp = ((rx_frame.data.u8[1] & 0xF8) >> 3) | ((rx_frame.data.u8[2] & 0x3F) << 5); //11|11@1- (0.1,40) [0|0] "C" PCS_chgPhCTemp = ((rx_frame.data.u8[2] & 0xC0) >> 6) | (rx_frame.data.u8[3] << 2) | ((rx_frame.data.u8[4] & 0x01) << 10); //22|11@1- (0.1,40) [0|0] "C" PCS_dcdcTemp = ((rx_frame.data.u8[4] & 0xFE) >> 1) | ((rx_frame.data.u8[5] & 0x0F) << 7); //33|11@1- (0.1,40) [0|0] "C" PCS_ambientTemp = ((rx_frame.data.u8[5] & 0xF0) >> 4) | (rx_frame.data.u8[6] << 4); //44|11@1- (0.1,40) [0|0] "C" break; case 0x2C4: // 708 PCS_logging: not all frames are listed, just ones relating to dcdc datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; mux = (rx_frame.data.u8[0] & (0x1FU)); //PCS_logMessageSelect = (rx_frame.data.u8[0] & (0x1FU)); //0|5@1+ (1,0) [0|0] "" if (mux == 6) { PCS_dcdcMaxLvOutputCurrent = ((rx_frame.data.u8[4] & (0xFFU)) << 4) | ((rx_frame.data.u8[3] >> 4) & (0x0FU)); //m6 : 28|12@1+ (0.1,0) [0|0] "A" X PCS_dcdcCurrentLimit = ((rx_frame.data.u8[6] & (0x0FU)) << 8) | (rx_frame.data.u8[5] & (0xFFU)); //m6 : 40|12@1+ (0.1,0) [0|0] "A" X PCS_dcdcLvOutputCurrentTempLimit = ((rx_frame.data.u8[7] & (0xFFU)) << 4) | ((rx_frame.data.u8[6] >> 4) & (0x0FU)); //m6 : 52|12@1+ (0.1,0) [0|0] "A" X } if (mux == 7) { PCS_dcdcUnifiedCommand = ((rx_frame.data.u8[1] & (0x7FU)) << 3) | ((rx_frame.data.u8[0] >> 5) & (0x07U)); //m7 : 5|10@1+ (0.001,0) [0|0] "1" X PCS_dcdcCLAControllerOutput = ((rx_frame.data.u8[3] & (0x03U)) << 8) | (rx_frame.data.u8[2] & (0xFFU)); //m7 : 16|10@1+ (0.001,0) [0|0] "1" X PCS_dcdcTankVoltage = ((rx_frame.data.u8[4] & (0x1FU)) << 6) | ((rx_frame.data.u8[3] >> 2) & (0x3FU)); //m7 : 26|11@1- (1,0) [0|0] "V" X PCS_dcdcTankVoltageTarget = ((rx_frame.data.u8[5] & (0x7FU)) << 3) | ((rx_frame.data.u8[4] >> 5) & (0x07U)); // m7 : 37|10@1+ (1,0) [0|0] "V" X PCS_dcdcClaCurrentFreq = ((rx_frame.data.u8[7] & (0x0FU)) << 8) | (rx_frame.data.u8[6] & (0xFFU)); //P m7 : 48|12@1+ (0.0976563,0) [0|0] "kHz" X } if (mux == 8) { PCS_dcdcTCommMeasured = ((rx_frame.data.u8[2] & (0xFFU)) << 8) | (rx_frame.data.u8[1] & (0xFFU)); // m8 : 8|16@1- (0.00195313,0) [0|0] "us" X PCS_dcdcShortTimeUs = ((rx_frame.data.u8[4] & (0xFFU)) << 8) | (rx_frame.data.u8[3] & (0xFFU)); // m8 : 24|16@1+ (0.000488281,0) [0|0] "us" X PCS_dcdcHalfPeriodUs = ((rx_frame.data.u8[6] & (0xFFU)) << 8) | (rx_frame.data.u8[5] & (0xFFU)); // m8 : 40|16@1+ (0.000488281,0) [0|0] "us" X } if (mux == 18) { PCS_dcdcIntervalMaxFrequency = ((rx_frame.data.u8[2] & (0x0FU)) << 8) | (rx_frame.data.u8[1] & (0xFFU)); // m18 : 8|12@1+ (1,0) [0|0] "kHz" X PCS_dcdcIntervalMaxHvBusVolt = ((rx_frame.data.u8[4] & (0x1FU)) << 8) | (rx_frame.data.u8[3] & (0xFFU)); //m18 : 24|13@1+ (0.1,0) [0|0] "V" X PCS_dcdcIntervalMaxLvBusVolt = ((rx_frame.data.u8[5] & (0x3FU)) << 3) | ((rx_frame.data.u8[4] >> 5) & (0x07U)); // m18 : 37|9@1+ (0.1,0) [0|0] "V" X PCS_dcdcIntervalMaxLvOutputCurr = ((rx_frame.data.u8[7] & (0x0FU)) << 8) | (rx_frame.data.u8[6] & (0xFFU)); //m18 : 48|12@1+ (1,0) [0|0] "A" X } if (mux == 19) { PCS_dcdcIntervalMinFrequency = ((rx_frame.data.u8[2] & (0x0FU)) << 8) | (rx_frame.data.u8[1] & (0xFFU)); //m19 : 8|12@1+ (1,0) [0|0] "kHz" X PCS_dcdcIntervalMinHvBusVolt = ((rx_frame.data.u8[4] & (0x1FU)) << 8) | (rx_frame.data.u8[3] & (0xFFU)); //m19 : 24|13@1+ (0.1,0) [0|0] "V" X PCS_dcdcIntervalMinLvBusVolt = ((rx_frame.data.u8[5] & (0x3FU)) << 3) | ((rx_frame.data.u8[4] >> 5) & (0x07U)); //m19 : 37|9@1+ (0.1,0) [0|0] "V" X PCS_dcdcIntervalMinLvOutputCurr = ((rx_frame.data.u8[7] & (0x0FU)) << 8) | (rx_frame.data.u8[6] & (0xFFU)); // m19 : 48|12@1+ (1,0) [0|0] "A" X } if (mux == 22) { PCS_dcdc12vSupportLifetimekWh = ((rx_frame.data.u8[3] & (0xFFU)) << 16) | ((rx_frame.data.u8[2] & (0xFFU)) << 8) | (rx_frame.data.u8[1] & (0xFFU)); // m22 : 8|24@1+ (0.01,0) [0|0] "kWh" X } break; case 0x401: // Cell stats //BrickVoltages datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; mux = (rx_frame.data.u8[0]); //MultiplexSelector M : 0|8@1+ (1,0) [0|0] "" //StatusFlags : 8|8@1+ (1,0) [0|0] "" //Brick0 m0 : 16|16@1+ (0.0001,0) [0|0] "V" //Brick1 m0 : 32|16@1+ (0.0001,0) [0|0] "V" //Brick2 m0 : 48|16@1+ (0.0001,0) [0|0] "V" static uint16_t volts; static uint8_t mux_zero_counter = 0u; static uint8_t mux_max = 0u; if (rx_frame.data.u8[1] == 0x2A) // status byte must be 0x2A to read cellvoltages { // Example, frame3=0x89,frame2=0x1D = 35101 / 10 = 3510mV volts = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) / 10; datalayer.battery.status.cell_voltages_mV[mux * 3] = volts; volts = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) / 10; datalayer.battery.status.cell_voltages_mV[1 + mux * 3] = volts; volts = ((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) / 10; datalayer.battery.status.cell_voltages_mV[2 + mux * 3] = volts; // Track the max value of mux. If we've seen two 0 values for mux, we've probably gathered all // cell voltages. Then, 2 + mux_max * 3 + 1 is the number of cell voltages. mux_max = (mux > mux_max) ? mux : mux_max; if (mux_zero_counter < 2 && mux == 0u) { mux_zero_counter++; if (mux_zero_counter == 2u) { // The max index will be 2 + mux_max * 3 (see above), so "+ 1" for the number of cells datalayer.battery.info.number_of_cells = 2 + 3 * mux_max + 1; // Increase the counter arbitrarily another time to make the initial if-statement evaluate to false mux_zero_counter++; } } } break; case 0x2d2: //BMSVAlimits: datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; BMS_min_voltage = ((rx_frame.data.u8[1] << 8) | rx_frame.data.u8[0]); //0|16@1+ (0.01,0) [0|430] "V" //Example 24148mv * 0.01 = 241.48 V BMS_max_voltage = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]); //16|16@1+ (0.01,0) [0|430] "V" //Example 40282mv * 0.01 = 402.82 V battery_max_charge_current = (((rx_frame.data.u8[5] & 0x3F) << 8) | rx_frame.data.u8[4]) * 0.1; //32|14@1+ (0.1,0) [0|1638.2] "A" //Example 1301? * 0.1 = 130.1? battery_max_discharge_current = (((rx_frame.data.u8[7] & 0x3F) << 8) | rx_frame.data.u8[6]) * 0.128; //48|14@1+ (0.128,0) [0|2096.9] "A" //Example 430? * 0.128 = 55.4? break; case 0x2b4: //PCS_dcdcRailStatus: datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_dcdcLvBusVolt = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[0]); //0|10@1+ (0.0390625,0) [0|39.9609] "V" battery_dcdcHvBusVolt = (((rx_frame.data.u8[2] & 0x3F) << 6) | ((rx_frame.data.u8[1] & 0xFC) >> 2)); //10|12@1+ (0.146484,0) [0|599.854] "V" battery_dcdcLvOutputCurrent = (((rx_frame.data.u8[4] & 0x0F) << 8) | rx_frame.data.u8[3]); //24|12@1+ (0.1,0) [0|400] "A" break; case 0x292: //BMS_socStatus datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; battery_beginning_of_life = (((rx_frame.data.u8[6] & 0x03) << 8) | rx_frame.data.u8[5]) * 0.1; //40|10@1+ (0.1,0) [0|102.3] "kWh" battery_soc_min = (((rx_frame.data.u8[1] & 0x03) << 8) | rx_frame.data.u8[0]); //0|10@1+ (0.1,0) [0|102.3] "%" battery_soc_ui = (((rx_frame.data.u8[2] & 0x0F) << 6) | ((rx_frame.data.u8[1] & 0xFC) >> 2)); //10|10@1+ (0.1,0) [0|102.3] "%" battery_soc_max = (((rx_frame.data.u8[3] & 0x3F) << 4) | ((rx_frame.data.u8[2] & 0xF0) >> 4)); //20|10@1+ (0.1,0) [0|102.3] "%" battery_soc_ave = ((rx_frame.data.u8[4] << 2) | ((rx_frame.data.u8[3] & 0xC0) >> 6)); //30|10@1+ (0.1,0) [0|102.3] "%" battery_battTempPct = (((rx_frame.data.u8[7] & 0x03) << 6) | (rx_frame.data.u8[6] & 0x3F) >> 2); //50|8@1+ (0.4,0) [0|100] "%" break; case 0x392: //BMS_packConfig datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; mux = (rx_frame.data.u8[0] & (0xFF)); if (mux == 1) { battery_packConfigMultiplexer = (rx_frame.data.u8[0] & (0xff)); //0|8@1+ (1,0) [0|1] "" battery_moduleType = (rx_frame.data.u8[1] & (0x07)); //8|3@1+ (1,0) [0|4] ""//0 "UNKNOWN" 1 "E3_NCT" 2 "E1_NCT" 3 "E3_CT" 4 "E1_CT" 5 "E1_CP" ;//to datalayer_extended battery_packMass = (rx_frame.data.u8[2]) + 300; //16|8@1+ (1,300) [342|469] "kg" battery_platformMaxBusVoltage = (((rx_frame.data.u8[4] & 0x03) << 8) | (rx_frame.data.u8[3])); //24|10@1+ (0.1,375) [0|0] "V" } if (mux == 0) { battery_reservedConfig = (rx_frame.data.u8[1] & (0x1F)); //8|5@1+ (1,0) [0|31] ""//0 "BMS_CONFIG_0" 1 "BMS_CONFIG_1" 10 "BMS_CONFIG_10" 11 "BMS_CONFIG_11" 12 "BMS_CONFIG_12" 13 "BMS_CONFIG_13" 14 "BMS_CONFIG_14" 15 "BMS_CONFIG_15" 16 "BMS_CONFIG_16" 17 "BMS_CONFIG_17" 18 "BMS_CONFIG_18" 19 "BMS_CONFIG_19" 2 "BMS_CONFIG_2" 20 "BMS_CONFIG_20" 21 "BMS_CONFIG_21" 22 "BMS_CONFIG_22" 23 "BMS_CONFIG_23" 24 "BMS_CONFIG_24" 25 "BMS_CONFIG_25" 26 "BMS_CONFIG_26" 27 "BMS_CONFIG_27" 28 "BMS_CONFIG_28" 29 "BMS_CONFIG_29" 3 "BMS_CONFIG_3" 30 "BMS_CONFIG_30" 31 "BMS_CONFIG_31" 4 "BMS_CONFIG_4" 5 "BMS_CONFIG_5" 6 "BMS_CONFIG_6" 7 "BMS_CONFIG_7" 8 "BMS_CONFIG_8" 9 "BMS_CONFIG_9" ; } break; case 0x7AA: //1962 HVP_debugMessage: datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; mux = (rx_frame.data.u8[0] & (0x0FU)); //HVP_debugMessageMultiplexer = (rx_frame.data.u8[0] & (0x0FU)); //0|4@1+ (1,0) [0|6] "" if (mux == 0) { HVP_gpioPassivePyroDepl = ((rx_frame.data.u8[0] >> 4) & (0x01U)); //: 4|1@1+ (1,0) [0|1] "" Receiver HVP_gpioPyroIsoEn = ((rx_frame.data.u8[0] >> 5) & (0x01U)); //: 5|1@1+ (1,0) [0|1] "" Receiver HVP_gpioCpFaultIn = ((rx_frame.data.u8[0] >> 6) & (0x01U)); //: 6|1@1+ (1,0) [0|1] "" Receiver HVP_gpioPackContPowerEn = ((rx_frame.data.u8[0] >> 7) & (0x01U)); //: 7|1@1+ (1,0) [0|1] "" Receiver HVP_gpioHvCablesOk = (rx_frame.data.u8[1] & (0x01U)); //: 8|1@1+ (1,0) [0|1] "" Receiver HVP_gpioHvpSelfEnable = ((rx_frame.data.u8[1] >> 1) & (0x01U)); //: 9|1@1+ (1,0) [0|1] "" Receiver HVP_gpioLed = ((rx_frame.data.u8[1] >> 2) & (0x01U)); //: 10|1@1+ (1,0) [0|1] "" Receiver HVP_gpioCrashSignal = ((rx_frame.data.u8[1] >> 3) & (0x01U)); //: 11|1@1+ (1,0) [0|1] "" Receiver HVP_gpioShuntDataReady = ((rx_frame.data.u8[1] >> 4) & (0x01U)); //: 12|1@1+ (1,0) [0|1] "" Receiver HVP_gpioFcContPosAux = ((rx_frame.data.u8[1] >> 5) & (0x01U)); //: 13|1@1+ (1,0) [0|1] "" Receiver HVP_gpioFcContNegAux = ((rx_frame.data.u8[1] >> 6) & (0x01U)); //: 14|1@1+ (1,0) [0|1] "" Receiver HVP_gpioBmsEout = ((rx_frame.data.u8[1] >> 7) & (0x01U)); //: 15|1@1+ (1,0) [0|1] "" Receiver HVP_gpioCpFaultOut = (rx_frame.data.u8[2] & (0x01U)); //: 16|1@1+ (1,0) [0|1] "" Receiver HVP_gpioPyroPor = ((rx_frame.data.u8[2] >> 1) & (0x01U)); //: 17|1@1+ (1,0) [0|1] "" Receiver HVP_gpioShuntEn = ((rx_frame.data.u8[2] >> 2) & (0x01U)); //: 18|1@1+ (1,0) [0|1] "" Receiver HVP_gpioHvpVerEn = ((rx_frame.data.u8[2] >> 3) & (0x01U)); //: 19|1@1+ (1,0) [0|1] "" Receiver HVP_gpioPackCoontPosFlywheel = ((rx_frame.data.u8[2] >> 4) & (0x01U)); //: 20|1@1+ (1,0) [0|1] "" Receiver HVP_gpioCpLatchEnable = ((rx_frame.data.u8[2] >> 5) & (0x01U)); //: 21|1@1+ (1,0) [0|1] "" Receiver HVP_gpioPcsEnable = ((rx_frame.data.u8[2] >> 6) & (0x01U)); //: 22|1@1+ (1,0) [0|1] "" Receiver HVP_gpioPcsDcdcPwmEnable = ((rx_frame.data.u8[2] >> 7) & (0x01U)); //: 23|1@1+ (1,0) [0|1] "" Receiver HVP_gpioPcsChargePwmEnable = (rx_frame.data.u8[3] & (0x01U)); //: 24|1@1+ (1,0) [0|1] "" Receiver HVP_gpioFcContPowerEnable = ((rx_frame.data.u8[3] >> 1) & (0x01U)); //: 25|1@1+ (1,0) [0|1] "" Receiver HVP_gpioHvilEnable = ((rx_frame.data.u8[3] >> 2) & (0x01U)); //: 26|1@1+ (1,0) [0|1] "" Receiver HVP_gpioSecDrdy = ((rx_frame.data.u8[3] >> 3) & (0x01U)); //: 27|1@1+ (1,0) [0|1] "" Receiver HVP_hvp1v5Ref = ((rx_frame.data.u8[4] & (0xFFU)) << 4) | ((rx_frame.data.u8[3] >> 4) & (0x0FU)); //: 28|12@1+ (0.1,0) [0|3] "V" Receiver HVP_shuntCurrentDebug = ((rx_frame.data.u8[6] & (0xFFU)) << 8) | (rx_frame.data.u8[5] & (0xFFU)); //: 40|16@1- (0.1,0) [-3276.8|3276.7] "A" Receiver HVP_packCurrentMia = (rx_frame.data.u8[7] & (0x01U)); //: 56|1@1+ (1,0) [0|1] "" Receiver HVP_auxCurrentMia = ((rx_frame.data.u8[7] >> 1) & (0x01U)); //: 57|1@1+ (1,0) [0|1] "" Receiver HVP_currentSenseMia = ((rx_frame.data.u8[7] >> 2) & (0x03U)); //: 58|1@1+ (1,0) [0|1] "" Receiver HVP_shuntRefVoltageMismatch = ((rx_frame.data.u8[7] >> 3) & (0x01U)); //: 59|1@1+ (1,0) [0|1] "" Receiver HVP_shuntThermistorMia = ((rx_frame.data.u8[7] >> 4) & (0x01U)); //: 60|1@1+ (1,0) [0|1] "" Receiver HVP_shuntHwMia = ((rx_frame.data.u8[7] >> 5) & (0x01U)); //: 61|1@1+ (1,0) [0|1] "" Receiver } if (mux == 1) { HVP_dcLinkVoltage = ((rx_frame.data.u8[2] & (0xFFU)) << 8) | (rx_frame.data.u8[1] & (0xFFU)); //: 8|16@1- (0.1,0) [-3276.8|3276.7] "V" Receiver HVP_packVoltage = ((rx_frame.data.u8[4] & (0xFFU)) << 8) | (rx_frame.data.u8[3] & (0xFFU)); //: 24|16@1- (0.1,0) [-3276.8|3276.7] "V" Receiver HVP_fcLinkVoltage = ((rx_frame.data.u8[6] & (0xFFU)) << 8) | (rx_frame.data.u8[5] & (0xFFU)); //: 40|16@1- (0.1,0) [-3276.8|3276.7] "V" Receiver } if (mux == 2) { HVP_packContVoltage = ((rx_frame.data.u8[1] & (0xFFU)) << 4) | ((rx_frame.data.u8[0] >> 4) & (0x0FU)); //: 4|12@1+ (0.1,0) [0|30] "V" Receiver HVP_packNegativeV = ((rx_frame.data.u8[3] & (0xFFU)) << 8) | (rx_frame.data.u8[2] & (0xFFU)); //: 16|16@1- (0.1,0) [-550|550] "V" Receiver HVP_packPositiveV = ((rx_frame.data.u8[5] & (0xFFU)) << 8) | (rx_frame.data.u8[4] & (0xFFU)); //: 32|16@1- (0.1,0) [-550|550] "V" Receiver HVP_pyroAnalog = ((rx_frame.data.u8[7] & (0x0FU)) << 8) | (rx_frame.data.u8[6] & (0xFFU)); //: 48|12@1+ (0.1,0) [0|3] "V" Receiver } if (mux == 3) { HVP_dcLinkNegativeV = ((rx_frame.data.u8[2] & (0xFFU)) << 8) | (rx_frame.data.u8[1] & (0xFFU)); //: 8|16@1- (0.1,0) [-550|550] "V" Receiver HVP_dcLinkPositiveV = ((rx_frame.data.u8[4] & (0xFFU)) << 8) | (rx_frame.data.u8[3] & (0xFFU)); //: 24|16@1- (0.1,0) [-550|550] "V" Receiver HVP_fcLinkNegativeV = ((rx_frame.data.u8[6] & (0xFFU)) << 8) | (rx_frame.data.u8[5] & (0xFFU)); //: 40|16@1- (0.1,0) [-550|550] "V" Receiver } if (mux == 4) { HVP_fcContCoilCurrent = ((rx_frame.data.u8[1] & (0xFFU)) << 4) | ((rx_frame.data.u8[0] >> 4) & (0x0FU)); //: 4|12@1+ (0.1,0) [0|7.5] "A" Receiver HVP_fcContVoltage = ((rx_frame.data.u8[3] & (0x0FU)) << 8) | (rx_frame.data.u8[2] & (0xFFU)); //: 16|12@1+ (0.1,0) [0|30] "V" Receiver HVP_hvilInVoltage = ((rx_frame.data.u8[4] & (0xFFU)) << 4) | ((rx_frame.data.u8[3] >> 4) & (0x0FU)); //: 28|12@1+ (0.1,0) [0|30] "V" Receiver HVP_hvilOutVoltage = ((rx_frame.data.u8[6] & (0x0FU)) << 8) | (rx_frame.data.u8[5] & (0xFFU)); //: 40|12@1+ (0.1,0) [0|30] "V" Receiver } if (mux == 5) { HVP_fcLinkPositiveV = ((rx_frame.data.u8[2] & (0xFFU)) << 8) | (rx_frame.data.u8[1] & (0xFFU)); //: 8|16@1- (0.1,0) [-550|550] "V" Receiver HVP_packContCoilCurrent = ((rx_frame.data.u8[4] & (0x0FU)) << 8) | (rx_frame.data.u8[3] & (0xFFU)); //: 24|12@1+ (0.1,0) [0|7.5] "A" Receiver HVP_battery12V = ((rx_frame.data.u8[5] & (0xFFU)) << 4) | ((rx_frame.data.u8[4] >> 4) & (0x0FU)); //: 36|12@1+ (0.1,0) [0|30] "V" Receiver HVP_shuntRefVoltageDbg = ((rx_frame.data.u8[7] & (0xFFU)) << 8) | (rx_frame.data.u8[6] & (0xFFU)); //: 48|16@1- (0.001,0) [-32.768|32.767] "V" Receiver } if (mux == 6) { HVP_shuntAuxCurrentDbg = ((rx_frame.data.u8[2] & (0xFFU)) << 8) | (rx_frame.data.u8[1] & (0xFFU)); //: 8|16@1- (0.1,0) [-3276.8|3276.7] "A" Receiver HVP_shuntBarTempDbg = ((rx_frame.data.u8[4] & (0xFFU)) << 8) | (rx_frame.data.u8[3] & (0xFFU)); //: 24|16@1- (0.01,0) [-327.67|327.67] "C" Receiver HVP_shuntAsicTempDbg = ((rx_frame.data.u8[6] & (0xFFU)) << 8) | (rx_frame.data.u8[5] & (0xFFU)); //: 40|16@1- (0.01,0) [-327.67|327.67] "C" Receiver HVP_shuntAuxCurrentStatus = (rx_frame.data.u8[7] & (0x03U)); //: 56|2@1+ (1,0) [0|3] "" Receiver HVP_shuntBarTempStatus = ((rx_frame.data.u8[7] >> 2) & (0x03U)); //: 58|2@1+ (1,0) [0|3] "" Receiver HVP_shuntAsicTempStatus = ((rx_frame.data.u8[7] >> 4) & (0x03U)); //: 60|2@1+ (1,0) [0|3] "" Receiver } break; /* We ignore 0x3AA for now, as on later software/firmware this is a muxed frame so values aren't correct case 0x3aa: //HVP_alertMatrix1 battery_WatchdogReset = (rx_frame.data.u8[0] & 0x01); battery_PowerLossReset = ((rx_frame.data.u8[0] & 0x02) >> 1); battery_SwAssertion = ((rx_frame.data.u8[0] & 0x04) >> 2); battery_CrashEvent = ((rx_frame.data.u8[0] & 0x08) >> 3); battery_OverDchgCurrentFault = ((rx_frame.data.u8[0] & 0x10) >> 4); battery_OverChargeCurrentFault = ((rx_frame.data.u8[0] & 0x20) >> 5); battery_OverCurrentFault = ((rx_frame.data.u8[0] & 0x40) >> 6); battery_OverTemperatureFault = ((rx_frame.data.u8[0] & 0x80) >> 7); battery_OverVoltageFault = (rx_frame.data.u8[1] & 0x01); battery_UnderVoltageFault = ((rx_frame.data.u8[1] & 0x02) >> 1); battery_PrimaryBmbMiaFault = ((rx_frame.data.u8[1] & 0x04) >> 2); battery_SecondaryBmbMiaFault = ((rx_frame.data.u8[1] & 0x08) >> 3); battery_BmbMismatchFault = ((rx_frame.data.u8[1] & 0x10) >> 4); battery_BmsHviMiaFault = ((rx_frame.data.u8[1] & 0x20) >> 5); battery_CpMiaFault = ((rx_frame.data.u8[1] & 0x40) >> 6); battery_PcsMiaFault = ((rx_frame.data.u8[1] & 0x80) >> 7); battery_BmsFault = (rx_frame.data.u8[2] & 0x01); battery_PcsFault = ((rx_frame.data.u8[2] & 0x02) >> 1); battery_CpFault = ((rx_frame.data.u8[2] & 0x04) >> 2); battery_ShuntHwMiaFault = ((rx_frame.data.u8[2] & 0x08) >> 3); battery_PyroMiaFault = ((rx_frame.data.u8[2] & 0x10) >> 4); battery_hvsMiaFault = ((rx_frame.data.u8[2] & 0x20) >> 5); battery_hviMiaFault = ((rx_frame.data.u8[2] & 0x40) >> 6); battery_Supply12vFault = ((rx_frame.data.u8[2] & 0x80) >> 7); battery_VerSupplyFault = (rx_frame.data.u8[3] & 0x01); battery_HvilFault = ((rx_frame.data.u8[3] & 0x02) >> 1); battery_BmsHvsMiaFault = ((rx_frame.data.u8[3] & 0x04) >> 2); battery_PackVoltMismatchFault = ((rx_frame.data.u8[3] & 0x08) >> 3); battery_EnsMiaFault = ((rx_frame.data.u8[3] & 0x10) >> 4); battery_PackPosCtrArcFault = ((rx_frame.data.u8[3] & 0x20) >> 5); battery_packNegCtrArcFault = ((rx_frame.data.u8[3] & 0x40) >> 6); battery_ShuntHwAndBmsMiaFault = ((rx_frame.data.u8[3] & 0x80) >> 7); battery_fcContHwFault = (rx_frame.data.u8[4] & 0x01); battery_robinOverVoltageFault = ((rx_frame.data.u8[4] & 0x02) >> 1); battery_packContHwFault = ((rx_frame.data.u8[4] & 0x04) >> 2); battery_pyroFuseBlown = ((rx_frame.data.u8[4] & 0x08) >> 3); battery_pyroFuseFailedToBlow = ((rx_frame.data.u8[4] & 0x10) >> 4); battery_CpilFault = ((rx_frame.data.u8[4] & 0x20) >> 5); battery_PackContactorFellOpen = ((rx_frame.data.u8[4] & 0x40) >> 6); battery_FcContactorFellOpen = ((rx_frame.data.u8[4] & 0x80) >> 7); battery_packCtrCloseBlocked = (rx_frame.data.u8[5] & 0x01); battery_fcCtrCloseBlocked = ((rx_frame.data.u8[5] & 0x02) >> 1); battery_packContactorForceOpen = ((rx_frame.data.u8[5] & 0x04) >> 2); battery_fcContactorForceOpen = ((rx_frame.data.u8[5] & 0x08) >> 3); battery_dcLinkOverVoltage = ((rx_frame.data.u8[5] & 0x10) >> 4); battery_shuntOverTemperature = ((rx_frame.data.u8[5] & 0x20) >> 5); battery_passivePyroDeploy = ((rx_frame.data.u8[5] & 0x40) >> 6); battery_logUploadRequest = ((rx_frame.data.u8[5] & 0x80) >> 7); battery_packCtrCloseFailed = (rx_frame.data.u8[6] & 0x01); battery_fcCtrCloseFailed = ((rx_frame.data.u8[6] & 0x02) >> 1); battery_shuntThermistorMia = ((rx_frame.data.u8[6] & 0x04) >> 2); break;*/ case 0x320: //800 BMS_alertMatrix //BMS_alertMatrix 800 BMS_alertMatrix: 8 VEH datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; mux = (rx_frame.data.u8[0] & (0x0F)); if (mux == 0) { //mux0 BMS_matrixIndex = (rx_frame.data.u8[0] & (0x0F)); // 0|4@1+ (1,0) [0|0] "" X BMS_a017_SW_Brick_OV = ((rx_frame.data.u8[2] >> 4) & (0x01)); //20|1@1+ (1,0) [0|0] "" X BMS_a018_SW_Brick_UV = ((rx_frame.data.u8[2] >> 5) & (0x01)); //21|1@1+ (1,0) [0|0] "" X BMS_a019_SW_Module_OT = ((rx_frame.data.u8[2] >> 6) & (0x01)); //22|1@1+ (1,0) [0|0] "" X BMS_a021_SW_Dr_Limits_Regulation = (rx_frame.data.u8[3] & (0x01U)); //24|1@1+ (1,0) [0|0] "" X BMS_a022_SW_Over_Current = ((rx_frame.data.u8[3] >> 1) & (0x01U)); //25|1@1+ (1,0) [0|0] "" X BMS_a023_SW_Stack_OV = ((rx_frame.data.u8[3] >> 2) & (0x01U)); //26|1@1+ (1,0) [0|0] "" X BMS_a024_SW_Islanded_Brick = ((rx_frame.data.u8[3] >> 3) & (0x01U)); //27|1@1+ (1,0) [0|0] "" X BMS_a025_SW_PwrBalance_Anomaly = ((rx_frame.data.u8[3] >> 4) & (0x01U)); //28|1@1+ (1,0) [0|0] "" X BMS_a026_SW_HFCurrent_Anomaly = ((rx_frame.data.u8[3] >> 5) & (0x01U)); //29|1@1+ (1,0) [0|0] "" X BMS_a034_SW_Passive_Isolation = ((rx_frame.data.u8[4] >> 5) & (0x01U)); //37|1@1+ (1,0) [0|0] "" X ? BMS_a035_SW_Isolation = ((rx_frame.data.u8[4] >> 6) & (0x01U)); //38|1@1+ (1,0) [0|0] "" X BMS_a036_SW_HvpHvilFault = ((rx_frame.data.u8[4] >> 6) & (0x01U)); //39|1@1+ (1,0) [0|0] "" X BMS_a037_SW_Flood_Port_Open = (rx_frame.data.u8[5] & (0x01U)); //40|1@1+ (1,0) [0|0] "" X BMS_a039_SW_DC_Link_Over_Voltage = ((rx_frame.data.u8[5] >> 2) & (0x01U)); //42|1@1+ (1,0) [0|0] "" X BMS_a041_SW_Power_On_Reset = ((rx_frame.data.u8[5] >> 4) & (0x01U)); //44|1@1+ (1,0) [0|0] "" X BMS_a042_SW_MPU_Error = ((rx_frame.data.u8[5] >> 5) & (0x01U)); //45|1@1+ (1,0) [0|0] "" X BMS_a043_SW_Watch_Dog_Reset = ((rx_frame.data.u8[5] >> 6) & (0x01U)); //46|1@1+ (1,0) [0|0] "" X BMS_a044_SW_Assertion = ((rx_frame.data.u8[5] >> 7) & (0x01U)); //47|1@1+ (1,0) [0|0] "" X BMS_a045_SW_Exception = (rx_frame.data.u8[6] & (0x01U)); //48|1@1+ (1,0) [0|0] "" X BMS_a046_SW_Task_Stack_Usage = ((rx_frame.data.u8[6] >> 1) & (0x01U)); //49|1@1+ (1,0) [0|0] "" X BMS_a047_SW_Task_Stack_Overflow = ((rx_frame.data.u8[6] >> 2) & (0x01U)); //50|1@1+ (1,0) [0|0] "" X BMS_a048_SW_Log_Upload_Request = ((rx_frame.data.u8[6] >> 3) & (0x01U)); //51|1@1+ (1,0) [0|0] "" X BMS_a050_SW_Brick_Voltage_MIA = ((rx_frame.data.u8[6] >> 5) & (0x01U)); //53|1@1+ (1,0) [0|0] "" X BMS_a051_SW_HVC_Vref_Bad = ((rx_frame.data.u8[6] >> 6) & (0x01U)); //54|1@1+ (1,0) [0|0] "" X BMS_a052_SW_PCS_MIA = ((rx_frame.data.u8[6] >> 7) & (0x01U)); //55|1@1+ (1,0) [0|0] "" X BMS_a053_SW_ThermalModel_Sanity = (rx_frame.data.u8[7] & (0x01U)); //56|1@1+ (1,0) [0|0] "" X BMS_a054_SW_Ver_Supply_Fault = ((rx_frame.data.u8[7] >> 1) & (0x01U)); //57|1@1+ (1,0) [0|0] "" X BMS_a059_SW_Pack_Voltage_Sensing = ((rx_frame.data.u8[7] >> 6) & (0x01U)); //62|1@1+ (1,0) [0|0] "" X BMS_a060_SW_Leakage_Test_Failure = ((rx_frame.data.u8[7] >> 7) & (0x01U)); //63|1@1+ (1,0) [0|0] "" X } if (mux == 1) { //mux1 BMS_a061_robinBrickOverVoltage = ((rx_frame.data.u8[0] >> 4) & (0x01U)); //4|1@1+ (1,0) [0|0] "" X BMS_a062_SW_BrickV_Imbalance = ((rx_frame.data.u8[0] >> 5) & (0x01U)); //5|1@1+ (1,0) [0|0] "" X BMS_a063_SW_ChargePort_Fault = ((rx_frame.data.u8[0] >> 6) & (0x01U)); //6|1@1+ (1,0) [0|0] "" X BMS_a064_SW_SOC_Imbalance = ((rx_frame.data.u8[0] >> 7) & (0x01U)); //7|1@1+ (1,0) [0|0] "" X BMS_a069_SW_Low_Power = ((rx_frame.data.u8[1] >> 4) & (0x01U)); //12|1@1+ (1,0) [0|0] "" X BMS_a071_SW_SM_TransCon_Not_Met = ((rx_frame.data.u8[1] >> 6) & (0x01U)); //14|1@1+ (1,0) [0|0] "" X BMS_a075_SW_Chg_Disable_Failure = ((rx_frame.data.u8[2] >> 2) & (0x01U)); //18|1@1+ (1,0) [0|0] "" X BMS_a076_SW_Dch_While_Charging = ((rx_frame.data.u8[2] >> 3) & (0x01U)); //19|1@1+ (1,0) [0|0] "" X BMS_a077_SW_Charger_Regulation = ((rx_frame.data.u8[2] >> 4) & (0x01U)); //20|1@1+ (1,0) [0|0] "" X BMS_a081_SW_Ctr_Close_Blocked = (rx_frame.data.u8[3] & (0x01U)); //24|1@1+ (1,0) [0|0] "" X BMS_a082_SW_Ctr_Force_Open = ((rx_frame.data.u8[3] >> 1) & (0x01U)); //25|1@1+ (1,0) [0|0] "" X BMS_a083_SW_Ctr_Close_Failure = ((rx_frame.data.u8[3] >> 2) & (0x01U)); //26|1@1+ (1,0) [0|0] "" X BMS_a084_SW_Sleep_Wake_Aborted = ((rx_frame.data.u8[3] >> 3) & (0x01U)); //27|1@1+ (1,0) [0|0] "" X BMS_a087_SW_Feim_Test_Blocked = ((rx_frame.data.u8[3] >> 6) & (0x01U)); //30|1@1+ (1,0) [0|0] "" X BMS_a088_SW_VcFront_MIA_InDrive = ((rx_frame.data.u8[3] >> 7) & (0x01U)); //31|1@1+ (1,0) [0|0] "" X BMS_a089_SW_VcFront_MIA = (rx_frame.data.u8[4] & (0x01U)); //32|1@1+ (1,0) [0|0] "" X BMS_a090_SW_Gateway_MIA = ((rx_frame.data.u8[4] >> 1) & (0x01U)); //33|1@1+ (1,0) [0|0] "" X BMS_a091_SW_ChargePort_MIA = ((rx_frame.data.u8[4] >> 2) & (0x01U)); //34|1@1+ (1,0) [0|0] "" X BMS_a092_SW_ChargePort_Mia_On_Hv = ((rx_frame.data.u8[4] >> 3) & (0x01U)); //35|1@1+ (1,0) [0|0] "" X BMS_a094_SW_Drive_Inverter_MIA = ((rx_frame.data.u8[4] >> 5) & (0x01U)); //37|1@1+ (1,0) [0|0] "" X BMS_a099_SW_BMB_Communication = ((rx_frame.data.u8[5] >> 2) & (0x01U)); //42|1@1+ (1,0) [0|0] "" X BMS_a105_SW_One_Module_Tsense = (rx_frame.data.u8[6] & (0x01U)); //48|1@1+ (1,0) [0|0] "" X BMS_a106_SW_All_Module_Tsense = ((rx_frame.data.u8[6] >> 1) & (0x01U)); //49|1@1+ (1,0) [0|0] "" X BMS_a107_SW_Stack_Voltage_MIA = ((rx_frame.data.u8[6] >> 2) & (0x01U)); //50|1@1+ (1,0) [0|0] "" X } if (mux == 2) { //mux2 BMS_a121_SW_NVRAM_Config_Error = ((rx_frame.data.u8[0] >> 4) & (0x01U)); // 4|1@1+ (1,0) [0|0] "" X BMS_a122_SW_BMS_Therm_Irrational = ((rx_frame.data.u8[0] >> 5) & (0x01U)); //5|1@1+ (1,0) [0|0] "" X BMS_a123_SW_Internal_Isolation = ((rx_frame.data.u8[0] >> 6) & (0x01U)); //6|1@1+ (1,0) [0|0] "" X BMS_a127_SW_shunt_SNA = ((rx_frame.data.u8[1] >> 2) & (0x01U)); //10|1@1+ (1,0) [0|0] "" X BMS_a128_SW_shunt_MIA = ((rx_frame.data.u8[1] >> 3) & (0x01U)); //11|1@1+ (1,0) [0|0] "" X BMS_a129_SW_VSH_Failure = ((rx_frame.data.u8[1] >> 4) & (0x01U)); //12|1@1+ (1,0) [0|0] "" X BMS_a130_IO_CAN_Error = ((rx_frame.data.u8[1] >> 5) & (0x01U)); //13|1@1+ (1,0) [0|0] "" X BMS_a131_Bleed_FET_Failure = ((rx_frame.data.u8[1] >> 6) & (0x01U)); //14|1@1+ (1,0) [0|0] "" X BMS_a132_HW_BMB_OTP_Uncorrctbl = ((rx_frame.data.u8[1] >> 7) & (0x01U)); //15|1@1+ (1,0) [0|0] "" X BMS_a134_SW_Delayed_Ctr_Off = ((rx_frame.data.u8[2] >> 1) & (0x01U)); //17|1@1+ (1,0) [0|0] "" X BMS_a136_SW_Module_OT_Warning = ((rx_frame.data.u8[2] >> 3) & (0x01U)); //19|1@1+ (1,0) [0|0] "" X BMS_a137_SW_Brick_UV_Warning = ((rx_frame.data.u8[2] >> 4) & (0x01U)); //20|1@1+ (1,0) [0|0] "" X BMS_a138_SW_Brick_OV_Warning = ((rx_frame.data.u8[2] >> 5) & (0x01U)); //21|1@1+ (1,0) [0|0] "" X BMS_a139_SW_DC_Link_V_Irrational = ((rx_frame.data.u8[2] >> 6) & (0x01U)); //22|1@1+ (1,0) [0|0] "" X BMS_a141_SW_BMB_Status_Warning = (rx_frame.data.u8[3] & (0x01U)); //24|1@1+ (1,0) [0|0] "" X BMS_a144_Hvp_Config_Mismatch = ((rx_frame.data.u8[3] >> 3) & (0x01U)); //27|1@1+ (1,0) [0|0] "" X BMS_a145_SW_SOC_Change = ((rx_frame.data.u8[3] >> 4) & (0x01U)); //28|1@1+ (1,0) [0|0] "" X BMS_a146_SW_Brick_Overdischarged = ((rx_frame.data.u8[3] >> 5) & (0x01U)); //29|1@1+ (1,0) [0|0] "" X BMS_a149_SW_Missing_Config_Block = (rx_frame.data.u8[4] & (0x01U)); //32|1@1+ (1,0) [0|0] "" X BMS_a151_SW_external_isolation = ((rx_frame.data.u8[4] >> 2) & (0x01U)); //34|1@1+ (1,0) [0|0] "" X BMS_a156_SW_BMB_Vref_bad = ((rx_frame.data.u8[4] >> 7) & (0x01U)); //39|1@1+ (1,0) [0|0] "" X BMS_a157_SW_HVP_HVS_Comms = (rx_frame.data.u8[5] & (0x01U)); //40|1@1+ (1,0) [0|0] "" X BMS_a158_SW_HVP_HVI_Comms = ((rx_frame.data.u8[5] >> 1) & (0x01U)); //41|1@1+ (1,0) [0|0] "" X BMS_a159_SW_HVP_ECU_Error = ((rx_frame.data.u8[5] >> 2) & (0x01U)); //42|1@1+ (1,0) [0|0] "" X BMS_a161_SW_DI_Open_Request = ((rx_frame.data.u8[5] >> 4) & (0x01U)); //44|1@1+ (1,0) [0|0] "" X BMS_a162_SW_No_Power_For_Support = ((rx_frame.data.u8[5] >> 5) & (0x01U)); //45|1@1+ (1,0) [0|0] "" X BMS_a163_SW_Contactor_Mismatch = ((rx_frame.data.u8[5] >> 6) & (0x01U)); //46|1@1+ (1,0) [0|0] "" X BMS_a164_SW_Uncontrolled_Regen = ((rx_frame.data.u8[5] >> 7) & (0x01U)); //47|1@1+ (1,0) [0|0] "" X BMS_a165_SW_Pack_Partial_Weld = (rx_frame.data.u8[6] & (0x01U)); //48|1@1+ (1,0) [0|0] "" X BMS_a166_SW_Pack_Full_Weld = ((rx_frame.data.u8[6] >> 1) & (0x01U)); //49|1@1+ (1,0) [0|0] "" X BMS_a167_SW_FC_Partial_Weld = ((rx_frame.data.u8[6] >> 2) & (0x01U)); //50|1@1+ (1,0) [0|0] "" X BMS_a168_SW_FC_Full_Weld = ((rx_frame.data.u8[6] >> 3) & (0x01U)); //51|1@1+ (1,0) [0|0] "" X BMS_a169_SW_FC_Pack_Weld = ((rx_frame.data.u8[6] >> 4) & (0x01U)); //52|1@1+ (1,0) [0|0] "" X BMS_a170_SW_Limp_Mode = ((rx_frame.data.u8[6] >> 5) & (0x01U)); //53|1@1+ (1,0) [0|0] "" X BMS_a171_SW_Stack_Voltage_Sense = ((rx_frame.data.u8[6] >> 6) & (0x01U)); //54|1@1+ (1,0) [0|0] "" X BMS_a174_SW_Charge_Failure = ((rx_frame.data.u8[7] >> 1) & (0x01U)); //57|1@1+ (1,0) [0|0] "" X BMS_a176_SW_GracefulPowerOff = ((rx_frame.data.u8[7] >> 3) & (0x01U)); //59|1@1+ (1,0) [0|0] "" X BMS_a179_SW_Hvp_12V_Fault = ((rx_frame.data.u8[7] >> 6) & (0x01U)); //62|1@1+ (1,0) [0|0] "" X BMS_a180_SW_ECU_reset_blocked = ((rx_frame.data.u8[7] >> 7) & (0x01U)); //63|1@1+ (1,0) [0|0] "" X } break; case 0x72A: //BMS_serialNumber datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; //Pack serial number in ASCII: 00 54 47 33 32 31 32 30 (mux 0) .TG32120 + 01 32 30 30 33 41 48 58 (mux 1) .2003AHX = TG321202003AHX if (rx_frame.data.u8[0] == 0x00 && !parsed_battery_serialNumber) { // Serial number 1-7 battery_serialNumber[0] = rx_frame.data.u8[1]; battery_serialNumber[1] = rx_frame.data.u8[2]; battery_serialNumber[2] = rx_frame.data.u8[3]; battery_serialNumber[3] = rx_frame.data.u8[4]; battery_serialNumber[4] = rx_frame.data.u8[5]; battery_serialNumber[5] = rx_frame.data.u8[6]; battery_serialNumber[6] = rx_frame.data.u8[7]; } if (rx_frame.data.u8[0] == 0x01 && !parsed_battery_serialNumber) { // Serial number 8-14 battery_serialNumber[7] = rx_frame.data.u8[1]; battery_serialNumber[8] = rx_frame.data.u8[2]; battery_serialNumber[9] = rx_frame.data.u8[3]; battery_serialNumber[10] = rx_frame.data.u8[4]; battery_serialNumber[11] = rx_frame.data.u8[5]; battery_serialNumber[12] = rx_frame.data.u8[6]; battery_serialNumber[13] = rx_frame.data.u8[7]; } if (battery_serialNumber[6] != 0 && battery_serialNumber[12] != 0 && !parsed_battery_serialNumber) { // Serial number complete //Manufacture year char yearStr[5]; // Full year string (including the "20" prefix) snprintf(yearStr, sizeof(yearStr), "20%c%c", battery_serialNumber[3], battery_serialNumber[4]); int year = atoi(yearStr); //Manufacture day (Julian calendar) char dayStr[4]; snprintf(dayStr, sizeof(dayStr), "%c%c%c", battery_serialNumber[5], battery_serialNumber[6], battery_serialNumber[7]); int day = atoi(dayStr); battery_manufactureDate = dayOfYearToDate(year, day); parsed_battery_serialNumber = true; } break; case 0x300: //BMS_info datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; //Display internal BMS info and other build/version data if (rx_frame.data.u8[0] == 0x0A) { // Mux 10: BUILD_HWID_COMPONENTID if (BMS_info_buildConfigId == 0) { BMS_info_buildConfigId = static_cast(extract_signal_value(rx_frame.data.u8, 16, 16)); } if (BMS_info_hardwareId == 0) { BMS_info_hardwareId = static_cast(extract_signal_value(rx_frame.data.u8, 32, 16)); } if (BMS_info_componentId == 0) { BMS_info_componentId = static_cast(extract_signal_value(rx_frame.data.u8, 48, 16)); } } /* if (rx_frame.data.u8[0] == 0x0B) { // Mux 11: PCBAID_ASSYID_USAGEID if (BMS_info_pcbaId == 0) {BMS_info_pcbaId = static_cast(extract_signal_value(rx_frame.data.u8, 16, 8));} if (BMS_info_assemblyId == 0) {BMS_info_assemblyId = static_cast(extract_signal_value(rx_frame.data.u8, 24, 8));} if (BMS_info_usageId == 0) {BMS_info_usageId = static_cast(extract_signal_value(rx_frame.data.u8, 32, 16));} if (BMS_info_subUsageId == 0) {BMS_info_subUsageId = static_cast(extract_signal_value(rx_frame.data.u8, 48, 16));} } if (rx_frame.data.u8[0] == 0x0D) { // Mux 13: APP_CRC if (BMS_info_platformType == 0) {BMS_info_platformType = static_cast(extract_signal_value(rx_frame.data.u8, 8, 8));} if (BMS_info_appCrc == 0) {BMS_info_appCrc = static_cast(extract_signal_value(rx_frame.data.u8, 32, 32));} } if (rx_frame.data.u8[0] == 0x12) { // Mux 18: BOOTLOADER_GITHASH if (BMS_info_bootGitHash == 0) {BMS_info_bootGitHash = static_cast(extract_signal_value(rx_frame.data.u8, 10, 54));} } if (rx_frame.data.u8[0] == 0x14) { // Mux 20: UDS_PROTOCOL_BOOTCRC if (BMS_info_bootUdsProtoVersion == 0) {BMS_info_bootUdsProtoVersion = static_cast(extract_signal_value(rx_frame.data.u8, 8, 8));} if (BMS_info_bootCrc == 0) {BMS_info_bootCrc = static_cast(extract_signal_value(rx_frame.data.u8, 32, 32));} } */ break; case 0x3C4: //PCS_info datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; //Display internal PCS info and other build/version data if (rx_frame.data.u8[0] == 0x0A) { // Mux 10: BUILD_HWID_COMPONENTID if (PCS_info_buildConfigId == 0) { PCS_info_buildConfigId = static_cast(extract_signal_value(rx_frame.data.u8, 16, 16)); } if (PCS_info_hardwareId == 0) { PCS_info_hardwareId = static_cast(extract_signal_value(rx_frame.data.u8, 32, 16)); } if (PCS_info_componentId == 0) { PCS_info_componentId = static_cast(extract_signal_value(rx_frame.data.u8, 48, 16)); } } /* if (rx_frame.data.u8[0] == 0x0B) { // Mux 11: PCBAID_ASSYID_USAGEID if (PCS_info_pcbaId == 0) {PCS_info_pcbaId = static_cast(extract_signal_value(rx_frame.data.u8, 16, 8));} if (PCS_info_assemblyId == 0) {PCS_info_assemblyId = static_cast(extract_signal_value(rx_frame.data.u8, 24, 8));} if (PCS_info_usageId == 0) {PCS_info_usageId = static_cast(extract_signal_value(rx_frame.data.u8, 32, 16));} if (PCS_info_subUsageId == 0) {PCS_info_subUsageId = static_cast(extract_signal_value(rx_frame.data.u8, 48, 16));} } if (rx_frame.data.u8[0] == 0x0D) { // Mux 13: APP_CRC PCS_info_platformType = static_cast(extract_signal_value(rx_frame.data.u8, 8, 8)); PCS_info_appCrc = static_cast(extract_signal_value(rx_frame.data.u8, 32, 32)); } if (rx_frame.data.u8[0] == 0x10) { // Mux 16: SUBCOMPONENT PCS_info_cpu2AppCrc = static_cast(extract_signal_value(rx_frame.data.u8, 32, 32)); } if (rx_frame.data.u8[0] == 0x12) { // Mux 18: BOOTLOADER_GITHASH PCS_info_bootGitHash = static_cast(extract_signal_value(rx_frame.data.u8, 10, 54)); } if (rx_frame.data.u8[0] == 0x14) { // Mux 20: UDS_PROTOCOL_BOOTCRC PCS_info_bootUdsProtoVersion = static_cast(extract_signal_value(rx_frame.data.u8, 8, 8)); PCS_info_bootCrc = static_cast(extract_signal_value(rx_frame.data.u8, 32, 32)); } */ //PCS Part Number in ASCII if (rx_frame.data.u8[0] == 0x19 && !parsed_PCS_partNumber) { // Part number 1-7 PCS_partNumber[0] = rx_frame.data.u8[1]; PCS_partNumber[1] = rx_frame.data.u8[2]; PCS_partNumber[2] = rx_frame.data.u8[3]; PCS_partNumber[3] = rx_frame.data.u8[4]; PCS_partNumber[4] = rx_frame.data.u8[5]; PCS_partNumber[5] = rx_frame.data.u8[6]; PCS_partNumber[6] = rx_frame.data.u8[7]; } if (rx_frame.data.u8[0] == 0x1A && !parsed_PCS_partNumber) { // Part number 8-14 PCS_partNumber[7] = rx_frame.data.u8[1]; PCS_partNumber[8] = rx_frame.data.u8[2]; PCS_partNumber[9] = rx_frame.data.u8[3]; PCS_partNumber[10] = rx_frame.data.u8[4]; PCS_partNumber[11] = rx_frame.data.u8[5]; PCS_partNumber[12] = rx_frame.data.u8[6]; } if (PCS_partNumber[6] != 0 && PCS_partNumber[11] != 0) { parsed_PCS_partNumber = true; } break; case 0x310: //HVP_info datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; //Display internal HVP info and other build/version data if (rx_frame.data.u8[0] == 0x0A) { // Mux 10: BUILD_HWID_COMPONENTID if (HVP_info_buildConfigId == 0) { HVP_info_buildConfigId = static_cast(extract_signal_value(rx_frame.data.u8, 16, 16)); } if (HVP_info_hardwareId == 0) { HVP_info_hardwareId = static_cast(extract_signal_value(rx_frame.data.u8, 32, 16)); } if (HVP_info_componentId == 0) { HVP_info_componentId = static_cast(extract_signal_value(rx_frame.data.u8, 48, 16)); } } /* if (rx_frame.data.u8[0] == 0x0B) { // Mux 11: PCBAID_ASSYID_USAGEID if (HVP_info_pcbaId == 0) {HVP_info_pcbaId = static_cast(extract_signal_value(rx_frame.data.u8, 16, 8));} if (HVP_info_assemblyId == 0) {HVP_info_assemblyId = static_cast(extract_signal_value(rx_frame.data.u8, 24, 8));} if (HVP_info_usageId == 0) {HVP_info_usageId = static_cast(extract_signal_value(rx_frame.data.u8, 32, 16));} if (HVP_info_subUsageId == 0) {HVP_info_subUsageId = static_cast(extract_signal_value(rx_frame.data.u8, 48, 16));} } if (rx_frame.data.u8[0] == 0x0D) { // Mux 13: APP_CRC HVP_info_platformType = static_cast(extract_signal_value(rx_frame.data.u8, 8, 8)); HVP_info_appCrc = static_cast(extract_signal_value(rx_frame.data.u8, 32, 32)); } if (rx_frame.data.u8[0] == 0x12) { // Mux 18: BOOTLOADER_GITHASH HVP_info_bootGitHash = static_cast(extract_signal_value(rx_frame.data.u8, 10, 54)); } if (rx_frame.data.u8[0] == 0x14) { // Mux 20: UDS_PROTOCOL_BOOTCRC HVP_info_bootUdsProtoVersion = static_cast(extract_signal_value(rx_frame.data.u8, 8, 8)); HVP_info_bootCrc = static_cast(extract_signal_value(rx_frame.data.u8, 32, 32)); } */ break; case 0x612: // CAN UDSs for BMS datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; //BMS Query if (stateMachineBMSQuery != 0xFF && stateMachineBMSReset == 0xFF) { if (memcmp(rx_frame.data.u8, "\x02\x50\x03\xAA\xAA\xAA\xAA\xAA", 8) == 0) { //Received initial response, proceed to actual query logging.println("CAN UDS: Received BMS query initial handshake reply"); stateMachineBMSQuery = 1; break; } if (memcmp(&rx_frame.data.u8[0], "\x10", 1) == 0) { //Received first data frame battery_partNumber[0] = rx_frame.data.u8[5]; battery_partNumber[1] = rx_frame.data.u8[6]; battery_partNumber[2] = rx_frame.data.u8[7]; logging.println("CAN UDS: Received BMS query data frame"); stateMachineBMSQuery = 2; break; } if (memcmp(&rx_frame.data.u8[0], "\x21", 1) == 0) { //Second part of part number after flow control battery_partNumber[3] = rx_frame.data.u8[1]; battery_partNumber[4] = rx_frame.data.u8[2]; battery_partNumber[5] = rx_frame.data.u8[3]; battery_partNumber[6] = rx_frame.data.u8[4]; battery_partNumber[7] = rx_frame.data.u8[5]; battery_partNumber[8] = rx_frame.data.u8[6]; battery_partNumber[9] = rx_frame.data.u8[7]; logging.println("CAN UDS: Received BMS query second data frame"); break; } if (memcmp(&rx_frame.data.u8[0], "\x22", 1) == 0) { //Final part of part number battery_partNumber[10] = rx_frame.data.u8[1]; battery_partNumber[11] = rx_frame.data.u8[2]; logging.println("CAN UDS: Received BMS query final data frame"); break; } if (memcmp(rx_frame.data.u8, "\x23\x00\x00\x00\xAA\xAA\xAA\xAA", 8) == 0) { //Received final frame logging.println("CAN UDS: Received BMS query termination frame"); parsed_battery_partNumber = true; stateMachineBMSQuery = 0xFF; break; } } //BMS Reset if (stateMachineBMSQuery == 0xFF) { // Make sure this is reset request not query if (memcmp(rx_frame.data.u8, "\x02\x67\x06\xAA\xAA\xAA\xAA\xAA", 8) == 0) { logging.println("CAN UDS: ECU unlocked"); } else if (memcmp(rx_frame.data.u8, "\x03\x7F\x11\x78\xAA\xAA\xAA\xAA", 8) == 0) { logging.println("CAN UDS: ECU reset request successful but ECU busy, response pending"); } else if (memcmp(rx_frame.data.u8, "\x02\x51\x01\xAA\xAA\xAA\xAA\xAA", 8) == 0) { logging.println("CAN UDS: ECU reset positive response, 1 second downtime"); } } break; default: break; } } CAN_frame can_msg_1CF[] = { {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0x60, 0x69}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0x80, 0x89}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0xA0, 0xA9}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0xC0, 0xC9}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0xE0, 0xE9}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0x00, 0x09}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0x20, 0x29}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x1CF, .data = {0x01, 0x00, 0x00, 0x1A, 0x1C, 0x02, 0x40, 0x49}}}; CAN_frame can_msg_118[] = { {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x61, 0x80, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x62, 0x81, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x63, 0x82, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x64, 0x83, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x65, 0x84, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x66, 0x85, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x67, 0x86, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x68, 0x87, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x69, 0x88, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x6A, 0x89, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x6B, 0x8A, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x6C, 0x8B, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x6D, 0x8C, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x6E, 0x8D, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x6F, 0x8E, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}, {.FD = false, .ext_ID = false, .DLC = 8, .ID = 0x118, .data = {0x70, 0x8F, 0x30, 0x10, 0x00, 0x08, 0x00, 0x80}}}; unsigned long lastSend1CF = 0; unsigned long lastSend118 = 0; int index_1CF = 0; int index_118 = 0; void TeslaBattery::transmit_can(unsigned long currentMillis) { if (user_selected_tesla_digital_HVIL) { //Special S/X? mode for 2024+ batteries if ((datalayer.system.status.inverter_allows_contactor_closing) && (datalayer.battery.status.bms_status != FAULT)) { if (currentMillis - lastSend1CF >= 10) { transmit_can_frame(&can_msg_1CF[index_1CF]); index_1CF = (index_1CF + 1) % 8; lastSend1CF = currentMillis; } if (currentMillis - lastSend118 >= 10) { transmit_can_frame(&can_msg_118[index_118]); index_118 = (index_118 + 1) % 16; lastSend118 = currentMillis; } } else { index_1CF = 0; index_118 = 0; } } //Send 10ms messages if (currentMillis - previousMillis10 >= INTERVAL_10_MS) { previousMillis10 = currentMillis; //0x118 DI_systemStatus transmit_can_frame(&TESLA_118); //0x2E1 VCFRONT_status switch (muxNumber_TESLA_2E1) { case 0: transmit_can_frame(&TESLA_2E1_VEHICLE_AND_RAILS); muxNumber_TESLA_2E1++; break; case 1: transmit_can_frame(&TESLA_2E1_HOMELINK); muxNumber_TESLA_2E1++; break; case 2: transmit_can_frame(&TESLA_2E1_REFRIGERANT_SYSTEM); muxNumber_TESLA_2E1++; break; case 3: transmit_can_frame(&TESLA_2E1_LV_BATTERY_DEBUG); muxNumber_TESLA_2E1++; break; case 4: transmit_can_frame(&TESLA_2E1_MUX_5); muxNumber_TESLA_2E1++; break; case 5: transmit_can_frame(&TESLA_2E1_BODY_CONTROLS); muxNumber_TESLA_2E1 = 0; break; default: break; } //Generate next frames generateFrameCounterChecksum(TESLA_118, 8, 4, 0, 8); } //Send 50ms messages if (currentMillis - previousMillis50 >= INTERVAL_50_MS) { previousMillis50 = currentMillis; //0x221 VCFRONT_LVPowerState if (vehicleState == 1) { // Drive switch (muxNumber_TESLA_221) { case 0: generateMuxFrameCounterChecksum(TESLA_221_DRIVE_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8); transmit_can_frame(&TESLA_221_DRIVE_Mux0); muxNumber_TESLA_221++; break; case 1: generateMuxFrameCounterChecksum(TESLA_221_DRIVE_Mux1, frameCounter_TESLA_221, 52, 4, 56, 8); transmit_can_frame(&TESLA_221_DRIVE_Mux1); muxNumber_TESLA_221 = 0; break; default: break; } //Generate next new frame frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } if (vehicleState == 2) { // Accessory switch (muxNumber_TESLA_221) { case 0: generateMuxFrameCounterChecksum(TESLA_221_ACCESSORY_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8); transmit_can_frame(&TESLA_221_ACCESSORY_Mux0); muxNumber_TESLA_221++; break; case 1: generateMuxFrameCounterChecksum(TESLA_221_ACCESSORY_Mux1, frameCounter_TESLA_221, 52, 4, 56, 8); transmit_can_frame(&TESLA_221_ACCESSORY_Mux1); muxNumber_TESLA_221 = 0; break; default: break; } //Generate next new frame frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } if (vehicleState == 3) { // Going down switch (muxNumber_TESLA_221) { case 0: generateMuxFrameCounterChecksum(TESLA_221_GOING_DOWN_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8); transmit_can_frame(&TESLA_221_GOING_DOWN_Mux0); muxNumber_TESLA_221++; break; case 1: generateMuxFrameCounterChecksum(TESLA_221_GOING_DOWN_Mux1, frameCounter_TESLA_221, 52, 4, 56, 8); transmit_can_frame(&TESLA_221_GOING_DOWN_Mux1); muxNumber_TESLA_221 = 0; break; default: break; } //Generate next new frame frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } if (vehicleState == 0) { // Off switch (muxNumber_TESLA_221) { case 0: generateMuxFrameCounterChecksum(TESLA_221_OFF_Mux0, frameCounter_TESLA_221, 52, 4, 56, 8); transmit_can_frame(&TESLA_221_OFF_Mux0); muxNumber_TESLA_221++; break; case 1: generateMuxFrameCounterChecksum(TESLA_221_OFF_Mux1, frameCounter_TESLA_221, 52, 4, 56, 8); transmit_can_frame(&TESLA_221_OFF_Mux1); muxNumber_TESLA_221 = 0; break; default: break; } //Generate next new frame frameCounter_TESLA_221 = (frameCounter_TESLA_221 + 1) % 16; } //0x3C2 VCLEFT_switchStatus switch (muxNumber_TESLA_3C2) { case 0: transmit_can_frame(&TESLA_3C2_Mux0); muxNumber_TESLA_3C2++; break; case 1: transmit_can_frame(&TESLA_3C2_Mux1); muxNumber_TESLA_3C2 = 0; break; default: break; } //0x39D IBST_status transmit_can_frame(&TESLA_39D); if (battery_contactor == 4) { // Contactors closed // Frames to be sent only when contactors closed //0x3A1 VCFRONT_vehicleStatus, critical otherwise VCFRONT_MIA triggered transmit_can_frame(&TESLA_3A1[frameCounter_TESLA_3A1]); frameCounter_TESLA_3A1 = (frameCounter_TESLA_3A1 + 1) % 16; } //Generate next frame generateFrameCounterChecksum(TESLA_39D, 8, 4, 0, 8); } //Send 100ms messages if (currentMillis - previousMillis100 >= INTERVAL_100_MS) { previousMillis100 = currentMillis; //0x102 VCLEFT_doorStatus, static transmit_can_frame(&TESLA_102); //0x103 VCRIGHT_doorStatus, static transmit_can_frame(&TESLA_103); //0x229 SCCM_rightStalk transmit_can_frame(&TESLA_229); //0x241 VCFRONT_coolant, static transmit_can_frame(&TESLA_241); //0x2D1 VCFRONT_okToUseHighPower, static transmit_can_frame(&TESLA_2D1); //0x2A8 CMPD_state transmit_can_frame(&TESLA_2A8); //0x2E8 EPBR_status transmit_can_frame(&TESLA_2E8); //0x7FF GTW_carConfig switch (muxNumber_TESLA_7FF) { case 0: transmit_can_frame(&TESLA_7FF_Mux1); muxNumber_TESLA_7FF++; break; case 1: transmit_can_frame(&TESLA_7FF_Mux2); muxNumber_TESLA_7FF++; break; case 2: transmit_can_frame(&TESLA_7FF_Mux3); muxNumber_TESLA_7FF++; break; case 3: transmit_can_frame(&TESLA_7FF_Mux4); muxNumber_TESLA_7FF++; break; case 4: transmit_can_frame(&TESLA_7FF_Mux5); muxNumber_TESLA_7FF = 0; break; default: break; } //Generate next frames generateTESLA_229(TESLA_229); generateFrameCounterChecksum(TESLA_2A8, 52, 4, 56, 8); generateFrameCounterChecksum(TESLA_2E8, 52, 4, 56, 8); if (stateMachineClearIsolationFault != 0xFF) { //This implementation should be rewritten to actually reply to the UDS responses sent by the BMS //While this may work, it is not the correct way to implement this clearing logic switch (stateMachineClearIsolationFault) { case 0: TESLA_602.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); stateMachineClearIsolationFault = 1; break; case 1: TESLA_602.data = {0x30, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); // BMS should reply 02 50 C0 FF FF FF FF FF stateMachineClearIsolationFault = 2; break; case 2: TESLA_602.data = {0x10, 0x12, 0x27, 0x06, 0x35, 0x34, 0x37, 0x36}; transmit_can_frame(&TESLA_602); // BMS should reply 7E FF FF FF FF FF FF stateMachineClearIsolationFault = 3; break; case 3: TESLA_602.data = {0x21, 0x31, 0x30, 0x33, 0x32, 0x3D, 0x3C, 0x3F}; transmit_can_frame(&TESLA_602); stateMachineClearIsolationFault = 4; break; case 4: TESLA_602.data = {0x22, 0x3E, 0x39, 0x38, 0x3B, 0x3A, 0x00, 0x00}; transmit_can_frame(&TESLA_602); //Should generate a CAN UDS log message indicating ECU unlocked stateMachineClearIsolationFault = 5; break; case 5: TESLA_602.data = {0x04, 0x31, 0x01, 0x04, 0x0A, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); stateMachineClearIsolationFault = 0xFF; break; default: //Something went wrong. Reset all and cancel stateMachineClearIsolationFault = 0xFF; break; } } if (stateMachineBMSReset != 0xFF) { //This implementation should be rewritten to actually reply to the UDS responses sent by the BMS //While this may work, it is not the correct way to implement this reset logic switch (stateMachineBMSReset) { case 0: TESLA_602.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); stateMachineBMSReset = 1; break; case 1: TESLA_602.data = {0x30, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); stateMachineBMSReset = 2; break; case 2: TESLA_602.data = {0x10, 0x12, 0x27, 0x06, 0x35, 0x34, 0x37, 0x36}; transmit_can_frame(&TESLA_602); stateMachineBMSReset = 3; break; case 3: TESLA_602.data = {0x21, 0x31, 0x30, 0x33, 0x32, 0x3D, 0x3C, 0x3F}; transmit_can_frame(&TESLA_602); stateMachineBMSReset = 4; break; case 4: TESLA_602.data = {0x22, 0x3E, 0x39, 0x38, 0x3B, 0x3A, 0x00, 0x00}; transmit_can_frame(&TESLA_602); //Should generate a CAN UDS log message indicating ECU unlocked stateMachineBMSReset = 5; break; case 5: TESLA_602.data = {0x02, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); stateMachineBMSReset = 6; break; case 6: TESLA_602.data = {0x02, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); stateMachineBMSReset = 7; break; case 7: TESLA_602.data = {0x02, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); //Should generate a CAN UDS log message(s) indicating ECU has reset stateMachineBMSReset = 0xFF; break; default: //Something went wrong. Reset all and cancel stateMachineBMSReset = 0xFF; break; } } if (stateMachineSOCReset != 0xFF) { //This implementation should be rewritten to actually reply to the UDS responses sent by the BMS //While this may work, it is not the correct way to implement this switch (stateMachineSOCReset) { case 0: TESLA_602.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); stateMachineSOCReset = 1; break; case 1: TESLA_602.data = {0x30, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); stateMachineSOCReset = 2; break; case 2: TESLA_602.data = {0x10, 0x12, 0x27, 0x06, 0x35, 0x34, 0x37, 0x36}; transmit_can_frame(&TESLA_602); stateMachineSOCReset = 3; break; case 3: TESLA_602.data = {0x21, 0x31, 0x30, 0x33, 0x32, 0x3D, 0x3C, 0x3F}; transmit_can_frame(&TESLA_602); stateMachineSOCReset = 4; break; case 4: TESLA_602.data = {0x22, 0x3E, 0x39, 0x38, 0x3B, 0x3A, 0x00, 0x00}; transmit_can_frame(&TESLA_602); //Should generate a CAN UDS log message indicating ECU unlocked stateMachineSOCReset = 5; break; case 5: TESLA_602.data = {0x04, 0x31, 0x01, 0x04, 0x07, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); stateMachineSOCReset = 0xFF; break; default: //Something went wrong. Reset all and cancel stateMachineSOCReset = 0xFF; break; } } if (stateMachineBMSQuery != 0xFF) { //This implementation should be rewritten to actually reply to the UDS responses sent by the BMS //While this may work, it is not the correct way to implement this query logic switch (stateMachineBMSQuery) { case 0: //Initial request logging.println("CAN UDS: Sending BMS query initial handshake"); TESLA_602.data = {0x02, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); break; case 1: //Send query logging.println("CAN UDS: Sending BMS query for pack part number"); TESLA_602.data = {0x03, 0x22, 0xF0, 0x14, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); break; case 2: //Flow control logging.println("CAN UDS: Sending BMS query flow control"); TESLA_602.data = {0x30, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00}; transmit_can_frame(&TESLA_602); break; case 3: break; case 4: break; default: //Something went wrong. Reset all and cancel stateMachineBMSQuery = 0xFF; break; } } } //Send 500ms messages if (currentMillis - previousMillis500 >= INTERVAL_500_MS) { previousMillis500 = currentMillis; transmit_can_frame(&TESLA_213); transmit_can_frame(&TESLA_284); transmit_can_frame(&TESLA_293); transmit_can_frame(&TESLA_313); transmit_can_frame(&TESLA_333); if (TESLA_334_INITIAL_SENT == false) { transmit_can_frame(&TESLA_334_INITIAL); TESLA_334_INITIAL_SENT = true; } else { transmit_can_frame(&TESLA_334); } transmit_can_frame(&TESLA_3B3); transmit_can_frame(&TESLA_55A); //Generate next frames generateTESLA_213(TESLA_213); generateFrameCounterChecksum(TESLA_293, 52, 4, 56, 8); generateFrameCounterChecksum(TESLA_313, 52, 4, 56, 8); generateFrameCounterChecksum(TESLA_334, 52, 4, 56, 8); } //Send 1000ms messages if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { previousMillis1000 = currentMillis; transmit_can_frame(&TESLA_082); transmit_can_frame(&TESLA_321); //Generate next frames generateFrameCounterChecksum(TESLA_321, 52, 4, 56, 8); } } void printDebugIfActive(uint8_t symbol, const char* message) { if (symbol == 1) { logging.println(message); } } void TeslaBattery::printFaultCodesIfActive() { if (battery_packCtrsClosingBlocked && battery_packContactorSetState != 5) { // Contactors blocked closing and not already closed logging.println("ERROR: Check high voltage connectors and interlock circuit, closing contactors not allowed!"); } if (battery_pyroTestInProgress) { logging.println("ERROR: Please wait for pyro test to finish, HV cables successfully seated!"); } if (datalayer.system.status.inverter_allows_contactor_closing == false) { logging.println( "ERROR: Solar inverter does not allow for contactor closing. Check communication connection to the inverter " "or " "disable the inverter protocol to proceed in Tesla battery testing mode."); } // Check each symbol and print debug information if its value is 1 // 0X3AA: 938 HVP_alertMatrix1 //printDebugIfActive(battery_WatchdogReset, "ERROR: The processor has experienced a reset due to watchdog reset"); //Uncommented due to not affecting usage printDebugIfActive(battery_PowerLossReset, "ERROR: The processor has experienced a reset due to power loss"); printDebugIfActive(battery_SwAssertion, "ERROR: An internal software assertion has failed"); printDebugIfActive(battery_CrashEvent, "ERROR: crash signal is detected by HVP"); printDebugIfActive(battery_OverDchgCurrentFault, "ERROR: Pack discharge current is above the safe max discharge current limit!"); printDebugIfActive(battery_OverChargeCurrentFault, "ERROR: Pack charge current is above the safe max charge current limit!"); printDebugIfActive(battery_OverCurrentFault, "ERROR: Pack current (discharge or charge) is above max current limit!"); printDebugIfActive(battery_OverTemperatureFault, "ERROR: A pack module temperature is above the max temperature limit!"); printDebugIfActive(battery_OverVoltageFault, "ERROR: A brick voltage is above maximum voltage limit"); printDebugIfActive(battery_UnderVoltageFault, "ERROR: A brick voltage is below minimum voltage limit"); printDebugIfActive(battery_PrimaryBmbMiaFault, "ERROR: Voltage and temperature readings from primary BMB chain are mia"); printDebugIfActive(battery_SecondaryBmbMiaFault, "ERROR: Voltage and temperature readings from secondary BMB chain are mia"); printDebugIfActive(battery_BmbMismatchFault, "ERROR: Primary and secondary BMB chain readings don't match with each other"); printDebugIfActive(battery_BmsHviMiaFault, "ERROR: BMS node is mia on HVS or HVI CAN"); //printDebugIfActive(battery_CpMiaFault, "ERROR: CP node is mia on HVS CAN"); //Uncommented due to not affecting usage printDebugIfActive(battery_PcsMiaFault, "ERROR: PCS node is mia on HVS CAN"); //printDebugIfActive(battery_BmsFault, "ERROR: BmsFault is active"); //Uncommented due to not affecting usage printDebugIfActive(battery_PcsFault, "ERROR: PcsFault is active"); //printDebugIfActive(battery_CpFault, "ERROR: CpFault is active"); //Uncommented due to not affecting usage printDebugIfActive(battery_ShuntHwMiaFault, "ERROR: Shunt current reading is not available"); printDebugIfActive(battery_PyroMiaFault, "ERROR: Pyro squib is not connected"); printDebugIfActive(battery_hvsMiaFault, "ERROR: Pack contactor hw fault"); printDebugIfActive(battery_hviMiaFault, "ERROR: FC contactor hw fault"); printDebugIfActive(battery_Supply12vFault, "ERROR: Low voltage (12V) battery is below minimum voltage threshold"); printDebugIfActive(battery_VerSupplyFault, "ERROR: Energy reserve voltage supply is below minimum voltage threshold"); printDebugIfActive(battery_HvilFault, "ERROR: High Voltage Inter Lock fault is detected"); printDebugIfActive(battery_BmsHvsMiaFault, "ERROR: BMS node is mia on HVS or HVI CAN"); printDebugIfActive(battery_PackVoltMismatchFault, "ERROR: Pack voltage doesn't match approximately with sum of brick voltages"); //printDebugIfActive(battery_EnsMiaFault, "ERROR: ENS line is not connected to HVC"); //Uncommented due to not affecting usage printDebugIfActive(battery_PackPosCtrArcFault, "ERROR: HVP detectes series arc at pack contactor"); printDebugIfActive(battery_packNegCtrArcFault, "ERROR: HVP detectes series arc at FC contactor"); printDebugIfActive(battery_ShuntHwAndBmsMiaFault, "ERROR: ShuntHwAndBmsMiaFault is active"); printDebugIfActive(battery_fcContHwFault, "ERROR: fcContHwFault is active"); printDebugIfActive(battery_robinOverVoltageFault, "ERROR: robinOverVoltageFault is active"); printDebugIfActive(battery_packContHwFault, "ERROR: packContHwFault is active"); printDebugIfActive(battery_pyroFuseBlown, "ERROR: pyroFuseBlown is active"); printDebugIfActive(battery_pyroFuseFailedToBlow, "ERROR: pyroFuseFailedToBlow is active"); //printDebugIfActive(battery_CpilFault, "ERROR: CpilFault is active"); //Uncommented due to not affecting usage printDebugIfActive(battery_PackContactorFellOpen, "ERROR: PackContactorFellOpen is active"); printDebugIfActive(battery_FcContactorFellOpen, "ERROR: FcContactorFellOpen is active"); printDebugIfActive(battery_packCtrCloseBlocked, "ERROR: packCtrCloseBlocked is active"); printDebugIfActive(battery_fcCtrCloseBlocked, "ERROR: fcCtrCloseBlocked is active"); printDebugIfActive(battery_packContactorForceOpen, "ERROR: packContactorForceOpen is active"); printDebugIfActive(battery_fcContactorForceOpen, "ERROR: fcContactorForceOpen is active"); printDebugIfActive(battery_dcLinkOverVoltage, "ERROR: dcLinkOverVoltage is active"); printDebugIfActive(battery_shuntOverTemperature, "ERROR: shuntOverTemperature is active"); printDebugIfActive(battery_passivePyroDeploy, "ERROR: passivePyroDeploy is active"); printDebugIfActive(battery_logUploadRequest, "ERROR: logUploadRequest is active"); printDebugIfActive(battery_packCtrCloseFailed, "ERROR: packCtrCloseFailed is active"); printDebugIfActive(battery_fcCtrCloseFailed, "ERROR: fcCtrCloseFailed is active"); printDebugIfActive(battery_shuntThermistorMia, "ERROR: shuntThermistorMia is active"); // 0x320 800 BMS_alertMatrix printDebugIfActive(BMS_a017_SW_Brick_OV, "ERROR: BMS_a017_SW_Brick_OV"); printDebugIfActive(BMS_a018_SW_Brick_UV, "ERROR: BMS_a018_SW_Brick_UV"); printDebugIfActive(BMS_a019_SW_Module_OT, "ERROR: BMS_a019_SW_Module_OT"); printDebugIfActive(BMS_a021_SW_Dr_Limits_Regulation, "ERROR: BMS_a021_SW_Dr_Limits_Regulation"); //printDebugIfActive(BMS_a022_SW_Over_Current, "ERROR: BMS_a022_SW_Over_Current"); printDebugIfActive(BMS_a023_SW_Stack_OV, "ERROR: BMS_a023_SW_Stack_OV"); printDebugIfActive(BMS_a024_SW_Islanded_Brick, "ERROR: BMS_a024_SW_Islanded_Brick"); printDebugIfActive(BMS_a025_SW_PwrBalance_Anomaly, "ERROR: BMS_a025_SW_PwrBalance_Anomaly"); printDebugIfActive(BMS_a026_SW_HFCurrent_Anomaly, "ERROR: BMS_a026_SW_HFCurrent_Anomaly"); printDebugIfActive(BMS_a034_SW_Passive_Isolation, "ERROR: BMS_a034_SW_Passive_Isolation"); printDebugIfActive(BMS_a035_SW_Isolation, "ERROR: BMS_a035_SW_Isolation"); printDebugIfActive(BMS_a036_SW_HvpHvilFault, "ERROR: BMS_a036_SW_HvpHvilFault"); printDebugIfActive(BMS_a037_SW_Flood_Port_Open, "ERROR: BMS_a037_SW_Flood_Port_Open"); printDebugIfActive(BMS_a039_SW_DC_Link_Over_Voltage, "ERROR: BMS_a039_SW_DC_Link_Over_Voltage"); printDebugIfActive(BMS_a041_SW_Power_On_Reset, "ERROR: BMS_a041_SW_Power_On_Reset"); printDebugIfActive(BMS_a042_SW_MPU_Error, "ERROR: BMS_a042_SW_MPU_Error"); printDebugIfActive(BMS_a043_SW_Watch_Dog_Reset, "ERROR: BMS_a043_SW_Watch_Dog_Reset"); printDebugIfActive(BMS_a044_SW_Assertion, "ERROR: BMS_a044_SW_Assertion"); printDebugIfActive(BMS_a045_SW_Exception, "ERROR: BMS_a045_SW_Exception"); printDebugIfActive(BMS_a046_SW_Task_Stack_Usage, "ERROR: BMS_a046_SW_Task_Stack_Usage"); printDebugIfActive(BMS_a047_SW_Task_Stack_Overflow, "ERROR: BMS_a047_SW_Task_Stack_Overflow"); printDebugIfActive(BMS_a048_SW_Log_Upload_Request, "ERROR: BMS_a048_SW_Log_Upload_Request"); //printDebugIfActive(BMS_a050_SW_Brick_Voltage_MIA, "ERROR: BMS_a050_SW_Brick_Voltage_MIA"); printDebugIfActive(BMS_a051_SW_HVC_Vref_Bad, "ERROR: BMS_a051_SW_HVC_Vref_Bad"); printDebugIfActive(BMS_a052_SW_PCS_MIA, "ERROR: BMS_a052_SW_PCS_MIA"); printDebugIfActive(BMS_a053_SW_ThermalModel_Sanity, "ERROR: BMS_a053_SW_ThermalModel_Sanity"); printDebugIfActive(BMS_a054_SW_Ver_Supply_Fault, "ERROR: BMS_a054_SW_Ver_Supply_Fault"); printDebugIfActive(BMS_a059_SW_Pack_Voltage_Sensing, "ERROR: BMS_a059_SW_Pack_Voltage_Sensing"); printDebugIfActive(BMS_a060_SW_Leakage_Test_Failure, "ERROR: BMS_a060_SW_Leakage_Test_Failure"); printDebugIfActive(BMS_a061_robinBrickOverVoltage, "ERROR: BMS_a061_robinBrickOverVoltage"); printDebugIfActive(BMS_a062_SW_BrickV_Imbalance, "ERROR: BMS_a062_SW_BrickV_Imbalance"); //printDebugIfActive(BMS_a063_SW_ChargePort_Fault, "ERROR: BMS_a063_SW_ChargePort_Fault"); printDebugIfActive(BMS_a064_SW_SOC_Imbalance, "ERROR: BMS_a064_SW_SOC_Imbalance"); printDebugIfActive(BMS_a069_SW_Low_Power, "ERROR: BMS_a069_SW_Low_Power"); printDebugIfActive(BMS_a071_SW_SM_TransCon_Not_Met, "ERROR: BMS_a071_SW_SM_TransCon_Not_Met"); printDebugIfActive(BMS_a075_SW_Chg_Disable_Failure, "ERROR: BMS_a075_SW_Chg_Disable_Failure"); printDebugIfActive(BMS_a076_SW_Dch_While_Charging, "ERROR: BMS_a076_SW_Dch_While_Charging"); printDebugIfActive(BMS_a077_SW_Charger_Regulation, "ERROR: BMS_a077_SW_Charger_Regulation"); printDebugIfActive(BMS_a081_SW_Ctr_Close_Blocked, "ERROR: BMS_a081_SW_Ctr_Close_Blocked"); printDebugIfActive(BMS_a082_SW_Ctr_Force_Open, "ERROR: BMS_a082_SW_Ctr_Force_Open"); printDebugIfActive(BMS_a083_SW_Ctr_Close_Failure, "ERROR: BMS_a083_SW_Ctr_Close_Failure"); printDebugIfActive(BMS_a084_SW_Sleep_Wake_Aborted, "ERROR: BMS_a084_SW_Sleep_Wake_Aborted"); printDebugIfActive(BMS_a087_SW_Feim_Test_Blocked, "ERROR: BMS_a087_SW_Feim_Test_Blocked"); printDebugIfActive(BMS_a088_SW_VcFront_MIA_InDrive, "ERROR: BMS_a088_SW_VcFront_MIA_InDrive"); printDebugIfActive(BMS_a089_SW_VcFront_MIA, "ERROR: BMS_a089_SW_VcFront_MIA"); printDebugIfActive(BMS_a090_SW_Gateway_MIA, "ERROR: BMS_a090_SW_Gateway_MIA"); //printDebugIfActive(BMS_a091_SW_ChargePort_MIA, "ERROR: BMS_a091_SW_ChargePort_MIA"); //printDebugIfActive(BMS_a092_SW_ChargePort_Mia_On_Hv, "ERROR: BMS_a092_SW_ChargePort_Mia_On_Hv"); //printDebugIfActive(BMS_a094_SW_Drive_Inverter_MIA, "ERROR: BMS_a094_SW_Drive_Inverter_MIA"); printDebugIfActive(BMS_a099_SW_BMB_Communication, "ERROR: BMS_a099_SW_BMB_Communication"); printDebugIfActive(BMS_a105_SW_One_Module_Tsense, "ERROR: BMS_a105_SW_One_Module_Tsense"); printDebugIfActive(BMS_a106_SW_All_Module_Tsense, "ERROR: BMS_a106_SW_All_Module_Tsense"); printDebugIfActive(BMS_a107_SW_Stack_Voltage_MIA, "ERROR: BMS_a107_SW_Stack_Voltage_MIA"); printDebugIfActive(BMS_a121_SW_NVRAM_Config_Error, "ERROR: BMS_a121_SW_NVRAM_Config_Error"); printDebugIfActive(BMS_a122_SW_BMS_Therm_Irrational, "ERROR: BMS_a122_SW_BMS_Therm_Irrational"); printDebugIfActive(BMS_a123_SW_Internal_Isolation, "ERROR: BMS_a123_SW_Internal_Isolation"); printDebugIfActive(BMS_a127_SW_shunt_SNA, "ERROR: BMS_a127_SW_shunt_SNA"); printDebugIfActive(BMS_a128_SW_shunt_MIA, "ERROR: BMS_a128_SW_shunt_MIA"); printDebugIfActive(BMS_a129_SW_VSH_Failure, "ERROR: BMS_a129_SW_VSH_Failure"); printDebugIfActive(BMS_a130_IO_CAN_Error, "ERROR: BMS_a130_IO_CAN_Error"); printDebugIfActive(BMS_a131_Bleed_FET_Failure, "ERROR: BMS_a131_Bleed_FET_Failure"); printDebugIfActive(BMS_a132_HW_BMB_OTP_Uncorrctbl, "ERROR: BMS_a132_HW_BMB_OTP_Uncorrctbl"); printDebugIfActive(BMS_a134_SW_Delayed_Ctr_Off, "ERROR: BMS_a134_SW_Delayed_Ctr_Off"); printDebugIfActive(BMS_a136_SW_Module_OT_Warning, "ERROR: BMS_a136_SW_Module_OT_Warning"); printDebugIfActive(BMS_a137_SW_Brick_UV_Warning, "ERROR: BMS_a137_SW_Brick_UV_Warning"); printDebugIfActive(BMS_a139_SW_DC_Link_V_Irrational, "ERROR: BMS_a139_SW_DC_Link_V_Irrational"); printDebugIfActive(BMS_a141_SW_BMB_Status_Warning, "ERROR: BMS_a141_SW_BMB_Status_Warning"); printDebugIfActive(BMS_a144_Hvp_Config_Mismatch, "ERROR: BMS_a144_Hvp_Config_Mismatch"); printDebugIfActive(BMS_a145_SW_SOC_Change, "ERROR: BMS_a145_SW_SOC_Change"); printDebugIfActive(BMS_a146_SW_Brick_Overdischarged, "ERROR: BMS_a146_SW_Brick_Overdischarged"); printDebugIfActive(BMS_a149_SW_Missing_Config_Block, "ERROR: BMS_a149_SW_Missing_Config_Block"); printDebugIfActive(BMS_a151_SW_external_isolation, "ERROR: BMS_a151_SW_external_isolation"); printDebugIfActive(BMS_a156_SW_BMB_Vref_bad, "ERROR: BMS_a156_SW_BMB_Vref_bad"); printDebugIfActive(BMS_a157_SW_HVP_HVS_Comms, "ERROR: BMS_a157_SW_HVP_HVS_Comms"); printDebugIfActive(BMS_a158_SW_HVP_HVI_Comms, "ERROR: BMS_a158_SW_HVP_HVI_Comms"); printDebugIfActive(BMS_a159_SW_HVP_ECU_Error, "ERROR: BMS_a159_SW_HVP_ECU_Error"); printDebugIfActive(BMS_a161_SW_DI_Open_Request, "ERROR: BMS_a161_SW_DI_Open_Request"); printDebugIfActive(BMS_a162_SW_No_Power_For_Support, "ERROR: BMS_a162_SW_No_Power_For_Support"); printDebugIfActive(BMS_a163_SW_Contactor_Mismatch, "ERROR: BMS_a163_SW_Contactor_Mismatch"); printDebugIfActive(BMS_a164_SW_Uncontrolled_Regen, "ERROR: BMS_a164_SW_Uncontrolled_Regen"); printDebugIfActive(BMS_a165_SW_Pack_Partial_Weld, "ERROR: BMS_a165_SW_Pack_Partial_Weld"); printDebugIfActive(BMS_a166_SW_Pack_Full_Weld, "ERROR: BMS_a166_SW_Pack_Full_Weld"); printDebugIfActive(BMS_a167_SW_FC_Partial_Weld, "ERROR: BMS_a167_SW_FC_Partial_Weld"); printDebugIfActive(BMS_a168_SW_FC_Full_Weld, "ERROR: BMS_a168_SW_FC_Full_Weld"); printDebugIfActive(BMS_a169_SW_FC_Pack_Weld, "ERROR: BMS_a169_SW_FC_Pack_Weld"); //printDebugIfActive(BMS_a170_SW_Limp_Mode, "ERROR: BMS_a170_SW_Limp_Mode"); printDebugIfActive(BMS_a171_SW_Stack_Voltage_Sense, "ERROR: BMS_a171_SW_Stack_Voltage_Sense"); printDebugIfActive(BMS_a174_SW_Charge_Failure, "ERROR: BMS_a174_SW_Charge_Failure"); printDebugIfActive(BMS_a176_SW_GracefulPowerOff, "ERROR: BMS_a176_SW_GracefulPowerOff"); printDebugIfActive(BMS_a179_SW_Hvp_12V_Fault, "ERROR: BMS_a179_SW_Hvp_12V_Fault"); printDebugIfActive(BMS_a180_SW_ECU_reset_blocked, "ERROR: BMS_a180_SW_ECU_reset_blocked"); } void TeslaModel3YBattery::setup(void) { // Performs one time setup at startup if (allows_contactor_closing) { *allows_contactor_closing = true; } //0x7FF GTW CAN frame values //Mux1 write_signal_value(&TESLA_7FF_Mux1, 16, 16, user_selected_tesla_GTW_country, false); write_signal_value(&TESLA_7FF_Mux1, 11, 1, user_selected_tesla_GTW_country, false); //Mux3 write_signal_value(&TESLA_7FF_Mux3, 8, 4, user_selected_tesla_GTW_mapRegion, false); write_signal_value(&TESLA_7FF_Mux3, 18, 3, user_selected_tesla_GTW_chassisType, false); write_signal_value(&TESLA_7FF_Mux3, 32, 5, user_selected_tesla_GTW_packEnergy, false); strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_LFP; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_LFP; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_LFP; } else { datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM; } } void TeslaModelSXBattery::setup(void) { if (allows_contactor_closing) { *allows_contactor_closing = true; } strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM; }