Skip to content

Commit

Permalink
Add Beta BMW PHEV Support (#804)
Browse files Browse the repository at this point in the history
* Add Beta BMW PHEV Support
  • Loading branch information
wjcloudy authored Jan 19, 2025
1 parent 681f57d commit a84a1d6
Show file tree
Hide file tree
Showing 9 changed files with 1,302 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/compile-all-batteries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ jobs:
battery:
- BMW_I3_BATTERY
- BMW_IX_BATTERY
- BMW_PHEV_BATTERY
- BYD_ATTO_3_BATTERY
- CELLPOWER_BMS
- CHADEMO_BATTERY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
battery:
- BMW_I3_BATTERY
- BMW_IX_BATTERY
- BMW_PHEV_BATTERY
- BYD_ATTO_3_BATTERY
- CELLPOWER_BMS
- CHADEMO_BATTERY
Expand Down
1 change: 1 addition & 0 deletions Software/USER_SETTINGS.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
/* Select battery used */
//#define BMW_I3_BATTERY
//#define BMW_IX_BATTERY
//#define BMW_PHEV_BATTERY
//#define BOLT_AMPERA_BATTERY
//#define BYD_ATTO_3_BATTERY
//#define CELLPOWER_BMS
Expand Down
4 changes: 4 additions & 0 deletions Software/src/battery/BATTERIES.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ void setup_can_shunt();
#include "BMW-IX-BATTERY.h"
#endif

#ifdef BMW_PHEV_BATTERY
#include "BMW-PHEV-BATTERY.h"
#endif

#ifdef BOLT_AMPERA_BATTERY
#include "BOLT-AMPERA-BATTERY.h"
#endif
Expand Down
1,088 changes: 1,088 additions & 0 deletions Software/src/battery/BMW-PHEV-BATTERY.cpp

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions Software/src/battery/BMW-PHEV-BATTERY.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef BMW_PHEV_BATTERY_H
#define BMW_PHEV_BATTERY_H
#include <Arduino.h>
#include "../include.h"

#define BATTERY_SELECTED

#define MAX_PACK_VOLTAGE_DV 4650 //4650 = 465.0V
#define MIN_PACK_VOLTAGE_DV 3000
#define MAX_CELL_DEVIATION_MV 250
#define MAX_CELL_VOLTAGE_MV 4300 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2800 //Battery is put into emergency stop if one cell goes below this value
#define MAX_DISCHARGE_POWER_ALLOWED_W 10000
#define MAX_CHARGE_POWER_ALLOWED_W 10000
#define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500
#define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
#define STALE_PERIOD_CONFIG \
3600000; //Number of milliseconds before critical values are classed as stale/stuck 1800000 = 3600 seconds / 60mins
void setup_battery(void);
void transmit_can_frame(CAN_frame* tx_frame, int interface);

#endif
57 changes: 57 additions & 0 deletions Software/src/datalayer/datalayer_extended.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,62 @@ typedef struct {

} DATALAYER_INFO_BMWIX;

typedef struct {
/** uint8_t */
/** Status isolation external, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal*/
uint8_t ST_iso_ext = 0;
/** uint8_t */
/** Status isolation external, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal*/
uint8_t ST_iso_int = 0;
/** uint8_t */
/** Status cooling valve error, 0 not evaluated, 1 OK valve closed, 2 error active valve open, 3 Invalid signal*/
uint8_t ST_valve_cooling = 0;
/** uint8_t */
/** Status interlock error, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal*/
uint8_t ST_interlock = 0;
/** uint8_t */
/** Status precharge, 0 no statement, 1 Not active closing not blocked, 2 error precharge blocked, 3 Invalid signal*/
uint8_t ST_precharge = 0;
/** uint8_t */
/** Status DC switch, 0 contactors open, 1 precharge ongoing, 2 contactors engaged, 3 Invalid signal*/
uint8_t ST_DCSW = 0;
/** uint8_t */
/** Status emergency, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal*/
uint8_t ST_EMG = 0;
/** uint8_t */
/** Status welding detection, 0 Contactors OK, 1 One contactor welded, 2 Two contactors welded, 3 Invalid signal*/
uint8_t ST_WELD = 0;
/** uint8_t */
/** Status isolation, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal*/
uint8_t ST_isolation = 0;
/** uint8_t */
/** Status cold shutoff valve, 0 OK, 1 Short circuit to GND, 2 Short circuit to 12V, 3 Line break, 6 Driver error, 12 Stuck, 13 Stuck, 15 Invalid Signal*/
uint8_t ST_cold_shutoff_valve = 0;
/** 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;
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;
int32_t iso_safety_int_kohm = 0; //STAT_ISOWIDERSTAND_INT_WERT
int32_t iso_safety_ext_kohm = 0; //STAT_ISOWIDERSTAND_EXT_STD_WERT
int32_t iso_safety_trg_kohm = 0;
int32_t iso_safety_ext_plausible = 0; //STAT_ISOWIDERSTAND_EXT_TRG_PLAUS
int32_t iso_safety_int_plausible = 0;
int32_t iso_safety_trg_plausible = 0;
int32_t iso_safety_kohm = 0; //STAT_R_ISO_ROH_01_WERT
int32_t iso_safety_kohm_quality = 0; //STAT_R_ISO_ROH_QAL_01_INFO Quality of measurement 0-21 (higher better)

} DATALAYER_INFO_BMWPHEV;

typedef struct {
/** uint16_t */
/** SOC% raw battery value. Might not always reach 100% */
Expand Down Expand Up @@ -619,6 +675,7 @@ class DataLayerExtended {
public:
DATALAYER_INFO_BOLTAMPERA boltampera;
DATALAYER_INFO_BMWIX bmwix;
DATALAYER_INFO_BMWPHEV bmwphev;
DATALAYER_INFO_BMWI3 bmwi3;
DATALAYER_INFO_BYDATTO3 bydAtto3;
DATALAYER_INFO_CELLPOWER cellpower;
Expand Down
6 changes: 6 additions & 0 deletions Software/src/devboard/mqtt/mqtt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ SensorConfig sensorConfigs[] = {
{"battery_current", "Battery Current", "{{ value_json.battery_current }}", "A", "current"},
{"cell_max_voltage", "Cell Max Voltage", "{{ value_json.cell_max_voltage }}", "V", "voltage"},
{"cell_min_voltage", "Cell Min Voltage", "{{ value_json.cell_min_voltage }}", "V", "voltage"},
{"cell_voltage_delta", "Cell Voltage Delta", "{{ value_json.cell_voltage_delta }}", "mV", "voltage"},
{"battery_voltage", "Battery Voltage", "{{ value_json.battery_voltage }}", "V", "voltage"},
{"total_capacity", "Battery Total Capacity", "{{ value_json.total_capacity }}", "Wh", "energy"},
{"remaining_capacity", "Battery Remaining Capacity (scaled)", "{{ value_json.remaining_capacity }}", "Wh",
Expand All @@ -79,6 +80,7 @@ SensorConfig sensorConfigs[] = {
{"battery_current_2", "Battery 2 Current", "{{ value_json.battery_current_2 }}", "A", "current"},
{"cell_max_voltage_2", "Cell Max Voltage 2", "{{ value_json.cell_max_voltage_2 }}", "V", "voltage"},
{"cell_min_voltage_2", "Cell Min Voltage 2", "{{ value_json.cell_min_voltage_2 }}", "V", "voltage"},
{"cell_voltage_delta_2", "Cell Voltage Delta 2", "{{ value_json.cell_voltage_delta_2 }}", "mV", "voltage"},
{"battery_voltage_2", "Battery 2 Voltage", "{{ value_json.battery_voltage_2 }}", "V", "voltage"},
{"total_capacity_2", "Battery 2 Total Capacity", "{{ value_json.total_capacity_2 }}", "Wh", "energy"},
{"remaining_capacity_2", "Battery 2 Remaining Capacity (scaled)", "{{ value_json.remaining_capacity_2 }}", "Wh",
Expand Down Expand Up @@ -174,6 +176,8 @@ static void publish_common_info(void) {
datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] != 0u) {
doc["cell_max_voltage"] = ((float)datalayer.battery.status.cell_max_voltage_mV) / 1000.0;
doc["cell_min_voltage"] = ((float)datalayer.battery.status.cell_min_voltage_mV) / 1000.0;
doc["cell_voltage_delta"] = ((float)datalayer.battery.status.cell_max_voltage_mV) -
((float)datalayer.battery.status.cell_min_voltage_mV);
}
doc["total_capacity"] = ((float)datalayer.battery.info.total_capacity_Wh);
doc["remaining_capacity_real"] = ((float)datalayer.battery.status.remaining_capacity_Wh);
Expand All @@ -197,6 +201,8 @@ static void publish_common_info(void) {
datalayer.battery2.status.cell_voltages_mV[datalayer.battery2.info.number_of_cells - 1] != 0u) {
doc["cell_max_voltage_2"] = ((float)datalayer.battery2.status.cell_max_voltage_mV) / 1000.0;
doc["cell_min_voltage_2"] = ((float)datalayer.battery2.status.cell_min_voltage_mV) / 1000.0;
doc["cell_voltage_delta_2"] = ((float)datalayer.battery2.status.cell_max_voltage_mV) -
((float)datalayer.battery2.status.cell_min_voltage_mV);
}
doc["total_capacity_2"] = ((float)datalayer.battery2.info.total_capacity_Wh);
doc["remaining_capacity_real_2"] = ((float)datalayer.battery2.status.remaining_capacity_Wh);
Expand Down
126 changes: 122 additions & 4 deletions Software/src/devboard/webserver/advanced_battery_html.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,123 @@ String advanced_battery_processor(const String& var) {
content += "<h4>Pyro Status PSS6: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss6])) + "</h4>";
#endif //BMW_IX_BATTERY

#ifdef BMW_PHEV_BATTERY
content +=
"<h4>Battery Voltage after Contactor: " + String(datalayer_extended.bmwphev.battery_voltage_after_contactor) +
" dV</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>";
static const char* balanceText[5] = {"0 Balancing Inactive - Balancing not needed", "1 Balancing Active",
"2 Balancing Inactive - Cells not in rest break wait 10mins",
"3 Balancing Inactive", "4 Unknown"};
content += "<h4>Balancing: " + String((balanceText[datalayer_extended.bmwphev.balancing_status])) + "</h4>";
static const char* pyroText[5] = {"0 Value Invalid", "1 Successfully Blown", "2 Disconnected",
"3 Not Activated - Pyro Intact", "4 Unknown"};
static const char* statusText[16] = {
"Not evaluated", "OK", "Error!", "Invalid signal", "", "", "", "", "", "", "", "", "", "", "", ""};
content += "<h4>Interlock: " + String(statusText[datalayer_extended.bmwphev.ST_interlock]) + "</h4>";
content += "<h4>Isolation external: " + String(statusText[datalayer_extended.bmwphev.ST_iso_ext]) + "</h4>";
content += "<h4>Isolation internal: " + String(statusText[datalayer_extended.bmwphev.ST_iso_int]) + "</h4>";
content += "<h4>Isolation: " + String(statusText[datalayer_extended.bmwphev.ST_isolation]) + "</h4>";
content += "<h4>Cooling valve: " + String(statusText[datalayer_extended.bmwphev.ST_valve_cooling]) + "</h4>";
content += "<h4>Emergency: " + String(statusText[datalayer_extended.bmwphev.ST_EMG]) + "</h4>";
static const char* prechargeText[16] = {"Not evaluated",
"Not active, closing not blocked",
"Error precharge blocked",
"Invalid signal",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
""};
content += "<h4>Precharge: " + String(prechargeText[datalayer_extended.bmwphev.ST_precharge]) +
"</h4>"; //Still unclear of enum
static const char* DCSWText[16] = {"Contactors open",
"Precharge ongoing",
"Contactors engaged",
"Invalid signal",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
""};
content += "<h4>Contactor status: " + String(DCSWText[datalayer_extended.bmwphev.ST_DCSW]) + "</h4>";
static const char* contText[16] = {"Contactors OK",
"One contactor welded!",
"Two contactors welded!",
"Invalid signal",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
""};
content += "<h4>Contactor weld: " + String(contText[datalayer_extended.bmwphev.ST_WELD]) + "</h4>";
static const char* valveText[16] = {"OK",
"Short circuit to GND",
"Short circuit to 12V",
"Line break",
"",
"",
"Driver error",
"",
"",
"",
"",
"",
"Stuck",
"Stuck",
"",
"Invalid Signal"};
content +=
"<h4>Cold shutoff valve: " + String(valveText[datalayer_extended.bmwphev.ST_cold_shutoff_valve]) + "</h4>";
content +=
"<h4>Min Cell Voltage Data Age: " + String(datalayer_extended.bmwphev.min_cell_voltage_data_age) + " ms</h4>";
content +=
"<h4>Max Cell Voltage Data Age: " + String(datalayer_extended.bmwphev.max_cell_voltage_data_age) + " ms</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>BMS Allowed Charge Amps: " + String(datalayer_extended.bmwphev.allowable_charge_amps) + " A</h4>";
content +=
"<h4>BMS Allowed Disharge Amps: " + String(datalayer_extended.bmwphev.allowable_discharge_amps) + " A</h4>";
content += "<h4>Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "</h4>";
content += "<h4>iso_safety_int_kohm: " + String(datalayer_extended.bmwphev.iso_safety_int_kohm) + "</h4>";
content += "<h4>iso_safety_ext_kohm: " + String(datalayer_extended.bmwphev.iso_safety_ext_kohm) + "</h4>";
content += "<h4>iso_safety_trg_kohm: " + String(datalayer_extended.bmwphev.iso_safety_trg_kohm) + "</h4>";
content += "<h4>iso_safety_ext_plausible: " + String(datalayer_extended.bmwphev.iso_safety_ext_plausible) + "</h4>";
content += "<h4>iso_safety_int_plausible: " + String(datalayer_extended.bmwphev.iso_safety_int_plausible) + "</h4>";
content += "<h4>iso_safety_trg_plausible: " + String(datalayer_extended.bmwphev.iso_safety_trg_plausible) + "</h4>";
content += "<h4>iso_safety_kohm: " + String(datalayer_extended.bmwphev.iso_safety_kohm) + "</h4>";
content += "<h4>iso_safety_kohm_quality: " + String(datalayer_extended.bmwphev.iso_safety_kohm_quality) + "</h4>";
content += "<br>";
content += "<h4>Todo";
content += "<br>";
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>T30 Terminal Voltage: " + String(datalayer_extended.bmwphev.T30_Voltage) + " mV</h4>";
content += "<br>";
#endif //BMW_PHEV_BATTERY

#ifdef BMW_I3_BATTERY
content += "<h4>SOC raw: " + String(datalayer_extended.bmwi3.SOC_raw) + "</h4>";
content += "<h4>SOC dash: " + String(datalayer_extended.bmwi3.SOC_dash) + "</h4>";
Expand Down Expand Up @@ -1139,10 +1256,11 @@ String advanced_battery_processor(const String& var) {
content += "<button onclick='Volvo_BECMecuReset()'>Restart BECM module</button>";
#endif // VOLVO_SPA_BATTERY

#if !defined(BMW_IX_BATTERY) && !defined(BOLT_AMPERA_BATTERY) && !defined(TESLA_BATTERY) && \
!defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && !defined(BYD_ATTO_3_BATTERY) && \
!defined(RENAULT_ZOE_GEN2_BATTERY) && !defined(CELLPOWER_BMS) && !defined(MEB_BATTERY) && \
!defined(VOLVO_SPA_BATTERY) && !defined(KIA_HYUNDAI_64_BATTERY) //Only the listed types have extra info
#if !defined(BMW_PHEV_BATTERY) && !defined(BMW_IX_BATTERY) && !defined(BOLT_AMPERA_BATTERY) && \
!defined(TESLA_BATTERY) && !defined(NISSAN_LEAF_BATTERY) && !defined(BMW_I3_BATTERY) && \
!defined(BYD_ATTO_3_BATTERY) && !defined(RENAULT_ZOE_GEN2_BATTERY) && !defined(CELLPOWER_BMS) && \
!defined(MEB_BATTERY) && !defined(VOLVO_SPA_BATTERY) && \
!defined(KIA_HYUNDAI_64_BATTERY) //Only the listed types have extra info
content += "No extra information available for this battery type";
#endif

Expand Down

0 comments on commit a84a1d6

Please sign in to comment.