mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
BMW iX: add functionality to close and open contactors, including webserver buttons to close and open contactors
This commit is contained in:
parent
eb293b38d4
commit
f3c250368a
5 changed files with 489 additions and 34 deletions
|
@ -6,11 +6,13 @@
|
|||
#include "BMW-IX-BATTERY.h"
|
||||
|
||||
/* Do not change code below unless you are sure what you are doing */
|
||||
static unsigned long previousMillis10 = 0; // will store last time a 20ms CAN Message was send
|
||||
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 600ms 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
|
||||
|
@ -19,6 +21,27 @@ enum CmdState { SOH, CELL_VOLTAGE_MINMAX, SOC, CELL_VOLTAGE_CELLNO, CELL_VOLTAGE
|
|||
|
||||
static CmdState cmdState = SOC;
|
||||
|
||||
static bool battery_awake = false;
|
||||
|
||||
bool contactorCloseReq = false;
|
||||
|
||||
struct ContactorCloseRequestStruct {
|
||||
bool previous;
|
||||
bool present;
|
||||
} ContactorCloseRequest = {false, false};
|
||||
|
||||
struct ContactorStateStruct {
|
||||
bool closed;
|
||||
bool open;
|
||||
};
|
||||
ContactorStateStruct ContactorState = {false, true};
|
||||
|
||||
struct InverterContactorCloseRequestStruct {
|
||||
bool previous;
|
||||
bool present;
|
||||
};
|
||||
InverterContactorCloseRequestStruct InverterContactorCloseRequest = {false, false};
|
||||
|
||||
/*
|
||||
SME output:
|
||||
0x12B8D087 5000ms - Extended ID
|
||||
|
@ -89,13 +112,17 @@ CAN_frame BMWiX_12B8D087 = {.FD = true,
|
|||
.data = {0xFC, 0xFF}}; // 5000ms SME output - Static values
|
||||
*/
|
||||
|
||||
CAN_frame BMWiX_16E = {
|
||||
.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x16E,
|
||||
// .data = {TODO:, TODO:, TODO: 0xC8 or 0xC9, 0xFF, TODO:, 0xC9, TODO:, TODO:, }
|
||||
}; // CCU output
|
||||
CAN_frame BMWiX_16E = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x16E,
|
||||
.data = {0x00, // Almost any possible number in 0x00 and 0xFF
|
||||
0xA0, // Almost any possible number in 0xA0 and 0xAF
|
||||
0xC9, 0xFF,
|
||||
0x60, // FIXME: find out what this value represents
|
||||
0xC9,
|
||||
0x3A, // 0x3A to close contactors, 0x33 to open contactors
|
||||
0xF7}}; // 0xF7 to close contactors, 0xF0 to open contactors // CCU output.
|
||||
|
||||
CAN_frame BMWiX_188 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
|
@ -127,12 +154,12 @@ CAN_frame BMWiX_21D = {
|
|||
// .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_276 = {.FD = true,
|
||||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x276,
|
||||
.data = {0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFD}}; // BDC output - vehicle condition. Used for contactor closing
|
||||
|
||||
CAN_frame BMWiX_2ED = {
|
||||
.FD = true,
|
||||
|
@ -228,8 +255,13 @@ CAN_frame BMWiX_510 = {
|
|||
.ext_ID = false,
|
||||
.DLC = 8,
|
||||
.ID = 0x510,
|
||||
.data = {0x40, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00,
|
||||
0x00}}; // 100ms BDC output - Values change in car logs, these bytes are the most common
|
||||
.data = {
|
||||
0x40, 0x10,
|
||||
0x04, // 0x02 at contactor closing, afterwards 0x04 and 0x10, 0x00 to open contactors
|
||||
0x00, 0x00,
|
||||
0x80, // 0x00 at start of contactor closing, changing to 0x80, afterwards 0x80
|
||||
0x01,
|
||||
0x00}}; // 100ms BDC output - Values change in car logs, these bytes are the most common. Used for contactor closing
|
||||
|
||||
CAN_frame BMWiX_6D = {
|
||||
.FD = true,
|
||||
|
@ -420,8 +452,6 @@ CAN_frame BMWiX_6F4_CELL_TEMP = {.FD = true,
|
|||
.data = {0x07, 0x03, 0x22, 0xE5, 0xCA}};
|
||||
//Request Data CAN End
|
||||
|
||||
static bool battery_awake = false;
|
||||
|
||||
//Setup UDS values to poll for
|
||||
CAN_frame* UDS_REQUESTS100MS[] = {&BMWiX_6F4_REQUEST_CELL_TEMP,
|
||||
&BMWiX_6F4_REQUEST_SOC,
|
||||
|
@ -495,6 +525,9 @@ const unsigned long STALE_PERIOD =
|
|||
|
||||
static uint8_t current_cell_polled = 0;
|
||||
|
||||
static uint16_t counter_10ms = 0; // max 65535 --> 655.35 seconds
|
||||
static uint8_t counter_100ms = 0; // max 255 --> 25.5 seconds
|
||||
|
||||
// Function to check if a value has gone stale over a specified time period
|
||||
bool isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime) {
|
||||
unsigned long currentTime = millis();
|
||||
|
@ -629,13 +662,53 @@ void update_values_battery() { //This function maps all the values fetched via
|
|||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||
battery_awake = true;
|
||||
switch (rx_frame.ID) {
|
||||
case 0x112:
|
||||
case 0x12B8D087:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x1D2:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x20B:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x2E2:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x31F:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x3EA:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x453:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x486:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x49C:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x4A1:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x4BB:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x4D0:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x507:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x587:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x607: //SME responds to UDS requests on 0x607
|
||||
|
||||
if (rx_frame.DLC > 6 && rx_frame.data.u8[0] == 0xF4 && rx_frame.data.u8[1] == 0x10 &&
|
||||
rx_frame.data.u8[2] == 0xE3 && rx_frame.data.u8[3] == 0x62 && rx_frame.data.u8[4] == 0xE5) {
|
||||
//First of multi frame data - Parse the first frame
|
||||
|
@ -785,7 +858,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
(rx_frame.data.u8[8] << 8 | rx_frame.data.u8[9]) == 10000) { //Qualifier Invalid Mode - Request Reboot
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Cell MinMax Qualifier Invalid - Requesting BMS Reset");
|
||||
#endif
|
||||
#endif // DEBUG_LOG
|
||||
//set_event(EVENT_BATTERY_VALUE_UNAVAILABLE, (millis())); //Eventually need new Info level event type
|
||||
transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery);
|
||||
} else { //Only ingest values if they are not the 10V Error state
|
||||
|
@ -832,38 +905,94 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
|||
battery_serial_number = strtoul(numberString, NULL, 10);
|
||||
}
|
||||
break;
|
||||
case 0x7AB:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0x8F:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
case 0xD0D087:
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void transmit_can_battery(unsigned long currentMillis) {
|
||||
// We can always send CAN as the iX BMS will wake up on vehicle comms
|
||||
if (currentMillis - previousMillis10 >= INTERVAL_10_MS) {
|
||||
previousMillis10 = currentMillis;
|
||||
ContactorCloseRequest.present = contactorCloseReq;
|
||||
// Detect edge
|
||||
if (ContactorCloseRequest.previous == false && ContactorCloseRequest.present == true) {
|
||||
// Rising edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Rising edge detected. Resetting 10ms counter.");
|
||||
#endif // DEBUG_LOG
|
||||
counter_10ms = 0; // reset counter
|
||||
} else if (ContactorCloseRequest.previous == true && ContactorCloseRequest.present == false) {
|
||||
// Dropping edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Dropping edge detected. Resetting 10ms counter.");
|
||||
#endif // DEBUG_LOG
|
||||
counter_10ms = 0; // reset counter
|
||||
}
|
||||
ContactorCloseRequest.previous = ContactorCloseRequest.present;
|
||||
HandleBmwIxCloseContactorsRequest(counter_10ms);
|
||||
HandleBmwIxOpenContactorsRequest(counter_10ms);
|
||||
counter_10ms++;
|
||||
|
||||
//if (battery_awake) { //We can always send CAN as the iX BMS will wake up on vehicle comms
|
||||
// prevent counter overflow: 2^16-1 = 65535
|
||||
if (counter_10ms == 65535) {
|
||||
counter_10ms = 1; // set to 1, to differentiate the counter being set to 0 by the functions above
|
||||
}
|
||||
}
|
||||
// Send 100ms CAN Message
|
||||
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
|
||||
previousMillis100 = currentMillis;
|
||||
HandleIncomingInverterRequest();
|
||||
|
||||
//Loop through and send a different UDS request each cycle
|
||||
uds_req_id_counter = increment_uds_req_id_counter(uds_req_id_counter);
|
||||
transmit_can_frame(UDS_REQUESTS100MS[uds_req_id_counter], can_config.battery);
|
||||
//Loop through and send a different UDS request once the contactors are closed
|
||||
if (contactorCloseReq == true &&
|
||||
ContactorState.closed ==
|
||||
true) { // Do not send unless the contactors are requested to be closed and are closed, as sending these does not allow the contactors to close
|
||||
uds_req_id_counter = increment_uds_req_id_counter(uds_req_id_counter);
|
||||
transmit_can_frame(UDS_REQUESTS100MS[uds_req_id_counter],
|
||||
can_config.battery); // FIXME: sending these does not allow the contactors to close
|
||||
} else { // FIXME: hotfix: If contactors are not requested to be closed, ensure the battery is reported as alive, even if no CAN messages are received
|
||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||
}
|
||||
|
||||
// Keep contactors closed if needed
|
||||
BmwIxKeepContactorsClosed(counter_100ms);
|
||||
counter_100ms++;
|
||||
if (counter_100ms == 140) {
|
||||
counter_100ms = 0; // reset counter every 14 seconds
|
||||
}
|
||||
|
||||
//Send SME Keep alive values 100ms
|
||||
transmit_can_frame(&BMWiX_510, can_config.battery);
|
||||
//transmit_can_frame(&BMWiX_510, can_config.battery);
|
||||
}
|
||||
// Send 200ms CAN Message
|
||||
if (currentMillis - previousMillis200 >= INTERVAL_200_MS) {
|
||||
previousMillis200 = currentMillis;
|
||||
|
||||
//Send SME Keep alive values 200ms
|
||||
BMWiX_C0.data.u8[0] = increment_C0_counter(BMWiX_C0.data.u8[0]); //Keep Alive 1
|
||||
transmit_can_frame(&BMWiX_C0, can_config.battery);
|
||||
//BMWiX_C0.data.u8[0] = increment_C0_counter(BMWiX_C0.data.u8[0]); //Keep Alive 1
|
||||
//transmit_can_frame(&BMWiX_C0, can_config.battery);
|
||||
}
|
||||
// Send 1000ms CAN Message
|
||||
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
|
||||
previousMillis1000 = currentMillis;
|
||||
|
||||
HandleIncomingUserRequest();
|
||||
}
|
||||
// Send 10000ms CAN Message
|
||||
if (currentMillis - previousMillis10000 >= INTERVAL_10_S) {
|
||||
previousMillis10000 = currentMillis;
|
||||
transmit_can_frame(&BMWiX_6F4_REQUEST_BALANCING_START2, can_config.battery);
|
||||
transmit_can_frame(&BMWiX_6F4_REQUEST_BALANCING_START, can_config.battery);
|
||||
//transmit_can_frame(&BMWiX_6F4_REQUEST_BALANCING_START2, can_config.battery);
|
||||
//transmit_can_frame(&BMWiX_6F4_REQUEST_BALANCING_START, can_config.battery);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -872,7 +1001,7 @@ void setup_battery(void) { // Performs one time setup at startup
|
|||
datalayer.system.info.battery_protocol[63] = '\0';
|
||||
|
||||
//Reset Battery at bootup
|
||||
transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery);
|
||||
//transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery);
|
||||
|
||||
//Before we have started up and detected which battery is in use, use 108S values
|
||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||
|
@ -883,4 +1012,211 @@ void setup_battery(void) { // Performs one time setup at startup
|
|||
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
void HandleIncomingUserRequest(void) {
|
||||
// Debug user request to open or close the contactors
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("User request: contactor close: ");
|
||||
logging.print(datalayer_extended.bmwix.UserRequestContactorClose);
|
||||
logging.print(" User request: contactor open: ");
|
||||
logging.println(datalayer_extended.bmwix.UserRequestContactorOpen);
|
||||
#endif // DEBUG_LOG
|
||||
if ((datalayer_extended.bmwix.UserRequestContactorClose == false) &&
|
||||
(datalayer_extended.bmwix.UserRequestContactorOpen == false)) {
|
||||
// do nothing
|
||||
} else if ((datalayer_extended.bmwix.UserRequestContactorClose == true) &&
|
||||
(datalayer_extended.bmwix.UserRequestContactorOpen == false)) {
|
||||
BmwIxCloseContactors();
|
||||
// set user request to false
|
||||
datalayer_extended.bmwix.UserRequestContactorClose = false;
|
||||
} else if ((datalayer_extended.bmwix.UserRequestContactorClose == false) &&
|
||||
(datalayer_extended.bmwix.UserRequestContactorOpen == true)) {
|
||||
BmwIxOpenContactors();
|
||||
// set user request to false
|
||||
datalayer_extended.bmwix.UserRequestContactorOpen = false;
|
||||
} else if ((datalayer_extended.bmwix.UserRequestContactorClose == true) &&
|
||||
(datalayer_extended.bmwix.UserRequestContactorOpen == true)) {
|
||||
// these flasgs should not be true at the same time, therefore open contactors, as that is the safest state
|
||||
BmwIxOpenContactors();
|
||||
// set user request to false
|
||||
datalayer_extended.bmwix.UserRequestContactorClose = false;
|
||||
datalayer_extended.bmwix.UserRequestContactorOpen = false;
|
||||
// print error, as both these flags shall not be true at the same time
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println(
|
||||
"Error: user requested contactors to close and open at the same time. Contactors have been opened.");
|
||||
#endif // DEBUG_LOG
|
||||
}
|
||||
}
|
||||
|
||||
void HandleIncomingInverterRequest(void) {
|
||||
InverterContactorCloseRequest.present = datalayer.system.status.inverter_allows_contactor_closing;
|
||||
// Detect edge
|
||||
if (InverterContactorCloseRequest.previous == false && InverterContactorCloseRequest.present == true) {
|
||||
// Rising edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter requests to close contactors");
|
||||
#endif // DEBUG_LOG
|
||||
BmwIxCloseContactors();
|
||||
} else if (InverterContactorCloseRequest.previous == true && InverterContactorCloseRequest.present == false) {
|
||||
// Falling edge detected
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Inverter requests to open contactors");
|
||||
#endif // DEBUG_LOG
|
||||
BmwIxOpenContactors();
|
||||
} // else: do nothing
|
||||
|
||||
// Update state
|
||||
InverterContactorCloseRequest.previous = InverterContactorCloseRequest.present;
|
||||
}
|
||||
|
||||
void BmwIxCloseContactors(void) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Closing contactors");
|
||||
#endif // DEBUG_LOG
|
||||
contactorCloseReq = true;
|
||||
}
|
||||
|
||||
void BmwIxOpenContactors(void) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Opening contactors");
|
||||
#endif // DEBUG_LOG
|
||||
contactorCloseReq = false;
|
||||
counter_100ms = 0; // reset counter, such that keep contactors closed message sequence starts from the beginning
|
||||
}
|
||||
|
||||
void HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms) {
|
||||
if (contactorCloseReq == true) { // Only when contactor close request is set to true
|
||||
if (ContactorState.closed == false &&
|
||||
ContactorState.open ==
|
||||
true) { // Only when the following commands have not been completed yet, because it shall not be run when commands have already been run, AND only when contactor open commands have finished
|
||||
// Initially 0x510[2] needs to be 0x02, and 0x510[5] needs to be 0x00
|
||||
BMWiX_510.data = {0x40, 0x10,
|
||||
0x02, // 0x02 at contactor closing, afterwards 0x04 and 0x10, 0x00 to open contactors
|
||||
0x00, 0x00,
|
||||
0x00, // 0x00 at start of contactor closing, changing to 0x80, afterwards 0x80
|
||||
0x01, // 0x01 at contactor closing
|
||||
0x00}; // Explicit declaration, to prevent modification by other functions
|
||||
BMWiX_16E.data = {
|
||||
0x00, // Almost any possible number in 0x00 and 0xFF
|
||||
0xA0, // Almost any possible number in 0xA0 and 0xAF
|
||||
0xC9, 0xFF, 0x60,
|
||||
0xC9, 0x3A, 0xF7}; // Explicit declaration of default values, to prevent modification by other functions
|
||||
|
||||
if (counter_10ms == 0) {
|
||||
// @0 ms
|
||||
transmit_can_frame(&BMWiX_510, can_config.battery);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x510 - 1/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 5) {
|
||||
// @50 ms
|
||||
transmit_can_frame(&BMWiX_276, can_config.battery);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x276 - 2/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 10) {
|
||||
// @100 ms
|
||||
BMWiX_510.data.u8[2] = 0x04; // TODO: check if needed
|
||||
transmit_can_frame(&BMWiX_510, can_config.battery);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x510 - 3/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 20) {
|
||||
// @200 ms
|
||||
BMWiX_510.data.u8[2] = 0x10; // TODO: check if needed
|
||||
BMWiX_510.data.u8[5] = 0x80; // needed to close contactors
|
||||
transmit_can_frame(&BMWiX_510, can_config.battery);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x510 - 4/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 30) {
|
||||
// @300 ms
|
||||
BMWiX_16E.data.u8[0] = 0x6A;
|
||||
BMWiX_16E.data.u8[1] = 0xAD;
|
||||
transmit_can_frame(&BMWiX_16E, can_config.battery);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x16E - 5/6");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_10ms == 50) {
|
||||
// @500 ms
|
||||
BMWiX_16E.data.u8[0] = 0x03;
|
||||
BMWiX_16E.data.u8[1] = 0xA9;
|
||||
transmit_can_frame(&BMWiX_16E, can_config.battery);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Transmitted 0x16E - 6/6");
|
||||
#endif // DEBUG_LOG
|
||||
ContactorState.closed = true;
|
||||
ContactorState.open = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BmwIxKeepContactorsClosed(uint8_t counter_100ms) {
|
||||
if ((ContactorState.closed == true) && (ContactorState.open == false)) {
|
||||
BMWiX_510.data = {0x40, 0x10,
|
||||
0x04, // 0x02 at contactor closing, afterwards 0x04 and 0x10, 0x00 to open contactors
|
||||
0x00, 0x00,
|
||||
0x80, // 0x00 at start of contactor closing, changing to 0x80, afterwards 0x80
|
||||
0x01, // 0x01 at contactor closing
|
||||
0x00}; // Explicit declaration, to prevent modification by other functions
|
||||
BMWiX_16E.data = {0x00, // Almost any possible number in 0x00 and 0xFF
|
||||
0xA0, // Almost any possible number in 0xA0 and 0xAF
|
||||
0xC9, 0xFF, 0x60,
|
||||
0xC9, 0x3A, 0xF7}; // Explicit declaration, to prevent modification by other functions
|
||||
|
||||
if (counter_100ms == 0) {
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Sending keep contactors closed messages started");
|
||||
#endif // DEBUG_LOG
|
||||
// @0 ms
|
||||
transmit_can_frame(&BMWiX_510, can_config.battery);
|
||||
} else if (counter_100ms == 7) {
|
||||
// @ 730 ms
|
||||
BMWiX_16E.data.u8[0] = 0x8C;
|
||||
BMWiX_16E.data.u8[1] = 0xA0;
|
||||
transmit_can_frame(&BMWiX_16E, can_config.battery);
|
||||
} else if (counter_100ms == 24) {
|
||||
// @2380 ms
|
||||
transmit_can_frame(&BMWiX_510, can_config.battery);
|
||||
} else if (counter_100ms == 29) {
|
||||
// @ 2900 ms
|
||||
BMWiX_16E.data.u8[0] = 0x02;
|
||||
BMWiX_16E.data.u8[1] = 0xA7;
|
||||
transmit_can_frame(&BMWiX_16E, can_config.battery);
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println("Sending keep contactors closed messages finished");
|
||||
#endif // DEBUG_LOG
|
||||
} else if (counter_100ms == 140) {
|
||||
// @14000 ms
|
||||
// reset counter (outside of this function)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms) {
|
||||
if (contactorCloseReq == false) { // if contactors are not requested to be closed, they are requested to be opened
|
||||
if (ContactorState.open == false) { // only if contactors are not open yet
|
||||
// message content to quickly open contactors
|
||||
if (counter_10ms == 0) {
|
||||
// @0 ms (0.00) RX0 510 [8] 40 10 00 00 00 80 00 00
|
||||
BMWiX_510.data = {0x40, 0x10, 0x00, 0x00,
|
||||
0x00, 0x80, 0x00, 0x00}; // Explicit declaration, to prevent modification by other functions
|
||||
transmit_can_frame(&BMWiX_510, can_config.battery);
|
||||
// set back to default values
|
||||
BMWiX_510.data = {0x40, 0x10, 0x04, 0x00, 0x00, 0x80, 0x01, 0x00}; // default values
|
||||
} else if (counter_10ms == 6) {
|
||||
// @60 ms (0.06) RX0 16E [8] E6 A4 C8 FF 60 C9 33 F0
|
||||
BMWiX_16E.data = {0xE6, 0xA4, 0xC8, 0xFF,
|
||||
0x60, 0xC9, 0x33, 0xF0}; // Explicit declaration, to prevent modification by other functions
|
||||
transmit_can_frame(&BMWiX_16E, can_config.battery);
|
||||
// set back to default values
|
||||
BMWiX_16E.data = {0x00, 0xA0, 0xC9, 0xFF, 0x60, 0xC9, 0x3A, 0xF7}; // default values
|
||||
ContactorState.closed = false;
|
||||
ContactorState.open = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BMW_IX_BATTERY
|
||||
|
|
|
@ -19,4 +19,69 @@
|
|||
void setup_battery(void);
|
||||
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
||||
|
||||
/**
|
||||
* @brief Handle incoming user request to close or open contactors
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void HandleIncomingUserRequest(void);
|
||||
|
||||
/**
|
||||
* @brief Handle incoming inverter request to close or open contactors.alignas
|
||||
*
|
||||
* This function uses the "inverter_allows_contactor_closing" flag from the datalayer, to determine if CAN messages shall be sent to the battery to close or open the contactors.
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void HandleIncomingInverterRequest(void);
|
||||
|
||||
/**
|
||||
* @brief Close contactors of the BMW iX battery
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void BmwIxCloseContactors(void);
|
||||
|
||||
/**
|
||||
* @brief Handle close contactors requests for the BMW iX battery
|
||||
*
|
||||
* @param[in] counter_10ms Counter that increments by 1, every 10ms
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void HandleBmwIxCloseContactorsRequest(uint16_t counter_10ms);
|
||||
|
||||
/**
|
||||
* @brief Keep contactors of the BMW iX battery closed
|
||||
*
|
||||
* @param[in] counter_100ms Counter that increments by 1, every 100 ms
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void BmwIxKeepContactorsClosed(uint8_t counter_100ms);
|
||||
|
||||
/**
|
||||
* @brief Open contactors of the BMW iX battery
|
||||
*
|
||||
* @param[in] void
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void BmwIxOpenContactors(void);
|
||||
|
||||
/**
|
||||
* @brief Handle open contactors requests for the BMW iX battery
|
||||
*
|
||||
* @param[in] counter_10ms Counter that increments by 1, every 10ms
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,6 +41,9 @@ typedef struct {
|
|||
} DATALAYER_INFO_BOLTAMPERA;
|
||||
|
||||
typedef struct {
|
||||
/** User requesting contactor open or close via WebUI*/
|
||||
bool UserRequestContactorClose = false;
|
||||
bool UserRequestContactorOpen = false;
|
||||
/** uint16_t */
|
||||
/** Terminal 30 - 12V SME Supply Voltage */
|
||||
uint16_t T30_Voltage = 0;
|
||||
|
|
|
@ -58,6 +58,8 @@ String advanced_battery_processor(const String& var) {
|
|||
#endif //BOLT_AMPERA_BATTERY
|
||||
|
||||
#ifdef BMW_IX_BATTERY
|
||||
content += "<button onclick='askContactorClose()'>Close Contactors</button>";
|
||||
content += "<button onclick='askContactorOpen()'>Open Contactors</button>";
|
||||
content +=
|
||||
"<h4>Battery Voltage after Contactor: " + String(datalayer_extended.bmwix.battery_voltage_after_contactor) +
|
||||
" dV</h4>";
|
||||
|
@ -1492,6 +1494,7 @@ String advanced_battery_processor(const String& var) {
|
|||
#endif
|
||||
|
||||
content += "</div>";
|
||||
|
||||
content += "<script>";
|
||||
content +=
|
||||
"function askTeslaClearIsolation() { if (window.confirm('Are you sure you want to clear any active isolation "
|
||||
|
@ -1504,6 +1507,7 @@ String advanced_battery_processor(const String& var) {
|
|||
content += "}";
|
||||
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||
content += "</script>";
|
||||
|
||||
content += "<script>";
|
||||
content +=
|
||||
"function askTeslaResetBMS() { if (window.confirm('Are you sure you want to reset the "
|
||||
|
@ -1516,6 +1520,7 @@ String advanced_battery_processor(const String& var) {
|
|||
content += "}";
|
||||
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||
content += "</script>";
|
||||
|
||||
content += "<script>";
|
||||
content +=
|
||||
"function askResetCrash() { if (window.confirm('Are you sure you want to reset crash data? "
|
||||
|
@ -1540,6 +1545,33 @@ String advanced_battery_processor(const String& var) {
|
|||
content += "}";
|
||||
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||
content += "</script>";
|
||||
|
||||
content += "<script>";
|
||||
content +=
|
||||
"function askContactorClose() { if (window.confirm('Are you sure you want to tirgger "
|
||||
"a contactor close request?')) { "
|
||||
"bmwIxCloseContactorRequest(); } }";
|
||||
content += "function bmwIxCloseContactorRequest() {";
|
||||
content += " var xhr = new XMLHttpRequest();";
|
||||
content += " xhr.open('GET', '/bmwIxCloseContactorRequest', true);";
|
||||
content += " xhr.send();";
|
||||
content += "}";
|
||||
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||
content += "</script>";
|
||||
|
||||
content += "<script>";
|
||||
content +=
|
||||
"function askContactorOpen() { if (window.confirm('Are you sure you want to tirgger "
|
||||
"a contactor open request?')) { "
|
||||
"bmwIxOpenContactorRequest(); } }";
|
||||
content += "function bmwIxOpenContactorRequest() {";
|
||||
content += " var xhr = new XMLHttpRequest();";
|
||||
content += " xhr.open('GET', '/bmwIxOpenContactorRequest', true);";
|
||||
content += " xhr.send();";
|
||||
content += "}";
|
||||
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||
content += "</script>";
|
||||
|
||||
content += "<script>";
|
||||
content +=
|
||||
"function askResetSOH() { if (window.confirm('Are you sure you want to reset degradation data? "
|
||||
|
@ -1552,6 +1584,7 @@ String advanced_battery_processor(const String& var) {
|
|||
content += "}";
|
||||
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||
content += "</script>";
|
||||
|
||||
content += "<script>";
|
||||
content +=
|
||||
"function Volvo_askEraseDTC() { if (window.confirm('Are you sure you want to erase DTCs?')) { "
|
||||
|
@ -1585,6 +1618,7 @@ String advanced_battery_processor(const String& var) {
|
|||
content += "}";
|
||||
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||
content += "</script>";
|
||||
|
||||
// Additial functions added
|
||||
content += "<script>";
|
||||
content += "function exportLog() { window.location.href = '/export_log'; }";
|
||||
|
|
|
@ -599,6 +599,24 @@ void init_webserver() {
|
|||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for closing BMW iX Contactors
|
||||
server.on("/bmwIxCloseContactorRequest", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer_extended.bmwix.UserRequestContactorClose = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for opening BMW iX Contactors
|
||||
server.on("/bmwIxOpenContactorRequest", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
return request->requestAuthentication();
|
||||
}
|
||||
datalayer_extended.bmwix.UserRequestContactorOpen = true;
|
||||
request->send(200, "text/plain", "Updated successfully");
|
||||
});
|
||||
|
||||
// Route for resetting SOH on Nissan LEAF batteries
|
||||
server.on("/resetSOH", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||
|
@ -1207,15 +1225,14 @@ String processor(const String& var) {
|
|||
}
|
||||
}
|
||||
|
||||
content += "<h4>Automatic contactor closing allowed:</h4>";
|
||||
content += "<h4>Battery: ";
|
||||
content += "<h4>Battery allows contactor closing: ";
|
||||
if (datalayer.system.status.battery_allows_contactor_closing == true) {
|
||||
content += "<span>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
|
||||
content += " Inverter: ";
|
||||
content += " Inverter allows contactor closing: ";
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
||||
content += "<span>✓</span></h4>";
|
||||
} else {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue