mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 02:39:57 +02:00
Merge pull request #1057 from dalathegreat/feature/BYD-unlock-crashed
Atto3: Add crashed BMS unlocking 🔒
This commit is contained in:
commit
0467401bab
4 changed files with 77 additions and 5 deletions
|
@ -7,15 +7,20 @@
|
||||||
|
|
||||||
/* Notes
|
/* Notes
|
||||||
- SOC% by default is now ESTIMATED.
|
- SOC% by default is now ESTIMATED.
|
||||||
- If you have a non-crashed pack, enable using real SOC. See Wiki for info.
|
- If you have a crash-locked pack, run the "Unlock crashed BMS" from the "More Battery Info" page
|
||||||
- TODO: In the future, we might be able to unlock crashed batteries and get SOC going always
|
- After battery is unlocked, you can remove the "USE_ESTIMATED_SOC" from the BYD-ATTO-3-BATTERY.h file
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 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 */
|
||||||
#define NOT_DETERMINED_YET 0
|
#define NOT_DETERMINED_YET 0
|
||||||
#define STANDARD_RANGE 1
|
#define STANDARD_RANGE 1
|
||||||
#define EXTENDED_RANGE 2
|
#define EXTENDED_RANGE 2
|
||||||
|
#define NOT_RUNNING 0xFF
|
||||||
|
#define STARTED 0
|
||||||
|
#define RUNNING_STEP_1 1
|
||||||
|
#define RUNNING_STEP_2 2
|
||||||
static uint8_t battery_type = NOT_DETERMINED_YET;
|
static uint8_t battery_type = NOT_DETERMINED_YET;
|
||||||
|
static uint8_t stateMachineClearCrash = NOT_RUNNING;
|
||||||
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 send
|
||||||
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 send
|
||||||
static unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send
|
static unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was send
|
||||||
|
@ -89,6 +94,16 @@ CAN_frame ATTO_3_7E7_POLL = {.FD = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x7E7, //Poll PID 03 22 00 05 (POLL_FOR_BATTERY_SOC)
|
.ID = 0x7E7, //Poll PID 03 22 00 05 (POLL_FOR_BATTERY_SOC)
|
||||||
.data = {0x03, 0x22, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00}};
|
.data = {0x03, 0x22, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame ATTO_3_7E7_ACK = {.FD = false,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x7E7, //ACK frame for long PIDs
|
||||||
|
.data = {0x30, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
CAN_frame ATTO_3_7E7_CLEAR_CRASH = {.FD = false,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x7E7,
|
||||||
|
.data = {0x02, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
|
|
||||||
// Define the data points for %SOC depending on pack voltage
|
// Define the data points for %SOC depending on pack voltage
|
||||||
const uint8_t numPoints = 14;
|
const uint8_t numPoints = 14;
|
||||||
|
@ -252,6 +267,12 @@ void update_values_battery() { //This function maps all the values fetched via
|
||||||
datalayer_extended.bydAtto3.battery_temperatures[7] = battery_daughterboard_temperatures[7];
|
datalayer_extended.bydAtto3.battery_temperatures[7] = battery_daughterboard_temperatures[7];
|
||||||
datalayer_extended.bydAtto3.battery_temperatures[8] = battery_daughterboard_temperatures[8];
|
datalayer_extended.bydAtto3.battery_temperatures[8] = battery_daughterboard_temperatures[8];
|
||||||
datalayer_extended.bydAtto3.battery_temperatures[9] = battery_daughterboard_temperatures[9];
|
datalayer_extended.bydAtto3.battery_temperatures[9] = battery_daughterboard_temperatures[9];
|
||||||
|
|
||||||
|
// Update requests from webserver datalayer
|
||||||
|
if (datalayer_extended.bydAtto3.UserRequestCrashReset && stateMachineClearCrash == NOT_RUNNING) {
|
||||||
|
stateMachineClearCrash = STARTED;
|
||||||
|
datalayer_extended.bydAtto3.UserRequestCrashReset = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
|
@ -356,6 +377,10 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
break;
|
break;
|
||||||
case 0x7EF: //OBD2 PID reply from battery
|
case 0x7EF: //OBD2 PID reply from battery
|
||||||
|
if (rx_frame.data.u8[0] == 0x10) {
|
||||||
|
transmit_can_frame(&ATTO_3_7E7_ACK, can_config.battery); //Send next line request
|
||||||
|
}
|
||||||
|
|
||||||
switch (rx_frame.data.u8[3]) {
|
switch (rx_frame.data.u8[3]) {
|
||||||
case POLL_FOR_BATTERY_SOC:
|
case POLL_FOR_BATTERY_SOC:
|
||||||
BMS_SOC = rx_frame.data.u8[4];
|
BMS_SOC = rx_frame.data.u8[4];
|
||||||
|
@ -437,7 +462,6 @@ void transmit_can_battery(unsigned long currentMillis) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (counter_100ms > 3) {
|
if (counter_100ms > 3) {
|
||||||
|
|
||||||
ATTO_3_441.data.u8[4] = 0x9D;
|
ATTO_3_441.data.u8[4] = 0x9D;
|
||||||
ATTO_3_441.data.u8[5] = 0x01;
|
ATTO_3_441.data.u8[5] = 0x01;
|
||||||
ATTO_3_441.data.u8[6] = 0xFF;
|
ATTO_3_441.data.u8[6] = 0xFF;
|
||||||
|
@ -448,6 +472,27 @@ void transmit_can_battery(unsigned long currentMillis) {
|
||||||
#ifdef DOUBLE_BATTERY
|
#ifdef DOUBLE_BATTERY
|
||||||
transmit_can_frame(&ATTO_3_441, can_config.battery_double);
|
transmit_can_frame(&ATTO_3_441, can_config.battery_double);
|
||||||
#endif //DOUBLE_BATTERY
|
#endif //DOUBLE_BATTERY
|
||||||
|
switch (stateMachineClearCrash) {
|
||||||
|
case STARTED:
|
||||||
|
ATTO_3_7E7_CLEAR_CRASH.data = {0x02, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
transmit_can_frame(&ATTO_3_7E7_CLEAR_CRASH, can_config.battery);
|
||||||
|
stateMachineClearCrash = RUNNING_STEP_1;
|
||||||
|
break;
|
||||||
|
case RUNNING_STEP_1:
|
||||||
|
ATTO_3_7E7_CLEAR_CRASH.data = {0x04, 0x14, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00};
|
||||||
|
transmit_can_frame(&ATTO_3_7E7_CLEAR_CRASH, can_config.battery);
|
||||||
|
stateMachineClearCrash = RUNNING_STEP_2;
|
||||||
|
break;
|
||||||
|
case RUNNING_STEP_2:
|
||||||
|
ATTO_3_7E7_CLEAR_CRASH.data = {0x03, 0x19, 0x02, 0x09, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
transmit_can_frame(&ATTO_3_7E7_CLEAR_CRASH, can_config.battery);
|
||||||
|
stateMachineClearCrash = NOT_RUNNING;
|
||||||
|
break;
|
||||||
|
case NOT_RUNNING:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Send 500ms CAN Message
|
// Send 500ms CAN Message
|
||||||
if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
|
if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
|
||||||
|
@ -491,10 +536,12 @@ void transmit_can_battery(unsigned long currentMillis) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
transmit_can_frame(&ATTO_3_7E7_POLL, can_config.battery);
|
if (stateMachineClearCrash == NOT_RUNNING) { //Don't poll battery for data if clear crash running
|
||||||
|
transmit_can_frame(&ATTO_3_7E7_POLL, can_config.battery);
|
||||||
#ifdef DOUBLE_BATTERY
|
#ifdef DOUBLE_BATTERY
|
||||||
transmit_can_frame(&ATTO_3_7E7_POLL, can_config.battery_double);
|
transmit_can_frame(&ATTO_3_7E7_POLL, can_config.battery_double);
|
||||||
#endif //DOUBLE_BATTERY
|
#endif //DOUBLE_BATTERY
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -163,6 +163,9 @@ typedef struct {
|
||||||
} DATALAYER_INFO_BMWI3;
|
} DATALAYER_INFO_BMWI3;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
/** bool */
|
||||||
|
/** User requesting crash reset via WebUI*/
|
||||||
|
bool UserRequestCrashReset = false;
|
||||||
/** bool */
|
/** bool */
|
||||||
/** Which SOC method currently used. 0 = Estimated, 1 = Measured */
|
/** Which SOC method currently used. 0 = Estimated, 1 = Measured */
|
||||||
bool SOC_method = 0;
|
bool SOC_method = 0;
|
||||||
|
|
|
@ -487,6 +487,7 @@ String advanced_battery_processor(const String& var) {
|
||||||
|
|
||||||
#ifdef BYD_ATTO_3_BATTERY
|
#ifdef BYD_ATTO_3_BATTERY
|
||||||
static const char* SOCmethod[2] = {"Estimated from voltage", "Measured by BMS"};
|
static const char* SOCmethod[2] = {"Estimated from voltage", "Measured by BMS"};
|
||||||
|
content += "<button onclick='askResetCrash()'>Unlock crashed BMS</button>";
|
||||||
content += "<h4>SOC method used: " + String(SOCmethod[datalayer_extended.bydAtto3.SOC_method]) + "</h4>";
|
content += "<h4>SOC method used: " + String(SOCmethod[datalayer_extended.bydAtto3.SOC_method]) + "</h4>";
|
||||||
content += "<h4>SOC estimated: " + String(datalayer_extended.bydAtto3.SOC_estimated) + "</h4>";
|
content += "<h4>SOC estimated: " + String(datalayer_extended.bydAtto3.SOC_estimated) + "</h4>";
|
||||||
content += "<h4>SOC highprec: " + String(datalayer_extended.bydAtto3.SOC_highprec) + "</h4>";
|
content += "<h4>SOC highprec: " + String(datalayer_extended.bydAtto3.SOC_highprec) + "</h4>";
|
||||||
|
@ -1502,6 +1503,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 askResetCrash() { if (window.confirm('Are you sure you want to reset crash data? "
|
||||||
|
"Note this will unlock your BMS and enable contactor closing and SOC calculation.')) { "
|
||||||
|
"resetCrash(); } }";
|
||||||
|
content += "function resetCrash() {";
|
||||||
|
content += " var xhr = new XMLHttpRequest();";
|
||||||
|
content += " xhr.open('GET', '/resetCrash', true);";
|
||||||
|
content += " xhr.send();";
|
||||||
|
content += "}";
|
||||||
|
content += "function goToMainPage() { window.location.href = '/'; }";
|
||||||
|
content += "</script>";
|
||||||
|
content += "<script>";
|
||||||
content +=
|
content +=
|
||||||
"function askTriggerNVROL() { if (window.confirm('Are you sure you want to trigger "
|
"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!')) { "
|
"an NVROL reset? Battery will be unavailable for 30 seconds while this is active!')) { "
|
||||||
|
|
|
@ -608,6 +608,15 @@ void init_webserver() {
|
||||||
request->send(200, "text/plain", "Updated successfully");
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Route for resetting Crash data on BYD Atto3 batteries
|
||||||
|
server.on("/resetCrash", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
|
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||||
|
return request->requestAuthentication();
|
||||||
|
}
|
||||||
|
datalayer_extended.bydAtto3.UserRequestCrashReset = true;
|
||||||
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
|
});
|
||||||
|
|
||||||
// Route for erasing DTC on Volvo/Polestar batteries
|
// Route for erasing DTC on Volvo/Polestar batteries
|
||||||
server.on("/volvoEraseDTC", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/volvoEraseDTC", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue