mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 10:49:42 +02:00
Merge pull request #1126 BMW I3 battery implemented as a class with double battery support
This commit is contained in:
commit
eb293b38d4
3 changed files with 414 additions and 750 deletions
|
@ -22,9 +22,15 @@ void setup_battery() {
|
|||
|
||||
#ifdef DOUBLE_BATTERY
|
||||
if (battery2 == nullptr) {
|
||||
#ifdef BMW_I3_BATTERY
|
||||
battery2 =
|
||||
new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer.system.status.battery2_allows_contactor_closing,
|
||||
can_config.battery_double, WUP_PIN2);
|
||||
#else
|
||||
battery2 =
|
||||
new SELECTED_BATTERY_CLASS(&datalayer.battery2, &datalayer.system.status.battery2_allows_contactor_closing,
|
||||
nullptr, can_config.battery_double);
|
||||
#endif
|
||||
}
|
||||
battery2->setup();
|
||||
#endif
|
||||
|
|
|
@ -1,29 +1,12 @@
|
|||
#include "../include.h"
|
||||
#ifdef BMW_I3_BATTERY
|
||||
#include "../communication/can/comm_can.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../devboard/utils/events.h"
|
||||
#include "BMW-I3-BATTERY.h"
|
||||
|
||||
/* Do not change code below unless you are sure what you are doing */
|
||||
static unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send
|
||||
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||
static unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was send
|
||||
static unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send
|
||||
static unsigned long previousMillis640 = 0; // will store last time a 600ms CAN Message was send
|
||||
static unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was send
|
||||
static unsigned long previousMillis5000 = 0; // will store last time a 5000ms CAN Message was send
|
||||
static unsigned long previousMillis10000 = 0; // will store last time a 10000ms CAN Message was send
|
||||
|
||||
#define ALIVE_MAX_VALUE 14 // BMW CAN messages contain alive counter, goes from 0...14
|
||||
|
||||
enum BatterySize { BATTERY_60AH, BATTERY_94AH, BATTERY_120AH };
|
||||
static BatterySize detectedBattery = BATTERY_60AH;
|
||||
static BatterySize detectedBattery2 = BATTERY_60AH; // For double battery setups
|
||||
|
||||
enum CmdState { SOH, CELL_VOLTAGE_MINMAX, SOC, CELL_VOLTAGE_CELLNO, CELL_VOLTAGE_CELLNO_LAST };
|
||||
|
||||
static CmdState cmdState = SOC;
|
||||
|
||||
const unsigned char crc8_table[256] =
|
||||
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
|
||||
|
@ -43,312 +26,6 @@ const unsigned char crc8_table[256] =
|
|||
0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09, 0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C, 0x97, 0x8A, 0xAD, 0xB0,
|
||||
0xE3, 0xFE, 0xD9, 0xC4};
|
||||
|
||||
/* CAN messages from PT-CAN2 not needed to operate the battery
|
||||
0AA 105 13D 0BB 0AD 0A5 150 100 1A1 10E 153 197 429 1AA 12F 59A 2E3 2BE 211 2b3 3FD 2E8 2B7 108 29D 29C 29B 2C0 330
|
||||
3E9 32F 19E 326 55E 515 509 50A 51A 2F5 3A4 432 3C9
|
||||
*/
|
||||
|
||||
CAN_frame BMW_10B = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 3,
|
||||
.ID = 0x10B,
|
||||
.data = {0xCD, 0x00, 0xFC}}; // Contactor closing command
|
||||
CAN_frame BMW_12F = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x12F,
|
||||
.data = {0xE6, 0x24, 0x86, 0x1A, 0xF1, 0x31, 0x30, 0x00}}; //0x12F Wakeup VCU
|
||||
CAN_frame BMW_13E = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x13E,
|
||||
.data = {0xFF, 0x31, 0xFA, 0xFA, 0xFA, 0xFA, 0x0C, 0x00}};
|
||||
CAN_frame BMW_192 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x192,
|
||||
.data = {0xFF, 0xFF, 0xA3, 0x8F, 0x93, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_19B = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x19B,
|
||||
.data = {0x20, 0x40, 0x40, 0x55, 0xFD, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_1D0 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x1D0,
|
||||
.data = {0x4D, 0xF0, 0xAE, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_2CA = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x2CA, .data = {0x57, 0x57}};
|
||||
CAN_frame BMW_2E2 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x2E2,
|
||||
.data = {0x4F, 0xDB, 0x7F, 0xB9, 0x07, 0x51, 0xff, 0x00}};
|
||||
CAN_frame BMW_30B = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x30B,
|
||||
.data = {0xe1, 0xf0, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xff}};
|
||||
CAN_frame BMW_328 = {.FD = false, .ext_ID = false, .DLC = 6, .ID = 0x328, .data = {0xB0, 0xE4, 0x87, 0x0E, 0x30, 0x22}};
|
||||
CAN_frame BMW_37B = {.FD = false, .ext_ID = false, .DLC = 6, .ID = 0x37B, .data = {0x40, 0x00, 0x00, 0xFF, 0xFF, 0x00}};
|
||||
CAN_frame BMW_380 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 7,
|
||||
.ID = 0x380,
|
||||
.data = {0x56, 0x5A, 0x37, 0x39, 0x34, 0x34, 0x34}};
|
||||
CAN_frame BMW_3A0 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3A0,
|
||||
.data = {0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC}};
|
||||
CAN_frame BMW_3A7 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 7,
|
||||
.ID = 0x3A7,
|
||||
.data = {0x05, 0xF5, 0x0A, 0x00, 0x4F, 0x11, 0xF0}};
|
||||
CAN_frame BMW_3C5 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3C5,
|
||||
.data = {0x30, 0x05, 0x47, 0x70, 0x2c, 0xce, 0xc3, 0x34}};
|
||||
CAN_frame BMW_3CA = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3CA,
|
||||
.data = {0x87, 0x80, 0x30, 0x0C, 0x0C, 0x81, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_3D0 = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x3D0, .data = {0xFD, 0xFF}};
|
||||
CAN_frame BMW_3E4 = {.FD = false, .ext_ID = false, .DLC = 6, .ID = 0x3E4, .data = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_3E5 = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x3E5, .data = {0xFC, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_3E8 = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x3E8, .data = {0xF0, 0xFF}}; //1000ms OBD reset
|
||||
CAN_frame BMW_3EC = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3EC,
|
||||
.data = {0xF5, 0x10, 0x00, 0x00, 0x80, 0x25, 0x0F, 0xFC}};
|
||||
CAN_frame BMW_3F9 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3F9,
|
||||
.data = {0xA7, 0x2A, 0x00, 0xE2, 0xA6, 0x30, 0xC3, 0xFF}};
|
||||
CAN_frame BMW_3FB = {.FD = false, .ext_ID = false, .DLC = 6, .ID = 0x3FB, .data = {0xFF, 0xFF, 0xFF, 0xFF, 0x5F, 0x00}};
|
||||
CAN_frame BMW_3FC = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x3FC, .data = {0xC0, 0xF9, 0x0F}};
|
||||
CAN_frame BMW_418 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x418,
|
||||
.data = {0xFF, 0x7C, 0xFF, 0x00, 0xC0, 0x3F, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_41D = {.FD = false, .ext_ID = false, .DLC = 4, .ID = 0x41D, .data = {0xFF, 0xF7, 0x7F, 0xFF}};
|
||||
CAN_frame BMW_433 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 4,
|
||||
.ID = 0x433,
|
||||
.data = {0xFF, 0x00, 0x0F, 0xFF}}; // HV specification
|
||||
CAN_frame BMW_512 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x512,
|
||||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12}}; // 0x512 Network management
|
||||
CAN_frame BMW_592_0 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x592,
|
||||
.data = {0x86, 0x10, 0x07, 0x21, 0x6e, 0x35, 0x5e, 0x86}};
|
||||
CAN_frame BMW_592_1 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x592,
|
||||
.data = {0x86, 0x21, 0xb4, 0xdd, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame BMW_5F8 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x5F8,
|
||||
.data = {0x64, 0x01, 0x00, 0x0B, 0x92, 0x03, 0x00, 0x05}};
|
||||
CAN_frame BMW_6F1_CELL = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0xDD, 0xBF}};
|
||||
CAN_frame BMW_6F1_SOH = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0x63, 0x35}};
|
||||
CAN_frame BMW_6F1_SOC = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0xDD, 0xBC}};
|
||||
CAN_frame BMW_6F1_CELL_VOLTAGE_AVG = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 5,
|
||||
.ID = 0x6F1,
|
||||
.data = {0x07, 0x03, 0x22, 0xDF, 0xA0}};
|
||||
CAN_frame BMW_6F1_CONTINUE = {.FD = false, .ext_ID = false, .DLC = 4, .ID = 0x6F1, .data = {0x07, 0x30, 0x00, 0x02}};
|
||||
CAN_frame BMW_6F4_CELL_VOLTAGE_CELLNO = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 7,
|
||||
.ID = 0x6F4,
|
||||
.data = {0x07, 0x05, 0x31, 0x01, 0xAD, 0x6E, 0x01}};
|
||||
CAN_frame BMW_6F4_CELL_CONTINUE = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 6,
|
||||
.ID = 0x6F4,
|
||||
.data = {0x07, 0x04, 0x31, 0x03, 0xAD, 0x6E}};
|
||||
|
||||
//The above CAN messages need to be sent towards the battery to keep it alive
|
||||
|
||||
static uint8_t startup_counter_contactor = 0;
|
||||
static uint8_t alive_counter_20ms = 0;
|
||||
static uint8_t alive_counter_100ms = 0;
|
||||
static uint8_t alive_counter_200ms = 0;
|
||||
static uint8_t alive_counter_500ms = 0;
|
||||
static uint8_t alive_counter_1000ms = 0;
|
||||
static uint8_t alive_counter_5000ms = 0;
|
||||
static uint8_t BMW_1D0_counter = 0;
|
||||
static uint8_t BMW_13E_counter = 0;
|
||||
static uint8_t BMW_380_counter = 0;
|
||||
static uint32_t BMW_328_seconds = 243785948; // Initialized to make the battery think vehicle was made 7.7years ago
|
||||
static uint16_t BMW_328_days =
|
||||
9244; //Time since 1.1.2000. Hacky implementation to make it think current date is 23rd April 2025
|
||||
static uint32_t BMS_328_seconds_to_day = 0; //Counter to keep track of days uptime
|
||||
|
||||
static bool battery_awake = false;
|
||||
static bool battery2_awake = false;
|
||||
static bool battery_info_available = false;
|
||||
static bool battery2_info_available = false;
|
||||
static bool skipCRCCheck = false;
|
||||
static bool CRCCheckPassedPreviously = false;
|
||||
static bool skipCRCCheck_battery2 = false;
|
||||
static bool CRCCheckPassedPreviously_battery2 = false;
|
||||
|
||||
static uint16_t cellvoltage_temp_mV = 0;
|
||||
static uint32_t battery_serial_number = 0;
|
||||
static uint32_t battery_available_power_shortterm_charge = 0;
|
||||
static uint32_t battery_available_power_shortterm_discharge = 0;
|
||||
static uint32_t battery_available_power_longterm_charge = 0;
|
||||
static uint32_t battery_available_power_longterm_discharge = 0;
|
||||
static uint32_t battery_BEV_available_power_shortterm_charge = 0;
|
||||
static uint32_t battery_BEV_available_power_shortterm_discharge = 0;
|
||||
static uint32_t battery_BEV_available_power_longterm_charge = 0;
|
||||
static uint32_t battery_BEV_available_power_longterm_discharge = 0;
|
||||
static uint16_t battery_energy_content_maximum_Wh = 0;
|
||||
static uint16_t battery_display_SOC = 0;
|
||||
static uint16_t battery_volts = 0;
|
||||
static uint16_t battery_HVBatt_SOC = 0;
|
||||
static uint16_t battery_DC_link_voltage = 0;
|
||||
static uint16_t battery_max_charge_voltage = 0;
|
||||
static uint16_t battery_min_discharge_voltage = 0;
|
||||
static uint16_t battery_predicted_energy_charge_condition = 0;
|
||||
static uint16_t battery_predicted_energy_charging_target = 0;
|
||||
static uint16_t battery_actual_value_power_heating = 0; //0 - 4094 W
|
||||
static uint16_t battery_prediction_voltage_shortterm_charge = 0;
|
||||
static uint16_t battery_prediction_voltage_shortterm_discharge = 0;
|
||||
static uint16_t battery_prediction_voltage_longterm_charge = 0;
|
||||
static uint16_t battery_prediction_voltage_longterm_discharge = 0;
|
||||
static uint16_t battery_prediction_duration_charging_minutes = 0;
|
||||
static uint16_t battery_target_voltage_in_CV_mode = 0;
|
||||
static uint16_t battery_soc = 0;
|
||||
static uint16_t battery_soc_hvmax = 0;
|
||||
static uint16_t battery_soc_hvmin = 0;
|
||||
static uint16_t battery_capacity_cah = 0;
|
||||
static int16_t battery_temperature_HV = 0;
|
||||
static int16_t battery_temperature_heat_exchanger = 0;
|
||||
static int16_t battery_temperature_max = 0;
|
||||
static int16_t battery_temperature_min = 0;
|
||||
static int16_t battery_max_charge_amperage = 0;
|
||||
static int16_t battery_max_discharge_amperage = 0;
|
||||
static int16_t battery_current = 0;
|
||||
static uint8_t battery_status_error_isolation_external_Bordnetz = 0;
|
||||
static uint8_t battery_status_error_isolation_internal_Bordnetz = 0;
|
||||
static uint8_t battery_request_cooling = 0;
|
||||
static uint8_t battery_status_valve_cooling = 0;
|
||||
static uint8_t battery_status_error_locking = 0;
|
||||
static uint8_t battery_status_precharge_locked = 0;
|
||||
static uint8_t battery_status_disconnecting_switch = 0;
|
||||
static uint8_t battery_status_emergency_mode = 0;
|
||||
static uint8_t battery_request_service = 0;
|
||||
static uint8_t battery_error_emergency_mode = 0;
|
||||
static uint8_t battery_status_error_disconnecting_switch = 0;
|
||||
static uint8_t battery_status_warning_isolation = 0;
|
||||
static uint8_t battery_status_cold_shutoff_valve = 0;
|
||||
static uint8_t battery_request_open_contactors = 0;
|
||||
static uint8_t battery_request_open_contactors_instantly = 0;
|
||||
static uint8_t battery_request_open_contactors_fast = 0;
|
||||
static uint8_t battery_charging_condition_delta = 0;
|
||||
static uint8_t battery_status_service_disconnection_plug = 0;
|
||||
static uint8_t battery_status_measurement_isolation = 0;
|
||||
static uint8_t battery_request_abort_charging = 0;
|
||||
static uint8_t battery_prediction_time_end_of_charging_minutes = 0;
|
||||
static uint8_t battery_request_operating_mode = 0;
|
||||
static uint8_t battery_request_charging_condition_minimum = 0;
|
||||
static uint8_t battery_request_charging_condition_maximum = 0;
|
||||
static uint8_t battery_status_cooling_HV = 0; //1 works, 2 does not start
|
||||
static uint8_t battery_status_diagnostics_HV = 0; // 0 all OK, 1 HV protection function error, 2 diag not yet expired
|
||||
static uint8_t battery_status_diagnosis_powertrain_maximum_multiplexer = 0;
|
||||
static uint8_t battery_status_diagnosis_powertrain_immediate_multiplexer = 0;
|
||||
static uint8_t battery_ID2 = 0;
|
||||
static uint8_t battery_soh = 99;
|
||||
|
||||
static uint16_t cellvoltage2_temp_mV = 0;
|
||||
static uint32_t battery2_serial_number = 0;
|
||||
static uint32_t battery2_available_power_shortterm_charge = 0;
|
||||
static uint32_t battery2_available_power_shortterm_discharge = 0;
|
||||
static uint32_t battery2_available_power_longterm_charge = 0;
|
||||
static uint32_t battery2_available_power_longterm_discharge = 0;
|
||||
static uint32_t battery2_BEV_available_power_shortterm_charge = 0;
|
||||
static uint32_t battery2_BEV_available_power_shortterm_discharge = 0;
|
||||
static uint32_t battery2_BEV_available_power_longterm_charge = 0;
|
||||
static uint32_t battery2_BEV_available_power_longterm_discharge = 0;
|
||||
static uint16_t battery2_energy_content_maximum_Wh = 0;
|
||||
static uint16_t battery2_display_SOC = 0;
|
||||
static uint16_t battery2_volts = 0;
|
||||
static uint16_t battery2_HVBatt_SOC = 0;
|
||||
static uint16_t battery2_DC_link_voltage = 0;
|
||||
static uint16_t battery2_max_charge_voltage = 0;
|
||||
static uint16_t battery2_min_discharge_voltage = 0;
|
||||
static uint16_t battery2_predicted_energy_charge_condition = 0;
|
||||
static uint16_t battery2_predicted_energy_charging_target = 0;
|
||||
static uint16_t battery2_actual_value_power_heating = 0; //0 - 4094 W
|
||||
static uint16_t battery2_prediction_voltage_shortterm_charge = 0;
|
||||
static uint16_t battery2_prediction_voltage_shortterm_discharge = 0;
|
||||
static uint16_t battery2_prediction_voltage_longterm_charge = 0;
|
||||
static uint16_t battery2_prediction_voltage_longterm_discharge = 0;
|
||||
static uint16_t battery2_prediction_duration_charging_minutes = 0;
|
||||
static uint16_t battery2_target_voltage_in_CV_mode = 0;
|
||||
static uint16_t battery2_soc = 0;
|
||||
static uint16_t battery2_soc_hvmax = 0;
|
||||
static uint16_t battery2_soc_hvmin = 0;
|
||||
static uint16_t battery2_capacity_cah = 0;
|
||||
static int16_t battery2_temperature_HV = 0;
|
||||
static int16_t battery2_temperature_heat_exchanger = 0;
|
||||
static int16_t battery2_temperature_max = 0;
|
||||
static int16_t battery2_temperature_min = 0;
|
||||
static int16_t battery2_max_charge_amperage = 0;
|
||||
static int16_t battery2_max_discharge_amperage = 0;
|
||||
static int16_t battery2_current = 0;
|
||||
static uint8_t battery2_status_error_isolation_external_Bordnetz = 0;
|
||||
static uint8_t battery2_status_error_isolation_internal_Bordnetz = 0;
|
||||
static uint8_t battery2_request_cooling = 0;
|
||||
static uint8_t battery2_status_valve_cooling = 0;
|
||||
static uint8_t battery2_status_error_locking = 0;
|
||||
static uint8_t battery2_status_precharge_locked = 0;
|
||||
static uint8_t battery2_status_disconnecting_switch = 0;
|
||||
static uint8_t battery2_status_emergency_mode = 0;
|
||||
static uint8_t battery2_request_service = 0;
|
||||
static uint8_t battery2_error_emergency_mode = 0;
|
||||
static uint8_t battery2_status_error_disconnecting_switch = 0;
|
||||
static uint8_t battery2_status_warning_isolation = 0;
|
||||
static uint8_t battery2_status_cold_shutoff_valve = 0;
|
||||
static uint8_t battery2_request_open_contactors = 0;
|
||||
static uint8_t battery2_request_open_contactors_instantly = 0;
|
||||
static uint8_t battery2_request_open_contactors_fast = 0;
|
||||
static uint8_t battery2_charging_condition_delta = 0;
|
||||
static uint8_t battery2_status_service_disconnection_plug = 0;
|
||||
static uint8_t battery2_status_measurement_isolation = 0;
|
||||
static uint8_t battery2_request_abort_charging = 0;
|
||||
static uint8_t battery2_prediction_time_end_of_charging_minutes = 0;
|
||||
static uint8_t battery2_request_operating_mode = 0;
|
||||
static uint8_t battery2_request_charging_condition_minimum = 0;
|
||||
static uint8_t battery2_request_charging_condition_maximum = 0;
|
||||
static uint8_t battery2_status_cooling_HV = 0; //1 works, 2 does not start
|
||||
static uint8_t battery2_status_diagnostics_HV = 0; // 0 all OK, 1 HV protection function error, 2 diag not yet expired
|
||||
static uint8_t battery2_status_diagnosis_powertrain_maximum_multiplexer = 0;
|
||||
static uint8_t battery2_status_diagnosis_powertrain_immediate_multiplexer = 0;
|
||||
static uint8_t battery2_ID2 = 0;
|
||||
static uint8_t battery2_soh = 99;
|
||||
|
||||
static uint8_t message_data[50];
|
||||
static uint8_t next_data = 0;
|
||||
static uint8_t current_cell_polled = 0;
|
||||
|
||||
static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_value) {
|
||||
uint8_t crc = initial_value;
|
||||
for (uint8_t j = 1; j < length; j++) { //start at 1, since 0 is the CRC
|
||||
|
@ -365,118 +42,54 @@ static uint8_t increment_alive_counter(uint8_t counter) {
|
|||
return counter;
|
||||
}
|
||||
|
||||
void update_values_battery2() { //This function maps all the values fetched via CAN2 to the battery2 datalayer
|
||||
if (!battery2_awake) {
|
||||
return;
|
||||
}
|
||||
|
||||
datalayer.battery2.status.real_soc = (battery2_display_SOC * 50);
|
||||
|
||||
datalayer.battery2.status.voltage_dV = battery2_volts; //Unit V+1 (5000 = 500.0V)
|
||||
|
||||
datalayer.battery2.status.current_dA = battery2_current;
|
||||
|
||||
datalayer.battery2.info.total_capacity_Wh = battery2_energy_content_maximum_Wh;
|
||||
|
||||
datalayer.battery2.status.remaining_capacity_Wh = battery2_predicted_energy_charge_condition;
|
||||
|
||||
datalayer.battery2.status.soh_pptt = battery2_soh * 100;
|
||||
|
||||
datalayer.battery2.status.max_discharge_power_W = battery2_BEV_available_power_longterm_discharge;
|
||||
|
||||
datalayer.battery2.status.max_charge_power_W = battery2_BEV_available_power_longterm_charge;
|
||||
|
||||
datalayer.battery2.status.temperature_min_dC = battery2_temperature_min * 10; // Add a decimal
|
||||
|
||||
datalayer.battery2.status.temperature_max_dC = battery2_temperature_max * 10; // Add a decimal
|
||||
|
||||
if (battery2_info_available) {
|
||||
// Start checking safeties. First up, cellvoltages!
|
||||
if (detectedBattery2 == BATTERY_60AH) {
|
||||
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
|
||||
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
|
||||
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_60AH;
|
||||
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_60AH;
|
||||
} else if (detectedBattery2 == BATTERY_94AH) {
|
||||
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH;
|
||||
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_94AH;
|
||||
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_94AH;
|
||||
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_94AH;
|
||||
} else { // BATTERY_120AH
|
||||
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH;
|
||||
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_120AH;
|
||||
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_120AH;
|
||||
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_120AH;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform other safety checks
|
||||
if (battery2_status_error_locking == 2) { // HVIL seated?
|
||||
set_event(EVENT_HVIL_FAILURE, 2);
|
||||
} else {
|
||||
clear_event(EVENT_HVIL_FAILURE);
|
||||
}
|
||||
if (battery2_status_error_disconnecting_switch > 0) { // Check if contactors are sticking / welded
|
||||
set_event(EVENT_CONTACTOR_WELDED, 0);
|
||||
} else {
|
||||
clear_event(EVENT_CONTACTOR_WELDED);
|
||||
}
|
||||
}
|
||||
|
||||
void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
|
||||
void BmwI3Battery::update_values() { //This function maps all the values fetched via CAN to the battery datalayer
|
||||
if (datalayer.system.settings.equipment_stop_active == true) {
|
||||
digitalWrite(WUP_PIN1, LOW); // Turn off WUP_PIN1
|
||||
#if defined(WUP_PIN2) && defined(DOUBLE_BATTERY)
|
||||
digitalWrite(WUP_PIN2, LOW); // Turn off WUP_PIN2
|
||||
#endif // defined(WUP_PIN2) && defined (DOUBLE_BATTERY)
|
||||
digitalWrite(wakeup_pin, LOW); // Turn off wakeup pin
|
||||
} else {
|
||||
digitalWrite(WUP_PIN1, HIGH); // Wake up the battery
|
||||
#if defined(WUP_PIN2) && defined(DOUBLE_BATTERY)
|
||||
digitalWrite(WUP_PIN2, HIGH); // Wake up the battery2
|
||||
#endif // defined(WUP_PIN2) && defined (DOUBLE_BATTERY)
|
||||
digitalWrite(wakeup_pin, HIGH); // Wake up the battery
|
||||
}
|
||||
|
||||
if (!battery_awake) {
|
||||
return;
|
||||
}
|
||||
|
||||
datalayer.battery.status.real_soc = (battery_display_SOC * 50);
|
||||
datalayer_battery->status.real_soc = (battery_display_SOC * 50);
|
||||
|
||||
datalayer.battery.status.voltage_dV = battery_volts; //Unit V+1 (5000 = 500.0V)
|
||||
datalayer_battery->status.voltage_dV = battery_volts; //Unit V+1 (5000 = 500.0V)
|
||||
|
||||
datalayer.battery.status.current_dA = battery_current;
|
||||
datalayer_battery->status.current_dA = battery_current;
|
||||
|
||||
datalayer.battery.info.total_capacity_Wh = battery_energy_content_maximum_Wh;
|
||||
datalayer_battery->info.total_capacity_Wh = battery_energy_content_maximum_Wh;
|
||||
|
||||
datalayer.battery.status.remaining_capacity_Wh = battery_predicted_energy_charge_condition;
|
||||
datalayer_battery->status.remaining_capacity_Wh = battery_predicted_energy_charge_condition;
|
||||
|
||||
datalayer.battery.status.soh_pptt = battery_soh * 100;
|
||||
datalayer_battery->status.soh_pptt = battery_soh * 100;
|
||||
|
||||
datalayer.battery.status.max_discharge_power_W = battery_BEV_available_power_longterm_discharge;
|
||||
datalayer_battery->status.max_discharge_power_W = battery_BEV_available_power_longterm_discharge;
|
||||
|
||||
datalayer.battery.status.max_charge_power_W = battery_BEV_available_power_longterm_charge;
|
||||
datalayer_battery->status.max_charge_power_W = battery_BEV_available_power_longterm_charge;
|
||||
|
||||
datalayer.battery.status.temperature_min_dC = battery_temperature_min * 10; // Add a decimal
|
||||
datalayer_battery->status.temperature_min_dC = battery_temperature_min * 10; // Add a decimal
|
||||
|
||||
datalayer.battery.status.temperature_max_dC = battery_temperature_max * 10; // Add a decimal
|
||||
datalayer_battery->status.temperature_max_dC = battery_temperature_max * 10; // Add a decimal
|
||||
|
||||
if (battery_info_available) {
|
||||
// Start checking safeties. First up, cellvoltages!
|
||||
if (detectedBattery == BATTERY_60AH) {
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_60AH;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_60AH;
|
||||
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
|
||||
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
|
||||
datalayer_battery->info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_60AH;
|
||||
datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_60AH;
|
||||
} else if (detectedBattery == BATTERY_94AH) {
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_94AH;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_94AH;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_94AH;
|
||||
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH;
|
||||
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_94AH;
|
||||
datalayer_battery->info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_94AH;
|
||||
datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_94AH;
|
||||
} else { // BATTERY_120AH
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_120AH;
|
||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_120AH;
|
||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_120AH;
|
||||
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH;
|
||||
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_120AH;
|
||||
datalayer_battery->info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_120AH;
|
||||
datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_120AH;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -508,15 +121,15 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer_extended.bmwi3.ST_cold_shutoff_valve = battery_status_cold_shutoff_valve;
|
||||
}
|
||||
|
||||
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||
void BmwI3Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||
switch (rx_frame.ID) {
|
||||
case 0x112: //BMS [10ms] Status Of High-Voltage Battery - 2
|
||||
battery_awake = true;
|
||||
datalayer.battery.status.CAN_battery_still_alive =
|
||||
datalayer_battery->status.CAN_battery_still_alive =
|
||||
CAN_STILL_ALIVE; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
|
||||
battery_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
|
||||
battery_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
|
||||
datalayer.battery.status.voltage_dV = battery_volts; // Update the datalayer as soon as possible with this info
|
||||
datalayer_battery->status.voltage_dV = battery_volts; // Update the datalayer as soon as possible with this info
|
||||
battery_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
|
||||
battery_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
|
||||
battery_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
|
||||
|
@ -552,7 +165,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
if (!skipCRCCheck) {
|
||||
if (calculateCRC(rx_frame, rx_frame.DLC, 0x15) != rx_frame.data.u8[0]) {
|
||||
// If calculated CRC does not match transmitted CRC, increase CANerror counter
|
||||
datalayer.battery.status.CAN_error_counter++;
|
||||
datalayer_battery->status.CAN_error_counter++;
|
||||
|
||||
// If the CRC check has never passed before, set the flag to skip future checks. Some SMEs have differing CRC checks.
|
||||
if (!CRCCheckPassedPreviously) {
|
||||
|
@ -638,10 +251,10 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
case 0x607: //BMS - responses to message requests on 0x615
|
||||
if ((cmdState == CELL_VOLTAGE_CELLNO || cmdState == CELL_VOLTAGE_CELLNO_LAST) && (rx_frame.data.u8[0] == 0xF4)) {
|
||||
if (rx_frame.DLC == 6) {
|
||||
transmit_can_frame(&BMW_6F4_CELL_CONTINUE, can_config.battery); // tell battery to send the cellvoltage
|
||||
transmit_can_frame(&BMW_6F4_CELL_CONTINUE, can_interface); // tell battery to send the cellvoltage
|
||||
}
|
||||
if (rx_frame.DLC == 8) { // We have the full value, map it
|
||||
datalayer.battery.status.cell_voltages_mV[current_cell_polled - 1] =
|
||||
datalayer_battery->status.cell_voltages_mV[current_cell_polled - 1] =
|
||||
(rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]);
|
||||
}
|
||||
}
|
||||
|
@ -651,7 +264,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
while (count < rx_frame.DLC && next_data < 49) {
|
||||
message_data[next_data++] = rx_frame.data.u8[count++];
|
||||
}
|
||||
transmit_can_frame(&BMW_6F1_CONTINUE, can_config.battery); // tell battery to send additional messages
|
||||
transmit_can_frame(&BMW_6F1_CONTINUE, can_interface); // tell battery to send additional messages
|
||||
|
||||
} else if (rx_frame.DLC > 3 && next_data > 0 && rx_frame.data.u8[0] == 0xf1 &&
|
||||
((rx_frame.data.u8[1] & 0xF0) == 0x20)) {
|
||||
|
@ -665,11 +278,11 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
if (next_data >= 4) {
|
||||
cellvoltage_temp_mV = (message_data[0] << 8 | message_data[1]);
|
||||
if (cellvoltage_temp_mV < 4500) { // Prevents garbage data from being read on bootup
|
||||
datalayer.battery.status.cell_min_voltage_mV = cellvoltage_temp_mV;
|
||||
datalayer_battery->status.cell_min_voltage_mV = cellvoltage_temp_mV;
|
||||
}
|
||||
cellvoltage_temp_mV = (message_data[2] << 8 | message_data[3]);
|
||||
if (cellvoltage_temp_mV < 4500) { // Prevents garbage data from being read on bootup
|
||||
datalayer.battery.status.cell_max_voltage_mV = cellvoltage_temp_mV;
|
||||
datalayer_battery->status.cell_max_voltage_mV = cellvoltage_temp_mV;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -693,194 +306,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
void handle_incoming_can_frame_battery2(CAN_frame rx_frame) {
|
||||
switch (rx_frame.ID) {
|
||||
case 0x112: //BMS [10ms] Status Of High-Voltage Battery - 2
|
||||
battery2_awake = true;
|
||||
datalayer.battery2.status.CAN_battery_still_alive =
|
||||
CAN_STILL_ALIVE; //This message is only sent if 30C (Wakeup pin on battery) is energized with 12V
|
||||
battery2_current = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) - 8192; //deciAmps (-819.2 to 819.0A)
|
||||
battery2_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
|
||||
datalayer.battery2.status.voltage_dV =
|
||||
battery2_volts; // Update the datalayer as soon as possible with this info, needed for contactor control
|
||||
battery2_HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 8 | rx_frame.data.u8[4]);
|
||||
battery2_request_open_contactors = (rx_frame.data.u8[5] & 0xC0) >> 6;
|
||||
battery2_request_open_contactors_instantly = (rx_frame.data.u8[6] & 0x03);
|
||||
battery2_request_open_contactors_fast = (rx_frame.data.u8[6] & 0x0C) >> 2;
|
||||
battery2_charging_condition_delta = (rx_frame.data.u8[6] & 0xF0) >> 4;
|
||||
battery2_DC_link_voltage = rx_frame.data.u8[7];
|
||||
break;
|
||||
case 0x1FA: //BMS [1000ms] Status Of High-Voltage Battery - 1
|
||||
battery2_status_error_isolation_external_Bordnetz = (rx_frame.data.u8[0] & 0x03);
|
||||
battery2_status_error_isolation_internal_Bordnetz = (rx_frame.data.u8[0] & 0x0C) >> 2;
|
||||
battery2_request_cooling = (rx_frame.data.u8[0] & 0x30) >> 4;
|
||||
battery2_status_valve_cooling = (rx_frame.data.u8[0] & 0xC0) >> 6;
|
||||
battery2_status_error_locking = (rx_frame.data.u8[1] & 0x03);
|
||||
battery2_status_precharge_locked = (rx_frame.data.u8[1] & 0x0C) >> 2;
|
||||
battery2_status_disconnecting_switch = (rx_frame.data.u8[1] & 0x30) >> 4;
|
||||
battery2_status_emergency_mode = (rx_frame.data.u8[1] & 0xC0) >> 6;
|
||||
battery2_request_service = (rx_frame.data.u8[2] & 0x03);
|
||||
battery2_error_emergency_mode = (rx_frame.data.u8[2] & 0x0C) >> 2;
|
||||
battery2_status_error_disconnecting_switch = (rx_frame.data.u8[2] & 0x30) >> 4;
|
||||
battery2_status_warning_isolation = (rx_frame.data.u8[2] & 0xC0) >> 6;
|
||||
battery2_status_cold_shutoff_valve = (rx_frame.data.u8[3] & 0x0F);
|
||||
battery2_temperature_HV = (rx_frame.data.u8[4] - 50);
|
||||
battery2_temperature_heat_exchanger = (rx_frame.data.u8[5] - 50);
|
||||
battery2_temperature_min = (rx_frame.data.u8[6] - 50);
|
||||
battery2_temperature_max = (rx_frame.data.u8[7] - 50);
|
||||
break;
|
||||
case 0x239: //BMS [200ms]
|
||||
battery2_predicted_energy_charge_condition = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[1]); //Wh
|
||||
battery2_predicted_energy_charging_target = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[3]) * 0.02); //kWh
|
||||
break;
|
||||
case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
|
||||
battery2_awake = true;
|
||||
if (!skipCRCCheck_battery2) {
|
||||
if (calculateCRC(rx_frame, rx_frame.DLC, 0x15) != rx_frame.data.u8[0]) {
|
||||
// If calculated CRC does not match transmitted CRC, increase CANerror counter
|
||||
datalayer.battery2.status.CAN_error_counter++;
|
||||
|
||||
// If the CRC check has never passed before, set the flag to skip future checks. Some SMEs have differing CRC checks.
|
||||
if (!CRCCheckPassedPreviously_battery2) {
|
||||
skipCRCCheck_battery2 = true;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// If CRC check passes, update the flag
|
||||
CRCCheckPassedPreviously_battery2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Process the data since CRC check is either passed or skipped
|
||||
battery2_status_diagnostics_HV = (rx_frame.data.u8[2] & 0x0F);
|
||||
break;
|
||||
case 0x2F5: //BMS [100ms] High-Voltage Battery Charge/Discharge Limitations
|
||||
battery2_max_charge_voltage = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
|
||||
battery2_max_charge_amperage = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 819.2);
|
||||
battery2_min_discharge_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
|
||||
battery2_max_discharge_amperage = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 819.2);
|
||||
break;
|
||||
case 0x2FF: //BMS [100ms] Status Heating High-Voltage Battery
|
||||
battery2_awake = true;
|
||||
battery2_actual_value_power_heating = (rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4);
|
||||
break;
|
||||
case 0x363: //BMS [1s] Identification High-Voltage Battery
|
||||
battery2_serial_number =
|
||||
(rx_frame.data.u8[3] << 24 | rx_frame.data.u8[2] << 16 | rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
|
||||
break;
|
||||
case 0x3C2: //BMS (94AH exclusive) - Status diagnostics OBD 2 powertrain
|
||||
battery2_status_diagnosis_powertrain_maximum_multiplexer =
|
||||
((rx_frame.data.u8[1] & 0x03) << 4 | rx_frame.data.u8[0] >> 4);
|
||||
battery2_status_diagnosis_powertrain_immediate_multiplexer = (rx_frame.data.u8[0] & 0xFC) >> 2;
|
||||
break;
|
||||
case 0x3EB: //BMS [1s] Status of charging high-voltage storage - 3
|
||||
battery2_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
|
||||
battery2_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
|
||||
battery2_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
|
||||
battery2_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
|
||||
break;
|
||||
case 0x40D: //BMS [1s] Charging status of high-voltage storage - 1
|
||||
battery2_BEV_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
|
||||
battery2_BEV_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
|
||||
battery2_BEV_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
|
||||
battery2_BEV_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
|
||||
break;
|
||||
case 0x41C: //BMS [1s] Operating Mode Status Of Hybrid - 2
|
||||
battery2_status_cooling_HV = (rx_frame.data.u8[1] & 0x03);
|
||||
break;
|
||||
case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
|
||||
battery2_prediction_voltage_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
|
||||
battery2_prediction_voltage_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
|
||||
battery2_prediction_voltage_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
|
||||
battery2_prediction_voltage_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]);
|
||||
break;
|
||||
case 0x431: //BMS [200ms] Data High-Voltage Battery Unit
|
||||
battery2_status_service_disconnection_plug = (rx_frame.data.u8[0] & 0x0F);
|
||||
battery2_status_measurement_isolation = (rx_frame.data.u8[0] & 0x0C) >> 2;
|
||||
battery2_request_abort_charging = (rx_frame.data.u8[0] & 0x30) >> 4;
|
||||
battery2_prediction_duration_charging_minutes = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
|
||||
battery2_prediction_time_end_of_charging_minutes = rx_frame.data.u8[4];
|
||||
battery2_energy_content_maximum_Wh = (((rx_frame.data.u8[6] & 0x0F) << 8) | rx_frame.data.u8[5]) * 20;
|
||||
if (battery2_energy_content_maximum_Wh > 33000) {
|
||||
detectedBattery2 = BATTERY_120AH;
|
||||
} else if (battery2_energy_content_maximum_Wh > 20000) {
|
||||
detectedBattery2 = BATTERY_94AH;
|
||||
} else {
|
||||
detectedBattery2 = BATTERY_60AH;
|
||||
}
|
||||
break;
|
||||
case 0x432: //BMS [200ms] SOC% info
|
||||
battery2_request_operating_mode = (rx_frame.data.u8[0] & 0x03);
|
||||
battery2_target_voltage_in_CV_mode = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
|
||||
battery2_request_charging_condition_minimum = (rx_frame.data.u8[2] / 2);
|
||||
battery2_request_charging_condition_maximum = (rx_frame.data.u8[3] / 2);
|
||||
battery2_display_SOC = rx_frame.data.u8[4];
|
||||
break;
|
||||
case 0x507: //BMS [640ms] Network Management - 2 - This message is sent on the bus for sleep coordination purposes
|
||||
break;
|
||||
case 0x587: //BMS [5s] Services
|
||||
battery2_ID2 = rx_frame.data.u8[0];
|
||||
break;
|
||||
case 0x607: //BMS - responses to message requests on 0x615
|
||||
if ((cmdState == CELL_VOLTAGE_CELLNO || cmdState == CELL_VOLTAGE_CELLNO_LAST) && (rx_frame.data.u8[0] == 0xF4)) {
|
||||
if (rx_frame.DLC == 6) {
|
||||
transmit_can_frame(&BMW_6F4_CELL_CONTINUE,
|
||||
can_config.battery_double); // tell battery to send the cellvoltage
|
||||
}
|
||||
if (rx_frame.DLC == 8) { // We have the full value, map it
|
||||
datalayer.battery2.status.cell_voltages_mV[current_cell_polled - 1] =
|
||||
(rx_frame.data.u8[6] << 8 | rx_frame.data.u8[7]);
|
||||
}
|
||||
}
|
||||
|
||||
if (rx_frame.DLC > 6 && next_data == 0 && rx_frame.data.u8[0] == 0xf1) {
|
||||
uint8_t count2 = 6;
|
||||
while (count2 < rx_frame.DLC && next_data < 49) {
|
||||
message_data[next_data++] = rx_frame.data.u8[count2++];
|
||||
}
|
||||
transmit_can_frame(&BMW_6F1_CONTINUE, can_config.battery_double);
|
||||
|
||||
} else if (rx_frame.DLC > 3 && next_data > 0 && rx_frame.data.u8[0] == 0xf1 &&
|
||||
((rx_frame.data.u8[1] & 0xF0) == 0x20)) {
|
||||
uint8_t count2 = 2;
|
||||
while (count2 < rx_frame.DLC && next_data < 49) {
|
||||
message_data[next_data++] = rx_frame.data.u8[count2++];
|
||||
}
|
||||
|
||||
switch (cmdState) {
|
||||
case CELL_VOLTAGE_MINMAX:
|
||||
if (next_data >= 4) {
|
||||
cellvoltage2_temp_mV = (message_data[0] << 8 | message_data[1]);
|
||||
if (cellvoltage2_temp_mV < 4500) { // Prevents garbage data from being read on bootup
|
||||
datalayer.battery2.status.cell_min_voltage_mV = cellvoltage2_temp_mV;
|
||||
}
|
||||
cellvoltage2_temp_mV = (message_data[2] << 8 | message_data[3]);
|
||||
if (cellvoltage_temp_mV < 4500) { // Prevents garbage data from being read on bootup
|
||||
datalayer.battery2.status.cell_max_voltage_mV = cellvoltage2_temp_mV;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SOH:
|
||||
if (next_data >= 4) {
|
||||
battery2_soh = message_data[3];
|
||||
battery2_info_available = true;
|
||||
}
|
||||
break;
|
||||
case SOC:
|
||||
if (next_data >= 6) {
|
||||
battery2_soc = (message_data[0] << 8 | message_data[1]);
|
||||
battery2_soc_hvmax = (message_data[2] << 8 | message_data[3]);
|
||||
battery2_soc_hvmin = (message_data[4] << 8 | message_data[5]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
void transmit_can_battery(unsigned long currentMillis) {
|
||||
void BmwI3Battery::transmit_can(unsigned long currentMillis) {
|
||||
|
||||
if (battery_awake) {
|
||||
//Send 20ms message
|
||||
|
@ -901,17 +328,11 @@ void transmit_can_battery(unsigned long currentMillis) {
|
|||
BMW_13E_counter++;
|
||||
BMW_13E.data.u8[4] = BMW_13E_counter;
|
||||
|
||||
if (datalayer.battery.status.bms_status == FAULT) {
|
||||
if (datalayer_battery->status.bms_status == FAULT) {
|
||||
} //If battery is not in Fault mode, allow contactor to close by sending 10B
|
||||
else {
|
||||
transmit_can_frame(&BMW_10B, can_config.battery);
|
||||
else if (*allows_contactor_closing == true) {
|
||||
transmit_can_frame(&BMW_10B, can_interface);
|
||||
}
|
||||
|
||||
#ifdef DOUBLE_BATTERY //If second battery is allowed to join in, also send 10B
|
||||
if (datalayer.system.status.battery2_allows_contactor_closing == true) {
|
||||
transmit_can_frame(&BMW_10B, can_config.battery_double);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// Send 100ms CAN Message
|
||||
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
|
||||
|
@ -922,10 +343,7 @@ void transmit_can_battery(unsigned long currentMillis) {
|
|||
|
||||
alive_counter_100ms = increment_alive_counter(alive_counter_100ms);
|
||||
|
||||
transmit_can_frame(&BMW_12F, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_12F, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_12F, can_interface);
|
||||
}
|
||||
// Send 200ms CAN Message
|
||||
if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
|
||||
|
@ -936,10 +354,7 @@ void transmit_can_battery(unsigned long currentMillis) {
|
|||
|
||||
alive_counter_200ms = increment_alive_counter(alive_counter_200ms);
|
||||
|
||||
transmit_can_frame(&BMW_19B, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_19B, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_19B, can_interface);
|
||||
}
|
||||
// Send 500ms CAN Message
|
||||
if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
|
||||
|
@ -950,21 +365,14 @@ void transmit_can_battery(unsigned long currentMillis) {
|
|||
|
||||
alive_counter_500ms = increment_alive_counter(alive_counter_500ms);
|
||||
|
||||
transmit_can_frame(&BMW_30B, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_30B, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_30B, can_interface);
|
||||
}
|
||||
// Send 640ms CAN Message
|
||||
if (currentMillis - previousMillis640 >= INTERVAL_640_MS) {
|
||||
previousMillis640 = currentMillis;
|
||||
|
||||
transmit_can_frame(&BMW_512, can_config.battery); // Keep BMS alive
|
||||
transmit_can_frame(&BMW_5F8, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_512, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_5F8, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_512, can_interface); // Keep BMS alive
|
||||
transmit_can_frame(&BMW_5F8, can_interface);
|
||||
}
|
||||
// Send 1000ms CAN Message
|
||||
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
|
||||
|
@ -1001,40 +409,22 @@ void transmit_can_battery(unsigned long currentMillis) {
|
|||
|
||||
alive_counter_1000ms = increment_alive_counter(alive_counter_1000ms);
|
||||
|
||||
transmit_can_frame(&BMW_3E8, can_config.battery); //Order comes from CAN logs
|
||||
transmit_can_frame(&BMW_328, can_config.battery);
|
||||
transmit_can_frame(&BMW_3F9, can_config.battery);
|
||||
transmit_can_frame(&BMW_2E2, can_config.battery);
|
||||
transmit_can_frame(&BMW_41D, can_config.battery);
|
||||
transmit_can_frame(&BMW_3D0, can_config.battery);
|
||||
transmit_can_frame(&BMW_3CA, can_config.battery);
|
||||
transmit_can_frame(&BMW_3A7, can_config.battery);
|
||||
transmit_can_frame(&BMW_2CA, can_config.battery);
|
||||
transmit_can_frame(&BMW_3FB, can_config.battery);
|
||||
transmit_can_frame(&BMW_418, can_config.battery);
|
||||
transmit_can_frame(&BMW_1D0, can_config.battery);
|
||||
transmit_can_frame(&BMW_3EC, can_config.battery);
|
||||
transmit_can_frame(&BMW_192, can_config.battery);
|
||||
transmit_can_frame(&BMW_13E, can_config.battery);
|
||||
transmit_can_frame(&BMW_433, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_3E8, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_328, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_3F9, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_2E2, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_41D, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_3D0, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_3CA, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_3A7, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_2CA, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_3FB, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_418, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_1D0, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_3EC, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_192, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_13E, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_433, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_3E8, can_interface); //Order comes from CAN logs
|
||||
transmit_can_frame(&BMW_328, can_interface);
|
||||
transmit_can_frame(&BMW_3F9, can_interface);
|
||||
transmit_can_frame(&BMW_2E2, can_interface);
|
||||
transmit_can_frame(&BMW_41D, can_interface);
|
||||
transmit_can_frame(&BMW_3D0, can_interface);
|
||||
transmit_can_frame(&BMW_3CA, can_interface);
|
||||
transmit_can_frame(&BMW_3A7, can_interface);
|
||||
transmit_can_frame(&BMW_2CA, can_interface);
|
||||
transmit_can_frame(&BMW_3FB, can_interface);
|
||||
transmit_can_frame(&BMW_418, can_interface);
|
||||
transmit_can_frame(&BMW_1D0, can_interface);
|
||||
transmit_can_frame(&BMW_3EC, can_interface);
|
||||
transmit_can_frame(&BMW_192, can_interface);
|
||||
transmit_can_frame(&BMW_13E, can_interface);
|
||||
transmit_can_frame(&BMW_433, can_interface);
|
||||
|
||||
BMW_433.data.u8[1] = 0x01; // First 433 message byte1 we send is unique, once we sent initial value send this
|
||||
BMW_3E8.data.u8[0] = 0xF1; // First 3E8 message byte0 we send is unique, once we sent initial value send this
|
||||
|
@ -1042,24 +432,15 @@ void transmit_can_battery(unsigned long currentMillis) {
|
|||
next_data = 0;
|
||||
switch (cmdState) {
|
||||
case SOC:
|
||||
transmit_can_frame(&BMW_6F1_CELL, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_6F1_CELL, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_6F1_CELL, can_interface);
|
||||
cmdState = CELL_VOLTAGE_MINMAX;
|
||||
break;
|
||||
case CELL_VOLTAGE_MINMAX:
|
||||
transmit_can_frame(&BMW_6F1_SOH, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_6F1_SOH, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_6F1_SOH, can_interface);
|
||||
cmdState = SOH;
|
||||
break;
|
||||
case SOH:
|
||||
transmit_can_frame(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_6F1_CELL_VOLTAGE_AVG, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_6F1_CELL_VOLTAGE_AVG, can_interface);
|
||||
cmdState = CELL_VOLTAGE_CELLNO;
|
||||
current_cell_polled = 0;
|
||||
|
||||
|
@ -1072,17 +453,11 @@ void transmit_can_battery(unsigned long currentMillis) {
|
|||
cmdState = CELL_VOLTAGE_CELLNO;
|
||||
|
||||
BMW_6F4_CELL_VOLTAGE_CELLNO.data.u8[6] = current_cell_polled;
|
||||
transmit_can_frame(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_6F4_CELL_VOLTAGE_CELLNO, can_interface);
|
||||
}
|
||||
break;
|
||||
case CELL_VOLTAGE_CELLNO_LAST:
|
||||
transmit_can_frame(&BMW_6F1_SOC, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_6F1_SOC, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_6F1_SOC, can_interface);
|
||||
cmdState = SOC;
|
||||
break;
|
||||
}
|
||||
|
@ -1094,26 +469,16 @@ void transmit_can_battery(unsigned long currentMillis) {
|
|||
BMW_3FC.data.u8[1] = ((BMW_3FC.data.u8[1] & 0xF0) + alive_counter_5000ms);
|
||||
BMW_3C5.data.u8[0] = ((BMW_3C5.data.u8[0] & 0xF0) + alive_counter_5000ms);
|
||||
|
||||
transmit_can_frame(&BMW_3FC, can_config.battery); //Order comes from CAN logs
|
||||
transmit_can_frame(&BMW_3C5, can_config.battery);
|
||||
transmit_can_frame(&BMW_3A0, can_config.battery);
|
||||
transmit_can_frame(&BMW_592_0, can_config.battery);
|
||||
transmit_can_frame(&BMW_592_1, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_3FC, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_3C5, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_3A0, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_592_0, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_592_1, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_3FC, can_interface); //Order comes from CAN logs
|
||||
transmit_can_frame(&BMW_3C5, can_interface);
|
||||
transmit_can_frame(&BMW_3A0, can_interface);
|
||||
transmit_can_frame(&BMW_592_0, can_interface);
|
||||
transmit_can_frame(&BMW_592_1, can_interface);
|
||||
|
||||
alive_counter_5000ms = increment_alive_counter(alive_counter_5000ms);
|
||||
|
||||
if (BMW_380_counter < 3) {
|
||||
transmit_can_frame(&BMW_380, can_config.battery); // This message stops after 3 times on startup
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_380, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_380, can_interface); // This message stops after 3 times on startup
|
||||
BMW_380_counter++;
|
||||
}
|
||||
}
|
||||
|
@ -1121,14 +486,9 @@ void transmit_can_battery(unsigned long currentMillis) {
|
|||
if (currentMillis - previousMillis10000 >= INTERVAL_10_S) {
|
||||
previousMillis10000 = currentMillis;
|
||||
|
||||
transmit_can_frame(&BMW_3E5, can_config.battery); //Order comes from CAN logs
|
||||
transmit_can_frame(&BMW_3E4, can_config.battery);
|
||||
transmit_can_frame(&BMW_37B, can_config.battery);
|
||||
#ifdef DOUBLE_BATTERY
|
||||
transmit_can_frame(&BMW_3E5, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_3E4, can_config.battery_double);
|
||||
transmit_can_frame(&BMW_37B, can_config.battery_double);
|
||||
#endif
|
||||
transmit_can_frame(&BMW_3E5, can_interface); //Order comes from CAN logs
|
||||
transmit_can_frame(&BMW_3E4, can_interface);
|
||||
transmit_can_frame(&BMW_37B, can_interface);
|
||||
|
||||
BMW_3E5.data.u8[0] = 0xFD; // First 3E5 message byte0 we send is unique, once we sent initial value send this
|
||||
}
|
||||
|
@ -1144,30 +504,19 @@ void transmit_can_battery(unsigned long currentMillis) {
|
|||
}
|
||||
}
|
||||
|
||||
void setup_battery(void) { // Performs one time setup at startup
|
||||
void BmwI3Battery::setup(void) { // Performs one time setup at startup
|
||||
strncpy(datalayer.system.info.battery_protocol, "BMW i3", 63);
|
||||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
|
||||
//Before we have started up and detected which battery is in use, use 60AH values
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
|
||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
|
||||
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
|
||||
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
|
||||
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
|
||||
datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
|
||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
datalayer.battery.info.number_of_cells = NUMBER_OF_CELLS;
|
||||
#ifdef DOUBLE_BATTERY
|
||||
datalayer.battery2.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV;
|
||||
datalayer.battery2.info.min_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV;
|
||||
datalayer.battery2.info.max_cell_voltage_deviation_mV = datalayer.battery.info.max_cell_voltage_deviation_mV;
|
||||
datalayer.battery2.status.voltage_dV =
|
||||
0; //Init voltage to 0 to allow contactor check to operate without fear of default values colliding
|
||||
datalayer.battery2.info.number_of_cells = NUMBER_OF_CELLS;
|
||||
#endif
|
||||
pinMode(WUP_PIN1, OUTPUT);
|
||||
digitalWrite(WUP_PIN1, HIGH); // Wake up the battery
|
||||
#if defined(DOUBLE_BATTERY) && defined(WUP_PIN2)
|
||||
pinMode(WUP_PIN2, OUTPUT);
|
||||
digitalWrite(WUP_PIN2, HIGH); // Wake up the battery
|
||||
#endif // defined(WUP_PIN2) && defined (DOUBLE_BATTERY)
|
||||
datalayer_battery->info.number_of_cells = NUMBER_OF_CELLS;
|
||||
|
||||
pinMode(wakeup_pin, OUTPUT);
|
||||
digitalWrite(wakeup_pin, HIGH); // Wake up the battery
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,25 +1,334 @@
|
|||
#ifndef BMW_I3_BATTERY_H
|
||||
#define BMW_I3_BATTERY_H
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "../include.h"
|
||||
#include "CanBattery.h"
|
||||
|
||||
#define BATTERY_SELECTED
|
||||
#define SELECTED_BATTERY_CLASS BmwI3Battery
|
||||
|
||||
#define MAX_CELL_VOLTAGE_60AH 4110 // Battery is put into emergency stop if one cell goes over this value
|
||||
#define MIN_CELL_VOLTAGE_60AH 2700 // Battery is put into emergency stop if one cell goes below this value
|
||||
#define MAX_CELL_VOLTAGE_94AH 4140 // Battery is put into emergency stop if one cell goes over this value
|
||||
#define MIN_CELL_VOLTAGE_94AH 2700 // Battery is put into emergency stop if one cell goes below this value
|
||||
#define MAX_CELL_VOLTAGE_120AH 4190 // Battery is put into emergency stop if one cell goes over this value
|
||||
#define MIN_CELL_VOLTAGE_120AH 2790 // Battery is put into emergency stop if one cell goes below this value
|
||||
#define MAX_CELL_DEVIATION_MV 250 // LED turns yellow on the board if mv delta exceeds this value
|
||||
#define MAX_PACK_VOLTAGE_60AH 3950 // Charge stops if pack voltage exceeds this value
|
||||
#define MIN_PACK_VOLTAGE_60AH 2590 // Discharge stops if pack voltage exceeds this value
|
||||
#define MAX_PACK_VOLTAGE_94AH 3980 // Charge stops if pack voltage exceeds this value
|
||||
#define MIN_PACK_VOLTAGE_94AH 2590 // Discharge stops if pack voltage exceeds this value
|
||||
#define MAX_PACK_VOLTAGE_120AH 4030 // Charge stops if pack voltage exceeds this value
|
||||
#define MIN_PACK_VOLTAGE_120AH 2680 // Discharge stops if pack voltage exceeds this value
|
||||
#define NUMBER_OF_CELLS 96
|
||||
void setup_battery(void);
|
||||
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
||||
class BmwI3Battery : public CanBattery {
|
||||
public:
|
||||
// Use this constructor for the second battery.
|
||||
BmwI3Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* allows_contactor_closing_ptr, int targetCan, int wakeup) {
|
||||
datalayer_battery = datalayer_ptr;
|
||||
allows_contactor_closing = allows_contactor_closing_ptr;
|
||||
can_interface = targetCan;
|
||||
wakeup_pin = wakeup;
|
||||
*allows_contactor_closing = true;
|
||||
|
||||
//Init voltage to 0 to allow contactor check to operate without fear of default values colliding
|
||||
battery_volts = 0;
|
||||
}
|
||||
|
||||
// Use the default constructor to create the first or single battery.
|
||||
BmwI3Battery() {
|
||||
datalayer_battery = &datalayer.battery;
|
||||
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||
can_interface = can_config.battery;
|
||||
wakeup_pin = WUP_PIN1;
|
||||
}
|
||||
|
||||
virtual void setup(void);
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
virtual void transmit_can(unsigned long currentMillis);
|
||||
|
||||
private:
|
||||
const int MAX_CELL_VOLTAGE_60AH = 4110; // Battery is put into emergency stop if one cell goes over this value
|
||||
const int MIN_CELL_VOLTAGE_60AH = 2700; // Battery is put into emergency stop if one cell goes below this value
|
||||
const int MAX_CELL_VOLTAGE_94AH = 4140; // Battery is put into emergency stop if one cell goes over this value
|
||||
const int MIN_CELL_VOLTAGE_94AH = 2700; // Battery is put into emergency stop if one cell goes below this value
|
||||
const int MAX_CELL_VOLTAGE_120AH = 4190; // Battery is put into emergency stop if one cell goes over this value
|
||||
const int MIN_CELL_VOLTAGE_120AH = 2790; // Battery is put into emergency stop if one cell goes below this value
|
||||
const int MAX_CELL_DEVIATION_MV = 250; // LED turns yellow on the board if mv delta exceeds this value
|
||||
const int MAX_PACK_VOLTAGE_60AH = 3950; // Charge stops if pack voltage exceeds this value
|
||||
const int MIN_PACK_VOLTAGE_60AH = 2590; // Discharge stops if pack voltage exceeds this value
|
||||
const int MAX_PACK_VOLTAGE_94AH = 3980; // Charge stops if pack voltage exceeds this value
|
||||
const int MIN_PACK_VOLTAGE_94AH = 2590; // Discharge stops if pack voltage exceeds this value
|
||||
const int MAX_PACK_VOLTAGE_120AH = 4030; // Charge stops if pack voltage exceeds this value
|
||||
const int MIN_PACK_VOLTAGE_120AH = 2680; // Discharge stops if pack voltage exceeds this value
|
||||
const int NUMBER_OF_CELLS = 96;
|
||||
|
||||
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||
bool* allows_contactor_closing;
|
||||
int wakeup_pin;
|
||||
|
||||
int can_interface;
|
||||
|
||||
unsigned long previousMillis20 = 0; // will store last time a 20ms CAN Message was send
|
||||
unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
|
||||
unsigned long previousMillis200 = 0; // will store last time a 200ms CAN Message was send
|
||||
unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send
|
||||
unsigned long previousMillis640 = 0; // will store last time a 600ms CAN Message was send
|
||||
unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was send
|
||||
unsigned long previousMillis5000 = 0; // will store last time a 5000ms CAN Message was send
|
||||
unsigned long previousMillis10000 = 0; // will store last time a 10000ms CAN Message was send
|
||||
|
||||
#define ALIVE_MAX_VALUE 14 // BMW CAN messages contain alive counter, goes from 0...14
|
||||
|
||||
enum BatterySize { BATTERY_60AH, BATTERY_94AH, BATTERY_120AH };
|
||||
BatterySize detectedBattery = BATTERY_60AH;
|
||||
|
||||
enum CmdState { SOH, CELL_VOLTAGE_MINMAX, SOC, CELL_VOLTAGE_CELLNO, CELL_VOLTAGE_CELLNO_LAST };
|
||||
|
||||
CmdState cmdState = SOC;
|
||||
|
||||
/* CAN messages from PT-CAN2 not needed to operate the battery
|
||||
0AA 105 13D 0BB 0AD 0A5 150 100 1A1 10E 153 197 429 1AA 12F 59A 2E3 2BE 211 2b3 3FD 2E8 2B7 108 29D 29C 29B 2C0 330
|
||||
3E9 32F 19E 326 55E 515 509 50A 51A 2F5 3A4 432 3C9
|
||||
*/
|
||||
|
||||
CAN_frame BMW_10B = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 3,
|
||||
.ID = 0x10B,
|
||||
.data = {0xCD, 0x00, 0xFC}}; // Contactor closing command
|
||||
CAN_frame BMW_12F = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x12F,
|
||||
.data = {0xE6, 0x24, 0x86, 0x1A, 0xF1, 0x31, 0x30, 0x00}}; //0x12F Wakeup VCU
|
||||
CAN_frame BMW_13E = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x13E,
|
||||
.data = {0xFF, 0x31, 0xFA, 0xFA, 0xFA, 0xFA, 0x0C, 0x00}};
|
||||
CAN_frame BMW_192 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x192,
|
||||
.data = {0xFF, 0xFF, 0xA3, 0x8F, 0x93, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_19B = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x19B,
|
||||
.data = {0x20, 0x40, 0x40, 0x55, 0xFD, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_1D0 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x1D0,
|
||||
.data = {0x4D, 0xF0, 0xAE, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_2CA = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x2CA, .data = {0x57, 0x57}};
|
||||
CAN_frame BMW_2E2 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x2E2,
|
||||
.data = {0x4F, 0xDB, 0x7F, 0xB9, 0x07, 0x51, 0xff, 0x00}};
|
||||
CAN_frame BMW_30B = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x30B,
|
||||
.data = {0xe1, 0xf0, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xff}};
|
||||
CAN_frame BMW_328 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 6,
|
||||
.ID = 0x328,
|
||||
.data = {0xB0, 0xE4, 0x87, 0x0E, 0x30, 0x22}};
|
||||
CAN_frame BMW_37B = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 6,
|
||||
.ID = 0x37B,
|
||||
.data = {0x40, 0x00, 0x00, 0xFF, 0xFF, 0x00}};
|
||||
CAN_frame BMW_380 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 7,
|
||||
.ID = 0x380,
|
||||
.data = {0x56, 0x5A, 0x37, 0x39, 0x34, 0x34, 0x34}};
|
||||
CAN_frame BMW_3A0 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3A0,
|
||||
.data = {0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC}};
|
||||
CAN_frame BMW_3A7 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 7,
|
||||
.ID = 0x3A7,
|
||||
.data = {0x05, 0xF5, 0x0A, 0x00, 0x4F, 0x11, 0xF0}};
|
||||
CAN_frame BMW_3C5 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3C5,
|
||||
.data = {0x30, 0x05, 0x47, 0x70, 0x2c, 0xce, 0xc3, 0x34}};
|
||||
CAN_frame BMW_3CA = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3CA,
|
||||
.data = {0x87, 0x80, 0x30, 0x0C, 0x0C, 0x81, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_3D0 = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x3D0, .data = {0xFD, 0xFF}};
|
||||
CAN_frame BMW_3E4 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 6,
|
||||
.ID = 0x3E4,
|
||||
.data = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_3E5 = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x3E5, .data = {0xFC, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_3E8 = {.FD = false, .ext_ID = false, .DLC = 2, .ID = 0x3E8, .data = {0xF0, 0xFF}}; //1000ms OBD reset
|
||||
CAN_frame BMW_3EC = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3EC,
|
||||
.data = {0xF5, 0x10, 0x00, 0x00, 0x80, 0x25, 0x0F, 0xFC}};
|
||||
CAN_frame BMW_3F9 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x3F9,
|
||||
.data = {0xA7, 0x2A, 0x00, 0xE2, 0xA6, 0x30, 0xC3, 0xFF}};
|
||||
CAN_frame BMW_3FB = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 6,
|
||||
.ID = 0x3FB,
|
||||
.data = {0xFF, 0xFF, 0xFF, 0xFF, 0x5F, 0x00}};
|
||||
CAN_frame BMW_3FC = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x3FC, .data = {0xC0, 0xF9, 0x0F}};
|
||||
CAN_frame BMW_418 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x418,
|
||||
.data = {0xFF, 0x7C, 0xFF, 0x00, 0xC0, 0x3F, 0xFF, 0xFF}};
|
||||
CAN_frame BMW_41D = {.FD = false, .ext_ID = false, .DLC = 4, .ID = 0x41D, .data = {0xFF, 0xF7, 0x7F, 0xFF}};
|
||||
CAN_frame BMW_433 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 4,
|
||||
.ID = 0x433,
|
||||
.data = {0xFF, 0x00, 0x0F, 0xFF}}; // HV specification
|
||||
CAN_frame BMW_512 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x512,
|
||||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12}}; // 0x512 Network management
|
||||
CAN_frame BMW_592_0 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x592,
|
||||
.data = {0x86, 0x10, 0x07, 0x21, 0x6e, 0x35, 0x5e, 0x86}};
|
||||
CAN_frame BMW_592_1 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x592,
|
||||
.data = {0x86, 0x21, 0xb4, 0xdd, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame BMW_5F8 = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x5F8,
|
||||
.data = {0x64, 0x01, 0x00, 0x0B, 0x92, 0x03, 0x00, 0x05}};
|
||||
CAN_frame BMW_6F1_CELL = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 5,
|
||||
.ID = 0x6F1,
|
||||
.data = {0x07, 0x03, 0x22, 0xDD, 0xBF}};
|
||||
CAN_frame BMW_6F1_SOH = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0x63, 0x35}};
|
||||
CAN_frame BMW_6F1_SOC = {.FD = false, .ext_ID = false, .DLC = 5, .ID = 0x6F1, .data = {0x07, 0x03, 0x22, 0xDD, 0xBC}};
|
||||
CAN_frame BMW_6F1_CELL_VOLTAGE_AVG = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 5,
|
||||
.ID = 0x6F1,
|
||||
.data = {0x07, 0x03, 0x22, 0xDF, 0xA0}};
|
||||
CAN_frame BMW_6F1_CONTINUE = {.FD = false, .ext_ID = false, .DLC = 4, .ID = 0x6F1, .data = {0x07, 0x30, 0x00, 0x02}};
|
||||
CAN_frame BMW_6F4_CELL_VOLTAGE_CELLNO = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 7,
|
||||
.ID = 0x6F4,
|
||||
.data = {0x07, 0x05, 0x31, 0x01, 0xAD, 0x6E, 0x01}};
|
||||
CAN_frame BMW_6F4_CELL_CONTINUE = {.FD = false,
|
||||
.ext_ID = false,
|
||||
.DLC = 6,
|
||||
.ID = 0x6F4,
|
||||
.data = {0x07, 0x04, 0x31, 0x03, 0xAD, 0x6E}};
|
||||
|
||||
//The above CAN messages need to be sent towards the battery to keep it alive
|
||||
|
||||
uint8_t startup_counter_contactor = 0;
|
||||
uint8_t alive_counter_20ms = 0;
|
||||
uint8_t alive_counter_100ms = 0;
|
||||
uint8_t alive_counter_200ms = 0;
|
||||
uint8_t alive_counter_500ms = 0;
|
||||
uint8_t alive_counter_1000ms = 0;
|
||||
uint8_t alive_counter_5000ms = 0;
|
||||
uint8_t BMW_1D0_counter = 0;
|
||||
uint8_t BMW_13E_counter = 0;
|
||||
uint8_t BMW_380_counter = 0;
|
||||
uint32_t BMW_328_seconds = 243785948; // Initialized to make the battery think vehicle was made 7.7years ago
|
||||
uint16_t BMW_328_days =
|
||||
9244; //Time since 1.1.2000. Hacky implementation to make it think current date is 23rd April 2025
|
||||
uint32_t BMS_328_seconds_to_day = 0; //Counter to keep track of days uptime
|
||||
|
||||
bool battery_awake = false;
|
||||
bool battery_info_available = false;
|
||||
bool skipCRCCheck = false;
|
||||
bool CRCCheckPassedPreviously = false;
|
||||
|
||||
uint16_t cellvoltage_temp_mV = 0;
|
||||
uint32_t battery_serial_number = 0;
|
||||
uint32_t battery_available_power_shortterm_charge = 0;
|
||||
uint32_t battery_available_power_shortterm_discharge = 0;
|
||||
uint32_t battery_available_power_longterm_charge = 0;
|
||||
uint32_t battery_available_power_longterm_discharge = 0;
|
||||
uint32_t battery_BEV_available_power_shortterm_charge = 0;
|
||||
uint32_t battery_BEV_available_power_shortterm_discharge = 0;
|
||||
uint32_t battery_BEV_available_power_longterm_charge = 0;
|
||||
uint32_t battery_BEV_available_power_longterm_discharge = 0;
|
||||
uint16_t battery_energy_content_maximum_Wh = 0;
|
||||
uint16_t battery_display_SOC = 0;
|
||||
uint16_t battery_volts = 0;
|
||||
uint16_t battery_HVBatt_SOC = 0;
|
||||
uint16_t battery_DC_link_voltage = 0;
|
||||
uint16_t battery_max_charge_voltage = 0;
|
||||
uint16_t battery_min_discharge_voltage = 0;
|
||||
uint16_t battery_predicted_energy_charge_condition = 0;
|
||||
uint16_t battery_predicted_energy_charging_target = 0;
|
||||
uint16_t battery_actual_value_power_heating = 0; //0 - 4094 W
|
||||
uint16_t battery_prediction_voltage_shortterm_charge = 0;
|
||||
uint16_t battery_prediction_voltage_shortterm_discharge = 0;
|
||||
uint16_t battery_prediction_voltage_longterm_charge = 0;
|
||||
uint16_t battery_prediction_voltage_longterm_discharge = 0;
|
||||
uint16_t battery_prediction_duration_charging_minutes = 0;
|
||||
uint16_t battery_target_voltage_in_CV_mode = 0;
|
||||
uint16_t battery_soc = 0;
|
||||
uint16_t battery_soc_hvmax = 0;
|
||||
uint16_t battery_soc_hvmin = 0;
|
||||
uint16_t battery_capacity_cah = 0;
|
||||
int16_t battery_temperature_HV = 0;
|
||||
int16_t battery_temperature_heat_exchanger = 0;
|
||||
int16_t battery_temperature_max = 0;
|
||||
int16_t battery_temperature_min = 0;
|
||||
int16_t battery_max_charge_amperage = 0;
|
||||
int16_t battery_max_discharge_amperage = 0;
|
||||
int16_t battery_current = 0;
|
||||
uint8_t battery_status_error_isolation_external_Bordnetz = 0;
|
||||
uint8_t battery_status_error_isolation_internal_Bordnetz = 0;
|
||||
uint8_t battery_request_cooling = 0;
|
||||
uint8_t battery_status_valve_cooling = 0;
|
||||
uint8_t battery_status_error_locking = 0;
|
||||
uint8_t battery_status_precharge_locked = 0;
|
||||
uint8_t battery_status_disconnecting_switch = 0;
|
||||
uint8_t battery_status_emergency_mode = 0;
|
||||
uint8_t battery_request_service = 0;
|
||||
uint8_t battery_error_emergency_mode = 0;
|
||||
uint8_t battery_status_error_disconnecting_switch = 0;
|
||||
uint8_t battery_status_warning_isolation = 0;
|
||||
uint8_t battery_status_cold_shutoff_valve = 0;
|
||||
uint8_t battery_request_open_contactors = 0;
|
||||
uint8_t battery_request_open_contactors_instantly = 0;
|
||||
uint8_t battery_request_open_contactors_fast = 0;
|
||||
uint8_t battery_charging_condition_delta = 0;
|
||||
uint8_t battery_status_service_disconnection_plug = 0;
|
||||
uint8_t battery_status_measurement_isolation = 0;
|
||||
uint8_t battery_request_abort_charging = 0;
|
||||
uint8_t battery_prediction_time_end_of_charging_minutes = 0;
|
||||
uint8_t battery_request_operating_mode = 0;
|
||||
uint8_t battery_request_charging_condition_minimum = 0;
|
||||
uint8_t battery_request_charging_condition_maximum = 0;
|
||||
uint8_t battery_status_cooling_HV = 0; //1 works, 2 does not start
|
||||
uint8_t battery_status_diagnostics_HV = 0; // 0 all OK, 1 HV protection function error, 2 diag not yet expired
|
||||
uint8_t battery_status_diagnosis_powertrain_maximum_multiplexer = 0;
|
||||
uint8_t battery_status_diagnosis_powertrain_immediate_multiplexer = 0;
|
||||
uint8_t battery_ID2 = 0;
|
||||
uint8_t battery_soh = 99;
|
||||
|
||||
uint8_t message_data[50];
|
||||
uint8_t next_data = 0;
|
||||
uint8_t current_cell_polled = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue