diff --git a/README.md b/README.md index 22c85ada..2184126e 100644 --- a/README.md +++ b/README.md @@ -109,8 +109,11 @@ It is also based on the information found in the following excellent repositorie - Pylon hacking https://www.eevblog.com/forum/programming/pylontech-sc0500-protocol-hacking/ ## Like this project? 💖 -Leave a ⭐ If you think this project is useful. Consider hopping onto my Patreon to encourage more open-source projects! +Leave a ⭐ If you think this project is useful. Consider hopping onto my Patreon to encourage more open-source projects! As a bonus, you will get access to the Discord server, where we hangout, develop, support, share, discuss etc. all things related to DIY EV storage solutions. See you on the server? ;) - + <------ Click here to learn more! + + +![image](https://github.com/user-attachments/assets/66b8e967-7f5e-409d-91ec-d012489a86d2) diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp index 788de470..10009a3f 100644 --- a/Software/src/battery/BMW-I3-BATTERY.cpp +++ b/Software/src/battery/BMW-I3-BATTERY.cpp @@ -103,21 +103,6 @@ void BmwI3Battery::update_values() { //This function maps all the values fetche } else { clear_event(EVENT_CONTACTOR_WELDED); } - - // Update webserver datalayer - datalayer_extended.bmwi3.SOC_raw = (battery_HVBatt_SOC * 10); - datalayer_extended.bmwi3.SOC_dash = (battery_display_SOC * 50); - datalayer_extended.bmwi3.SOC_OBD2 = battery_soc; - datalayer_extended.bmwi3.ST_iso_ext = battery_status_error_isolation_external_Bordnetz; - datalayer_extended.bmwi3.ST_iso_int = battery_status_error_isolation_internal_Bordnetz; - datalayer_extended.bmwi3.ST_valve_cooling = battery_status_valve_cooling; - datalayer_extended.bmwi3.ST_interlock = battery_status_error_locking; - datalayer_extended.bmwi3.ST_precharge = battery_status_precharge_locked; - datalayer_extended.bmwi3.ST_DCSW = battery_status_disconnecting_switch; - datalayer_extended.bmwi3.ST_EMG = battery_status_emergency_mode; - datalayer_extended.bmwi3.ST_WELD = battery_status_error_disconnecting_switch; - datalayer_extended.bmwi3.ST_isolation = battery_status_warning_isolation; - datalayer_extended.bmwi3.ST_cold_shutoff_valve = battery_status_cold_shutoff_valve; } void BmwI3Battery::handle_incoming_can_frame(CAN_frame rx_frame) { diff --git a/Software/src/battery/BMW-I3-BATTERY.h b/Software/src/battery/BMW-I3-BATTERY.h index 21b1bdc9..aaa0b94b 100644 --- a/Software/src/battery/BMW-I3-BATTERY.h +++ b/Software/src/battery/BMW-I3-BATTERY.h @@ -2,7 +2,6 @@ #define BMW_I3_BATTERY_H #include "../datalayer/datalayer.h" -#include "../datalayer/datalayer_extended.h" #include "../include.h" #include "BMW-I3-HTML.h" #include "CanBattery.h" @@ -16,7 +15,7 @@ class BmwI3Battery : public CanBattery { // Use this constructor for the second battery. BmwI3Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, bool* contactor_closing_allowed_ptr, CAN_Interface targetCan, gpio_num_t wakeup) - : CanBattery(targetCan) { + : CanBattery(targetCan), renderer(*this) { datalayer_battery = datalayer_ptr; contactor_closing_allowed = contactor_closing_allowed_ptr; allows_contactor_closing = nullptr; @@ -27,7 +26,7 @@ class BmwI3Battery : public CanBattery { } // Use the default constructor to create the first or single battery. - BmwI3Battery() { + BmwI3Battery() : renderer(*this) { datalayer_battery = &datalayer.battery; allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing; contactor_closing_allowed = nullptr; @@ -40,6 +39,33 @@ class BmwI3Battery : public CanBattery { virtual void transmit_can(unsigned long currentMillis); static constexpr const char* Name = "BMW i3"; + // SOC% raw battery value. Might not always reach 100% + uint16_t SOC_raw() { return (battery_HVBatt_SOC * 10); } + // SOC% instrumentation cluster value. Will always reach 100% + uint16_t SOC_dash() { return (battery_display_SOC * 50); } + // SOC% OBD2 value, polled actively + uint16_t SOC_OBD2() { return battery_soc; } + // Status isolation external, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal + uint8_t ST_iso_ext() { return battery_status_error_isolation_external_Bordnetz; } + // Status isolation external, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal + uint8_t ST_iso_int() { return battery_status_error_isolation_internal_Bordnetz; } + // Status cooling valve error, 0 not evaluated, 1 OK valve closed, 2 error active valve open, 3 Invalid signal + uint8_t ST_valve_cooling() { return battery_status_valve_cooling; } + // Status interlock error, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal + uint8_t ST_interlock() { return battery_status_error_locking; } + // Status precharge, 0 no statement, 1 Not active closing not blocked, 2 error precharge blocked, 3 Invalid signal + uint8_t ST_precharge() { return battery_status_precharge_locked; } + // Status DC switch, 0 contactors open, 1 precharge ongoing, 2 contactors engaged, 3 Invalid signal + uint8_t ST_DCSW() { return battery_status_disconnecting_switch; } + // Status emergency, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal + uint8_t ST_EMG() { return battery_status_emergency_mode; } + // Status welding detection, 0 Contactors OK, 1 One contactor welded, 2 Two contactors welded, 3 Invalid signal + uint8_t ST_WELD() { return battery_status_error_disconnecting_switch; } + // Status isolation, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal + uint8_t ST_isolation() { return battery_status_warning_isolation; } + // 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() { return battery_status_cold_shutoff_valve; } + BatteryHtmlRenderer& get_status_renderer() { return renderer; } private: diff --git a/Software/src/battery/BMW-I3-HTML.cpp b/Software/src/battery/BMW-I3-HTML.cpp new file mode 100644 index 00000000..b982adf4 --- /dev/null +++ b/Software/src/battery/BMW-I3-HTML.cpp @@ -0,0 +1,99 @@ +#include "BMW-I3-HTML.h" +#include "../include.h" +#include "BMW-I3-BATTERY.h" + +// Helper function for safe array access +static const char* safeArrayAccess(const char* const arr[], size_t arrSize, int idx) { + if (idx >= 0 && static_cast(idx) < arrSize) { + return arr[idx]; + } + return "Unknown"; +} + +String BmwI3HtmlRenderer::get_status_html() { + String content; + + content += "

SOC raw: " + String(batt.SOC_raw()) + "

"; + content += "

SOC dash: " + String(batt.SOC_dash()) + "

"; + content += "

SOC OBD2: " + String(batt.SOC_OBD2()) + "

"; + static const char* statusText[16] = { + "Not evaluated", "OK", "Error!", "Invalid signal", "", "", "", "", "", "", "", "", "", "", "", ""}; + content += "

Interlock: " + String(safeArrayAccess(statusText, 16, batt.ST_interlock())) + "

"; + content += "

Isolation external: " + String(safeArrayAccess(statusText, 16, batt.ST_iso_ext())) + "

"; + content += "

Isolation internal: " + String(safeArrayAccess(statusText, 16, batt.ST_iso_int())) + "

"; + content += "

Isolation: " + String(safeArrayAccess(statusText, 16, batt.ST_isolation())) + "

"; + content += "

Cooling valve: " + String(safeArrayAccess(statusText, 16, batt.ST_valve_cooling())) + "

"; + content += "

Emergency: " + String(safeArrayAccess(statusText, 16, batt.ST_EMG())) + "

"; + static const char* prechargeText[16] = {"Not evaluated", + "Not active, closing not blocked", + "Error precharge blocked", + "Invalid signal", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""}; + content += "

Precharge: " + String(safeArrayAccess(prechargeText, 16, batt.ST_precharge())) + + "

"; //Still unclear of enum + static const char* DCSWText[16] = {"Contactors open", + "Precharge ongoing", + "Contactors engaged", + "Invalid signal", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""}; + content += "

Contactor status: " + String(safeArrayAccess(DCSWText, 16, batt.ST_DCSW())) + "

"; + static const char* contText[16] = {"Contactors OK", + "One contactor welded!", + "Two contactors welded!", + "Invalid signal", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ""}; + content += "

Contactor weld: " + String(safeArrayAccess(contText, 16, batt.ST_WELD())) + "

"; + static const char* valveText[16] = {"OK", + "Short circuit to GND", + "Short circuit to 12V", + "Line break", + "", + "", + "Driver error", + "", + "", + "", + "", + "", + "Stuck", + "Stuck", + "", + "Invalid Signal"}; + content += + "

Cold shutoff valve: " + String(safeArrayAccess(valveText, 16, batt.ST_cold_shutoff_valve())) + "

"; + + return content; +} diff --git a/Software/src/battery/BMW-I3-HTML.h b/Software/src/battery/BMW-I3-HTML.h index 88322a6d..a0ba265d 100644 --- a/Software/src/battery/BMW-I3-HTML.h +++ b/Software/src/battery/BMW-I3-HTML.h @@ -5,94 +5,16 @@ #include "../datalayer/datalayer_extended.h" #include "src/devboard/webserver/BatteryHtmlRenderer.h" +class BmwI3Battery; + class BmwI3HtmlRenderer : public BatteryHtmlRenderer { + private: + BmwI3Battery& batt; + public: - String get_status_html() { - String content; + BmwI3HtmlRenderer(BmwI3Battery& b) : batt(b) {} - content += "

SOC raw: " + String(datalayer_extended.bmwi3.SOC_raw) + "

"; - content += "

SOC dash: " + String(datalayer_extended.bmwi3.SOC_dash) + "

"; - content += "

SOC OBD2: " + String(datalayer_extended.bmwi3.SOC_OBD2) + "

"; - static const char* statusText[16] = { - "Not evaluated", "OK", "Error!", "Invalid signal", "", "", "", "", "", "", "", "", "", "", "", ""}; - content += "

Interlock: " + String(statusText[datalayer_extended.bmwi3.ST_interlock]) + "

"; - content += "

Isolation external: " + String(statusText[datalayer_extended.bmwi3.ST_iso_ext]) + "

"; - content += "

Isolation internal: " + String(statusText[datalayer_extended.bmwi3.ST_iso_int]) + "

"; - content += "

Isolation: " + String(statusText[datalayer_extended.bmwi3.ST_isolation]) + "

"; - content += "

Cooling valve: " + String(statusText[datalayer_extended.bmwi3.ST_valve_cooling]) + "

"; - content += "

Emergency: " + String(statusText[datalayer_extended.bmwi3.ST_EMG]) + "

"; - static const char* prechargeText[16] = {"Not evaluated", - "Not active, closing not blocked", - "Error precharge blocked", - "Invalid signal", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - ""}; - content += "

Precharge: " + String(prechargeText[datalayer_extended.bmwi3.ST_precharge]) + - "

"; //Still unclear of enum - static const char* DCSWText[16] = {"Contactors open", - "Precharge ongoing", - "Contactors engaged", - "Invalid signal", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - ""}; - content += "

Contactor status: " + String(DCSWText[datalayer_extended.bmwi3.ST_DCSW]) + "

"; - static const char* contText[16] = {"Contactors OK", - "One contactor welded!", - "Two contactors welded!", - "Invalid signal", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - ""}; - content += "

Contactor weld: " + String(contText[datalayer_extended.bmwi3.ST_WELD]) + "

"; - static const char* valveText[16] = {"OK", - "Short circuit to GND", - "Short circuit to 12V", - "Line break", - "", - "", - "Driver error", - "", - "", - "", - "", - "", - "Stuck", - "Stuck", - "", - "Invalid Signal"}; - content += "

Cold shutoff valve: " + String(contText[datalayer_extended.bmwi3.ST_cold_shutoff_valve]) + "

"; - - return content; - } + String get_status_html(); }; #endif diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.h b/Software/src/battery/BYD-ATTO-3-BATTERY.h index ef4c6fac..3f80b8ae 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.h +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.h @@ -61,6 +61,11 @@ class BydAttoBattery : public CanBattery { BatteryHtmlRenderer& get_status_renderer() { return renderer; } private: + BydAtto3HtmlRenderer renderer; + DATALAYER_BATTERY_TYPE* datalayer_battery; + DATALAYER_INFO_BYDATTO3* datalayer_bydatto; + bool* allows_contactor_closing; + static const int CELLCOUNT_EXTENDED = 126; static const int CELLCOUNT_STANDARD = 104; static const int MAX_PACK_VOLTAGE_EXTENDED_DV = 4410; //Extended range @@ -71,11 +76,6 @@ class BydAttoBattery : public CanBattery { static const int MAX_CELL_VOLTAGE_MV = 3650; //Charging stops if one cell exceeds this value static const int MIN_CELL_VOLTAGE_MV = 2800; //Discharging stops if one cell goes below this value - BydAtto3HtmlRenderer renderer; - DATALAYER_BATTERY_TYPE* datalayer_battery; - DATALAYER_INFO_BYDATTO3* datalayer_bydatto; - bool* allows_contactor_closing; - static const int POLL_FOR_BATTERY_SOC = 0x0005; static const uint8_t NOT_DETERMINED_YET = 0; static const uint8_t STANDARD_RANGE = 1; diff --git a/Software/src/battery/Battery.h b/Software/src/battery/Battery.h index a069991d..d41277f8 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -84,6 +84,8 @@ class Battery { virtual bool supports_real_BMS_status() { return false; } virtual bool supports_toggle_SOC_method() { return false; } virtual bool supports_factory_mode_method() { return false; } + virtual bool supports_chademo_restart() { return false; } + virtual bool supports_chademo_stop() { return false; } virtual void clear_isolation() {} virtual void reset_BMS() {} @@ -98,6 +100,8 @@ class Battery { virtual void request_close_contactors() {} virtual void toggle_SOC_method() {} virtual void set_factory_mode() {} + virtual void chademo_restart() {} + virtual void chademo_stop() {} virtual void set_fake_voltage(float v) {} virtual float get_voltage(); diff --git a/Software/src/battery/CHADEMO-BATTERY-HTML.h b/Software/src/battery/CHADEMO-BATTERY-HTML.h new file mode 100644 index 00000000..c9ed2277 --- /dev/null +++ b/Software/src/battery/CHADEMO-BATTERY-HTML.h @@ -0,0 +1,72 @@ +#ifndef _CHADEMO_BATTERY_HTML_H +#define _CHADEMO_BATTERY_HTML_H + +#include "../datalayer/datalayer.h" +#include "../datalayer/datalayer_extended.h" +#include "src/devboard/webserver/BatteryHtmlRenderer.h" + +class ChademoBatteryHtmlRenderer : public BatteryHtmlRenderer { + public: + String get_status_html() { + String content; + content += "

Chademo state: "; + switch (datalayer_extended.chademo.CHADEMO_Status) { + case 0: + content += String("FAULT

"); + break; + case 1: + content += String("STOP"); + break; + case 2: + content += String("IDLE"); + break; + case 3: + content += String("CONNECTED"); + break; + case 4: + content += String("INIT"); + break; + case 5: + content += String("NEGOTIATE"); + break; + case 6: + content += String("EV ALLOWED"); + break; + case 7: + content += String("EVSE PREPARE"); + break; + case 8: + content += String("EVSE START"); + break; + case 9: + content += String("EVSE CONTACTORS ENABLED"); + break; + case 10: + content += String("POWERFLOW"); + break; + default: + content += String("Unknown"); + break; + } + if (datalayer_extended.chademo.FaultBatteryCurrentDeviation) { + content += "

FAULT: Battery Current Deviation

"; + } + if (datalayer_extended.chademo.FaultBatteryOverVoltage) { + content += "

FAULT: Battery Overvoltage

"; + } + if (datalayer_extended.chademo.FaultBatteryUnderVoltage) { + content += "

FAULT: Battery Undervoltage

"; + } + if (datalayer_extended.chademo.FaultBatteryVoltageDeviation) { + content += "

FAULT: Battery Voltage Deviation

"; + } + if (datalayer_extended.chademo.FaultHighBatteryTemperature) { + content += "

FAULT: Battery Temperature

"; + } + content += "

Protocol: " + String(datalayer_extended.chademo.ControlProtocolNumberEV) + "

"; + + return content; + } +}; + +#endif diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index 63f283b1..6791c452 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -4,17 +4,24 @@ #include "../include.h" #include "CHADEMO-SHUNTS.h" -/* CHADEMO handling runs at 6.25 times the rate of most other code, so, rather than the - * default value of 12 (for 12 iterations of the 5s value update loop) * 5 for a 60s timeout, - * instead use 75 for 75*0.8s = 60s - */ -#undef CAN_STILL_ALIVE -#define CAN_STILL_ALIVE 75 -//#define CH_CAN_DEBUG - //This function maps all the values fetched via CAN to the correct parameters used for the inverter void ChademoBattery::update_values() { + datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + //On this integration, we don't care if no CAN messages flow (normal before user plugs in) + //Always write the CAN as alive! + + //Check if user is requesting an action, if so, have statemachine jump there + if (datalayer_extended.chademo.UserRequestStop) { + CHADEMO_Status = CHADEMO_STOP; + datalayer_extended.chademo.UserRequestStop = false; + } + + if (datalayer_extended.chademo.UserRequestRestart) { + CHADEMO_Status = CHADEMO_IDLE; + datalayer_extended.chademo.UserRequestRestart = false; + } + datalayer.battery.status.real_soc = x102_chg_session.StateOfCharge * 100; //Convert % to pptt datalayer.battery.status.max_discharge_power_W = @@ -47,6 +54,15 @@ void ChademoBattery::update_values() { chargingrate = x102_chg_session.StateOfCharge / x100_chg_lim.ConstantOfChargingRateIndication * 100; } } + + //Update extended datalayer for easier visualization of what's going on + datalayer_extended.chademo.CHADEMO_Status = CHADEMO_Status; + datalayer_extended.chademo.ControlProtocolNumberEV = x102_chg_session.ControlProtocolNumberEV; + datalayer_extended.chademo.FaultBatteryVoltageDeviation = x102_chg_session.f.fault.FaultBatteryVoltageDeviation; + datalayer_extended.chademo.FaultHighBatteryTemperature = x102_chg_session.f.fault.FaultHighBatteryTemperature; + datalayer_extended.chademo.FaultBatteryCurrentDeviation = x102_chg_session.f.fault.FaultBatteryCurrentDeviation; + datalayer_extended.chademo.FaultBatteryUnderVoltage = x102_chg_session.f.fault.FaultBatteryUnderVoltage; + datalayer_extended.chademo.FaultBatteryOverVoltage = x102_chg_session.f.fault.FaultBatteryOverVoltage; } //TODO simplified start/stop helper functions @@ -276,19 +292,6 @@ void ChademoBattery::process_vehicle_vendor_ID(CAN_frame rx_frame) { } void ChademoBattery::handle_incoming_can_frame(CAN_frame rx_frame) { -#ifdef CH_CAN_DEBUG - logging.print(millis()); // Example printout, time, ID, length, data: 7553 1DB 8 FF C0 B9 EA 0 0 2 5D - logging.print(" "); - logging.print(rx_frame.ID, HEX); - logging.print(" "); - logging.print(rx_frame.DLC); - logging.print(" "); - for (int i = 0; i < rx_frame.DLC; ++i) { - logging.print(rx_frame.data.u8[i], HEX); - logging.print(" "); - } - logging.println(""); -#endif // CHADEMO coexists with a CAN-based shunt. Only process CHADEMO-specific IDs // 202 is unknown @@ -302,9 +305,6 @@ void ChademoBattery::handle_incoming_can_frame(CAN_frame rx_frame) { * has not yet been receied from a vehicle */ - datalayer.battery.status.CAN_battery_still_alive = - CAN_STILL_ALIVE; //We are getting CAN messages from the vehicle, inform the watchdog - switch (rx_frame.ID) { case 0x100: process_vehicle_charging_minimums(rx_frame); diff --git a/Software/src/battery/CHADEMO-BATTERY.h b/Software/src/battery/CHADEMO-BATTERY.h index 19f39566..23d56828 100644 --- a/Software/src/battery/CHADEMO-BATTERY.h +++ b/Software/src/battery/CHADEMO-BATTERY.h @@ -1,7 +1,10 @@ #ifndef CHADEMO_BATTERY_H #define CHADEMO_BATTERY_H #include +#include "../datalayer/datalayer.h" +#include "../datalayer/datalayer_extended.h" #include "../include.h" +#include "CHADEMO-BATTERY-HTML.h" #include "CanBattery.h" #ifdef CHADEMO_BATTERY @@ -30,10 +33,18 @@ class ChademoBattery : public CanBattery { virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + bool supports_chademo_restart() { return true; } + bool supports_chademo_stop() { return true; } + + void chademo_restart() { datalayer_extended.chademo.UserRequestRestart = true; } + void chademo_stop() { datalayer_extended.chademo.UserRequestStop = true; } + + BatteryHtmlRenderer& get_status_renderer() { return renderer; } static constexpr const char* Name = "Chademo V2X mode"; private: gpio_num_t pin2, pin10, pin4, pin7, pin_lock, precharge, positive_contactor; + ChademoBatteryHtmlRenderer renderer; void process_vehicle_charging_minimums(CAN_frame rx_frame); void process_vehicle_charging_maximums(CAN_frame rx_frame); @@ -53,19 +64,17 @@ class ChademoBattery : public CanBattery { static const int MAX_EVSE_OUTPUT_VOLTAGE = 410; static const int MAX_EVSE_OUTPUT_CURRENT = 11; - enum CHADEMO_STATE { - CHADEMO_FAULT, - CHADEMO_STOP, - CHADEMO_IDLE, - CHADEMO_CONNECTED, - CHADEMO_INIT, // intermediate state indicating CAN from Vehicle not yet received after connection - CHADEMO_NEGOTIATE, - CHADEMO_EV_ALLOWED, - CHADEMO_EVSE_PREPARE, - CHADEMO_EVSE_START, - CHADEMO_EVSE_CONTACTORS_ENABLED, - CHADEMO_POWERFLOW, - }; +#define CHADEMO_FAULT 0 +#define CHADEMO_STOP 1 +#define CHADEMO_IDLE 2 +#define CHADEMO_CONNECTED 3 +#define CHADEMO_INIT 4 // intermediate state indicating CAN from Vehicle not yet received after connection +#define CHADEMO_NEGOTIATE 5 +#define CHADEMO_EV_ALLOWED 6 +#define CHADEMO_EVSE_PREPARE 7 +#define CHADEMO_EVSE_START 8 +#define CHADEMO_EVSE_CONTACTORS_ENABLED 9 +#define CHADEMO_POWERFLOW 10 enum Mode { CHADEMO_CHARGE, CHADEMO_DISCHARGE, CHADEMO_BIDIRECTIONAL }; @@ -304,7 +313,7 @@ should determine that the other is the EVSE or the vehicle of the model before t // permissible rate of change is -20A/s to 20A/s relative to 102.3 Mode EVSE_mode = CHADEMO_DISCHARGE; - CHADEMO_STATE CHADEMO_Status = CHADEMO_IDLE; + uint8_t CHADEMO_Status = CHADEMO_IDLE; /* Charge/discharge sequence, indicating applicable V2H guideline * If sequence number is not agreed upon via H201/H209 between EVSE and Vehicle, diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h index 2d78d6e2..d004f373 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h @@ -36,14 +36,14 @@ class RenaultZoeGen1Battery : public CanBattery { BatteryHtmlRenderer& get_status_renderer() { return renderer; } private: + RenaultZoeGen1HtmlRenderer renderer; + static const int MAX_PACK_VOLTAGE_DV = 4200; //5000 = 500.0V static const int MIN_PACK_VOLTAGE_DV = 3000; static const int MAX_CELL_DEVIATION_MV = 150; static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value - RenaultZoeGen1HtmlRenderer renderer; - DATALAYER_BATTERY_TYPE* datalayer_battery; DATALAYER_INFO_ZOE* datalayer_zoe; diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp index 4970f7bd..4c2a44bc 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp @@ -24,35 +24,35 @@ https://github.com/fesch/CanZE/tree/master/app/src/main/assets/ZOE_Ph2 void RenaultZoeGen2Battery::update_values() { - datalayer.battery.status.soh_pptt = battery_soh; + datalayer_battery->status.soh_pptt = battery_soh; if (battery_soc >= 300) { - datalayer.battery.status.real_soc = battery_soc - 300; + datalayer_battery->status.real_soc = battery_soc - 300; } else { - datalayer.battery.status.real_soc = 0; + datalayer_battery->status.real_soc = 0; } - datalayer.battery.status.voltage_dV = battery_pack_voltage; + datalayer_battery->status.voltage_dV = battery_pack_voltage; - datalayer.battery.status.current_dA = ((battery_current - 32640) * 0.3125); + datalayer_battery->status.current_dA = ((battery_current - 32640) * 0.3125); //Calculate the remaining Wh amount from SOC% and max Wh value. - datalayer.battery.status.remaining_capacity_Wh = static_cast( - (static_cast(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh); + datalayer_battery->status.remaining_capacity_Wh = static_cast( + (static_cast(datalayer_battery->status.real_soc) / 10000) * datalayer_battery->info.total_capacity_Wh); - datalayer.battery.status.max_discharge_power_W = battery_max_available * 10; + datalayer_battery->status.max_discharge_power_W = battery_max_available * 10; - datalayer.battery.status.max_charge_power_W = battery_max_generated * 10; + datalayer_battery->status.max_charge_power_W = battery_max_generated * 10; //Temperatures and voltages update at slow rate. Only publish new values once both have been sampled to avoid events if ((battery_min_temp != 920) && (battery_max_temp != 920)) { - datalayer.battery.status.temperature_min_dC = ((battery_min_temp - 640) * 0.625); - datalayer.battery.status.temperature_max_dC = ((battery_max_temp - 640) * 0.625); + datalayer_battery->status.temperature_min_dC = ((battery_min_temp - 640) * 0.625); + datalayer_battery->status.temperature_max_dC = ((battery_max_temp - 640) * 0.625); } if ((battery_min_cell_voltage != 3700) && (battery_max_cell_voltage != 3700)) { - datalayer.battery.status.cell_min_voltage_mV = (battery_min_cell_voltage * 0.976563); - datalayer.battery.status.cell_max_voltage_mV = (battery_max_cell_voltage * 0.976563); + datalayer_battery->status.cell_min_voltage_mV = (battery_min_cell_voltage * 0.976563); + datalayer_battery->status.cell_max_voltage_mV = (battery_max_cell_voltage * 0.976563); } if (battery_12v < 11000) { //11.000V @@ -103,12 +103,12 @@ void RenaultZoeGen2Battery::update_values() { } void RenaultZoeGen2Battery::handle_incoming_can_frame(CAN_frame rx_frame) { - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; switch (rx_frame.ID) { case 0x18DAF1DB: // LBC Reply from active polling if (rx_frame.data.u8[0] == 0x10) { //First frame of a group - transmit_can_frame(&ZOE_POLL_FLOW_CONTROL, can_config.battery); + transmit_can_frame(&ZOE_POLL_FLOW_CONTROL, can_interface); //frame 2 & 3 contains which PID is sent reply_poll = (rx_frame.data.u8[3] << 8) | rx_frame.data.u8[4]; } @@ -360,292 +360,292 @@ void RenaultZoeGen2Battery::handle_incoming_can_frame(CAN_frame rx_frame) { battery_soc_max = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_0: - datalayer.battery.status.cell_voltages_mV[0] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[0] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_1: - datalayer.battery.status.cell_voltages_mV[1] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[1] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_2: - datalayer.battery.status.cell_voltages_mV[2] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[2] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_3: - datalayer.battery.status.cell_voltages_mV[3] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[3] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_4: - datalayer.battery.status.cell_voltages_mV[4] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[4] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_5: - datalayer.battery.status.cell_voltages_mV[5] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[5] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_6: - datalayer.battery.status.cell_voltages_mV[6] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[6] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_7: - datalayer.battery.status.cell_voltages_mV[7] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[7] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_8: - datalayer.battery.status.cell_voltages_mV[8] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[8] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_9: - datalayer.battery.status.cell_voltages_mV[9] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[9] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_10: - datalayer.battery.status.cell_voltages_mV[10] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[10] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_11: - datalayer.battery.status.cell_voltages_mV[11] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[11] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_12: - datalayer.battery.status.cell_voltages_mV[12] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[12] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_13: - datalayer.battery.status.cell_voltages_mV[13] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[13] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_14: - datalayer.battery.status.cell_voltages_mV[14] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[14] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_15: - datalayer.battery.status.cell_voltages_mV[15] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[15] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_16: - datalayer.battery.status.cell_voltages_mV[16] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[16] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_17: - datalayer.battery.status.cell_voltages_mV[17] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[17] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_18: - datalayer.battery.status.cell_voltages_mV[18] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[18] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_19: - datalayer.battery.status.cell_voltages_mV[19] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[19] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_20: - datalayer.battery.status.cell_voltages_mV[20] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[20] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_21: - datalayer.battery.status.cell_voltages_mV[21] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[21] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_22: - datalayer.battery.status.cell_voltages_mV[22] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[22] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_23: - datalayer.battery.status.cell_voltages_mV[23] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[23] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_24: - datalayer.battery.status.cell_voltages_mV[24] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[24] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_25: - datalayer.battery.status.cell_voltages_mV[25] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[25] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_26: - datalayer.battery.status.cell_voltages_mV[26] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[26] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_27: - datalayer.battery.status.cell_voltages_mV[27] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[27] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_28: - datalayer.battery.status.cell_voltages_mV[28] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[28] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_29: - datalayer.battery.status.cell_voltages_mV[29] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[29] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_30: - datalayer.battery.status.cell_voltages_mV[30] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[30] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_31: - datalayer.battery.status.cell_voltages_mV[31] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[31] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_32: - datalayer.battery.status.cell_voltages_mV[32] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[32] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_33: - datalayer.battery.status.cell_voltages_mV[33] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[33] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_34: - datalayer.battery.status.cell_voltages_mV[34] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[34] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_35: - datalayer.battery.status.cell_voltages_mV[35] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[35] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_36: - datalayer.battery.status.cell_voltages_mV[36] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[36] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_37: - datalayer.battery.status.cell_voltages_mV[37] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[37] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_38: - datalayer.battery.status.cell_voltages_mV[38] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[38] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_39: - datalayer.battery.status.cell_voltages_mV[39] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[39] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_40: - datalayer.battery.status.cell_voltages_mV[40] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[40] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_41: - datalayer.battery.status.cell_voltages_mV[41] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[41] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_42: - datalayer.battery.status.cell_voltages_mV[42] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[42] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_43: - datalayer.battery.status.cell_voltages_mV[43] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[43] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_44: - datalayer.battery.status.cell_voltages_mV[44] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[44] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_45: - datalayer.battery.status.cell_voltages_mV[45] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[45] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_46: - datalayer.battery.status.cell_voltages_mV[46] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[46] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_47: - datalayer.battery.status.cell_voltages_mV[47] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[47] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_48: - datalayer.battery.status.cell_voltages_mV[48] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[48] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_49: - datalayer.battery.status.cell_voltages_mV[49] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[49] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_50: - datalayer.battery.status.cell_voltages_mV[50] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[50] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_51: - datalayer.battery.status.cell_voltages_mV[51] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[51] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_52: - datalayer.battery.status.cell_voltages_mV[52] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[52] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_53: - datalayer.battery.status.cell_voltages_mV[53] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[53] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_54: - datalayer.battery.status.cell_voltages_mV[54] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[54] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_55: - datalayer.battery.status.cell_voltages_mV[55] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[55] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_56: - datalayer.battery.status.cell_voltages_mV[56] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[56] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_57: - datalayer.battery.status.cell_voltages_mV[57] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[57] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_58: - datalayer.battery.status.cell_voltages_mV[58] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[58] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_59: - datalayer.battery.status.cell_voltages_mV[59] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[59] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_60: - datalayer.battery.status.cell_voltages_mV[60] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[60] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_61: - datalayer.battery.status.cell_voltages_mV[61] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[61] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_62: - datalayer.battery.status.cell_voltages_mV[62] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[62] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_63: - datalayer.battery.status.cell_voltages_mV[63] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[63] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_64: - datalayer.battery.status.cell_voltages_mV[64] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[64] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_65: - datalayer.battery.status.cell_voltages_mV[65] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[65] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_66: - datalayer.battery.status.cell_voltages_mV[66] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[66] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_67: - datalayer.battery.status.cell_voltages_mV[67] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[67] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_68: - datalayer.battery.status.cell_voltages_mV[68] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[68] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_69: - datalayer.battery.status.cell_voltages_mV[69] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[69] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_70: - datalayer.battery.status.cell_voltages_mV[70] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[70] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_71: - datalayer.battery.status.cell_voltages_mV[71] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[71] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_72: - datalayer.battery.status.cell_voltages_mV[72] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[72] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_73: - datalayer.battery.status.cell_voltages_mV[73] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[73] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_74: - datalayer.battery.status.cell_voltages_mV[74] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[74] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_75: - datalayer.battery.status.cell_voltages_mV[75] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[75] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_76: - datalayer.battery.status.cell_voltages_mV[76] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[76] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_77: - datalayer.battery.status.cell_voltages_mV[77] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[77] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_78: - datalayer.battery.status.cell_voltages_mV[78] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[78] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_79: - datalayer.battery.status.cell_voltages_mV[79] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[79] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_80: - datalayer.battery.status.cell_voltages_mV[80] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[80] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_81: - datalayer.battery.status.cell_voltages_mV[81] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[81] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_82: - datalayer.battery.status.cell_voltages_mV[82] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[82] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_83: - datalayer.battery.status.cell_voltages_mV[83] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[83] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_84: - datalayer.battery.status.cell_voltages_mV[84] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[84] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_85: - datalayer.battery.status.cell_voltages_mV[85] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[85] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_86: - datalayer.battery.status.cell_voltages_mV[86] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[86] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_87: - datalayer.battery.status.cell_voltages_mV[87] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[87] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_88: - datalayer.battery.status.cell_voltages_mV[88] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[88] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_89: - datalayer.battery.status.cell_voltages_mV[89] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[89] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_90: - datalayer.battery.status.cell_voltages_mV[90] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[90] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_91: - datalayer.battery.status.cell_voltages_mV[91] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[91] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_92: - datalayer.battery.status.cell_voltages_mV[92] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[92] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_93: - datalayer.battery.status.cell_voltages_mV[93] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[93] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_94: - datalayer.battery.status.cell_voltages_mV[94] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[94] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; case POLL_CELL_95: - datalayer.battery.status.cell_voltages_mV[95] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; + datalayer_battery->status.cell_voltages_mV[95] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]; break; default: // Unknown reply break; @@ -676,7 +676,7 @@ void RenaultZoeGen2Battery::transmit_can(unsigned long currentMillis) { counter_373 = (counter_373 + 1) % 10; */ - transmit_can_frame(&ZOE_373, can_config.battery); + transmit_can_frame(&ZOE_373, can_interface); transmit_can_frame_376(); } @@ -691,7 +691,7 @@ void RenaultZoeGen2Battery::transmit_can(unsigned long currentMillis) { ZOE_POLL_18DADBF1.data.u8[2] = (uint8_t)((currentpoll & 0xFF00) >> 8); ZOE_POLL_18DADBF1.data.u8[3] = (uint8_t)(currentpoll & 0x00FF); - transmit_can_frame(&ZOE_POLL_18DADBF1, can_config.battery); + transmit_can_frame(&ZOE_POLL_18DADBF1, can_interface); } if (currentMillis - previousMillis1000 >= INTERVAL_1_S) { @@ -707,12 +707,12 @@ void RenaultZoeGen2Battery::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; - datalayer.battery.info.number_of_cells = 96; - 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.max_cell_voltage_mV = MAX_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.number_of_cells = 96; + 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.max_cell_voltage_mV = MAX_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; } void RenaultZoeGen2Battery::transmit_can_frame_376(void) { @@ -733,7 +733,7 @@ void RenaultZoeGen2Battery::transmit_can_frame_376(void) { ZOE_376.data.u8[4] = hourSeg; ZOE_376.data.u8[5] = minuteSeg; - transmit_can_frame(&ZOE_376, can_config.battery); + transmit_can_frame(&ZOE_376, can_interface); } void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) { @@ -742,14 +742,14 @@ void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) { startTimeNVROL = millis(); // NVROL reset, part 1: send 0x021003AAAAAAAAAA ZOE_POLL_18DADBF1.data = {0x02, 0x10, 0x03, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; - transmit_can_frame(&ZOE_POLL_18DADBF1, can_config.battery); + transmit_can_frame(&ZOE_POLL_18DADBF1, can_interface); NVROLstateMachine = 1; break; case 1: // wait 100 ms if ((millis() - startTimeNVROL) > INTERVAL_100_MS) { // NVROL reset, part 2: send 0x043101B00900AAAA ZOE_POLL_18DADBF1.data = {0x04, 0x31, 0x01, 0xB0, 0x09, 0x00, 0xAA, 0xAA}; - transmit_can_frame(&ZOE_POLL_18DADBF1, can_config.battery); + transmit_can_frame(&ZOE_POLL_18DADBF1, can_interface); startTimeNVROL = millis(); //Reset time start, so we can check time for next step NVROLstateMachine = 2; } @@ -758,7 +758,7 @@ void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) { if ((millis() - startTimeNVROL) > INTERVAL_1_S) { // Enable temporisation before sleep, part 1: send 0x021003AAAAAAAAAA ZOE_POLL_18DADBF1.data = {0x02, 0x10, 0x03, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; - transmit_can_frame(&ZOE_POLL_18DADBF1, can_config.battery); + transmit_can_frame(&ZOE_POLL_18DADBF1, can_interface); startTimeNVROL = millis(); //Reset time start, so we can check time for next step NVROLstateMachine = 3; } @@ -767,7 +767,7 @@ void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) { if ((millis() - startTimeNVROL) > INTERVAL_100_MS) { // Enable temporisation before sleep, part 2: send 0x042E928101AAAAAA ZOE_POLL_18DADBF1.data = {0x04, 0x2E, 0x92, 0x81, 0x01, 0xAA, 0xAA, 0xAA}; - transmit_can_frame(&ZOE_POLL_18DADBF1, can_config.battery); + transmit_can_frame(&ZOE_POLL_18DADBF1, can_interface); // Set data back to init values, we are done with the ZOE_POLL_18DADBF1 frame ZOE_POLL_18DADBF1.data = {0x03, 0x22, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00}; poll_index = 0; diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h index 2cdbc737..c375b996 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h @@ -11,6 +11,23 @@ class RenaultZoeGen2Battery : public CanBattery { public: + // Use this constructor for the second battery. + RenaultZoeGen2Battery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_ZOE_PH2* extended, + CAN_Interface targetCan) + : CanBattery(targetCan) { + datalayer_battery = datalayer_ptr; + allows_contactor_closing = nullptr; + datalayer_zoePH2 = extended; + + battery_pack_voltage = 0; + } + + // Use the default constructor to create the first or single battery. + RenaultZoeGen2Battery() { + datalayer_battery = &datalayer.battery; + allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing; + datalayer_zoePH2 = &datalayer_extended.zoePH2; + } virtual void setup(void); virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); @@ -25,6 +42,13 @@ class RenaultZoeGen2Battery : public CanBattery { private: RenaultZoeGen2HtmlRenderer renderer; + + DATALAYER_BATTERY_TYPE* datalayer_battery; + DATALAYER_INFO_ZOE_PH2* datalayer_zoePH2; + + // If not null, this battery decides when the contactor can be closed and writes the value here. + bool* allows_contactor_closing; + static const int MAX_PACK_VOLTAGE_DV = 4100; //5000 = 500.0V static const int MIN_PACK_VOLTAGE_DV = 3000; static const int MAX_CELL_DEVIATION_MV = 150; @@ -174,7 +198,7 @@ class RenaultZoeGen2Battery : public CanBattery { uint16_t battery_soc = 0; uint16_t battery_usable_soc = 5000; uint16_t battery_soh = 10000; - uint16_t battery_pack_voltage = 370; + uint16_t battery_pack_voltage = 3700; uint16_t battery_max_cell_voltage = 3700; uint16_t battery_min_cell_voltage = 3700; uint16_t battery_12v = 12000; diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index 9c10deab..a78911c0 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -124,48 +124,6 @@ typedef struct { } DATALAYER_INFO_BMWPHEV; -typedef struct { - /** uint16_t */ - /** SOC% raw battery value. Might not always reach 100% */ - uint16_t SOC_raw = 0; - /** uint16_t */ - /** SOC% instrumentation cluster value. Will always reach 100% */ - uint16_t SOC_dash = 0; - /** uint16_t */ - /** SOC% OBD2 value, polled actively */ - uint16_t SOC_OBD2 = 0; - /** 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; -} DATALAYER_INFO_BMWI3; - typedef struct { /** bool */ /** User requesting crash reset via WebUI*/ @@ -274,6 +232,19 @@ typedef struct { bool warning_Charger_not_responding = false; } DATALAYER_INFO_CELLPOWER; +typedef struct { + bool UserRequestRestart = false; + bool UserRequestStop = false; + bool FaultBatteryVoltageDeviation = false; + bool FaultHighBatteryTemperature = false; + bool FaultBatteryCurrentDeviation = false; + bool FaultBatteryUnderVoltage = false; + bool FaultBatteryOverVoltage = false; + uint8_t CHADEMO_Status = 0; + uint8_t ControlProtocolNumberEV = 0; + +} DATALAYER_INFO_CHADEMO; + typedef struct { uint16_t soc_z = 0; uint16_t soc_u = 0; @@ -882,9 +853,9 @@ class DataLayerExtended { DATALAYER_INFO_BOLTAMPERA boltampera; DATALAYER_INFO_BMWIX bmwix; DATALAYER_INFO_BMWPHEV bmwphev; - DATALAYER_INFO_BMWI3 bmwi3; DATALAYER_INFO_BYDATTO3 bydAtto3; DATALAYER_INFO_CELLPOWER cellpower; + DATALAYER_INFO_CHADEMO chademo; DATALAYER_INFO_CMFAEV CMFAEV; DATALAYER_INFO_ECMP stellantisECMP; DATALAYER_INFO_GEELY_GEOMETRY_C geometryC; diff --git a/Software/src/devboard/webserver/advanced_battery_html.cpp b/Software/src/devboard/webserver/advanced_battery_html.cpp index f1877137..911ea166 100644 --- a/Software/src/devboard/webserver/advanced_battery_html.cpp +++ b/Software/src/devboard/webserver/advanced_battery_html.cpp @@ -12,6 +12,15 @@ std::vector battery_commands = { [](Battery* b) { b->clear_isolation(); }}, + {"chademoRestart", "Restart", "restart the V2X session?", + [](Battery* b) { return b && b->supports_chademo_restart(); }, + [](Battery* b) { + b->chademo_restart(); + }}, + {"chademoStop", "Stop", "stop V2X?", [](Battery* b) { return b && b->supports_chademo_restart(); }, + [](Battery* b) { + b->chademo_restart(); + }}, {"resetBMS", "BMS reset", "reset the BMS?", [](Battery* b) { return b && b->supports_reset_BMS(); }, [](Battery* b) { b->reset_BMS();