mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
Merge branch 'main' into hal2
This commit is contained in:
commit
c63ae6eb23
20 changed files with 588 additions and 328 deletions
|
@ -142,7 +142,7 @@
|
||||||
#define HA_AUTODISCOVERY // Enable this line to send Home Assistant autodiscovery messages. If not enabled manual configuration of Home Assitant is required
|
#define HA_AUTODISCOVERY // Enable this line to send Home Assistant autodiscovery messages. If not enabled manual configuration of Home Assitant is required
|
||||||
|
|
||||||
/* Battery settings */
|
/* Battery settings */
|
||||||
// Predefined total energy capacity of the battery in Watt-hours
|
// Predefined total energy capacity of the battery in Watt-hours (updates automatically from battery data when available)
|
||||||
#define BATTERY_WH_MAX 30000
|
#define BATTERY_WH_MAX 30000
|
||||||
// Increases battery life. If true will rescale SOC between the configured min/max-percentage
|
// Increases battery life. If true will rescale SOC between the configured min/max-percentage
|
||||||
#define BATTERY_USE_SCALED_SOC true
|
#define BATTERY_USE_SCALED_SOC true
|
||||||
|
|
|
@ -21,7 +21,6 @@ class BmwI3Battery : public CanBattery {
|
||||||
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
contactor_closing_allowed = contactor_closing_allowed_ptr;
|
||||||
allows_contactor_closing = nullptr;
|
allows_contactor_closing = nullptr;
|
||||||
wakeup_pin = wakeup;
|
wakeup_pin = wakeup;
|
||||||
*allows_contactor_closing = true;
|
|
||||||
|
|
||||||
//Init voltage to 0 to allow contactor check to operate without fear of default values colliding
|
//Init voltage to 0 to allow contactor check to operate without fear of default values colliding
|
||||||
battery_volts = 0;
|
battery_volts = 0;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 +=
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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>";
|
||||||
|
|
|
@ -20,7 +20,9 @@ void ChademoBattery::update_values() {
|
||||||
datalayer.battery.status.max_discharge_power_W =
|
datalayer.battery.status.max_discharge_power_W =
|
||||||
(x200_discharge_limits.MaximumDischargeCurrent * x100_chg_lim.MaximumBatteryVoltage); //In Watts, Convert A to P
|
(x200_discharge_limits.MaximumDischargeCurrent * x100_chg_lim.MaximumBatteryVoltage); //In Watts, Convert A to P
|
||||||
|
|
||||||
|
if (vehicle_can_received) { // Only update the value sent towards inverter if vehicle is connected (avoids false positive events)
|
||||||
datalayer.battery.status.voltage_dV = get_measured_voltage() * 10;
|
datalayer.battery.status.voltage_dV = get_measured_voltage() * 10;
|
||||||
|
}
|
||||||
|
|
||||||
datalayer.battery.info.total_capacity_Wh = (x101_chg_est.RatedBatteryCapacity * 100);
|
datalayer.battery.info.total_capacity_Wh = (x101_chg_est.RatedBatteryCapacity * 100);
|
||||||
//(Added in CHAdeMO v1.0.1), maybe handle hardcoded on lower protocol version?
|
//(Added in CHAdeMO v1.0.1), maybe handle hardcoded on lower protocol version?
|
||||||
|
@ -201,7 +203,7 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
logging.println("UNHANDLED STATE IN process_vehicle_charging_session()");
|
logging.println("UNHANDLED CHADEMO STATE, try unplugging chademo cable, reboot emulator, and retry!");
|
||||||
#endif
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -228,6 +230,10 @@ void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) {
|
||||||
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) {
|
if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) {
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
logging.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
logging.println("x200 minimum discharge voltage met or exceeded, stopping.");
|
||||||
|
logging.print("Measured: ");
|
||||||
|
logging.print(get_measured_voltage());
|
||||||
|
logging.print("Minimum voltage: ");
|
||||||
|
logging.print(x200_discharge_limits.MinimumDischargeVoltage);
|
||||||
#endif
|
#endif
|
||||||
CHADEMO_Status = CHADEMO_STOP;
|
CHADEMO_Status = CHADEMO_STOP;
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,7 +140,7 @@ uint8_t CHADEMO_seq = 0x0;
|
||||||
} status;
|
} status;
|
||||||
} s;
|
} s;
|
||||||
|
|
||||||
uint8_t StateOfCharge = 0; //6 state of charge?
|
uint8_t StateOfCharge = 50; //6 state of charge?
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ---------- CHARGING: EVSE Data structures */
|
/* ---------- CHARGING: EVSE Data structures */
|
||||||
|
@ -193,7 +193,7 @@ uint8_t CHADEMO_seq = 0x0;
|
||||||
//H200 - Vehicle - Discharge limits
|
//H200 - Vehicle - Discharge limits
|
||||||
struct x200_Vehicle_Discharge_Limits {
|
struct x200_Vehicle_Discharge_Limits {
|
||||||
uint8_t MaximumDischargeCurrent = 0xFF;
|
uint8_t MaximumDischargeCurrent = 0xFF;
|
||||||
uint16_t MinimumDischargeVoltage = 0;
|
uint16_t MinimumDischargeVoltage = 260; //Initialized to a semi-sane value, updates via CAN later
|
||||||
uint16_t MinimumBatteryDischargeLevel = 0;
|
uint16_t MinimumBatteryDischargeLevel = 0;
|
||||||
uint16_t MaxRemainingCapacityForCharging = 0;
|
uint16_t MaxRemainingCapacityForCharging = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,68 +27,68 @@ uint16_t CmfaEvBattery::rescale_raw_SOC(uint32_t raw_SOC) {
|
||||||
|
|
||||||
void CmfaEvBattery::
|
void CmfaEvBattery::
|
||||||
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
update_values() { //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 = rescale_raw_SOC(SOC_raw);
|
datalayer_battery->status.real_soc = rescale_raw_SOC(SOC_raw);
|
||||||
|
|
||||||
datalayer.battery.status.current_dA = current * 10;
|
datalayer_battery->status.current_dA = current * 10;
|
||||||
|
|
||||||
datalayer.battery.status.voltage_dV = average_voltage_of_cells / 100;
|
datalayer_battery->status.voltage_dV = average_voltage_of_cells / 100;
|
||||||
|
|
||||||
datalayer.battery.info.total_capacity_Wh = 27000;
|
datalayer_battery->info.total_capacity_Wh = 27000;
|
||||||
|
|
||||||
//Calculate the remaining Wh amount from SOC% and max Wh value.
|
//Calculate the remaining Wh amount from SOC% and max Wh value.
|
||||||
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
|
datalayer_battery->status.remaining_capacity_Wh = static_cast<uint32_t>(
|
||||||
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
|
(static_cast<double>(datalayer_battery->status.real_soc) / 10000) * datalayer_battery->info.total_capacity_Wh);
|
||||||
|
|
||||||
datalayer.battery.status.max_discharge_power_W = discharge_power_w;
|
datalayer_battery->status.max_discharge_power_W = discharge_power_w;
|
||||||
|
|
||||||
datalayer.battery.status.max_charge_power_W = charge_power_w;
|
datalayer_battery->status.max_charge_power_W = charge_power_w;
|
||||||
|
|
||||||
datalayer.battery.status.temperature_min_dC = (lowest_cell_temperature * 10);
|
datalayer_battery->status.temperature_min_dC = (lowest_cell_temperature * 10);
|
||||||
|
|
||||||
datalayer.battery.status.temperature_max_dC = (highest_cell_temperature * 10);
|
datalayer_battery->status.temperature_max_dC = (highest_cell_temperature * 10);
|
||||||
|
|
||||||
datalayer.battery.status.cell_min_voltage_mV = lowest_cell_voltage_mv;
|
datalayer_battery->status.cell_min_voltage_mV = lowest_cell_voltage_mv;
|
||||||
|
|
||||||
datalayer.battery.status.cell_max_voltage_mV = highest_cell_voltage_mv;
|
datalayer_battery->status.cell_max_voltage_mV = highest_cell_voltage_mv;
|
||||||
|
|
||||||
//Map all cell voltages to the global array
|
//Map all cell voltages to the global array
|
||||||
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_mv, 72 * sizeof(uint16_t));
|
memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_mv, 72 * sizeof(uint16_t));
|
||||||
|
|
||||||
if (lead_acid_voltage < 11000) { //11.000V
|
if (lead_acid_voltage < 11000) { //11.000V
|
||||||
set_event(EVENT_12V_LOW, lead_acid_voltage);
|
set_event(EVENT_12V_LOW, lead_acid_voltage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update webserver datalayer
|
// Update webserver datalayer
|
||||||
datalayer_extended.CMFAEV.soc_u = soc_u;
|
datalayer_cmfa->soc_u = soc_u;
|
||||||
datalayer_extended.CMFAEV.soc_z = soc_z;
|
datalayer_cmfa->soc_z = soc_z;
|
||||||
datalayer_extended.CMFAEV.lead_acid_voltage = lead_acid_voltage;
|
datalayer_cmfa->lead_acid_voltage = lead_acid_voltage;
|
||||||
datalayer_extended.CMFAEV.highest_cell_voltage_number = highest_cell_voltage_number;
|
datalayer_cmfa->highest_cell_voltage_number = highest_cell_voltage_number;
|
||||||
datalayer_extended.CMFAEV.lowest_cell_voltage_number = lowest_cell_voltage_number;
|
datalayer_cmfa->lowest_cell_voltage_number = lowest_cell_voltage_number;
|
||||||
datalayer_extended.CMFAEV.max_regen_power = max_regen_power;
|
datalayer_cmfa->max_regen_power = max_regen_power;
|
||||||
datalayer_extended.CMFAEV.max_discharge_power = max_discharge_power;
|
datalayer_cmfa->max_discharge_power = max_discharge_power;
|
||||||
datalayer_extended.CMFAEV.average_temperature = average_temperature;
|
datalayer_cmfa->average_temperature = average_temperature;
|
||||||
datalayer_extended.CMFAEV.minimum_temperature = minimum_temperature;
|
datalayer_cmfa->minimum_temperature = minimum_temperature;
|
||||||
datalayer_extended.CMFAEV.maximum_temperature = maximum_temperature;
|
datalayer_cmfa->maximum_temperature = maximum_temperature;
|
||||||
datalayer_extended.CMFAEV.maximum_charge_power = maximum_charge_power;
|
datalayer_cmfa->maximum_charge_power = maximum_charge_power;
|
||||||
datalayer_extended.CMFAEV.SOH_available_power = SOH_available_power;
|
datalayer_cmfa->SOH_available_power = SOH_available_power;
|
||||||
datalayer_extended.CMFAEV.SOH_generated_power = SOH_generated_power;
|
datalayer_cmfa->SOH_generated_power = SOH_generated_power;
|
||||||
datalayer_extended.CMFAEV.cumulative_energy_when_discharging = cumulative_energy_when_discharging;
|
datalayer_cmfa->cumulative_energy_when_discharging = cumulative_energy_when_discharging;
|
||||||
datalayer_extended.CMFAEV.cumulative_energy_when_charging = cumulative_energy_when_charging;
|
datalayer_cmfa->cumulative_energy_when_charging = cumulative_energy_when_charging;
|
||||||
datalayer_extended.CMFAEV.cumulative_energy_in_regen = cumulative_energy_in_regen;
|
datalayer_cmfa->cumulative_energy_in_regen = cumulative_energy_in_regen;
|
||||||
datalayer_extended.CMFAEV.soh_average = soh_average;
|
datalayer_cmfa->soh_average = soh_average;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmfaEvBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
void CmfaEvBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
switch (rx_frame.ID) { //These frames are transmitted by the battery
|
switch (rx_frame.ID) { //These frames are transmitted by the battery
|
||||||
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_raw = ((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;
|
||||||
charge_power_w = rx_frame.data.u8[2] * 500;
|
charge_power_w = rx_frame.data.u8[2] * 500;
|
||||||
discharge_power_w = rx_frame.data.u8[3] * 500;
|
discharge_power_w = rx_frame.data.u8[3] * 500;
|
||||||
lowest_cell_temperature = (rx_frame.data.u8[4] - 40);
|
lowest_cell_temperature = (rx_frame.data.u8[4] - 40);
|
||||||
|
@ -97,34 +97,34 @@ void CmfaEvBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
highest_cell_temperature = (rx_frame.data.u8[7] - 40);
|
highest_cell_temperature = (rx_frame.data.u8[7] - 40);
|
||||||
break;
|
break;
|
||||||
case 0x3D7: //100ms
|
case 0x3D7: //100ms
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
pack_voltage = ((rx_frame.data.u8[6] << 4 | (rx_frame.data.u8[5] & 0x0F)));
|
pack_voltage = ((rx_frame.data.u8[6] << 4 | (rx_frame.data.u8[5] & 0x0F)));
|
||||||
break;
|
break;
|
||||||
case 0x3D8: //100ms
|
case 0x3D8: //100ms
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
//counter_3D8 = rx_frame.data.u8[3]; //?
|
//counter_3D8 = rx_frame.data.u8[3]; //?
|
||||||
//CRC_3D8 = rx_frame.data.u8[4]; //?
|
//CRC_3D8 = rx_frame.data.u8[4]; //?
|
||||||
break;
|
break;
|
||||||
case 0x43C: //100ms
|
case 0x43C: //100ms
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
heartbeat2 = rx_frame.data.u8[2]; //Alternates between 0x55 and 0xAA every 5th frame
|
heartbeat2 = rx_frame.data.u8[2]; //Alternates between 0x55 and 0xAA every 5th frame
|
||||||
break;
|
break;
|
||||||
case 0x431: //100ms
|
case 0x431: //100ms
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
//byte0 9C always
|
//byte0 9C always
|
||||||
//byte1 40 always
|
//byte1 40 always
|
||||||
break;
|
break;
|
||||||
case 0x5A9:
|
case 0x5A9:
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
break;
|
break;
|
||||||
case 0x5AB:
|
case 0x5AB:
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
break;
|
break;
|
||||||
case 0x5C8:
|
case 0x5C8:
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
break;
|
break;
|
||||||
case 0x5E1:
|
case 0x5E1:
|
||||||
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
|
||||||
break;
|
break;
|
||||||
case 0x7BB: // Reply from battery
|
case 0x7BB: // Reply from battery
|
||||||
if (rx_frame.data.u8[0] == 0x10) { //PID header
|
if (rx_frame.data.u8[0] == 0x10) { //PID header
|
||||||
|
@ -943,10 +943,10 @@ void CmfaEvBattery::setup(void) { // Performs one time setup at startup
|
||||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||||
datalayer.system.info.battery_protocol[63] = '\0';
|
datalayer.system.info.battery_protocol[63] = '\0';
|
||||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||||
datalayer.battery.info.number_of_cells = 72;
|
datalayer_battery->info.number_of_cells = 72;
|
||||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||||
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
datalayer_battery->info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
|
||||||
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
|
||||||
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
|
datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,23 @@
|
||||||
|
|
||||||
class CmfaEvBattery : public CanBattery {
|
class CmfaEvBattery : public CanBattery {
|
||||||
public:
|
public:
|
||||||
|
// Use this constructor for the second battery.
|
||||||
|
CmfaEvBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_CMFAEV* extended, CAN_Interface targetCan)
|
||||||
|
: CanBattery(targetCan) {
|
||||||
|
datalayer_battery = datalayer_ptr;
|
||||||
|
allows_contactor_closing = nullptr;
|
||||||
|
datalayer_cmfa = extended;
|
||||||
|
|
||||||
|
average_voltage_of_cells = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the default constructor to create the first or single battery.
|
||||||
|
CmfaEvBattery() {
|
||||||
|
datalayer_battery = &datalayer.battery;
|
||||||
|
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
|
||||||
|
datalayer_cmfa = &datalayer_extended.CMFAEV;
|
||||||
|
}
|
||||||
|
|
||||||
virtual void setup(void);
|
virtual void setup(void);
|
||||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||||
virtual void update_values();
|
virtual void update_values();
|
||||||
|
@ -22,6 +39,12 @@ class CmfaEvBattery : public CanBattery {
|
||||||
private:
|
private:
|
||||||
CmfaEvHtmlRenderer renderer;
|
CmfaEvHtmlRenderer renderer;
|
||||||
|
|
||||||
|
DATALAYER_BATTERY_TYPE* datalayer_battery;
|
||||||
|
DATALAYER_INFO_CMFAEV* datalayer_cmfa;
|
||||||
|
|
||||||
|
// If not null, this battery decides when the contactor can be closed and writes the value here.
|
||||||
|
bool* allows_contactor_closing;
|
||||||
|
|
||||||
uint16_t rescale_raw_SOC(uint32_t raw_SOC);
|
uint16_t rescale_raw_SOC(uint32_t raw_SOC);
|
||||||
|
|
||||||
static const int MAX_PACK_VOLTAGE_DV = 3040; // 5000 = 500.0V
|
static const int MAX_PACK_VOLTAGE_DV = 3040; // 5000 = 500.0V
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
|
|
||||||
/* TODO:
|
/* TODO:
|
||||||
This integration is still ongoing. Here is what still needs to be done in order to use this battery type
|
This integration is still ongoing. Here is what still needs to be done in order to use this battery type
|
||||||
- Disable the isolation resistance requirement that opens contactors after 30s
|
- Disable the isolation resistance requirement that opens contactors after 30s under load. Factory mode?
|
||||||
- Battery says it might need 37E and 485, but no logs of this?
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 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 */
|
||||||
|
@ -20,7 +19,7 @@ void EcmpBattery::update_values() {
|
||||||
|
|
||||||
datalayer.battery.status.voltage_dV = battery_voltage * 10;
|
datalayer.battery.status.voltage_dV = battery_voltage * 10;
|
||||||
|
|
||||||
datalayer.battery.status.current_dA = battery_current * 10;
|
datalayer.battery.status.current_dA = -(battery_current * 10);
|
||||||
|
|
||||||
datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt
|
datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt
|
||||||
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
||||||
|
@ -374,19 +373,31 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
if (datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring) {
|
if (datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring) {
|
||||||
if ((rx_frame.data.u8[0] == 0x06) && (rx_frame.data.u8[1] == 0x50) && (rx_frame.data.u8[2] == 0x03)) {
|
if ((rx_frame.data.u8[0] == 0x06) && (rx_frame.data.u8[1] == 0x50) && (rx_frame.data.u8[2] == 0x03)) {
|
||||||
//06,50,03,00,C8,00,14,00,
|
//06,50,03,00,C8,00,14,00,
|
||||||
DisableIsoMonitoringStatemachine = 2; //Send ECMP_FACTORY_MODE_ACTIVATION next loop
|
DisableIsoMonitoringStatemachine = 2; //Send ECMP_ACK_MESSAGE (02 3e 00)
|
||||||
}
|
}
|
||||||
//if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F) && (rx_frame.data.u8[2] == 0x2E)) {
|
if ((rx_frame.data.u8[0] == 0x02) && (rx_frame.data.u8[1] == 0x7E) && (rx_frame.data.u8[2] == 0x00)) {
|
||||||
if (DisableIsoMonitoringStatemachine == 3) {
|
//Expected 02,7E,00
|
||||||
DisableIsoMonitoringStatemachine = 4;
|
DisableIsoMonitoringStatemachine = 4; //Send ECMP_FACTORY_MODE_ACTIVATION next loop
|
||||||
}
|
}
|
||||||
//if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F) && (rx_frame.data.u8[2] == 0x2E)) {
|
if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x6E) && (rx_frame.data.u8[2] == 0xD9)) {
|
||||||
if (DisableIsoMonitoringStatemachine == 5) {
|
//Factory mode ENTRY: 2E.D9.00.01
|
||||||
DisableIsoMonitoringStatemachine = 6;
|
DisableIsoMonitoringStatemachine = 6; //Send ECMP_DISABLE_ISOLATION_REQ next loop
|
||||||
}
|
}
|
||||||
//if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F) && (rx_frame.data.u8[2] == 0x31)) {
|
if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F) && (rx_frame.data.u8[2] == 0x2E)) {
|
||||||
if (DisableIsoMonitoringStatemachine == 7) {
|
//Factory mode fails to enter with 7F
|
||||||
//UNKNOWN? 03,7F,31,24 (or 7F?)
|
set_event(EVENT_PID_FAILED, rx_frame.data.u8[2]);
|
||||||
|
DisableIsoMonitoringStatemachine =
|
||||||
|
6; //Send ECMP_DISABLE_ISOLATION_REQ next loop (pointless, since it will fail)
|
||||||
|
}
|
||||||
|
if ((rx_frame.data.u8[0] == 0x04) && (rx_frame.data.u8[1] == 0x31) && (rx_frame.data.u8[2] == 0x02)) {
|
||||||
|
//Disable isolation successful 04 31 02 df e1
|
||||||
|
DisableIsoMonitoringStatemachine = COMPLETED_STATE;
|
||||||
|
datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring = false;
|
||||||
|
timeSpentDisableIsoMonitoring = COMPLETED_STATE;
|
||||||
|
}
|
||||||
|
if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F) && (rx_frame.data.u8[2] == 0x31)) {
|
||||||
|
//Disable Isolation fails to enter with 7F
|
||||||
|
set_event(EVENT_PID_FAILED, rx_frame.data.u8[2]);
|
||||||
DisableIsoMonitoringStatemachine = COMPLETED_STATE;
|
DisableIsoMonitoringStatemachine = COMPLETED_STATE;
|
||||||
datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring = false;
|
datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring = false;
|
||||||
timeSpentDisableIsoMonitoring = COMPLETED_STATE;
|
timeSpentDisableIsoMonitoring = COMPLETED_STATE;
|
||||||
|
@ -534,10 +545,10 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
pid_avg_cell_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
pid_avg_cell_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
||||||
break;
|
break;
|
||||||
case PID_CURRENT:
|
case PID_CURRENT:
|
||||||
pid_current = (((rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16) | (rx_frame.data.u8[6] << 8) |
|
pid_current = -(((rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16) | (rx_frame.data.u8[6] << 8) |
|
||||||
rx_frame.data.u8[7]) -
|
rx_frame.data.u8[7]) -
|
||||||
76800) *
|
76800) *
|
||||||
10;
|
20;
|
||||||
break;
|
break;
|
||||||
case PID_INSULATION_NEG:
|
case PID_INSULATION_NEG:
|
||||||
pid_insulation_res_neg = ((rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16) |
|
pid_insulation_res_neg = ((rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16) |
|
||||||
|
@ -578,7 +589,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
|
||||||
pid_lowest_cell_voltage_num = (rx_frame.data.u8[4]);
|
pid_lowest_cell_voltage_num = (rx_frame.data.u8[4]);
|
||||||
break;
|
break;
|
||||||
case PID_SUM_OF_CELLS:
|
case PID_SUM_OF_CELLS:
|
||||||
pid_sum_of_cells = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
pid_sum_of_cells = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 2;
|
||||||
break;
|
break;
|
||||||
case PID_CELL_MIN_CAPACITY:
|
case PID_CELL_MIN_CAPACITY:
|
||||||
pid_cell_min_capacity = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
pid_cell_min_capacity = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
|
||||||
|
@ -845,15 +856,15 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
|
||||||
DisableIsoMonitoringStatemachine = 1;
|
DisableIsoMonitoringStatemachine = 1;
|
||||||
}
|
}
|
||||||
if (DisableIsoMonitoringStatemachine == 2) {
|
if (DisableIsoMonitoringStatemachine == 2) {
|
||||||
transmit_can_frame(&ECMP_FACTORY_MODE_ACTIVATION_NEW, can_config.battery);
|
transmit_can_frame(&ECMP_ACK_MESSAGE, can_config.battery);
|
||||||
DisableIsoMonitoringStatemachine = 3;
|
DisableIsoMonitoringStatemachine = 3;
|
||||||
}
|
}
|
||||||
if (DisableIsoMonitoringStatemachine == 4) {
|
if (DisableIsoMonitoringStatemachine == 4) {
|
||||||
transmit_can_frame(&ECMP_DISABLE_ISOLATION_REQ, can_config.battery);
|
transmit_can_frame(&ECMP_FACTORY_MODE_ACTIVATION, can_config.battery);
|
||||||
DisableIsoMonitoringStatemachine = 5;
|
DisableIsoMonitoringStatemachine = 5;
|
||||||
}
|
}
|
||||||
if (DisableIsoMonitoringStatemachine == 6) {
|
if (DisableIsoMonitoringStatemachine == 6) {
|
||||||
transmit_can_frame(&ECMP_FACTORY_MODE_ACTIVATION, can_config.battery);
|
transmit_can_frame(&ECMP_DISABLE_ISOLATION_REQ, can_config.battery);
|
||||||
DisableIsoMonitoringStatemachine = 7;
|
DisableIsoMonitoringStatemachine = 7;
|
||||||
}
|
}
|
||||||
timeSpentDisableIsoMonitoring++;
|
timeSpentDisableIsoMonitoring++;
|
||||||
|
@ -1321,10 +1332,12 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
|
||||||
transmit_can_frame(&ECMP_0C5, can_config.battery); //DC2_0C5
|
transmit_can_frame(&ECMP_0C5, can_config.battery); //DC2_0C5
|
||||||
transmit_can_frame(&ECMP_17B, can_config.battery); //VCU_PCANInfo_17B
|
transmit_can_frame(&ECMP_17B, can_config.battery); //VCU_PCANInfo_17B
|
||||||
transmit_can_frame(&ECMP_0F2, can_config.battery); //CtrlMCU1_0F2
|
transmit_can_frame(&ECMP_0F2, can_config.battery); //CtrlMCU1_0F2
|
||||||
|
if (simulateEntireCar) {
|
||||||
transmit_can_frame(&ECMP_111, can_config.battery);
|
transmit_can_frame(&ECMP_111, can_config.battery);
|
||||||
transmit_can_frame(&ECMP_110, can_config.battery);
|
transmit_can_frame(&ECMP_110, can_config.battery);
|
||||||
transmit_can_frame(&ECMP_114, can_config.battery);
|
transmit_can_frame(&ECMP_114, can_config.battery);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send 20ms periodic CAN Message simulating the car still being attached
|
// Send 20ms periodic CAN Message simulating the car still being attached
|
||||||
if (currentMillis - previousMillis20 >= INTERVAL_20_MS) {
|
if (currentMillis - previousMillis20 >= INTERVAL_20_MS) {
|
||||||
|
@ -1355,7 +1368,7 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
|
||||||
ECMP_27A.data = {0x4F, 0x58, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00};
|
ECMP_27A.data = {0x4F, 0x58, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00};
|
||||||
}
|
}
|
||||||
transmit_can_frame(&ECMP_230, can_config.battery); //OBC3_230
|
transmit_can_frame(&ECMP_230, can_config.battery); //OBC3_230
|
||||||
transmit_can_frame(&ECMP_27A, can_config.battery); //PSA specific VCU message (VCU_BSI_Wakeup_27A)
|
transmit_can_frame(&ECMP_27A, can_config.battery); //VCU_BSI_Wakeup_27A
|
||||||
}
|
}
|
||||||
// Send 100ms periodic CAN Message simulating the car still being attached
|
// Send 100ms periodic CAN Message simulating the car still being attached
|
||||||
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
|
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
|
||||||
|
@ -1463,21 +1476,24 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
|
||||||
transmit_can_frame(&ECMP_345, can_config.battery); //DC1_345
|
transmit_can_frame(&ECMP_345, can_config.battery); //DC1_345
|
||||||
transmit_can_frame(&ECMP_3A2, can_config.battery); //OBC2_3A2
|
transmit_can_frame(&ECMP_3A2, can_config.battery); //OBC2_3A2
|
||||||
transmit_can_frame(&ECMP_3A3, can_config.battery); //OBC1_3A3
|
transmit_can_frame(&ECMP_3A3, can_config.battery); //OBC1_3A3
|
||||||
|
transmit_can_frame(&ECMP_010, can_config.battery); //VCU_BCM_Crash
|
||||||
|
if (simulateEntireCar) {
|
||||||
transmit_can_frame(&ECMP_31E, can_config.battery);
|
transmit_can_frame(&ECMP_31E, can_config.battery);
|
||||||
transmit_can_frame(&ECMP_383, can_config.battery);
|
transmit_can_frame(&ECMP_383, can_config.battery);
|
||||||
transmit_can_frame(&ECMP_010, can_config.battery);
|
|
||||||
transmit_can_frame(&ECMP_0A6, can_config.battery); //Not in all logs
|
transmit_can_frame(&ECMP_0A6, can_config.battery); //Not in all logs
|
||||||
transmit_can_frame(&ECMP_37F, can_config.battery); //Seems to be temperatures of some sort
|
transmit_can_frame(&ECMP_37F, can_config.battery); //Seems to be temperatures of some sort
|
||||||
transmit_can_frame(&ECMP_372, can_config.battery);
|
transmit_can_frame(&ECMP_372, can_config.battery);
|
||||||
transmit_can_frame(&ECMP_351, can_config.battery);
|
transmit_can_frame(&ECMP_351, can_config.battery);
|
||||||
transmit_can_frame(&ECMP_31D, can_config.battery);
|
transmit_can_frame(&ECMP_31D, can_config.battery);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Send 500ms periodic CAN Message simulating the car still being attached
|
// Send 500ms periodic CAN Message simulating the car still being attached
|
||||||
if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
|
if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
|
||||||
previousMillis500 = currentMillis;
|
previousMillis500 = currentMillis;
|
||||||
|
if (simulateEntireCar) {
|
||||||
transmit_can_frame(&ECMP_0AE, can_config.battery);
|
transmit_can_frame(&ECMP_0AE, can_config.battery);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Send 1s CAN Message
|
// Send 1s CAN Message
|
||||||
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
|
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
|
||||||
previousMillis1000 = currentMillis;
|
previousMillis1000 = currentMillis;
|
||||||
|
@ -1499,21 +1515,24 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
|
||||||
ECMP_552.data.u8[2] = ((ticks_552 & 0x0000FF00) >> 8);
|
ECMP_552.data.u8[2] = ((ticks_552 & 0x0000FF00) >> 8);
|
||||||
ECMP_552.data.u8[3] = (ticks_552 & 0x000000FF);
|
ECMP_552.data.u8[3] = (ticks_552 & 0x000000FF);
|
||||||
|
|
||||||
transmit_can_frame(&ECMP_439, can_config.battery); //PSA Specific? Not in all logs
|
transmit_can_frame(&ECMP_439, can_config.battery); //OBC4
|
||||||
|
transmit_can_frame(&ECMP_552, can_config.battery); //VCU_552 timetracking
|
||||||
|
if (simulateEntireCar) {
|
||||||
transmit_can_frame(&ECMP_486, can_config.battery); //Not in all logs
|
transmit_can_frame(&ECMP_486, can_config.battery); //Not in all logs
|
||||||
transmit_can_frame(&ECMP_041, can_config.battery); //Not in all logs
|
transmit_can_frame(&ECMP_041, can_config.battery); //Not in all logs
|
||||||
transmit_can_frame(&ECMP_786, can_config.battery); //Not in all logs
|
transmit_can_frame(&ECMP_786, can_config.battery); //Not in all logs
|
||||||
transmit_can_frame(&ECMP_591, can_config.battery); //Not in all logs
|
transmit_can_frame(&ECMP_591, can_config.battery); //Not in all logs
|
||||||
transmit_can_frame(&ECMP_552, can_config.battery); //VCU_552 timetracking
|
|
||||||
transmit_can_frame(&ECMP_794, can_config.battery); //Not in all logs
|
transmit_can_frame(&ECMP_794, can_config.battery); //Not in all logs
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Send 5s periodic CAN Message simulating the car still being attached
|
// Send 5s periodic CAN Message simulating the car still being attached
|
||||||
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
|
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
|
||||||
previousMillis5000 = currentMillis;
|
previousMillis5000 = currentMillis;
|
||||||
|
if (simulateEntireCar) {
|
||||||
transmit_can_frame(&ECMP_55F, can_config.battery);
|
transmit_can_frame(&ECMP_55F, can_config.battery);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EcmpBattery::setup(void) { // Performs one time setup at startup
|
void EcmpBattery::setup(void) { // Performs one time setup at startup
|
||||||
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
strncpy(datalayer.system.info.battery_protocol, Name, 63);
|
||||||
|
|
|
@ -39,6 +39,8 @@ class EcmpBattery : public CanBattery {
|
||||||
static const int MAX_CELL_DEVIATION_MV = 100;
|
static const int MAX_CELL_DEVIATION_MV = 100;
|
||||||
static const int MAX_CELL_VOLTAGE_MV = 4250;
|
static const int MAX_CELL_VOLTAGE_MV = 4250;
|
||||||
static const int MIN_CELL_VOLTAGE_MV = 2700;
|
static const int MIN_CELL_VOLTAGE_MV = 2700;
|
||||||
|
bool simulateEntireCar =
|
||||||
|
false; //Set this to true to simulate the whole car (useful for when using external diagnostic tools)
|
||||||
static const int NOT_SAMPLED_YET = 255;
|
static const int NOT_SAMPLED_YET = 255;
|
||||||
static const int COMPLETED_STATE = 0;
|
static const int COMPLETED_STATE = 0;
|
||||||
bool battery_RelayOpenRequest = false;
|
bool battery_RelayOpenRequest = false;
|
||||||
|
@ -219,7 +221,7 @@ class EcmpBattery : public CanBattery {
|
||||||
uint16_t poll_state = PID_WELD_CHECK;
|
uint16_t poll_state = PID_WELD_CHECK;
|
||||||
uint16_t incoming_poll = 0;
|
uint16_t incoming_poll = 0;
|
||||||
|
|
||||||
CAN_frame ECMP_010 = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x010, .data = {0xB4}};
|
CAN_frame ECMP_010 = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x010, .data = {0xB4}}; //VCU_BCM_Crash 100ms
|
||||||
CAN_frame ECMP_041 = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x041, .data = {0x00}};
|
CAN_frame ECMP_041 = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x041, .data = {0x00}};
|
||||||
CAN_frame ECMP_0A6 = {.FD = false,
|
CAN_frame ECMP_0A6 = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
|
@ -335,7 +337,7 @@ class EcmpBattery : public CanBattery {
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x3A3,
|
.ID = 0x3A3,
|
||||||
.data = {0x4A, 0x4A, 0x40, 0x00, 0x00, 0x08, 0x00, 0x0F}};
|
.data = {0x4A, 0x4A, 0x40, 0x00, 0x00, 0x08, 0x00, 0x0F}};
|
||||||
CAN_frame ECMP_439 = {.FD = false, //??? 1s periodic (Perfectly emulated in Battery-Emulator)
|
CAN_frame ECMP_439 = {.FD = false, //OBC4 1s periodic (Perfectly emulated in Battery-Emulator)
|
||||||
.ext_ID = false, //Same content always, fully static
|
.ext_ID = false, //Same content always, fully static
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x439,
|
.ID = 0x439,
|
||||||
|
@ -414,16 +416,12 @@ class EcmpBattery : public CanBattery {
|
||||||
.DLC = 5,
|
.DLC = 5,
|
||||||
.ID = 0x6B4,
|
.ID = 0x6B4,
|
||||||
.data = {0x04, 0x2E, 0xD9, 0x00, 0x01}};
|
.data = {0x04, 0x2E, 0xD9, 0x00, 0x01}};
|
||||||
CAN_frame ECMP_FACTORY_MODE_ACTIVATION_NEW = {.FD = false,
|
|
||||||
.ext_ID = false,
|
|
||||||
.DLC = 4,
|
|
||||||
.ID = 0x6B4,
|
|
||||||
.data = {0x04, 0x2E, 0x19, 0x01}};
|
|
||||||
CAN_frame ECMP_DISABLE_ISOLATION_REQ = {.FD = false,
|
CAN_frame ECMP_DISABLE_ISOLATION_REQ = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 5,
|
.DLC = 5,
|
||||||
.ID = 0x6B4,
|
.ID = 0x6B4,
|
||||||
.data = {0x04, 0x31, 0x02, 0xDF, 0xE1}};
|
.data = {0x04, 0x31, 0x02, 0xDF, 0xE1}};
|
||||||
|
CAN_frame ECMP_ACK_MESSAGE = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x6B4, .data = {0x02, 0x3E, 0x00}};
|
||||||
uint8_t data_010_CRC[8] = {0xB4, 0x96, 0x78, 0x5A, 0x3C, 0x1E, 0xF0, 0xD2};
|
uint8_t data_010_CRC[8] = {0xB4, 0x96, 0x78, 0x5A, 0x3C, 0x1E, 0xF0, 0xD2};
|
||||||
uint8_t data_3A2_CRC[16] = {0x0C, 0x1B, 0x2A, 0x39, 0x48, 0x57,
|
uint8_t data_3A2_CRC[16] = {0x0C, 0x1B, 0x2A, 0x39, 0x48, 0x57,
|
||||||
0x66, 0x75, 0x84, 0x93, 0xA2, 0xB1}; // NOTE. Changes on BMS state
|
0x66, 0x75, 0x84, 0x93, 0xA2, 0xB1}; // NOTE. Changes on BMS state
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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>";
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -27,7 +27,11 @@ class VolvoSpaBattery : public CanBattery {
|
||||||
bool supports_reset_BECM() { return true; }
|
bool supports_reset_BECM() { return true; }
|
||||||
void reset_BECM() { datalayer_extended.VolvoPolestar.UserRequestBECMecuReset = true; }
|
void reset_BECM() { datalayer_extended.VolvoPolestar.UserRequestBECMecuReset = true; }
|
||||||
|
|
||||||
|
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
VolvoSpaHtmlRenderer renderer;
|
||||||
|
|
||||||
void readCellVoltages();
|
void readCellVoltages();
|
||||||
|
|
||||||
static const int MAX_PACK_VOLTAGE_108S_DV = 4540;
|
static const int MAX_PACK_VOLTAGE_108S_DV = 4540;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -121,6 +121,7 @@ void init_events(void) {
|
||||||
events.entries[EVENT_RJXZS_LOG].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_RJXZS_LOG].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_PAUSE_BEGIN].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_PAUSE_BEGIN].level = EVENT_LEVEL_WARNING;
|
||||||
events.entries[EVENT_PAUSE_END].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_PAUSE_END].level = EVENT_LEVEL_INFO;
|
||||||
|
events.entries[EVENT_PID_FAILED].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_WIFI_CONNECT].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_WIFI_CONNECT].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_WIFI_DISCONNECT].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_WIFI_DISCONNECT].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_MQTT_CONNECT].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_MQTT_CONNECT].level = EVENT_LEVEL_INFO;
|
||||||
|
@ -351,6 +352,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
|
||||||
return "The emulator is trying to pause the battery.";
|
return "The emulator is trying to pause the battery.";
|
||||||
case EVENT_PAUSE_END:
|
case EVENT_PAUSE_END:
|
||||||
return "The emulator is attempting to resume battery operation from pause.";
|
return "The emulator is attempting to resume battery operation from pause.";
|
||||||
|
case EVENT_PID_FAILED:
|
||||||
|
return "Failed to write PID request to battery";
|
||||||
case EVENT_WIFI_CONNECT:
|
case EVENT_WIFI_CONNECT:
|
||||||
return "Wifi connected.";
|
return "Wifi connected.";
|
||||||
case EVENT_WIFI_DISCONNECT:
|
case EVENT_WIFI_DISCONNECT:
|
||||||
|
|
|
@ -95,6 +95,7 @@
|
||||||
XX(EVENT_RJXZS_LOG) \
|
XX(EVENT_RJXZS_LOG) \
|
||||||
XX(EVENT_PAUSE_BEGIN) \
|
XX(EVENT_PAUSE_BEGIN) \
|
||||||
XX(EVENT_PAUSE_END) \
|
XX(EVENT_PAUSE_END) \
|
||||||
|
XX(EVENT_PID_FAILED) \
|
||||||
XX(EVENT_WIFI_CONNECT) \
|
XX(EVENT_WIFI_CONNECT) \
|
||||||
XX(EVENT_WIFI_DISCONNECT) \
|
XX(EVENT_WIFI_DISCONNECT) \
|
||||||
XX(EVENT_MQTT_CONNECT) \
|
XX(EVENT_MQTT_CONNECT) \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue