Merge branch 'main' into common-final

This commit is contained in:
Jaakko Haakana 2025-06-21 17:21:54 +03:00 committed by GitHub
commit 82d89bb6e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 432 additions and 225 deletions

View file

@ -25,13 +25,37 @@ class BmwIXHtmlRenderer : public BatteryHtmlRenderer {
content += "<h4>Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W</h4>"; content += "<h4>Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W</h4>";
content += "<h4>T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV</h4>"; content += "<h4>T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV</h4>";
content += "<h4>Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "</h4>"; content += "<h4>Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "</h4>";
static const char* balanceText[5] = {"0 No balancing mode active", "1 Voltage-Controlled Balancing Mode", content += "<h4>Balancing: ";
"2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging", switch (datalayer_extended.bmwix.balancing_status) {
"3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage", case 0:
"4 No balancing mode active, qualifier invalid"}; content += "0 No balancing mode active</h4>";
content += "<h4>Balancing: " + String((balanceText[datalayer_extended.bmwix.balancing_status])) + "</h4>"; break;
static const char* hvilText[2] = {"Error (Loop Open)", "OK (Loop Closed)"}; case 1:
content += "<h4>HVIL Status: " + String(hvilText[datalayer_extended.bmwix.hvil_status]) + "</h4>"; content += "1 Voltage-Controlled Balancing Mode</h4>";
break;
case 2:
content += "2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging</h4>";
break;
case 3:
content += "3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage</h4>";
break;
case 4:
content += "4 No balancing mode active, qualifier invalid</h4>";
break;
default:
content += "Unknown</h4>";
}
content += "<h4>HVIL Status: ";
switch (datalayer_extended.bmwix.hvil_status) {
case 0:
content += "Error (Loop Open)</h4>";
break;
case 1:
content += "OK (Loop Closed)</h4>";
break;
default:
content += "Unknown</h4>";
}
content += "<h4>BMS Uptime: " + String(datalayer_extended.bmwix.bms_uptime) + " seconds</h4>"; content += "<h4>BMS Uptime: " + String(datalayer_extended.bmwix.bms_uptime) + " seconds</h4>";
content += "<h4>BMS Allowed Charge Amps: " + String(datalayer_extended.bmwix.allowable_charge_amps) + " A</h4>"; content += "<h4>BMS Allowed Charge Amps: " + String(datalayer_extended.bmwix.allowable_charge_amps) + " A</h4>";
content += content +=
@ -41,11 +65,66 @@ class BmwIXHtmlRenderer : public BatteryHtmlRenderer {
content += "<h4>Isolation Positive: " + String(datalayer_extended.bmwix.iso_safety_positive) + " kOhm</h4>"; content += "<h4>Isolation Positive: " + String(datalayer_extended.bmwix.iso_safety_positive) + " kOhm</h4>";
content += "<h4>Isolation Negative: " + String(datalayer_extended.bmwix.iso_safety_negative) + " kOhm</h4>"; content += "<h4>Isolation Negative: " + String(datalayer_extended.bmwix.iso_safety_negative) + " kOhm</h4>";
content += "<h4>Isolation Parallel: " + String(datalayer_extended.bmwix.iso_safety_parallel) + " kOhm</h4>"; content += "<h4>Isolation Parallel: " + String(datalayer_extended.bmwix.iso_safety_parallel) + " kOhm</h4>";
static const char* pyroText[5] = {"0 Value Invalid", "1 Successfully Blown", "2 Disconnected", content += "<h4>Pyro Status PSS1: ";
"3 Not Activated - Pyro Intact", "4 Unknown"}; switch (datalayer_extended.bmwix.pyro_status_pss1) {
content += "<h4>Pyro Status PSS1: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss1])) + "</h4>"; case 0:
content += "<h4>Pyro Status PSS4: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss4])) + "</h4>"; content += "0 Value Invalid</h4>";
content += "<h4>Pyro Status PSS6: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss6])) + "</h4>"; break;
case 1:
content += "1 Successfully Blown</h4>";
break;
case 2:
content += "2 Disconnected</h4>";
break;
case 3:
content += "3 Not Activated - Pyro Intact</h4>";
break;
case 4:
content += "4 Unknown</h4>";
break;
default:
content += "Unknown</h4>";
}
content += "<h4>Pyro Status PSS4: ";
switch (datalayer_extended.bmwix.pyro_status_pss4) {
case 0:
content += "0 Value Invalid</h4>";
break;
case 1:
content += "1 Successfully Blown</h4>";
break;
case 2:
content += "2 Disconnected</h4>";
break;
case 3:
content += "3 Not Activated - Pyro Intact</h4>";
break;
case 4:
content += "4 Unknown</h4>";
break;
default:
content += "Unknown</h4>";
}
content += "<h4>Pyro Status PSS6: ";
switch (datalayer_extended.bmwix.pyro_status_pss6) {
case 0:
content += "0 Value Invalid</h4>";
break;
case 1:
content += "1 Successfully Blown</h4>";
break;
case 2:
content += "2 Disconnected</h4>";
break;
case 3:
content += "3 Not Activated - Pyro Intact</h4>";
break;
case 4:
content += "4 Unknown</h4>";
break;
default:
content += "Unknown</h4>";
}
return content; return content;
} }

View file

@ -15,90 +15,203 @@ class BmwPhevHtmlRenderer : public BatteryHtmlRenderer {
" dV</h4>"; " dV</h4>";
content += "<h4>Allowed Discharge Power: " + String(datalayer.battery.status.max_discharge_power_W) + " W</h4>"; content += "<h4>Allowed Discharge Power: " + String(datalayer.battery.status.max_discharge_power_W) + " W</h4>";
content += "<h4>Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W</h4>"; content += "<h4>Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W</h4>";
static const char* balanceText[5] = {"0 Balancing Inactive - Balancing not needed", "1 Balancing Active", content += "<h4>Balancing: ";
"2 Balancing Inactive - Cells not in rest break wait 10mins", switch (datalayer_extended.bmwphev.balancing_status) {
"3 Balancing Inactive", "4 Unknown"}; case 0:
content += "<h4>Balancing: " + String((balanceText[datalayer_extended.bmwphev.balancing_status])) + "</h4>"; content += String("0 Balancing Inactive - Balancing not needed</h4>");
static const char* pyroText[5] = {"0 Value Invalid", "1 Successfully Blown", "2 Disconnected", break;
"3 Not Activated - Pyro Intact", "4 Unknown"}; case 1:
static const char* statusText[16] = { content += String("1 Balancing Active</h4>");
"Not evaluated", "OK", "Error!", "Invalid signal", "", "", "", "", "", "", "", "", "", "", "", ""}; break;
content += "<h4>Interlock: " + String(statusText[datalayer_extended.bmwphev.ST_interlock]) + "</h4>"; case 2:
content += "<h4>Isolation external: " + String(statusText[datalayer_extended.bmwphev.ST_iso_ext]) + "</h4>"; content += String("2 Balancing Inactive - Cells not in rest break wait 10mins</h4>");
content += "<h4>Isolation internal: " + String(statusText[datalayer_extended.bmwphev.ST_iso_int]) + "</h4>"; break;
content += "<h4>Isolation: " + String(statusText[datalayer_extended.bmwphev.ST_isolation]) + "</h4>"; case 3:
content += "<h4>Cooling valve: " + String(statusText[datalayer_extended.bmwphev.ST_valve_cooling]) + "</h4>"; content += String("3 Balancing Inactive</h4>");
content += "<h4>Emergency: " + String(statusText[datalayer_extended.bmwphev.ST_EMG]) + "</h4>"; break;
static const char* prechargeText[16] = {"Not evaluated", case 4:
"Not active, closing not blocked", content += String("4 Unknown</h4>");
"Error precharge blocked", break;
"Invalid signal", default:
"", content += String("Unknown</h4>");
"", }
"", content += "<h4>Interlock: ";
"", switch (datalayer_extended.bmwphev.ST_interlock) {
"", case 0:
"", content += String("Not Evaluated</h4>");
"", break;
"", case 1:
"", content += String("OK</h4>");
"", break;
"", case 2:
""}; content += String("Error! Not seated!</h4>");
content += "<h4>Precharge: " + String(prechargeText[datalayer_extended.bmwphev.ST_precharge]) + break;
"</h4>"; //Still unclear of enum case 3:
static const char* DCSWText[16] = {"Contactors open", content += String("Invalid signal</h4>");
"Precharge ongoing", break;
"Contactors engaged", default:
"Invalid signal", content += String("Unknown</h4>");
"", }
"", content += "<h4>Isolation external: ";
"", switch (datalayer_extended.bmwphev.ST_iso_ext) {
"", case 0:
"", content += String("Not Evaluated</h4>");
"", break;
"", case 1:
"", content += String("OK</h4>");
"", break;
"", case 2:
"", content += String("Error!</h4>");
""}; break;
content += "<h4>Contactor status: " + String(DCSWText[datalayer_extended.bmwphev.ST_DCSW]) + "</h4>"; case 3:
static const char* contText[16] = {"Contactors OK", content += String("Invalid signal</h4>");
"One contactor welded!", break;
"Two contactors welded!", default:
"Invalid signal", content += String("Unknown</h4>");
"", }
"", content += "<h4>Isolation internal: ";
"", switch (datalayer_extended.bmwphev.ST_iso_int) {
"", case 0:
"", content += String("Not Evaluated</h4>");
"", break;
"", case 1:
"", content += String("OK</h4>");
"", break;
"", case 2:
"", content += String("Error!</h4>");
""}; break;
content += "<h4>Contactor weld: " + String(contText[datalayer_extended.bmwphev.ST_WELD]) + "</h4>"; case 3:
static const char* valveText[16] = {"OK", content += String("Invalid signal</h4>");
"Short circuit to GND", break;
"Short circuit to 12V", default:
"Line break", content += String("Unknown</h4>");
"", }
"", content += "<h4>Isolation: ";
"Driver error", switch (datalayer_extended.bmwphev.ST_isolation) {
"", case 0:
"", content += String("Not Evaluated</h4>");
"", break;
"", case 1:
"", content += String("OK</h4>");
"Stuck", break;
"Stuck", case 2:
"", content += String("Error!</h4>");
"Invalid Signal"}; break;
content += case 3:
"<h4>Cold shutoff valve: " + String(valveText[datalayer_extended.bmwphev.ST_cold_shutoff_valve]) + "</h4>"; content += String("Invalid signal</h4>");
break;
default:
content += String("Unknown</h4>");
}
content += "<h4>Cooling valve: ";
switch (datalayer_extended.bmwphev.ST_valve_cooling) {
case 0:
content += String("Not Evaluated</h4>");
break;
case 1:
content += String("OK</h4>");
break;
case 2:
content += String("Error!</h4>");
break;
case 3:
content += String("Invalid signal</h4>");
break;
default:
content += String("Unknown</h4>");
}
content += "<h4>Emergency: ";
switch (datalayer_extended.bmwphev.ST_EMG) {
case 0:
content += String("Not Evaluated</h4>");
break;
case 1:
content += String("OK</h4>");
break;
case 2:
content += String("Error!</h4>");
break;
case 3:
content += String("Invalid signal</h4>");
break;
default:
content += String("Unknown</h4>");
}
content += "<h4>Precharge: ";
switch (datalayer_extended.bmwphev.ST_precharge) {
case 0:
content += String("Not Evaluated</h4>");
break;
case 1:
content += String("Not active, closing not blocked</h4>");
break;
case 2:
content += String("Error precharge blocked</h4>");
break;
case 3:
content += String("Invalid signal</h4>");
break;
default:
content += String("Unknown</h4>"); //Still unclear of enum
}
content += "<h4>Contactor status: ";
switch (datalayer_extended.bmwphev.ST_DCSW) {
case 0:
content += String("Contactors open</h4>");
break;
case 1:
content += String("Precharge ongoing</h4>");
break;
case 2:
content += String("Contactors engaged</h4>");
break;
case 3:
content += String("Invalid signal</h4>");
break;
default:
content += String("Unknown</h4>");
}
content += "<h4>Contactor weld: ";
switch (datalayer_extended.bmwphev.ST_WELD) {
case 0:
content += String("Contactors OK</h4>");
break;
case 1:
content += String("One contactor welded!</h4>");
break;
case 2:
content += String("Two contactors welded!</h4>");
break;
case 3:
content += String("Invalid signal</h4>");
break;
default:
content += String("Unknown</h4>");
}
content += "<h4>Cold shutoff valve: ";
switch (datalayer_extended.bmwphev.ST_cold_shutoff_valve) {
case 0:
content += String("OK</h4>");
break;
case 1:
content += String("Short circuit to GND</h4>");
break;
case 2:
content += String("Short circuit to 12V</h4>");
break;
case 3:
content += String("Line break</h4>");
break;
case 6:
content += String("Driver error</h4>");
break;
case 12:
case 13:
content += String("Stuck</h4>");
break;
default:
content += String("Invalid Signal</h4>");
}
content += content +=
"<h4>Min Cell Voltage Data Age: " + String(datalayer_extended.bmwphev.min_cell_voltage_data_age) + " ms</h4>"; "<h4>Min Cell Voltage Data Age: " + String(datalayer_extended.bmwphev.min_cell_voltage_data_age) + " ms</h4>";
content += content +=

View file

@ -23,10 +23,10 @@ After battery has been unlocked, you can remove the "USE_ESTIMATED_SOC" from the
#define POLL_MAX_CHARGE_POWER 0x000A #define POLL_MAX_CHARGE_POWER 0x000A
#define UNKNOWN_POLL_3 0x000B //0x00B1 (177 interesting!) #define UNKNOWN_POLL_3 0x000B //0x00B1 (177 interesting!)
#define UNKNOWN_POLL_4 0x000E //0x0B27 (2855 interesting!) #define UNKNOWN_POLL_4 0x000E //0x0B27 (2855 interesting!)
#define UNKNOWN_POLL_5 0x000F //0x00237B (9083 interesting!) #define POLL_TOTAL_CHARGED_AH 0x000F
#define UNKNOWN_POLL_6 0x0010 //0x00231B (8987 interesting!) #define POLL_TOTAL_DISCHARGED_AH 0x0010
#define UNKNOWN_POLL_7 0x0011 //0x0E4E (3662 interesting!) #define POLL_TOTAL_CHARGED_KWH 0x0011
#define UNKNOWN_POLL_8 0x0012 //0x0E27 (3623 interesting) #define POLL_TOTAL_DISCHARGED_KWH 0x0012
#define UNKNOWN_POLL_9 0x0004 //0x0034 (52 interesting!) #define UNKNOWN_POLL_9 0x0004 //0x0034 (52 interesting!)
#define UNKNOWN_POLL_10 0x002A //0x5B #define UNKNOWN_POLL_10 0x002A //0x5B
#define UNKNOWN_POLL_11 0x002E //0x08 (probably module number, or cell number?) #define UNKNOWN_POLL_11 0x002E //0x08 (probably module number, or cell number?)
@ -180,6 +180,9 @@ void BydAttoBattery::
datalayer_battery->status.cell_min_voltage_mV = BMS_lowest_cell_voltage_mV; datalayer_battery->status.cell_min_voltage_mV = BMS_lowest_cell_voltage_mV;
datalayer_battery->status.total_discharged_battery_Wh = BMS_total_discharged_kwh * 1000;
datalayer_battery->status.total_charged_battery_Wh = BMS_total_charged_kwh * 1000;
//Map all cell voltages to the global array //Map all cell voltages to the global array
memcpy(datalayer_battery->status.cell_voltages_mV, battery_cellvoltages, CELLCOUNT_EXTENDED * sizeof(uint16_t)); memcpy(datalayer_battery->status.cell_voltages_mV, battery_cellvoltages, CELLCOUNT_EXTENDED * sizeof(uint16_t));
@ -276,10 +279,10 @@ void BydAttoBattery::
datalayer_bydatto->chargePower = BMS_allowed_charge_power; datalayer_bydatto->chargePower = BMS_allowed_charge_power;
datalayer_bydatto->unknown3 = BMS_unknown3; datalayer_bydatto->unknown3 = BMS_unknown3;
datalayer_bydatto->unknown4 = BMS_unknown4; datalayer_bydatto->unknown4 = BMS_unknown4;
datalayer_bydatto->unknown5 = BMS_unknown5; datalayer_bydatto->total_charged_ah = BMS_total_charged_ah;
datalayer_bydatto->unknown6 = BMS_unknown6; datalayer_bydatto->total_discharged_ah = BMS_total_discharged_ah;
datalayer_bydatto->unknown7 = BMS_unknown7; datalayer_bydatto->total_charged_kwh = BMS_total_charged_kwh;
datalayer_bydatto->unknown8 = BMS_unknown8; datalayer_bydatto->total_discharged_kwh = BMS_total_discharged_kwh;
datalayer_bydatto->unknown9 = BMS_unknown9; datalayer_bydatto->unknown9 = BMS_unknown9;
datalayer_bydatto->unknown10 = BMS_unknown10; datalayer_bydatto->unknown10 = BMS_unknown10;
datalayer_bydatto->unknown11 = BMS_unknown11; datalayer_bydatto->unknown11 = BMS_unknown11;
@ -442,17 +445,17 @@ void BydAttoBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
case UNKNOWN_POLL_4: case UNKNOWN_POLL_4:
BMS_unknown4 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; BMS_unknown4 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break; break;
case UNKNOWN_POLL_5: case POLL_TOTAL_CHARGED_AH:
BMS_unknown5 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; BMS_total_charged_ah = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break; break;
case UNKNOWN_POLL_6: case POLL_TOTAL_DISCHARGED_AH:
BMS_unknown6 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; BMS_total_discharged_ah = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break; break;
case UNKNOWN_POLL_7: case POLL_TOTAL_CHARGED_KWH:
BMS_unknown7 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; BMS_total_charged_kwh = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break; break;
case UNKNOWN_POLL_8: case POLL_TOTAL_DISCHARGED_KWH:
BMS_unknown8 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; BMS_total_discharged_kwh = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
break; break;
case UNKNOWN_POLL_9: case UNKNOWN_POLL_9:
BMS_unknown9 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; BMS_unknown9 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4];
@ -622,26 +625,26 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) {
case UNKNOWN_POLL_4: case UNKNOWN_POLL_4:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_4 & 0xFF00) >> 8); ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_4 & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_4 & 0x00FF); ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_4 & 0x00FF);
poll_state = UNKNOWN_POLL_5; poll_state = POLL_TOTAL_CHARGED_AH;
break; break;
case UNKNOWN_POLL_5: case POLL_TOTAL_CHARGED_AH:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_5 & 0xFF00) >> 8); ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_TOTAL_CHARGED_AH & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_5 & 0x00FF); ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_TOTAL_CHARGED_AH & 0x00FF);
poll_state = UNKNOWN_POLL_6; poll_state = POLL_TOTAL_DISCHARGED_AH;
break; break;
case UNKNOWN_POLL_6: case POLL_TOTAL_DISCHARGED_AH:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_6 & 0xFF00) >> 8); ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_TOTAL_DISCHARGED_AH & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_6 & 0x00FF); ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_TOTAL_DISCHARGED_AH & 0x00FF);
poll_state = UNKNOWN_POLL_7; poll_state = POLL_TOTAL_CHARGED_KWH;
break; break;
case UNKNOWN_POLL_7: case POLL_TOTAL_CHARGED_KWH:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_7 & 0xFF00) >> 8); ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_TOTAL_CHARGED_KWH & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_7 & 0x00FF); ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_TOTAL_CHARGED_KWH & 0x00FF);
poll_state = UNKNOWN_POLL_8; poll_state = POLL_TOTAL_DISCHARGED_KWH;
break; break;
case UNKNOWN_POLL_8: case POLL_TOTAL_DISCHARGED_KWH:
ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_8 & 0xFF00) >> 8); ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_TOTAL_DISCHARGED_KWH & 0xFF00) >> 8);
ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_8 & 0x00FF); ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_TOTAL_DISCHARGED_KWH & 0x00FF);
poll_state = UNKNOWN_POLL_9; poll_state = UNKNOWN_POLL_9;
break; break;
case UNKNOWN_POLL_9: case UNKNOWN_POLL_9:

View file

@ -46,6 +46,7 @@ class BydAttoBattery : public CanBattery {
static constexpr char* Name = "BYD Atto 3"; static constexpr char* Name = "BYD Atto 3";
bool supports_charged_energy() { return true; }
bool supports_reset_crash() { return true; } bool supports_reset_crash() { return true; }
void reset_crash() { datalayer_bydatto->UserRequestCrashReset = true; } void reset_crash() { datalayer_bydatto->UserRequestCrashReset = true; }
@ -117,10 +118,10 @@ class BydAttoBattery : public CanBattery {
uint16_t BMS_allowed_charge_power = 0; uint16_t BMS_allowed_charge_power = 0;
uint16_t BMS_unknown3 = 0; uint16_t BMS_unknown3 = 0;
uint16_t BMS_unknown4 = 0; uint16_t BMS_unknown4 = 0;
uint16_t BMS_unknown5 = 0; uint16_t BMS_total_charged_ah = 0;
uint16_t BMS_unknown6 = 0; uint16_t BMS_total_discharged_ah = 0;
uint16_t BMS_unknown7 = 0; uint16_t BMS_total_charged_kwh = 0;
uint16_t BMS_unknown8 = 0; uint16_t BMS_total_discharged_kwh = 0;
uint16_t BMS_unknown9 = 0; uint16_t BMS_unknown9 = 0;
uint8_t BMS_unknown10 = 0; uint8_t BMS_unknown10 = 0;
uint8_t BMS_unknown11 = 0; uint8_t BMS_unknown11 = 0;

View file

@ -34,10 +34,10 @@ class BydAtto3HtmlRenderer : public BatteryHtmlRenderer {
content += "<h4>Charge power raw: " + String(byd_datalayer->chargePower) + "</h4>"; content += "<h4>Charge power raw: " + String(byd_datalayer->chargePower) + "</h4>";
content += "<h4>Unknown3: " + String(byd_datalayer->unknown3) + "</h4>"; content += "<h4>Unknown3: " + String(byd_datalayer->unknown3) + "</h4>";
content += "<h4>Unknown4: " + String(byd_datalayer->unknown4) + "</h4>"; content += "<h4>Unknown4: " + String(byd_datalayer->unknown4) + "</h4>";
content += "<h4>Unknown5: " + String(byd_datalayer->unknown5) + "</h4>"; content += "<h4>Total charged Ah: " + String(byd_datalayer->total_charged_ah) + "</h4>";
content += "<h4>Unknown6: " + String(byd_datalayer->unknown6) + "</h4>"; content += "<h4>Total discharged Ah: " + String(byd_datalayer->total_discharged_ah) + "</h4>";
content += "<h4>Unknown7: " + String(byd_datalayer->unknown7) + "</h4>"; content += "<h4>Total charged kWh: " + String(byd_datalayer->total_charged_kwh) + "</h4>";
content += "<h4>Unknown8: " + String(byd_datalayer->unknown8) + "</h4>"; content += "<h4>Total discharged kWh: " + String(byd_datalayer->total_discharged_kwh) + "</h4>";
content += "<h4>Unknown9: " + String(byd_datalayer->unknown9) + "</h4>"; content += "<h4>Unknown9: " + String(byd_datalayer->unknown9) + "</h4>";
content += "<h4>Unknown10: " + String(byd_datalayer->unknown10) + "</h4>"; content += "<h4>Unknown10: " + String(byd_datalayer->unknown10) + "</h4>";
content += "<h4>Unknown11: " + String(byd_datalayer->unknown11) + "</h4>"; content += "<h4>Unknown11: " + String(byd_datalayer->unknown11) + "</h4>";

View file

@ -111,11 +111,9 @@ void KiaHyundai64Battery::
} }
void KiaHyundai64Battery::update_number_of_cells() { void KiaHyundai64Battery::update_number_of_cells() {
//If we have cell values and number_of_cells not initialized yet
if (cellvoltages_mv[0] > 0 && datalayer_battery->info.number_of_cells == 0) {
// Check if we have 98S or 90S battery. If the 98th cell is valid range, we are on a 98S battery // Check if we have 98S or 90S battery. If the 98th cell is valid range, we are on a 98S battery
if ((datalayer_battery->status.cell_voltages_mV[97] > 2000) && if ((datalayer_battery->status.cell_voltages_mV[97] > 2000) &&
(datalayer_battery->status.cell_voltages_mV[97] < 4300)) { (datalayer_battery->status.cell_voltages_mV[97] < 4500)) {
datalayer_battery->info.number_of_cells = 98; datalayer_battery->info.number_of_cells = 98;
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV; datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV;
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_98S_DV; datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_98S_DV;
@ -127,7 +125,6 @@ void KiaHyundai64Battery::update_number_of_cells() {
datalayer_battery->info.total_capacity_Wh = 40000; datalayer_battery->info.total_capacity_Wh = 40000;
} }
} }
}
void KiaHyundai64Battery::handle_incoming_can_frame(CAN_frame rx_frame) { void KiaHyundai64Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) { switch (rx_frame.ID) {
@ -364,6 +361,7 @@ void KiaHyundai64Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
} }
break; break;
case 0x26: //Sixth datarow in PID group case 0x26: //Sixth datarow in PID group
if (poll_data_pid == 5) {
//We have read all cells, check that content is valid: //We have read all cells, check that content is valid:
for (uint8_t i = 85; i < 97; ++i) { for (uint8_t i = 85; i < 97; ++i) {
if (cellvoltages_mv[i] < 300) { // Zero the value if it's below 300 if (cellvoltages_mv[i] < 300) { // Zero the value if it's below 300
@ -374,6 +372,7 @@ void KiaHyundai64Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_mv, 98 * sizeof(uint16_t)); memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_mv, 98 * sizeof(uint16_t));
//Update number of cells //Update number of cells
update_number_of_cells(); update_number_of_cells();
}
break; break;
case 0x27: //Seventh datarow in PID group case 0x27: //Seventh datarow in PID group
if (poll_data_pid == 1) { if (poll_data_pid == 1) {

View file

@ -20,119 +20,119 @@ class MebHtmlRenderer : public BatteryHtmlRenderer {
content += "<h4>HVIL status: "; content += "<h4>HVIL status: ";
switch (datalayer_extended.meb.HVIL) { switch (datalayer_extended.meb.HVIL) {
case 0: case 0:
content += String("Init"); content += "Init";
break; break;
case 1: case 1:
content += String("Closed"); content += "Closed";
break; break;
case 2: case 2:
content += String("Open!"); content += "Open!";
break; break;
case 3: case 3:
content += String("Fault"); content += "Fault";
break; break;
default: default:
content += String("?"); content += "?";
} }
content += "</h4><h4>KL30C status: "; content += "</h4><h4>KL30C status: ";
switch (datalayer_extended.meb.BMS_Kl30c_Status) { switch (datalayer_extended.meb.BMS_Kl30c_Status) {
case 0: case 0:
content += String("Init"); content += "Init";
break; break;
case 1: case 1:
content += String("Closed"); content += "Closed";
break; break;
case 2: case 2:
content += String("Open!"); content += "Open!";
break; break;
case 3: case 3:
content += String("Fault"); content += "Fault";
break; break;
default: default:
content += String("?"); content += "?";
} }
content += "</h4><h4>BMS mode: "; content += "</h4><h4>BMS mode: ";
switch (datalayer_extended.meb.BMS_mode) { switch (datalayer_extended.meb.BMS_mode) {
case 0: case 0:
content += String("HV inactive"); content += "HV inactive";
break; break;
case 1: case 1:
content += String("HV active"); content += "HV active";
break; break;
case 2: case 2:
content += String("Balancing"); content += "Balancing";
break; break;
case 3: case 3:
content += String("Extern charging"); content += "Extern charging";
break; break;
case 4: case 4:
content += String("AC charging"); content += "AC charging";
break; break;
case 5: case 5:
content += String("Battery error"); content += "Battery error";
break; break;
case 6: case 6:
content += String("DC charging"); content += "DC charging";
break; break;
case 7: case 7:
content += String("Init"); content += "Init";
break; break;
default: default:
content += String("?"); content += "?";
} }
content += String("</h4><h4>Charging: ") + (datalayer_extended.meb.charging_active ? "active" : "not active"); content += String("</h4><h4>Charging: ") + (datalayer_extended.meb.charging_active ? "active" : "not active");
content += String("</h4><h4>Balancing: "); content += String("</h4><h4>Balancing: ");
switch (datalayer_extended.meb.balancing_active) { switch (datalayer_extended.meb.balancing_active) {
case 0: case 0:
content += String("init"); content += "init";
break; break;
case 1: case 1:
content += String("active"); content += "active";
break; break;
case 2: case 2:
content += String("inactive"); content += "inactive";
break; break;
default: default:
content += String("?"); content += "?";
} }
content += content +=
String("</h4><h4>Slow charging: ") + (datalayer_extended.meb.balancing_request ? "requested" : "not requested"); String("</h4><h4>Slow charging: ") + (datalayer_extended.meb.balancing_request ? "requested" : "not requested");
content += "</h4><h4>Diagnostic: "; content += "</h4><h4>Diagnostic: ";
switch (datalayer_extended.meb.battery_diagnostic) { switch (datalayer_extended.meb.battery_diagnostic) {
case 0: case 0:
content += String("Init"); content += "Init";
break; break;
case 1: case 1:
content += String("Battery display"); content += "Battery display";
break; break;
case 4: case 4:
content += String("Battery display OK"); content += "Battery display OK";
break; break;
case 6: case 6:
content += String("Battery display check"); content += "Battery display check";
break; break;
case 7: case 7:
content += String("Fault"); content += "Fault";
break; break;
default: default:
content += String("?"); content += "?";
} }
content += "</h4><h4>HV line status: "; content += "</h4><h4>HV line status: ";
switch (datalayer_extended.meb.status_HV_line) { switch (datalayer_extended.meb.status_HV_line) {
case 0: case 0:
content += String("Init"); content += "Init";
break; break;
case 1: case 1:
content += String("No open HV line detected"); content += "No open HV line detected";
break; break;
case 2: case 2:
content += String("Open HV line"); content += "Open HV line";
break; break;
case 3: case 3:
content += String("Fault"); content += "Fault";
break; break;
default: default:
content += String("? ") + String(datalayer_extended.meb.status_HV_line); content += "? " + String(datalayer_extended.meb.status_HV_line);
} }
content += "</h4>"; content += "</h4>";
content += datalayer_extended.meb.BMS_fault_performance ? "<h4>BMS fault performance: Active!</h4>" content += datalayer_extended.meb.BMS_fault_performance ? "<h4>BMS fault performance: Active!</h4>"
@ -147,83 +147,83 @@ class MebHtmlRenderer : public BatteryHtmlRenderer {
content += "<h4>Welded contactors: "; content += "<h4>Welded contactors: ";
switch (datalayer_extended.meb.BMS_welded_contactors_status) { switch (datalayer_extended.meb.BMS_welded_contactors_status) {
case 0: case 0:
content += String("Init"); content += "Init";
break; break;
case 1: case 1:
content += String("No contactor welded"); content += "No contactor welded";
break; break;
case 2: case 2:
content += String("At least 1 contactor welded"); content += "At least 1 contactor welded";
break; break;
case 3: case 3:
content += String("Protection status detection error"); content += "Protection status detection error";
break; break;
default: default:
content += String("?"); content += "?";
} }
content += "</h4><h4>Warning support: "; content += "</h4><h4>Warning support: ";
switch (datalayer_extended.meb.warning_support) { switch (datalayer_extended.meb.warning_support) {
case 0: case 0:
content += String("OK"); content += "OK";
break; break;
case 1: case 1:
content += String("Not OK"); content += "Not OK";
break; break;
case 6: case 6:
content += String("Init"); content += "Init";
break; break;
case 7: case 7:
content += String("Fault"); content += "Fault";
break; break;
default: default:
content += String("?"); content += "?";
} }
content += "</h4><h4>Interm. Voltage (" + String(datalayer_extended.meb.BMS_voltage_intermediate_dV / 10.0, 1) + content += "</h4><h4>Interm. Voltage (" + String(datalayer_extended.meb.BMS_voltage_intermediate_dV / 10.0, 1) +
"V) status: "; "V) status: ";
switch (datalayer_extended.meb.BMS_status_voltage_free) { switch (datalayer_extended.meb.BMS_status_voltage_free) {
case 0: case 0:
content += String("Init"); content += "Init";
break; break;
case 1: case 1:
content += String("BMS interm circuit voltage free (U<20V)"); content += "BMS interm circuit voltage free (U<20V)";
break; break;
case 2: case 2:
content += String("BMS interm circuit not voltage free (U >= 25V)"); content += "BMS interm circuit not voltage free (U >= 25V)";
break; break;
case 3: case 3:
content += String("Error"); content += "Error";
break; break;
default: default:
content += String("?"); content += "?";
} }
content += "</h4><h4>BMS error status: "; content += "</h4><h4>BMS error status: ";
switch (datalayer_extended.meb.BMS_error_status) { switch (datalayer_extended.meb.BMS_error_status) {
case 0: case 0:
content += String("Component IO"); content += "Component IO";
break; break;
case 1: case 1:
content += String("Iso Error 1"); content += "Iso Error 1";
break; break;
case 2: case 2:
content += String("Iso Error 2"); content += "Iso Error 2";
break; break;
case 3: case 3:
content += String("Interlock"); content += "Interlock";
break; break;
case 4: case 4:
content += String("SD"); content += "SD";
break; break;
case 5: case 5:
content += String("Performance red"); content += "Performance red";
break; break;
case 6: case 6:
content += String("No component function"); content += "No component function";
break; break;
case 7: case 7:
content += String("Init"); content += "Init";
break; break;
default: default:
content += String("?"); content += "?";
} }
content += "</h4><h4>BMS voltage: " + String(datalayer_extended.meb.BMS_voltage_dV / 10.0, 1) + "</h4>"; content += "</h4><h4>BMS voltage: " + String(datalayer_extended.meb.BMS_voltage_dV / 10.0, 1) + "</h4>";
content += datalayer_extended.meb.BMS_OBD_MIL ? "<h4>OBD MIL: ON!</h4>" : "<h4>OBD MIL: Off</h4>"; content += datalayer_extended.meb.BMS_OBD_MIL ? "<h4>OBD MIL: ON!</h4>" : "<h4>OBD MIL: Off</h4>";

View file

@ -10,8 +10,20 @@ class NissanLeafHtmlRenderer : public BatteryHtmlRenderer {
String get_status_html() { String get_status_html() {
String content; String content;
static const char* LEAFgen[] = {"ZE0", "AZE0", "ZE1"}; content += "<h4>LEAF generation: ";
content += "<h4>LEAF generation: " + String(LEAFgen[datalayer_extended.nissanleaf.LEAF_gen]) + "</h4>"; switch (datalayer_extended.nissanleaf.LEAF_gen) {
case 0:
content += String("ZE0</h4>");
break;
case 1:
content += String("AZE0</h4>");
break;
case 2:
content += String("ZE1</h4>");
break;
default:
content += String("Unknown</h4>");
}
char readableSerialNumber[16]; // One extra space for null terminator char readableSerialNumber[16]; // One extra space for null terminator
memcpy(readableSerialNumber, datalayer_extended.nissanleaf.BatterySerialNumber, memcpy(readableSerialNumber, datalayer_extended.nissanleaf.BatterySerialNumber,
sizeof(datalayer_extended.nissanleaf.BatterySerialNumber)); sizeof(datalayer_extended.nissanleaf.BatterySerialNumber));

View file

@ -197,10 +197,10 @@ typedef struct {
uint16_t chargePower = 0; uint16_t chargePower = 0;
uint16_t unknown3 = 0; uint16_t unknown3 = 0;
uint16_t unknown4 = 0; uint16_t unknown4 = 0;
uint16_t unknown5 = 0; uint16_t total_charged_ah = 0;
uint16_t unknown6 = 0; uint16_t total_discharged_ah = 0;
uint16_t unknown7 = 0; uint16_t total_charged_kwh = 0;
uint16_t unknown8 = 0; uint16_t total_discharged_kwh = 0;
uint16_t unknown9 = 0; uint16_t unknown9 = 0;
uint8_t unknown10 = 0; uint8_t unknown10 = 0;
uint8_t unknown11 = 0; uint8_t unknown11 = 0;