Feature: Add Tesla BMS ECU reset, basic UDS response logging

This commit is contained in:
James Brookes 2025-04-06 19:40:57 +01:00
parent 9a8c1f1f77
commit a5c99488dd
4 changed files with 104 additions and 6 deletions

View file

@ -8,9 +8,11 @@
/* 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 */
/* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */ /* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */
static unsigned long previousMillis10 = 0; // will store last time a 50ms CAN Message was send static unsigned long previousMillis10 = 0; // will store last time a 50ms CAN Message was sent
static unsigned long previousMillis50 = 0; // will store last time a 50ms CAN Message was send static unsigned long previousMillis50 = 0; // will store last time a 50ms CAN Message was sent
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was sent
static unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was sent
static unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was sent
static bool alternate243 = false; static bool alternate243 = false;
//0x221 545 VCFRONT_LVPowerState: "GenMsgCycleTime" 50ms //0x221 545 VCFRONT_LVPowerState: "GenMsgCycleTime" 50ms
CAN_frame TESLA_221_1 = { CAN_frame TESLA_221_1 = {
@ -50,12 +52,14 @@ CAN_frame TESLA_129 = {.FD = false,
.DLC = 8, .DLC = 8,
.ID = 0x129, .ID = 0x129,
.data = {0x21, 0x24, 0x36, 0x5F, 0x00, 0x20, 0xFF, 0x3F}}; .data = {0x21, 0x24, 0x36, 0x5F, 0x00, 0x20, 0xFF, 0x3F}};
//0x612 UDS diagnostic requests - on demand
CAN_frame TESLA_602 = {.FD = false, CAN_frame TESLA_602 = {.FD = false,
.ext_ID = false, .ext_ID = false,
.DLC = 8, .DLC = 8,
.ID = 0x602, .ID = 0x602,
.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Diagnostic request .data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}};
static uint8_t stateMachineClearIsolationFault = 0xFF; static uint8_t stateMachineClearIsolationFault = 0xFF;
static uint8_t stateMachineBMSReset = 0xFF;
static uint16_t sendContactorClosingMessagesStill = 300; static uint16_t sendContactorClosingMessagesStill = 300;
static uint16_t battery_cell_max_v = 3300; static uint16_t battery_cell_max_v = 3300;
static uint16_t battery_cell_min_v = 3300; static uint16_t battery_cell_min_v = 3300;
@ -867,9 +871,18 @@ void update_values_battery() { //This function maps all the values fetched via
// Check if user requests some action // Check if user requests some action
if (datalayer.battery.settings.user_requests_isolation_clear) { if (datalayer.battery.settings.user_requests_isolation_clear) {
stateMachineClearIsolationFault = 0; //Start the statemachine stateMachineClearIsolationFault = 0; //Start the isolation fault statemachine
datalayer.battery.settings.user_requests_isolation_clear = false; datalayer.battery.settings.user_requests_isolation_clear = false;
} }
if (datalayer.battery.settings.user_requests_bms_ecu_reset) {
if (battery_contactor == 1 && battery_BMS_a180_SW_ECU_reset_blocked == false) {
stateMachineBMSReset =
0; //Start the BMS ECU reset statemachine, only if contactors are OPEN and BMS ECU allows it
datalayer.battery.settings.user_requests_bms_ecu_reset = false;
} else {
logging.println("ERROR: BMS ECU reset failed due to contactors not being open, or BMS ECU not allowing it");
}
}
// Update webserver datalayer // Update webserver datalayer
//0x20A //0x20A
@ -1801,6 +1814,15 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
BMS_SerialNumber[13] = rx_frame.data.u8[7]; BMS_SerialNumber[13] = rx_frame.data.u8[7];
} }
break; break;
case 0x612: // CAN UDS responses for BMS ECU reset
if (memcmp(rx_frame.data.u8, "\x02\x67\x06\xAA\xAA\xAA\xAA\xAA", 8) == 0) {
logging.println("CAN UDS response: ECU unlocked");
} else if (memcmp(rx_frame.data.u8, "\x03\x7F\x11\x78\xAA\xAA\xAA\xAA", 8) == 0) {
logging.println("CAN UDS response: ECU reset request successful but ECU busy, response pending");
} else if (memcmp(rx_frame.data.u8, "\x02\x51\x01\xAA\xAA\xAA\xAA\xAA", 8) == 0) {
logging.println("CAN UDS response: ECU reset positive response, 1 second downtime");
}
break;
default: default:
break; break;
} }
@ -1949,6 +1971,7 @@ the first, for a few cycles, then stop all messages which causes the contactor
case 4: case 4:
TESLA_602.data = {0x22, 0x3E, 0x39, 0x38, 0x3B, 0x3A, 0x00, 0x00}; TESLA_602.data = {0x22, 0x3E, 0x39, 0x38, 0x3B, 0x3A, 0x00, 0x00};
transmit_can_frame(&TESLA_602, can_config.battery); transmit_can_frame(&TESLA_602, can_config.battery);
//Should generate a CAN UDS log message indicating ECU unlocked
stateMachineClearIsolationFault = 5; stateMachineClearIsolationFault = 5;
break; break;
case 5: case 5:
@ -1961,6 +1984,58 @@ the first, for a few cycles, then stop all messages which causes the contactor
stateMachineClearIsolationFault = 0xFF; stateMachineClearIsolationFault = 0xFF;
break; break;
} }
if (stateMachineBMSReset != 0xFF) {
//This implementation should be rewritten to actually replying to the UDS replied sent by the BMS
//While this may work, it is not the correct way to implement this clearing logic
switch (stateMachineBMSReset) {
case 0:
TESLA_602.data = {0x02, 0x27, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&TESLA_602, can_config.battery);
stateMachineBMSReset = 1;
break;
case 1:
TESLA_602.data = {0x30, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&TESLA_602, can_config.battery);
stateMachineBMSReset = 2;
break;
case 2:
TESLA_602.data = {0x10, 0x12, 0x27, 0x06, 0x35, 0x34, 0x37, 0x36};
transmit_can_frame(&TESLA_602, can_config.battery);
stateMachineBMSReset = 3;
break;
case 3:
TESLA_602.data = {0x21, 0x31, 0x30, 0x33, 0x32, 0x3D, 0x3C, 0x3F};
transmit_can_frame(&TESLA_602, can_config.battery);
stateMachineBMSReset = 4;
break;
case 4:
TESLA_602.data = {0x22, 0x3E, 0x39, 0x38, 0x3B, 0x3A, 0x00, 0x00};
transmit_can_frame(&TESLA_602, can_config.battery);
//Should generate a CAN UDS log message indicating ECU unlocked
stateMachineBMSReset = 5;
break;
case 5:
TESLA_602.data = {0x02, 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&TESLA_602, can_config.battery);
stateMachineBMSReset = 6;
break;
case 6:
TESLA_602.data = {0x02, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&TESLA_602, can_config.battery);
stateMachineBMSReset = 7;
break;
case 7:
TESLA_602.data = {0x02, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
transmit_can_frame(&TESLA_602, can_config.battery);
//Should generate a CAN UDS log message(s) indicating ECU has reset
stateMachineBMSReset = 0xFF;
break;
default:
//Something went wrong. Reset all and cancel
stateMachineBMSReset = 0xFF;
break;
}
}
} }
} }
} }

