Merge pull request #1291 from kyberias/bmw-ix-ext

Eliminate BMW IX extended datalayer
This commit is contained in:
Jaakko Haakana 2025-07-06 13:20:17 +03:00 committed by GitHub
commit ae218e98ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 208 additions and 196 deletions

View file

@ -101,36 +101,6 @@ void BmwIXBattery::update_values() { //This function maps all the values fetche
datalayer.battery.info.number_of_cells = detected_number_of_cells;
datalayer_extended.bmwix.min_cell_voltage_data_age = (millis() - min_cell_voltage_lastchanged);
datalayer_extended.bmwix.max_cell_voltage_data_age = (millis() - max_cell_voltage_lastchanged);
datalayer_extended.bmwix.T30_Voltage = terminal30_12v_voltage;
datalayer_extended.bmwix.hvil_status = hvil_status;
datalayer_extended.bmwix.bms_uptime = sme_uptime;
datalayer_extended.bmwix.pyro_status_pss1 = pyro_status_pss1;
datalayer_extended.bmwix.pyro_status_pss4 = pyro_status_pss4;
datalayer_extended.bmwix.pyro_status_pss6 = pyro_status_pss6;
datalayer_extended.bmwix.iso_safety_positive = iso_safety_positive;
datalayer_extended.bmwix.iso_safety_negative = iso_safety_negative;
datalayer_extended.bmwix.iso_safety_parallel = iso_safety_parallel;
datalayer_extended.bmwix.allowable_charge_amps = allowable_charge_amps;
datalayer_extended.bmwix.allowable_discharge_amps = allowable_discharge_amps;
datalayer_extended.bmwix.balancing_status = balancing_status;
datalayer_extended.bmwix.battery_voltage_after_contactor = battery_voltage_after_contactor;
if (battery_info_available) {
// If we have data from battery - override the defaults to suit
datalayer.battery.info.max_design_voltage_dV = max_design_voltage;
@ -493,30 +463,26 @@ void BmwIXBattery::HandleIncomingUserRequest(void) {
// Debug user request to open or close the contactors
#ifdef DEBUG_LOG
logging.print("User request: contactor close: ");
logging.print(datalayer_extended.bmwix.UserRequestContactorClose);
logging.print(userRequestContactorClose);
logging.print(" User request: contactor open: ");
logging.println(datalayer_extended.bmwix.UserRequestContactorOpen);
logging.println(userRequestContactorOpen);
#endif // DEBUG_LOG
if ((datalayer_extended.bmwix.UserRequestContactorClose == false) &&
(datalayer_extended.bmwix.UserRequestContactorOpen == false)) {
if ((userRequestContactorClose == false) && (userRequestContactorOpen == false)) {
// do nothing
} else if ((datalayer_extended.bmwix.UserRequestContactorClose == true) &&
(datalayer_extended.bmwix.UserRequestContactorOpen == false)) {
} else if ((userRequestContactorClose == true) && (userRequestContactorOpen == false)) {
BmwIxCloseContactors();
// set user request to false
datalayer_extended.bmwix.UserRequestContactorClose = false;
} else if ((datalayer_extended.bmwix.UserRequestContactorClose == false) &&
(datalayer_extended.bmwix.UserRequestContactorOpen == true)) {
userRequestContactorClose = false;
} else if ((userRequestContactorClose == false) && (userRequestContactorOpen == true)) {
BmwIxOpenContactors();
// set user request to false
datalayer_extended.bmwix.UserRequestContactorOpen = false;
} else if ((datalayer_extended.bmwix.UserRequestContactorClose == true) &&
(datalayer_extended.bmwix.UserRequestContactorOpen == true)) {
userRequestContactorOpen = false;
} else if ((userRequestContactorClose == true) && (userRequestContactorOpen == true)) {
// these flasgs should not be true at the same time, therefore open contactors, as that is the safest state
BmwIxOpenContactors();
// set user request to false
datalayer_extended.bmwix.UserRequestContactorClose = false;
datalayer_extended.bmwix.UserRequestContactorOpen = false;
userRequestContactorClose = false;
userRequestContactorOpen = false;
// print error, as both these flags shall not be true at the same time
#ifdef DEBUG_LOG
logging.println(
@ -695,3 +661,50 @@ void BmwIXBattery::HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms) {
}
}
}
// Getter implementations for HTML renderer
int BmwIXBattery::get_battery_voltage_after_contactor() const {
return battery_voltage_after_contactor;
}
unsigned long BmwIXBattery::get_min_cell_voltage_data_age() const {
return millis() - min_cell_voltage_lastchanged;
}
unsigned long BmwIXBattery::get_max_cell_voltage_data_age() const {
return millis() - max_cell_voltage_lastchanged;
}
int BmwIXBattery::get_T30_Voltage() const {
return terminal30_12v_voltage;
}
int BmwIXBattery::get_balancing_status() const {
return balancing_status;
}
int BmwIXBattery::get_hvil_status() const {
return hvil_status;
}
unsigned long BmwIXBattery::get_bms_uptime() const {
return sme_uptime;
}
int BmwIXBattery::get_allowable_charge_amps() const {
return allowable_charge_amps;
}
int BmwIXBattery::get_allowable_discharge_amps() const {
return allowable_discharge_amps;
}
int BmwIXBattery::get_iso_safety_positive() const {
return iso_safety_positive;
}
int BmwIXBattery::get_iso_safety_negative() const {
return iso_safety_negative;
}
int BmwIXBattery::get_iso_safety_parallel() const {
return iso_safety_parallel;
}
int BmwIXBattery::get_pyro_status_pss1() const {
return pyro_status_pss1;
}
int BmwIXBattery::get_pyro_status_pss4() const {
return pyro_status_pss4;
}
int BmwIXBattery::get_pyro_status_pss6() const {
return pyro_status_pss6;
}

View file

@ -11,6 +11,8 @@
class BmwIXBattery : public CanBattery {
public:
BmwIXBattery() : renderer(*this) {}
virtual void setup(void);
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
@ -19,12 +21,32 @@ class BmwIXBattery : public CanBattery {
bool supports_contactor_close() { return true; }
void request_open_contactors() { datalayer_extended.bmwix.UserRequestContactorOpen = true; }
void request_close_contactors() { datalayer_extended.bmwix.UserRequestContactorClose = true; }
void request_open_contactors() { userRequestContactorOpen = true; }
void request_close_contactors() { userRequestContactorClose = true; }
static constexpr const char* Name = "BMW iX and i4-7 platform";
// Getter methods for HTML renderer
int get_battery_voltage_after_contactor() const;
unsigned long get_min_cell_voltage_data_age() const;
unsigned long get_max_cell_voltage_data_age() const;
int get_T30_Voltage() const;
int get_balancing_status() const;
int get_hvil_status() const;
unsigned long get_bms_uptime() const;
int get_allowable_charge_amps() const;
int get_allowable_discharge_amps() const;
int get_iso_safety_positive() const;
int get_iso_safety_negative() const;
int get_iso_safety_parallel() const;
int get_pyro_status_pss1() const;
int get_pyro_status_pss4() const;
int get_pyro_status_pss6() const;
private:
bool userRequestContactorClose = false;
bool userRequestContactorOpen = false;
BmwIXHtmlRenderer renderer;
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V
static const int MIN_PACK_VOLTAGE_DV = 3000;

View file

@ -0,0 +1,120 @@
#include "BMW-IX-HTML.h"
#include "../include.h"
#include "BMW-IX-BATTERY.h"
String BmwIXHtmlRenderer::get_status_html() {
String content;
content += "<h4>Battery Voltage after Contactor: " + String(batt.get_battery_voltage_after_contactor()) + " dV</h4>";
content += "<h4>Max Design Voltage: " + String(datalayer.battery.info.max_design_voltage_dV) + " dV</h4>";
content += "<h4>Min Design Voltage: " + String(datalayer.battery.info.min_design_voltage_dV) + " dV</h4>";
content += "<h4>Max Cell Design Voltage: " + String(datalayer.battery.info.max_cell_voltage_mV) + " mV</h4>";
content += "<h4>Min Cell Design Voltage: " + String(datalayer.battery.info.min_cell_voltage_mV) + " mV</h4>";
content += "<h4>Min Cell Voltage Data Age: " + String(batt.get_min_cell_voltage_data_age()) + " ms</h4>";
content += "<h4>Max Cell Voltage Data Age: " + String(batt.get_max_cell_voltage_data_age()) + " ms</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>T30 Terminal Voltage: " + String(batt.get_T30_Voltage()) + " mV</h4>";
content += "<h4>Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "</h4>";
content += "<h4>Balancing: ";
switch (batt.get_balancing_status()) {
case 0:
content += "0 No balancing mode active</h4>";
break;
case 1:
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 (batt.get_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(batt.get_bms_uptime()) + " seconds</h4>";
content += "<h4>BMS Allowed Charge Amps: " + String(batt.get_allowable_charge_amps()) + " A</h4>";
content += "<h4>BMS Allowed Disharge Amps: " + String(batt.get_allowable_discharge_amps()) + " A</h4>";
content += "<br>";
content += "<h3>HV Isolation (2147483647kOhm = maximum/invalid)</h3>";
content += "<h4>Isolation Positive: " + String(batt.get_iso_safety_positive()) + " kOhm</h4>";
content += "<h4>Isolation Negative: " + String(batt.get_iso_safety_negative()) + " kOhm</h4>";
content += "<h4>Isolation Parallel: " + String(batt.get_iso_safety_parallel()) + " kOhm</h4>";
content += "<h4>Pyro Status PSS1: ";
switch (batt.get_pyro_status_pss1()) {
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 PSS4: ";
switch (batt.get_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 (batt.get_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;
}

View file

@ -2,132 +2,18 @@
#define _BMW_IX_HTML_H
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
class BmwIXBattery;
class BmwIXHtmlRenderer : public BatteryHtmlRenderer {
private:
BmwIXBattery& batt;
public:
String get_status_html() {
String content;
BmwIXHtmlRenderer(BmwIXBattery& b) : batt(b) {}
content +=
"<h4>Battery Voltage after Contactor: " + String(datalayer_extended.bmwix.battery_voltage_after_contactor) +
" dV</h4>";
content += "<h4>Max Design Voltage: " + String(datalayer.battery.info.max_design_voltage_dV) + " dV</h4>";
content += "<h4>Min Design Voltage: " + String(datalayer.battery.info.min_design_voltage_dV) + " dV</h4>";
content += "<h4>Max Cell Design Voltage: " + String(datalayer.battery.info.max_cell_voltage_mV) + " mV</h4>";
content += "<h4>Min Cell Design Voltage: " + String(datalayer.battery.info.min_cell_voltage_mV) + " mV</h4>";
content +=
"<h4>Min Cell Voltage Data Age: " + String(datalayer_extended.bmwix.min_cell_voltage_data_age) + " ms</h4>";
content +=
"<h4>Max Cell Voltage Data Age: " + String(datalayer_extended.bmwix.max_cell_voltage_data_age) + " ms</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>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>Balancing: ";
switch (datalayer_extended.bmwix.balancing_status) {
case 0:
content += "0 No balancing mode active</h4>";
break;
case 1:
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 Allowed Charge Amps: " + String(datalayer_extended.bmwix.allowable_charge_amps) + " A</h4>";
content +=
"<h4>BMS Allowed Disharge Amps: " + String(datalayer_extended.bmwix.allowable_discharge_amps) + " A</h4>";
content += "<br>";
content += "<h3>HV Isolation (2147483647kOhm = maximum/invalid)</h3>";
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 Parallel: " + String(datalayer_extended.bmwix.iso_safety_parallel) + " kOhm</h4>";
content += "<h4>Pyro Status PSS1: ";
switch (datalayer_extended.bmwix.pyro_status_pss1) {
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 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;
}
String get_status_html();
};
#endif

View file

@ -40,34 +40,6 @@ typedef struct {
int16_t battery_current_7E4 = 0;
} DATALAYER_INFO_BOLTAMPERA;
typedef struct {
/** User requesting contactor open or close via WebUI*/
bool UserRequestContactorClose = false;
bool UserRequestContactorOpen = false;
/** uint16_t */
/** Terminal 30 - 12V SME Supply Voltage */
uint16_t T30_Voltage = 0;
/** Status HVIL, 1 HVIL OK, 0 HVIL disconnected*/
uint8_t hvil_status = 0;
/** Min/Max Cell SOH*/
uint16_t min_soh_state = 0;
uint16_t max_soh_state = 0;
uint32_t bms_uptime = 0;
uint8_t pyro_status_pss1 = 0;
uint8_t pyro_status_pss4 = 0;
uint8_t pyro_status_pss6 = 0;
int32_t iso_safety_positive = 0;
int32_t iso_safety_negative = 0;
int32_t iso_safety_parallel = 0;
int32_t allowable_charge_amps = 0;
int32_t allowable_discharge_amps = 0;
int16_t balancing_status = 0;
int16_t battery_voltage_after_contactor = 0;
unsigned long min_cell_voltage_data_age = 0;
unsigned long max_cell_voltage_data_age = 0;
} DATALAYER_INFO_BMWIX;
typedef struct {
/** uint8_t */
/** Status isolation external, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal*/
@ -851,7 +823,6 @@ typedef struct {
class DataLayerExtended {
public:
DATALAYER_INFO_BOLTAMPERA boltampera;
DATALAYER_INFO_BMWIX bmwix;
DATALAYER_INFO_BMWPHEV bmwphev;
DATALAYER_INFO_BYDATTO3 bydAtto3;
DATALAYER_INFO_CELLPOWER cellpower;