mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
Merge branch 'main' into feature/BYD-unlock-crashed
This commit is contained in:
commit
f205c7f6e1
14 changed files with 330 additions and 107 deletions
|
@ -49,7 +49,7 @@
|
||||||
volatile unsigned long long bmsResetTimeOffset = 0;
|
volatile unsigned long long bmsResetTimeOffset = 0;
|
||||||
|
|
||||||
// The current software version, shown on webserver
|
// The current software version, shown on webserver
|
||||||
const char* version_number = "8.11.dev";
|
const char* version_number = "8.12.dev";
|
||||||
|
|
||||||
// Interval timers
|
// Interval timers
|
||||||
unsigned long previousMillis10ms = 0;
|
unsigned long previousMillis10ms = 0;
|
||||||
|
|
|
@ -195,7 +195,10 @@ static uint8_t alive_counter_5000ms = 0;
|
||||||
static uint8_t BMW_1D0_counter = 0;
|
static uint8_t BMW_1D0_counter = 0;
|
||||||
static uint8_t BMW_13E_counter = 0;
|
static uint8_t BMW_13E_counter = 0;
|
||||||
static uint8_t BMW_380_counter = 0;
|
static uint8_t BMW_380_counter = 0;
|
||||||
static uint32_t BMW_328_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 battery_awake = false;
|
||||||
static bool battery2_awake = false;
|
static bool battery2_awake = false;
|
||||||
|
@ -973,12 +976,23 @@ void transmit_can_battery() {
|
||||||
// Send 1000ms CAN Message
|
// Send 1000ms CAN Message
|
||||||
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
|
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
|
||||||
previousMillis1000 = currentMillis;
|
previousMillis1000 = currentMillis;
|
||||||
|
//BMW_328 byte0-3 = Second Counter (T_SEC_COU_REL) time_second_counter_relative
|
||||||
BMW_328_counter++; // Used to increment seconds
|
// This signal shows the time in seconds since the system time was started (typically in the factory)
|
||||||
BMW_328.data.u8[0] = BMW_328_counter;
|
BMW_328_seconds++; // Used to increment seconds
|
||||||
BMW_328.data.u8[1] = BMW_328_counter << 8;
|
BMW_328.data.u8[0] = (uint8_t)(BMW_328_seconds & 0xFF);
|
||||||
BMW_328.data.u8[2] = BMW_328_counter << 16;
|
BMW_328.data.u8[1] = (uint8_t)((BMW_328_seconds >> 8) & 0xFF);
|
||||||
BMW_328.data.u8[3] = BMW_328_counter << 24;
|
BMW_328.data.u8[2] = (uint8_t)((BMW_328_seconds >> 16) & 0xFF);
|
||||||
|
BMW_328.data.u8[3] = (uint8_t)((BMW_328_seconds >> 24) & 0xFF);
|
||||||
|
//BMW_328 byte 4-5 = Day Counter (T_DAY_COU_ABSL) time_day_counter_absolute
|
||||||
|
//This value goes from 1 ... 65543
|
||||||
|
// Day 1 = 1.1.2000 ... Day 65543 = year 2179
|
||||||
|
BMS_328_seconds_to_day++;
|
||||||
|
if (BMS_328_seconds_to_day > 86400) {
|
||||||
|
BMW_328_days++;
|
||||||
|
BMS_328_seconds_to_day = 0;
|
||||||
|
}
|
||||||
|
BMW_328.data.u8[4] = (uint8_t)(BMW_328_days & 0xFF);
|
||||||
|
BMW_328.data.u8[5] = (uint8_t)((BMW_328_days >> 8) & 0xFF);
|
||||||
|
|
||||||
BMW_1D0.data.u8[1] = ((BMW_1D0.data.u8[1] & 0xF0) + alive_counter_1000ms);
|
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_1D0.data.u8[0] = calculateCRC(BMW_1D0, 8, 0xF9);
|
||||||
|
|
|
@ -22,84 +22,169 @@ enum CmdState { SOH, CELL_VOLTAGE_MINMAX, SOC, CELL_VOLTAGE_CELLNO, CELL_VOLTAGE
|
||||||
static CmdState cmdState = SOC;
|
static CmdState cmdState = SOC;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Suspected Vehicle comms required:
|
SME output:
|
||||||
0x06D DLC? 1000ms - counters?
|
0x12B8D087 5000ms - Extended ID
|
||||||
0x2F1 DLC? 1000ms during run : 0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x00, 0xF3, 0xFF - at startup 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xF3, 0xFF. Suspect byte [4] is a counter
|
0x1D2 DLC8 1000ms
|
||||||
0x439 DLC4 1000ms STATIC
|
0x20B DLC8 1000ms
|
||||||
0x0C0 DLC2 200ms needs counter
|
0x2E2 DLC16 1000ms
|
||||||
0x587 DLC8 appears at startup 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF , 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF, 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF, 0x06 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x82 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF
|
0x31F DLC16 100ms - 2 downward counters?
|
||||||
|
0x3EA DLC8
|
||||||
|
0x453 DLC20 200ms
|
||||||
|
0x486 DLC48 1000ms
|
||||||
|
0x49C DLC8 1000ms
|
||||||
|
0x4A1 DLC8 1000ms
|
||||||
|
0x4BB DLC64 200ms - seems multplexed on [0]
|
||||||
|
0x4D0 DLC64 1000ms - some slow/flickering values - possible change during fault
|
||||||
|
0x507 DLC8
|
||||||
|
0x587 DLC8 - appears at startup 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF , 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF, 0x78 0x07 0x00 0x00 0xFF 0xFF 0xFF 0xFF, 0x06 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x82 0xFF 0xFF 0xFF 0xFF 0xFF, 0x01 0x03 0x80 0xFF 0xFF 0xFF 0xFF 0xFF
|
||||||
|
0x607 - UDS response
|
||||||
|
0x7AB DLC64 - seen at startup
|
||||||
|
0x8F DLC48 10ms - appears to have analog readings like volt/temp/current
|
||||||
|
0xD0D087 DLC4
|
||||||
|
|
||||||
SME Output:
|
BDC output:
|
||||||
0x08F DLC48 10ms - Appears to have analog readings like volt/temp/current
|
0x276 DLC8 - vehicle condition
|
||||||
0x12B8D087 5000ms - Extended ID
|
0x2F1 DLC8 1000ms - during run: 0xFF, 0xFF, 0xFF, 0xFF, 0x9B, 0x00, 0xF3, 0xFF - at startup 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xF3, 0xFF. Suspect byte [4] is a counter
|
||||||
0x1D2 DLC8 1000ms
|
0x439 DLC4 1000ms - STATIC: byte0, byte1. byte3 changes, byte4 = 0xF2 OR 0xF3
|
||||||
0x20B DLC8 1000ms
|
0x4EB DLC8 - RSU condition
|
||||||
0x2E2 DLC16 1000ms
|
0x510 DLC8 100ms - FIXME:(update as this content is not seen in car logs:) STATIC 40 10 40 00 6F DF 19 00 during run - Startup sends this once: 0x40 0x10 0x02 0x00 0x00 0x00 0x00 0x00
|
||||||
0x2F1 DLC8 1000ms
|
0x6D DLC8 1000ms - counters? counter on byte3
|
||||||
0x31F DLC16 100ms - 2 downward counters?
|
0xC0 DLC2 200ms - needs counter
|
||||||
0x453 DLC20 200ms
|
|
||||||
0x486 DLC48 1000ms
|
|
||||||
0x49C DLC8 1000ms
|
|
||||||
0x4A1 DLC8 1000ms
|
|
||||||
0x4BB DLC64 200ms - seems multplexed on [0]
|
|
||||||
0x4D0 DLC64 1000ms - some slow/flickering values - possible change during fault
|
|
||||||
0x510 DLC8 100ms STATIC 40 10 40 00 6F DF 19 00 during run - Startup sends this once: 0x40 0x10 0x02 0x00 0x00 0x00 0x00 0x00
|
|
||||||
0x607 UDS Response
|
|
||||||
|
|
||||||
No vehicle log available, SME asks for:
|
SME asks for:
|
||||||
0x125 (CCU)
|
0x125 (CCU)
|
||||||
0x16E (CCU)
|
0x16E (CCU)
|
||||||
0x340 (CCU)
|
|
||||||
0x4F8 (CCU)
|
|
||||||
0x188 (CCU)
|
0x188 (CCU)
|
||||||
|
0x1A1 (DSC) - vehicle speed (not seen in car logs)
|
||||||
|
0x1EA (KOMBI)
|
||||||
|
0x1FC (FIXME:(add transmitter node.))
|
||||||
|
0x21D (FIXME:(add transmitter node.))
|
||||||
|
0x276 (BDC)
|
||||||
|
0x2ED (FIXME:(add transmitter node.))
|
||||||
|
0x340 (CCU)
|
||||||
|
0x380 (FIXME:(add transmitter node.))
|
||||||
|
0x442 (FIXME:(add transmitter node.))
|
||||||
|
0x4EB (BDC)
|
||||||
|
0x4F8 (CCU)
|
||||||
0x91 (EME1)
|
0x91 (EME1)
|
||||||
0xAA (EME2)
|
0xAA (EME2) - all wheel drive only
|
||||||
0x?? Suspect there is a drive mode flag somewhere - balancing might only be active in some modes
|
0x?? Suspect there is a drive mode flag somewhere - balancing might only be active in some modes
|
||||||
|
|
||||||
TODO
|
TODO:
|
||||||
- Request batt serial number on F1 8C (already parsing RX)
|
- Request batt serial number on F1 8C (already parsing RX)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//Vehicle CAN START
|
//Vehicle CAN START
|
||||||
CAN_frame BMWiX_06D = {
|
CAN_frame BMWiX_125 = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 20,
|
||||||
|
.ID = 0x125,
|
||||||
|
//.data = {TODO:, TODO:, TODO:, TODO:, 0xFE, 0x7F, 0xFE, 0x7F, TODO:, TODO:, TODO:, TODO:, TODO:, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF}
|
||||||
|
}; // CCU output
|
||||||
|
|
||||||
|
/* SME output
|
||||||
|
CAN_frame BMWiX_12B8D087 = {.FD = true,
|
||||||
|
.ext_ID = true,
|
||||||
|
.DLC = 2,
|
||||||
|
.ID = 0x12B8D087,
|
||||||
|
.data = {0xFC, 0xFF}}; // 5000ms SME output - Static values
|
||||||
|
*/
|
||||||
|
|
||||||
|
CAN_frame BMWiX_16E = {
|
||||||
.FD = true,
|
.FD = true,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x06D,
|
.ID = 0x16E,
|
||||||
.data = {
|
// .data = {TODO:, TODO:, TODO: 0xC8 or 0xC9, 0xFF, TODO:, 0xC9, TODO:, TODO:, }
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
}; // CCU output
|
||||||
0xFF}}; // 1000ms BDC Output - [0] static [1,2][3,4] counter x2. 3,4 is 9 higher than 1,2 is needed? [5-7] static
|
|
||||||
|
|
||||||
CAN_frame BMWiX_0C0 = {
|
CAN_frame BMWiX_188 = {.FD = true,
|
||||||
.FD = true,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 2,
|
|
||||||
.ID = 0x0C0,
|
|
||||||
.data = {
|
|
||||||
0xF0,
|
|
||||||
0x00}}; // Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 00 static - MINIMUM ID TO KEEP SME AWAKE
|
|
||||||
|
|
||||||
CAN_frame BMWiX_276 = {.FD = true,
|
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x476,
|
.ID = 0x188,
|
||||||
.data = {0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
|
.data = {0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF}}; // CCU output - values while driving
|
||||||
0xFC}}; // 5000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED
|
|
||||||
|
CAN_frame BMWiX_1EA = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x1EA,
|
||||||
|
//.data = {TODO:km_least_significant, TODO:, TODO:, TODO:, TODO:km_most_significant, 0xFF, TODO:, TODO:}
|
||||||
|
}; // KOMBI output - kilometerstand
|
||||||
|
|
||||||
|
CAN_frame BMWiX_1FC = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x1FC,
|
||||||
|
.data = {0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0xC0,
|
||||||
|
0x00}}; // FIXME:(add transmitter node) output - heat management engine control - static values
|
||||||
|
|
||||||
|
CAN_frame BMWiX_21D = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x21D,
|
||||||
|
// .data = {TODO:, TODO:, TODO:, 0xFF, 0xFF, 0xFF, 0xFF, TODO:}
|
||||||
|
}; // FIXME:(add transmitter node) output - request heating and air conditioning system 1
|
||||||
|
|
||||||
|
CAN_frame BMWiX_276 = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x276,
|
||||||
|
.data = {0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC}}; // 5000ms BDC output - vehicle condition
|
||||||
|
|
||||||
|
CAN_frame BMWiX_2ED = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x2ED,
|
||||||
|
.data = {
|
||||||
|
0x75,
|
||||||
|
0x75}}; // FIXME:(add transmitter node) output - ambient temperature (values seen in logs vary between 0x72 and 0x79)
|
||||||
|
|
||||||
CAN_frame BMWiX_2F1 = {
|
CAN_frame BMWiX_2F1 = {
|
||||||
.FD = true,
|
.FD = true,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x2F1,
|
.ID = 0x2F1,
|
||||||
.data = {0xFF, 0xFF, 0xD0, 0x39, 0x94, 0x00, 0xF3, 0xFF}}; // 1000ms BDC Output - Static values - varies at startup
|
.data = {0xFF, 0xFF, 0xD0, 0x39, 0x94, 0x00, 0xF3, 0xFF}}; // 1000ms BDC output - Static values - varies at startup
|
||||||
|
|
||||||
|
CAN_frame BMWiX_340 = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 12,
|
||||||
|
.ID = 0x340,
|
||||||
|
// .data = {TODO:, TODO:, TODO:, 0xFF, TODO:, TODO:, 0x00, 0x00, TODO:, TODO:, TODO:, 0xFF, 0xFF, }
|
||||||
|
}; // CCU output
|
||||||
|
|
||||||
|
CAN_frame BMWiX_380 = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 7,
|
||||||
|
.ID = 0x380,
|
||||||
|
// .data = {FIXME:(VIN_char1), FIXME:(VIN_char2), FIXME:(VIN_char3), FIXME:(VIN_char4), FIXME:(VIN_char5), FIXME:(VIN_char6), FIXME:(VIN_char7)}
|
||||||
|
}; // FIXME:(add transmitter node) output - VIN: ASCII2HEX
|
||||||
|
|
||||||
|
/* Not requested by SME
|
||||||
CAN_frame BMWiX_439 = {.FD = true,
|
CAN_frame BMWiX_439 = {.FD = true,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 4,
|
.DLC = 4,
|
||||||
.ID = 0x439,
|
.ID = 0x439,
|
||||||
.data = {0xFF, 0x3F, 0xFF, 0xF3}}; // 1000ms BDC Output
|
.data = {0xFF, 0x3F, 0xFF, 0xF3}}; // 1000ms BDC output
|
||||||
|
*/
|
||||||
|
|
||||||
|
CAN_frame BMWiX_442 = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 6,
|
||||||
|
.ID = 0x442,
|
||||||
|
// .data = {TODO: relative time byte 0, TODO: relative time byte1, 0xA9, 0x00, 0xE0, 0x23}
|
||||||
|
}; // FIXME:(add transmitter node) output - relative time BN2020
|
||||||
|
|
||||||
|
/* SME output
|
||||||
CAN_frame
|
CAN_frame
|
||||||
BMWiX_486 =
|
BMWiX_486 =
|
||||||
{
|
{
|
||||||
|
@ -112,14 +197,33 @@ CAN_frame
|
||||||
0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
|
0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE,
|
||||||
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF,
|
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF,
|
||||||
0xFE, 0xFF, 0xFF, 0x7F, 0x33, 0xFD, 0xFD, 0xFD, 0xFD, 0xC0, 0x41, 0xFF, 0xFF,
|
0xFE, 0xFF, 0xFF, 0x7F, 0x33, 0xFD, 0xFD, 0xFD, 0xFD, 0xC0, 0x41, 0xFF, 0xFF,
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; // 1000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; // 1000ms SME output - Suspected keep alive Static CONFIRM NEEDED
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* SME output
|
||||||
CAN_frame BMWiX_49C = {.FD = true,
|
CAN_frame BMWiX_49C = {.FD = true,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 4,
|
.DLC = 4,
|
||||||
.ID = 0x49C,
|
.ID = 0x49C,
|
||||||
.data = {0xD2, 0xF2, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
|
.data = {0xD2, 0xF2, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
0xFF}}; // 1000ms BDC Output - Suspected keep alive Static CONFIRM NEEDED
|
0xFF}}; // 1000ms SME output - Suspected keep alive Static CONFIRM NEEDED
|
||||||
|
*/
|
||||||
|
|
||||||
|
CAN_frame BMWiX_4EB = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x4EB,
|
||||||
|
// .data = {TODO:, TODO:, TODO: 0xE0 or 0xE5 (while driving), 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
|
||||||
|
}; // BDC output - RSU condition
|
||||||
|
|
||||||
|
CAN_frame BMWiX_4F8 = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x4F8,
|
||||||
|
// .data = {0xFF, 0xFD, 0xFF, 0xFF, 0xFF, TODO:, TODO:, 0xC8, 0x00, 0x00, 0xF0, 0x40, 0xFE, 0xFF, 0xFD, 0xFF, TODO:, TODO:, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
|
||||||
|
}; // CCU output
|
||||||
|
|
||||||
CAN_frame BMWiX_510 = {
|
CAN_frame BMWiX_510 = {
|
||||||
.FD = true,
|
.FD = true,
|
||||||
|
@ -127,13 +231,25 @@ CAN_frame BMWiX_510 = {
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x510,
|
.ID = 0x510,
|
||||||
.data = {0x40, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00,
|
.data = {0x40, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00,
|
||||||
0x00}}; // 100ms BDC Output - Values change in car logs, these bytes are the most common
|
0x00}}; // 100ms BDC output - Values change in car logs, these bytes are the most common
|
||||||
|
|
||||||
CAN_frame BMWiX_12B8D087 = {.FD = true,
|
CAN_frame BMWiX_6D = {
|
||||||
.ext_ID = true,
|
.FD = true,
|
||||||
.DLC = 2,
|
.ext_ID = false,
|
||||||
.ID = 0x12B8D087,
|
.DLC = 8,
|
||||||
.data = {0xFC, 0xFF}}; // 5000ms SME Output - Static values
|
.ID = 0x6D,
|
||||||
|
.data = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||||
|
0xFF}}; // 1000ms BDC output - [0] static [1,2][3,4] counter x2. 3,4 is 9 higher than 1,2 is needed? [5-7] static
|
||||||
|
|
||||||
|
CAN_frame BMWiX_C0 = {
|
||||||
|
.FD = true,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 2,
|
||||||
|
.ID = 0xC0,
|
||||||
|
.data = {
|
||||||
|
0xF0,
|
||||||
|
0x00}}; // BDC output - Keep Alive 2 BDC>SME 200ms First byte cycles F0 > FE second byte 00 static - MINIMUM ID TO KEEP SME AWAKE
|
||||||
//Vehicle CAN END
|
//Vehicle CAN END
|
||||||
|
|
||||||
//Request Data CAN START
|
//Request Data CAN START
|
||||||
|
@ -294,12 +410,6 @@ CAN_frame BMWiX_6F4_CONTINUE_DATA = {.FD = true,
|
||||||
.data = {0x07, 0x30, 0x00, 0x02}};
|
.data = {0x07, 0x30, 0x00, 0x02}};
|
||||||
|
|
||||||
//Action Requests:
|
//Action Requests:
|
||||||
CAN_frame BMW_10B = {.FD = true,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 3,
|
|
||||||
.ID = 0x10B,
|
|
||||||
.data = {0xCD, 0x00, 0xFC}}; // Contactor closing command?
|
|
||||||
|
|
||||||
CAN_frame BMWiX_6F4_CELL_SOC = {.FD = true,
|
CAN_frame BMWiX_6F4_CELL_SOC = {.FD = true,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 5,
|
.DLC = 5,
|
||||||
|
@ -383,9 +493,6 @@ static uint8_t uds_req_id_counter = 0;
|
||||||
static uint8_t detected_number_of_cells = 108;
|
static uint8_t detected_number_of_cells = 108;
|
||||||
const unsigned long STALE_PERIOD =
|
const unsigned long STALE_PERIOD =
|
||||||
STALE_PERIOD_CONFIG; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds)
|
STALE_PERIOD_CONFIG; // Time in milliseconds to check for staleness (e.g., 5000 ms = 5 seconds)
|
||||||
|
|
||||||
static byte iX_0C0_counter = 0xF0; // Initialize to 0xF0
|
|
||||||
|
|
||||||
//End iX Intermediate vars
|
//End iX Intermediate vars
|
||||||
|
|
||||||
static uint8_t current_cell_polled = 0;
|
static uint8_t current_cell_polled = 0;
|
||||||
|
@ -422,7 +529,7 @@ static uint8_t increment_alive_counter(uint8_t counter) {
|
||||||
return counter;
|
return counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte increment_0C0_counter(byte counter) {
|
static byte increment_C0_counter(byte counter) {
|
||||||
counter++;
|
counter++;
|
||||||
// Reset to 0xF0 if it exceeds 0xFE
|
// Reset to 0xF0 if it exceeds 0xFE
|
||||||
if (counter > 0xFE) {
|
if (counter > 0xFE) {
|
||||||
|
@ -752,17 +859,12 @@ void transmit_can_battery() {
|
||||||
previousMillis200 = currentMillis;
|
previousMillis200 = currentMillis;
|
||||||
|
|
||||||
//Send SME Keep alive values 200ms
|
//Send SME Keep alive values 200ms
|
||||||
BMWiX_0C0.data.u8[0] = increment_0C0_counter(BMWiX_0C0.data.u8[0]); //Keep Alive 1
|
BMWiX_C0.data.u8[0] = increment_C0_counter(BMWiX_C0.data.u8[0]); //Keep Alive 1
|
||||||
transmit_can_frame(&BMWiX_0C0, can_config.battery);
|
transmit_can_frame(&BMWiX_C0, can_config.battery);
|
||||||
}
|
}
|
||||||
// Send 1000ms CAN Message
|
// Send 1000ms CAN Message
|
||||||
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
|
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
|
||||||
previousMillis1000 = currentMillis;
|
previousMillis1000 = currentMillis;
|
||||||
|
|
||||||
//Send SME Keep alive values 1000ms
|
|
||||||
//Don't believe this is needed: transmit_can_frame(&BMWiX_06D, can_config.battery);
|
|
||||||
//Don't believe this is needed: transmit_can_frame(&BMWiX_2F1, can_config.battery);
|
|
||||||
//Don't believe this is needed: transmit_can_frame(&BMWiX_439, can_config.battery);
|
|
||||||
}
|
}
|
||||||
// Send 5000ms CAN Message
|
// Send 5000ms CAN Message
|
||||||
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
|
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
|
||||||
|
|
|
@ -5,12 +5,6 @@
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "CMFA-EV-BATTERY.h"
|
#include "CMFA-EV-BATTERY.h"
|
||||||
|
|
||||||
/* TODO:
|
|
||||||
Integration considered stable! Following points can still be improved:
|
|
||||||
- Cellvoltage Min missing. Value now mapped to same as Cellvoltage Max
|
|
||||||
- All individual cellvoltages can not yet be viewed in the cellmonitor
|
|
||||||
/*
|
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
CAN_frame CMFA_1EA = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x1EA, .data = {0x00}};
|
CAN_frame CMFA_1EA = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x1EA, .data = {0x00}};
|
||||||
CAN_frame CMFA_125 = {.FD = false,
|
CAN_frame CMFA_125 = {.FD = false,
|
||||||
|
@ -75,9 +69,12 @@ static unsigned long previousMillis200ms = 0;
|
||||||
static unsigned long previousMillis100ms = 0;
|
static unsigned long previousMillis100ms = 0;
|
||||||
static unsigned long previousMillis10ms = 0;
|
static unsigned long previousMillis10ms = 0;
|
||||||
|
|
||||||
|
#define MAXSOC 9000 //90.00 Raw SOC displays this value when battery is at 100%
|
||||||
|
#define MINSOC 500 //5.00 Raw SOC displays this value when battery is at 0%
|
||||||
|
|
||||||
static uint8_t heartbeat = 0; //Alternates between 0x55 and 0xAA every 5th frame
|
static uint8_t heartbeat = 0; //Alternates between 0x55 and 0xAA every 5th frame
|
||||||
static uint8_t heartbeat2 = 0; //Alternates between 0x55 and 0xAA every 5th frame
|
static uint8_t heartbeat2 = 0; //Alternates between 0x55 and 0xAA every 5th frame
|
||||||
static uint32_t SOC = 0;
|
static uint32_t SOC_raw = 0;
|
||||||
static uint16_t SOH = 99;
|
static uint16_t SOH = 99;
|
||||||
static int16_t current = 0;
|
static int16_t current = 0;
|
||||||
static uint16_t pack_voltage = 2700;
|
static uint16_t pack_voltage = 2700;
|
||||||
|
@ -86,10 +83,30 @@ static int16_t lowest_cell_temperature = 0;
|
||||||
static uint32_t discharge_power_w = 0;
|
static uint32_t discharge_power_w = 0;
|
||||||
static uint32_t charge_power_w = 0;
|
static uint32_t charge_power_w = 0;
|
||||||
|
|
||||||
|
/* The raw SOC value sits at 90% when the battery is full, so we should report back 100% once this value is reached
|
||||||
|
Same goes for low point, when 10% is reached we report 0% */
|
||||||
|
|
||||||
|
uint16_t rescale_raw_SOC(uint32_t raw_SOC) {
|
||||||
|
|
||||||
|
uint32_t calc_soc;
|
||||||
|
calc_soc = (raw_SOC * 0.25);
|
||||||
|
if (calc_soc > MAXSOC) { //Constrain if needed
|
||||||
|
calc_soc = MAXSOC;
|
||||||
|
}
|
||||||
|
if (calc_soc < MINSOC) { //Constrain if needed
|
||||||
|
calc_soc = MINSOC;
|
||||||
|
}
|
||||||
|
// Perform scaling between the two points
|
||||||
|
calc_soc = 10000 * (calc_soc - MINSOC);
|
||||||
|
calc_soc = calc_soc / (MAXSOC - MINSOC);
|
||||||
|
|
||||||
|
return (uint16_t)calc_soc;
|
||||||
|
}
|
||||||
|
|
||||||
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
datalayer.battery.status.soh_pptt = (SOH * 100);
|
datalayer.battery.status.soh_pptt = (SOH * 100);
|
||||||
|
|
||||||
datalayer.battery.status.real_soc = (SOC * 0.25);
|
datalayer.battery.status.real_soc = rescale_raw_SOC(SOC_raw);
|
||||||
|
|
||||||
datalayer.battery.status.current_dA = current * 10;
|
datalayer.battery.status.current_dA = current * 10;
|
||||||
|
|
||||||
|
@ -145,7 +162,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
case 0x127: //10ms , Same structure as old Zoe 0x155 message!
|
case 0x127: //10ms , Same structure as old Zoe 0x155 message!
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
current = (((((rx_frame.data.u8[1] & 0x0F) << 8) | rx_frame.data.u8[2]) * 0.25) - 500);
|
current = (((((rx_frame.data.u8[1] & 0x0F) << 8) | rx_frame.data.u8[2]) * 0.25) - 500);
|
||||||
SOC = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
SOC_raw = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||||
break;
|
break;
|
||||||
case 0x3D6: //100ms, Same structure as old Zoe 0x424 message!
|
case 0x3D6: //100ms, Same structure as old Zoe 0x424 message!
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
#define MAX_PACK_VOLTAGE_DV 3040 //5000 = 500.0V
|
#define MAX_PACK_VOLTAGE_DV 3040 //5000 = 500.0V
|
||||||
#define MIN_PACK_VOLTAGE_DV 2150
|
#define MIN_PACK_VOLTAGE_DV 2185
|
||||||
#define MAX_CELL_DEVIATION_MV 100
|
#define MAX_CELL_DEVIATION_MV 100
|
||||||
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
|
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
|
||||||
#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value
|
#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value
|
||||||
|
|
|
@ -74,15 +74,16 @@ const uint16_t SOC[] = {10000, 9900, 9800, 9700, 9600, 9500, 9400, 9300, 9200, 9
|
||||||
2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100,
|
2500, 2400, 2300, 2200, 2100, 2000, 1900, 1800, 1700, 1600, 1500, 1400, 1300, 1200, 1100,
|
||||||
1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0};
|
1000, 900, 800, 700, 600, 500, 400, 300, 200, 100, 0};
|
||||||
|
|
||||||
const uint16_t voltage[] = {4200, 4171, 4143, 4117, 4093, 4070, 4050, 4031, 4013, 3998, 3985, 3973, 3964, 3957, 3952,
|
const uint16_t voltage[] = {4200, 4173, 4148, 4124, 4102, 4080, 4060, 4041, 4023, 4007, 3993, 3980, 3969, 3959, 3953,
|
||||||
3950, 3941, 3933, 3924, 3916, 3907, 3899, 3890, 3881, 3873, 3864, 3856, 3847, 3839, 3830,
|
3950, 3941, 3932, 3924, 3915, 3907, 3898, 3890, 3881, 3872, 3864, 3855, 3847, 3838, 3830,
|
||||||
3821, 3813, 3804, 3796, 3787, 3779, 3770, 3761, 3753, 3744, 3736, 3727, 3719, 3710, 3701,
|
3821, 3812, 3804, 3795, 3787, 3778, 3770, 3761, 3752, 3744, 3735, 3727, 3718, 3710, 3701,
|
||||||
3693, 3684, 3676, 3667, 3659, 3650, 3641, 3633, 3624, 3616, 3607, 3599, 3590, 3581, 3573,
|
3692, 3684, 3675, 3667, 3658, 3650, 3641, 3632, 3624, 3615, 3607, 3598, 3590, 3581, 3572,
|
||||||
3564, 3556, 3547, 3539, 3530, 3521, 3513, 3504, 3496, 3487, 3479, 3470, 3461, 3453, 3444,
|
3564, 3555, 3547, 3538, 3530, 3521, 3512, 3504, 3495, 3487, 3478, 3470, 3461, 3452, 3444,
|
||||||
3436, 3427, 3419, 3410, 3401, 3393, 3384, 3376, 3367, 3359, 3350, 3333, 3315, 3297, 3278,
|
3435, 3427, 3418, 3410, 3401, 3392, 3384, 3375, 3367, 3358, 3350, 3338, 3325, 3313, 3299,
|
||||||
3258, 3237, 3215, 3192, 3166, 3139, 3108, 3074, 3033, 2979, 2850};
|
3285, 3271, 3255, 3239, 3221, 3202, 3180, 3156, 3127, 3090, 3000};
|
||||||
|
|
||||||
uint16_t estimateSOC(uint16_t cellVoltage) { // Linear interpolation function
|
// Function to estimate SOC based on cell voltage
|
||||||
|
uint16_t estimateSOCFromCell(uint16_t cellVoltage) {
|
||||||
if (cellVoltage >= voltage[0]) {
|
if (cellVoltage >= voltage[0]) {
|
||||||
return SOC[0];
|
return SOC[0];
|
||||||
}
|
}
|
||||||
|
@ -92,7 +93,7 @@ uint16_t estimateSOC(uint16_t cellVoltage) { // Linear interpolation function
|
||||||
|
|
||||||
for (int i = 1; i < numPoints; ++i) {
|
for (int i = 1; i < numPoints; ++i) {
|
||||||
if (cellVoltage >= voltage[i]) {
|
if (cellVoltage >= voltage[i]) {
|
||||||
// Fix: Cast to float or double to ensure proper floating-point division
|
// Cast to float for proper division
|
||||||
float t = (float)(cellVoltage - voltage[i]) / (float)(voltage[i - 1] - voltage[i]);
|
float t = (float)(cellVoltage - voltage[i]) / (float)(voltage[i - 1] - voltage[i]);
|
||||||
|
|
||||||
// Calculate interpolated SOC value
|
// Calculate interpolated SOC value
|
||||||
|
@ -105,12 +106,56 @@ uint16_t estimateSOC(uint16_t cellVoltage) { // Linear interpolation function
|
||||||
return 0; // Default return for safety, should never reach here
|
return 0; // Default return for safety, should never reach here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simplified version of the pack-based SOC estimation with compensation
|
||||||
|
uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps) {
|
||||||
|
// If cell count is still the default 192 but we haven't confirmed it yet
|
||||||
|
if (!set_voltage_limits && cellCount == 192) {
|
||||||
|
// Fall back to BMS-reported SOC while cell count is uncertain
|
||||||
|
return (SOC_Display * 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cellCount == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Convert pack voltage (decivolts) to millivolts
|
||||||
|
uint32_t packVoltageMv = packVoltage * 100;
|
||||||
|
|
||||||
|
// Apply internal resistance compensation
|
||||||
|
// Current is in deciamps (-150 = -15.0A, 150 = 15.0A)
|
||||||
|
// Resistance is in milliohms
|
||||||
|
int32_t voltageDrop = (currentAmps * PACK_INTERNAL_RESISTANCE_MOHM) / 10;
|
||||||
|
|
||||||
|
// Compensate the pack voltage (add the voltage drop)
|
||||||
|
uint32_t compensatedPackVoltageMv = packVoltageMv + voltageDrop;
|
||||||
|
|
||||||
|
// Calculate average cell voltage in millivolts
|
||||||
|
uint16_t avgCellVoltage = compensatedPackVoltageMv / cellCount;
|
||||||
|
|
||||||
|
#ifdef DEBUG_LOG
|
||||||
|
logging.print("Pack: ");
|
||||||
|
logging.print(packVoltage / 10.0);
|
||||||
|
logging.print("V, Current: ");
|
||||||
|
logging.print(currentAmps / 10.0);
|
||||||
|
logging.print("A, Drop: ");
|
||||||
|
logging.print(voltageDrop / 1000.0);
|
||||||
|
logging.print("V, Comp Pack: ");
|
||||||
|
logging.print(compensatedPackVoltageMv / 1000.0);
|
||||||
|
logging.print("V, Avg Cell: ");
|
||||||
|
logging.print(avgCellVoltage);
|
||||||
|
logging.println("mV");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Use the cell voltage lookup table to estimate SOC
|
||||||
|
return estimateSOCFromCell(avgCellVoltage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix: Change parameter types to uint16_t to match SOC values
|
||||||
uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high) {
|
uint16_t selectSOC(uint16_t SOC_low, uint16_t SOC_high) {
|
||||||
if (SOC_low == 0 || SOC_high == 0) {
|
if (SOC_low == 0 || SOC_high == 0) {
|
||||||
return 0; // If either value is 0, return 0
|
return 0; // If either value is 0, return 0
|
||||||
}
|
}
|
||||||
if (SOC_low == 10000 || SOC_high == 10000) {
|
if (SOC_low == 10000 || SOC_high == 10000) {
|
||||||
return 10000; // If either value is 100, return 100
|
return 10000; // If either value is 100%, return 100%
|
||||||
}
|
}
|
||||||
return (SOC_low < SOC_high) ? SOC_low : SOC_high; // Otherwise, return the lowest value
|
return (SOC_low < SOC_high) ? SOC_low : SOC_high; // Otherwise, return the lowest value
|
||||||
}
|
}
|
||||||
|
@ -687,9 +732,12 @@ static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_
|
||||||
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
|
|
||||||
#ifdef ESTIMATE_SOC_FROM_CELLVOLTAGE
|
#ifdef ESTIMATE_SOC_FROM_CELLVOLTAGE
|
||||||
SOC_estimated_lowest = estimateSOC(CellVoltMin_mV);
|
// Use the simplified pack-based SOC estimation with proper compensation
|
||||||
SOC_estimated_highest = estimateSOC(CellVoltMax_mV);
|
datalayer.battery.status.real_soc = estimateSOC(batteryVoltage, datalayer.battery.info.number_of_cells, batteryAmps);
|
||||||
datalayer.battery.status.real_soc = selectSOC(SOC_estimated_lowest, SOC_estimated_highest);
|
|
||||||
|
// For comparison or fallback, we can still calculate from min/max cell voltages
|
||||||
|
SOC_estimated_lowest = estimateSOCFromCell(CellVoltMin_mV);
|
||||||
|
SOC_estimated_highest = estimateSOCFromCell(CellVoltMax_mV);
|
||||||
#else
|
#else
|
||||||
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
|
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,6 +19,10 @@ extern ACAN2517FD canfd;
|
||||||
#define RAMPDOWN_SOC 9000 // 90.00 SOC% to start ramping down from max charge power towards 0 at 100.00%
|
#define RAMPDOWN_SOC 9000 // 90.00 SOC% to start ramping down from max charge power towards 0 at 100.00%
|
||||||
#define RAMPDOWNPOWERALLOWED 10000 // What power we ramp down from towards top balancing
|
#define RAMPDOWNPOWERALLOWED 10000 // What power we ramp down from towards top balancing
|
||||||
|
|
||||||
|
// Used for SoC compensation - Define internal resistance value in milliohms for the entire pack
|
||||||
|
// How to calculate: voltage_drop_under_known_load [Volts] / load [Amps] = Resistance
|
||||||
|
#define PACK_INTERNAL_RESISTANCE_MOHM 200 // 200 milliohms for the whole pack
|
||||||
|
|
||||||
void setup_battery(void);
|
void setup_battery(void);
|
||||||
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
||||||
|
|
||||||
|
|
|
@ -704,6 +704,8 @@ typedef struct {
|
||||||
} DATALAYER_INFO_VOLVO_HYBRID;
|
} DATALAYER_INFO_VOLVO_HYBRID;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
/** User requesting NVROL reset via WebUI*/
|
||||||
|
bool UserRequestNVROLReset = false;
|
||||||
/** uint16_t */
|
/** uint16_t */
|
||||||
/** Values WIP*/
|
/** Values WIP*/
|
||||||
uint16_t battery_soc = 0;
|
uint16_t battery_soc = 0;
|
||||||
|
|
|
@ -19,6 +19,13 @@ battery_pause_status emulator_pause_status = NORMAL;
|
||||||
//battery pause status end
|
//battery pause status end
|
||||||
|
|
||||||
void update_machineryprotection() {
|
void update_machineryprotection() {
|
||||||
|
// Check if the CPU is too hot
|
||||||
|
if (datalayer.system.info.CPU_temperature > 80.0f) {
|
||||||
|
set_event(EVENT_CPU_OVERHEAT, 0);
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_CPU_OVERHEAT);
|
||||||
|
}
|
||||||
|
|
||||||
// Check health status of CAN interfaces
|
// Check health status of CAN interfaces
|
||||||
if (datalayer.system.info.can_native_send_fail) {
|
if (datalayer.system.info.can_native_send_fail) {
|
||||||
set_event(EVENT_CAN_NATIVE_TX_FAILURE, 0);
|
set_event(EVENT_CAN_NATIVE_TX_FAILURE, 0);
|
||||||
|
@ -72,6 +79,7 @@ void update_machineryprotection() {
|
||||||
// Battery voltage is over designed max voltage!
|
// Battery voltage is over designed max voltage!
|
||||||
if (datalayer.battery.status.voltage_dV > datalayer.battery.info.max_design_voltage_dV) {
|
if (datalayer.battery.status.voltage_dV > datalayer.battery.info.max_design_voltage_dV) {
|
||||||
set_event(EVENT_BATTERY_OVERVOLTAGE, datalayer.battery.status.voltage_dV);
|
set_event(EVENT_BATTERY_OVERVOLTAGE, datalayer.battery.status.voltage_dV);
|
||||||
|
datalayer.battery.status.max_charge_power_W = 0;
|
||||||
} else {
|
} else {
|
||||||
clear_event(EVENT_BATTERY_OVERVOLTAGE);
|
clear_event(EVENT_BATTERY_OVERVOLTAGE);
|
||||||
}
|
}
|
||||||
|
@ -79,6 +87,7 @@ void update_machineryprotection() {
|
||||||
// Battery voltage is under designed min voltage!
|
// Battery voltage is under designed min voltage!
|
||||||
if (datalayer.battery.status.voltage_dV < datalayer.battery.info.min_design_voltage_dV) {
|
if (datalayer.battery.status.voltage_dV < datalayer.battery.info.min_design_voltage_dV) {
|
||||||
set_event(EVENT_BATTERY_UNDERVOLTAGE, datalayer.battery.status.voltage_dV);
|
set_event(EVENT_BATTERY_UNDERVOLTAGE, datalayer.battery.status.voltage_dV);
|
||||||
|
datalayer.battery.status.max_discharge_power_W = 0;
|
||||||
} else {
|
} else {
|
||||||
clear_event(EVENT_BATTERY_UNDERVOLTAGE);
|
clear_event(EVENT_BATTERY_UNDERVOLTAGE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ void init_events(void) {
|
||||||
events.entries[EVENT_CAN_CHARGER_MISSING].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_CAN_CHARGER_MISSING].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_CAN_INVERTER_MISSING].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_CAN_INVERTER_MISSING].level = EVENT_LEVEL_WARNING;
|
||||||
events.entries[EVENT_CONTACTOR_WELDED].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_CONTACTOR_WELDED].level = EVENT_LEVEL_WARNING;
|
||||||
|
events.entries[EVENT_CPU_OVERHEAT].level = EVENT_LEVEL_WARNING;
|
||||||
events.entries[EVENT_WATER_INGRESS].level = EVENT_LEVEL_ERROR;
|
events.entries[EVENT_WATER_INGRESS].level = EVENT_LEVEL_ERROR;
|
||||||
events.entries[EVENT_CHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_CHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_DISCHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_DISCHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO;
|
||||||
|
@ -191,6 +192,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
|
||||||
return "Inverter not sending messages via CAN for the last 60 seconds. Check wiring!";
|
return "Inverter not sending messages via CAN for the last 60 seconds. Check wiring!";
|
||||||
case EVENT_CONTACTOR_WELDED:
|
case EVENT_CONTACTOR_WELDED:
|
||||||
return "Contactors sticking/welded. Inspect battery with caution!";
|
return "Contactors sticking/welded. Inspect battery with caution!";
|
||||||
|
case EVENT_CPU_OVERHEAT:
|
||||||
|
return "Battery-Emulator CPU overheating! Increase airflow/cooling to increase hardware lifespan!";
|
||||||
case EVENT_CHARGE_LIMIT_EXCEEDED:
|
case EVENT_CHARGE_LIMIT_EXCEEDED:
|
||||||
return "Inverter is charging faster than battery is allowing.";
|
return "Inverter is charging faster than battery is allowing.";
|
||||||
case EVENT_DISCHARGE_LIMIT_EXCEEDED:
|
case EVENT_DISCHARGE_LIMIT_EXCEEDED:
|
||||||
|
@ -348,7 +351,7 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
|
||||||
case EVENT_MQTT_DISCONNECT:
|
case EVENT_MQTT_DISCONNECT:
|
||||||
return "MQTT disconnected.";
|
return "MQTT disconnected.";
|
||||||
case EVENT_EQUIPMENT_STOP:
|
case EVENT_EQUIPMENT_STOP:
|
||||||
return "EQUIPMENT STOP ACTIVATED!!!";
|
return "User requested stop, either via equipment stop circuit or webserver Open Contactor button";
|
||||||
case EVENT_SD_INIT_FAILED:
|
case EVENT_SD_INIT_FAILED:
|
||||||
return "SD card initialization failed, check hardware. Power must be removed to reset the SD card.";
|
return "SD card initialization failed, check hardware. Power must be removed to reset the SD card.";
|
||||||
case EVENT_PERIODIC_BMS_RESET:
|
case EVENT_PERIODIC_BMS_RESET:
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
XX(EVENT_CAN_NATIVE_TX_FAILURE) \
|
XX(EVENT_CAN_NATIVE_TX_FAILURE) \
|
||||||
XX(EVENT_CHARGE_LIMIT_EXCEEDED) \
|
XX(EVENT_CHARGE_LIMIT_EXCEEDED) \
|
||||||
XX(EVENT_CONTACTOR_WELDED) \
|
XX(EVENT_CONTACTOR_WELDED) \
|
||||||
|
XX(EVENT_CPU_OVERHEAT) \
|
||||||
XX(EVENT_DISCHARGE_LIMIT_EXCEEDED) \
|
XX(EVENT_DISCHARGE_LIMIT_EXCEEDED) \
|
||||||
XX(EVENT_WATER_INGRESS) \
|
XX(EVENT_WATER_INGRESS) \
|
||||||
XX(EVENT_12V_LOW) \
|
XX(EVENT_12V_LOW) \
|
||||||
|
|
|
@ -1235,6 +1235,7 @@ String advanced_battery_processor(const String& var) {
|
||||||
#endif //MEB_BATTERY
|
#endif //MEB_BATTERY
|
||||||
|
|
||||||
#ifdef RENAULT_ZOE_GEN2_BATTERY
|
#ifdef RENAULT_ZOE_GEN2_BATTERY
|
||||||
|
content += "<button onclick='askTriggerNVROL()'>Perform NVROL reset</button>";
|
||||||
content += "<h4>soc: " + String(datalayer_extended.zoePH2.battery_soc) + "</h4>";
|
content += "<h4>soc: " + String(datalayer_extended.zoePH2.battery_soc) + "</h4>";
|
||||||
content += "<h4>usable soc: " + String(datalayer_extended.zoePH2.battery_usable_soc) + "</h4>";
|
content += "<h4>usable soc: " + String(datalayer_extended.zoePH2.battery_usable_soc) + "</h4>";
|
||||||
content += "<h4>soh: " + String(datalayer_extended.zoePH2.battery_soh) + "</h4>";
|
content += "<h4>soh: " + String(datalayer_extended.zoePH2.battery_soh) + "</h4>";
|
||||||
|
@ -1514,6 +1515,18 @@ String advanced_battery_processor(const String& var) {
|
||||||
content += "function goToMainPage() { window.location.href = '/'; }";
|
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||||
content += "</script>";
|
content += "</script>";
|
||||||
content += "<script>";
|
content += "<script>";
|
||||||
|
content +=
|
||||||
|
"function askTriggerNVROL() { if (window.confirm('Are you sure you want to trigger "
|
||||||
|
"an NVROL reset? Battery will be unavailable for 30 seconds while this is active!')) { "
|
||||||
|
"TriggerNVROL(); } }";
|
||||||
|
content += "function TriggerNVROL() {";
|
||||||
|
content += " var xhr = new XMLHttpRequest();";
|
||||||
|
content += " xhr.open('GET', '/triggerNVROL', true);";
|
||||||
|
content += " xhr.send();";
|
||||||
|
content += "}";
|
||||||
|
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||||
|
content += "</script>";
|
||||||
|
content += "<script>";
|
||||||
content +=
|
content +=
|
||||||
"function askResetSOH() { if (window.confirm('Are you sure you want to reset degradation data? "
|
"function askResetSOH() { if (window.confirm('Are you sure you want to reset degradation data? "
|
||||||
"Note this should only be used on 2011-2017 24/30kWh batteries!')) { "
|
"Note this should only be used on 2011-2017 24/30kWh batteries!')) { "
|
||||||
|
|
|
@ -588,6 +588,15 @@ void init_webserver() {
|
||||||
request->send(200, "text/plain", "Updated successfully");
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Route for triggering NVROL reset on Zoe Gen2 batteries
|
||||||
|
server.on("/triggerNVROL", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
|
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||||
|
return request->requestAuthentication();
|
||||||
|
}
|
||||||
|
datalayer_extended.zoePH2.UserRequestNVROLReset = true;
|
||||||
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
|
});
|
||||||
|
|
||||||
// Route for resetting SOH on Nissan LEAF batteries
|
// Route for resetting SOH on Nissan LEAF batteries
|
||||||
server.on("/resetSOH", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/resetSOH", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||||
|
|
|
@ -30,8 +30,9 @@ void handle_static_data_modbus_byd() {
|
||||||
static uint16_t byd_data[] = {16985, 17408, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
static uint16_t byd_data[] = {16985, 17408, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
static uint16_t battery_data[] = {16985, 17440, 16993, 29812, 25970, 31021, 17007, 30752,
|
static uint16_t battery_data[] = {16985, 17440, 16993, 29812, 25970, 31021, 17007, 30752,
|
||||||
20594, 25965, 26997, 27936, 18518, 0, 0, 0};
|
20594, 25965, 26997, 27936, 18518, 0, 0, 0};
|
||||||
static uint16_t volt_data[] = {13614, 12288, 0, 0, 0, 0, 0, 0, 13102, 12598, 0, 0, 0, 0, 0, 0};
|
static uint16_t volt_data[] = {13614, 12288, 0, 0, 0, 0, 0, 0, 13102, 12854, 0, 0, 0, 0, 0, 0};
|
||||||
static uint16_t serial_data[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
static uint16_t serial_data[] = {20528, 13104, 21552, 12848, 23089, 14641, 12593, 14384,
|
||||||
|
12336, 12337, 0, 0, 0, 0, 0, 0};
|
||||||
static uint16_t static_data[] = {1, 0};
|
static uint16_t static_data[] = {1, 0};
|
||||||
static uint16_t* data_array_pointers[] = {si_data, byd_data, battery_data, volt_data, serial_data, static_data};
|
static uint16_t* data_array_pointers[] = {si_data, byd_data, battery_data, volt_data, serial_data, static_data};
|
||||||
static uint16_t data_sizes[] = {sizeof(si_data), sizeof(byd_data), sizeof(battery_data),
|
static uint16_t data_sizes[] = {sizeof(si_data), sizeof(byd_data), sizeof(battery_data),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue