Battery-Emulator/Software/src/battery/MEB-BATTERY.cpp

2132 lines
109 KiB
C++

#include "../include.h"
#ifdef MEB_BATTERY
#include <algorithm> // For std::min and std::max
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "MEB-BATTERY.h"
/*
TODO list
- Check value mappings on the PID polls
- Check all TODO:s in the code
- 0x1B000044 & 1B00008F seems to be missing from logs? (Classic CAN)
- Scaled remaining capacity, should take already scaled total capacity into account, or we
should undo the scaling on the total capacity (which is calculated from the ah value now,
which is scaled already).
- Investigate why opening and then closing contactors from webpage does not always work
- Invertigate why contactors don't close when lilygo and battery are powered on simultaneously -> timeout on can msgs triggers to late, reset when open contactors is executed
- Find out how to get the battery in balancing mode
*/
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis10ms = 0; // will store last time a 10ms CAN Message was send
static unsigned long previousMillis20ms = 0; // will store last time a 20ms CAN Message was send
static unsigned long previousMillis40ms = 0; // will store last time a 40ms CAN Message was send
static unsigned long previousMillis50ms = 0; // will store last time a 50ms CAN Message was send
static unsigned long previousMillis100ms = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis200ms = 0; // will store last time a 200ms CAN Message was send
static unsigned long previousMillis500ms = 0; // will store last time a 200ms CAN Message was send
static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
static bool toggle = false;
static uint8_t counter_1000ms = 0;
static uint8_t counter_200ms = 0;
static uint8_t counter_100ms = 0;
static uint8_t counter_50ms = 0;
static uint8_t counter_40ms = 0;
static uint8_t counter_20ms = 0;
static uint8_t counter_10ms = 0;
static uint8_t counter_040 = 0;
static uint8_t counter_0F7 = 0;
static uint8_t counter_3b5 = 0;
static uint32_t poll_pid = PID_CELLVOLTAGE_CELL_85; // We start here to quickly determine the cell size of the pack.
static bool nof_cells_determined = false;
static uint32_t pid_reply = 0;
static uint16_t battery_soc_polled = 0;
static uint16_t battery_voltage_polled = 1480;
static int16_t battery_current_polled = 0;
static int16_t battery_max_temp = 600;
static int16_t battery_min_temp = 600;
static uint16_t battery_max_charge_voltage = 0;
static uint16_t battery_min_discharge_voltage = 0;
static uint16_t battery_allowed_charge_power = 0;
static uint16_t battery_allowed_discharge_power = 0;
static uint16_t cellvoltages_polled[108];
static uint16_t tempval = 0;
static uint8_t BMS_5A2_CRC = 0;
static uint8_t BMS_5CA_CRC = 0;
static uint8_t BMS_0CF_CRC = 0;
static uint8_t BMS_578_CRC = 0;
static uint8_t BMS_0C0_CRC = 0;
static uint8_t BMS_16A954A6_CRC = 0;
static uint8_t BMS_5A2_counter = 0;
static uint8_t BMS_5CA_counter = 0;
static uint8_t BMS_0CF_counter = 0;
static uint8_t BMS_0C0_counter = 0;
static uint8_t BMS_578_counter = 0;
static uint8_t BMS_16A954A6_counter = 0;
static bool BMS_ext_limits_active =
false; //The current current limits of the HV battery are expanded to start the combustion engine / confirmation of the request
static uint8_t BMS_mode =
0x07; //0: standby; Gates open; Communication active 1: Main contactor closed / HV network activated / normal driving operation
//2: assigned depending on the project (e.g. balancing, extended DC fast charging) //3: external charging
static uint8_t BMS_HVIL_status = 0; //0 init, 1 seated, 2 open, 3 fault
static bool BMS_fault_performance = false; //Error: Battery performance is limited (e.g. due to sensor or fan failure)
static uint16_t BMS_current = 16300;
static bool BMS_fault_emergency_shutdown_crash =
false; //Error: Safety-critical error (crash detection) Battery contactors are already opened / will be opened immediately Signal is read directly by the EMS and initiates an AKS of the PWR and an active discharge of the DC link
static uint32_t BMS_voltage_intermediate = 0;
static uint32_t BMS_voltage = 1480;
static uint8_t BMS_status_voltage_free =
0; //0=Init, 1=BMS intermediate circuit voltage-free (U_Zwkr < 20V), 2=BMS intermediate circuit not voltage-free (U_Zwkr >/= 25V, hysteresis), 3=Error
static bool BMS_OBD_MIL = false;
static uint8_t BMS_error_status =
0x7; //0 Component_IO, 1 Restricted_CompFkt_Isoerror_I, 2 Restricted_CompFkt_Isoerror_II, 3 Restricted_CompFkt_Interlock, 4 Restricted_CompFkt_SD, 5 Restricted_CompFkt_Performance red, 6 = No component function, 7 = Init
static uint16_t BMS_capacity_ah = 0;
static bool BMS_error_lamp_req = false;
static bool BMS_warning_lamp_req = false;
static uint8_t BMS_Kl30c_Status = 0; // 0 init, 1 closed, 2 open, 3 fault
static bool service_disconnect_switch_missing = false;
static bool pilotline_open = false;
static bool balancing_request = false;
static uint8_t battery_diagnostic = 0;
static uint16_t battery_Wh_left = 0;
static uint16_t battery_Wh_max = 1000;
static uint8_t battery_potential_status = 0;
static uint8_t battery_temperature_warning = 0;
static uint16_t max_discharge_power_watt = 0;
static uint16_t max_discharge_current_amp = 0;
static uint16_t max_charge_power_watt = 0;
static uint16_t max_charge_current_amp = 0;
static uint16_t battery_SOC = 1;
static uint16_t usable_energy_amount_Wh = 0;
static uint8_t status_HV_line = 0; //0 init, 1 No open HV line, 2 open HV line detected, 3 fault
static uint8_t warning_support = 0;
static bool battery_heating_active = false;
static uint16_t power_discharge_percentage = 0;
static uint16_t power_charge_percentage = 0;
static uint16_t actual_battery_voltage = 0;
static uint16_t regen_battery = 0;
static uint16_t energy_extracted_from_battery = 0;
static uint16_t max_fastcharging_current_amp = 0; //maximum DC charging current allowed
static uint8_t BMS_Status_DCLS =
0; //Status of the voltage monitoring on the DC charging interface. 0 inactive, 1 I_O , 2 N_I_O , 3 active
static uint16_t DC_voltage_DCLS =
0; //Factor 1, DC voltage of the charging station. Measurement between the DC HV lines.
static uint16_t DC_voltage_chargeport =
0; //Factor 0.5, Current voltage at the HV battery DC charging terminals; Outlet to the DC charging plug.
static uint8_t BMS_welded_contactors_status =
0; //0: Init no diagnostic result, 1: no contactor welded, 2: at least 1 contactor welded, 3: Protection status detection error
static bool BMS_error_shutdown_request =
false; // Fault: Fault condition, requires battery contactors to be opened internal battery error; Advance notification of an impending opening of the battery contactors by the BMS
static bool BMS_error_shutdown =
false; // Fault: Fault condition, requires battery contactors to be opened Internal battery error, battery contactors opened without notice by the BMS
static uint16_t power_battery_heating_watt = 0;
static uint16_t power_battery_heating_req_watt = 0;
static uint8_t cooling_request =
0; //0 = No cooling, 1 = Light cooling, cabin prio, 2= higher cooling, 3 = immediate cooling, 4 = emergency cooling
static uint8_t heating_request = 0; //0 = init, 1= maintain temp, 2=higher demand, 3 = immediate heat demand
static uint8_t balancing_active = false; //0 = init, 1 active, 2 not active
static bool charging_active = false;
static uint16_t max_energy_Wh = 0;
static uint16_t max_charge_percent = 0;
static uint16_t min_charge_percent = 0;
static uint16_t isolation_resistance_kOhm = 0;
static bool battery_heating_installed = false;
static bool error_NT_circuit = false;
static uint8_t pump_1_control = 0; //0x0D not installed, 0x0E init, 0x0F fault
static uint8_t pump_2_control = 0; //0x0D not installed, 0x0E init, 0x0F fault
static uint8_t target_flow_temperature_C = 0; //*0,5 -40
static uint8_t return_temperature_C = 0; //*0,5 -40
static uint8_t status_valve_1 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
static uint8_t status_valve_2 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
static uint8_t battery_temperature = 0;
static uint8_t temperature_request =
0; //0 high cooling, 1 medium cooling, 2 low cooling, 3 no temp requirement init, 4 low heating , 5 medium heating, 6 high heating, 7 circulation
static uint16_t performance_index_discharge_peak_temperature_percentage = 0;
static uint16_t performance_index_charge_peak_temperature_percentage = 0;
static uint8_t temperature_status_discharge =
0; //0 init, 1 temp under optimal, 2 temp optimal, 3 temp over optimal, 7 fault
static uint8_t temperature_status_charge =
0; //0 init, 1 temp under optimal, 2 temp optimal, 3 temp over optimal, 7 fault
static uint8_t isolation_fault =
0; //0 init, 1 no iso fault, 2 iso fault threshold1, 3 iso fault threshold2, 4 IWU defective
static uint8_t isolation_status =
0; // 0 init, 1 = larger threshold1, 2 = smaller threshold1 total, 3 = smaller threshold1 intern, 4 = smaller threshold2 total, 5 = smaller threshold2 intern, 6 = no measurement, 7 = measurement active
static uint8_t actual_temperature_highest_C = 0; //*0,5 -40
static uint8_t actual_temperature_lowest_C = 0; //*0,5 -40
static uint16_t actual_cellvoltage_highest_mV = 0; //bias 1000
static uint16_t actual_cellvoltage_lowest_mV = 0; //bias 1000
static uint16_t predicted_power_dyn_standard_watt = 0;
static uint8_t predicted_time_dyn_standard_minutes = 0;
static uint8_t mux = 0;
static int8_t celltemperature[56] = {0}; //Temperatures 1-56. Value is 0xFD if sensor not present
static uint16_t cellvoltages[160] = {0};
static uint16_t duration_discharge_power_watt = 0;
static uint16_t duration_charge_power_watt = 0;
static uint16_t maximum_voltage = 0;
static uint16_t minimum_voltage = 0;
static uint8_t battery_serialnumber[26];
static uint8_t realtime_overcurrent_monitor = 0;
static uint8_t realtime_CAN_communication_fault = 0;
static uint8_t realtime_overcharge_warning = 0;
static uint8_t realtime_SOC_too_high = 0;
static uint8_t realtime_SOC_too_low = 0;
static uint8_t realtime_SOC_jumping_warning = 0;
static uint8_t realtime_temperature_difference_warning = 0;
static uint8_t realtime_cell_overtemperature_warning = 0;
static uint8_t realtime_cell_undertemperature_warning = 0;
static uint8_t realtime_battery_overvoltage_warning = 0;
static uint8_t realtime_battery_undervoltage_warning = 0;
static uint8_t realtime_cell_overvoltage_warning = 0;
static uint8_t realtime_cell_undervoltage_warning = 0;
static uint8_t realtime_cell_imbalance_warning = 0;
static uint8_t realtime_warning_battery_unathorized = 0;
static bool component_protection_active = false;
static bool shutdown_active = false;
static bool transportation_mode_active = false;
static uint8_t KL15_mode = 0;
static uint8_t bus_knockout_timer = 0;
static uint8_t hybrid_wakeup_reason = 0;
static uint8_t wakeup_type = 0;
static bool instrumentation_cluster_request = false;
static uint8_t seconds = 0;
static uint32_t first_can_msg = 0;
static uint32_t last_can_msg_timestamp = 0;
#define TIME_YEAR 2024
#define TIME_MONTH 8
#define TIME_DAY 20
#define TIME_HOUR 10
#define TIME_MINUTE 5
#define TIME_SECOND 0
#define BMS_TARGET_HV_OFF 0
#define BMS_TARGET_HV_ON 1
#define BMS_TARGET_AC_CHARGING_EXT 3 //(HS + AC_2 contactors closed)
#define BMS_TARGET_AC_CHARGING 4 //(HS + AC contactors closed)
#define BMS_TARGET_DC_CHARGING 6 //(HS + DC contactors closed)
#define BMS_TARGET_INIT 7
#define DC_FASTCHARGE_NO_START_REQUEST 0x00
#define DC_FASTCHARGE_VEHICLE 0x40
#define DC_FASTCHARGE_LS1 0x80
#define DC_FASTCHARGE_LS2 0xC0
CAN_frame MEB_POLLING_FRAME = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1C40007B, // SOC 02 8C
.data = {0x03, 0x22, 0x02, 0x8C, 0x55, 0x55, 0x55, 0x55}};
CAN_frame MEB_ACK_FRAME = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1C40007B, // Ack
.data = {0x30, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0x55}};
//Messages needed for contactor closing
CAN_frame MEB_040 = {.FD = true, // Airbag
.ext_ID = false,
.DLC = 8,
.ID = 0x040, //Frame5 has HV deactivate request. Needs to be 0x00
.data = {0x7E, 0x83, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00}};
CAN_frame MEB_0C0 = {
.FD = true, // EM1 message
.ext_ID = false,
.DLC = 32,
.ID = 0x0C0, //Frame 5-6 and maybe 7-8 important (external voltage at inverter)
.data = {0x77, 0x0A, 0xFE, 0xE7, 0x7F, 0x10, 0x27, 0x00, 0xE0, 0x7F, 0xFF, 0xF3, 0x3F, 0xFF, 0xF3, 0x3F,
0xFC, 0x0F, 0x00, 0x00, 0xC0, 0xFF, 0xFE, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_0FC = {
.FD = true, //
.ext_ID = false,
.DLC = 48,
.ID = 0x0FC, //This message contains emergency regen request?(byte19), battery needs to see this message
.data = {0x07, 0x08, 0x00, 0x00, 0x7E, 0x00, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFE, 0xFE, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF4, 0x01, 0x40, 0xFF, 0xEB, 0x7F, 0x0A, 0x88, 0xE3, 0x81, 0xAF, 0x42}};
CAN_frame MEB_6B2 = {.FD = true, // Diagnostics
.ext_ID = false,
.DLC = 8,
.ID = 0x6B2,
.data = {0x6A, 0xA7, 0x37, 0x80, 0xC9, 0xBD, 0xF6, 0xC2}};
CAN_frame MEB_17FC007B_poll = {.FD = true, // Non period request
.ext_ID = true,
.DLC = 8,
.ID = 0x17FC007B,
.data = {0x03, 0x22, 0x1E, 0x3D, 0x55, 0x55, 0x55, 0x55}};
CAN_frame MEB_1A5555A6 = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1A5555A6,
.data = {0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_585 = {
.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x585,
.data = {0xCF, 0x38, 0xAF, 0x5B, 0x25, 0x00, 0x00, 0x00}}; // CF 38 AF 5B 25 00 00 00 in start4.log
// .data = {0xCF, 0x38, 0x20, 0x02, 0x25, 0xF7, 0x30, 0x00}}; // CF 38 AF 5B 25 00 00 00 in start4.log
CAN_frame MEB_5F5 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x5F5,
.data = {0x23, 0x02, 0x39, 0xC0, 0x1B, 0x8B, 0xC8, 0x1B}};
CAN_frame MEB_641 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x641,
.data = {0x37, 0x18, 0x00, 0x00, 0xF0, 0x00, 0xAA, 0x70}};
CAN_frame MEB_3C0 = {.FD = true,
.ext_ID = false,
.DLC = 4,
.ID = 0x3C0,
.data = {0x66, 0x00, 0x00, 0x00}}; // Klemmen_status_01
CAN_frame MEB_0FD = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x0FD, //CRC and counter, otherwise static
.data = {0x5F, 0xD0, 0x1F, 0x81, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_16A954FB = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x16A954FB,
.data = {0x00, 0xC0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_1A555548 = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1A555548,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_1A55552B = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x1A55552B,
.data = {0x00, 0x00, 0x00, 0xA0, 0x02, 0x04, 0x00, 0x30}};
CAN_frame MEB_569 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x569, //HVEM
.data = {0x00, 0x00, 0x01, 0x3A, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_16A954B4 = {.FD = true,
.ext_ID = true,
.DLC = 8,
.ID = 0x16A954B4, //eTM
.data = {0xFE, 0xB6, 0x0D, 0x00, 0x00, 0xD5, 0x48, 0xFD}};
CAN_frame MEB_1B000046 = {.FD = false, // Not FD
.ext_ID = true,
.DLC = 8,
.ID = 0x1B000046, // Klima
.data = {0x00, 0x40, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_1B000010 = {.FD = false, // Not FD
.ext_ID = true,
.DLC = 8,
.ID = 0x1B000010, // Gateway
.data = {0x00, 0x50, 0x08, 0x50, 0x01, 0xFF, 0x30, 0x00}};
CAN_frame MEB_1B0000B9 = {.FD = false, // Not FD
.ext_ID = true,
.DLC = 8,
.ID = 0x1B0000B9, //DC/DC converter
.data = {0x00, 0x40, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00}};
CAN_frame MEB_153 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x153, // Static content
.data = {0x00, 0x00, 0x00, 0xFF, 0xEF, 0xFE, 0xFF, 0xFF}};
CAN_frame MEB_5E1 = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x5E1, // Static content
.data = {0x7F, 0x2A, 0x00, 0x60, 0xFE, 0x00, 0x00, 0x00}};
CAN_frame MEB_3BE = {.FD = true,
.ext_ID = false,
.DLC = 8,
.ID = 0x3BE, // CRC, otherwise Static content
.data = {0x57, 0x0D, 0x00, 0x00, 0x00, 0x02, 0x04, 0x40}};
CAN_frame MEB_272 = {.FD = true, //HVLM_14
.ext_ID = false,
.DLC = 8,
.ID = 0x272, // Static content
.data = {0x00, 0x00, 0x00, 0x00, 0x48, 0x08, 0x00, 0x94}};
CAN_frame MEB_503 = {.FD = true, //HVK_01
.ext_ID = false,
.DLC = 8,
.ID = 0x503, // Content varies. Frame1 & 3 has HV req
.data = {0x5D, 0x61, 0x00, 0xFF, 0x7F, 0x80, 0xE3, 0x03}};
CAN_frame MEB_14C = {
.FD = true, //Motor message
.ext_ID = false,
.DLC = 32,
.ID = 0x14C, //CRC needed, static content otherwise
.data = {0x38, 0x0A, 0xFF, 0x01, 0x01, 0xFF, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x25, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE}};
/** Calculate the CRC checksum for VAG CAN Messages
*
* The method used is described in Chapter "7.2.1.2 8-bit 0x2F polynomial CRC Calculation".
* CRC Parameters:
* 0x2F - Polynomial
* 0xFF - Initial Value
* 0xFF - XOR Output
*
* @see https://github.com/crasbe/VW-OnBoard-Charger
* @see https://github.com/colinoflynn/crcbeagle for CRC hacking :)
* @see https://github.com/commaai/opendbc/blob/master/can/common.cc#L110
* @see https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
* @see https://web.archive.org/web/20221105210302/https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
*/
uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint16_t address) {
const uint8_t poly = 0x2F;
const uint8_t xor_output = 0xFF;
// VAG Magic Bytes
const uint8_t MB0040[16] = {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40};
const uint8_t MB00C0[16] = {0x2f, 0x44, 0x72, 0xd3, 0x07, 0xf2, 0x39, 0x09,
0x8d, 0x6f, 0x57, 0x20, 0x37, 0xf9, 0x9b, 0xfa};
const uint8_t MB00FC[16] = {0x77, 0x5c, 0xa0, 0x89, 0x4b, 0x7c, 0xbb, 0xd6,
0x1f, 0x6c, 0x4f, 0xf6, 0x20, 0x2b, 0x43, 0xdd};
const uint8_t MB00FD[16] = {0xb4, 0xef, 0xf8, 0x49, 0x1e, 0xe5, 0xc2, 0xc0,
0x97, 0x19, 0x3c, 0xc9, 0xf1, 0x98, 0xd6, 0x61};
const uint8_t MB0097[16] = {0x3C, 0x54, 0xCF, 0xA3, 0x81, 0x93, 0x0B, 0xC7,
0x3E, 0xDF, 0x1C, 0xB0, 0xA7, 0x25, 0xD3, 0xD8};
const uint8_t MB00F7[16] = {0x5F, 0xA0, 0x44, 0xD0, 0x63, 0x59, 0x5B, 0xA2,
0x68, 0x04, 0x90, 0x87, 0x52, 0x12, 0xB4, 0x9E};
const uint8_t MB0124[16] = {0x12, 0x7E, 0x34, 0x16, 0x25, 0x8F, 0x8E, 0x35,
0xBA, 0x7F, 0xEA, 0x59, 0x4C, 0xF0, 0x88, 0x15};
const uint8_t MB0153[16] = {0x03, 0x13, 0x23, 0x7a, 0x40, 0x51, 0x68, 0xba,
0xa8, 0xbe, 0x55, 0x02, 0x11, 0x31, 0x76, 0xec};
const uint8_t MB014C[16] = {0x16, 0x35, 0x59, 0x15, 0x9a, 0x2a, 0x97, 0xb8,
0x0e, 0x4e, 0x30, 0xcc, 0xb3, 0x07, 0x01, 0xad};
const uint8_t MB0187[16] = {0x7F, 0xED, 0x17, 0xC2, 0x7C, 0xEB, 0x44, 0x21,
0x01, 0xFA, 0xDB, 0x15, 0x4A, 0x6B, 0x23, 0x05};
const uint8_t MB03A6[16] = {0xB6, 0x1C, 0xC1, 0x23, 0x6D, 0x8B, 0x0C, 0x51,
0x38, 0x32, 0x24, 0xA8, 0x3F, 0x3A, 0xA4, 0x02};
const uint8_t MB03AF[16] = {0x94, 0x6A, 0xB5, 0x38, 0x8A, 0xB4, 0xAB, 0x27,
0xCB, 0x22, 0x88, 0xEF, 0xA3, 0xE1, 0xD0, 0xBB};
const uint8_t MB03BE[16] = {0x1f, 0x28, 0xc6, 0x85, 0xe6, 0xf8, 0xb0, 0x19,
0x5b, 0x64, 0x35, 0x21, 0xe4, 0xf7, 0x9c, 0x24};
const uint8_t MB03C0[16] = {0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3,
0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3};
const uint8_t MB0503[16] = {0xed, 0xd6, 0x96, 0x63, 0xa5, 0x12, 0xd5, 0x9a,
0x1e, 0x0d, 0x24, 0xcd, 0x8c, 0xa6, 0x2f, 0x41};
const uint8_t MB0578[16] = {0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48};
const uint8_t MB05CA[16] = {0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43};
const uint8_t MB0641[16] = {0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47};
const uint8_t MB06A3[16] = {0xC1, 0x8B, 0x38, 0xA8, 0xA4, 0x27, 0xEB, 0xC8,
0xEF, 0x05, 0x9A, 0xBB, 0x39, 0xF7, 0x80, 0xA7};
const uint8_t MB06A4[16] = {0xC7, 0xD8, 0xF1, 0xC4, 0xE3, 0x5E, 0x9A, 0xE2,
0xA1, 0xCB, 0x02, 0x4F, 0x57, 0x4E, 0x8E, 0xE4};
const uint8_t MB16A954A6[16] = {0x79, 0xB9, 0x67, 0xAD, 0xD5, 0xF7, 0x70, 0xAA,
0x44, 0x61, 0x5A, 0xDC, 0x26, 0xB4, 0xD2, 0xC3};
uint8_t crc = 0xFF;
uint8_t magicByte = 0x00;
uint8_t counter = inputBytes[1] & 0x0F; // only the low byte of the couner is relevant
switch (address) {
case 0x0040: // Airbag
magicByte = MB0040[counter];
break;
case 0x00C0: //
magicByte = MB00C0[counter];
break;
case 0x00FC:
magicByte = MB00FC[counter];
break;
case 0x00FD:
magicByte = MB00FD[counter];
break;
case 0x0097: // ??
magicByte = MB0097[counter];
break;
case 0x00F7: // ??
magicByte = MB00F7[counter];
break;
case 0x0124: // ??
magicByte = MB0124[counter];
break;
case 0x014C: // Motor
magicByte = MB014C[counter];
break;
case 0x0153: // HYB30
magicByte = MB0153[counter];
break;
case 0x0187: // EV_Gearshift "Gear" selection data for EVs with no gearbox
magicByte = MB0187[counter];
break;
case 0x03A6: // ??
magicByte = MB03A6[counter];
break;
case 0x03AF: // ??
magicByte = MB03AF[counter];
break;
case 0x03BE: // Motor
magicByte = MB03BE[counter];
break;
case 0x03C0: // Klemmen status
magicByte = MB03C0[counter];
break;
case 0x0503: // HVK
magicByte = MB0503[counter];
break;
case 0x0578: // BMS DC
magicByte = MB0578[counter];
break;
case 0x05CA: // BMS
magicByte = MB05CA[counter];
break;
case 0x0641: // Motor
magicByte = MB0641[counter];
break;
case 0x06A3: // ??
magicByte = MB06A3[counter];
break;
case 0x06A4: // ??
magicByte = MB06A4[counter];
break;
case 0x16A954A6:
magicByte = MB16A954A6[counter];
break;
default: // this won't lead to correct CRC checksums
magicByte = 0x00;
break;
}
for (uint8_t i = 1; i < length + 1; i++) {
// We skip the empty CRC position and start at the timer
// The last element is the VAG magic byte for address 0x187 depending on the counter value.
if (i < length)
crc ^= inputBytes[i];
else
crc ^= magicByte;
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x80)
crc = (crc << 1) ^ poly;
else
crc = (crc << 1);
}
}
crc ^= xor_output;
return crc;
}
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100 battery_soc_polled * 10;
//Alternatively use battery_SOC for more precision
datalayer.battery.status.soh_pptt;
datalayer.battery.status.voltage_dV = BMS_voltage * 2.5; // *0.25*10
datalayer.battery.status.current_dA = (BMS_current / 10) - 1630;
datalayer.battery.info.total_capacity_Wh =
((float)datalayer.battery.info.number_of_cells) * 3.6458 * ((float)BMS_capacity_ah) * 0.2;
datalayer.battery.status.remaining_capacity_Wh = usable_energy_amount_Wh * 5;
//Alternatively use battery_Wh_left
datalayer.battery.status.max_charge_power_W = (max_charge_power_watt * 100);
datalayer.battery.status.max_discharge_power_W = (max_discharge_power_watt * 100);
//Power in watts, Negative = charging batt
datalayer.battery.status.active_power_W =
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
datalayer.battery.status.temperature_min_dC = (battery_min_temp - 350) / 2;
datalayer.battery.status.temperature_max_dC = (battery_max_temp - 350) / 2;
//Map all cell voltages to the global array
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t));
// Initialize min and max, lets find which cells are min and max!
uint16_t min_cell_mv_value = std::numeric_limits<uint16_t>::max();
uint16_t max_cell_mv_value = 0;
// Loop to find the min and max while ignoring zero values
for (uint8_t i = 0; i < 108; ++i) {
uint16_t voltage_mV = datalayer.battery.status.cell_voltages_mV[i];
if (voltage_mV != 0) { // Skip unread values (0)
min_cell_mv_value = std::min(min_cell_mv_value, voltage_mV);
max_cell_mv_value = std::max(max_cell_mv_value, voltage_mV);
}
}
// If all array values are 0, reset min/max to 3700
if (min_cell_mv_value == std::numeric_limits<uint16_t>::max()) {
min_cell_mv_value = 3700;
max_cell_mv_value = 3700;
}
datalayer.battery.status.cell_min_voltage_mV = min_cell_mv_value;
datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value;
//TODO, use actual_cellvoltage_lowest_mV instead to save performance
if (service_disconnect_switch_missing) {
set_event(EVENT_HVIL_FAILURE, 1);
} else {
clear_event(EVENT_HVIL_FAILURE);
}
if (pilotline_open || BMS_HVIL_status == 2) {
set_event(EVENT_HVIL_FAILURE, 2);
} else {
clear_event(EVENT_HVIL_FAILURE);
}
// Update webserver datalayer for "More battery info" page
datalayer_extended.meb.SDSW = service_disconnect_switch_missing;
datalayer_extended.meb.pilotline = pilotline_open;
datalayer_extended.meb.transportmode = transportation_mode_active;
datalayer_extended.meb.componentprotection = component_protection_active;
datalayer_extended.meb.shutdown_active = shutdown_active;
datalayer_extended.meb.HVIL = BMS_HVIL_status;
datalayer_extended.meb.BMS_mode = BMS_mode;
datalayer_extended.meb.battery_diagnostic = battery_diagnostic;
datalayer_extended.meb.status_HV_line = status_HV_line;
datalayer_extended.meb.warning_support = warning_support;
datalayer_extended.meb.BMS_status_voltage_free = BMS_status_voltage_free;
datalayer_extended.meb.BMS_OBD_MIL = BMS_OBD_MIL;
datalayer_extended.meb.BMS_error_status = BMS_error_status;
datalayer_extended.meb.BMS_error_lamp_req = BMS_error_lamp_req;
datalayer_extended.meb.BMS_warning_lamp_req = BMS_warning_lamp_req;
datalayer_extended.meb.BMS_Kl30c_Status = BMS_Kl30c_Status;
datalayer_extended.meb.BMS_voltage_intermediate_dV = (BMS_voltage_intermediate - 2000) * 10 / 2;
datalayer_extended.meb.BMS_voltage_dV = BMS_voltage * 10 / 4;
datalayer_extended.meb.isolation_resistance = isolation_resistance_kOhm * 5;
datalayer_extended.meb.battery_heating = battery_heating_active;
datalayer_extended.meb.rt_overcurrent = realtime_overcurrent_monitor;
datalayer_extended.meb.rt_CAN_fault = realtime_CAN_communication_fault;
datalayer_extended.meb.rt_overcharge = realtime_overcharge_warning;
datalayer_extended.meb.rt_SOC_high = realtime_SOC_too_high;
datalayer_extended.meb.rt_SOC_low = realtime_SOC_too_low;
datalayer_extended.meb.rt_SOC_jumping = realtime_SOC_jumping_warning;
datalayer_extended.meb.rt_temp_difference = realtime_temperature_difference_warning;
datalayer_extended.meb.rt_cell_overtemp = realtime_cell_overtemperature_warning;
datalayer_extended.meb.rt_cell_undertemp = realtime_cell_undertemperature_warning;
datalayer_extended.meb.rt_battery_overvolt = realtime_battery_overvoltage_warning;
datalayer_extended.meb.rt_battery_undervol = realtime_battery_undervoltage_warning;
datalayer_extended.meb.rt_cell_overvolt = realtime_cell_overvoltage_warning;
datalayer_extended.meb.rt_cell_undervol = realtime_cell_undervoltage_warning;
datalayer_extended.meb.rt_cell_imbalance = realtime_cell_imbalance_warning;
datalayer_extended.meb.rt_battery_unathorized = realtime_warning_battery_unathorized;
}
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
last_can_msg_timestamp = millis();
if (first_can_msg == 0)
first_can_msg = last_can_msg_timestamp;
switch (rx_frame.ID) {
case 0x17F0007B: // BMS 500ms
component_protection_active = (rx_frame.data.u8[0] & 0x01);
shutdown_active = ((rx_frame.data.u8[0] & 0x02) >> 1);
transportation_mode_active = ((rx_frame.data.u8[0] & 0x02) >> 1);
KL15_mode = ((rx_frame.data.u8[0] & 0xF0) >> 4);
//0 = communication only when terminal 15 = ON (no run-on, cannot be woken up)
//1 = communication after terminal 15 = OFF (run-on, cannot be woken up)
//2 = communication when terminal 15 = OFF (run-on, can be woken up)
bus_knockout_timer = rx_frame.data.u8[5];
hybrid_wakeup_reason = rx_frame.data.u8[6]; //(if several active, lowest wins)
//0 = wakeup cause not known 1 = Bus wakeup2 = KL15 HW 3 = TPA active
break;
case 0x17FE007B: // BMS - Offboard tester diag response
break;
case 0x1B00007B: // BMS - 200ms
wakeup_type =
((rx_frame.data.u8[1] & 0x10) >> 4); //0 passive, SG has not woken up, 1 active, SG has woken up the network
instrumentation_cluster_request = ((rx_frame.data.u8[1] & 0x40) >> 6); //True/false
break;
case 0x12DD54D0: // BMS Limits 100ms
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
max_discharge_power_watt =
((rx_frame.data.u8[6] & 0x07) << 10) | (rx_frame.data.u8[5] << 2) | (rx_frame.data.u8[4] & 0xC0) >> 6; //*100
max_discharge_current_amp =
((rx_frame.data.u8[3] & 0x01) << 12) | (rx_frame.data.u8[2] << 4) | (rx_frame.data.u8[1] >> 4); //*0.2
max_charge_power_watt = (rx_frame.data.u8[7] << 5) | (rx_frame.data.u8[6] >> 3); //*100
max_charge_current_amp = ((rx_frame.data.u8[4] & 0x3F) << 7) | (rx_frame.data.u8[3] >> 1); //*0.2
break;
case 0x12DD54D1: // BMS 100ms
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
if (rx_frame.data.u8[6] != 0xFE || rx_frame.data.u8[7] != 0xFF) { // Init state, values below invalid
battery_SOC = ((rx_frame.data.u8[3] & 0x0F) << 7) | (rx_frame.data.u8[2] >> 1); //*0.05
usable_energy_amount_Wh = (rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]; //*5
power_discharge_percentage = ((rx_frame.data.u8[4] & 0x3F) << 4) | rx_frame.data.u8[3] >> 4; //*0.2
power_charge_percentage = (rx_frame.data.u8[5] << 2) | rx_frame.data.u8[4] >> 6; //*0.2
}
status_HV_line = ((rx_frame.data.u8[2] & 0x01) << 1) | rx_frame.data.u8[1] >> 7;
warning_support = (rx_frame.data.u8[1] & 0x70) >> 4;
break;
case 0x12DD54D2: // BMS 100ms
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
battery_heating_active = (rx_frame.data.u8[4] & 0x40) >> 6;
heating_request = (rx_frame.data.u8[5] & 0xE0) >> 5;
cooling_request = (rx_frame.data.u8[5] & 0x1C) >> 2;
power_battery_heating_watt = rx_frame.data.u8[6];
power_battery_heating_req_watt = rx_frame.data.u8[7];
break;
case 0x1A555550: // BMS 500ms
balancing_active = (rx_frame.data.u8[1] & 0xC0) >> 6;
charging_active = (rx_frame.data.u8[2] & 0x01);
max_energy_Wh = ((rx_frame.data.u8[6] & 0x1F) << 8) | rx_frame.data.u8[5]; //*40
max_charge_percent = ((rx_frame.data.u8[7] << 3) | rx_frame.data.u8[6] >> 5); //*0.05
min_charge_percent = ((rx_frame.data.u8[4] << 3) | rx_frame.data.u8[3] >> 5); //*0.05
isolation_resistance_kOhm = (((rx_frame.data.u8[3] & 0x1F) << 7) | rx_frame.data.u8[2] >> 1); //*5
break;
case 0x1A555551: // BMS 500ms
battery_heating_installed = (rx_frame.data.u8[1] & 0x20) >> 5;
error_NT_circuit = (rx_frame.data.u8[1] & 0x40) >> 6;
pump_1_control = rx_frame.data.u8[2] & 0x0F; //*10, percent
pump_2_control = (rx_frame.data.u8[2] & 0xF0) >> 4; //*10, percent
status_valve_1 = (rx_frame.data.u8[3] & 0x1C) >> 2;
status_valve_2 = (rx_frame.data.u8[3] & 0xE0) >> 5;
temperature_request = (((rx_frame.data.u8[2] & 0x03) << 1) | rx_frame.data.u8[1] >> 7);
battery_temperature = rx_frame.data.u8[5]; //*0,5 -40
target_flow_temperature_C = rx_frame.data.u8[6]; //*0,5 -40
return_temperature_C = rx_frame.data.u8[7]; //*0,5 -40
break;
case 0x1A5555B2: // BMS
performance_index_discharge_peak_temperature_percentage =
(((rx_frame.data.u8[3] & 0x07) << 6) | rx_frame.data.u8[2] >> 2); //*0.2
performance_index_charge_peak_temperature_percentage =
(((rx_frame.data.u8[4] & 0x3F) << 3) | rx_frame.data.u8[3] >> 5); //*0.2
temperature_status_discharge = (rx_frame.data.u8[1] & 0x70) >> 4;
temperature_status_charge = (((rx_frame.data.u8[2] & 0x03) << 1) | rx_frame.data.u8[1] >> 7);
break;
case 0x16A954A6: // BMS
BMS_16A954A6_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
BMS_16A954A6_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
isolation_fault = (rx_frame.data.u8[2] & 0xE0) >> 5;
isolation_status = (rx_frame.data.u8[2] & 0x1E) >> 1;
actual_temperature_highest_C = rx_frame.data.u8[3]; //*0,5 -40
actual_temperature_lowest_C = rx_frame.data.u8[4]; //*0,5 -40
actual_cellvoltage_highest_mV = (((rx_frame.data.u8[6] & 0x0F) << 8) | rx_frame.data.u8[5]);
actual_cellvoltage_lowest_mV = ((rx_frame.data.u8[7] << 4) | rx_frame.data.u8[6] >> 4);
break;
case 0x16A954F8: // BMS
predicted_power_dyn_standard_watt = ((rx_frame.data.u8[6] << 1) | rx_frame.data.u8[5] >> 7); //*50
predicted_time_dyn_standard_minutes = rx_frame.data.u8[7];
break;
case 0x16A954E8: // BMS Temperature and cellvoltages - 180ms
mux = (rx_frame.data.u8[0] & 0x0F);
switch (mux) {
case 0: // Temperatures 1-56. Value is 0xFD if sensor not present
for (uint8_t i = 0; i < 56; i++) {
celltemperature[i] = (rx_frame.data.u8[i + 1] / 2) - 40;
}
break;
case 1: // Cellvoltages 1-42
cellvoltages[0] = (((rx_frame.data.u8[2] & 0x0F) << 8) | rx_frame.data.u8[1]) + 1000;
cellvoltages[1] = ((rx_frame.data.u8[3] << 4) | (rx_frame.data.u8[2] >> 4)) + 1000;
cellvoltages[2] = (((rx_frame.data.u8[5] & 0x0F) << 8) | rx_frame.data.u8[4]) + 1000;
cellvoltages[3] = ((rx_frame.data.u8[6] << 4) | (rx_frame.data.u8[5] >> 4)) + 1000;
cellvoltages[4] = (((rx_frame.data.u8[8] & 0x0F) << 8) | rx_frame.data.u8[7]) + 1000;
cellvoltages[5] = ((rx_frame.data.u8[9] << 4) | (rx_frame.data.u8[8] >> 4)) + 1000;
cellvoltages[6] = (((rx_frame.data.u8[11] & 0x0F) << 8) | rx_frame.data.u8[10]) + 1000;
cellvoltages[7] = ((rx_frame.data.u8[12] << 4) | (rx_frame.data.u8[11] >> 4)) + 1000;
cellvoltages[8] = (((rx_frame.data.u8[14] & 0x0F) << 8) | rx_frame.data.u8[13]) + 1000;
cellvoltages[9] = ((rx_frame.data.u8[15] << 4) | (rx_frame.data.u8[14] >> 4)) + 1000;
cellvoltages[10] = (((rx_frame.data.u8[17] & 0x0F) << 8) | rx_frame.data.u8[16]) + 1000;
cellvoltages[11] = ((rx_frame.data.u8[18] << 4) | (rx_frame.data.u8[17] >> 4)) + 1000;
cellvoltages[12] = (((rx_frame.data.u8[20] & 0x0F) << 8) | rx_frame.data.u8[19]) + 1000;
cellvoltages[13] = ((rx_frame.data.u8[21] << 4) | (rx_frame.data.u8[20] >> 4)) + 1000;
cellvoltages[14] = (((rx_frame.data.u8[23] & 0x0F) << 8) | rx_frame.data.u8[22]) + 1000;
cellvoltages[15] = ((rx_frame.data.u8[24] << 4) | (rx_frame.data.u8[23] >> 4)) + 1000;
cellvoltages[16] = (((rx_frame.data.u8[26] & 0x0F) << 8) | rx_frame.data.u8[25]) + 1000;
cellvoltages[17] = ((rx_frame.data.u8[27] << 4) | (rx_frame.data.u8[26] >> 4)) + 1000;
cellvoltages[18] = (((rx_frame.data.u8[29] & 0x0F) << 8) | rx_frame.data.u8[28]) + 1000;
cellvoltages[19] = ((rx_frame.data.u8[30] << 4) | (rx_frame.data.u8[29] >> 4)) + 1000;
cellvoltages[20] = (((rx_frame.data.u8[32] & 0x0F) << 8) | rx_frame.data.u8[31]) + 1000;
cellvoltages[21] = ((rx_frame.data.u8[33] << 4) | (rx_frame.data.u8[32] >> 4)) + 1000;
cellvoltages[22] = (((rx_frame.data.u8[35] & 0x0F) << 8) | rx_frame.data.u8[34]) + 1000;
cellvoltages[23] = ((rx_frame.data.u8[36] << 4) | (rx_frame.data.u8[35] >> 4)) + 1000;
cellvoltages[24] = (((rx_frame.data.u8[38] & 0x0F) << 8) | rx_frame.data.u8[37]) + 1000;
cellvoltages[25] = ((rx_frame.data.u8[39] << 4) | (rx_frame.data.u8[38] >> 4)) + 1000;
cellvoltages[26] = (((rx_frame.data.u8[41] & 0x0F) << 8) | rx_frame.data.u8[40]) + 1000;
cellvoltages[27] = ((rx_frame.data.u8[42] << 4) | (rx_frame.data.u8[41] >> 4)) + 1000;
cellvoltages[28] = (((rx_frame.data.u8[44] & 0x0F) << 8) | rx_frame.data.u8[43]) + 1000;
cellvoltages[29] = ((rx_frame.data.u8[45] << 4) | (rx_frame.data.u8[44] >> 4)) + 1000;
cellvoltages[30] = (((rx_frame.data.u8[47] & 0x0F) << 8) | rx_frame.data.u8[46]) + 1000;
cellvoltages[31] = ((rx_frame.data.u8[48] << 4) | (rx_frame.data.u8[47] >> 4)) + 1000;
cellvoltages[32] = (((rx_frame.data.u8[50] & 0x0F) << 8) | rx_frame.data.u8[49]) + 1000;
cellvoltages[33] = ((rx_frame.data.u8[51] << 4) | (rx_frame.data.u8[50] >> 4)) + 1000;
cellvoltages[34] = (((rx_frame.data.u8[53] & 0x0F) << 8) | rx_frame.data.u8[52]) + 1000;
cellvoltages[35] = ((rx_frame.data.u8[54] << 4) | (rx_frame.data.u8[53] >> 4)) + 1000;
cellvoltages[36] = (((rx_frame.data.u8[56] & 0x0F) << 8) | rx_frame.data.u8[55]) + 1000;
cellvoltages[37] = ((rx_frame.data.u8[57] << 4) | (rx_frame.data.u8[56] >> 4)) + 1000;
cellvoltages[38] = (((rx_frame.data.u8[59] & 0x0F) << 8) | rx_frame.data.u8[58]) + 1000;
cellvoltages[39] = ((rx_frame.data.u8[60] << 4) | (rx_frame.data.u8[59] >> 4)) + 1000;
cellvoltages[40] = (((rx_frame.data.u8[62] & 0x0F) << 8) | rx_frame.data.u8[61]) + 1000;
cellvoltages[41] = ((rx_frame.data.u8[63] << 4) | (rx_frame.data.u8[62] >> 4)) + 1000;
break;
case 2: // Cellvoltages 43-84
cellvoltages[42] = (((rx_frame.data.u8[2] & 0x0F) << 8) | rx_frame.data.u8[1]) + 1000;
cellvoltages[43] = ((rx_frame.data.u8[3] << 4) | (rx_frame.data.u8[2] >> 4)) + 1000;
cellvoltages[44] = (((rx_frame.data.u8[5] & 0x0F) << 8) | rx_frame.data.u8[4]) + 1000;
cellvoltages[45] = ((rx_frame.data.u8[6] << 4) | (rx_frame.data.u8[5] >> 4)) + 1000;
cellvoltages[46] = (((rx_frame.data.u8[8] & 0x0F) << 8) | rx_frame.data.u8[7]) + 1000;
cellvoltages[47] = ((rx_frame.data.u8[9] << 4) | (rx_frame.data.u8[8] >> 4)) + 1000;
cellvoltages[48] = (((rx_frame.data.u8[11] & 0x0F) << 8) | rx_frame.data.u8[10]) + 1000;
cellvoltages[49] = ((rx_frame.data.u8[12] << 4) | (rx_frame.data.u8[11] >> 4)) + 1000;
cellvoltages[50] = (((rx_frame.data.u8[14] & 0x0F) << 8) | rx_frame.data.u8[13]) + 1000;
cellvoltages[51] = ((rx_frame.data.u8[15] << 4) | (rx_frame.data.u8[14] >> 4)) + 1000;
cellvoltages[52] = (((rx_frame.data.u8[17] & 0x0F) << 8) | rx_frame.data.u8[16]) + 1000;
cellvoltages[53] = ((rx_frame.data.u8[18] << 4) | (rx_frame.data.u8[17] >> 4)) + 1000;
cellvoltages[54] = (((rx_frame.data.u8[20] & 0x0F) << 8) | rx_frame.data.u8[19]) + 1000;
cellvoltages[55] = ((rx_frame.data.u8[21] << 4) | (rx_frame.data.u8[20] >> 4)) + 1000;
cellvoltages[56] = (((rx_frame.data.u8[23] & 0x0F) << 8) | rx_frame.data.u8[22]) + 1000;
cellvoltages[57] = ((rx_frame.data.u8[24] << 4) | (rx_frame.data.u8[23] >> 4)) + 1000;
cellvoltages[58] = (((rx_frame.data.u8[26] & 0x0F) << 8) | rx_frame.data.u8[25]) + 1000;
cellvoltages[59] = ((rx_frame.data.u8[27] << 4) | (rx_frame.data.u8[26] >> 4)) + 1000;
cellvoltages[60] = (((rx_frame.data.u8[29] & 0x0F) << 8) | rx_frame.data.u8[28]) + 1000;
cellvoltages[61] = ((rx_frame.data.u8[30] << 4) | (rx_frame.data.u8[29] >> 4)) + 1000;
cellvoltages[62] = (((rx_frame.data.u8[32] & 0x0F) << 8) | rx_frame.data.u8[31]) + 1000;
cellvoltages[63] = ((rx_frame.data.u8[33] << 4) | (rx_frame.data.u8[32] >> 4)) + 1000;
cellvoltages[64] = (((rx_frame.data.u8[35] & 0x0F) << 8) | rx_frame.data.u8[34]) + 1000;
cellvoltages[65] = ((rx_frame.data.u8[36] << 4) | (rx_frame.data.u8[35] >> 4)) + 1000;
cellvoltages[66] = (((rx_frame.data.u8[38] & 0x0F) << 8) | rx_frame.data.u8[37]) + 1000;
cellvoltages[67] = ((rx_frame.data.u8[39] << 4) | (rx_frame.data.u8[38] >> 4)) + 1000;
cellvoltages[68] = (((rx_frame.data.u8[41] & 0x0F) << 8) | rx_frame.data.u8[40]) + 1000;
cellvoltages[69] = ((rx_frame.data.u8[42] << 4) | (rx_frame.data.u8[41] >> 4)) + 1000;
cellvoltages[70] = (((rx_frame.data.u8[44] & 0x0F) << 8) | rx_frame.data.u8[43]) + 1000;
cellvoltages[71] = ((rx_frame.data.u8[45] << 4) | (rx_frame.data.u8[44] >> 4)) + 1000;
cellvoltages[72] = (((rx_frame.data.u8[47] & 0x0F) << 8) | rx_frame.data.u8[46]) + 1000;
cellvoltages[73] = ((rx_frame.data.u8[48] << 4) | (rx_frame.data.u8[47] >> 4)) + 1000;
cellvoltages[74] = (((rx_frame.data.u8[50] & 0x0F) << 8) | rx_frame.data.u8[49]) + 1000;
cellvoltages[75] = ((rx_frame.data.u8[51] << 4) | (rx_frame.data.u8[50] >> 4)) + 1000;
cellvoltages[76] = (((rx_frame.data.u8[53] & 0x0F) << 8) | rx_frame.data.u8[52]) + 1000;
cellvoltages[77] = ((rx_frame.data.u8[54] << 4) | (rx_frame.data.u8[53] >> 4)) + 1000;
cellvoltages[78] = (((rx_frame.data.u8[56] & 0x0F) << 8) | rx_frame.data.u8[55]) + 1000;
cellvoltages[79] = ((rx_frame.data.u8[57] << 4) | (rx_frame.data.u8[56] >> 4)) + 1000;
cellvoltages[80] = (((rx_frame.data.u8[59] & 0x0F) << 8) | rx_frame.data.u8[58]) + 1000;
cellvoltages[81] = ((rx_frame.data.u8[60] << 4) | (rx_frame.data.u8[59] >> 4)) + 1000;
cellvoltages[82] = (((rx_frame.data.u8[62] & 0x0F) << 8) | rx_frame.data.u8[61]) + 1000;
cellvoltages[83] = ((rx_frame.data.u8[63] << 4) | (rx_frame.data.u8[62] >> 4)) + 1000;
break;
case 3: // Cellvoltages 85-126
cellvoltages[84] = (((rx_frame.data.u8[2] & 0x0F) << 8) | rx_frame.data.u8[1]) + 1000;
cellvoltages[85] = ((rx_frame.data.u8[3] << 4) | (rx_frame.data.u8[2] >> 4)) + 1000;
cellvoltages[86] = (((rx_frame.data.u8[5] & 0x0F) << 8) | rx_frame.data.u8[4]) + 1000;
cellvoltages[87] = ((rx_frame.data.u8[6] << 4) | (rx_frame.data.u8[5] >> 4)) + 1000;
cellvoltages[88] = (((rx_frame.data.u8[8] & 0x0F) << 8) | rx_frame.data.u8[7]) + 1000;
cellvoltages[89] = ((rx_frame.data.u8[9] << 4) | (rx_frame.data.u8[8] >> 4)) + 1000;
cellvoltages[90] = (((rx_frame.data.u8[11] & 0x0F) << 8) | rx_frame.data.u8[10]) + 1000;
cellvoltages[91] = ((rx_frame.data.u8[12] << 4) | (rx_frame.data.u8[11] >> 4)) + 1000;
cellvoltages[92] = (((rx_frame.data.u8[14] & 0x0F) << 8) | rx_frame.data.u8[13]) + 1000;
cellvoltages[93] = ((rx_frame.data.u8[15] << 4) | (rx_frame.data.u8[14] >> 4)) + 1000;
cellvoltages[94] = (((rx_frame.data.u8[17] & 0x0F) << 8) | rx_frame.data.u8[16]) + 1000;
cellvoltages[95] = ((rx_frame.data.u8[18] << 4) | (rx_frame.data.u8[17] >> 4)) + 1000;
cellvoltages[96] = (((rx_frame.data.u8[20] & 0x0F) << 8) | rx_frame.data.u8[19]) + 1000;
cellvoltages[97] = ((rx_frame.data.u8[21] << 4) | (rx_frame.data.u8[20] >> 4)) + 1000;
cellvoltages[98] = (((rx_frame.data.u8[23] & 0x0F) << 8) | rx_frame.data.u8[22]) + 1000;
cellvoltages[99] = ((rx_frame.data.u8[24] << 4) | (rx_frame.data.u8[23] >> 4)) + 1000;
cellvoltages[100] = (((rx_frame.data.u8[26] & 0x0F) << 8) | rx_frame.data.u8[25]) + 1000;
cellvoltages[101] = ((rx_frame.data.u8[27] << 4) | (rx_frame.data.u8[26] >> 4)) + 1000;
cellvoltages[102] = (((rx_frame.data.u8[29] & 0x0F) << 8) | rx_frame.data.u8[28]) + 1000;
cellvoltages[103] = ((rx_frame.data.u8[30] << 4) | (rx_frame.data.u8[29] >> 4)) + 1000;
cellvoltages[104] = (((rx_frame.data.u8[32] & 0x0F) << 8) | rx_frame.data.u8[31]) + 1000;
cellvoltages[105] = ((rx_frame.data.u8[33] << 4) | (rx_frame.data.u8[32] >> 4)) + 1000;
cellvoltages[106] = (((rx_frame.data.u8[35] & 0x0F) << 8) | rx_frame.data.u8[34]) + 1000;
cellvoltages[107] = ((rx_frame.data.u8[36] << 4) | (rx_frame.data.u8[35] >> 4)) + 1000;
cellvoltages[108] = (((rx_frame.data.u8[38] & 0x0F) << 8) | rx_frame.data.u8[37]) + 1000;
cellvoltages[109] = ((rx_frame.data.u8[39] << 4) | (rx_frame.data.u8[38] >> 4)) + 1000;
cellvoltages[110] = (((rx_frame.data.u8[41] & 0x0F) << 8) | rx_frame.data.u8[40]) + 1000;
cellvoltages[111] = ((rx_frame.data.u8[42] << 4) | (rx_frame.data.u8[41] >> 4)) + 1000;
cellvoltages[112] = (((rx_frame.data.u8[44] & 0x0F) << 8) | rx_frame.data.u8[43]) + 1000;
cellvoltages[113] = ((rx_frame.data.u8[45] << 4) | (rx_frame.data.u8[44] >> 4)) + 1000;
cellvoltages[114] = (((rx_frame.data.u8[47] & 0x0F) << 8) | rx_frame.data.u8[46]) + 1000;
cellvoltages[115] = ((rx_frame.data.u8[48] << 4) | (rx_frame.data.u8[47] >> 4)) + 1000;
cellvoltages[116] = (((rx_frame.data.u8[50] & 0x0F) << 8) | rx_frame.data.u8[49]) + 1000;
cellvoltages[117] = ((rx_frame.data.u8[51] << 4) | (rx_frame.data.u8[50] >> 4)) + 1000;
cellvoltages[118] = (((rx_frame.data.u8[53] & 0x0F) << 8) | rx_frame.data.u8[52]) + 1000;
cellvoltages[119] = ((rx_frame.data.u8[54] << 4) | (rx_frame.data.u8[53] >> 4)) + 1000;
cellvoltages[120] = (((rx_frame.data.u8[56] & 0x0F) << 8) | rx_frame.data.u8[55]) + 1000;
cellvoltages[121] = ((rx_frame.data.u8[57] << 4) | (rx_frame.data.u8[56] >> 4)) + 1000;
cellvoltages[122] = (((rx_frame.data.u8[59] & 0x0F) << 8) | rx_frame.data.u8[58]) + 1000;
cellvoltages[123] = ((rx_frame.data.u8[60] << 4) | (rx_frame.data.u8[59] >> 4)) + 1000;
cellvoltages[124] = (((rx_frame.data.u8[62] & 0x0F) << 8) | rx_frame.data.u8[61]) + 1000;
cellvoltages[125] = ((rx_frame.data.u8[63] << 4) | (rx_frame.data.u8[62] >> 4)) + 1000;
break;
case 4: //Cellvoltages 127-160
cellvoltages[126] = (((rx_frame.data.u8[2] & 0x0F) << 8) | rx_frame.data.u8[1]) + 1000;
cellvoltages[127] = ((rx_frame.data.u8[3] << 4) | (rx_frame.data.u8[2] >> 4)) + 1000;
cellvoltages[128] = (((rx_frame.data.u8[5] & 0x0F) << 8) | rx_frame.data.u8[4]) + 1000;
cellvoltages[129] = ((rx_frame.data.u8[6] << 4) | (rx_frame.data.u8[5] >> 4)) + 1000;
cellvoltages[130] = (((rx_frame.data.u8[8] & 0x0F) << 8) | rx_frame.data.u8[7]) + 1000;
cellvoltages[131] = ((rx_frame.data.u8[9] << 4) | (rx_frame.data.u8[8] >> 4)) + 1000;
cellvoltages[132] = (((rx_frame.data.u8[11] & 0x0F) << 8) | rx_frame.data.u8[10]) + 1000;
cellvoltages[133] = ((rx_frame.data.u8[12] << 4) | (rx_frame.data.u8[11] >> 4)) + 1000;
cellvoltages[134] = (((rx_frame.data.u8[14] & 0x0F) << 8) | rx_frame.data.u8[13]) + 1000;
cellvoltages[135] = ((rx_frame.data.u8[15] << 4) | (rx_frame.data.u8[14] >> 4)) + 1000;
cellvoltages[136] = (((rx_frame.data.u8[17] & 0x0F) << 8) | rx_frame.data.u8[16]) + 1000;
cellvoltages[137] = ((rx_frame.data.u8[18] << 4) | (rx_frame.data.u8[17] >> 4)) + 1000;
cellvoltages[138] = (((rx_frame.data.u8[20] & 0x0F) << 8) | rx_frame.data.u8[19]) + 1000;
cellvoltages[139] = ((rx_frame.data.u8[21] << 4) | (rx_frame.data.u8[20] >> 4)) + 1000;
cellvoltages[140] = (((rx_frame.data.u8[23] & 0x0F) << 8) | rx_frame.data.u8[22]) + 1000;
cellvoltages[141] = ((rx_frame.data.u8[24] << 4) | (rx_frame.data.u8[23] >> 4)) + 1000;
cellvoltages[142] = (((rx_frame.data.u8[26] & 0x0F) << 8) | rx_frame.data.u8[25]) + 1000;
cellvoltages[143] = ((rx_frame.data.u8[27] << 4) | (rx_frame.data.u8[26] >> 4)) + 1000;
cellvoltages[144] = (((rx_frame.data.u8[29] & 0x0F) << 8) | rx_frame.data.u8[28]) + 1000;
cellvoltages[145] = ((rx_frame.data.u8[30] << 4) | (rx_frame.data.u8[29] >> 4)) + 1000;
cellvoltages[146] = (((rx_frame.data.u8[32] & 0x0F) << 8) | rx_frame.data.u8[31]) + 1000;
cellvoltages[147] = ((rx_frame.data.u8[33] << 4) | (rx_frame.data.u8[32] >> 4)) + 1000;
cellvoltages[148] = (((rx_frame.data.u8[35] & 0x0F) << 8) | rx_frame.data.u8[34]) + 1000;
cellvoltages[149] = ((rx_frame.data.u8[36] << 4) | (rx_frame.data.u8[35] >> 4)) + 1000;
cellvoltages[150] = (((rx_frame.data.u8[38] & 0x0F) << 8) | rx_frame.data.u8[37]) + 1000;
cellvoltages[151] = ((rx_frame.data.u8[39] << 4) | (rx_frame.data.u8[38] >> 4)) + 1000;
cellvoltages[152] = (((rx_frame.data.u8[41] & 0x0F) << 8) | rx_frame.data.u8[40]) + 1000;
cellvoltages[153] = ((rx_frame.data.u8[42] << 4) | (rx_frame.data.u8[41] >> 4)) + 1000;
cellvoltages[154] = (((rx_frame.data.u8[44] & 0x0F) << 8) | rx_frame.data.u8[43]) + 1000;
cellvoltages[155] = ((rx_frame.data.u8[45] << 4) | (rx_frame.data.u8[44] >> 4)) + 1000;
cellvoltages[156] = (((rx_frame.data.u8[47] & 0x0F) << 8) | rx_frame.data.u8[46]) + 1000;
cellvoltages[157] = ((rx_frame.data.u8[48] << 4) | (rx_frame.data.u8[47] >> 4)) + 1000;
cellvoltages[158] = (((rx_frame.data.u8[50] & 0x0F) << 8) | rx_frame.data.u8[49]) + 1000;
cellvoltages[159] = ((rx_frame.data.u8[51] << 4) | (rx_frame.data.u8[50] >> 4)) + 1000;
break;
default: //Invalid mux
//TODO: Add corrupted CAN message counter tick?
break;
}
break;
case 0x1C42017B: // BMS - Non-Cyclic, TP_ISO
//hybrid_01_response_fd_data (Whole frame)
break;
case 0x1A5555B0: // BMS 1000ms cyclic
duration_discharge_power_watt = ((rx_frame.data.u8[6] & 0x0F) << 8) | rx_frame.data.u8[5];
duration_charge_power_watt = (rx_frame.data.u8[7] << 4) | rx_frame.data.u8[6] >> 4;
maximum_voltage = ((rx_frame.data.u8[3] & 0x3F) << 4) | rx_frame.data.u8[2] >> 4;
minimum_voltage = (rx_frame.data.u8[4] << 2) | rx_frame.data.u8[3] >> 6;
break;
case 0x1A5555B1: // BMS 1000ms cyclic
// All realtime_ have same enumeration, 0 = no fault, 1 = error level 1, 2 error level 2, 3 error level 3
realtime_overcurrent_monitor = ((rx_frame.data.u8[3] & 0x01) << 2) | rx_frame.data.u8[2] >> 6;
realtime_CAN_communication_fault = (rx_frame.data.u8[3] & 0x0E) >> 1;
realtime_overcharge_warning = (rx_frame.data.u8[3] & 0x70) >> 4;
realtime_SOC_too_high = ((rx_frame.data.u8[4] & 0x03) << 1) | rx_frame.data.u8[3] >> 7;
realtime_SOC_too_low = (rx_frame.data.u8[4] & 0x1C) >> 2;
realtime_SOC_jumping_warning = (rx_frame.data.u8[4] & 0xE0) >> 5;
realtime_temperature_difference_warning = rx_frame.data.u8[5] & 0x07;
realtime_cell_overtemperature_warning = (rx_frame.data.u8[5] & 0x38) >> 3;
realtime_cell_undertemperature_warning = ((rx_frame.data.u8[6] & 0x01) << 2) | rx_frame.data.u8[5] >> 6;
realtime_battery_overvoltage_warning = (rx_frame.data.u8[6] & 0x0E) >> 1;
realtime_battery_undervoltage_warning = (rx_frame.data.u8[6] & 0x70) >> 4;
realtime_cell_overvoltage_warning = ((rx_frame.data.u8[7] & 0x03) << 1) | rx_frame.data.u8[6] >> 7;
realtime_cell_undervoltage_warning = (rx_frame.data.u8[7] & 0x1C) >> 2;
realtime_cell_imbalance_warning = (rx_frame.data.u8[7] & 0xE0) >> 5;
for (uint8_t i = 0; i < 26; i++) { // Frame 9 to 34 is S/N for battery
battery_serialnumber[i] = rx_frame.data.u8[i + 9];
}
realtime_warning_battery_unathorized = (rx_frame.data.u8[40] & 0x07);
break;
case 0x2AF: // BMS 50ms
actual_battery_voltage =
((rx_frame.data.u8[1] & 0x3F) << 8) | rx_frame.data.u8[0]; //*0.0625 // Seems to be 0.125 in logging
regen_battery = ((rx_frame.data.u8[5] & 0x7F) << 8) | rx_frame.data.u8[4];
energy_extracted_from_battery = ((rx_frame.data.u8[7] & 0x7F) << 8) | rx_frame.data.u8[6];
break;
case 0x578: // BMS 100ms
BMS_578_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
BMS_578_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
BMS_Status_DCLS = ((rx_frame.data.u8[1] & 0x30) >> 4);
DC_voltage_DCLS = (rx_frame.data.u8[2] << 6) | (rx_frame.data.u8[1] >> 6);
max_fastcharging_current_amp = ((rx_frame.data.u8[4] & 0x01) << 8) | rx_frame.data.u8[3];
DC_voltage_chargeport = (rx_frame.data.u8[7] << 4) | (rx_frame.data.u8[6] >> 4);
break;
case 0x5A2: // BMS 500ms normal, 100ms fast
BMS_5A2_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
BMS_5A2_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
service_disconnect_switch_missing = (rx_frame.data.u8[1] & 0x20) >> 5;
pilotline_open = (rx_frame.data.u8[1] & 0x10) >> 4;
BMS_status_voltage_free = (rx_frame.data.u8[1] & 0xC0) >> 6;
BMS_OBD_MIL = (rx_frame.data.u8[2] & 0x01);
BMS_error_status = (rx_frame.data.u8[2] & 0x70) >> 4;
BMS_error_lamp_req = (rx_frame.data.u8[4] & 0x04) >> 2;
BMS_warning_lamp_req = (rx_frame.data.u8[4] & 0x08) >> 3;
BMS_Kl30c_Status = (rx_frame.data.u8[4] & 0x30) >> 4;
if (BMS_Kl30c_Status != 0) { // init state
BMS_capacity_ah = ((rx_frame.data.u8[4] & 0x03) << 9) | (rx_frame.data.u8[3] << 1) | (rx_frame.data.u8[2] >> 7);
}
break;
case 0x5CA: // BMS 500ms
BMS_5CA_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
BMS_5CA_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
balancing_request = (rx_frame.data.u8[5] & 0x08) >> 3; //True/False
battery_diagnostic = (rx_frame.data.u8[3] & 0x07);
battery_Wh_left =
(rx_frame.data.u8[2] << 4) | (rx_frame.data.u8[1] >> 4); //*50 ! Not usable, seems to always contain 0x7F0
battery_potential_status =
(rx_frame.data.u8[5] & 0x30) >> 4; //0 = function not enabled, 1= no potential, 2 = potential on, 3 = fault
battery_temperature_warning =
(rx_frame.data.u8[7] & 0x0C) >> 2; // 0 = no warning, 1 = temp level 1, 2=temp level 2
battery_Wh_max =
((rx_frame.data.u8[5] & 0x07) << 8) | rx_frame.data.u8[4]; //*50 ! Not usable, seems to always contain 0x7F0
break;
case 0x0CF: //BMS 10ms
BMS_0CF_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
BMS_0CF_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
BMS_welded_contactors_status = (rx_frame.data.u8[1] & 0x60) >> 5;
BMS_ext_limits_active = (rx_frame.data.u8[1] & 0x80) >> 7;
BMS_mode = (rx_frame.data.u8[2] & 0x07);
switch (BMS_mode) {
case 1:
case 3:
case 4:
datalayer.system.status.battery_allows_contactor_closing = true;
break;
default:
datalayer.system.status.battery_allows_contactor_closing = false;
}
BMS_HVIL_status = (rx_frame.data.u8[2] & 0x18) >> 3;
BMS_error_shutdown = (rx_frame.data.u8[2] & 0x20) >> 5;
BMS_error_shutdown_request = (rx_frame.data.u8[2] & 0x40) >> 6;
BMS_fault_performance = (rx_frame.data.u8[2] & 0x80) >> 7;
BMS_fault_emergency_shutdown_crash = (rx_frame.data.u8[4] & 0x80) >> 7;
if (BMS_mode != 7) { // Init state, values below are invalid
BMS_current = ((rx_frame.data.u8[4] & 0x7F) << 8) | rx_frame.data.u8[3];
BMS_voltage_intermediate = (((rx_frame.data.u8[6] & 0x0F) << 8) + (rx_frame.data.u8[5]));
BMS_voltage = ((rx_frame.data.u8[7] << 4) + ((rx_frame.data.u8[6] & 0xF0) >> 4));
}
break;
case 0x1C42007B: // Reply from battery
if (rx_frame.data.u8[0] == 0x10) { //PID header
transmit_can_frame(&MEB_ACK_FRAME, can_config.battery);
}
if (rx_frame.DLC == 8) {
pid_reply = (rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3];
} else { //12 or 24bit message has reply in other location
pid_reply = (rx_frame.data.u8[3] << 8) + rx_frame.data.u8[4];
}
switch (pid_reply) {
case PID_SOC:
battery_soc_polled = rx_frame.data.u8[4] * 4; // 135*4 = 54.0%
case PID_VOLTAGE:
battery_voltage_polled = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
break;
case PID_CURRENT: // IDLE 0A: 00 08 62 1E 3D (00 02) 49 F0 39 AA AA
battery_current_polled = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[6]); //TODO: right bits?
break;
case PID_MAX_TEMP:
battery_max_temp = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
break;
case PID_MIN_TEMP:
battery_min_temp = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
break;
case PID_MAX_CHARGE_VOLTAGE:
battery_max_charge_voltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
break;
case PID_MIN_DISCHARGE_VOLTAGE:
battery_min_discharge_voltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
break;
case PID_ALLOWED_CHARGE_POWER:
battery_allowed_charge_power = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
break;
case PID_ALLOWED_DISCHARGE_POWER:
battery_allowed_discharge_power = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
break;
case PID_CELLVOLTAGE_CELL_1:
cellvoltages_polled[0] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_2:
cellvoltages_polled[1] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_3:
cellvoltages_polled[2] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_4:
cellvoltages_polled[3] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_5:
cellvoltages_polled[4] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_6:
cellvoltages_polled[5] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_7:
cellvoltages_polled[6] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_8:
cellvoltages_polled[7] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_9:
cellvoltages_polled[8] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_10:
cellvoltages_polled[9] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_11:
cellvoltages_polled[10] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_12:
cellvoltages_polled[11] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_13:
cellvoltages_polled[12] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_14:
cellvoltages_polled[13] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_15:
cellvoltages_polled[14] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_16:
cellvoltages_polled[15] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_17:
cellvoltages_polled[16] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_18:
cellvoltages_polled[17] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_19:
cellvoltages_polled[18] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_20:
cellvoltages_polled[19] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_21:
cellvoltages_polled[20] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_22:
cellvoltages_polled[21] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_23:
cellvoltages_polled[22] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_24:
cellvoltages_polled[23] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_25:
cellvoltages_polled[24] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_26:
cellvoltages_polled[25] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_27:
cellvoltages_polled[26] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_28:
cellvoltages_polled[27] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_29:
cellvoltages_polled[28] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_30:
cellvoltages_polled[29] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_31:
cellvoltages_polled[30] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_32:
cellvoltages_polled[31] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_33:
cellvoltages_polled[32] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_34:
cellvoltages_polled[33] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_35:
cellvoltages_polled[34] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_36:
cellvoltages_polled[35] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_37:
cellvoltages_polled[36] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_38:
cellvoltages_polled[37] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_39:
cellvoltages_polled[38] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_40:
cellvoltages_polled[39] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_41:
cellvoltages_polled[40] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_42:
cellvoltages_polled[41] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_43:
cellvoltages_polled[42] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_44:
cellvoltages_polled[43] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_45:
cellvoltages_polled[44] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_46:
cellvoltages_polled[45] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_47:
cellvoltages_polled[46] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_48:
cellvoltages_polled[47] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_49:
cellvoltages_polled[48] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_50:
cellvoltages_polled[49] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_51:
cellvoltages_polled[50] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_52:
cellvoltages_polled[51] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_53:
cellvoltages_polled[52] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_54:
cellvoltages_polled[53] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_55:
cellvoltages_polled[54] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_56:
cellvoltages_polled[55] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_57:
cellvoltages_polled[56] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_58:
cellvoltages_polled[57] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_59:
cellvoltages_polled[58] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_60:
cellvoltages_polled[59] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_61:
cellvoltages_polled[60] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_62:
cellvoltages_polled[61] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_63:
cellvoltages_polled[62] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_64:
cellvoltages_polled[63] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_65:
cellvoltages_polled[64] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_66:
cellvoltages_polled[65] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_67:
cellvoltages_polled[66] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_68:
cellvoltages_polled[67] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_69:
cellvoltages_polled[68] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_70:
cellvoltages_polled[69] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_71:
cellvoltages_polled[70] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_72:
cellvoltages_polled[71] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_73:
cellvoltages_polled[72] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_74:
cellvoltages_polled[73] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_75:
cellvoltages_polled[74] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_76:
cellvoltages_polled[75] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_77:
cellvoltages_polled[76] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_78:
cellvoltages_polled[77] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_79:
cellvoltages_polled[78] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_80:
cellvoltages_polled[79] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_81:
cellvoltages_polled[80] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_82:
cellvoltages_polled[81] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_83:
cellvoltages_polled[82] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_84:
cellvoltages_polled[83] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) + 1000);
break;
case PID_CELLVOLTAGE_CELL_85:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[84] = (tempval + 1000);
} else { // Cell 85 unavailable. We have a 84S battery (48kWh)
datalayer.battery.info.number_of_cells = 84;
nof_cells_determined = true;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_84S_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_84S_DV;
}
break;
case PID_CELLVOLTAGE_CELL_86:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[85] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_87:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[86] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_88:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[87] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_89:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[88] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_90:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[89] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_91:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[90] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_92:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[91] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_93:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[92] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_94:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[93] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_95:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[94] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_96:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[95] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_97:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[96] = (tempval + 1000);
} else { // Cell 97 unavailable. We have a 96S battery (55kWh) (Unless already specified as 84S)
if (datalayer.battery.info.number_of_cells == 84) {
// Do nothing, we already identified it as 84S
} else {
datalayer.battery.info.number_of_cells = 96;
nof_cells_determined = true;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_96S_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_96S_DV;
}
}
break;
case PID_CELLVOLTAGE_CELL_98:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[97] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_99:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[98] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_100:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[99] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_101:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[100] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_102:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[101] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_103:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[102] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_104:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[103] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_105:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[104] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_106:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[105] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_107:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
if (tempval != 0xFFE) {
cellvoltages_polled[106] = (tempval + 1000);
}
break;
case PID_CELLVOLTAGE_CELL_108:
tempval = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
nof_cells_determined = true; // This is placed outside of the if, to make
// sure we only take the shortcuts to determine the number of cells once.
if (tempval != 0xFFE) {
cellvoltages_polled[107] = (tempval + 1000);
datalayer.battery.info.number_of_cells = 108;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_108S_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_108S_DV;
}
break;
default:
break;
}
break;
default:
break;
}
}
void transmit_can_battery() {
unsigned long currentMillis = millis();
// Send 10ms CAN Message
if (currentMillis > last_can_msg_timestamp + 500) {
first_can_msg = 0;
}
if (currentMillis - previousMillis10ms >= INTERVAL_10_MS) {
// Check if sending of CAN messages has been delayed too much.
if ((currentMillis - previousMillis10ms >= INTERVAL_10_MS_DELAYED) && (currentMillis > BOOTUP_TIME)) {
set_event(EVENT_CAN_OVERRUN, (currentMillis - previousMillis10ms));
} else {
clear_event(EVENT_CAN_OVERRUN);
}
previousMillis10ms = currentMillis;
MEB_0FC.data.u8[1] = ((MEB_0FC.data.u8[1] & 0xF0) | counter_10ms);
MEB_0FC.data.u8[0] = vw_crc_calc(MEB_0FC.data.u8, MEB_0FC.DLC, MEB_0FC.ID);
counter_10ms = (counter_10ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_0FC, can_config.battery); // Required for contactor closing
}
// Send 20ms CAN Message
if (currentMillis - previousMillis20ms >= INTERVAL_20_MS) {
previousMillis20ms = currentMillis;
MEB_0FD.data.u8[1] = ((MEB_0FD.data.u8[1] & 0xF0) | counter_20ms);
MEB_0FD.data.u8[0] = vw_crc_calc(MEB_0FD.data.u8, MEB_0FD.DLC, MEB_0FD.ID);
counter_20ms = (counter_20ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_0FD, can_config.battery); // Required for contactor closing
}
// Send 40ms CAN Message
if (currentMillis - previousMillis40ms >= INTERVAL_40_MS) {
previousMillis40ms = currentMillis;
/* Handle content for 0x040 message */
/* Airbag message, needed for BMS to function */
MEB_040.data.u8[7] = counter_040;
MEB_040.data.u8[1] = ((MEB_040.data.u8[1] & 0xF0) | counter_40ms);
MEB_040.data.u8[0] = vw_crc_calc(MEB_040.data.u8, MEB_040.DLC, MEB_040.ID);
counter_40ms = (counter_40ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
if (toggle) {
counter_040 = (counter_040 + 1) % 256; // Increment only on every other pass
}
toggle = !toggle; // Flip the toggle each time the code block is executed
transmit_can_frame(&MEB_040, can_config.battery); // Airbag message - Needed for contactor closing
}
// Send 50ms CAN Message
if (currentMillis - previousMillis50ms >= INTERVAL_50_MS) {
previousMillis50ms = currentMillis;
/* Handle content for 0x0C0 message */
/* BMS needs to see this EM1 message. Content located in frame5&6 especially (can be static?)*/
/* Also the voltage seen externally to battery is in frame 7&8. At least for the 62kWh ID3 version does not seem to matter, but we send it anyway. */
MEB_0C0.data.u8[1] = ((MEB_0C0.data.u8[1] & 0xF0) | counter_50ms);
MEB_0C0.data.u8[7] = ((datalayer.battery.status.voltage_dV / 10) * 4) & 0x00FF;
MEB_0C0.data.u8[8] =
((MEB_0C0.data.u8[8] & 0xF0) | ((((datalayer.battery.status.voltage_dV / 10) * 4) >> 8) & 0x0F));
MEB_0C0.data.u8[0] = vw_crc_calc(MEB_0C0.data.u8, MEB_0C0.DLC, MEB_0C0.ID);
counter_50ms = (counter_50ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_0C0, can_config.battery); // Needed for contactor closing
}
// Send 100ms CAN Message
if (currentMillis - previousMillis100ms >= INTERVAL_100_MS) {
previousMillis100ms = currentMillis;
//HV request and DC/DC control lies in 0x503
MEB_503.data.u8[3] = 0x00;
if (datalayer.battery.status.bms_status != FAULT && first_can_msg > 0 && currentMillis > first_can_msg + 2000) {
MEB_503.data.u8[1] = 0xB0;
MEB_503.data.u8[3] = BMS_TARGET_HV_ON; //BMS_TARGET_AC_CHARGING; //TODO, should we try AC_2 or DC charging?
MEB_503.data.u8[5] = 0x82; // Bordnetz Active
MEB_503.data.u8[6] = 0xE0; // Request emergency shutdown HV system == 0, false
} else if (first_can_msg > 0 && currentMillis > first_can_msg + 2000) { //FAULT STATE, open contactors
MEB_503.data.u8[1] = 0x90;
MEB_503.data.u8[3] = BMS_TARGET_HV_OFF;
MEB_503.data.u8[5] = 0x80; // Bordnetz Inactive
MEB_503.data.u8[6] =
0xE3; // Request emergency shutdown HV system == init (3) (not sure if we dare activate this, this is done with 0xE1)
}
MEB_503.data.u8[1] = ((MEB_503.data.u8[1] & 0xF0) | counter_100ms);
MEB_503.data.u8[0] = vw_crc_calc(MEB_503.data.u8, MEB_503.DLC, MEB_503.ID);
//Bidirectional charging message
MEB_272.data.u8[1] =
0x00; //0x80; // Bidirectional charging active (Set to 0x00 incase no bidirectional charging wanted)
MEB_272.data.u8[2] = 0x00;
//0x01; // High load bidirectional charging active (Set to 0x00 incase no bidirectional charging wanted)
MEB_272.data.u8[5] = DC_FASTCHARGE_NO_START_REQUEST; //DC_FASTCHARGE_VEHICLE; //DC charging
//Klemmen status
MEB_3C0.data.u8[2] = 0x00; //0x02; //bit to signal that KL_15 is ON // Always 0 in start4.log
MEB_3C0.data.u8[1] = ((MEB_3C0.data.u8[1] & 0xF0) | counter_100ms);
MEB_3C0.data.u8[0] = vw_crc_calc(MEB_3C0.data.u8, MEB_3C0.DLC, MEB_3C0.ID);
MEB_3BE.data.u8[1] = ((MEB_3BE.data.u8[1] & 0xF0) | counter_100ms);
MEB_3BE.data.u8[0] = vw_crc_calc(MEB_3BE.data.u8, MEB_3BE.DLC, MEB_3BE.ID);
MEB_14C.data.u8[1] = ((MEB_14C.data.u8[1] & 0xF0) | counter_100ms);
MEB_14C.data.u8[0] = vw_crc_calc(MEB_14C.data.u8, MEB_14C.DLC, MEB_14C.ID);
counter_100ms = (counter_100ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_503, can_config.battery);
transmit_can_frame(&MEB_272, can_config.battery);
transmit_can_frame(&MEB_3C0, can_config.battery);
transmit_can_frame(&MEB_3BE, can_config.battery);
transmit_can_frame(&MEB_14C, can_config.battery);
}
//Send 200ms message
if (currentMillis - previousMillis200ms >= INTERVAL_200_MS) {
previousMillis200ms = currentMillis;
//TODO: 153 does not seem to need CRC even though it has it? Empty in some logs and still works
//TODO: MEB_1B0000B9 & MEB_1B000010 & MEB_1B000046 has CAN sleep commands, static OK?
transmit_can_frame(&MEB_5E1, can_config.battery);
transmit_can_frame(&MEB_153, can_config.battery);
transmit_can_frame(&MEB_1B0000B9, can_config.battery);
transmit_can_frame(&MEB_1B000010, can_config.battery);
transmit_can_frame(&MEB_1B000046, can_config.battery);
switch (poll_pid) {
case PID_SOC:
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_SOC >> 8); // High byte
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_SOC; // Low byte
poll_pid = PID_VOLTAGE;
break;
case PID_VOLTAGE:
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_VOLTAGE >> 8);
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_VOLTAGE;
poll_pid = PID_CURRENT;
break;
case PID_CURRENT:
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_CURRENT >> 8);
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CURRENT;
poll_pid = PID_MAX_TEMP;
break;
case PID_MAX_TEMP:
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_MAX_TEMP >> 8);
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_MAX_TEMP;
poll_pid = PID_MIN_TEMP;
break;
case PID_MIN_TEMP:
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_MIN_TEMP >> 8);
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_MIN_TEMP;
poll_pid = PID_MAX_CHARGE_VOLTAGE;
break;
case PID_MAX_CHARGE_VOLTAGE:
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_MAX_CHARGE_VOLTAGE >> 8);
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_MAX_CHARGE_VOLTAGE;
poll_pid = PID_MIN_DISCHARGE_VOLTAGE;
break;
case PID_MIN_DISCHARGE_VOLTAGE:
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_MIN_DISCHARGE_VOLTAGE >> 8);
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_MIN_DISCHARGE_VOLTAGE;
poll_pid = PID_ALLOWED_CHARGE_POWER;
break;
case PID_ALLOWED_CHARGE_POWER:
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_ALLOWED_CHARGE_POWER >> 8);
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_ALLOWED_CHARGE_POWER;
poll_pid = PID_ALLOWED_DISCHARGE_POWER;
break;
case PID_ALLOWED_DISCHARGE_POWER:
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_ALLOWED_DISCHARGE_POWER >> 8);
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_ALLOWED_DISCHARGE_POWER;
poll_pid = PID_CELLVOLTAGE_CELL_1; // Start polling cell voltages
break;
// Cell Voltage Cases
case PID_CELLVOLTAGE_CELL_1:
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_CELLVOLTAGE_CELL_1 >> 8);
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_1;
poll_pid = PID_CELLVOLTAGE_CELL_2;
break;
case PID_CELLVOLTAGE_CELL_2:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_2;
poll_pid = PID_CELLVOLTAGE_CELL_3;
break;
case PID_CELLVOLTAGE_CELL_3:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_3;
poll_pid = PID_CELLVOLTAGE_CELL_4;
break;
case PID_CELLVOLTAGE_CELL_4:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_4;
poll_pid = PID_CELLVOLTAGE_CELL_5;
break;
case PID_CELLVOLTAGE_CELL_5:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_5;
poll_pid = PID_CELLVOLTAGE_CELL_6;
break;
case PID_CELLVOLTAGE_CELL_6:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_6;
poll_pid = PID_CELLVOLTAGE_CELL_7;
break;
case PID_CELLVOLTAGE_CELL_7:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_7;
poll_pid = PID_CELLVOLTAGE_CELL_8;
break;
case PID_CELLVOLTAGE_CELL_8:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_8;
poll_pid = PID_CELLVOLTAGE_CELL_9;
break;
case PID_CELLVOLTAGE_CELL_9:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_9;
poll_pid = PID_CELLVOLTAGE_CELL_10;
break;
case PID_CELLVOLTAGE_CELL_10:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_10;
poll_pid = PID_CELLVOLTAGE_CELL_11;
break;
case PID_CELLVOLTAGE_CELL_11:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_11;
poll_pid = PID_CELLVOLTAGE_CELL_12;
break;
case PID_CELLVOLTAGE_CELL_12:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_12;
poll_pid = PID_CELLVOLTAGE_CELL_13;
break;
case PID_CELLVOLTAGE_CELL_13:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_13;
poll_pid = PID_CELLVOLTAGE_CELL_14;
break;
case PID_CELLVOLTAGE_CELL_14:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_14;
poll_pid = PID_CELLVOLTAGE_CELL_15;
break;
case PID_CELLVOLTAGE_CELL_15:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_15;
poll_pid = PID_CELLVOLTAGE_CELL_16;
break;
case PID_CELLVOLTAGE_CELL_16:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_16;
poll_pid = PID_CELLVOLTAGE_CELL_17;
break;
case PID_CELLVOLTAGE_CELL_17:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_17;
poll_pid = PID_CELLVOLTAGE_CELL_18;
break;
case PID_CELLVOLTAGE_CELL_18:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_18;
poll_pid = PID_CELLVOLTAGE_CELL_19;
break;
case PID_CELLVOLTAGE_CELL_19:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_19;
poll_pid = PID_CELLVOLTAGE_CELL_20;
break;
case PID_CELLVOLTAGE_CELL_20:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_20;
poll_pid = PID_CELLVOLTAGE_CELL_21;
break;
case PID_CELLVOLTAGE_CELL_21:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_21;
poll_pid = PID_CELLVOLTAGE_CELL_22;
break;
case PID_CELLVOLTAGE_CELL_22:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_22;
poll_pid = PID_CELLVOLTAGE_CELL_23;
break;
case PID_CELLVOLTAGE_CELL_23:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_23;
poll_pid = PID_CELLVOLTAGE_CELL_24;
break;
case PID_CELLVOLTAGE_CELL_24:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_24;
poll_pid = PID_CELLVOLTAGE_CELL_25;
break;
case PID_CELLVOLTAGE_CELL_25:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_25;
poll_pid = PID_CELLVOLTAGE_CELL_26;
break;
case PID_CELLVOLTAGE_CELL_26:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_26;
poll_pid = PID_CELLVOLTAGE_CELL_27;
break;
case PID_CELLVOLTAGE_CELL_27:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_27;
poll_pid = PID_CELLVOLTAGE_CELL_28;
break;
case PID_CELLVOLTAGE_CELL_28:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_28;
poll_pid = PID_CELLVOLTAGE_CELL_29;
break;
case PID_CELLVOLTAGE_CELL_29:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_29;
poll_pid = PID_CELLVOLTAGE_CELL_30;
break;
case PID_CELLVOLTAGE_CELL_30:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_30;
poll_pid = PID_CELLVOLTAGE_CELL_31;
break;
case PID_CELLVOLTAGE_CELL_31:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_31;
poll_pid = PID_CELLVOLTAGE_CELL_32;
break;
case PID_CELLVOLTAGE_CELL_32:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_32;
poll_pid = PID_CELLVOLTAGE_CELL_33;
break;
case PID_CELLVOLTAGE_CELL_33:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_33;
poll_pid = PID_CELLVOLTAGE_CELL_34;
break;
case PID_CELLVOLTAGE_CELL_34:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_34;
poll_pid = PID_CELLVOLTAGE_CELL_35;
break;
case PID_CELLVOLTAGE_CELL_35:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_35;
poll_pid = PID_CELLVOLTAGE_CELL_36;
break;
case PID_CELLVOLTAGE_CELL_36:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_36;
poll_pid = PID_CELLVOLTAGE_CELL_37;
break;
case PID_CELLVOLTAGE_CELL_37:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_37;
poll_pid = PID_CELLVOLTAGE_CELL_38;
break;
case PID_CELLVOLTAGE_CELL_38:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_38;
poll_pid = PID_CELLVOLTAGE_CELL_39;
break;
case PID_CELLVOLTAGE_CELL_39:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_39;
poll_pid = PID_CELLVOLTAGE_CELL_40;
break;
case PID_CELLVOLTAGE_CELL_40:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_40;
poll_pid = PID_CELLVOLTAGE_CELL_41;
break;
case PID_CELLVOLTAGE_CELL_41:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_41;
poll_pid = PID_CELLVOLTAGE_CELL_42;
break;
case PID_CELLVOLTAGE_CELL_42:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_42;
poll_pid = PID_CELLVOLTAGE_CELL_43;
break;
case PID_CELLVOLTAGE_CELL_43:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_43;
poll_pid = PID_CELLVOLTAGE_CELL_44;
break;
case PID_CELLVOLTAGE_CELL_44:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_44;
poll_pid = PID_CELLVOLTAGE_CELL_45;
break;
case PID_CELLVOLTAGE_CELL_45:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_45;
poll_pid = PID_CELLVOLTAGE_CELL_46;
break;
case PID_CELLVOLTAGE_CELL_46:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_46;
poll_pid = PID_CELLVOLTAGE_CELL_47;
break;
case PID_CELLVOLTAGE_CELL_47:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_47;
poll_pid = PID_CELLVOLTAGE_CELL_48;
break;
case PID_CELLVOLTAGE_CELL_48:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_48;
poll_pid = PID_CELLVOLTAGE_CELL_49;
break;
case PID_CELLVOLTAGE_CELL_49:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_49;
poll_pid = PID_CELLVOLTAGE_CELL_50;
break;
case PID_CELLVOLTAGE_CELL_50:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_50;
poll_pid = PID_CELLVOLTAGE_CELL_51;
break;
case PID_CELLVOLTAGE_CELL_51:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_51;
poll_pid = PID_CELLVOLTAGE_CELL_52;
break;
case PID_CELLVOLTAGE_CELL_52:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_52;
poll_pid = PID_CELLVOLTAGE_CELL_53;
break;
case PID_CELLVOLTAGE_CELL_53:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_53;
poll_pid = PID_CELLVOLTAGE_CELL_54;
break;
case PID_CELLVOLTAGE_CELL_54:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_54;
poll_pid = PID_CELLVOLTAGE_CELL_55;
break;
case PID_CELLVOLTAGE_CELL_55:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_55;
poll_pid = PID_CELLVOLTAGE_CELL_56;
break;
case PID_CELLVOLTAGE_CELL_56:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_56;
poll_pid = PID_CELLVOLTAGE_CELL_57;
break;
case PID_CELLVOLTAGE_CELL_57:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_57;
poll_pid = PID_CELLVOLTAGE_CELL_58;
break;
case PID_CELLVOLTAGE_CELL_58:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_58;
poll_pid = PID_CELLVOLTAGE_CELL_59;
break;
case PID_CELLVOLTAGE_CELL_59:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_59;
poll_pid = PID_CELLVOLTAGE_CELL_60;
break;
case PID_CELLVOLTAGE_CELL_60:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_60;
poll_pid = PID_CELLVOLTAGE_CELL_61;
break;
case PID_CELLVOLTAGE_CELL_61:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_61;
poll_pid = PID_CELLVOLTAGE_CELL_62;
break;
case PID_CELLVOLTAGE_CELL_62:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_62;
poll_pid = PID_CELLVOLTAGE_CELL_63;
break;
case PID_CELLVOLTAGE_CELL_63:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_63;
poll_pid = PID_CELLVOLTAGE_CELL_64;
break;
case PID_CELLVOLTAGE_CELL_64:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_64;
poll_pid = PID_CELLVOLTAGE_CELL_65;
break;
case PID_CELLVOLTAGE_CELL_65:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_65;
poll_pid = PID_CELLVOLTAGE_CELL_66;
break;
case PID_CELLVOLTAGE_CELL_66:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_66;
poll_pid = PID_CELLVOLTAGE_CELL_67;
break;
case PID_CELLVOLTAGE_CELL_67:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_67;
poll_pid = PID_CELLVOLTAGE_CELL_68;
break;
case PID_CELLVOLTAGE_CELL_68:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_68;
poll_pid = PID_CELLVOLTAGE_CELL_69;
break;
case PID_CELLVOLTAGE_CELL_69:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_69;
poll_pid = PID_CELLVOLTAGE_CELL_70;
break;
case PID_CELLVOLTAGE_CELL_70:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_70;
poll_pid = PID_CELLVOLTAGE_CELL_71;
break;
case PID_CELLVOLTAGE_CELL_71:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_71;
poll_pid = PID_CELLVOLTAGE_CELL_72;
break;
case PID_CELLVOLTAGE_CELL_72:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_72;
poll_pid = PID_CELLVOLTAGE_CELL_73;
break;
case PID_CELLVOLTAGE_CELL_73:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_73;
poll_pid = PID_CELLVOLTAGE_CELL_74;
break;
case PID_CELLVOLTAGE_CELL_74:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_74;
poll_pid = PID_CELLVOLTAGE_CELL_75;
break;
case PID_CELLVOLTAGE_CELL_75:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_75;
poll_pid = PID_CELLVOLTAGE_CELL_76;
break;
case PID_CELLVOLTAGE_CELL_76:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_76;
poll_pid = PID_CELLVOLTAGE_CELL_77;
break;
case PID_CELLVOLTAGE_CELL_77:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_77;
poll_pid = PID_CELLVOLTAGE_CELL_78;
break;
case PID_CELLVOLTAGE_CELL_78:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_78;
poll_pid = PID_CELLVOLTAGE_CELL_79;
break;
case PID_CELLVOLTAGE_CELL_79:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_79;
poll_pid = PID_CELLVOLTAGE_CELL_80;
break;
case PID_CELLVOLTAGE_CELL_80:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_80;
poll_pid = PID_CELLVOLTAGE_CELL_81;
break;
case PID_CELLVOLTAGE_CELL_81:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_81;
poll_pid = PID_CELLVOLTAGE_CELL_82;
break;
case PID_CELLVOLTAGE_CELL_82:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_82;
poll_pid = PID_CELLVOLTAGE_CELL_83;
break;
case PID_CELLVOLTAGE_CELL_83:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_83;
poll_pid = PID_CELLVOLTAGE_CELL_84;
break;
case PID_CELLVOLTAGE_CELL_84:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_84;
if (datalayer.battery.info.number_of_cells > 84) {
if (nof_cells_determined) {
poll_pid = PID_CELLVOLTAGE_CELL_85;
} else {
poll_pid = PID_CELLVOLTAGE_CELL_97;
}
} else {
poll_pid = PID_SOC;
}
break;
case PID_CELLVOLTAGE_CELL_85:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_85;
poll_pid = PID_CELLVOLTAGE_CELL_86;
break;
case PID_CELLVOLTAGE_CELL_86:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_86;
poll_pid = PID_CELLVOLTAGE_CELL_87;
break;
case PID_CELLVOLTAGE_CELL_87:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_87;
poll_pid = PID_CELLVOLTAGE_CELL_88;
break;
case PID_CELLVOLTAGE_CELL_88:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_88;
poll_pid = PID_CELLVOLTAGE_CELL_89;
break;
case PID_CELLVOLTAGE_CELL_89:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_89;
poll_pid = PID_CELLVOLTAGE_CELL_90;
break;
case PID_CELLVOLTAGE_CELL_90:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_90;
poll_pid = PID_CELLVOLTAGE_CELL_91;
break;
case PID_CELLVOLTAGE_CELL_91:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_91;
poll_pid = PID_CELLVOLTAGE_CELL_92;
break;
case PID_CELLVOLTAGE_CELL_92:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_92;
poll_pid = PID_CELLVOLTAGE_CELL_93;
break;
case PID_CELLVOLTAGE_CELL_93:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_93;
poll_pid = PID_CELLVOLTAGE_CELL_94;
break;
case PID_CELLVOLTAGE_CELL_94:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_94;
poll_pid = PID_CELLVOLTAGE_CELL_95;
break;
case PID_CELLVOLTAGE_CELL_95:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_95;
poll_pid = PID_CELLVOLTAGE_CELL_96;
break;
case PID_CELLVOLTAGE_CELL_96:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_96;
if (datalayer.battery.info.number_of_cells > 96)
poll_pid = PID_CELLVOLTAGE_CELL_97;
else
poll_pid = PID_SOC;
break;
case PID_CELLVOLTAGE_CELL_97:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_97;
poll_pid = PID_CELLVOLTAGE_CELL_98;
break;
case PID_CELLVOLTAGE_CELL_98:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_98;
poll_pid = PID_CELLVOLTAGE_CELL_99;
break;
case PID_CELLVOLTAGE_CELL_99:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_99;
poll_pid = PID_CELLVOLTAGE_CELL_100;
break;
case PID_CELLVOLTAGE_CELL_100:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_100;
poll_pid = PID_CELLVOLTAGE_CELL_101;
break;
case PID_CELLVOLTAGE_CELL_101:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_101;
poll_pid = PID_CELLVOLTAGE_CELL_102;
break;
case PID_CELLVOLTAGE_CELL_102:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_102;
poll_pid = PID_CELLVOLTAGE_CELL_103;
break;
case PID_CELLVOLTAGE_CELL_103:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_103;
poll_pid = PID_CELLVOLTAGE_CELL_104;
break;
case PID_CELLVOLTAGE_CELL_104:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_104;
poll_pid = PID_CELLVOLTAGE_CELL_105;
break;
case PID_CELLVOLTAGE_CELL_105:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_105;
poll_pid = PID_CELLVOLTAGE_CELL_106;
break;
case PID_CELLVOLTAGE_CELL_106:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_106;
poll_pid = PID_CELLVOLTAGE_CELL_107;
break;
case PID_CELLVOLTAGE_CELL_107:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_107;
poll_pid = PID_CELLVOLTAGE_CELL_108;
break;
case PID_CELLVOLTAGE_CELL_108:
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_CELLVOLTAGE_CELL_108;
poll_pid = PID_SOC;
break;
default:
poll_pid = PID_SOC;
break;
}
if (first_can_msg > 0 && currentMillis > first_can_msg + 2000) {
transmit_can_frame(&MEB_POLLING_FRAME, can_config.battery);
}
}
// Send 500ms CAN Message
if (currentMillis - previousMillis500ms >= INTERVAL_500_MS) {
previousMillis500ms = currentMillis;
transmit_can_frame(&MEB_16A954B4, can_config.battery); //eTM, Cooling valves and pumps for BMS
transmit_can_frame(&MEB_569, can_config.battery); // Battery heating requests
transmit_can_frame(&MEB_1A55552B, can_config.battery); //Climate, heatpump and priorities
transmit_can_frame(&MEB_1A555548, can_config.battery); //ORU, OTA update message for reserving battery
transmit_can_frame(&MEB_16A954FB, can_config.battery); //Climate, request to BMS for starting preconditioning
}
//Send 1s CANFD message
if (currentMillis - previousMillis1s >= INTERVAL_1_S) {
previousMillis1s = currentMillis;
MEB_641.data.u8[1] = ((MEB_641.data.u8[1] & 0xF0) | counter_1000ms);
MEB_641.data.u8[0] = vw_crc_calc(MEB_641.data.u8, MEB_641.DLC, MEB_641.ID);
MEB_1A5555A6.data.u8[2] = 0x7F; //Outside temperature, factor 0.5, offset -50
MEB_6B2.data.u8[0] = //driving cycle counter, 0-254 wrap around. 255 = invalid value
//MEB_6B2.data.u8[1-2-3b0-4] // Odometer, km (20 bits long)
MEB_6B2.data.u8[3] = (uint8_t)((TIME_YEAR - 2000) << 4) | MEB_6B2.data.u8[3];
MEB_6B2.data.u8[4] = (uint8_t)((TIME_DAY & 0x01) << 7 | TIME_MONTH << 3 | (TIME_YEAR - 2000) >> 4);
MEB_6B2.data.u8[5] = (uint8_t)((TIME_HOUR & 0x0F) << 4 | TIME_DAY >> 1);
MEB_6B2.data.u8[6] = (uint8_t)((seconds & 0x01) << 7 | TIME_MINUTE << 1 | TIME_HOUR >> 4);
MEB_6B2.data.u8[7] = (uint8_t)((seconds & 0x3E) >> 1);
seconds = (seconds + 1) % 60;
counter_1000ms = (counter_1000ms + 1) % 16; //Goes from 0-1-2-3...15-0-1-2-3..
transmit_can_frame(&MEB_6B2, can_config.battery); // Diagnostics - Needed for contactor closing
transmit_can_frame(&MEB_641, can_config.battery); // Motor - OBD
transmit_can_frame(&MEB_5F5, can_config.battery); // Loading profile
transmit_can_frame(&MEB_585, can_config.battery); // Systeminfo
transmit_can_frame(&MEB_1A5555A6, can_config.battery); // Temperature QBit
}
}
void setup_battery(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Volkswagen Group MEB platform via CAN-FD", 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 108; //Startup in 108S mode. We figure out the actual count later.
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_108S_DV; //Defined later to correct pack size
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_84S_DV; //Defined later to correct pack size
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
}
#endif