View file

@ -142,6 +142,7 @@ typedef struct {
/* Bool for specifying if user has requested manual function */ /* Bool for specifying if user has requested manual function */
bool user_requests_balancing = false; bool user_requests_balancing = false;
bool user_requests_isolation_clear = false; bool user_requests_isolation_clear = false;
bool user_requests_bms_ecu_reset = false;
/* Forced balancing max time & start timestamp */ /* Forced balancing max time & start timestamp */
uint32_t balancing_time_ms = 3600000; //1h default, (60min*60sec*1000ms) uint32_t balancing_time_ms = 3600000; //1h default, (60min*60sec*1000ms)
uint32_t balancing_start_time_ms = 0; //For keeping track when balancing started uint32_t balancing_start_time_ms = 0; //For keeping track when balancing started

View file

@ -657,6 +657,7 @@ String advanced_battery_processor(const String& var) {
//Buttons for user action //Buttons for user action
content += "<button onclick='askClearIsolation()'>Clear isolation fault</button>"; content += "<button onclick='askClearIsolation()'>Clear isolation fault</button>";
content += "<button onclick='askBMSReset()'>BMS reset</button>";
//0x20A 522 HVP_contatorState //0x20A 522 HVP_contatorState
content += "<h4>Contactor Status: " + String(contactorText[datalayer_extended.tesla.status_contactor]) + "</h4>"; content += "<h4>Contactor Status: " + String(contactorText[datalayer_extended.tesla.status_contactor]) + "</h4>";
content += "<h4>HVIL: " + String(hvilStatusState[datalayer_extended.tesla.hvil_status]) + "</h4>"; content += "<h4>HVIL: " + String(hvilStatusState[datalayer_extended.tesla.hvil_status]) + "</h4>";
@ -683,7 +684,7 @@ String advanced_battery_processor(const String& var) {
sizeof(datalayer_extended.tesla.BMS_SerialNumber)); sizeof(datalayer_extended.tesla.BMS_SerialNumber));
readableSerialNumber[14] = '\0'; // Null terminate the string readableSerialNumber[14] = '\0'; // Null terminate the string
content += "<h4>BMS Serial number: " + String(readableSerialNumber) + "</h4>"; content += "<h4>BMS Serial number: " + String(readableSerialNumber) + "</h4>";
// Comment what data you would like to dislay, order can be changed. // Comment what data you would like to display, order can be changed.
//0x352 850 BMS_energyStatus //0x352 850 BMS_energyStatus
if (datalayer_extended.tesla.BMS352_mux == false) { if (datalayer_extended.tesla.BMS352_mux == false) {
content += "<h3>BMS 0x352 w/o mux</h3>"; //if using older BMS <2021 and comment 0x352 without MUX content += "<h3>BMS 0x352 w/o mux</h3>"; //if using older BMS <2021 and comment 0x352 without MUX
@ -1464,6 +1465,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 askBMSReset() { if (window.confirm('Are you sure you want to reset the BMS "
"ECU?')) { "
"bmsECUReset(); } }";
content += "function bmsECUReset() {";
content += " var xhr = new XMLHttpRequest();";
content += " xhr.open('GET', '/bmsECUReset', 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!')) { "

View file

@ -579,6 +579,15 @@ void init_webserver() {
request->send(200, "text/plain", "Updated successfully"); request->send(200, "text/plain", "Updated successfully");
}); });
// Route for resetting BMS ECU on Tesla
server.on("/bmsECUReset", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
return request->requestAuthentication();
}
datalayer.battery.settings.user_requests_bms_ecu_reset = 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)) {