mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 02:39:57 +02:00
Merge pull request #224 from dalathegreat/feature/bmwi3
New battery: BMW i3 ✅🔋
This commit is contained in:
commit
d4ecbfddbd
2 changed files with 631 additions and 105 deletions
|
@ -5,16 +5,57 @@
|
|||
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||
#include "BMW-I3-BATTERY.h"
|
||||
|
||||
//TODO: before using
|
||||
// Map the final values in update_values_battery, set some to static values if not available (current, discharge max , charge max)
|
||||
// Check if I3 battery stays alive with only 10B and 512. If not, add 12F. If that doesn't help, add more from CAN log (ask Dala)
|
||||
|
||||
/* 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 previousMillis600 = 0; // will store last time a 600ms 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
|
||||
static const int interval20 = 20; // interval (ms) at which send CAN Messages
|
||||
static const int interval600 = 600; // interval (ms) at which send CAN Messages
|
||||
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
|
||||
static const int interval100 = 100; // interval (ms) at which send CAN Messages
|
||||
static const int interval200 = 200; // interval (ms) at which send CAN Messages
|
||||
static const int interval500 = 500; // interval (ms) at which send CAN Messages
|
||||
static const int interval640 = 640; // interval (ms) at which send CAN Messages
|
||||
static const int interval1000 = 1000; // interval (ms) at which send CAN Messages
|
||||
static const int interval5000 = 5000; // interval (ms) at which send CAN Messages
|
||||
static const int interval10000 = 10000; // interval (ms) at which send CAN Messages
|
||||
static uint8_t CANstillAlive = 12; // counter for checking if CAN is still alive
|
||||
static uint16_t CANerror = 0; // counter on how many CAN errors encountered
|
||||
#define MAX_CAN_FAILURES 500 // Amount of malformed CAN messages to allow before raising a warning
|
||||
#define ALIVE_MAX_VALUE 14 // BMW CAN messages contain alive counter, goes from 0...14
|
||||
|
||||
static const uint16_t WUPonDuration = 477; // in milliseconds how long WUP should be ON after poweron
|
||||
static const uint16_t WUPoffDuration = 105; // in milliseconds how long WUP should be OFF after on pulse
|
||||
unsigned long lastChangeTime; // Variables to store timestamps
|
||||
unsigned long turnOnTime; // Variables to store timestamps
|
||||
enum State { POWERON, STATE_ON, STATE_OFF };
|
||||
static State WUPState = POWERON;
|
||||
|
||||
const unsigned char crc8_table[256] =
|
||||
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
|
||||
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0,
|
||||
0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E, 0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0,
|
||||
0xF3, 0xEE, 0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C, 0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23,
|
||||
0x04, 0x19, 0xA2, 0xBF, 0x98, 0x85, 0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
|
||||
0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9, 0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B,
|
||||
0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65, 0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B,
|
||||
0x08, 0x15, 0x32, 0x2F, 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A, 0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8,
|
||||
0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01, 0x52, 0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
|
||||
0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, 0x03, 0x1E, 0x39, 0x24, 0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC,
|
||||
0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2, 0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B,
|
||||
0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7, 0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C,
|
||||
0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA, 0xA9, 0xB4, 0x93, 0x8E, 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
|
||||
0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95, 0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47,
|
||||
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_t BMW_10B = {.FIR = {.B =
|
||||
{
|
||||
|
@ -22,71 +63,336 @@ CAN_frame_t BMW_10B = {.FIR = {.B =
|
|||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x10B,
|
||||
.data = {0xCD, 0x01, 0xFC}};
|
||||
CAN_frame_t BMW_512 = {
|
||||
.FIR = {.B =
|
||||
.data = {0xCD, 0x00, 0xFC}}; // Contactor closing command
|
||||
CAN_frame_t BMW_12F = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x512,
|
||||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12}}; //0x512 Network management edme VCU
|
||||
//CAN_frame_t BMW_12F //Might be needed, Wake up ,VCU 100ms
|
||||
//These CAN messages need to be sent towards the battery to keep it alive
|
||||
.MsgID = 0x12F,
|
||||
.data = {0xE6, 0x24, 0x86, 0x1A, 0xF1, 0x31, 0x30, 0x00}}; //0x12F Wakeup VCU
|
||||
CAN_frame_t BMW_13E = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x13E,
|
||||
.data = {0xFF, 0x31, 0xFA, 0xFA, 0xFA, 0xFA, 0x0C, 0x00}};
|
||||
CAN_frame_t BMW_192 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x192,
|
||||
.data = {0xFF, 0xFF, 0xA3, 0x8F, 0x93, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame_t BMW_19B = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x19B,
|
||||
.data = {0x20, 0x40, 0x40, 0x55, 0xFD, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame_t BMW_1D0 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x1D0,
|
||||
.data = {0x4D, 0xF0, 0xAE, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF}};
|
||||
CAN_frame_t BMW_2CA = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 2,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x2CA,
|
||||
.data = {0x57, 0x57}};
|
||||
CAN_frame_t BMW_2E2 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x2E2,
|
||||
.data = {0x4F, 0xDB, 0x7F, 0xB9, 0x07, 0x51, 0xff, 0x00}};
|
||||
CAN_frame_t BMW_30B = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x30B,
|
||||
.data = {0xe1, 0xf0, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xff}};
|
||||
CAN_frame_t BMW_328 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 6,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x328,
|
||||
.data = {0xB0, 0xE4, 0x87, 0x0E, 0x30, 0x22}};
|
||||
CAN_frame_t BMW_37B = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 6,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x37B,
|
||||
.data = {0x40, 0x00, 0x00, 0xFF, 0xFF, 0x00}};
|
||||
CAN_frame_t BMW_380 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 7,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x380,
|
||||
.data = {0x56, 0x5A, 0x37, 0x39, 0x34, 0x34, 0x34}};
|
||||
CAN_frame_t BMW_3A0 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3A0,
|
||||
.data = {0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC}};
|
||||
CAN_frame_t BMW_3A7 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 7,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3A7,
|
||||
.data = {0x05, 0xF5, 0x0A, 0x00, 0x4F, 0x11, 0xF0}};
|
||||
CAN_frame_t BMW_3C5 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3C5,
|
||||
.data = {0x30, 0x05, 0x47, 0x70, 0x2c, 0xce, 0xc3, 0x34}};
|
||||
CAN_frame_t BMW_3CA = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3CA,
|
||||
.data = {0x87, 0x80, 0x30, 0x0C, 0x0C, 0x81, 0xFF, 0xFF}};
|
||||
CAN_frame_t BMW_3D0 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 2,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3D0,
|
||||
.data = {0xFD, 0xFF}};
|
||||
CAN_frame_t BMW_3E4 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 6,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3E4,
|
||||
.data = {0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF}};
|
||||
CAN_frame_t BMW_3E5 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 3,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3E5,
|
||||
.data = {0xFC, 0xFF, 0xFF}};
|
||||
CAN_frame_t BMW_3E8 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 2,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3E8,
|
||||
.data = {0xF0, 0xFF}}; //1000ms OBD reset
|
||||
CAN_frame_t BMW_3EC = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3EC,
|
||||
.data = {0xF5, 0x10, 0x00, 0x00, 0x80, 0x25, 0x0F, 0xFC}};
|
||||
CAN_frame_t BMW_3F9 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3F9,
|
||||
.data = {0xA7, 0x2A, 0x00, 0xE2, 0xA6, 0x30, 0xC3, 0xFF}};
|
||||
CAN_frame_t BMW_3FB = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 6,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3FB,
|
||||
.data = {0xFF, 0xFF, 0xFF, 0xFF, 0x5F, 0x00}};
|
||||
CAN_frame_t BMW_3FC = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 3,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x3FC,
|
||||
.data = {0xC0, 0xF9, 0x0F}};
|
||||
CAN_frame_t BMW_418 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x418,
|
||||
.data = {0xFF, 0x7C, 0xFF, 0x00, 0xC0, 0x3F, 0xFF, 0xFF}};
|
||||
CAN_frame_t BMW_41D = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 4,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x41D,
|
||||
.data = {0xFF, 0xF7, 0x7F, 0xFF}};
|
||||
CAN_frame_t BMW_433 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 4,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x433,
|
||||
.data = {0xFF, 0x00, 0x0F, 0xFF}}; // HV specification
|
||||
CAN_frame_t BMW_512 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x512, // Required to keep BMS active
|
||||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12}}; // 0x512 Network management
|
||||
CAN_frame_t BMW_592_0 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x592,
|
||||
.data = {0x86, 0x10, 0x07, 0x21, 0x6e, 0x35, 0x5e, 0x86}};
|
||||
CAN_frame_t BMW_592_1 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x592,
|
||||
.data = {0x86, 0x21, 0xb4, 0xdd, 0x00, 0x00, 0x00, 0x00}};
|
||||
CAN_frame_t BMW_5F8 = {.FIR = {.B =
|
||||
{
|
||||
.DLC = 8,
|
||||
.FF = CAN_frame_std,
|
||||
}},
|
||||
.MsgID = 0x5F8,
|
||||
.data = {0x64, 0x01, 0x00, 0x0B, 0x92, 0x03, 0x00, 0x05}};
|
||||
|
||||
static const uint8_t BMW_10B_0[15] = {0xCD, 0x19, 0x94, 0x6D, 0xE0, 0x34, 0x78, 0xDB,
|
||||
0x97, 0x43, 0x0F, 0xF6, 0xBA, 0x6E, 0x81};
|
||||
static const uint8_t BMW_10B_1[15] = {0x01, 0x02, 0x33, 0x34, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x00};
|
||||
static uint8_t BMW_10B_counter = 0;
|
||||
//The above CAN messages need to be sent towards the battery to keep it alive
|
||||
|
||||
static int16_t Battery_Current = 0;
|
||||
static uint16_t Battery_Capacity_kWh = 0;
|
||||
static uint16_t Voltage_Setpoint = 0;
|
||||
static uint16_t Low_SOC = 0;
|
||||
static uint16_t High_SOC = 0;
|
||||
static uint16_t Display_SOC = 0;
|
||||
static uint16_t Calculated_SOC = 0;
|
||||
static uint16_t Battery_Volts = 0;
|
||||
static uint16_t HVBatt_SOC = 0;
|
||||
static uint16_t Battery_Status = 0;
|
||||
static uint16_t DC_link = 0;
|
||||
static int16_t Battery_Power = 0;
|
||||
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_counter = 0;
|
||||
static bool battery_awake = false;
|
||||
|
||||
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_kWh = 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 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_power = 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_cellvoltage_mux = 0;
|
||||
|
||||
static uint8_t calculateCRC(CAN_frame_t 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
|
||||
crc = crc8_table[(crc ^ static_cast<uint8_t>(rx_frame.data.u8[j])) % 256];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static uint8_t increment_alive_counter(uint8_t counter) {
|
||||
counter++;
|
||||
if (counter > ALIVE_MAX_VALUE) {
|
||||
counter = 0;
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
|
||||
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||
//Calculate the SOC% value to send to inverter
|
||||
system_real_SOC_pptt = (Display_SOC * 100); //increase Display_SOC range from 0-100 -> 100.00
|
||||
|
||||
system_battery_voltage_dV = Battery_Volts; //Unit V+1 (5000 = 500.0V)
|
||||
system_real_SOC_pptt = (battery_display_SOC * 100); //increase Display_SOC range from 0-100 -> 100.00
|
||||
|
||||
system_battery_current_dA = Battery_Current;
|
||||
system_battery_voltage_dV = battery_volts; //Unit V+1 (5000 = 500.0V)
|
||||
|
||||
system_battery_current_dA = battery_current;
|
||||
|
||||
system_capacity_Wh = BATTERY_WH_MAX;
|
||||
|
||||
system_remaining_capacity_Wh = (Battery_Capacity_kWh * 1000);
|
||||
system_remaining_capacity_Wh = (battery_energy_content_maximum_kWh * 1000); // Convert kWh to Wh
|
||||
|
||||
if (system_scaled_SOC_pptt > 9900) //If Soc is over 99%, stop charging
|
||||
{
|
||||
system_max_charge_power_W = 0;
|
||||
} else {
|
||||
system_max_charge_power_W = 5000; //Hardcoded value for testing. TODO: read real value from battery when discovered
|
||||
}
|
||||
system_max_charge_power_W = (battery_max_charge_amperage * system_battery_voltage_dV);
|
||||
|
||||
if (system_scaled_SOC_pptt < 500) //If Soc is under 5%, stop dicharging
|
||||
{
|
||||
system_max_discharge_power_W = 0;
|
||||
} else {
|
||||
system_max_discharge_power_W =
|
||||
5000; //Hardcoded value for testing. TODO: read real value from battery when discovered
|
||||
}
|
||||
system_max_discharge_power_W = (battery_max_discharge_amperage * system_battery_voltage_dV);
|
||||
|
||||
Battery_Power = (Battery_Current * (Battery_Volts / 10));
|
||||
battery_power = (system_battery_current_dA * (system_battery_voltage_dV / 10));
|
||||
|
||||
system_active_power_W = Battery_Power; //TODO:, is mapping OK?
|
||||
system_active_power_W = battery_power;
|
||||
|
||||
system_temperature_min_dC; //hardcoded to 5*C in startup, TODO:, find from battery CAN later
|
||||
system_temperature_min_dC = battery_temperature_min * 10; // Add a decimal
|
||||
|
||||
system_temperature_max_dC; //hardcoded to 6*C in startup, TODO:, find from battery CAN later
|
||||
system_temperature_max_dC = battery_temperature_max * 10; // Add a decimal
|
||||
|
||||
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
|
||||
if (!CANstillAlive) {
|
||||
|
@ -95,64 +401,156 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
CANstillAlive--;
|
||||
clear_event(EVENT_CAN_RX_FAILURE);
|
||||
}
|
||||
// Check if we have encountered any malformed CAN messages
|
||||
if (CANerror > MAX_CAN_FAILURES) {
|
||||
set_event(EVENT_CAN_RX_WARNING, 0);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_VIA_USB
|
||||
Serial.print("SOC% battery: ");
|
||||
Serial.print(Display_SOC);
|
||||
Serial.print(" SOC% sent to inverter: ");
|
||||
Serial.print(system_scaled_SOC_pptt);
|
||||
Serial.println(" ");
|
||||
Serial.print("Values sent to inverter: ");
|
||||
Serial.print("Real SOC%: ");
|
||||
Serial.print(system_real_SOC_pptt * 0.01);
|
||||
Serial.print(" Battery voltage: ");
|
||||
Serial.print(system_battery_voltage_dV);
|
||||
Serial.print(system_battery_voltage_dV * 0.1);
|
||||
Serial.print(" Battery current: ");
|
||||
Serial.print(system_battery_current_dA * 0.1);
|
||||
Serial.print(" Wh when full: ");
|
||||
Serial.print(system_capacity_Wh);
|
||||
Serial.print(" Remaining Wh: ");
|
||||
Serial.print(system_remaining_capacity_Wh);
|
||||
Serial.print(" Max charge power: ");
|
||||
Serial.print(system_max_charge_power_W);
|
||||
Serial.print(" Max discharge power: ");
|
||||
Serial.print(system_max_discharge_power_W);
|
||||
Serial.print(" Active power: ");
|
||||
Serial.print(system_active_power_W);
|
||||
Serial.print(" Min temp: ");
|
||||
Serial.print(system_temperature_min_dC * 0.1);
|
||||
Serial.print(" Max temp: ");
|
||||
Serial.print(system_temperature_max_dC * 0.1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void receive_can_battery(CAN_frame_t rx_frame) {
|
||||
CANstillAlive = 12;
|
||||
switch (rx_frame.MsgID) {
|
||||
case 0x431: //Battery capacity [200ms]
|
||||
Battery_Capacity_kWh = (((rx_frame.data.u8[1] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
|
||||
case 0x112: //BMS [10ms] Status Of High-Voltage Battery - 2
|
||||
battery_awake = true;
|
||||
CANstillAlive = 12; //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]) / 10) - 819; //Amps
|
||||
battery_volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
|
||||
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);
|
||||
battery_request_open_contactors_fast = (rx_frame.data.u8[6] & 0x0C) >> 2;
|
||||
battery_charging_condition_delta = (rx_frame.data.u8[6] & 0xF0) >> 4;
|
||||
battery_DC_link_voltage = rx_frame.data.u8[7];
|
||||
break;
|
||||
case 0x432: //SOC% charged [200ms]
|
||||
Voltage_Setpoint = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
|
||||
Low_SOC = (rx_frame.data.u8[2] / 2);
|
||||
High_SOC = (rx_frame.data.u8[3] / 2);
|
||||
Display_SOC = (rx_frame.data.u8[4] / 2);
|
||||
case 0x1FA: //BMS [1000ms] Status Of High-Voltage Battery - 1
|
||||
battery_status_error_isolation_external_Bordnetz = (rx_frame.data.u8[0] & 0x03);
|
||||
battery_status_error_isolation_internal_Bordnetz = (rx_frame.data.u8[0] & 0x0C) >> 2;
|
||||
battery_request_cooling = (rx_frame.data.u8[0] & 0x30) >> 4;
|
||||
battery_status_valve_cooling = (rx_frame.data.u8[0] & 0xC0) >> 6;
|
||||
battery_status_error_locking = (rx_frame.data.u8[1] & 0x03);
|
||||
battery_status_precharge_locked = (rx_frame.data.u8[1] & 0x0C) >> 2;
|
||||
battery_status_disconnecting_switch = (rx_frame.data.u8[1] & 0x30) >> 4;
|
||||
battery_status_emergency_mode = (rx_frame.data.u8[1] & 0xC0) >> 6;
|
||||
battery_request_service = (rx_frame.data.u8[2] & 0x03);
|
||||
battery_error_emergency_mode = (rx_frame.data.u8[2] & 0x0C) >> 2;
|
||||
battery_status_error_disconnecting_switch = (rx_frame.data.u8[2] & 0x30) >> 4;
|
||||
battery_status_warning_isolation = (rx_frame.data.u8[2] & 0xC0) >> 6;
|
||||
battery_status_cold_shutoff_valve = (rx_frame.data.u8[3] & 0x0F);
|
||||
battery_temperature_HV = (rx_frame.data.u8[4] - 50);
|
||||
battery_temperature_heat_exchanger = (rx_frame.data.u8[5] - 50);
|
||||
battery_temperature_max = (rx_frame.data.u8[6] - 50);
|
||||
battery_temperature_min = (rx_frame.data.u8[7] - 50);
|
||||
break;
|
||||
case 0x112: //BMS status [10ms]
|
||||
CANstillAlive = 12;
|
||||
Battery_Current = ((rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) / 10) - 819; //Amps
|
||||
Battery_Volts = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]); //500.0 V
|
||||
HVBatt_SOC = ((rx_frame.data.u8[5] & 0x0F) << 4 | rx_frame.data.u8[4]) / 10;
|
||||
Battery_Status = (rx_frame.data.u8[6] & 0x0F);
|
||||
DC_link = rx_frame.data.u8[7];
|
||||
case 0x239: //BMS [200ms]
|
||||
battery_predicted_energy_charge_condition = (rx_frame.data.u8[2] << 8 | rx_frame.data.u8[1]); //Wh
|
||||
battery_predicted_energy_charging_target = ((rx_frame.data.u8[4] << 8 | rx_frame.data.u8[3]) * 0.02); //kWh
|
||||
break;
|
||||
case 0x430:
|
||||
case 0x2BD: //BMS [100ms] Status diagnosis high voltage - 1
|
||||
battery_awake = true;
|
||||
if (calculateCRC(rx_frame, 3, 0x15) != rx_frame.data.u8[0]) {
|
||||
//If calculated CRC does not match transmitted CRC, increase CANerror counter
|
||||
CANerror++;
|
||||
break;
|
||||
case 0x1FA:
|
||||
}
|
||||
battery_status_diagnostics_HV = (rx_frame.data.u8[2] & 0x0F);
|
||||
break;
|
||||
case 0x40D:
|
||||
case 0x2F5: //BMS [100ms] High-Voltage Battery Charge/Discharge Limitations
|
||||
battery_max_charge_voltage = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
|
||||
battery_max_charge_amperage = (((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) - 819.2);
|
||||
battery_min_discharge_voltage = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
|
||||
battery_max_discharge_amperage = (((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) - 819.2);
|
||||
break;
|
||||
case 0x2FF:
|
||||
case 0x2FF: //BMS [100ms] Status Heating High-Voltage Battery
|
||||
battery_awake = true;
|
||||
battery_actual_value_power_heating = (rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4);
|
||||
break;
|
||||
case 0x239:
|
||||
case 0x363: //BMS [1s] Identification High-Voltage Battery
|
||||
battery_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 0x2BD:
|
||||
case 0x3C2: //BMS (94AH exclusive) - Status diagnostics OBD 2 powertrain
|
||||
battery_status_diagnosis_powertrain_maximum_multiplexer =
|
||||
((rx_frame.data.u8[1] & 0x03) << 4 | rx_frame.data.u8[0] >> 4);
|
||||
battery_status_diagnosis_powertrain_immediate_multiplexer = (rx_frame.data.u8[0] & 0xFC) >> 2;
|
||||
break;
|
||||
case 0x2F5:
|
||||
case 0x3EB: //BMS [1s] Status of charging high-voltage storage - 3
|
||||
battery_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
|
||||
battery_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
|
||||
battery_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
|
||||
battery_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
|
||||
break;
|
||||
case 0x3EB:
|
||||
case 0x40D: //BMS [1s] Charging status of high-voltage storage - 1
|
||||
battery_BEV_available_power_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]) * 3;
|
||||
battery_BEV_available_power_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]) * 3;
|
||||
battery_BEV_available_power_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]) * 3;
|
||||
battery_BEV_available_power_longterm_discharge = (rx_frame.data.u8[7] << 8 | rx_frame.data.u8[6]) * 3;
|
||||
break;
|
||||
case 0x363:
|
||||
case 0x41C: //BMS [1s] Operating Mode Status Of Hybrid - 2
|
||||
battery_status_cooling_HV = (rx_frame.data.u8[1] & 0x03);
|
||||
break;
|
||||
case 0x507:
|
||||
case 0x426: // TODO: Figure out how to trigger sending of this. Does the SME require some CAN command?
|
||||
battery_cellvoltage_mux = rx_frame.data.u8[0];
|
||||
if (battery_cellvoltage_mux == 0) {
|
||||
system_cellvoltages_mV[0] = ((rx_frame.data.u8[1] * 10) + 1800);
|
||||
system_cellvoltages_mV[1] = ((rx_frame.data.u8[2] * 10) + 1800);
|
||||
system_cellvoltages_mV[2] = ((rx_frame.data.u8[3] * 10) + 1800);
|
||||
system_cellvoltages_mV[3] = ((rx_frame.data.u8[4] * 10) + 1800);
|
||||
system_cellvoltages_mV[4] = ((rx_frame.data.u8[5] * 10) + 1800);
|
||||
system_cellvoltages_mV[5] = ((rx_frame.data.u8[6] * 10) + 1800);
|
||||
system_cellvoltages_mV[5] = ((rx_frame.data.u8[7] * 10) + 1800);
|
||||
}
|
||||
break;
|
||||
case 0x41C:
|
||||
case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
|
||||
battery_prediction_voltage_shortterm_charge = (rx_frame.data.u8[1] << 8 | rx_frame.data.u8[0]);
|
||||
battery_prediction_voltage_shortterm_discharge = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
|
||||
battery_prediction_voltage_longterm_charge = (rx_frame.data.u8[5] << 8 | rx_frame.data.u8[4]);
|
||||
battery_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
|
||||
battery_status_service_disconnection_plug = (rx_frame.data.u8[0] & 0x0F);
|
||||
battery_status_measurement_isolation = (rx_frame.data.u8[0] & 0x0C) >> 2;
|
||||
battery_request_abort_charging = (rx_frame.data.u8[0] & 0x30) >> 4;
|
||||
battery_prediction_duration_charging_minutes = (rx_frame.data.u8[3] << 8 | rx_frame.data.u8[2]);
|
||||
battery_prediction_time_end_of_charging_minutes = rx_frame.data.u8[4];
|
||||
battery_energy_content_maximum_kWh = (((rx_frame.data.u8[6] & 0x0F) << 8 | rx_frame.data.u8[5])) / 50;
|
||||
break;
|
||||
case 0x432: //BMS [200ms] SOC% info
|
||||
battery_request_operating_mode = (rx_frame.data.u8[0] & 0x03);
|
||||
battery_target_voltage_in_CV_mode = ((rx_frame.data.u8[1] << 4 | rx_frame.data.u8[0] >> 4)) / 10;
|
||||
battery_request_charging_condition_minimum = (rx_frame.data.u8[2] / 2);
|
||||
battery_request_charging_condition_maximum = (rx_frame.data.u8[3] / 2);
|
||||
battery_display_SOC = (rx_frame.data.u8[4] / 2);
|
||||
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
|
||||
battery_ID2 = rx_frame.data.u8[0];
|
||||
break;
|
||||
case 0x607: //BMS - No use for this message
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -160,32 +558,156 @@ void receive_can_battery(CAN_frame_t rx_frame) {
|
|||
}
|
||||
void send_can_battery() {
|
||||
unsigned long currentMillis = millis();
|
||||
// Send 600ms CAN Message
|
||||
if (currentMillis - previousMillis600 >= interval600) {
|
||||
previousMillis600 = currentMillis;
|
||||
|
||||
ESP32Can.CANWriteFrame(&BMW_512);
|
||||
}
|
||||
if (battery_awake) {
|
||||
//Send 20ms message
|
||||
if (currentMillis - previousMillis20 >= interval20) {
|
||||
previousMillis20 = currentMillis;
|
||||
|
||||
BMW_10B.data.u8[0] = BMW_10B_0[BMW_10B_counter];
|
||||
BMW_10B.data.u8[1] = BMW_10B_1[BMW_10B_counter];
|
||||
BMW_10B_counter++;
|
||||
if (BMW_10B_counter > 14) {
|
||||
BMW_10B_counter = 0;
|
||||
if (startup_counter_contactor < 160) {
|
||||
startup_counter_contactor++;
|
||||
} else { //After 160 messages, turn on the request
|
||||
BMW_10B.data.u8[1] = 0x10; // Close contactors
|
||||
}
|
||||
|
||||
if (system_bms_status == FAULT) {
|
||||
BMW_10B.data.u8[1] = 0x00; // Open contactors (TODO: test if this works)
|
||||
}
|
||||
|
||||
BMW_10B.data.u8[1] = ((BMW_10B.data.u8[1] & 0xF0) + alive_counter_20ms);
|
||||
BMW_10B.data.u8[0] = calculateCRC(BMW_10B, 3, 0x3F);
|
||||
|
||||
alive_counter_20ms = increment_alive_counter(alive_counter_20ms);
|
||||
|
||||
BMW_13E_counter++;
|
||||
BMW_13E.data.u8[4] = BMW_13E_counter;
|
||||
|
||||
ESP32Can.CANWriteFrame(&BMW_10B);
|
||||
}
|
||||
// Send 100ms CAN Message
|
||||
if (currentMillis - previousMillis100 >= interval100) {
|
||||
previousMillis100 = currentMillis;
|
||||
|
||||
BMW_12F.data.u8[1] = ((BMW_12F.data.u8[1] & 0xF0) + alive_counter_100ms);
|
||||
BMW_12F.data.u8[0] = calculateCRC(BMW_12F, 8, 0x60);
|
||||
|
||||
alive_counter_100ms = increment_alive_counter(alive_counter_100ms);
|
||||
|
||||
ESP32Can.CANWriteFrame(&BMW_12F);
|
||||
}
|
||||
// Send 200ms CAN Message
|
||||
if (currentMillis - previousMillis200 >= interval200) {
|
||||
previousMillis200 = currentMillis;
|
||||
|
||||
BMW_19B.data.u8[1] = ((BMW_19B.data.u8[1] & 0xF0) + alive_counter_200ms);
|
||||
BMW_19B.data.u8[0] = calculateCRC(BMW_19B, 8, 0x6C);
|
||||
|
||||
alive_counter_200ms = increment_alive_counter(alive_counter_200ms);
|
||||
|
||||
ESP32Can.CANWriteFrame(&BMW_19B);
|
||||
}
|
||||
// Send 500ms CAN Message
|
||||
if (currentMillis - previousMillis500 >= interval500) {
|
||||
previousMillis500 = currentMillis;
|
||||
|
||||
BMW_30B.data.u8[1] = ((BMW_30B.data.u8[1] & 0xF0) + alive_counter_500ms);
|
||||
BMW_30B.data.u8[0] = calculateCRC(BMW_30B, 8, 0xBE);
|
||||
|
||||
alive_counter_500ms = increment_alive_counter(alive_counter_500ms);
|
||||
|
||||
ESP32Can.CANWriteFrame(&BMW_30B);
|
||||
}
|
||||
// Send 640ms CAN Message
|
||||
if (currentMillis - previousMillis640 >= interval640) {
|
||||
previousMillis640 = currentMillis;
|
||||
|
||||
ESP32Can.CANWriteFrame(&BMW_512); // Keep BMS alive
|
||||
ESP32Can.CANWriteFrame(&BMW_5F8);
|
||||
}
|
||||
// Send 1000ms CAN Message
|
||||
if (currentMillis - previousMillis1000 >= interval1000) {
|
||||
previousMillis1000 = currentMillis;
|
||||
|
||||
BMW_328_counter++; // Used to increment seconds
|
||||
BMW_328.data.u8[0] = BMW_328_counter;
|
||||
BMW_328.data.u8[1] = BMW_328_counter << 8;
|
||||
BMW_328.data.u8[2] = BMW_328_counter << 16;
|
||||
BMW_328.data.u8[3] = BMW_328_counter << 24;
|
||||
|
||||
BMW_1D0.data.u8[1] = ((BMW_1D0.data.u8[1] & 0xF0) + alive_counter_1000ms);
|
||||
BMW_1D0.data.u8[0] = calculateCRC(BMW_1D0, 8, 0xF9);
|
||||
|
||||
BMW_3F9.data.u8[1] = ((BMW_3F9.data.u8[1] & 0xF0) + alive_counter_1000ms);
|
||||
BMW_3F9.data.u8[0] = calculateCRC(BMW_3F9, 8, 0x38);
|
||||
|
||||
BMW_3EC.data.u8[1] = ((BMW_3EC.data.u8[1] & 0xF0) + alive_counter_1000ms);
|
||||
BMW_3EC.data.u8[0] = calculateCRC(BMW_3EC, 8, 0x53);
|
||||
|
||||
BMW_3A7.data.u8[1] = ((BMW_3A7.data.u8[1] & 0xF0) + alive_counter_1000ms);
|
||||
BMW_3A7.data.u8[0] = calculateCRC(BMW_3A7, 8, 0x05);
|
||||
|
||||
alive_counter_1000ms = increment_alive_counter(alive_counter_1000ms);
|
||||
|
||||
ESP32Can.CANWriteFrame(&BMW_3E8); //Order comes from CAN logs
|
||||
ESP32Can.CANWriteFrame(&BMW_328);
|
||||
ESP32Can.CANWriteFrame(&BMW_3F9);
|
||||
ESP32Can.CANWriteFrame(&BMW_2E2);
|
||||
ESP32Can.CANWriteFrame(&BMW_41D);
|
||||
ESP32Can.CANWriteFrame(&BMW_3D0);
|
||||
ESP32Can.CANWriteFrame(&BMW_3CA);
|
||||
ESP32Can.CANWriteFrame(&BMW_3A7);
|
||||
ESP32Can.CANWriteFrame(&BMW_2CA);
|
||||
ESP32Can.CANWriteFrame(&BMW_3FB);
|
||||
ESP32Can.CANWriteFrame(&BMW_418);
|
||||
ESP32Can.CANWriteFrame(&BMW_1D0);
|
||||
ESP32Can.CANWriteFrame(&BMW_3EC);
|
||||
ESP32Can.CANWriteFrame(&BMW_192);
|
||||
ESP32Can.CANWriteFrame(&BMW_13E);
|
||||
ESP32Can.CANWriteFrame(&BMW_433);
|
||||
|
||||
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
|
||||
}
|
||||
// Send 5000ms CAN Message
|
||||
if (currentMillis - previousMillis5000 >= interval5000) {
|
||||
previousMillis5000 = 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);
|
||||
|
||||
ESP32Can.CANWriteFrame(&BMW_3FC); //Order comes from CAN logs
|
||||
ESP32Can.CANWriteFrame(&BMW_3C5);
|
||||
ESP32Can.CANWriteFrame(&BMW_3A0);
|
||||
ESP32Can.CANWriteFrame(&BMW_592_0);
|
||||
ESP32Can.CANWriteFrame(&BMW_592_1);
|
||||
|
||||
alive_counter_5000ms = increment_alive_counter(alive_counter_5000ms);
|
||||
|
||||
if (BMW_380_counter < 3) {
|
||||
ESP32Can.CANWriteFrame(&BMW_380); // This message stops after 3 times on startup
|
||||
BMW_380_counter++;
|
||||
}
|
||||
}
|
||||
// Send 10000ms CAN Message
|
||||
if (currentMillis - previousMillis10000 >= interval10000) {
|
||||
previousMillis10000 = currentMillis;
|
||||
|
||||
ESP32Can.CANWriteFrame(&BMW_3E5); //Order comes from CAN logs
|
||||
ESP32Can.CANWriteFrame(&BMW_3E4);
|
||||
ESP32Can.CANWriteFrame(&BMW_37B);
|
||||
|
||||
BMW_3E5.data.u8[0] = 0xFD; // First 3E5 message byte0 we send is unique, once we sent initial value send this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup_battery(void) { // Performs one time setup at startup
|
||||
Serial.println("BMW i3 battery selected");
|
||||
|
||||
system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
|
||||
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
|
||||
system_min_design_voltage_dV = 2800; // 280.0V under this, discharging further is disabled
|
||||
|
||||
digitalWrite(WUP_PIN, HIGH); // Wake up the battery
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
#define PRECHARGE_PIN 25
|
||||
#endif
|
||||
|
||||
#ifdef BMW_I3_BATTERY
|
||||
#define WUP_PIN 25
|
||||
#endif
|
||||
|
||||
#define SD_MISO_PIN 2
|
||||
#define SD_MOSI_PIN 15
|
||||
#define SD_SCLK_PIN 14
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue