From 5d0b93986cdd60f8d9137763bd72c1d260e7df1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 8 Jun 2025 20:22:50 +0300 Subject: [PATCH 01/32] Start to work on making webserver crash free --- Software/src/battery/BMW-PHEV-HTML.h | 138 +++++++++++++++++++++--- Software/src/battery/NISSAN-LEAF-HTML.h | 16 ++- 2 files changed, 138 insertions(+), 16 deletions(-) diff --git a/Software/src/battery/BMW-PHEV-HTML.h b/Software/src/battery/BMW-PHEV-HTML.h index 812fd101..1d03ef06 100644 --- a/Software/src/battery/BMW-PHEV-HTML.h +++ b/Software/src/battery/BMW-PHEV-HTML.h @@ -15,20 +15,130 @@ class BmwPhevHtmlRenderer : public BatteryHtmlRenderer { " dV"; content += "

Allowed Discharge Power: " + String(datalayer.battery.status.max_discharge_power_W) + " W

"; content += "

Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W

"; - 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 += "

Balancing: " + String((balanceText[datalayer_extended.bmwphev.balancing_status])) + "

"; - 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 += "

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

"; - content += "

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

"; - content += "

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

"; - content += "

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

"; - content += "

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

"; - content += "

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

"; + content += "

Balancing: "; + switch (datalayer_extended.bmwphev.balancing_status) { + case 0: + content += String("0 Balancing Inactive - Balancing not needed

"); + break; + case 1: + content += String("1 Balancing Active"); + break; + case 2: + content += String("2 Balancing Inactive - Cells not in rest break wait 10mins"); + break; + case 3: + content += String("3 Balancing Inactive"); + break; + case 4: + content += String("4 Unknown"); + break; + default: + content += String("Unknown"); + } + content += "

Interlock: "; + switch (datalayer_extended.bmwphev.ST_interlock) { + case 0: + content += String("Not Evaluated

"); + break; + case 1: + content += String("OK"); + break; + case 2: + content += String("Error! Not seated!"); + break; + case 3: + content += String("Invalid signal"); + break; + default: + content += String("Unknown"); + } + content += "

Isolation external: "; + switch (datalayer_extended.bmwphev.ST_iso_ext) { + case 0: + content += String("Not Evaluated

"); + break; + case 1: + content += String("OK"); + break; + case 2: + content += String("Error!"); + break; + case 3: + content += String("Invalid signal"); + break; + default: + content += String("Unknown"); + } + content += "

Isolation internal: "; + switch (datalayer_extended.bmwphev.ST_iso_int) { + case 0: + content += String("Not Evaluated

"); + break; + case 1: + content += String("OK"); + break; + case 2: + content += String("Error!"); + break; + case 3: + content += String("Invalid signal"); + break; + default: + content += String("Unknown"); + } + content += "

Isolation: "; + switch (datalayer_extended.bmwphev.ST_isolation) { + case 0: + content += String("Not Evaluated

"); + break; + case 1: + content += String("OK"); + break; + case 2: + content += String("Error!"); + break; + case 3: + content += String("Invalid signal"); + break; + default: + content += String("Unknown"); + } + content += "

Cooling valve: "; + switch (datalayer_extended.bmwphev.ST_valve_cooling) { + case 0: + content += String("Not Evaluated

"); + break; + case 1: + content += String("OK"); + break; + case 2: + content += String("Error!"); + break; + case 3: + content += String("Invalid signal"); + break; + default: + content += String("Unknown"); + } + content += "

Emergency: "; + switch (datalayer_extended.bmwphev.ST_EMG) { + case 0: + content += String("Not Evaluated

"); + break; + case 1: + content += String("OK"); + break; + case 2: + content += String("Error!"); + break; + case 3: + content += String("Invalid signal"); + break; + default: + content += String("Unknown"); + } + + static const char* prechargeText[16] = {"Not evaluated", "Not active, closing not blocked", "Error precharge blocked", diff --git a/Software/src/battery/NISSAN-LEAF-HTML.h b/Software/src/battery/NISSAN-LEAF-HTML.h index 18aac585..275bef7b 100644 --- a/Software/src/battery/NISSAN-LEAF-HTML.h +++ b/Software/src/battery/NISSAN-LEAF-HTML.h @@ -10,8 +10,20 @@ class NissanLeafHtmlRenderer : public BatteryHtmlRenderer { String get_status_html() { String content; - static const char* LEAFgen[] = {"ZE0", "AZE0", "ZE1"}; - content += "

LEAF generation: " + String(LEAFgen[datalayer_extended.nissanleaf.LEAF_gen]) + "

"; + content += "

LEAF generation: "; + switch (datalayer_extended.nissanleaf.LEAF_gen) { + case 0: + content += String("ZE0

"); + break; + case 1: + content += String("AZE0"); + break; + case 2: + content += String("ZE1"); + break; + default: + content += String("Unknown"); + } char readableSerialNumber[16]; // One extra space for null terminator memcpy(readableSerialNumber, datalayer_extended.nissanleaf.BatterySerialNumber, sizeof(datalayer_extended.nissanleaf.BatterySerialNumber)); From 75c66cb6d3c191e00f0681eca474529a924884cc Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Mon, 9 Jun 2025 22:29:52 +0300 Subject: [PATCH 02/32] Eliminate CAN_SHUNT_SELECTED define --- Software/src/battery/BMW-SBOX.h | 1 - Software/src/battery/Shunt.h | 3 +++ Software/src/devboard/webserver/settings_html.cpp | 8 ++++---- Software/src/devboard/webserver/webserver.cpp | 10 +++++----- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Software/src/battery/BMW-SBOX.h b/Software/src/battery/BMW-SBOX.h index 97ef3cd5..ad4046f6 100644 --- a/Software/src/battery/BMW-SBOX.h +++ b/Software/src/battery/BMW-SBOX.h @@ -2,7 +2,6 @@ #define BMW_SBOX_CONTROL_H #include "../include.h" -#define CAN_SHUNT_SELECTED #define SELECTED_SHUNT_CLASS BmwSbox #include "Shunt.h" diff --git a/Software/src/battery/Shunt.h b/Software/src/battery/Shunt.h index 6672d998..55b0c849 100644 --- a/Software/src/battery/Shunt.h +++ b/Software/src/battery/Shunt.h @@ -11,6 +11,9 @@ class CanShunt : public Transmitter, CanReceiver { virtual void transmit_can(unsigned long currentMillis) = 0; virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0; + // The name of the comm interface the shunt is using. + virtual String interface_name() { return getCANInterfaceName(can_config.shunt); } + void transmit(unsigned long currentMillis) { if (allowed_to_send_CAN) { transmit_can(currentMillis); diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 1d55e063..13439de5 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -40,10 +40,10 @@ String settings_processor(const String& var) { String(inverter->interface_name()) + ""; } -#ifdef CAN_SHUNT_SELECTED - content += "

Shunt Interface: " + - String(getCANInterfaceName(can_config.shunt)) + "

"; -#endif //CAN_SHUNT_SELECTED + if (shunt) { + content += + "

Shunt Interface: " + shunt->interface_name() + "

"; + } // Close the block content += ""; diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 0cbc0838..77f8fae3 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -960,11 +960,11 @@ String processor(const String& var) { } content += ""; -#ifdef CAN_SHUNT_SELECTED - content += "

Shunt protocol: "; - content += datalayer.system.info.shunt_protocol; - content += "

"; -#endif + if (shunt) { + content += "

Shunt protocol: "; + content += datalayer.system.info.shunt_protocol; + content += "

"; + } if (charger) { content += "

Charger protocol: "; From c101f1a7b11e6514c27adeb4738d629497fa7d84 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Mon, 9 Jun 2025 22:40:27 +0300 Subject: [PATCH 03/32] Initial changes --- Software/src/battery/BATTERIES.h | 19 +------------------ Software/src/battery/BMW-I3-BATTERY.h | 3 ++- Software/src/battery/BMW-IX-BATTERY.h | 3 ++- Software/src/battery/BMW-PHEV-BATTERY.h | 3 ++- Software/src/battery/BMW-SBOX.h | 2 ++ Software/src/battery/BOLT-AMPERA-BATTERY.h | 3 ++- Software/src/battery/BYD-ATTO-3-BATTERY.h | 3 ++- Software/src/battery/CELLPOWER-BMS.h | 1 - Software/src/battery/CHADEMO-BATTERY.h | 1 - Software/src/battery/CMFA-EV-BATTERY.h | 1 - Software/src/battery/DALY-BMS.h | 3 --- Software/src/battery/ECMP-BATTERY.h | 1 - Software/src/battery/FOXESS-BATTERY.h | 1 - .../src/battery/GEELY-GEOMETRY-C-BATTERY.h | 1 - .../src/battery/IMIEV-CZERO-ION-BATTERY.h | 1 - Software/src/battery/JAGUAR-IPACE-BATTERY.h | 1 - Software/src/battery/KIA-E-GMP-BATTERY.h | 1 - Software/src/battery/KIA-HYUNDAI-64-BATTERY.h | 1 - .../src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h | 1 - Software/src/battery/MEB-BATTERY.h | 1 - Software/src/battery/MG-5-BATTERY.h | 1 - Software/src/battery/NISSAN-LEAF-BATTERY.h | 1 - Software/src/battery/ORION-BMS.h | 1 - Software/src/battery/PYLON-BATTERY.h | 1 - .../src/battery/RANGE-ROVER-PHEV-BATTERY.h | 1 - Software/src/battery/RENAULT-KANGOO-BATTERY.h | 1 - Software/src/battery/RENAULT-TWIZY.h | 1 - .../src/battery/RENAULT-ZOE-GEN1-BATTERY.h | 1 - .../src/battery/RENAULT-ZOE-GEN2-BATTERY.h | 1 - Software/src/battery/RJXZS-BMS.h | 1 - Software/src/battery/SANTA-FE-PHEV-BATTERY.h | 1 - Software/src/battery/SIMPBMS-BATTERY.h | 1 - Software/src/battery/SONO-BATTERY.h | 1 - Software/src/battery/TESLA-BATTERY.h | 1 - Software/src/battery/TEST-FAKE-BATTERY.h | 1 - Software/src/battery/VOLVO-SPA-BATTERY.h | 1 - .../src/battery/VOLVO-SPA-HYBRID-BATTERY.h | 1 - Software/src/include.h | 2 +- 38 files changed, 14 insertions(+), 56 deletions(-) diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index a1e38da3..eaad5bd8 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -11,29 +11,12 @@ extern Battery* battery2; void setup_can_shunt(); -#ifdef BMW_SBOX -#include "BMW-SBOX.h" -#endif - -#ifdef BMW_I3_BATTERY #include "BMW-I3-BATTERY.h" -#endif - -#ifdef BMW_IX_BATTERY #include "BMW-IX-BATTERY.h" -#endif - -#ifdef BMW_PHEV_BATTERY #include "BMW-PHEV-BATTERY.h" -#endif - -#ifdef BOLT_AMPERA_BATTERY +#include "BMW-SBOX.h" #include "BOLT-AMPERA-BATTERY.h" -#endif - -#ifdef BYD_ATTO_3_BATTERY #include "BYD-ATTO-3-BATTERY.h" -#endif #ifdef CELLPOWER_BMS #include "CELLPOWER-BMS.h" diff --git a/Software/src/battery/BMW-I3-BATTERY.h b/Software/src/battery/BMW-I3-BATTERY.h index 9a60a0ce..cf79f4db 100644 --- a/Software/src/battery/BMW-I3-BATTERY.h +++ b/Software/src/battery/BMW-I3-BATTERY.h @@ -7,8 +7,9 @@ #include "BMW-I3-HTML.h" #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef BMW_I3_BATTERY #define SELECTED_BATTERY_CLASS BmwI3Battery +#endif class BmwI3Battery : public CanBattery { public: diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h index 68935fad..ed67cb10 100644 --- a/Software/src/battery/BMW-IX-BATTERY.h +++ b/Software/src/battery/BMW-IX-BATTERY.h @@ -5,8 +5,9 @@ #include "BMW-IX-HTML.h" #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef BMW_IX_BATTERY #define SELECTED_BATTERY_CLASS BmwIXBattery +#endif class BmwIXBattery : public CanBattery { public: diff --git a/Software/src/battery/BMW-PHEV-BATTERY.h b/Software/src/battery/BMW-PHEV-BATTERY.h index c50992c5..98f0e640 100644 --- a/Software/src/battery/BMW-PHEV-BATTERY.h +++ b/Software/src/battery/BMW-PHEV-BATTERY.h @@ -5,8 +5,9 @@ #include "BMW-PHEV-HTML.h" #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef BMW_PHEV_BATTERY #define SELECTED_BATTERY_CLASS BmwPhevBattery +#endif class BmwPhevBattery : public CanBattery { public: diff --git a/Software/src/battery/BMW-SBOX.h b/Software/src/battery/BMW-SBOX.h index ad4046f6..1909c48b 100644 --- a/Software/src/battery/BMW-SBOX.h +++ b/Software/src/battery/BMW-SBOX.h @@ -2,7 +2,9 @@ #define BMW_SBOX_CONTROL_H #include "../include.h" +#ifdef BMW_SBOX #define SELECTED_SHUNT_CLASS BmwSbox +#endif #include "Shunt.h" diff --git a/Software/src/battery/BOLT-AMPERA-BATTERY.h b/Software/src/battery/BOLT-AMPERA-BATTERY.h index 57fdba84..bf3e6fac 100644 --- a/Software/src/battery/BOLT-AMPERA-BATTERY.h +++ b/Software/src/battery/BOLT-AMPERA-BATTERY.h @@ -6,8 +6,9 @@ #include "BOLT-AMPERA-HTML.h" #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef BOLT_AMPERA_BATTERY #define SELECTED_BATTERY_CLASS BoltAmperaBattery +#endif class BoltAmperaBattery : public CanBattery { public: diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.h b/Software/src/battery/BYD-ATTO-3-BATTERY.h index 84baa4d5..7d665622 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.h +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.h @@ -18,8 +18,9 @@ //#define SKIP_TEMPERATURE_SENSOR_NUMBER 1 /* Do not modify the rows below */ -#define BATTERY_SELECTED +#ifdef BYD_ATTO_3_BATTERY #define SELECTED_BATTERY_CLASS BydAttoBattery +#endif #define CELLCOUNT_EXTENDED 126 #define CELLCOUNT_STANDARD 104 diff --git a/Software/src/battery/CELLPOWER-BMS.h b/Software/src/battery/CELLPOWER-BMS.h index 15a4662f..eef6b0d2 100644 --- a/Software/src/battery/CELLPOWER-BMS.h +++ b/Software/src/battery/CELLPOWER-BMS.h @@ -5,7 +5,6 @@ #include "CELLPOWER-HTML.h" #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS CellPowerBms class CellPowerBms : public CanBattery { diff --git a/Software/src/battery/CHADEMO-BATTERY.h b/Software/src/battery/CHADEMO-BATTERY.h index 7e50841f..57851095 100644 --- a/Software/src/battery/CHADEMO-BATTERY.h +++ b/Software/src/battery/CHADEMO-BATTERY.h @@ -11,7 +11,6 @@ // other measurement sources may be added in the future #define ISA_SHUNT -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS ChademoBattery class ChademoBattery : public CanBattery { diff --git a/Software/src/battery/CMFA-EV-BATTERY.h b/Software/src/battery/CMFA-EV-BATTERY.h index 0455838e..ebd51231 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.h +++ b/Software/src/battery/CMFA-EV-BATTERY.h @@ -5,7 +5,6 @@ #include "CMFA-EV-HTML.h" #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS CmfaEvBattery class CmfaEvBattery : public CanBattery { diff --git a/Software/src/battery/DALY-BMS.h b/Software/src/battery/DALY-BMS.h index a8902f85..0ca1b020 100644 --- a/Software/src/battery/DALY-BMS.h +++ b/Software/src/battery/DALY-BMS.h @@ -13,9 +13,6 @@ #define POWER_PER_DEGREE_C 60 // max power added/removed per degree above/below 0°C #define POWER_AT_0_DEGREE_C 800 // power at 0°C -/* Do not modify any rows below*/ -#define BATTERY_SELECTED -#define RS485_BATTERY_SELECTED #define SELECTED_BATTERY_CLASS DalyBms class DalyBms : public RS485Battery { diff --git a/Software/src/battery/ECMP-BATTERY.h b/Software/src/battery/ECMP-BATTERY.h index ad3fb041..46e4a8c3 100644 --- a/Software/src/battery/ECMP-BATTERY.h +++ b/Software/src/battery/ECMP-BATTERY.h @@ -6,7 +6,6 @@ #include "CanBattery.h" #include "ECMP-HTML.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS EcmpBattery class EcmpBattery : public CanBattery { diff --git a/Software/src/battery/FOXESS-BATTERY.h b/Software/src/battery/FOXESS-BATTERY.h index b5102b8e..429f2a61 100644 --- a/Software/src/battery/FOXESS-BATTERY.h +++ b/Software/src/battery/FOXESS-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS FoxessBattery class FoxessBattery : public CanBattery { diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h index f724c937..16a126fb 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h @@ -6,7 +6,6 @@ #include "CanBattery.h" #include "GEELY-GEOMETRY-C-HTML.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS GeelyGeometryCBattery #define POLL_SOC 0x4B35 diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h index 7ed242a4..14008d61 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS ImievCZeroIonBattery class ImievCZeroIonBattery : public CanBattery { diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.h b/Software/src/battery/JAGUAR-IPACE-BATTERY.h index f91444a0..5c9435eb 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.h +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.h @@ -3,7 +3,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS JaguarIpaceBattery #define MAX_PACK_VOLTAGE_DV 4546 //5000 = 500.0V diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.h b/Software/src/battery/KIA-E-GMP-BATTERY.h index 13e8b6eb..01ec6fb3 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.h +++ b/Software/src/battery/KIA-E-GMP-BATTERY.h @@ -9,7 +9,6 @@ extern ACAN2517FD canfd; #define ESTIMATE_SOC_FROM_CELLVOLTAGE -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS KiaEGmpBattery class KiaEGmpBattery : public CanBattery { diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h index 62dbbde0..ba0181c4 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h @@ -7,7 +7,6 @@ #include "CanBattery.h" #include "KIA-HYUNDAI-64-HTML.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS KiaHyundai64Battery class KiaHyundai64Battery : public CanBattery { diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h index 915fdcef..16753590 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS KiaHyundaiHybridBattery class KiaHyundaiHybridBattery : public CanBattery { diff --git a/Software/src/battery/MEB-BATTERY.h b/Software/src/battery/MEB-BATTERY.h index 7895247c..c20f479d 100644 --- a/Software/src/battery/MEB-BATTERY.h +++ b/Software/src/battery/MEB-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" #include "MEB-HTML.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS MebBattery class MebBattery : public CanBattery { diff --git a/Software/src/battery/MG-5-BATTERY.h b/Software/src/battery/MG-5-BATTERY.h index ee34249a..4078d38c 100644 --- a/Software/src/battery/MG-5-BATTERY.h +++ b/Software/src/battery/MG-5-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS Mg5Battery class Mg5Battery : public CanBattery { diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.h b/Software/src/battery/NISSAN-LEAF-BATTERY.h index 16a68bc2..c29783a6 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.h +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.h @@ -7,7 +7,6 @@ #include "CanBattery.h" #include "NISSAN-LEAF-HTML.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS NissanLeafBattery #define EXTENDED_DATA_PTR (&datalayer_extended.nissanleaf) diff --git a/Software/src/battery/ORION-BMS.h b/Software/src/battery/ORION-BMS.h index e2785059..d06c2c90 100644 --- a/Software/src/battery/ORION-BMS.h +++ b/Software/src/battery/ORION-BMS.h @@ -5,7 +5,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS OrionBms class OrionBms : public CanBattery { diff --git a/Software/src/battery/PYLON-BATTERY.h b/Software/src/battery/PYLON-BATTERY.h index cf21aeb5..16843368 100644 --- a/Software/src/battery/PYLON-BATTERY.h +++ b/Software/src/battery/PYLON-BATTERY.h @@ -6,7 +6,6 @@ #include "../include.h" #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS PylonBattery class PylonBattery : public CanBattery { diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h index d1f1b13a..fcde6148 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS RangeRoverPhevBattery class RangeRoverPhevBattery : public CanBattery { diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.h b/Software/src/battery/RENAULT-KANGOO-BATTERY.h index 535f0369..cadca65f 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.h +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS RenaultKangooBattery class RenaultKangooBattery : public CanBattery { diff --git a/Software/src/battery/RENAULT-TWIZY.h b/Software/src/battery/RENAULT-TWIZY.h index f4ab501c..25f15c03 100644 --- a/Software/src/battery/RENAULT-TWIZY.h +++ b/Software/src/battery/RENAULT-TWIZY.h @@ -3,7 +3,6 @@ #include "../include.h" #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS RenaultTwizyBattery class RenaultTwizyBattery : public CanBattery { diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h index 1e566b04..ea38c3a9 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h @@ -4,7 +4,6 @@ #include "CanBattery.h" #include "RENAULT-ZOE-GEN1-HTML.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery #define MAX_PACK_VOLTAGE_DV 4200 //5000 = 500.0V diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h index b3b2b5d7..faae6e73 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" #include "RENAULT-ZOE-GEN2-HTML.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery class RenaultZoeGen2Battery : public CanBattery { diff --git a/Software/src/battery/RJXZS-BMS.h b/Software/src/battery/RJXZS-BMS.h index baabbbb5..e6283f7a 100644 --- a/Software/src/battery/RJXZS-BMS.h +++ b/Software/src/battery/RJXZS-BMS.h @@ -5,7 +5,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS RjxzsBms class RjxzsBms : public CanBattery { diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.h b/Software/src/battery/SANTA-FE-PHEV-BATTERY.h index f73e80e3..2b240ba7 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.h +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.h @@ -5,7 +5,6 @@ #include "../include.h" #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS SantaFePhevBattery class SantaFePhevBattery : public CanBattery { diff --git a/Software/src/battery/SIMPBMS-BATTERY.h b/Software/src/battery/SIMPBMS-BATTERY.h index ac33580d..7c5be60e 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.h +++ b/Software/src/battery/SIMPBMS-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS SimpBmsBattery class SimpBmsBattery : public CanBattery { diff --git a/Software/src/battery/SONO-BATTERY.h b/Software/src/battery/SONO-BATTERY.h index 47907f5e..de62dcdd 100644 --- a/Software/src/battery/SONO-BATTERY.h +++ b/Software/src/battery/SONO-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS SonoBattery class SonoBattery : public CanBattery { diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index 7b60cb91..9910c838 100644 --- a/Software/src/battery/TESLA-BATTERY.h +++ b/Software/src/battery/TESLA-BATTERY.h @@ -5,7 +5,6 @@ #include "CanBattery.h" #include "TESLA-HTML.h" -#define BATTERY_SELECTED #ifdef TESLA_MODEL_3Y_BATTERY #define SELECTED_BATTERY_CLASS TeslaModel3YBattery #endif diff --git a/Software/src/battery/TEST-FAKE-BATTERY.h b/Software/src/battery/TEST-FAKE-BATTERY.h index ed79c8b1..1223a634 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.h +++ b/Software/src/battery/TEST-FAKE-BATTERY.h @@ -4,7 +4,6 @@ #include "../include.h" #include "CanBattery.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS TestFakeBattery class TestFakeBattery : public CanBattery { diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.h b/Software/src/battery/VOLVO-SPA-BATTERY.h index 4ea009ed..b958d5da 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-BATTERY.h @@ -6,7 +6,6 @@ #include "CanBattery.h" #include "VOLVO-SPA-HTML.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS VolvoSpaBattery class VolvoSpaBattery : public CanBattery { diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h index 70b58cb8..e553dce7 100644 --- a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h @@ -6,7 +6,6 @@ #include "CanBattery.h" #include "VOLVO-SPA-HYBRID-HTML.h" -#define BATTERY_SELECTED #define SELECTED_BATTERY_CLASS VolvoSpaHybridBattery class VolvoSpaHybridBattery : public CanBattery { diff --git a/Software/src/include.h b/Software/src/include.h index d06d89dd..daacf9b0 100644 --- a/Software/src/include.h +++ b/Software/src/include.h @@ -39,7 +39,7 @@ #endif #endif -#ifndef BATTERY_SELECTED +#if !defined(COMMON_IMAGE) && !defined(SELECTED_BATTERY_CLASS) #error No battery selected! Choose one from the USER_SETTINGS.h file #endif From 7d7b6a0d10ecb202f05332e509fd93be31139ab0 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Mon, 9 Jun 2025 23:04:13 +0300 Subject: [PATCH 04/32] More changes --- Software/src/battery/CELLPOWER-BMS.h | 2 ++ Software/src/battery/CHADEMO-BATTERY.h | 6 ++++-- Software/src/battery/CMFA-EV-BATTERY.h | 2 ++ Software/src/battery/FOXESS-BATTERY.h | 2 ++ Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h | 7 ++++++- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Software/src/battery/CELLPOWER-BMS.h b/Software/src/battery/CELLPOWER-BMS.h index eef6b0d2..d5ba8d78 100644 --- a/Software/src/battery/CELLPOWER-BMS.h +++ b/Software/src/battery/CELLPOWER-BMS.h @@ -5,7 +5,9 @@ #include "CELLPOWER-HTML.h" #include "CanBattery.h" +#ifdef CELLPOWER_BMS #define SELECTED_BATTERY_CLASS CellPowerBms +#endif class CellPowerBms : public CanBattery { public: diff --git a/Software/src/battery/CHADEMO-BATTERY.h b/Software/src/battery/CHADEMO-BATTERY.h index 57851095..5108cced 100644 --- a/Software/src/battery/CHADEMO-BATTERY.h +++ b/Software/src/battery/CHADEMO-BATTERY.h @@ -4,14 +4,16 @@ #include "../include.h" #include "CanBattery.h" +#ifdef CHADEMO_BATTERY +#define SELECTED_BATTERY_CLASS ChademoBattery + //Contactor control is required for CHADEMO support #define CONTACTOR_CONTROL //ISA shunt is currently required for CHADEMO support // other measurement sources may be added in the future #define ISA_SHUNT - -#define SELECTED_BATTERY_CLASS ChademoBattery +#endif class ChademoBattery : public CanBattery { public: diff --git a/Software/src/battery/CMFA-EV-BATTERY.h b/Software/src/battery/CMFA-EV-BATTERY.h index ebd51231..625601ae 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.h +++ b/Software/src/battery/CMFA-EV-BATTERY.h @@ -5,7 +5,9 @@ #include "CMFA-EV-HTML.h" #include "CanBattery.h" +#ifdef CMFA_EV_BATTERY #define SELECTED_BATTERY_CLASS CmfaEvBattery +#endif class CmfaEvBattery : public CanBattery { public: diff --git a/Software/src/battery/FOXESS-BATTERY.h b/Software/src/battery/FOXESS-BATTERY.h index 429f2a61..b7ce01e3 100644 --- a/Software/src/battery/FOXESS-BATTERY.h +++ b/Software/src/battery/FOXESS-BATTERY.h @@ -5,7 +5,9 @@ #include "CanBattery.h" +#ifdef FOXESS_BATTERY #define SELECTED_BATTERY_CLASS FoxessBattery +#endif class FoxessBattery : public CanBattery { public: diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h index 16a126fb..75634c5c 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h @@ -6,7 +6,9 @@ #include "CanBattery.h" #include "GEELY-GEOMETRY-C-HTML.h" +#ifdef GEELY_GEOMETRY_C_BATTERY #define SELECTED_BATTERY_CLASS GeelyGeometryCBattery +#endif #define POLL_SOC 0x4B35 #define POLL_CC2_VOLTAGE 0x4BCF @@ -212,7 +214,10 @@ class GeelyGeometryCBattery : public CanBattery { uint16_t poll_unknown7 = 0; uint16_t poll_unknown8 = 0; int16_t poll_temperature[6] = {0}; -#define TEMP_OFFSET 30 //TODO, not calibrated yet, best guess + + //TODO, not calibrated yet, best guess + static const int TEMP_OFFSET = 30; + uint8_t poll_software_version[16] = {0}; uint8_t poll_hardware_version[16] = {0}; }; From 289fa0d16ef80d92979cf000895c6fd1d382efd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 10 Jun 2025 13:07:22 +0300 Subject: [PATCH 05/32] Make PHEV HTML array crash proof --- Software/src/battery/BMW-PHEV-HTML.h | 223 ++++++++++++++------------- 1 file changed, 113 insertions(+), 110 deletions(-) diff --git a/Software/src/battery/BMW-PHEV-HTML.h b/Software/src/battery/BMW-PHEV-HTML.h index 1d03ef06..3b171d41 100644 --- a/Software/src/battery/BMW-PHEV-HTML.h +++ b/Software/src/battery/BMW-PHEV-HTML.h @@ -17,198 +17,201 @@ class BmwPhevHtmlRenderer : public BatteryHtmlRenderer { content += "

Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W

"; content += "

Balancing: "; switch (datalayer_extended.bmwphev.balancing_status) { - case 0: + case 0: content += String("0 Balancing Inactive - Balancing not needed

"); break; - case 1: + case 1: content += String("1 Balancing Active

"); break; - case 2: + case 2: content += String("2 Balancing Inactive - Cells not in rest break wait 10mins"); break; - case 3: + case 3: content += String("3 Balancing Inactive"); break; - case 4: + case 4: content += String("4 Unknown"); break; - default: + default: content += String("Unknown"); } - content += "

Interlock: "; + content += "

Interlock: "; switch (datalayer_extended.bmwphev.ST_interlock) { - case 0: + case 0: content += String("Not Evaluated

"); break; - case 1: + case 1: content += String("OK"); break; - case 2: + case 2: content += String("Error! Not seated!"); break; - case 3: + case 3: content += String("Invalid signal"); break; - default: + default: content += String("Unknown"); } - content += "

Isolation external: "; + content += "

Isolation external: "; switch (datalayer_extended.bmwphev.ST_iso_ext) { - case 0: + case 0: content += String("Not Evaluated

"); break; - case 1: + case 1: content += String("OK"); break; - case 2: + case 2: content += String("Error!"); break; - case 3: + case 3: content += String("Invalid signal"); break; - default: + default: content += String("Unknown"); } content += "

Isolation internal: "; switch (datalayer_extended.bmwphev.ST_iso_int) { - case 0: + case 0: content += String("Not Evaluated

"); break; - case 1: + case 1: content += String("OK"); break; - case 2: + case 2: content += String("Error!"); break; - case 3: + case 3: content += String("Invalid signal"); break; - default: + default: content += String("Unknown"); } content += "

Isolation: "; switch (datalayer_extended.bmwphev.ST_isolation) { - case 0: + case 0: content += String("Not Evaluated

"); break; - case 1: + case 1: content += String("OK"); break; - case 2: + case 2: content += String("Error!"); break; - case 3: + case 3: content += String("Invalid signal"); break; - default: + default: content += String("Unknown"); } content += "

Cooling valve: "; switch (datalayer_extended.bmwphev.ST_valve_cooling) { - case 0: + case 0: content += String("Not Evaluated

"); break; - case 1: + case 1: content += String("OK"); break; - case 2: + case 2: content += String("Error!"); break; - case 3: + case 3: content += String("Invalid signal"); break; - default: + default: content += String("Unknown"); } content += "

Emergency: "; switch (datalayer_extended.bmwphev.ST_EMG) { - case 0: + case 0: content += String("Not Evaluated

"); break; - case 1: + case 1: content += String("OK"); break; - case 2: + case 2: content += String("Error!"); break; - case 3: + case 3: content += String("Invalid signal"); break; - default: + default: content += String("Unknown"); } - - - static const char* prechargeText[16] = {"Not evaluated", - "Not active, closing not blocked", - "Error precharge blocked", - "Invalid signal", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - ""}; - content += "

Precharge: " + String(prechargeText[datalayer_extended.bmwphev.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.bmwphev.ST_DCSW]) + "

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

Contactor weld: " + String(contText[datalayer_extended.bmwphev.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(valveText[datalayer_extended.bmwphev.ST_cold_shutoff_valve]) + "

"; + content += "

Precharge: "; + switch (datalayer_extended.bmwphev.ST_precharge) { + case 0: + content += String("Not Evaluated

"); + break; + case 1: + content += String("Not active, closing not blocked"); + break; + case 2: + content += String("Error precharge blocked"); + break; + case 3: + content += String("Invalid signal"); + break; + default: + content += String("Unknown"); //Still unclear of enum + } + content += "

Contactor status: "; + switch (datalayer_extended.bmwphev.ST_DCSW) { + case 0: + content += String("Contactors open

"); + break; + case 1: + content += String("Precharge ongoing"); + break; + case 2: + content += String("Contactors engaged"); + break; + case 3: + content += String("Invalid signal"); + break; + default: + content += String("Unknown"); + } + content += "

Contactor weld: "; + switch (datalayer_extended.bmwphev.ST_WELD) { + case 0: + content += String("Contactors OK

"); + break; + case 1: + content += String("One contactor welded!"); + break; + case 2: + content += String("Two contactors welded!"); + break; + case 3: + content += String("Invalid signal"); + break; + default: + content += String("Unknown"); + } + content += "

Cold shutoff valve: "; + switch (datalayer_extended.bmwphev.ST_cold_shutoff_valve) { + case 0: + content += String("OK

"); + break; + case 1: + content += String("Short circuit to GND"); + break; + case 2: + content += String("Short circuit to 12V"); + break; + case 3: + content += String("Line break"); + break; + case 6: + content += String("Driver error"); + break; + case 12: + case 13: + content += String("Stuck"); + break; + default: + content += String("Invalid Signal"); + } content += "

Min Cell Voltage Data Age: " + String(datalayer_extended.bmwphev.min_cell_voltage_data_age) + " ms

"; content += From 6dedcf0a405447afa6a9e34226adc3032202a4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 10 Jun 2025 13:16:42 +0300 Subject: [PATCH 06/32] Make iX HTML arrays crash proof --- Software/src/battery/BMW-IX-HTML.h | 103 +++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 12 deletions(-) diff --git a/Software/src/battery/BMW-IX-HTML.h b/Software/src/battery/BMW-IX-HTML.h index 64b88fb9..02be12eb 100644 --- a/Software/src/battery/BMW-IX-HTML.h +++ b/Software/src/battery/BMW-IX-HTML.h @@ -25,13 +25,37 @@ class BmwIXHtmlRenderer : public BatteryHtmlRenderer { content += "

Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W

"; content += "

T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV

"; content += "

Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "

"; - static const char* balanceText[5] = {"0 No balancing mode active", "1 Voltage-Controlled Balancing Mode", - "2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging", - "3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage", - "4 No balancing mode active, qualifier invalid"}; - content += "

Balancing: " + String((balanceText[datalayer_extended.bmwix.balancing_status])) + "

"; - static const char* hvilText[2] = {"Error (Loop Open)", "OK (Loop Closed)"}; - content += "

HVIL Status: " + String(hvilText[datalayer_extended.bmwix.hvil_status]) + "

"; + content += "

Balancing: "; + switch (datalayer_extended.bmwix.balancing_status) { + case 0: + content += String("0 No balancing mode active

"); + break; + case 1: + content += String("1 Voltage-Controlled Balancing Mode"); + break; + case 2: + content += String("2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging"); + break; + case 3: + content += String("3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage"); + break; + case 4: + content += String("4 No balancing mode active, qualifier invalid"); + break; + default: + content += String("Unknown"); + } + content += "

HVIL Status: "; + switch (datalayer_extended.bmwix.hvil_status) { + case 0: + content += String("Error (Loop Open)

"); + break; + case 1: + content += String("OK (Loop Closed)"); + break; + default: + content += String("Unknown"); + } content += "

BMS Uptime: " + String(datalayer_extended.bmwix.bms_uptime) + " seconds

"; content += "

BMS Allowed Charge Amps: " + String(datalayer_extended.bmwix.allowable_charge_amps) + " A

"; content += @@ -41,11 +65,66 @@ class BmwIXHtmlRenderer : public BatteryHtmlRenderer { content += "

Isolation Positive: " + String(datalayer_extended.bmwix.iso_safety_positive) + " kOhm

"; content += "

Isolation Negative: " + String(datalayer_extended.bmwix.iso_safety_negative) + " kOhm

"; content += "

Isolation Parallel: " + String(datalayer_extended.bmwix.iso_safety_parallel) + " kOhm

"; - static const char* pyroText[5] = {"0 Value Invalid", "1 Successfully Blown", "2 Disconnected", - "3 Not Activated - Pyro Intact", "4 Unknown"}; - content += "

Pyro Status PSS1: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss1])) + "

"; - content += "

Pyro Status PSS4: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss4])) + "

"; - content += "

Pyro Status PSS6: " + String((pyroText[datalayer_extended.bmwix.pyro_status_pss6])) + "

"; + content += "

Pyro Status PSS1: "; + switch (datalayer_extended.bmwix.pyro_status_pss1) { + case 0: + content += String("0 Value Invalid

"); + break; + case 1: + content += String("1 Successfully Blown"); + break; + case 2: + content += String("2 Disconnected"); + break; + case 3: + content += String("3 Not Activated - Pyro Intact"); + break; + case 4: + content += String("4 Unknown"); + break; + default: + content += String("Unknown"); + } + content += "

Pyro Status PSS4: "; + switch (datalayer_extended.bmwix.pyro_status_pss4) { + case 0: + content += String("0 Value Invalid

"); + break; + case 1: + content += String("1 Successfully Blown"); + break; + case 2: + content += String("2 Disconnected"); + break; + case 3: + content += String("3 Not Activated - Pyro Intact"); + break; + case 4: + content += String("4 Unknown"); + break; + default: + content += String("Unknown"); + } + content += "

Pyro Status PSS6: "; + switch (datalayer_extended.bmwix.pyro_status_pss6) { + case 0: + content += String("0 Value Invalid

"); + break; + case 1: + content += String("1 Successfully Blown"); + break; + case 2: + content += String("2 Disconnected"); + break; + case 3: + content += String("3 Not Activated - Pyro Intact"); + break; + case 4: + content += String("4 Unknown"); + break; + default: + content += String("Unknown"); + } return content; } From ae460f98262775bb264b6c76f7174b09f5d64da6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 10:20:06 +0000 Subject: [PATCH 07/32] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Software/src/battery/NISSAN-LEAF-HTML.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/NISSAN-LEAF-HTML.h b/Software/src/battery/NISSAN-LEAF-HTML.h index 275bef7b..4d21d4dd 100644 --- a/Software/src/battery/NISSAN-LEAF-HTML.h +++ b/Software/src/battery/NISSAN-LEAF-HTML.h @@ -10,7 +10,7 @@ class NissanLeafHtmlRenderer : public BatteryHtmlRenderer { String get_status_html() { String content; - content += "

LEAF generation: "; + content += "

LEAF generation: "; switch (datalayer_extended.nissanleaf.LEAF_gen) { case 0: content += String("ZE0

"); From bd95cbd7cc97dfc965f6f7d777480c59a1655f1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 12 Jun 2025 23:26:43 +0300 Subject: [PATCH 08/32] Simplify GPIO for secondary battery --- .../comm_contactorcontrol.cpp | 20 ++++--------------- Software/src/devboard/hal/hw_3LB.h | 6 +----- Software/src/devboard/hal/hw_devkit.h | 2 +- Software/src/devboard/hal/hw_lilygo.h | 3 ++- Software/src/devboard/hal/hw_stark.h | 1 + Software/src/devboard/webserver/webserver.cpp | 13 +++--------- 6 files changed, 12 insertions(+), 33 deletions(-) diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index b509a223..5180f884 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -82,27 +82,17 @@ void init_contactors() { set(PRECHARGE_PIN, OFF); #endif // CONTACTOR_CONTROL #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY - pinMode(SECOND_POSITIVE_CONTACTOR_PIN, OUTPUT); - set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); - pinMode(SECOND_NEGATIVE_CONTACTOR_PIN, OUTPUT); - set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF); + pinMode(SECOND_BATTERY_CONTACTORS_PIN, OUTPUT); + set(SECOND_BATTERY_CONTACTORS_PIN, OFF); #endif // CONTACTOR_CONTROL_DOUBLE_BATTERY // Init BMS contactor #if defined HW_STARK || defined HW_3LB // This hardware has dedicated pin, always enable on start pinMode(BMS_POWER, OUTPUT); //LilyGo is omitted from this, only enabled if user selects PERIODIC_BMS_RESET digitalWrite(BMS_POWER, HIGH); -#ifdef BMS_2_POWER //Hardware supports 2x BMS - pinMode(BMS_2_POWER, OUTPUT); - digitalWrite(BMS_2_POWER, HIGH); -#endif //BMS_2_POWER #endif // HW with dedicated BMS pins #if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET) // User has enabled BMS reset, turn on output on start pinMode(BMS_POWER, OUTPUT); digitalWrite(BMS_POWER, HIGH); -#ifdef BMS_2_POWER //Hardware supports 2x BMS - pinMode(BMS_2_POWER, OUTPUT); - digitalWrite(BMS_2_POWER, HIGH); -#endif //BMS_2_POWER #endif //PERIODIC_BMS_RESET } @@ -226,12 +216,10 @@ void handle_contactors() { #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY void handle_contactors_battery2() { if ((contactorStatus == COMPLETED) && datalayer.system.status.battery2_allowed_contactor_closing) { - set(SECOND_NEGATIVE_CONTACTOR_PIN, ON); - set(SECOND_POSITIVE_CONTACTOR_PIN, ON); + set(SECOND_BATTERY_CONTACTORS_PIN, ON); datalayer.system.status.contactors_battery2_engaged = true; } else { // Closing contactors on secondary battery not allowed - set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF); - set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); + set(SECOND_BATTERY_CONTACTORS_PIN, OFF); datalayer.system.status.contactors_battery2_engaged = false; } } diff --git a/Software/src/devboard/hal/hw_3LB.h b/Software/src/devboard/hal/hw_3LB.h index 7d070726..8a808b9e 100644 --- a/Software/src/devboard/hal/hw_3LB.h +++ b/Software/src/devboard/hal/hw_3LB.h @@ -52,11 +52,7 @@ #define NEGATIVE_CONTACTOR_PIN 33 #define PRECHARGE_PIN 25 #define BMS_POWER 2 - -#define SECOND_POSITIVE_CONTACTOR_PIN 13 -#define SECOND_NEGATIVE_CONTACTOR_PIN 16 -#define SECOND_PRECHARGE_PIN 18 -#define BMS_2_POWER 12 +#define SECOND_BATTERY_CONTACTORS_PIN 13 // SMA CAN contactor pins #define INVERTER_CONTACTOR_ENABLE_PIN 36 diff --git a/Software/src/devboard/hal/hw_devkit.h b/Software/src/devboard/hal/hw_devkit.h index f028bbbc..107780ae 100644 --- a/Software/src/devboard/hal/hw_devkit.h +++ b/Software/src/devboard/hal/hw_devkit.h @@ -50,7 +50,7 @@ The pin layout below supports the following: #define POSITIVE_CONTACTOR_PIN GPIO_NUM_5 #define NEGATIVE_CONTACTOR_PIN GPIO_NUM_16 #define PRECHARGE_PIN GPIO_NUM_17 - +#define SECOND_BATTERY_CONTACTORS_PIN GPIO_NUM_32 // SMA CAN contactor pins #define INVERTER_CONTACTOR_ENABLE_PIN GPIO_NUM_14 diff --git a/Software/src/devboard/hal/hw_lilygo.h b/Software/src/devboard/hal/hw_lilygo.h index f560019d..6b2463e1 100644 --- a/Software/src/devboard/hal/hw_lilygo.h +++ b/Software/src/devboard/hal/hw_lilygo.h @@ -51,7 +51,8 @@ #define POSITIVE_CONTACTOR_PIN 32 #define NEGATIVE_CONTACTOR_PIN 33 #define PRECHARGE_PIN 25 -#define BMS_POWER 18 // Note, this pin collides with CAN add-ons and Chademo +#define BMS_POWER 18 // Note, this pin collides with CAN add-ons and Chademo +#define SECOND_BATTERY_CONTACTORS_PIN 15 //Note, this pin collides with SD card pins // Automatic precharging #define HIA4V1_PIN 25 diff --git a/Software/src/devboard/hal/hw_stark.h b/Software/src/devboard/hal/hw_stark.h index 9d098b62..4c91dbe0 100644 --- a/Software/src/devboard/hal/hw_stark.h +++ b/Software/src/devboard/hal/hw_stark.h @@ -50,6 +50,7 @@ GPIOs on extra header #define NEGATIVE_CONTACTOR_PIN 33 #define PRECHARGE_PIN 25 #define BMS_POWER 23 +#define SECOND_BATTERY_CONTACTORS_PIN 19 //Available as extra GPIO via pin header // Automatic precharging #define HIA4V1_PIN 19 //Available as extra GPIO via pin header diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 6c55a316..49f2ae54 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1338,8 +1338,8 @@ String processor(const String& var) { } content += ""; #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY - content += "

Cont. Neg.: "; #ifdef PWM_CONTACTOR_CONTROL + content += "

Cont. Neg.: "; if (datalayer.system.status.contactors_battery2_engaged) { content += "Economized"; content += " Cont. Pos.: "; @@ -1349,16 +1349,9 @@ String processor(const String& var) { content += " Cont. Pos.: "; content += ""; } - #else // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded - if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) { - content += ""; - } else { - content += ""; - } - - content += " Cont. Pos.: "; - if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) { + content += "

Cont. Neg.: "; + if (digitalRead(SECOND_BATTERY_CONTACTORS_PIN) == HIGH) { content += ""; } else { content += ""; From c4503551030a3d97d599e27c2e0bf980dea18803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 12 Jun 2025 23:49:25 +0300 Subject: [PATCH 09/32] Add support for Double CMFA-EV batteries --- Software/src/battery/CMFA-EV-BATTERY.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Software/src/battery/CMFA-EV-BATTERY.h b/Software/src/battery/CMFA-EV-BATTERY.h index 0455838e..c5aff585 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.h +++ b/Software/src/battery/CMFA-EV-BATTERY.h @@ -10,6 +10,23 @@ class CmfaEvBattery : public CanBattery { 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 handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); @@ -20,6 +37,12 @@ class CmfaEvBattery : public CanBattery { private: 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); static const int MAX_PACK_VOLTAGE_DV = 3040; // 5000 = 500.0V From 8b767a10c51bd62d242d54f7596912c283dc6462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Fri, 13 Jun 2025 13:59:44 +0300 Subject: [PATCH 10/32] Update datalayer writing --- Software/src/battery/CMFA-EV-BATTERY.cpp | 94 ++++++++++++------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/Software/src/battery/CMFA-EV-BATTERY.cpp b/Software/src/battery/CMFA-EV-BATTERY.cpp index e9eddf64..9e2e940c 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.cpp +++ b/Software/src/battery/CMFA-EV-BATTERY.cpp @@ -28,68 +28,68 @@ uint16_t CmfaEvBattery::rescale_raw_SOC(uint32_t raw_SOC) { void CmfaEvBattery:: 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. - 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 = 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 - 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 set_event(EVENT_12V_LOW, lead_acid_voltage); } // Update webserver datalayer - datalayer_extended.CMFAEV.soc_u = soc_u; - datalayer_extended.CMFAEV.soc_z = soc_z; - datalayer_extended.CMFAEV.lead_acid_voltage = lead_acid_voltage; - datalayer_extended.CMFAEV.highest_cell_voltage_number = highest_cell_voltage_number; - datalayer_extended.CMFAEV.lowest_cell_voltage_number = lowest_cell_voltage_number; - datalayer_extended.CMFAEV.max_regen_power = max_regen_power; - datalayer_extended.CMFAEV.max_discharge_power = max_discharge_power; - datalayer_extended.CMFAEV.average_temperature = average_temperature; - datalayer_extended.CMFAEV.minimum_temperature = minimum_temperature; - datalayer_extended.CMFAEV.maximum_temperature = maximum_temperature; - datalayer_extended.CMFAEV.maximum_charge_power = maximum_charge_power; - datalayer_extended.CMFAEV.SOH_available_power = SOH_available_power; - datalayer_extended.CMFAEV.SOH_generated_power = SOH_generated_power; - datalayer_extended.CMFAEV.cumulative_energy_when_discharging = cumulative_energy_when_discharging; - datalayer_extended.CMFAEV.cumulative_energy_when_charging = cumulative_energy_when_charging; - datalayer_extended.CMFAEV.cumulative_energy_in_regen = cumulative_energy_in_regen; - datalayer_extended.CMFAEV.soh_average = soh_average; + datalayer_cmfa->soc_u = soc_u; + datalayer_cmfa->soc_z = soc_z; + datalayer_cmfa->lead_acid_voltage = lead_acid_voltage; + datalayer_cmfa->highest_cell_voltage_number = highest_cell_voltage_number; + datalayer_cmfa->lowest_cell_voltage_number = lowest_cell_voltage_number; + datalayer_cmfa->max_regen_power = max_regen_power; + datalayer_cmfa->max_discharge_power = max_discharge_power; + datalayer_cmfa->average_temperature = average_temperature; + datalayer_cmfa->minimum_temperature = minimum_temperature; + datalayer_cmfa->maximum_temperature = maximum_temperature; + datalayer_cmfa->maximum_charge_power = maximum_charge_power; + datalayer_cmfa->SOH_available_power = SOH_available_power; + datalayer_cmfa->SOH_generated_power = SOH_generated_power; + datalayer_cmfa->cumulative_energy_when_discharging = cumulative_energy_when_discharging; + datalayer_cmfa->cumulative_energy_when_charging = cumulative_energy_when_charging; + datalayer_cmfa->cumulative_energy_in_regen = cumulative_energy_in_regen; + datalayer_cmfa->soh_average = soh_average; } void CmfaEvBattery::handle_incoming_can_frame(CAN_frame rx_frame) { switch (rx_frame.ID) { //These frames are transmitted by the battery 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); SOC_raw = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]); break; 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; discharge_power_w = rx_frame.data.u8[3] * 500; lowest_cell_temperature = (rx_frame.data.u8[4] - 40); @@ -98,34 +98,34 @@ void CmfaEvBattery::handle_incoming_can_frame(CAN_frame rx_frame) { highest_cell_temperature = (rx_frame.data.u8[7] - 40); break; 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))); break; 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]; //? //CRC_3D8 = rx_frame.data.u8[4]; //? break; 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 break; 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 //byte1 40 always break; case 0x5A9: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; case 0x5AB: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; case 0x5C8: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; case 0x5E1: - datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE; + datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE; break; case 0x7BB: // Reply from battery if (rx_frame.data.u8[0] == 0x10) { //PID header @@ -944,12 +944,12 @@ void CmfaEvBattery::setup(void) { // Performs one time setup at startup strncpy(datalayer.system.info.battery_protocol, "CMFA platform, 27 kWh battery", 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.status.battery_allows_contactor_closing = true; - datalayer.battery.info.number_of_cells = 72; - 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 = 72; + 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; } #endif //CMFA_EV_BATTERY From 95957a503c8c16bd2c031634d7ba53b3502d980f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 15 Jun 2025 12:04:13 +0300 Subject: [PATCH 11/32] Remove unecessary string calls --- Software/src/battery/BMW-IX-HTML.h | 54 +++++++------- Software/src/battery/MEB-HTML.h | 116 ++++++++++++++--------------- 2 files changed, 85 insertions(+), 85 deletions(-) diff --git a/Software/src/battery/BMW-IX-HTML.h b/Software/src/battery/BMW-IX-HTML.h index 02be12eb..27bd3d56 100644 --- a/Software/src/battery/BMW-IX-HTML.h +++ b/Software/src/battery/BMW-IX-HTML.h @@ -28,33 +28,33 @@ class BmwIXHtmlRenderer : public BatteryHtmlRenderer { content += "

Balancing: "; switch (datalayer_extended.bmwix.balancing_status) { case 0: - content += String("0 No balancing mode active

"); + content += "0 No balancing mode active

"; break; case 1: - content += String("1 Voltage-Controlled Balancing Mode"); + content += "1 Voltage-Controlled Balancing Mode"; break; case 2: - content += String("2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging"); + content += "2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging"; break; case 3: - content += String("3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage"); + content += "3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage"; break; case 4: - content += String("4 No balancing mode active, qualifier invalid"); + content += "4 No balancing mode active, qualifier invalid"; break; default: - content += String("Unknown"); + content += "Unknown"; } content += "

HVIL Status: "; switch (datalayer_extended.bmwix.hvil_status) { case 0: - content += String("Error (Loop Open)

"); + content += "Error (Loop Open)"; break; case 1: - content += String("OK (Loop Closed)"); + content += "OK (Loop Closed)"; break; default: - content += String("Unknown"); + content += "Unknown"; } content += "

BMS Uptime: " + String(datalayer_extended.bmwix.bms_uptime) + " seconds

"; content += "

BMS Allowed Charge Amps: " + String(datalayer_extended.bmwix.allowable_charge_amps) + " A

"; @@ -68,62 +68,62 @@ class BmwIXHtmlRenderer : public BatteryHtmlRenderer { content += "

Pyro Status PSS1: "; switch (datalayer_extended.bmwix.pyro_status_pss1) { case 0: - content += String("0 Value Invalid

"); + content += "0 Value Invalid"; break; case 1: - content += String("1 Successfully Blown"); + content += "1 Successfully Blown"; break; case 2: - content += String("2 Disconnected"); + content += "2 Disconnected"; break; case 3: - content += String("3 Not Activated - Pyro Intact"); + content += "3 Not Activated - Pyro Intact"; break; case 4: - content += String("4 Unknown"); + content += "4 Unknown"; break; default: - content += String("Unknown"); + content += "Unknown"; } content += "

Pyro Status PSS4: "; switch (datalayer_extended.bmwix.pyro_status_pss4) { case 0: - content += String("0 Value Invalid

"); + content += "0 Value Invalid"; break; case 1: - content += String("1 Successfully Blown"); + content += "1 Successfully Blown"; break; case 2: - content += String("2 Disconnected"); + content += "2 Disconnected"; break; case 3: - content += String("3 Not Activated - Pyro Intact"); + content += "3 Not Activated - Pyro Intact"; break; case 4: - content += String("4 Unknown"); + content += "4 Unknown"; break; default: - content += String("Unknown"); + content += "Unknown"; } content += "

Pyro Status PSS6: "; switch (datalayer_extended.bmwix.pyro_status_pss6) { case 0: - content += String("0 Value Invalid

"); + content += "0 Value Invalid"; break; case 1: - content += String("1 Successfully Blown"); + content += "1 Successfully Blown"; break; case 2: - content += String("2 Disconnected"); + content += "2 Disconnected"; break; case 3: - content += String("3 Not Activated - Pyro Intact"); + content += "3 Not Activated - Pyro Intact"; break; case 4: - content += String("4 Unknown"); + content += "4 Unknown"; break; default: - content += String("Unknown"); + content += "Unknown"; } return content; diff --git a/Software/src/battery/MEB-HTML.h b/Software/src/battery/MEB-HTML.h index 401236fd..20363276 100644 --- a/Software/src/battery/MEB-HTML.h +++ b/Software/src/battery/MEB-HTML.h @@ -20,119 +20,119 @@ class MebHtmlRenderer : public BatteryHtmlRenderer { content += "

HVIL status: "; switch (datalayer_extended.meb.HVIL) { case 0: - content += String("Init"); + content += "Init"; break; case 1: - content += String("Closed"); + content += "Closed"; break; case 2: - content += String("Open!"); + content += "Open!"; break; case 3: - content += String("Fault"); + content += "Fault"; break; default: - content += String("?"); + content += "?"; } content += "

KL30C status: "; switch (datalayer_extended.meb.BMS_Kl30c_Status) { case 0: - content += String("Init"); + content += "Init"; break; case 1: - content += String("Closed"); + content += "Closed"; break; case 2: - content += String("Open!"); + content += "Open!"; break; case 3: - content += String("Fault"); + content += "Fault"; break; default: - content += String("?"); + content += "?"; } content += "

BMS mode: "; switch (datalayer_extended.meb.BMS_mode) { case 0: - content += String("HV inactive"); + content += "HV inactive"; break; case 1: - content += String("HV active"); + content += "HV active"; break; case 2: - content += String("Balancing"); + content += "Balancing"; break; case 3: - content += String("Extern charging"); + content += "Extern charging"; break; case 4: - content += String("AC charging"); + content += "AC charging"; break; case 5: - content += String("Battery error"); + content += "Battery error"; break; case 6: - content += String("DC charging"); + content += "DC charging"; break; case 7: - content += String("Init"); + content += "Init"; break; default: - content += String("?"); + content += "?"; } content += String("

Charging: ") + (datalayer_extended.meb.charging_active ? "active" : "not active"); content += String("

Balancing: "); switch (datalayer_extended.meb.balancing_active) { case 0: - content += String("init"); + content += "init"; break; case 1: - content += String("active"); + content += "active"; break; case 2: - content += String("inactive"); + content += "inactive"; break; default: - content += String("?"); + content += "?"; } content += String("

Slow charging: ") + (datalayer_extended.meb.balancing_request ? "requested" : "not requested"); content += "

Diagnostic: "; switch (datalayer_extended.meb.battery_diagnostic) { case 0: - content += String("Init"); + content += "Init"; break; case 1: - content += String("Battery display"); + content += "Battery display"; break; case 4: - content += String("Battery display OK"); + content += "Battery display OK"; break; case 6: - content += String("Battery display check"); + content += "Battery display check"; break; case 7: - content += String("Fault"); + content += "Fault"; break; default: - content += String("?"); + content += "?"; } content += "

HV line status: "; switch (datalayer_extended.meb.status_HV_line) { case 0: - content += String("Init"); + content += "Init"; break; case 1: - content += String("No open HV line detected"); + content += "No open HV line detected"; break; case 2: - content += String("Open HV line"); + content += "Open HV line"; break; case 3: - content += String("Fault"); + content += "Fault"; break; default: - content += String("? ") + String(datalayer_extended.meb.status_HV_line); + content += "? " + String(datalayer_extended.meb.status_HV_line); } content += "

"; content += datalayer_extended.meb.BMS_fault_performance ? "

BMS fault performance: Active!

" @@ -147,83 +147,83 @@ class MebHtmlRenderer : public BatteryHtmlRenderer { content += "

Welded contactors: "; switch (datalayer_extended.meb.BMS_welded_contactors_status) { case 0: - content += String("Init"); + content += "Init"; break; case 1: - content += String("No contactor welded"); + content += "No contactor welded"; break; case 2: - content += String("At least 1 contactor welded"); + content += "At least 1 contactor welded"; break; case 3: - content += String("Protection status detection error"); + content += "Protection status detection error"; break; default: - content += String("?"); + content += "?"; } content += "

Warning support: "; switch (datalayer_extended.meb.warning_support) { case 0: - content += String("OK"); + content += "OK"; break; case 1: - content += String("Not OK"); + content += "Not OK"; break; case 6: - content += String("Init"); + content += "Init"; break; case 7: - content += String("Fault"); + content += "Fault"; break; default: - content += String("?"); + content += "?"; } content += "

Interm. Voltage (" + String(datalayer_extended.meb.BMS_voltage_intermediate_dV / 10.0, 1) + "V) status: "; switch (datalayer_extended.meb.BMS_status_voltage_free) { case 0: - content += String("Init"); + content += "Init"; break; case 1: - content += String("BMS interm circuit voltage free (U<20V)"); + content += "BMS interm circuit voltage free (U<20V)"; break; case 2: - content += String("BMS interm circuit not voltage free (U >= 25V)"); + content += "BMS interm circuit not voltage free (U >= 25V)"; break; case 3: - content += String("Error"); + content += "Error"; break; default: - content += String("?"); + content += "?"; } content += "

BMS error status: "; switch (datalayer_extended.meb.BMS_error_status) { case 0: - content += String("Component IO"); + content += "Component IO"; break; case 1: - content += String("Iso Error 1"); + content += "Iso Error 1"; break; case 2: - content += String("Iso Error 2"); + content += "Iso Error 2"; break; case 3: - content += String("Interlock"); + content += "Interlock"; break; case 4: - content += String("SD"); + content += "SD"; break; case 5: - content += String("Performance red"); + content += "Performance red"; break; case 6: - content += String("No component function"); + content += "No component function"; break; case 7: - content += String("Init"); + content += "Init"; break; default: - content += String("?"); + content += "?"; } content += "

BMS voltage: " + String(datalayer_extended.meb.BMS_voltage_dV / 10.0, 1) + "

"; content += datalayer_extended.meb.BMS_OBD_MIL ? "

OBD MIL: ON!

" : "

OBD MIL: Off

"; From 0884acc72403a76e28a3b91ea103adb71159cb70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Sun, 15 Jun 2025 19:50:04 +0300 Subject: [PATCH 12/32] Improve startup to throw less events --- Software/src/battery/CHADEMO-BATTERY.cpp | 4 +++- Software/src/battery/CHADEMO-BATTERY.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index ff08d86e..a4ba76c8 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -21,7 +21,9 @@ void ChademoBattery::update_values() { datalayer.battery.status.max_discharge_power_W = (x200_discharge_limits.MaximumDischargeCurrent * x100_chg_lim.MaximumBatteryVoltage); //In Watts, Convert A to P - datalayer.battery.status.voltage_dV = get_measured_voltage() * 10; + 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.info.total_capacity_Wh = (x101_chg_est.RatedBatteryCapacity * 100); //(Added in CHAdeMO v1.0.1), maybe handle hardcoded on lower protocol version? diff --git a/Software/src/battery/CHADEMO-BATTERY.h b/Software/src/battery/CHADEMO-BATTERY.h index 7e50841f..29f1b3c6 100644 --- a/Software/src/battery/CHADEMO-BATTERY.h +++ b/Software/src/battery/CHADEMO-BATTERY.h @@ -127,7 +127,7 @@ uint8_t CHADEMO_seq = 0x0; } status; } s; - uint8_t StateOfCharge = 0; //6 state of charge? + uint8_t StateOfCharge = 50; //6 state of charge? }; /* ---------- CHARGING: EVSE Data structures */ From 02917dcbea0a66c6f804e897338aff47c049414f Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Tue, 17 Jun 2025 23:19:26 +0300 Subject: [PATCH 13/32] More batteries to common image --- Software/src/battery/BATTERIES.cpp | 181 +++-- Software/src/battery/BMW-SBOX.cpp | 2 +- Software/src/battery/BMW-SBOX.h | 1 + Software/src/battery/Battery.cpp | 6 + Software/src/battery/Battery.h | 76 +- Software/src/battery/CHADEMO-SHUNTS.h | 3 + Software/src/battery/CMFA-EV-BATTERY.cpp | 2 +- Software/src/battery/CMFA-EV-BATTERY.h | 1 + Software/src/battery/CanBattery.cpp | 8 + Software/src/battery/CanBattery.h | 6 +- Software/src/battery/DALY-BMS.cpp | 2 +- Software/src/battery/DALY-BMS.h | 1 + Software/src/battery/ECMP-BATTERY.cpp | 2 +- Software/src/battery/ECMP-BATTERY.h | 147 ++-- Software/src/battery/FOXESS-BATTERY.cpp | 2 +- Software/src/battery/FOXESS-BATTERY.h | 1 + .../src/battery/GEELY-GEOMETRY-C-BATTERY.cpp | 2 +- .../src/battery/GEELY-GEOMETRY-C-BATTERY.h | 1 + .../src/battery/IMIEV-CZERO-ION-BATTERY.cpp | 2 +- .../src/battery/IMIEV-CZERO-ION-BATTERY.h | 1 + Software/src/battery/JAGUAR-IPACE-BATTERY.h | 1 + Software/src/battery/KIA-E-GMP-BATTERY.cpp | 2 +- Software/src/battery/KIA-E-GMP-BATTERY.h | 1 + .../src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 2 +- Software/src/battery/KIA-HYUNDAI-64-BATTERY.h | 1 + .../battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp | 2 +- .../src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h | 1 + Software/src/battery/MEB-BATTERY.cpp | 2 +- Software/src/battery/MEB-BATTERY.h | 1 + Software/src/battery/MG-5-BATTERY.h | 1 + Software/src/battery/ORION-BMS.cpp | 2 +- Software/src/battery/ORION-BMS.h | 1 + Software/src/battery/PYLON-BATTERY.h | 1 + .../src/battery/RANGE-ROVER-PHEV-BATTERY.cpp | 2 +- .../src/battery/RANGE-ROVER-PHEV-BATTERY.h | 1 + .../src/battery/RENAULT-KANGOO-BATTERY.cpp | 2 +- Software/src/battery/RENAULT-KANGOO-BATTERY.h | 1 + Software/src/battery/RENAULT-TWIZY.cpp | 2 +- Software/src/battery/RENAULT-TWIZY.h | 1 + .../src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp | 2 +- .../src/battery/RENAULT-ZOE-GEN1-BATTERY.h | 1 + .../src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp | 2 +- .../src/battery/RENAULT-ZOE-GEN2-BATTERY.h | 1 + Software/src/battery/RJXZS-BMS.cpp | 2 +- Software/src/battery/RJXZS-BMS.h | 1 + .../src/battery/SANTA-FE-PHEV-BATTERY.cpp | 2 +- Software/src/battery/SANTA-FE-PHEV-BATTERY.h | 1 + Software/src/battery/SIMPBMS-BATTERY.cpp | 2 +- Software/src/battery/SIMPBMS-BATTERY.h | 1 + Software/src/battery/SONO-BATTERY.cpp | 2 +- Software/src/battery/SONO-BATTERY.h | 1 + Software/src/battery/VOLVO-SPA-BATTERY.cpp | 2 +- Software/src/battery/VOLVO-SPA-BATTERY.h | 1 + .../src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp | 4 +- .../src/battery/VOLVO-SPA-HYBRID-BATTERY.h | 1 + Software/src/communication/can/CanReceiver.h | 1 - .../comm_contactorcontrol.cpp | 22 +- .../contactorcontrol/comm_contactorcontrol.h | 1 + Software/src/communication/nvm/comm_nvm.cpp | 1 + Software/src/communication/nvm/comm_nvm.h | 18 +- Software/src/devboard/safety/safety.cpp | 279 +++---- Software/src/devboard/utils/types.h | 3 + .../src/devboard/webserver/settings_html.cpp | 23 +- Software/src/devboard/webserver/webserver.cpp | 759 +++++++++--------- 64 files changed, 885 insertions(+), 722 deletions(-) create mode 100644 Software/src/battery/Battery.cpp create mode 100644 Software/src/battery/CanBattery.cpp diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index f001dd4a..89d5f89c 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -19,6 +19,8 @@ std::vector supported_battery_types() { extern const char* name_for_type(BatteryType type) { switch (type) { + case BatteryType::None: + return "None"; case BatteryType::BmwI3: return BmwI3Battery::Name; case BatteryType::BmwIx: @@ -31,19 +33,67 @@ extern const char* name_for_type(BatteryType type) { return CellPowerBms::Name; case BatteryType::Chademo: return ChademoBattery::Name; + case BatteryType::CmfaEv: + return CmfaEvBattery::Name; + case BatteryType::Foxess: + return FoxessBattery::Name; + case BatteryType::GeelyGeometryC: + return GeelyGeometryCBattery::Name; + case BatteryType::OrionBms: + return OrionBms::Name; + case BatteryType::Sono: + return SonoBattery::Name; + case BatteryType::StellantisEcmp: + return EcmpBattery::Name; + case BatteryType::ImievCZeroIon: + return ImievCZeroIonBattery::Name; + case BatteryType::JaguarIpace: + return JaguarIpaceBattery::Name; + case BatteryType::KiaEGmp: + return KiaEGmpBattery::Name; + case BatteryType::KiaHyundai64: + return KiaHyundai64Battery::Name; + case BatteryType::KiaHyundaiHybrid: + return KiaHyundaiHybridBattery::Name; + case BatteryType::Meb: + return MebBattery::Name; + case BatteryType::Mg5: + return Mg5Battery::Name; case BatteryType::NissanLeaf: return NissanLeafBattery::Name; - case BatteryType::TestFake: - return TestFakeBattery::Name; + case BatteryType::Pylon: + return PylonBattery::Name; + case BatteryType::DalyBms: + return DalyBms::Name; + case BatteryType::RjxzsBms: + return RjxzsBms::Name; + case BatteryType::RangeRoverPhev: + return RangeRoverPhevBattery::Name; + case BatteryType::RenaultKangoo: + return RenaultKangooBattery::Name; + case BatteryType::RenaultTwizy: + return RenaultTwizyBattery::Name; + case BatteryType::RenaultZoe1: + return RenaultZoeGen1Battery::Name; + case BatteryType::RenaultZoe2: + return RenaultZoeGen2Battery::Name; + case BatteryType::SantaFePhev: + return SantaFePhevBattery::Name; + case BatteryType::SimpBms: + return SimpBmsBattery::Name; case BatteryType::TeslaModel3Y: return TeslaModel3YBattery::Name; case BatteryType::TeslaModelSX: return TeslaModelSXBattery::Name; - case BatteryType::None: - return "None"; + case BatteryType::TestFake: + return TestFakeBattery::Name; + case BatteryType::VolvoSpa: + return VolvoSpaBattery::Name; + case BatteryType::VolvoSpaHybrid: + return VolvoSpaHybridBattery::Name; + default: + return nullptr; } - - return nullptr; } #ifdef COMMON_IMAGE @@ -54,51 +104,92 @@ extern const char* name_for_type(BatteryType type) { BatteryType user_selected_battery_type = BatteryType::NissanLeaf; bool user_selected_second_battery = false; -void setup_battery() { +Battery* create_battery(BatteryType type) { + switch (type) { + case BatteryType::None: + return nullptr; + case BatteryType::BmwI3: + return new BmwI3Battery(); + case BatteryType::BmwIx: + return new BmwIXBattery(); + case BatteryType::BoltAmpera: + return new BoltAmperaBattery(); + case BatteryType::BydAtto3: + return new BydAttoBattery(); + case BatteryType::CellPowerBms: + return new CellPowerBms(); + case BatteryType::Chademo: + return new ChademoBattery(); + case BatteryType::CmfaEv: + return new CmfaEvBattery(); + case BatteryType::Foxess: + return new FoxessBattery(); + case BatteryType::GeelyGeometryC: + return new GeelyGeometryCBattery(); + case BatteryType::OrionBms: + return new OrionBms(); + case BatteryType::Sono: + return new SonoBattery(); + case BatteryType::StellantisEcmp: + return new EcmpBattery(); + case BatteryType::ImievCZeroIon: + return new ImievCZeroIonBattery(); + case BatteryType::JaguarIpace: + return new JaguarIpaceBattery(); + case BatteryType::KiaEGmp: + return new KiaEGmpBattery(); + case BatteryType::KiaHyundai64: + return new KiaHyundai64Battery(); + case BatteryType::KiaHyundaiHybrid: + return new KiaHyundaiHybridBattery(); + case BatteryType::Meb: + return new MebBattery(); + case BatteryType::Mg5: + return new Mg5Battery(); + case BatteryType::NissanLeaf: + return new NissanLeafBattery(); + case BatteryType::Pylon: + return new PylonBattery(); + case BatteryType::DalyBms: + return new DalyBms(); + case BatteryType::RjxzsBms: + return new RjxzsBms(); + case BatteryType::RangeRoverPhev: + return new RangeRoverPhevBattery(); + case BatteryType::RenaultKangoo: + return new RenaultKangooBattery(); + case BatteryType::RenaultTwizy: + return new RenaultTwizyBattery(); + case BatteryType::RenaultZoe1: + return new RenaultZoeGen1Battery(); + case BatteryType::RenaultZoe2: + return new RenaultZoeGen2Battery(); + case BatteryType::SantaFePhev: + return new SantaFePhevBattery(); + case BatteryType::SimpBms: + return new SimpBmsBattery(); + case BatteryType::TeslaModel3Y: + return new TeslaModel3YBattery(); + case BatteryType::TeslaModelSX: + return new TeslaModelSXBattery(); + case BatteryType::TestFake: + return new TestFakeBattery(); + case BatteryType::VolvoSpa: + return new VolvoSpaBattery(); + case BatteryType::VolvoSpaHybrid: + return new VolvoSpaHybridBattery(); + default: + return nullptr; + } +} +void setup_battery() { if (battery) { // Let's not create the battery again. return; } - switch (user_selected_battery_type) { - case BatteryType::BmwI3: - battery = new BmwI3Battery(); - break; - case BatteryType::BmwIx: - battery = new BmwIXBattery(); - break; - case BatteryType::BoltAmpera: - battery = new BoltAmperaBattery(); - break; - case BatteryType::BydAtto3: - battery = new BydAttoBattery(); - break; - case BatteryType::CellPowerBms: - battery = new CellPowerBms(); - break; - case BatteryType::Chademo: - battery = new ChademoBattery(); - break; - case BatteryType::CmfaEv: - battery = new CmfaEvBattery(); - break; - case BatteryType::DalyBms: - battery = new DalyBms(); - break; - case BatteryType::NissanLeaf: - battery = new NissanLeafBattery(); - break; - case BatteryType::TeslaModel3Y: - battery = new TeslaModel3YBattery(); - break; - case BatteryType::TeslaModelSX: - battery = new TeslaModelSXBattery(); - break; - case BatteryType::TestFake: - battery = new TestFakeBattery(); - break; - } + battery = create_battery(user_selected_battery_type); if (battery) { battery->setup(); diff --git a/Software/src/battery/BMW-SBOX.cpp b/Software/src/battery/BMW-SBOX.cpp index 506aa310..69760107 100644 --- a/Software/src/battery/BMW-SBOX.cpp +++ b/Software/src/battery/BMW-SBOX.cpp @@ -178,6 +178,6 @@ void BmwSbox::transmit_can(unsigned long currentMillis) { } void BmwSbox::setup() { - strncpy(datalayer.system.info.shunt_protocol, "BMW SBOX", 63); + strncpy(datalayer.system.info.shunt_protocol, Name, 63); datalayer.system.info.shunt_protocol[63] = '\0'; } diff --git a/Software/src/battery/BMW-SBOX.h b/Software/src/battery/BMW-SBOX.h index 1909c48b..ea2bd72c 100644 --- a/Software/src/battery/BMW-SBOX.h +++ b/Software/src/battery/BMW-SBOX.h @@ -13,6 +13,7 @@ class BmwSbox : public CanShunt { void setup(); void transmit_can(unsigned long currentMillis); void handle_incoming_can_frame(CAN_frame rx_frame); + static constexpr char* Name = "BMW SBOX"; private: /** Minimum input voltage required to enable relay control **/ diff --git a/Software/src/battery/Battery.cpp b/Software/src/battery/Battery.cpp new file mode 100644 index 00000000..02280f52 --- /dev/null +++ b/Software/src/battery/Battery.cpp @@ -0,0 +1,6 @@ +#include "Battery.h" +#include "../datalayer/datalayer.h" + +float Battery::get_voltage() { + return static_cast(datalayer.battery.status.voltage_dV) / 10.0; +} diff --git a/Software/src/battery/Battery.h b/Software/src/battery/Battery.h index a323f20a..61a798d6 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -1,47 +1,47 @@ #ifndef BATTERY_H #define BATTERY_H -#include "../datalayer/datalayer.h" +#include #include "src/devboard/webserver/BatteryHtmlRenderer.h" enum class BatteryType { None = 0, - BmwSbox, - BmwI3, - BmwIx, - BoltAmpera, - BydAtto3, - CellPowerBms, - Chademo, - CmfaEv, - Foxess, - GeelyGeometryC, - OrionBms, - Sono, - StellantisEcmp, - ImievCZeroIon, - JaguarIpace, - KiaEGmp, - KiaHyundai64, - KiaHyundaiHybrid, - Meb, - Mg5, - NissanLeaf, - Pylon, - DalyBms, - RjxzsBms, - RangeRoverPhev, - RenaultKangoo, - RenaultTwizy, - RenaultZoe1, - RenaultZoe2, - SantaFePhev, - SimpBms, - TeslaModel3Y, - TeslaModelSX, - TestFake, - VolvoSpa, - VolvoSpaHybrid, + BmwSbox = 1, + BmwI3 = 2, + BmwIx = 3, + BoltAmpera = 4, + BydAtto3 = 5, + CellPowerBms = 6, + Chademo = 7, + CmfaEv = 8, + Foxess = 9, + GeelyGeometryC = 10, + OrionBms = 11, + Sono = 12, + StellantisEcmp = 13, + ImievCZeroIon = 14, + JaguarIpace = 15, + KiaEGmp = 16, + KiaHyundai64 = 17, + KiaHyundaiHybrid = 18, + Meb = 19, + Mg5 = 20, + NissanLeaf = 21, + Pylon = 22, + DalyBms = 23, + RjxzsBms = 24, + RangeRoverPhev = 25, + RenaultKangoo = 26, + RenaultTwizy = 27, + RenaultZoe1 = 28, + RenaultZoe2 = 29, + SantaFePhev = 30, + SimpBms = 31, + TeslaModel3Y = 32, + TeslaModelSX = 33, + TestFake = 34, + VolvoSpa = 35, + VolvoSpaHybrid = 36, Highest }; @@ -95,7 +95,7 @@ class Battery { virtual void set_factory_mode() {} virtual void set_fake_voltage(float v) {} - virtual float get_voltage() { return static_cast(datalayer.battery.status.voltage_dV) / 10.0; } + virtual float get_voltage(); // This allows for battery specific SOC plausibility calculations to be performed. virtual bool soc_plausible() { return true; } diff --git a/Software/src/battery/CHADEMO-SHUNTS.h b/Software/src/battery/CHADEMO-SHUNTS.h index ce23bb1a..7a3cf855 100644 --- a/Software/src/battery/CHADEMO-SHUNTS.h +++ b/Software/src/battery/CHADEMO-SHUNTS.h @@ -1,6 +1,9 @@ #ifndef CHADEMO_SHUNTS_H #define CHADEMO_SHUNTS_H +#include +#include "../devboard/utils/types.h" + uint16_t get_measured_voltage(); uint16_t get_measured_current(); void ISA_handleFrame(CAN_frame* frame); diff --git a/Software/src/battery/CMFA-EV-BATTERY.cpp b/Software/src/battery/CMFA-EV-BATTERY.cpp index eaa82140..34d47059 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.cpp +++ b/Software/src/battery/CMFA-EV-BATTERY.cpp @@ -940,7 +940,7 @@ void CmfaEvBattery::transmit_can(unsigned long currentMillis) { } void CmfaEvBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "CMFA platform, 27 kWh battery", 63); + 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 = 72; diff --git a/Software/src/battery/CMFA-EV-BATTERY.h b/Software/src/battery/CMFA-EV-BATTERY.h index 625601ae..b1dcb3da 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.h +++ b/Software/src/battery/CMFA-EV-BATTERY.h @@ -15,6 +15,7 @@ class CmfaEvBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "CMFA platform, 27 kWh battery"; BatteryHtmlRenderer& get_status_renderer() { return renderer; } diff --git a/Software/src/battery/CanBattery.cpp b/Software/src/battery/CanBattery.cpp new file mode 100644 index 00000000..69366999 --- /dev/null +++ b/Software/src/battery/CanBattery.cpp @@ -0,0 +1,8 @@ +#include "CanBattery.h" +#include "../../src/include.h" + +CanBattery::CanBattery() { + can_interface = can_config.battery; + register_transmitter(this); + register_can_receiver(this, can_interface); +} diff --git a/Software/src/battery/CanBattery.h b/Software/src/battery/CanBattery.h index 936c2a74..0b8b0467 100644 --- a/Software/src/battery/CanBattery.h +++ b/Software/src/battery/CanBattery.h @@ -22,11 +22,7 @@ class CanBattery : public Battery, Transmitter, CanReceiver { protected: CAN_Interface can_interface; - CanBattery() { - can_interface = can_config.battery; - register_transmitter(this); - register_can_receiver(this, can_interface); - } + CanBattery(); CanBattery(CAN_Interface interface) { can_interface = interface; diff --git a/Software/src/battery/DALY-BMS.cpp b/Software/src/battery/DALY-BMS.cpp index dfb635c4..d949445a 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -60,7 +60,7 @@ void DalyBms::update_values() { } void DalyBms::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "DALY RS485", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = CELL_COUNT; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/DALY-BMS.h b/Software/src/battery/DALY-BMS.h index f597fa2e..1c5a2015 100644 --- a/Software/src/battery/DALY-BMS.h +++ b/Software/src/battery/DALY-BMS.h @@ -13,6 +13,7 @@ class DalyBms : public RS485Battery { void update_values(); void transmit_rs485(unsigned long currentMillis); void receive(); + static constexpr char* Name = "DALY RS485"; private: /* Tweak these according to your battery build */ diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index 04a04f02..7f6d3d59 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -1516,7 +1516,7 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) { } void EcmpBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Stellantis ECMP battery", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 108; datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; diff --git a/Software/src/battery/ECMP-BATTERY.h b/Software/src/battery/ECMP-BATTERY.h index e473b52a..c6f0f451 100644 --- a/Software/src/battery/ECMP-BATTERY.h +++ b/Software/src/battery/ECMP-BATTERY.h @@ -16,6 +16,7 @@ class EcmpBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Stellantis ECMP battery"; bool supports_clear_isolation() { return true; } void clear_isolation() { datalayer_extended.stellantisECMP.UserRequestIsolationReset = true; } @@ -38,8 +39,8 @@ class EcmpBattery : public CanBattery { static const int MAX_CELL_DEVIATION_MV = 100; static const int MAX_CELL_VOLTAGE_MV = 4250; static const int MIN_CELL_VOLTAGE_MV = 2700; -#define NOT_SAMPLED_YET 255 -#define COMPLETED_STATE 0 + static const int NOT_SAMPLED_YET = 255; + static const int COMPLETED_STATE = 0; bool battery_RelayOpenRequest = false; bool battery_InterlockOpen = false; uint8_t ContactorResetStatemachine = 0; @@ -143,77 +144,77 @@ class EcmpBattery : public CanBattery { unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was sent unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was sent unsigned long previousMillis5000 = 0; // will store last time a 1000ms CAN Message was sent -#define PID_WELD_CHECK 0xD814 -#define PID_CONT_REASON_OPEN 0xD812 -#define PID_CONTACTOR_STATUS 0xD813 -#define PID_NEG_CONT_CONTROL 0xD44F -#define PID_NEG_CONT_STATUS 0xD453 -#define PID_POS_CONT_CONTROL 0xD44E -#define PID_POS_CONT_STATUS 0xD452 -#define PID_CONTACTOR_NEGATIVE 0xD44C -#define PID_CONTACTOR_POSITIVE 0xD44D -#define PID_PRECHARGE_RELAY_CONTROL 0xD44B -#define PID_PRECHARGE_RELAY_STATUS 0xD451 -#define PID_RECHARGE_STATUS 0xD864 -#define PID_DELTA_TEMPERATURE 0xD878 -#define PID_COLDEST_MODULE 0xD446 -#define PID_LOWEST_TEMPERATURE 0xD87D -#define PID_AVERAGE_TEMPERATURE 0xD877 -#define PID_HIGHEST_TEMPERATURE 0xD817 -#define PID_HOTTEST_MODULE 0xD445 -#define PID_AVG_CELL_VOLTAGE 0xD43D -#define PID_CURRENT 0xD816 -#define PID_INSULATION_NEG 0xD87C -#define PID_INSULATION_POS 0xD87B -#define PID_MAX_CURRENT_10S 0xD876 -#define PID_MAX_DISCHARGE_10S 0xD873 -#define PID_MAX_DISCHARGE_30S 0xD874 -#define PID_MAX_CHARGE_10S 0xD871 -#define PID_MAX_CHARGE_30S 0xD872 -#define PID_ENERGY_CAPACITY 0xD860 -#define PID_HIGH_CELL_NUM 0xD43B -#define PID_LOW_CELL_NUM 0xD43C -#define PID_SUM_OF_CELLS 0xD438 -#define PID_CELL_MIN_CAPACITY 0xD413 -#define PID_CELL_VOLTAGE_MEAS_STATUS 0xD48A -#define PID_INSULATION_RES 0xD47A -#define PID_PACK_VOLTAGE 0xD815 -#define PID_HIGH_CELL_VOLTAGE 0xD870 -#define PID_ALL_CELL_VOLTAGES 0xD440 //Multi-frame -#define PID_LOW_CELL_VOLTAGE 0xD86F -#define PID_BATTERY_ENERGY 0xD865 -#define PID_BATTERY_ENERGY 0xD865 -#define PID_CELLBALANCE_STATUS 0xD46F //Multi-frame? -#define PID_CELLBALANCE_HWERR_MASK 0xD470 //Multi-frame -#define PID_CRASH_COUNTER 0xD42F -#define PID_WIRE_CRASH 0xD87F -#define PID_CAN_CRASH 0xD48D -#define PID_HISTORY_DATA 0xD465 -#define PID_LOWSOC_COUNTER 0xD492 //Not supported on all batteris -#define PID_LAST_CAN_FAILURE_DETAIL 0xD89E //Not supported on all batteris -#define PID_HW_VERSION_NUM 0xF193 //Not supported on all batteris -#define PID_SW_VERSION_NUM 0xF195 //Not supported on all batteris -#define PID_FACTORY_MODE_CONTROL 0xD900 -#define PID_BATTERY_SERIAL 0xD901 -#define PID_ALL_CELL_SOH 0xD4B5 //Very long message reply, too much data for this integration -#define PID_AUX_FUSE_STATE 0xD86C -#define PID_BATTERY_STATE 0xD811 -#define PID_PRECHARGE_SHORT_CIRCUIT 0xD4D8 -#define PID_ESERVICE_PLUG_STATE 0xD86A -#define PID_MAINFUSE_STATE 0xD86B -#define PID_MOST_CRITICAL_FAULT 0xD481 -#define PID_CURRENT_TIME 0xD47F -#define PID_TIME_SENT_BY_CAR 0xD4CA -#define PID_12V 0xD822 -#define PID_12V_ABNORMAL 0xD42B -#define PID_HVIL_IN_VOLTAGE 0xD46B -#define PID_HVIL_OUT_VOLTAGE 0xD46A -#define PID_HVIL_STATE 0xD869 -#define PID_BMS_STATE 0xD45A -#define PID_VEHICLE_SPEED 0xD802 -#define PID_TIME_SPENT_OVER_55C 0xE082 -#define PID_CONTACTOR_CLOSING_COUNTER 0xD416 -#define PID_DATE_OF_MANUFACTURE 0xF18B + + static const uint16_t PID_WELD_CHECK = 0xD814; + static const uint16_t PID_CONT_REASON_OPEN = 0xD812; + static const uint16_t PID_CONTACTOR_STATUS = 0xD813; + static const uint16_t PID_NEG_CONT_CONTROL = 0xD44F; + static const uint16_t PID_NEG_CONT_STATUS = 0xD453; + static const uint16_t PID_POS_CONT_CONTROL = 0xD44E; + static const uint16_t PID_POS_CONT_STATUS = 0xD452; + static const uint16_t PID_CONTACTOR_NEGATIVE = 0xD44C; + static const uint16_t PID_CONTACTOR_POSITIVE = 0xD44D; + static const uint16_t PID_PRECHARGE_RELAY_CONTROL = 0xD44B; + static const uint16_t PID_PRECHARGE_RELAY_STATUS = 0xD451; + static const uint16_t PID_RECHARGE_STATUS = 0xD864; + static const uint16_t PID_DELTA_TEMPERATURE = 0xD878; + static const uint16_t PID_COLDEST_MODULE = 0xD446; + static const uint16_t PID_LOWEST_TEMPERATURE = 0xD87D; + static const uint16_t PID_AVERAGE_TEMPERATURE = 0xD877; + static const uint16_t PID_HIGHEST_TEMPERATURE = 0xD817; + static const uint16_t PID_HOTTEST_MODULE = 0xD445; + static const uint16_t PID_AVG_CELL_VOLTAGE = 0xD43D; + static const uint16_t PID_CURRENT = 0xD816; + static const uint16_t PID_INSULATION_NEG = 0xD87C; + static const uint16_t PID_INSULATION_POS = 0xD87B; + static const uint16_t PID_MAX_CURRENT_10S = 0xD876; + static const uint16_t PID_MAX_DISCHARGE_10S = 0xD873; + static const uint16_t PID_MAX_DISCHARGE_30S = 0xD874; + static const uint16_t PID_MAX_CHARGE_10S = 0xD871; + static const uint16_t PID_MAX_CHARGE_30S = 0xD872; + static const uint16_t PID_ENERGY_CAPACITY = 0xD860; + static const uint16_t PID_HIGH_CELL_NUM = 0xD43B; + static const uint16_t PID_LOW_CELL_NUM = 0xD43C; + static const uint16_t PID_SUM_OF_CELLS = 0xD438; + static const uint16_t PID_CELL_MIN_CAPACITY = 0xD413; + static const uint16_t PID_CELL_VOLTAGE_MEAS_STATUS = 0xD48A; + static const uint16_t PID_INSULATION_RES = 0xD47A; + static const uint16_t PID_PACK_VOLTAGE = 0xD815; + static const uint16_t PID_HIGH_CELL_VOLTAGE = 0xD870; + static const uint16_t PID_ALL_CELL_VOLTAGES = 0xD440; //Multi-frame + static const uint16_t PID_LOW_CELL_VOLTAGE = 0xD86F; + static const uint16_t PID_BATTERY_ENERGY = 0xD865; + static const uint16_t PID_CELLBALANCE_STATUS = 0xD46F; //Multi-frame? + static const uint16_t PID_CELLBALANCE_HWERR_MASK = 0xD470; //Multi-frame + static const uint16_t PID_CRASH_COUNTER = 0xD42F; + static const uint16_t PID_WIRE_CRASH = 0xD87F; + static const uint16_t PID_CAN_CRASH = 0xD48D; + static const uint16_t PID_HISTORY_DATA = 0xD465; + static const uint16_t PID_LOWSOC_COUNTER = 0xD492; //Not supported on all batteris + static const uint16_t PID_LAST_CAN_FAILURE_DETAIL = 0xD89E; //Not supported on all batteris + static const uint16_t PID_HW_VERSION_NUM = 0xF193; //Not supported on all batteris + static const uint16_t PID_SW_VERSION_NUM = 0xF195; //Not supported on all batteris + static const uint16_t PID_FACTORY_MODE_CONTROL = 0xD900; + static const uint16_t PID_BATTERY_SERIAL = 0xD901; + static const uint16_t PID_ALL_CELL_SOH = 0xD4B5; //Very long message reply, too much data for this integration + static const uint16_t PID_AUX_FUSE_STATE = 0xD86C; + static const uint16_t PID_BATTERY_STATE = 0xD811; + static const uint16_t PID_PRECHARGE_SHORT_CIRCUIT = 0xD4D8; + static const uint16_t PID_ESERVICE_PLUG_STATE = 0xD86A; + static const uint16_t PID_MAINFUSE_STATE = 0xD86B; + static const uint16_t PID_MOST_CRITICAL_FAULT = 0xD481; + static const uint16_t PID_CURRENT_TIME = 0xD47F; + static const uint16_t PID_TIME_SENT_BY_CAR = 0xD4CA; + static const uint16_t PID_12V = 0xD822; + static const uint16_t PID_12V_ABNORMAL = 0xD42B; + static const uint16_t PID_HVIL_IN_VOLTAGE = 0xD46B; + static const uint16_t PID_HVIL_OUT_VOLTAGE = 0xD46A; + static const uint16_t PID_HVIL_STATE = 0xD869; + static const uint16_t PID_BMS_STATE = 0xD45A; + static const uint16_t PID_VEHICLE_SPEED = 0xD802; + static const uint16_t PID_TIME_SPENT_OVER_55C = 0xE082; + static const uint16_t PID_CONTACTOR_CLOSING_COUNTER = 0xD416; + static const uint16_t PID_DATE_OF_MANUFACTURE = 0xF18B; uint16_t poll_state = PID_WELD_CHECK; uint16_t incoming_poll = 0; diff --git a/Software/src/battery/FOXESS-BATTERY.cpp b/Software/src/battery/FOXESS-BATTERY.cpp index 20d7f105..4225d509 100644 --- a/Software/src/battery/FOXESS-BATTERY.cpp +++ b/Software/src/battery/FOXESS-BATTERY.cpp @@ -572,7 +572,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) { } void FoxessBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "FoxESS HV2600/ECS4100 OEM battery", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 0; //Startup with no cells, populates later when we know packsize datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/FOXESS-BATTERY.h b/Software/src/battery/FOXESS-BATTERY.h index b7ce01e3..e5143dc6 100644 --- a/Software/src/battery/FOXESS-BATTERY.h +++ b/Software/src/battery/FOXESS-BATTERY.h @@ -15,6 +15,7 @@ class FoxessBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "FoxESS HV2600/ECS4100 OEM battery"; private: static const int MAX_PACK_VOLTAGE_DV = 4672; //467.2V for HS20.8 (used during startup, refined later) diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp index 11269d08..ede65130 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp @@ -660,7 +660,7 @@ void GeelyGeometryCBattery::transmit_can(unsigned long currentMillis) { } void GeelyGeometryCBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Geely Geometry C", 63); + 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 = 102; //70kWh pack has 102S, startup in this mode diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h index 6d6d7c44..abba1220 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h @@ -16,6 +16,7 @@ class GeelyGeometryCBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Geely Geometry C"; BatteryHtmlRenderer& get_status_renderer() { return renderer; } diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp index 6e31a616..69e4478e 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp @@ -190,7 +190,7 @@ void ImievCZeroIonBattery::transmit_can(unsigned long currentMillis) { } void ImievCZeroIonBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "I-Miev / C-Zero / Ion Triplet", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h index df3dc437..1c6dc127 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h @@ -15,6 +15,7 @@ class ImievCZeroIonBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "I-Miev / C-Zero / Ion Triplet"; private: static const int MAX_PACK_VOLTAGE_DV = 3696; //5000 = 500.0V diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.h b/Software/src/battery/JAGUAR-IPACE-BATTERY.h index c997bef4..e97ccab5 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.h +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.h @@ -13,6 +13,7 @@ class JaguarIpaceBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Jaguar I-PACE"; private: static const int MAX_PACK_VOLTAGE_DV = 4546; //5000 = 500.0V diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index d6f5bd4f..43576212 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -1090,7 +1090,7 @@ void KiaEGmpBattery::transmit_can(unsigned long currentMillis) { } void KiaEGmpBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai EGMP platform", 63); + 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 = 192; // TODO: will vary depending on battery diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.h b/Software/src/battery/KIA-E-GMP-BATTERY.h index 66edc935..faabeb27 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.h +++ b/Software/src/battery/KIA-E-GMP-BATTERY.h @@ -19,6 +19,7 @@ class KiaEGmpBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Kia/Hyundai EGMP platform"; private: uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps); diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp index ad95ac77..f0c2a7cc 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp @@ -464,7 +464,7 @@ void KiaHyundai64Battery::transmit_can(unsigned long currentMillis) { } void KiaHyundai64Battery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai 64/40kWh battery", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV; //Start with 98S value. Precised later datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV; //Start with 90S value. Precised later diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h index 80f17277..943dfa53 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h @@ -35,6 +35,7 @@ class KiaHyundai64Battery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Kia/Hyundai 64/40kWh battery"; BatteryHtmlRenderer& get_status_renderer() { return renderer; } diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp index f7a03814..1372dd8f 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp @@ -214,7 +214,7 @@ void KiaHyundaiHybridBattery::transmit_can(unsigned long currentMillis) { } void KiaHyundaiHybridBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai Hybrid", 63); + 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 = 56; // HEV , TODO: Make dynamic according to HEV/PHEV diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h index 343eadca..35503754 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h @@ -15,6 +15,7 @@ class KiaHyundaiHybridBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Kia/Hyundai Hybrid"; private: static const int MAX_PACK_VOLTAGE_DV = 2550; //5000 = 500.0V diff --git a/Software/src/battery/MEB-BATTERY.cpp b/Software/src/battery/MEB-BATTERY.cpp index 776c3597..0ac01e26 100644 --- a/Software/src/battery/MEB-BATTERY.cpp +++ b/Software/src/battery/MEB-BATTERY.cpp @@ -2035,7 +2035,7 @@ void MebBattery::transmit_can(unsigned long currentMillis) { } void MebBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Volkswagen Group MEB platform via CAN-FD", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 108; //Startup in 108S mode. We figure out the actual count later. datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_108S_DV; //Defined later to correct pack size diff --git a/Software/src/battery/MEB-BATTERY.h b/Software/src/battery/MEB-BATTERY.h index 69def6a3..33e93434 100644 --- a/Software/src/battery/MEB-BATTERY.h +++ b/Software/src/battery/MEB-BATTERY.h @@ -17,6 +17,7 @@ class MebBattery : public CanBattery { virtual void transmit_can(unsigned long currentMillis); bool supports_real_BMS_status() { return true; } bool supports_charged_energy() { return true; } + static constexpr char* Name = "Volkswagen Group MEB platform via CAN-FD"; BatteryHtmlRenderer& get_status_renderer() { return renderer; } diff --git a/Software/src/battery/MG-5-BATTERY.h b/Software/src/battery/MG-5-BATTERY.h index 9e9680e6..2c343bdf 100644 --- a/Software/src/battery/MG-5-BATTERY.h +++ b/Software/src/battery/MG-5-BATTERY.h @@ -15,6 +15,7 @@ class Mg5Battery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "MG 5 battery"; private: static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V diff --git a/Software/src/battery/ORION-BMS.cpp b/Software/src/battery/ORION-BMS.cpp index 40876223..a8688b56 100644 --- a/Software/src/battery/ORION-BMS.cpp +++ b/Software/src/battery/ORION-BMS.cpp @@ -114,7 +114,7 @@ void OrionBms::transmit_can(unsigned long currentMillis) { } void OrionBms::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "DIY battery with Orion BMS (Victron setting)", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = NUMBER_OF_CELLS; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/ORION-BMS.h b/Software/src/battery/ORION-BMS.h index fe549693..6fa17fc3 100644 --- a/Software/src/battery/ORION-BMS.h +++ b/Software/src/battery/ORION-BMS.h @@ -15,6 +15,7 @@ class OrionBms : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "DIY battery with Orion BMS (Victron setting)"; private: /* Change the following to suit your battery */ diff --git a/Software/src/battery/PYLON-BATTERY.h b/Software/src/battery/PYLON-BATTERY.h index 308d7d07..9b5bb88d 100644 --- a/Software/src/battery/PYLON-BATTERY.h +++ b/Software/src/battery/PYLON-BATTERY.h @@ -31,6 +31,7 @@ class PylonBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Pylon compatible battery"; private: /* Change the following to suit your battery */ diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp index 7d481ff5..bfa50b6d 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp @@ -207,7 +207,7 @@ void RangeRoverPhevBattery::transmit_can(unsigned long currentMillis) { } void RangeRoverPhevBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Range Rover 13kWh PHEV battery (L494/L405)", 63); + 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.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h index 9c69fbe7..d3d4da37 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h @@ -15,6 +15,7 @@ class RangeRoverPhevBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Range Rover 13kWh PHEV battery (L494/L405)"; private: /* Change the following to suit your battery */ diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp index 903389d1..db43b95b 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp @@ -181,7 +181,7 @@ void RenaultKangooBattery::transmit_can(unsigned long currentMillis) { } void RenaultKangooBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Renault Kangoo", 63); + 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.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.h b/Software/src/battery/RENAULT-KANGOO-BATTERY.h index 68bb2939..3a6f6732 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.h +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.h @@ -15,6 +15,7 @@ class RenaultKangooBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Renault Kangoo"; private: static const int MAX_PACK_VOLTAGE_DV = 4150; //5000 = 500.0V diff --git a/Software/src/battery/RENAULT-TWIZY.cpp b/Software/src/battery/RENAULT-TWIZY.cpp index 051a5ee7..21aa8a46 100644 --- a/Software/src/battery/RENAULT-TWIZY.cpp +++ b/Software/src/battery/RENAULT-TWIZY.cpp @@ -118,7 +118,7 @@ void RenaultTwizyBattery::transmit_can(unsigned long currentMillis) { } void RenaultTwizyBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Renault Twizy", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 14; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RENAULT-TWIZY.h b/Software/src/battery/RENAULT-TWIZY.h index 5796bb73..f2388a4f 100644 --- a/Software/src/battery/RENAULT-TWIZY.h +++ b/Software/src/battery/RENAULT-TWIZY.h @@ -13,6 +13,7 @@ class RenaultTwizyBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Renault Twizy"; private: static const int MAX_PACK_VOLTAGE_DV = 579; // 57.9V at 100% SOC (with 70% SOH, new one might be higher) diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp index d9985494..fa64d9f4 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp @@ -513,7 +513,7 @@ void RenaultZoeGen1Battery::transmit_can(unsigned long currentMillis) { } void RenaultZoeGen1Battery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen1 22/40kWh", 63); + 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; diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h index 093ccf6d..8b15d164 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h @@ -31,6 +31,7 @@ class RenaultZoeGen1Battery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Renault Zoe Gen1 22/40kWh"; BatteryHtmlRenderer& get_status_renderer() { return renderer; } diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp index 4bb230c8..4970f7bd 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp @@ -704,7 +704,7 @@ void RenaultZoeGen2Battery::transmit_can(unsigned long currentMillis) { } void RenaultZoeGen2Battery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen2 50kWh", 63); + 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; diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h index ce3d37d0..2d908345 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h @@ -15,6 +15,7 @@ class RenaultZoeGen2Battery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Renault Zoe Gen2 50kWh"; bool supports_reset_NVROL() { return true; } diff --git a/Software/src/battery/RJXZS-BMS.cpp b/Software/src/battery/RJXZS-BMS.cpp index ff62bb8c..fda2c42b 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -516,7 +516,7 @@ void RjxzsBms::transmit_can(unsigned long currentMillis) { } void RjxzsBms::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "RJXZS BMS, DIY battery", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/RJXZS-BMS.h b/Software/src/battery/RJXZS-BMS.h index 4c85ae5d..569c62e5 100644 --- a/Software/src/battery/RJXZS-BMS.h +++ b/Software/src/battery/RJXZS-BMS.h @@ -15,6 +15,7 @@ class RjxzsBms : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "RJXZS BMS, DIY battery"; private: /* Tweak these according to your battery build */ diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp index 2b7dbe64..783e42b4 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp @@ -319,7 +319,7 @@ void SantaFePhevBattery::transmit_can(unsigned long currentMillis) { } void SantaFePhevBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Santa Fe PHEV", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer_battery->info.number_of_cells = 96; datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.h b/Software/src/battery/SANTA-FE-PHEV-BATTERY.h index b7b385ee..c926a883 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.h +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.h @@ -27,6 +27,7 @@ class SantaFePhevBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Santa Fe PHEV"; private: DATALAYER_BATTERY_TYPE* datalayer_battery; diff --git a/Software/src/battery/SIMPBMS-BATTERY.cpp b/Software/src/battery/SIMPBMS-BATTERY.cpp index e997b3ea..3d6e8379 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.cpp +++ b/Software/src/battery/SIMPBMS-BATTERY.cpp @@ -92,7 +92,7 @@ void SimpBmsBattery::transmit_can(unsigned long currentMillis) { } void SimpBmsBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "SIMPBMS battery", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = CELL_COUNT; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/SIMPBMS-BATTERY.h b/Software/src/battery/SIMPBMS-BATTERY.h index 0e799ab2..4d415147 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.h +++ b/Software/src/battery/SIMPBMS-BATTERY.h @@ -15,6 +15,7 @@ class SimpBmsBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "SIMPBMS battery"; private: /* DEFAULT VALUES BMS will send configured */ diff --git a/Software/src/battery/SONO-BATTERY.cpp b/Software/src/battery/SONO-BATTERY.cpp index ab7eb043..47631cc2 100644 --- a/Software/src/battery/SONO-BATTERY.cpp +++ b/Software/src/battery/SONO-BATTERY.cpp @@ -140,7 +140,7 @@ void SonoBattery::transmit_can(unsigned long currentMillis) { } void SonoBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Sono Motors Sion 64kWh LFP ", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 96; datalayer.system.status.battery_allows_contactor_closing = true; diff --git a/Software/src/battery/SONO-BATTERY.h b/Software/src/battery/SONO-BATTERY.h index 51b46222..b1e366ad 100644 --- a/Software/src/battery/SONO-BATTERY.h +++ b/Software/src/battery/SONO-BATTERY.h @@ -15,6 +15,7 @@ class SonoBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Sono Motors Sion 64kWh LFP "; private: static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index 66d67afd..fe27647c 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -377,7 +377,7 @@ void VolvoSpaBattery::transmit_can(unsigned long currentMillis) { } void VolvoSpaBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Volvo / Polestar 69/78kWh SPA battery", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 0; // Initializes when all cells have been read datalayer.battery.info.total_capacity_Wh = 78200; //Startout in 78kWh mode (This value used for SOC calc) diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.h b/Software/src/battery/VOLVO-SPA-BATTERY.h index f29c580a..1e90237c 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-BATTERY.h @@ -16,6 +16,7 @@ class VolvoSpaBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Volvo / Polestar 69/78kWh SPA battery"; bool supports_reset_DTC() { return true; } void reset_DTC() { datalayer_extended.VolvoPolestar.UserRequestDTCreset = true; } diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp index 258afcb0..19507a7c 100644 --- a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp @@ -552,8 +552,8 @@ void VolvoSpaHybridBattery::transmit_can(unsigned long currentMillis) { } } -void VolvoSpaHybridBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Volvo PHEV battery", 63); //changed +void VolvoSpaHybridBattery::setup(void) { // Performs one time setup at startup + strncpy(datalayer.system.info.battery_protocol, Name, 63); //changed datalayer.system.info.battery_protocol[63] = '\0'; datalayer.battery.info.number_of_cells = 102; //was 108, changed datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h index ec3de17f..e31c9967 100644 --- a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h @@ -16,6 +16,7 @@ class VolvoSpaHybridBattery : public CanBattery { virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Volvo PHEV battery"; bool supports_reset_DTC() { return true; } void reset_DTC() { datalayer_extended.VolvoHybrid.UserRequestDTCreset = true; } diff --git a/Software/src/communication/can/CanReceiver.h b/Software/src/communication/can/CanReceiver.h index 22a9ba58..2a510606 100644 --- a/Software/src/communication/can/CanReceiver.h +++ b/Software/src/communication/can/CanReceiver.h @@ -2,7 +2,6 @@ #define _CANRECEIVER_H #include "src/devboard/utils/types.h" -#include "src/include.h" class CanReceiver { public: diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index 3b2238a6..08fee8f0 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -29,6 +29,13 @@ const bool remote_bms_reset_default = true; #endif bool remote_bms_reset = remote_bms_reset_default; +#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY +const bool contactor_control_enabled_double_battery_default = true; +#else +const bool contactor_control_enabled_double_battery_default = false; +#endif +bool contactor_control_enabled_double_battery = contactor_control_enabled_double_battery_default; + // TODO: Ensure valid values at run-time // Parameters @@ -105,12 +112,15 @@ void init_contactors() { set(PRECHARGE_PIN, OFF); } -#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY - pinMode(SECOND_POSITIVE_CONTACTOR_PIN, OUTPUT); - set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); - pinMode(SECOND_NEGATIVE_CONTACTOR_PIN, OUTPUT); - set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF); -#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY +#if defined(SECOND_POSITIVE_CONTACTOR_PIN) && defined(SECOND_NEGATIVE_CONTACTOR_PIN) + if (contactor_control_enabled_double_battery) { + pinMode(SECOND_POSITIVE_CONTACTOR_PIN, OUTPUT); + set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); + pinMode(SECOND_NEGATIVE_CONTACTOR_PIN, OUTPUT); + set(SECOND_NEGATIVE_CONTACTOR_PIN, OFF); + } +#endif + // Init BMS contactor #if defined HW_STARK || defined HW_3LB // This hardware has dedicated pin, always enable on start pinMode(BMS_POWER, OUTPUT); //LilyGo is omitted from this, only enabled if user selects PERIODIC_BMS_RESET diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.h b/Software/src/communication/contactorcontrol/comm_contactorcontrol.h index 9a7b7b00..e18e8431 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.h +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.h @@ -8,6 +8,7 @@ // Settings that can be changed at run-time extern bool contactor_control_enabled; +extern bool contactor_control_enabled_double_battery; extern bool pwm_contactor_control; extern bool periodic_bms_reset; extern bool remote_bms_reset; diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index fec14290..4ce46a17 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -81,6 +81,7 @@ void init_stored_settings() { user_selected_charger_type = (ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None); user_selected_second_battery = settings.getBool("DBLBTR", false); contactor_control_enabled = settings.getBool("CNTCTRL", false); + contactor_control_enabled_double_battery = settings.getBool("CNTCTRLDBL", false); pwm_contactor_control = settings.getBool("PWMCNTCTRL", false); periodic_bms_reset = settings.getBool("PERBMSRESET", false); remote_bms_reset = settings.getBool("REMBMSRESET", false); diff --git a/Software/src/communication/nvm/comm_nvm.h b/Software/src/communication/nvm/comm_nvm.h index f472fbfc..023a757b 100644 --- a/Software/src/communication/nvm/comm_nvm.h +++ b/Software/src/communication/nvm/comm_nvm.h @@ -34,21 +34,25 @@ void store_settings_equipment_stop(); */ void store_settings(); +// Wraps the Preferences object begin/end calls, so that the scope of this object +// runs them automatically (via constructor/destructor). class BatteryEmulatorSettingsStore { public: - BatteryEmulatorSettingsStore() { settings.begin("batterySettings", false); } + BatteryEmulatorSettingsStore(bool readOnly = false) { + if (!settings.begin("batterySettings", readOnly)) { + set_event(EVENT_PERSISTENT_SAVE_INFO, 0); + } + } ~BatteryEmulatorSettingsStore() { settings.end(); } - BatteryType get_batterytype() { return (BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None); } + uint32_t getUInt(const char* name, uint32_t defaultValue) { return settings.getUInt(name, defaultValue); } - void set_batterytype(BatteryType type) { settings.putUInt("BATTTYPE", (int)type); } + void saveUInt(const char* name, uint32_t value) { settings.putUInt(name, value); } - InverterProtocolType get_invertertype() { - return (InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None); - } + bool getBool(const char* name) { return settings.getBool(name, false); } - void set_invertertype(InverterProtocolType type) { settings.putUInt("INVTYPE", (int)type); } + void saveBool(const char* name, bool value) { settings.putBool(name, value); } private: Preferences settings; diff --git a/Software/src/devboard/safety/safety.cpp b/Software/src/devboard/safety/safety.cpp index a276d5ee..8c26de32 100644 --- a/Software/src/devboard/safety/safety.cpp +++ b/Software/src/devboard/safety/safety.cpp @@ -55,161 +55,166 @@ void update_machineryprotection() { } // Start checking that the battery is within reason. Incase we see any funny business, raise an event! + // Don't check any battery issues if battery is not configured + if (battery) { - // Pause function is on OR we have a critical fault event active - if (emulator_pause_request_ON || (datalayer.battery.status.bms_status == FAULT)) { - datalayer.battery.status.max_discharge_power_W = 0; - datalayer.battery.status.max_charge_power_W = 0; - } - - // Battery is overheated! - if (datalayer.battery.status.temperature_max_dC > BATTERY_MAXTEMPERATURE) { - set_event(EVENT_BATTERY_OVERHEAT, datalayer.battery.status.temperature_max_dC); - } else { - clear_event(EVENT_BATTERY_OVERHEAT); - } - - // Battery is frozen! - if (datalayer.battery.status.temperature_min_dC < BATTERY_MINTEMPERATURE) { - set_event(EVENT_BATTERY_FROZEN, datalayer.battery.status.temperature_min_dC); - } else { - clear_event(EVENT_BATTERY_FROZEN); - } - - if (labs(datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC) > - BATTERY_MAX_TEMPERATURE_DEVIATION) { - set_event_latched(EVENT_BATTERY_TEMP_DEVIATION_HIGH, - datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC); - } else { - clear_event(EVENT_BATTERY_TEMP_DEVIATION_HIGH); - } - - // Battery voltage is over designed max voltage! - if (datalayer.battery.status.voltage_dV > datalayer.battery.info.max_design_voltage_dV) { - set_event(EVENT_BATTERY_OVERVOLTAGE, datalayer.battery.status.voltage_dV); - datalayer.battery.status.max_charge_power_W = 0; - } else { - clear_event(EVENT_BATTERY_OVERVOLTAGE); - } - - // Battery voltage is under designed min voltage! - if (datalayer.battery.status.voltage_dV < datalayer.battery.info.min_design_voltage_dV) { - set_event(EVENT_BATTERY_UNDERVOLTAGE, datalayer.battery.status.voltage_dV); - datalayer.battery.status.max_discharge_power_W = 0; - } else { - clear_event(EVENT_BATTERY_UNDERVOLTAGE); - } - - // Cell overvoltage, further charging not possible. Battery might be imbalanced. - if (datalayer.battery.status.cell_max_voltage_mV >= datalayer.battery.info.max_cell_voltage_mV) { - set_event(EVENT_CELL_OVER_VOLTAGE, 0); - datalayer.battery.status.max_charge_power_W = 0; - } - // Cell CRITICAL overvoltage, critical latching error without automatic reset. Requires user action to inspect battery. - if (datalayer.battery.status.cell_max_voltage_mV >= (datalayer.battery.info.max_cell_voltage_mV + CELL_CRITICAL_MV)) { - set_event(EVENT_CELL_CRITICAL_OVER_VOLTAGE, 0); - } - - // Cell undervoltage. Further discharge not possible. Battery might be imbalanced. - if (datalayer.battery.status.cell_min_voltage_mV <= datalayer.battery.info.min_cell_voltage_mV) { - set_event(EVENT_CELL_UNDER_VOLTAGE, 0); - datalayer.battery.status.max_discharge_power_W = 0; - } - //Cell CRITICAL undervoltage. critical latching error without automatic reset. Requires user action to inspect battery. - if (datalayer.battery.status.cell_min_voltage_mV <= (datalayer.battery.info.min_cell_voltage_mV - CELL_CRITICAL_MV)) { - set_event(EVENT_CELL_CRITICAL_UNDER_VOLTAGE, 0); - } - - // Battery is fully charged. Dont allow any more power into it - // Normally the BMS will send 0W allowed, but this acts as an additional layer of safety - if (datalayer.battery.status.reported_soc == 10000) //Scaled SOC% value is 100.00% - { - if (!battery_full_event_fired) { - set_event(EVENT_BATTERY_FULL, 0); - battery_full_event_fired = true; + // Pause function is on OR we have a critical fault event active + if (emulator_pause_request_ON || (datalayer.battery.status.bms_status == FAULT)) { + datalayer.battery.status.max_discharge_power_W = 0; + datalayer.battery.status.max_charge_power_W = 0; } - datalayer.battery.status.max_charge_power_W = 0; - } else { - clear_event(EVENT_BATTERY_FULL); - battery_full_event_fired = false; - } - // Battery is empty. Do not allow further discharge. - // Normally the BMS will send 0W allowed, but this acts as an additional layer of safety - if (datalayer.battery.status.bms_status == ACTIVE) { - if (datalayer.battery.status.reported_soc == 0) { //Scaled SOC% value is 0.00% - if (!battery_empty_event_fired) { - set_event(EVENT_BATTERY_EMPTY, 0); - battery_empty_event_fired = true; - } + // Battery is overheated! + if (datalayer.battery.status.temperature_max_dC > BATTERY_MAXTEMPERATURE) { + set_event(EVENT_BATTERY_OVERHEAT, datalayer.battery.status.temperature_max_dC); + } else { + clear_event(EVENT_BATTERY_OVERHEAT); + } + + // Battery is frozen! + if (datalayer.battery.status.temperature_min_dC < BATTERY_MINTEMPERATURE) { + set_event(EVENT_BATTERY_FROZEN, datalayer.battery.status.temperature_min_dC); + } else { + clear_event(EVENT_BATTERY_FROZEN); + } + + if (labs(datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC) > + BATTERY_MAX_TEMPERATURE_DEVIATION) { + set_event_latched(EVENT_BATTERY_TEMP_DEVIATION_HIGH, + datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC); + } else { + clear_event(EVENT_BATTERY_TEMP_DEVIATION_HIGH); + } + + // Battery voltage is over designed max voltage! + if (datalayer.battery.status.voltage_dV > datalayer.battery.info.max_design_voltage_dV) { + set_event(EVENT_BATTERY_OVERVOLTAGE, datalayer.battery.status.voltage_dV); + datalayer.battery.status.max_charge_power_W = 0; + } else { + clear_event(EVENT_BATTERY_OVERVOLTAGE); + } + + // Battery voltage is under designed min voltage! + if (datalayer.battery.status.voltage_dV < datalayer.battery.info.min_design_voltage_dV) { + set_event(EVENT_BATTERY_UNDERVOLTAGE, datalayer.battery.status.voltage_dV); datalayer.battery.status.max_discharge_power_W = 0; } else { - clear_event(EVENT_BATTERY_EMPTY); - battery_empty_event_fired = false; + clear_event(EVENT_BATTERY_UNDERVOLTAGE); } - } - // Battery is extremely degraded, not fit for secondlifestorage! - if (datalayer.battery.status.soh_pptt < 2500) { - set_event(EVENT_SOH_LOW, datalayer.battery.status.soh_pptt); - } else { - clear_event(EVENT_SOH_LOW); - } + // Cell overvoltage, further charging not possible. Battery might be imbalanced. + if (datalayer.battery.status.cell_max_voltage_mV >= datalayer.battery.info.max_cell_voltage_mV) { + set_event(EVENT_CELL_OVER_VOLTAGE, 0); + datalayer.battery.status.max_charge_power_W = 0; + } + // Cell CRITICAL overvoltage, critical latching error without automatic reset. Requires user action to inspect battery. + if (datalayer.battery.status.cell_max_voltage_mV >= + (datalayer.battery.info.max_cell_voltage_mV + CELL_CRITICAL_MV)) { + set_event(EVENT_CELL_CRITICAL_OVER_VOLTAGE, 0); + } - if (battery && !battery->soc_plausible()) { - set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc); - } + // Cell undervoltage. Further discharge not possible. Battery might be imbalanced. + if (datalayer.battery.status.cell_min_voltage_mV <= datalayer.battery.info.min_cell_voltage_mV) { + set_event(EVENT_CELL_UNDER_VOLTAGE, 0); + datalayer.battery.status.max_discharge_power_W = 0; + } + //Cell CRITICAL undervoltage. critical latching error without automatic reset. Requires user action to inspect battery. + if (datalayer.battery.status.cell_min_voltage_mV <= + (datalayer.battery.info.min_cell_voltage_mV - CELL_CRITICAL_MV)) { + set_event(EVENT_CELL_CRITICAL_UNDER_VOLTAGE, 0); + } - // Check diff between highest and lowest cell - cell_deviation_mV = - std::abs(datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV); - if (cell_deviation_mV > datalayer.battery.info.max_cell_voltage_deviation_mV) { - set_event(EVENT_CELL_DEVIATION_HIGH, (cell_deviation_mV / 20)); - } else { - clear_event(EVENT_CELL_DEVIATION_HIGH); - } - - // Inverter is charging with more power than battery wants! - if (datalayer.battery.status.active_power_W > 0) { // Charging - if (datalayer.battery.status.active_power_W > (datalayer.battery.status.max_charge_power_W + 2000)) { - if (charge_limit_failures > MAX_CHARGE_DISCHARGE_LIMIT_FAILURES) { - set_event(EVENT_CHARGE_LIMIT_EXCEEDED, 0); // Alert when 2kW over requested max - } else { - charge_limit_failures++; + // Battery is fully charged. Dont allow any more power into it + // Normally the BMS will send 0W allowed, but this acts as an additional layer of safety + if (datalayer.battery.status.reported_soc == 10000) //Scaled SOC% value is 100.00% + { + if (!battery_full_event_fired) { + set_event(EVENT_BATTERY_FULL, 0); + battery_full_event_fired = true; } + datalayer.battery.status.max_charge_power_W = 0; } else { - clear_event(EVENT_CHARGE_LIMIT_EXCEEDED); - charge_limit_failures = 0; + clear_event(EVENT_BATTERY_FULL); + battery_full_event_fired = false; } - } - // Inverter is pulling too much power from battery! - if (datalayer.battery.status.active_power_W < 0) { // Discharging - if (-datalayer.battery.status.active_power_W > (datalayer.battery.status.max_discharge_power_W + 2000)) { - if (discharge_limit_failures > MAX_CHARGE_DISCHARGE_LIMIT_FAILURES) { - set_event(EVENT_DISCHARGE_LIMIT_EXCEEDED, 0); // Alert when 2kW over requested max + // Battery is empty. Do not allow further discharge. + // Normally the BMS will send 0W allowed, but this acts as an additional layer of safety + if (datalayer.battery.status.bms_status == ACTIVE) { + if (datalayer.battery.status.reported_soc == 0) { //Scaled SOC% value is 0.00% + if (!battery_empty_event_fired) { + set_event(EVENT_BATTERY_EMPTY, 0); + battery_empty_event_fired = true; + } + datalayer.battery.status.max_discharge_power_W = 0; } else { - discharge_limit_failures++; + clear_event(EVENT_BATTERY_EMPTY); + battery_empty_event_fired = false; } - } else { - clear_event(EVENT_DISCHARGE_LIMIT_EXCEEDED); - discharge_limit_failures = 0; } - } - // Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error - if (!datalayer.battery.status.CAN_battery_still_alive) { - set_event(EVENT_CAN_BATTERY_MISSING, can_config.battery); - } else { - datalayer.battery.status.CAN_battery_still_alive--; - clear_event(EVENT_CAN_BATTERY_MISSING); - } + // Battery is extremely degraded, not fit for secondlifestorage! + if (datalayer.battery.status.soh_pptt < 2500) { + set_event(EVENT_SOH_LOW, datalayer.battery.status.soh_pptt); + } else { + clear_event(EVENT_SOH_LOW); + } - // Too many malformed CAN messages recieved! - if (datalayer.battery.status.CAN_error_counter > MAX_CAN_FAILURES) { - set_event(EVENT_CAN_CORRUPTED_WARNING, can_config.battery); - } else { - clear_event(EVENT_CAN_CORRUPTED_WARNING); + if (battery && !battery->soc_plausible()) { + set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc); + } + + // Check diff between highest and lowest cell + cell_deviation_mV = + std::abs(datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV); + if (cell_deviation_mV > datalayer.battery.info.max_cell_voltage_deviation_mV) { + set_event(EVENT_CELL_DEVIATION_HIGH, (cell_deviation_mV / 20)); + } else { + clear_event(EVENT_CELL_DEVIATION_HIGH); + } + + // Inverter is charging with more power than battery wants! + if (datalayer.battery.status.active_power_W > 0) { // Charging + if (datalayer.battery.status.active_power_W > (datalayer.battery.status.max_charge_power_W + 2000)) { + if (charge_limit_failures > MAX_CHARGE_DISCHARGE_LIMIT_FAILURES) { + set_event(EVENT_CHARGE_LIMIT_EXCEEDED, 0); // Alert when 2kW over requested max + } else { + charge_limit_failures++; + } + } else { + clear_event(EVENT_CHARGE_LIMIT_EXCEEDED); + charge_limit_failures = 0; + } + } + + // Inverter is pulling too much power from battery! + if (datalayer.battery.status.active_power_W < 0) { // Discharging + if (-datalayer.battery.status.active_power_W > (datalayer.battery.status.max_discharge_power_W + 2000)) { + if (discharge_limit_failures > MAX_CHARGE_DISCHARGE_LIMIT_FAILURES) { + set_event(EVENT_DISCHARGE_LIMIT_EXCEEDED, 0); // Alert when 2kW over requested max + } else { + discharge_limit_failures++; + } + } else { + clear_event(EVENT_DISCHARGE_LIMIT_EXCEEDED); + discharge_limit_failures = 0; + } + } + + // Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error + if (!datalayer.battery.status.CAN_battery_still_alive) { + set_event(EVENT_CAN_BATTERY_MISSING, can_config.battery); + } else { + datalayer.battery.status.CAN_battery_still_alive--; + clear_event(EVENT_CAN_BATTERY_MISSING); + } + + // Too many malformed CAN messages recieved! + if (datalayer.battery.status.CAN_error_counter > MAX_CAN_FAILURES) { + set_event(EVENT_CAN_CORRUPTED_WARNING, can_config.battery); + } else { + clear_event(EVENT_CAN_CORRUPTED_WARNING); + } } if (inverter && inverter->interface_type() == InverterInterfaceType::Can) { diff --git a/Software/src/devboard/utils/types.h b/Software/src/devboard/utils/types.h index b06379fe..98a73726 100644 --- a/Software/src/devboard/utils/types.h +++ b/Software/src/devboard/utils/types.h @@ -42,6 +42,9 @@ enum PrechargeState { #define CAN_STILL_ALIVE 60 // Set by battery each time we get a CAN message. Decrements every second. When reaching 0, sets event +typedef enum { CAN_NATIVE = 0, CANFD_NATIVE = 1, CAN_ADDON_MCP2515 = 2, CANFD_ADDON_MCP2518 = 3 } CAN_Interface; +extern const char* getCANInterfaceName(CAN_Interface interface); + /* CAN Frame structure */ typedef struct { bool FD; diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index 223990f0..da08a3fd 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -107,29 +107,24 @@ String settings_processor(const String& var) { "
"; content += ""; content += ""; content += ""; - //content += ""; // TODO: Generalize settings: define settings in one place and use the definitions to render // UI and handle load/save - render_checkbox(content, "Double battery", settings.get_doublebattery(), "dblbtr"); - render_checkbox(content, "Contactor control", get_bool("CNTCTRL"), "contctrl"); - render_checkbox(content, "PWM contactor control", get_bool("PWMCNTCTRL"), "pwmcontctrl"); - render_checkbox(content, "Periodic BMS reset", get_bool("PERBMSRESET"), "PERBMSRESET"); - render_checkbox(content, "Remote BMS reset", get_bool("REMBMSRESET"), "REMBMSRESET"); + render_checkbox(content, "Double battery", settings.getBool("DBLBTR"), "dblbtr"); + render_checkbox(content, "Contactor control", settings.getBool("CNTCTRL"), "contctrl"); + render_checkbox(content, "Contactor control double battery", settings.getBool("CNTCTRLDBL"), "contctrldbl"); + render_checkbox(content, "PWM contactor control", settings.getBool("PWMCNTCTRL"), "pwmcontctrl"); + render_checkbox(content, "Periodic BMS reset", settings.getBool("PERBMSRESET"), "PERBMSRESET"); + render_checkbox(content, "Remote BMS reset", settings.getBool("REMBMSRESET"), "REMBMSRESET"); - /* content += - "
Save
"; diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index a8208c62..9525b262 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -4,6 +4,7 @@ #include "../../../USER_SECRETS.h" #include "../../battery/BATTERIES.h" #include "../../battery/Battery.h" +#include "../../communication/contactorcontrol/comm_contactorcontrol.h" #include "../../communication/nvm/comm_nvm.h" #include "../../datalayer/datalayer.h" #include "../../datalayer/datalayer_extended.h" @@ -39,7 +40,7 @@ const char get_firmware_info_html[] = R"rawliteral(%X%)rawliteral"; String importedLogs = ""; // Store the uploaded logfile contents in RAM bool isReplayRunning = false; // Global flag to track replay state -// True when user has updated settings and a reboot is needed. +// True when user has updated settings that need a reboot to be effective. bool settingsUpdated = false; CAN_frame currentFrame = {.FD = true, .ext_ID = false, .DLC = 64, .ID = 0x12F, .data = {0}}; @@ -389,30 +390,32 @@ void init_webserver() { #ifdef COMMON_IMAGE // Handles the form POST from UI to save certain settings: battery/inverter type and double battery on/off server.on("/saveSettings", HTTP_POST, [](AsyncWebServerRequest* request) { + BatteryEmulatorSettingsStore settings; + int params = request->params(); - // dblbtr not present in form content if not checked. - bool secondBattery = false; for (int i = 0; i < params; i++) { auto p = request->getParam(i); if (p->name() == "inverter") { auto type = static_cast(atoi(p->value().c_str())); - store_uint("INVTYPE", (int)type); + settings.saveUInt("INVTYPE", (int)type); } else if (p->name() == "battery") { auto type = static_cast(atoi(p->value().c_str())); - store_uint("BATTTYPE", (int)type); + settings.saveUInt("BATTTYPE", (int)type); } else if (p->name() == "charger") { auto type = static_cast(atoi(p->value().c_str())); - store_uint("CHGTYPE", (int)type); + settings.saveUInt("CHGTYPE", (int)type); } else if (p->name() == "dblbtr") { - store_bool("DBLBTR", p->value() == "on"); + settings.saveBool("DBLBTR", p->value() == "on"); } else if (p->name() == "contctrl") { - store_bool("CNTCTRL", p->value() == "on"); + settings.saveBool("CNTCTRL", p->value() == "on"); + } else if (p->name() == "contctrldbl") { + settings.saveBool("CNTCTRLDBL", p->value() == "on"); } else if (p->name() == "pwmcontctrl") { - store_bool("PWMCNTCTRL", p->value() == "on"); + settings.saveBool("PWMCNTCTRL", p->value() == "on"); } else if (p->name() == "PERBMSRESET") { - store_bool("PERBMSRESET", p->value() == "on"); + settings.saveBool("PERBMSRESET", p->value() == "on"); } else if (p->name() == "REMBMSRESET") { - store_bool("REMBMSRESET", p->value() == "on"); + settings.saveBool("REMBMSRESET", p->value() == "on"); } } @@ -937,6 +940,10 @@ String processor(const String& var) { // Show version number content += "

Software: " + String(version_number); + +#ifdef COMMON_IMAGE + content += " (Common image) "; +#endif // Show hardware used: #ifdef HW_LILYGO content += " Hardware: LilyGo T-CAN485"; @@ -980,315 +987,98 @@ String processor(const String& var) { // Close the block content += ""; - // Start a new block with a specific background color - content += "
"; + if (inverter || battery || shunt || charger) { + // Start a new block with a specific background color + content += "
"; - // Display which components are used - if (inverter) { - content += "

Inverter protocol: "; - content += datalayer.system.info.inverter_protocol; - content += " "; - content += datalayer.system.info.inverter_brand; - content += "

"; + // Display which components are used + if (inverter) { + content += "

Inverter protocol: "; + content += datalayer.system.info.inverter_protocol; + content += " "; + content += datalayer.system.info.inverter_brand; + content += "

"; + } + + if (battery) { + content += "

Battery protocol: "; + content += datalayer.system.info.battery_protocol; + if (battery2) { + content += " (Double battery)"; + } + if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { + content += " (LFP)"; + } + content += "

"; + } + + if (shunt) { + content += "

Shunt protocol: "; + content += datalayer.system.info.shunt_protocol; + content += "

"; + } + + if (charger) { + content += "

Charger protocol: "; + content += charger->name(); + content += "

"; + } + + // Close the block + content += "
"; } if (battery) { - content += "

Battery protocol: "; - content += datalayer.system.info.battery_protocol; if (battery2) { - content += " (Double battery)"; - } - if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { - content += " (LFP)"; - } - content += "

"; - } - - if (shunt) { - content += "

Shunt protocol: "; - content += datalayer.system.info.shunt_protocol; - content += "

"; - } - - if (charger) { - content += "

Charger protocol: "; - content += charger->name(); - content += "

"; - } - - // Close the block - content += "
"; - - if (battery2) { - // Start a new block with a specific background color. Color changes depending on BMS status - content += "
"; - content += "
(datalayer.battery.status.real_soc) / 100.0; // Convert to float and divide by 100 - float socScaledFloat = - static_cast(datalayer.battery.status.reported_soc) / 100.0; // Convert to float and divide by 100 - float sohFloat = - static_cast(datalayer.battery.status.soh_pptt) / 100.0; // Convert to float and divide by 100 - float voltageFloat = - static_cast(datalayer.battery.status.voltage_dV) / 10.0; // Convert to float and divide by 10 - float currentFloat = - static_cast(datalayer.battery.status.current_dA) / 10.0; // Convert to float and divide by 10 - float powerFloat = static_cast(datalayer.battery.status.active_power_W); // Convert to float - float tempMaxFloat = static_cast(datalayer.battery.status.temperature_max_dC) / 10.0; // Convert to float - float tempMinFloat = static_cast(datalayer.battery.status.temperature_min_dC) / 10.0; // Convert to float - float maxCurrentChargeFloat = - static_cast(datalayer.battery.status.max_charge_current_dA) / 10.0; // Convert to float - float maxCurrentDischargeFloat = - static_cast(datalayer.battery.status.max_discharge_current_dA) / 10.0; // Convert to float - uint16_t cell_delta_mv = - datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV; - - if (datalayer.battery.settings.soc_scaling_active) - content += "

Scaled SOC: " + String(socScaledFloat, 2) + - "% (real: " + String(socRealFloat, 2) + "%)

"; - else - content += "

SOC: " + String(socRealFloat, 2) + "%

"; - - content += "

SOH: " + String(sohFloat, 2) + "%

"; - content += "

Voltage: " + String(voltageFloat, 1) + - " V   Current: " + String(currentFloat, 1) + " A

"; - content += formatPowerValue("Power", powerFloat, "", 1); - - if (datalayer.battery.settings.soc_scaling_active) - content += "

Scaled total capacity: " + - formatPowerValue(datalayer.battery.info.reported_total_capacity_Wh, "h", 1) + - " (real: " + formatPowerValue(datalayer.battery.info.total_capacity_Wh, "h", 1) + ")

"; - else - content += formatPowerValue("Total capacity", datalayer.battery.info.total_capacity_Wh, "h", 1); - - if (datalayer.battery.settings.soc_scaling_active) - content += "

Scaled remaining capacity: " + - formatPowerValue(datalayer.battery.status.reported_remaining_capacity_Wh, "h", 1) + - " (real: " + formatPowerValue(datalayer.battery.status.remaining_capacity_Wh, "h", 1) + ")

"; - else - content += formatPowerValue("Remaining capacity", datalayer.battery.status.remaining_capacity_Wh, "h", 1); - - if (datalayer.system.settings.equipment_stop_active) { - content += formatPowerValue("Max discharge power", datalayer.battery.status.max_discharge_power_W, "", 1, "red"); - content += formatPowerValue("Max charge power", datalayer.battery.status.max_charge_power_W, "", 1, "red"); - content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; - content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; - } else { - content += formatPowerValue("Max discharge power", datalayer.battery.status.max_discharge_power_W, "", 1); - content += formatPowerValue("Max charge power", datalayer.battery.status.max_charge_power_W, "", 1); - content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A"; - if (datalayer.battery.settings.user_settings_limit_discharge) { - content += " (Manual)

"; + // Start a new block with a specific background color. Color changes depending on BMS status + content += "
"; + content += "
"; - if (cell_delta_mv > datalayer.battery.info.max_cell_voltage_deviation_mV) { - content += "

Cell delta: " + String(cell_delta_mv) + " mV

"; - } else { - content += "

Cell delta: " + String(cell_delta_mv) + " mV

"; - } - content += - "

Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) + " °C

"; - - content += "

System status: "; - switch (datalayer.battery.status.bms_status) { - case ACTIVE: - content += String("OK"); - break; - case UPDATING: - content += String("UPDATING"); - break; - case FAULT: - content += String("FAULT"); - break; - case INACTIVE: - content += String("INACTIVE"); - break; - case STANDBY: - content += String("STANDBY"); - break; - default: - content += String("??"); - break; - } - content += "

"; - - if (battery && battery->supports_real_BMS_status()) { - content += "

Battery BMS status: "; - switch (datalayer.battery.status.real_bms_status) { - case BMS_ACTIVE: - content += String("OK"); - break; - case BMS_FAULT: - content += String("FAULT"); - break; - case BMS_DISCONNECTED: - content += String("DISCONNECTED"); - break; - case BMS_STANDBY: - content += String("STANDBY"); - break; - default: - content += String("??"); - break; - } - content += "

"; - } - - if (datalayer.battery.status.current_dA == 0) { - content += "

Battery idle

"; - } else if (datalayer.battery.status.current_dA < 0) { - content += "

Battery discharging!"; - if (datalayer.battery.settings.inverter_limits_discharge) { - content += " (Inverter limiting)

"; - } else { - if (datalayer.battery.settings.user_settings_limit_discharge) { - content += " (Settings limiting)

"; - } else { - content += " (Battery limiting)"; - } - } - content += ""; - } else { // > 0 , positive current - content += "

Battery charging!"; - if (datalayer.battery.settings.inverter_limits_charge) { - content += " (Inverter limiting)

"; - } else { - if (datalayer.battery.settings.user_settings_limit_charge) { - content += " (Settings limiting)"; - } else { - content += " (Battery limiting)"; - } - } - } - - content += "

Battery allows contactor closing: "; - if (datalayer.system.status.battery_allows_contactor_closing == true) { - content += ""; - } else { - content += ""; - } - - content += " Inverter allows contactor closing: "; - if (datalayer.system.status.inverter_allows_contactor_closing == true) { - content += "

"; - } else { - content += ""; - } - if (emulator_pause_status == NORMAL) - content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; - else - content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; - -#ifdef CONTACTOR_CONTROL - content += "

Contactors controlled by emulator, state: "; - if (datalayer.system.status.contactors_engaged) { - content += "ON"; - } else { - content += "OFF"; - } - content += "

"; - - content += "

Precharge: ("; - content += PRECHARGE_TIME_MS; - content += " ms) Cont. Neg.: "; -#ifdef PWM_CONTACTOR_CONTROL - if (datalayer.system.status.contactors_engaged) { - content += "Economized"; - content += " Cont. Pos.: "; - content += "Economized"; - } else { - content += ""; - content += " Cont. Pos.: "; - content += ""; - } - -#else // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded - if (digitalRead(NEGATIVE_CONTACTOR_PIN) == HIGH) { - content += ""; - } else { - content += ""; - } - - content += " Cont. Pos.: "; - if (digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH) { - content += ""; - } else { - content += ""; - } -#endif //no PWM_CONTACTOR_CONTROL - content += "

"; -#endif - - // Close the block - content += ""; - - if (battery2) { - content += "
"; // Display battery statistics within this block - socRealFloat = - static_cast(datalayer.battery2.status.real_soc) / 100.0; // Convert to float and divide by 100 - //socScaledFloat; // Same value used for bat2 - sohFloat = static_cast(datalayer.battery2.status.soh_pptt) / 100.0; // Convert to float and divide by 100 - voltageFloat = - static_cast(datalayer.battery2.status.voltage_dV) / 10.0; // Convert to float and divide by 10 - currentFloat = - static_cast(datalayer.battery2.status.current_dA) / 10.0; // Convert to float and divide by 10 - powerFloat = static_cast(datalayer.battery2.status.active_power_W); // Convert to float - tempMaxFloat = static_cast(datalayer.battery2.status.temperature_max_dC) / 10.0; // Convert to float - tempMinFloat = static_cast(datalayer.battery2.status.temperature_min_dC) / 10.0; // Convert to float - cell_delta_mv = datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV; + float socRealFloat = + static_cast(datalayer.battery.status.real_soc) / 100.0; // Convert to float and divide by 100 + float socScaledFloat = + static_cast(datalayer.battery.status.reported_soc) / 100.0; // Convert to float and divide by 100 + float sohFloat = + static_cast(datalayer.battery.status.soh_pptt) / 100.0; // Convert to float and divide by 100 + float voltageFloat = + static_cast(datalayer.battery.status.voltage_dV) / 10.0; // Convert to float and divide by 10 + float currentFloat = + static_cast(datalayer.battery.status.current_dA) / 10.0; // Convert to float and divide by 10 + float powerFloat = static_cast(datalayer.battery.status.active_power_W); // Convert to float + float tempMaxFloat = static_cast(datalayer.battery.status.temperature_max_dC) / 10.0; // Convert to float + float tempMinFloat = static_cast(datalayer.battery.status.temperature_min_dC) / 10.0; // Convert to float + float maxCurrentChargeFloat = + static_cast(datalayer.battery.status.max_charge_current_dA) / 10.0; // Convert to float + float maxCurrentDischargeFloat = + static_cast(datalayer.battery.status.max_discharge_current_dA) / 10.0; // Convert to float + uint16_t cell_delta_mv = + datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV; if (datalayer.battery.settings.soc_scaling_active) content += "

Scaled SOC: " + String(socScaledFloat, 2) + @@ -1303,117 +1093,342 @@ String processor(const String& var) { if (datalayer.battery.settings.soc_scaling_active) content += "

Scaled total capacity: " + - formatPowerValue(datalayer.battery2.info.reported_total_capacity_Wh, "h", 1) + - " (real: " + formatPowerValue(datalayer.battery2.info.total_capacity_Wh, "h", 1) + ")

"; + formatPowerValue(datalayer.battery.info.reported_total_capacity_Wh, "h", 1) + + " (real: " + formatPowerValue(datalayer.battery.info.total_capacity_Wh, "h", 1) + ")"; else - content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 1); + content += formatPowerValue("Total capacity", datalayer.battery.info.total_capacity_Wh, "h", 1); if (datalayer.battery.settings.soc_scaling_active) content += "

Scaled remaining capacity: " + - formatPowerValue(datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1) + - " (real: " + formatPowerValue(datalayer.battery2.status.remaining_capacity_Wh, "h", 1) + ")

"; + formatPowerValue(datalayer.battery.status.reported_remaining_capacity_Wh, "h", 1) + + " (real: " + formatPowerValue(datalayer.battery.status.remaining_capacity_Wh, "h", 1) + ")"; else - content += formatPowerValue("Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1); + content += formatPowerValue("Remaining capacity", datalayer.battery.status.remaining_capacity_Wh, "h", 1); if (datalayer.system.settings.equipment_stop_active) { content += - formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1, "red"); - content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1, "red"); + formatPowerValue("Max discharge power", datalayer.battery.status.max_discharge_power_W, "", 1, "red"); + content += formatPowerValue("Max charge power", datalayer.battery.status.max_charge_power_W, "", 1, "red"); content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; } else { - content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1); - content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1); - content += - "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; - content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; + content += formatPowerValue("Max discharge power", datalayer.battery.status.max_discharge_power_W, "", 1); + content += formatPowerValue("Max charge power", datalayer.battery.status.max_charge_power_W, "", 1); + content += "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A"; + if (datalayer.battery.settings.user_settings_limit_discharge) { + content += " (Manual)

"; + } else { + content += " (BMS)"; + } + content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A"; + if (datalayer.battery.settings.user_settings_limit_charge) { + content += " (Manual)

"; + } else { + content += " (BMS)"; + } } - content += "

Cell min/max: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV / " + - String(datalayer.battery2.status.cell_max_voltage_mV) + " mV

"; - if (cell_delta_mv > datalayer.battery2.info.max_cell_voltage_deviation_mV) { + content += "

Cell min/max: " + String(datalayer.battery.status.cell_min_voltage_mV) + " mV / " + + String(datalayer.battery.status.cell_max_voltage_mV) + " mV

"; + if (cell_delta_mv > datalayer.battery.info.max_cell_voltage_deviation_mV) { content += "

Cell delta: " + String(cell_delta_mv) + " mV

"; } else { content += "

Cell delta: " + String(cell_delta_mv) + " mV

"; } content += "

Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) + " °C

"; - if (datalayer.battery.status.bms_status == ACTIVE) { - content += "

System status: OK

"; - } else if (datalayer.battery.status.bms_status == UPDATING) { - content += "

System status: UPDATING

"; - } else { - content += "

System status: FAULT

"; + + content += "

System status: "; + switch (datalayer.battery.status.bms_status) { + case ACTIVE: + content += String("OK"); + break; + case UPDATING: + content += String("UPDATING"); + break; + case FAULT: + content += String("FAULT"); + break; + case INACTIVE: + content += String("INACTIVE"); + break; + case STANDBY: + content += String("STANDBY"); + break; + default: + content += String("??"); + break; } - if (datalayer.battery2.status.current_dA == 0) { - content += "

Battery idle

"; - } else if (datalayer.battery2.status.current_dA < 0) { - content += "

Battery discharging!

"; - } else { // > 0 - content += "

Battery charging!

"; + content += ""; + + if (battery && battery->supports_real_BMS_status()) { + content += "

Battery BMS status: "; + switch (datalayer.battery.status.real_bms_status) { + case BMS_ACTIVE: + content += String("OK"); + break; + case BMS_FAULT: + content += String("FAULT"); + break; + case BMS_DISCONNECTED: + content += String("DISCONNECTED"); + break; + case BMS_STANDBY: + content += String("STANDBY"); + break; + default: + content += String("??"); + break; + } + content += "

"; } - content += "

Automatic contactor closing allowed:

"; - content += "

Battery: "; - if (datalayer.system.status.battery2_allowed_contactor_closing == true) { + if (datalayer.battery.status.current_dA == 0) { + content += "

Battery idle

"; + } else if (datalayer.battery.status.current_dA < 0) { + content += "

Battery discharging!"; + if (datalayer.battery.settings.inverter_limits_discharge) { + content += " (Inverter limiting)

"; + } else { + if (datalayer.battery.settings.user_settings_limit_discharge) { + content += " (Settings limiting)"; + } else { + content += " (Battery limiting)"; + } + } + content += ""; + } else { // > 0 , positive current + content += "

Battery charging!"; + if (datalayer.battery.settings.inverter_limits_charge) { + content += " (Inverter limiting)

"; + } else { + if (datalayer.battery.settings.user_settings_limit_charge) { + content += " (Settings limiting)"; + } else { + content += " (Battery limiting)"; + } + } + } + + content += "

Battery allows contactor closing: "; + if (datalayer.system.status.battery_allows_contactor_closing == true) { content += ""; } else { content += ""; } - content += " Inverter: "; + content += " Inverter allows contactor closing: "; if (datalayer.system.status.inverter_allows_contactor_closing == true) { content += "

"; } else { content += ""; } - if (emulator_pause_status == NORMAL) content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; else content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; -#ifdef CONTACTOR_CONTROL - content += "

Contactors controlled by emulator, state: "; - if (datalayer.system.status.contactors_battery2_engaged) { - content += "ON"; - } else { - content += "OFF"; - } - content += "

"; -#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY - content += "

Cont. Neg.: "; -#ifdef PWM_CONTACTOR_CONTROL - if (datalayer.system.status.contactors_battery2_engaged) { - content += "Economized"; - content += " Cont. Pos.: "; - content += "Economized"; - } else { - content += ""; - content += " Cont. Pos.: "; - content += ""; + if (contactor_control_enabled) { + content += "

Contactors controlled by emulator, state: "; + if (datalayer.system.status.contactors_engaged) { + content += "ON"; + } else { + content += "OFF"; + } + content += "

"; + + content += "

Precharge: ("; + content += PRECHARGE_TIME_MS; + content += " ms) Cont. Neg.: "; + + if (pwm_contactor_control) { + if (datalayer.system.status.contactors_engaged) { + content += "Economized"; + content += " Cont. Pos.: "; + content += "Economized"; + } else { + content += ""; + content += " Cont. Pos.: "; + content += ""; + } + } else { // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded + if (digitalRead(NEGATIVE_CONTACTOR_PIN) == HIGH) { + content += ""; + } else { + content += ""; + } + + content += " Cont. Pos.: "; + if (digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH) { + content += ""; + } else { + content += ""; + } + } + content += "

"; } -#else // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded - if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) { - content += ""; - } else { - content += ""; - } - - content += " Cont. Pos.: "; - if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) { - content += ""; - } else { - content += ""; - } -#endif //no PWM_CONTACTOR_CONTROL - content += ""; -#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY -#endif // CONTACTOR_CONTROL - - content += "
"; + // Close the block content += ""; + + if (battery2) { + content += "
"; + + // Display battery statistics within this block + socRealFloat = + static_cast(datalayer.battery2.status.real_soc) / 100.0; // Convert to float and divide by 100 + //socScaledFloat; // Same value used for bat2 + sohFloat = + static_cast(datalayer.battery2.status.soh_pptt) / 100.0; // Convert to float and divide by 100 + voltageFloat = + static_cast(datalayer.battery2.status.voltage_dV) / 10.0; // Convert to float and divide by 10 + currentFloat = + static_cast(datalayer.battery2.status.current_dA) / 10.0; // Convert to float and divide by 10 + powerFloat = static_cast(datalayer.battery2.status.active_power_W); // Convert to float + tempMaxFloat = static_cast(datalayer.battery2.status.temperature_max_dC) / 10.0; // Convert to float + tempMinFloat = static_cast(datalayer.battery2.status.temperature_min_dC) / 10.0; // Convert to float + cell_delta_mv = datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV; + + if (datalayer.battery.settings.soc_scaling_active) + content += "

Scaled SOC: " + String(socScaledFloat, 2) + + "% (real: " + String(socRealFloat, 2) + "%)

"; + else + content += "

SOC: " + String(socRealFloat, 2) + "%

"; + + content += "

SOH: " + String(sohFloat, 2) + "%

"; + content += "

Voltage: " + String(voltageFloat, 1) + + " V   Current: " + String(currentFloat, 1) + " A

"; + content += formatPowerValue("Power", powerFloat, "", 1); + + if (datalayer.battery.settings.soc_scaling_active) + content += "

Scaled total capacity: " + + formatPowerValue(datalayer.battery2.info.reported_total_capacity_Wh, "h", 1) + + " (real: " + formatPowerValue(datalayer.battery2.info.total_capacity_Wh, "h", 1) + ")

"; + else + content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 1); + + if (datalayer.battery.settings.soc_scaling_active) + content += "

Scaled remaining capacity: " + + formatPowerValue(datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1) + + " (real: " + formatPowerValue(datalayer.battery2.status.remaining_capacity_Wh, "h", 1) + ")

"; + else + content += formatPowerValue("Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1); + + if (datalayer.system.settings.equipment_stop_active) { + content += + formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1, "red"); + content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1, "red"); + content += + "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; + content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; + } else { + content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1); + content += formatPowerValue("Max charge power", datalayer.battery2.status.max_charge_power_W, "", 1); + content += + "

Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A

"; + content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; + } + + content += "

Cell min/max: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV / " + + String(datalayer.battery2.status.cell_max_voltage_mV) + " mV

"; + if (cell_delta_mv > datalayer.battery2.info.max_cell_voltage_deviation_mV) { + content += "

Cell delta: " + String(cell_delta_mv) + " mV

"; + } else { + content += "

Cell delta: " + String(cell_delta_mv) + " mV

"; + } + content += "

Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) + + " °C

"; + if (datalayer.battery.status.bms_status == ACTIVE) { + content += "

System status: OK

"; + } else if (datalayer.battery.status.bms_status == UPDATING) { + content += "

System status: UPDATING

"; + } else { + content += "

System status: FAULT

"; + } + if (datalayer.battery2.status.current_dA == 0) { + content += "

Battery idle

"; + } else if (datalayer.battery2.status.current_dA < 0) { + content += "

Battery discharging!

"; + } else { // > 0 + content += "

Battery charging!

"; + } + + content += "

Automatic contactor closing allowed:

"; + content += "

Battery: "; + if (datalayer.system.status.battery2_allowed_contactor_closing == true) { + content += ""; + } else { + content += ""; + } + + content += " Inverter: "; + if (datalayer.system.status.inverter_allows_contactor_closing == true) { + content += "

"; + } else { + content += ""; + } + + if (emulator_pause_status == NORMAL) + content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; + else + content += "

Power status: " + String(get_emulator_pause_status().c_str()) + "

"; + + if (contactor_control_enabled) { + content += "

Contactors controlled by emulator, state: "; + if (datalayer.system.status.contactors_battery2_engaged) { + content += "ON"; + } else { + content += "OFF"; + } + content += "

"; + + if (contactor_control_enabled_double_battery) { + content += "

Cont. Neg.: "; + if (pwm_contactor_control) { + if (datalayer.system.status.contactors_battery2_engaged) { + content += "Economized"; + content += " Cont. Pos.: "; + content += "Economized"; + } else { + content += ""; + content += " Cont. Pos.: "; + content += ""; + } + } else { // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded +#if defined(SECOND_POSITIVE_CONTACTOR_PIN) && defined(SECOND_NEGATIVE_CONTACTOR_PIN) + if (digitalRead(SECOND_NEGATIVE_CONTACTOR_PIN) == HIGH) { + content += ""; + } else { + content += ""; + } + + content += " Cont. Pos.: "; + if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) { + content += ""; + } else { + content += ""; + } +#endif + } + content += "

"; + } + } + content += "
"; + content += ""; + } } if (charger) { From 57cb1186b8a2cc8de13d0a8b812cdace61ef7968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 17 Jun 2025 23:21:27 +0300 Subject: [PATCH 14/32] Update MinimumDischargeVoltage handling --- Software/src/battery/CHADEMO-BATTERY.cpp | 4 ++++ Software/src/battery/CHADEMO-BATTERY.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index a4ba76c8..0b6339d2 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -231,6 +231,10 @@ void ChademoBattery::process_vehicle_charging_limits(CAN_frame rx_frame) { if (get_measured_voltage() <= x200_discharge_limits.MinimumDischargeVoltage && CHADEMO_Status > CHADEMO_NEGOTIATE) { #ifdef DEBUG_LOG 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 CHADEMO_Status = CHADEMO_STOP; } diff --git a/Software/src/battery/CHADEMO-BATTERY.h b/Software/src/battery/CHADEMO-BATTERY.h index 29f1b3c6..5f19df83 100644 --- a/Software/src/battery/CHADEMO-BATTERY.h +++ b/Software/src/battery/CHADEMO-BATTERY.h @@ -180,7 +180,7 @@ uint8_t CHADEMO_seq = 0x0; //H200 - Vehicle - Discharge limits struct x200_Vehicle_Discharge_Limits { 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 MaxRemainingCapacityForCharging = 0; }; From 11280bc1ef67ed9f0c585a2bb04ae54e81ef4a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Tue, 17 Jun 2025 23:22:45 +0300 Subject: [PATCH 15/32] Update note on state --- Software/src/battery/CHADEMO-BATTERY.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index 0b6339d2..833989cb 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -204,7 +204,7 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) { } #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 return; } From 0886b0993d8d78aa97dd0864ab0da5dfc4d55350 Mon Sep 17 00:00:00 2001 From: freddanastrom <31804858+freddanastrom@users.noreply.github.com> Date: Wed, 18 Jun 2025 14:35:36 +0200 Subject: [PATCH 16/32] Always show estimated SOC on More info page regardless of SOC method (#1246) --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 17 +++++++++-------- Software/src/battery/BYD-ATTO-3-BATTERY.h | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index 540b0cf0..d8893204 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -151,6 +151,13 @@ void BydAttoBattery:: datalayer_battery->status.voltage_dV = BMS_voltage * 10; } + if (battery_type == EXTENDED_RANGE) { + battery_estimated_SOC = estimateSOCextended(datalayer_battery->status.voltage_dV); + } + if (battery_type == STANDARD_RANGE) { + battery_estimated_SOC = estimateSOCstandard(datalayer_battery->status.voltage_dV); + } + if (SOC_method == MEASURED) { // Pack is not crashed, we can use periodically transmitted SOC datalayer_battery->status.real_soc = battery_highprecision_SOC * 10; @@ -158,12 +165,7 @@ void BydAttoBattery:: // When the battery is crashed hard, it locks itself and SOC becomes unavailable. // We instead estimate the SOC% based on the battery voltage. // This is a bad solution, you wont be able to use 100% of the battery - if (battery_type == EXTENDED_RANGE) { - datalayer_battery->status.real_soc = estimateSOCextended(datalayer_battery->status.voltage_dV); - } - if (battery_type == STANDARD_RANGE) { - datalayer_battery->status.real_soc = estimateSOCstandard(datalayer_battery->status.voltage_dV); - } + datalayer_battery->status.real_soc = battery_estimated_SOC; } datalayer_battery->status.current_dA = -BMS_current; @@ -255,8 +257,7 @@ void BydAttoBattery:: // Update webserver datalayer if (datalayer_bydatto) { datalayer_bydatto->SOC_method = SOC_method; - datalayer_bydatto->SOC_estimated = datalayer_battery->status.real_soc; - //Once we implement switching logic, remember to change from where the estimated is taken + datalayer_bydatto->SOC_estimated = battery_estimated_SOC; datalayer_bydatto->SOC_highprec = battery_highprecision_SOC; datalayer_bydatto->SOC_polled = BMS_SOC; datalayer_bydatto->voltage_periodic = battery_voltage; diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.h b/Software/src/battery/BYD-ATTO-3-BATTERY.h index 84baa4d5..a5aa9043 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.h +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.h @@ -100,6 +100,7 @@ class BydAttoBattery : public CanBattery { int16_t battery_calc_min_temperature = 0; int16_t battery_calc_max_temperature = 0; uint16_t battery_highprecision_SOC = 0; + uint16_t battery_estimated_SOC = 0; uint16_t BMS_SOC = 0; uint16_t BMS_voltage = 0; int16_t BMS_current = 0; From 9c036915509055eab6893edc802d81b051c42811 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Wed, 18 Jun 2025 18:58:32 +0300 Subject: [PATCH 17/32] Fix battery and inverter selection in UI --- Software/src/battery/BATTERIES.cpp | 2 +- Software/src/battery/Battery.h | 2 +- Software/src/charger/CHARGERS.cpp | 2 +- Software/src/charger/CanCharger.h | 2 +- .../src/devboard/webserver/settings_html.cpp | 97 +++++++------ Software/src/devboard/webserver/webserver.cpp | 37 ++++- Software/src/inverter/AFORE-CAN.cpp | 2 +- Software/src/inverter/AFORE-CAN.h | 1 + Software/src/inverter/FOXESS-CAN.cpp | 2 +- Software/src/inverter/FOXESS-CAN.h | 1 + Software/src/inverter/GROWATT-HV-CAN.cpp | 2 +- Software/src/inverter/GROWATT-HV-CAN.h | 1 + Software/src/inverter/GROWATT-LV-CAN.cpp | 2 +- Software/src/inverter/GROWATT-LV-CAN.h | 1 + Software/src/inverter/INVERTERS.cpp | 135 ++++++++++++++++-- Software/src/inverter/InverterProtocol.h | 25 +++- Software/src/inverter/KOSTAL-RS485.cpp | 2 +- Software/src/inverter/KOSTAL-RS485.h | 1 + Software/src/inverter/PYLON-CAN.cpp | 2 +- Software/src/inverter/PYLON-CAN.h | 1 + Software/src/inverter/PYLON-LV-CAN.cpp | 2 +- Software/src/inverter/PYLON-LV-CAN.h | 1 + Software/src/inverter/SCHNEIDER-CAN.cpp | 2 +- Software/src/inverter/SCHNEIDER-CAN.h | 1 + Software/src/inverter/SMA-BYD-H-CAN.cpp | 2 +- Software/src/inverter/SMA-BYD-H-CAN.h | 1 + Software/src/inverter/SMA-BYD-HVS-CAN.cpp | 2 +- Software/src/inverter/SMA-BYD-HVS-CAN.h | 1 + Software/src/inverter/SMA-LV-CAN.cpp | 2 +- Software/src/inverter/SMA-LV-CAN.h | 1 + Software/src/inverter/SMA-TRIPOWER-CAN.cpp | 2 +- Software/src/inverter/SMA-TRIPOWER-CAN.h | 1 + Software/src/inverter/SOFAR-CAN.cpp | 2 +- Software/src/inverter/SOFAR-CAN.h | 1 + Software/src/inverter/SOLAX-CAN.cpp | 2 +- Software/src/inverter/SOLAX-CAN.h | 1 + Software/src/inverter/SUNGROW-CAN.cpp | 2 +- Software/src/inverter/SUNGROW-CAN.h | 1 + 38 files changed, 260 insertions(+), 87 deletions(-) diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index 89d5f89c..e7f4bf12 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -17,7 +17,7 @@ std::vector supported_battery_types() { return types; } -extern const char* name_for_type(BatteryType type) { +extern const char* name_for_battery_type(BatteryType type) { switch (type) { case BatteryType::None: return "None"; diff --git a/Software/src/battery/Battery.h b/Software/src/battery/Battery.h index 61a798d6..187b29d0 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -46,7 +46,7 @@ enum class BatteryType { }; extern std::vector supported_battery_types(); -extern const char* name_for_type(BatteryType type); +extern const char* name_for_battery_type(BatteryType type); extern BatteryType user_selected_battery_type; extern bool user_selected_second_battery; diff --git a/Software/src/charger/CHARGERS.cpp b/Software/src/charger/CHARGERS.cpp index b0aee330..6ba343b9 100644 --- a/Software/src/charger/CHARGERS.cpp +++ b/Software/src/charger/CHARGERS.cpp @@ -14,7 +14,7 @@ std::vector supported_charger_types() { return types; } -extern const char* name_for_type(ChargerType type) { +extern const char* name_for_charger_type(ChargerType type) { switch (type) { case ChargerType::ChevyVolt: return ChevyVoltCharger::Name; diff --git a/Software/src/charger/CanCharger.h b/Software/src/charger/CanCharger.h index f8586740..5f8efed4 100644 --- a/Software/src/charger/CanCharger.h +++ b/Software/src/charger/CanCharger.h @@ -12,7 +12,7 @@ enum class ChargerType { None, NissanLeaf, ChevyVolt, Highest }; extern ChargerType user_selected_charger_type; extern std::vector supported_charger_types(); -extern const char* name_for_type(ChargerType type); +extern const char* name_for_charger_type(ChargerType type); // Generic base class for all chargers class Charger { diff --git a/Software/src/devboard/webserver/settings_html.cpp b/Software/src/devboard/webserver/settings_html.cpp index da08a3fd..241e4e3c 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -8,61 +8,56 @@ extern bool settingsUpdated; -#ifdef COMMON_IMAGE -String battery_options(BatteryType selected) { - String options; +template +constexpr auto to_underlying(E e) noexcept { + return static_cast>(e); +} - auto batteries = supported_battery_types(); - for (BatteryType type : batteries) { +template +std::vector enum_values() { + static_assert(std::is_enum_v, "Template argument must be an enum type."); + + constexpr auto count = to_underlying(EnumType::Highest); + std::vector values; + for (int i = 1; i < count; ++i) { + values.push_back(static_cast(i)); + } + return values; +} + +template +std::vector> enum_values_and_names(Func name_for_type) { + auto values = enum_values(); + + std::vector> pairs; + + for (auto& type : values) { auto name = name_for_type(type); if (name != nullptr) { - options += - (""; + pairs.push_back(std::pair(String(name), type)); } } - return options; + std::sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); + + pairs.insert(pairs.begin(), std::pair(name_for_type(EnumType::None), EnumType::None)); + + return pairs; } -String inverter_options(InverterProtocolType selected) { +template +String options_for_enum(TEnum selected, Func name_for_type) { String options; - - auto inverters = supported_inverter_protocols(); - - for (InverterProtocolType type : inverters) { - auto name = name_for_type(type); - if (name != nullptr) { - options += - (""; - } + auto values = enum_values_and_names(name_for_type); + for (const auto& [name, type] : values) { + options += + (""; } - return options; } -String charger_options(ChargerType selected) { - String options; - - auto chargers = supported_charger_types(); - - for (ChargerType type : chargers) { - auto name = name_for_type(type); - if (name != nullptr) { - options += - (""; - } - } - - return options; -} -#endif - void render_checkbox(String& content, const char* label, bool enabled, const char* name) { content += ""; content += ""; content += ""; content += ""; content += ""; // TODO: Generalize settings: define settings in one place and use the definitions to render // UI and handle load/save - render_checkbox(content, "Double battery", settings.getBool("DBLBTR"), "dblbtr"); - render_checkbox(content, "Contactor control", settings.getBool("CNTCTRL"), "contctrl"); - render_checkbox(content, "Contactor control double battery", settings.getBool("CNTCTRLDBL"), "contctrldbl"); - render_checkbox(content, "PWM contactor control", settings.getBool("PWMCNTCTRL"), "pwmcontctrl"); + render_checkbox(content, "Double battery", settings.getBool("DBLBTR"), "DBLBTR"); + render_checkbox(content, "Contactor control", settings.getBool("CNTCTRL"), "CNTCTRL"); + render_checkbox(content, "Contactor control double battery", settings.getBool("CNTCTRLDBL"), "CNTCTRLDBL"); + render_checkbox(content, "PWM contactor control", settings.getBool("PWMCNTCTRL"), "PWMCNTCTRL"); render_checkbox(content, "Periodic BMS reset", settings.getBool("PERBMSRESET"), "PERBMSRESET"); render_checkbox(content, "Remote BMS reset", settings.getBool("REMBMSRESET"), "REMBMSRESET"); diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 9525b262..b0716f47 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1,6 +1,7 @@ #include "webserver.h" #include #include +#include #include "../../../USER_SECRETS.h" #include "../../battery/BATTERIES.h" #include "../../battery/Battery.h" @@ -387,13 +388,27 @@ void init_webserver() { request->send(200, "text/html", response); }); + struct BoolSetting { + const char* name; + bool existingValue; + bool newValue; + }; + + const char* boolSettingNames[] = {"DBLBTR", "CNTCTRL", "CNTCTRLDBL", "PWMCNTCTRL", "PERBMSRESET", "REMBMSRESET"}; + #ifdef COMMON_IMAGE // Handles the form POST from UI to save certain settings: battery/inverter type and double battery on/off - server.on("/saveSettings", HTTP_POST, [](AsyncWebServerRequest* request) { + server.on("/saveSettings", HTTP_POST, [boolSettingNames](AsyncWebServerRequest* request) { BatteryEmulatorSettingsStore settings; - int params = request->params(); - for (int i = 0; i < params; i++) { + std::vector boolSettings; + + for (auto& name : boolSettingNames) { + boolSettings.push_back({name, settings.getBool(name), false}); + } + + int numParams = request->params(); + for (int i = 0; i < numParams; i++) { auto p = request->getParam(i); if (p->name() == "inverter") { auto type = static_cast(atoi(p->value().c_str())); @@ -404,8 +419,8 @@ void init_webserver() { } else if (p->name() == "charger") { auto type = static_cast(atoi(p->value().c_str())); settings.saveUInt("CHGTYPE", (int)type); - } else if (p->name() == "dblbtr") { - settings.saveBool("DBLBTR", p->value() == "on"); + } /*else if (p->name() == "dblbtr") { + newDoubleBattery = p->value() == "on"; } else if (p->name() == "contctrl") { settings.saveBool("CNTCTRL", p->value() == "on"); } else if (p->name() == "contctrldbl") { @@ -416,6 +431,18 @@ void init_webserver() { settings.saveBool("PERBMSRESET", p->value() == "on"); } else if (p->name() == "REMBMSRESET") { settings.saveBool("REMBMSRESET", p->value() == "on"); + }*/ + + for (auto& boolSetting : boolSettings) { + if (p->name() == boolSetting.name) { + boolSetting.newValue = p->value() == "on"; + } + } + } + + for (auto& boolSetting : boolSettings) { + if (boolSetting.existingValue != boolSetting.newValue) { + settings.saveBool(boolSetting.name, boolSetting.newValue); } } diff --git a/Software/src/inverter/AFORE-CAN.cpp b/Software/src/inverter/AFORE-CAN.cpp index 8f2545a7..7cffecbe 100644 --- a/Software/src/inverter/AFORE-CAN.cpp +++ b/Software/src/inverter/AFORE-CAN.cpp @@ -171,6 +171,6 @@ void AforeCanInverter::transmit_can(unsigned long currentMillis) { } void AforeCanInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "Afore battery over CAN", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/AFORE-CAN.h b/Software/src/inverter/AFORE-CAN.h index 9f5cdd9d..1311fda2 100644 --- a/Software/src/inverter/AFORE-CAN.h +++ b/Software/src/inverter/AFORE-CAN.h @@ -14,6 +14,7 @@ class AforeCanInverter : public CanInverterProtocol { void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); void update_values(); + static constexpr char* Name = "Afore battery over CAN"; private: /* The code is following the Afore 2.3 CAN standard, little-endian, 500kbps, from 2023.08.07 */ diff --git a/Software/src/inverter/FOXESS-CAN.cpp b/Software/src/inverter/FOXESS-CAN.cpp index 5d9019d2..7fb0b96c 100644 --- a/Software/src/inverter/FOXESS-CAN.cpp +++ b/Software/src/inverter/FOXESS-CAN.cpp @@ -562,6 +562,6 @@ void FoxessCanInverter::map_can_frame_to_variable(CAN_frame rx_frame) { } } void FoxessCanInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "FoxESS compatible HV2600/ECS4100 battery", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/FOXESS-CAN.h b/Software/src/inverter/FOXESS-CAN.h index 454f79bb..6b240ca0 100644 --- a/Software/src/inverter/FOXESS-CAN.h +++ b/Software/src/inverter/FOXESS-CAN.h @@ -14,6 +14,7 @@ class FoxessCanInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "FoxESS compatible HV2600/ECS4100 battery"; private: int16_t temperature_average = 0; diff --git a/Software/src/inverter/GROWATT-HV-CAN.cpp b/Software/src/inverter/GROWATT-HV-CAN.cpp index b19543cd..7bfc0511 100644 --- a/Software/src/inverter/GROWATT-HV-CAN.cpp +++ b/Software/src/inverter/GROWATT-HV-CAN.cpp @@ -451,6 +451,6 @@ void GrowattHvInverter::transmit_can(unsigned long currentMillis) { } void GrowattHvInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "Growatt High Voltage protocol via CAN", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/GROWATT-HV-CAN.h b/Software/src/inverter/GROWATT-HV-CAN.h index 16658119..72ffe7dc 100644 --- a/Software/src/inverter/GROWATT-HV-CAN.h +++ b/Software/src/inverter/GROWATT-HV-CAN.h @@ -14,6 +14,7 @@ class GrowattHvInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "Growatt High Voltage protocol via CAN"; private: //Total number of Cells (1-512) diff --git a/Software/src/inverter/GROWATT-LV-CAN.cpp b/Software/src/inverter/GROWATT-LV-CAN.cpp index ca420583..4a5915fe 100644 --- a/Software/src/inverter/GROWATT-LV-CAN.cpp +++ b/Software/src/inverter/GROWATT-LV-CAN.cpp @@ -204,6 +204,6 @@ void GrowattLvInverter::transmit_can(unsigned long currentMillis) { } void GrowattLvInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "Growatt Low Voltage (48V) protocol via CAN", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/GROWATT-LV-CAN.h b/Software/src/inverter/GROWATT-LV-CAN.h index a3d52f5a..a2a49c6a 100644 --- a/Software/src/inverter/GROWATT-LV-CAN.h +++ b/Software/src/inverter/GROWATT-LV-CAN.h @@ -14,6 +14,7 @@ class GrowattLvInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "Growatt Low Voltage (48V) protocol via CAN"; private: //Actual content messages diff --git a/Software/src/inverter/INVERTERS.cpp b/Software/src/inverter/INVERTERS.cpp index cd9556d8..f242368f 100644 --- a/Software/src/inverter/INVERTERS.cpp +++ b/Software/src/inverter/INVERTERS.cpp @@ -14,22 +14,65 @@ std::vector supported_inverter_protocols() { return types; } -extern const char* name_for_type(InverterProtocolType type) { +extern const char* name_for_inverter_type(InverterProtocolType type) { switch (type) { - case InverterProtocolType::BydCan: - return BydCanInverter::Name; - break; - case InverterProtocolType::BydModbus: - return BydModbusInverter::Name; - break; - case InverterProtocolType::FerroampCan: - return FerroampCanInverter::Name; - break; case InverterProtocolType::None: return "None"; - break; - } + case InverterProtocolType::AforeCan: + return AforeCanInverter::Name; + + case InverterProtocolType::BydCan: + return BydCanInverter::Name; + + case InverterProtocolType::BydModbus: + return BydModbusInverter::Name; + + case InverterProtocolType::FerroampCan: + return FerroampCanInverter::Name; + + case InverterProtocolType::Foxess: + return FoxessCanInverter::Name; + + case InverterProtocolType::GrowattHv: + return GrowattHvInverter::Name; + + case InverterProtocolType::GrowattLv: + return GrowattLvInverter::Name; + + case InverterProtocolType::Kostal: + return KostalInverterProtocol::Name; + + case InverterProtocolType::Pylon: + return PylonInverter::Name; + + case InverterProtocolType::PylonLv: + return PylonLvInverter::Name; + + case InverterProtocolType::Schneider: + return SchneiderInverter::Name; + + case InverterProtocolType::SmaBydH: + return SmaBydHInverter::Name; + + case InverterProtocolType::SmaBydHvs: + return SmaBydHvsInverter::Name; + + case InverterProtocolType::SmaLv: + return SmaLvInverter::Name; + + case InverterProtocolType::SmaTripower: + return SmaTripowerInverter::Name; + + case InverterProtocolType::Sofar: + return SofarInverter::Name; + + case InverterProtocolType::Solax: + return SolaxInverter::Name; + + case InverterProtocolType::Sungrow: + return SungrowInverter::Name; + } return nullptr; } @@ -44,15 +87,83 @@ void setup_inverter() { } switch (user_selected_inverter_protocol) { + case InverterProtocolType::AforeCan: + inverter = new AforeCanInverter(); + break; + case InverterProtocolType::BydCan: inverter = new BydCanInverter(); break; + case InverterProtocolType::BydModbus: inverter = new BydModbusInverter(); break; + case InverterProtocolType::FerroampCan: inverter = new FerroampCanInverter(); break; + + case InverterProtocolType::Foxess: + inverter = new FoxessCanInverter(); + break; + + case InverterProtocolType::GrowattHv: + inverter = new GrowattHvInverter(); + break; + + case InverterProtocolType::GrowattLv: + inverter = new GrowattLvInverter(); + break; + + case InverterProtocolType::Kostal: + inverter = new KostalInverterProtocol(); + break; + + case InverterProtocolType::Pylon: + inverter = new PylonInverter(); + break; + + case InverterProtocolType::PylonLv: + inverter = new PylonLvInverter(); + break; + + case InverterProtocolType::Schneider: + inverter = new SchneiderInverter(); + break; + + case InverterProtocolType::SmaBydH: + inverter = new SmaBydHInverter(); + break; + + case InverterProtocolType::SmaBydHvs: + inverter = new SmaBydHvsInverter(); + break; + + case InverterProtocolType::SmaLv: + inverter = new SmaLvInverter(); + break; + + case InverterProtocolType::SmaTripower: + inverter = new SmaTripowerInverter(); + break; + + case InverterProtocolType::Sofar: + inverter = new SofarInverter(); + break; + + case InverterProtocolType::Solax: + inverter = new SolaxInverter(); + break; + + case InverterProtocolType::Sungrow: + inverter = new SungrowInverter(); + break; + + case InverterProtocolType::None: + case InverterProtocolType::Highest: + default: + inverter = nullptr; // Or handle as error + break; } if (inverter) { diff --git a/Software/src/inverter/InverterProtocol.h b/Software/src/inverter/InverterProtocol.h index 565e0581..bd786ff3 100644 --- a/Software/src/inverter/InverterProtocol.h +++ b/Software/src/inverter/InverterProtocol.h @@ -1,12 +1,33 @@ #ifndef INVERTER_PROTOCOL_H #define INVERTER_PROTOCOL_H -enum class InverterProtocolType { None = 0, BydCan, BydModbus, FerroampCan, Highest }; +enum class InverterProtocolType { + None = 0, + AforeCan, + BydCan, + BydModbus, + FerroampCan, + Foxess, + GrowattHv, + GrowattLv, + Kostal, + Pylon, + PylonLv, + Schneider, + SmaBydH, + SmaBydHvs, + SmaLv, + SmaTripower, + Sofar, + Solax, + Sungrow, + Highest +}; extern InverterProtocolType user_selected_inverter_protocol; extern std::vector supported_inverter_protocols(); -extern const char* name_for_type(InverterProtocolType type); +extern const char* name_for_inverter_type(InverterProtocolType type); enum class InverterInterfaceType { Can, Rs485, Modbus }; diff --git a/Software/src/inverter/KOSTAL-RS485.cpp b/Software/src/inverter/KOSTAL-RS485.cpp index 37ef14b6..94f2c910 100644 --- a/Software/src/inverter/KOSTAL-RS485.cpp +++ b/Software/src/inverter/KOSTAL-RS485.cpp @@ -304,7 +304,7 @@ void KostalInverterProtocol::receive() // Runs as fast as possible to handle th void KostalInverterProtocol::setup(void) { // Performs one time setup at startup datalayer.system.status.inverter_allows_contactor_closing = false; dbg_message("inverter_allows_contactor_closing -> false"); - strncpy(datalayer.system.info.inverter_protocol, "BYD battery via Kostal RS485", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; Serial2.begin(baud_rate(), SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN); diff --git a/Software/src/inverter/KOSTAL-RS485.h b/Software/src/inverter/KOSTAL-RS485.h index fdf748cb..44a25396 100644 --- a/Software/src/inverter/KOSTAL-RS485.h +++ b/Software/src/inverter/KOSTAL-RS485.h @@ -22,6 +22,7 @@ class KostalInverterProtocol : public Rs485InverterProtocol { void setup(); void receive(); void update_values(); + static constexpr char* Name = "BYD battery via Kostal RS485"; private: int baud_rate() { return 57600; } diff --git a/Software/src/inverter/PYLON-CAN.cpp b/Software/src/inverter/PYLON-CAN.cpp index de00361f..5de88ffa 100644 --- a/Software/src/inverter/PYLON-CAN.cpp +++ b/Software/src/inverter/PYLON-CAN.cpp @@ -354,6 +354,6 @@ void PylonInverter::send_system_data() { //System equipment information } void PylonInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "Pylontech battery over CAN bus", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/PYLON-CAN.h b/Software/src/inverter/PYLON-CAN.h index c744ab82..76079fce 100644 --- a/Software/src/inverter/PYLON-CAN.h +++ b/Software/src/inverter/PYLON-CAN.h @@ -14,6 +14,7 @@ class PylonInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "Pylontech battery over CAN bus"; private: void send_system_data(); diff --git a/Software/src/inverter/PYLON-LV-CAN.cpp b/Software/src/inverter/PYLON-LV-CAN.cpp index 3ce16cba..21c9aeac 100644 --- a/Software/src/inverter/PYLON-LV-CAN.cpp +++ b/Software/src/inverter/PYLON-LV-CAN.cpp @@ -146,6 +146,6 @@ void PylonLvInverter::transmit_can(unsigned long currentMillis) { } void PylonLvInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "Pylontech LV battery over CAN bus", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/PYLON-LV-CAN.h b/Software/src/inverter/PYLON-LV-CAN.h index b22c6b07..8ffcb784 100644 --- a/Software/src/inverter/PYLON-LV-CAN.h +++ b/Software/src/inverter/PYLON-LV-CAN.h @@ -14,6 +14,7 @@ class PylonLvInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "Pylontech LV battery over CAN bus"; private: void send_system_data(); diff --git a/Software/src/inverter/SCHNEIDER-CAN.cpp b/Software/src/inverter/SCHNEIDER-CAN.cpp index af869c7a..d0579b76 100644 --- a/Software/src/inverter/SCHNEIDER-CAN.cpp +++ b/Software/src/inverter/SCHNEIDER-CAN.cpp @@ -227,6 +227,6 @@ void SchneiderInverter::transmit_can(unsigned long currentMillis) { } void SchneiderInverter::setup(void) { // Performs one time setup - strncpy(datalayer.system.info.inverter_protocol, "Schneider V2 SE BMS CAN", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/SCHNEIDER-CAN.h b/Software/src/inverter/SCHNEIDER-CAN.h index ace28795..b9c02f8d 100644 --- a/Software/src/inverter/SCHNEIDER-CAN.h +++ b/Software/src/inverter/SCHNEIDER-CAN.h @@ -14,6 +14,7 @@ class SchneiderInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "Schneider V2 SE BMS CAN"; private: static const int STATE_OFFLINE = 0; diff --git a/Software/src/inverter/SMA-BYD-H-CAN.cpp b/Software/src/inverter/SMA-BYD-H-CAN.cpp index a0b5d2ba..6407ebbe 100644 --- a/Software/src/inverter/SMA-BYD-H-CAN.cpp +++ b/Software/src/inverter/SMA-BYD-H-CAN.cpp @@ -260,7 +260,7 @@ void SmaBydHInverter::transmit_can_init() { } void SmaBydHInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "BYD over SMA CAN", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first pinMode(INVERTER_CONTACTOR_ENABLE_PIN, INPUT); diff --git a/Software/src/inverter/SMA-BYD-H-CAN.h b/Software/src/inverter/SMA-BYD-H-CAN.h index 911a46cc..bf18265e 100644 --- a/Software/src/inverter/SMA-BYD-H-CAN.h +++ b/Software/src/inverter/SMA-BYD-H-CAN.h @@ -14,6 +14,7 @@ class SmaBydHInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "BYD over SMA CAN"; private: static const int READY_STATE = 0x03; diff --git a/Software/src/inverter/SMA-BYD-HVS-CAN.cpp b/Software/src/inverter/SMA-BYD-HVS-CAN.cpp index a14fbb42..bd9dd8d0 100644 --- a/Software/src/inverter/SMA-BYD-HVS-CAN.cpp +++ b/Software/src/inverter/SMA-BYD-HVS-CAN.cpp @@ -278,7 +278,7 @@ void SmaBydHvsInverter::transmit_can(unsigned long currentMillis) { } void SmaBydHvsInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "BYD Battery-Box HVS over SMA CAN", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first pinMode(INVERTER_CONTACTOR_ENABLE_PIN, INPUT); diff --git a/Software/src/inverter/SMA-BYD-HVS-CAN.h b/Software/src/inverter/SMA-BYD-HVS-CAN.h index ab29460d..8e761559 100644 --- a/Software/src/inverter/SMA-BYD-HVS-CAN.h +++ b/Software/src/inverter/SMA-BYD-HVS-CAN.h @@ -14,6 +14,7 @@ class SmaBydHvsInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "BYD Battery-Box HVS over SMA CAN"; private: static const int READY_STATE = 0x03; diff --git a/Software/src/inverter/SMA-LV-CAN.cpp b/Software/src/inverter/SMA-LV-CAN.cpp index cd206aaf..c78157bf 100644 --- a/Software/src/inverter/SMA-LV-CAN.cpp +++ b/Software/src/inverter/SMA-LV-CAN.cpp @@ -109,6 +109,6 @@ void SmaLvInverter::transmit_can(unsigned long currentMillis) { } void SmaLvInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "SMA Low Voltage (48V) protocol via CAN", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/SMA-LV-CAN.h b/Software/src/inverter/SMA-LV-CAN.h index c8ff13ed..4c62a32e 100644 --- a/Software/src/inverter/SMA-LV-CAN.h +++ b/Software/src/inverter/SMA-LV-CAN.h @@ -14,6 +14,7 @@ class SmaLvInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "SMA Low Voltage (48V) protocol via CAN"; private: static const int READY_STATE = 0x03; diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp index 7e031f63..f7dbeebf 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.cpp +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.cpp @@ -183,7 +183,7 @@ void SmaTripowerInverter::transmit_can_init() { } void SmaTripowerInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "SMA Tripower CAN", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first pinMode(INVERTER_CONTACTOR_ENABLE_PIN, INPUT); diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.h b/Software/src/inverter/SMA-TRIPOWER-CAN.h index dadb7493..fcd7f099 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.h +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.h @@ -14,6 +14,7 @@ class SmaTripowerInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "SMA Tripower CAN"; private: const int READY_STATE = 0x03; diff --git a/Software/src/inverter/SOFAR-CAN.cpp b/Software/src/inverter/SOFAR-CAN.cpp index ca8485a6..a4c6c1aa 100644 --- a/Software/src/inverter/SOFAR-CAN.cpp +++ b/Software/src/inverter/SOFAR-CAN.cpp @@ -68,6 +68,6 @@ void SofarInverter::transmit_can(unsigned long currentMillis) { } void SofarInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "Sofar BMS (Extended Frame) over CAN bus", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/SOFAR-CAN.h b/Software/src/inverter/SOFAR-CAN.h index 13ee83b4..678c79f0 100644 --- a/Software/src/inverter/SOFAR-CAN.h +++ b/Software/src/inverter/SOFAR-CAN.h @@ -14,6 +14,7 @@ class SofarInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "Sofar BMS (Extended Frame) over CAN bus"; private: unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp index b66fa3f3..187ec772 100644 --- a/Software/src/inverter/SOLAX-CAN.cpp +++ b/Software/src/inverter/SOLAX-CAN.cpp @@ -208,7 +208,7 @@ void SolaxInverter::map_can_frame_to_variable(CAN_frame rx_frame) { } void SolaxInverter::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.inverter_protocol, "SolaX Triple Power LFP over CAN bus", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; datalayer.system.status.inverter_allows_contactor_closing = false; // The inverter needs to allow first } diff --git a/Software/src/inverter/SOLAX-CAN.h b/Software/src/inverter/SOLAX-CAN.h index 169ca9c8..5b5cf672 100644 --- a/Software/src/inverter/SOLAX-CAN.h +++ b/Software/src/inverter/SOLAX-CAN.h @@ -14,6 +14,7 @@ class SolaxInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "SolaX Triple Power LFP over CAN bus"; private: // Timeout in milliseconds diff --git a/Software/src/inverter/SUNGROW-CAN.cpp b/Software/src/inverter/SUNGROW-CAN.cpp index 0331a4c7..40d2b8be 100644 --- a/Software/src/inverter/SUNGROW-CAN.cpp +++ b/Software/src/inverter/SUNGROW-CAN.cpp @@ -354,6 +354,6 @@ void SungrowInverter::transmit_can(unsigned long currentMillis) { } void SungrowInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "Sungrow SBR064 battery over CAN bus", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; } diff --git a/Software/src/inverter/SUNGROW-CAN.h b/Software/src/inverter/SUNGROW-CAN.h index 5947b9d3..4088759b 100644 --- a/Software/src/inverter/SUNGROW-CAN.h +++ b/Software/src/inverter/SUNGROW-CAN.h @@ -14,6 +14,7 @@ class SungrowInverter : public CanInverterProtocol { void update_values(); void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "Sungrow SBR064 battery over CAN bus"; private: unsigned long previousMillis500ms = 0; From a663e027f22932e5826f44e387d2ce9084533897 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Wed, 18 Jun 2025 19:12:58 +0300 Subject: [PATCH 18/32] Remove CAN definitions from user settings header --- Software/USER_SETTINGS.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index c17f7bc9..57b92625 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -2,6 +2,7 @@ #define __USER_SETTINGS_H__ #include #include +#include "src/devboard/utils/types.h" /* This file contains all the battery/inverter protocol settings Battery-Emulator software */ /* To switch between batteries/inverters, uncomment a line to enable, comment out to disable. */ @@ -173,7 +174,6 @@ /* Do not change any code below this line */ /* Only change battery specific settings above and in "USER_SETTINGS.cpp" */ -typedef enum { CAN_NATIVE = 0, CANFD_NATIVE = 1, CAN_ADDON_MCP2515 = 2, CANFD_ADDON_MCP2518 = 3 } CAN_Interface; typedef struct { CAN_Interface battery; CAN_Interface inverter; @@ -181,7 +181,6 @@ typedef struct { CAN_Interface charger; CAN_Interface shunt; } CAN_Configuration; -extern const char* getCANInterfaceName(CAN_Interface interface); extern volatile CAN_Configuration can_config; extern volatile uint8_t AccessPointEnabled; extern const uint8_t wifi_channel; From 435f201553c5a832b4325cddbee93ff21274985b Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Wed, 18 Jun 2025 19:33:29 +0300 Subject: [PATCH 19/32] Make battery selection check in cpp file to avoid header file conflicts --- Software/src/battery/BATTERIES.cpp | 4 ++++ Software/src/include.h | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index e7f4bf12..91384952 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -4,6 +4,10 @@ #include "CanBattery.h" #include "RS485Battery.h" +#if !defined(COMMON_IMAGE) && !defined(SELECTED_BATTERY_CLASS) +#error No battery selected! Choose one from the USER_SETTINGS.h file +#endif + Battery* battery = nullptr; Battery* battery2 = nullptr; diff --git a/Software/src/include.h b/Software/src/include.h index daacf9b0..6a72f9b4 100644 --- a/Software/src/include.h +++ b/Software/src/include.h @@ -39,10 +39,6 @@ #endif #endif -#if !defined(COMMON_IMAGE) && !defined(SELECTED_BATTERY_CLASS) -#error No battery selected! Choose one from the USER_SETTINGS.h file -#endif - #if defined(LOG_CAN_TO_SD) || defined(LOG_TO_SD) #if !defined(HW_LILYGO) #error The SD card logging feature is only available on LilyGo hardware From 742650268c3b01342b121f02e7ae243dca79b3ef Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Wed, 18 Jun 2025 20:27:01 +0300 Subject: [PATCH 20/32] More conditional compilation for Chademo and BMS --- Software/src/battery/BATTERIES.cpp | 4 ++++ Software/src/battery/CHADEMO-BATTERY.cpp | 4 ++++ .../contactorcontrol/comm_contactorcontrol.cpp | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index 91384952..185c7540 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -35,8 +35,10 @@ extern const char* name_for_battery_type(BatteryType type) { return BydAttoBattery::Name; case BatteryType::CellPowerBms: return CellPowerBms::Name; +#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms case BatteryType::Chademo: return ChademoBattery::Name; +#endif case BatteryType::CmfaEv: return CmfaEvBattery::Name; case BatteryType::Foxess: @@ -122,8 +124,10 @@ Battery* create_battery(BatteryType type) { return new BydAttoBattery(); case BatteryType::CellPowerBms: return new CellPowerBms(); +#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms case BatteryType::Chademo: return new ChademoBattery(); +#endif case BatteryType::CmfaEv: return new CmfaEvBattery(); case BatteryType::Foxess: diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index 8ea592fb..6adf68db 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -4,6 +4,8 @@ #include "../include.h" #include "CHADEMO-SHUNTS.h" +#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms + /* 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 @@ -987,3 +989,5 @@ void ChademoBattery::setup(void) { // Performs one time setup at startup setupMillis = millis(); } + +#endif diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index 08fee8f0..f04b2c1f 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -130,6 +130,7 @@ void init_contactors() { digitalWrite(BMS_2_POWER, HIGH); #endif //BMS_2_POWER #endif // HW with dedicated BMS pins +#ifdef BMS_POWER if (periodic_bms_reset || remote_bms_reset) { pinMode(BMS_POWER, OUTPUT); digitalWrite(BMS_POWER, HIGH); @@ -138,6 +139,7 @@ void init_contactors() { digitalWrite(BMS_2_POWER, HIGH); #endif //BMS_2_POWER } +#endif } static void dbg_contactors(const char* state) { @@ -156,7 +158,9 @@ void handle_contactors() { datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN); #endif +#ifdef BMS_POWER handle_BMSpower(); // Some batteries need to be periodically power cycled +#endif #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY handle_contactors_battery2(); @@ -281,6 +285,7 @@ This makes the BMS recalculate all SOC% and avoid memory leaks During that time we also set the emulator state to paused in order to not try and send CAN messages towards the battery Feature is only used if user has enabled PERIODIC_BMS_RESET in the USER_SETTINGS */ +#ifdef BMS_POWER void handle_BMSpower() { if (periodic_bms_reset || remote_bms_reset) { // Get current time @@ -314,6 +319,7 @@ void handle_BMSpower() { } } } +#endif void start_bms_reset() { if (periodic_bms_reset || remote_bms_reset) { @@ -329,7 +335,9 @@ void start_bms_reset() { // We try to keep contactors engaged during this pause, and just ramp power down to 0. setBatteryPause(true, false, false, false); +#ifdef BMS_POWER digitalWrite(BMS_POWER, LOW); // Remove power by setting the BMS power pin to LOW +#endif #ifdef BMS_2_POWER digitalWrite(BMS_2_POWER, LOW); // Same for battery 2 #endif From 2d30370777acb56ef5ddc4e666b0cf92b9b30d49 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Wed, 18 Jun 2025 20:52:01 +0300 Subject: [PATCH 21/32] Make conflicting globals static --- .../communication/contactorcontrol/comm_contactorcontrol.cpp | 2 +- .../src/communication/precharge_control/precharge_control.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index f04b2c1f..ccc898db 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -65,7 +65,7 @@ const int OFF = 0; #define PWM_ON_DUTY 1023 #define PWM_Positive_Channel 0 #define PWM_Negative_Channel 1 -unsigned long prechargeStartTime = 0; +static unsigned long prechargeStartTime = 0; unsigned long negativeStartTime = 0; unsigned long prechargeCompletedTime = 0; unsigned long timeSpentInFaultedMode = 0; diff --git a/Software/src/communication/precharge_control/precharge_control.cpp b/Software/src/communication/precharge_control/precharge_control.cpp index 6b792e04..a695a0cd 100644 --- a/Software/src/communication/precharge_control/precharge_control.cpp +++ b/Software/src/communication/precharge_control/precharge_control.cpp @@ -18,9 +18,9 @@ #define ON 1 #define OFF 0 #endif -unsigned long prechargeStartTime = 0; +static unsigned long prechargeStartTime = 0; static uint32_t freq = Precharge_default_PWM_Freq; -uint16_t delta_freq = 1; +static uint16_t delta_freq = 1; static int32_t prev_external_voltage = 20000; // Initialization functions From 71e30a01eeb556d92fc2c9d8fd7a0ebdb2576dbc Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Wed, 18 Jun 2025 21:11:57 +0300 Subject: [PATCH 22/32] Retain common image binary --- .github/workflows/compile-common-image-lilygo.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/compile-common-image-lilygo.yml b/.github/workflows/compile-common-image-lilygo.yml index 31ef7372..9e74cb63 100644 --- a/.github/workflows/compile-common-image-lilygo.yml +++ b/.github/workflows/compile-common-image-lilygo.yml @@ -50,3 +50,8 @@ jobs: - name: Compile Sketch run: arduino-cli compile --fqbn esp32:esp32:esp32 --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -DCOMMON_IMAGE -DHW_LILYGO" ./Software + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + path: esp32.esp32.esp32/Software.ino.bin From b88dc219c7e10f01055b295d46a60a7e16e7e243 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Wed, 18 Jun 2025 21:21:55 +0300 Subject: [PATCH 23/32] Fix binary path --- .github/workflows/compile-common-image-lilygo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/compile-common-image-lilygo.yml b/.github/workflows/compile-common-image-lilygo.yml index 9e74cb63..40cf16fc 100644 --- a/.github/workflows/compile-common-image-lilygo.yml +++ b/.github/workflows/compile-common-image-lilygo.yml @@ -49,9 +49,9 @@ jobs: arduino-cli core install esp32:esp32 - name: Compile Sketch - run: arduino-cli compile --fqbn esp32:esp32:esp32 --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -DCOMMON_IMAGE -DHW_LILYGO" ./Software + run: arduino-cli compile --output-dir ./ --fqbn esp32:esp32:esp32 --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -DCOMMON_IMAGE -DHW_LILYGO" ./Software - name: Upload artifact uses: actions/upload-artifact@v4 with: - path: esp32.esp32.esp32/Software.ino.bin + path: Software.ino.bin From 447955acb2079f3f256a28ee7350445660f5d131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 19 Jun 2025 00:01:32 +0300 Subject: [PATCH 24/32] Make cellvoltage detection more reliable --- .../src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp index 0bf2368d..3e9d35ba 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp @@ -112,21 +112,18 @@ void KiaHyundai64Battery:: } 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 - if ((datalayer_battery->status.cell_voltages_mV[97] > 2000) && - (datalayer_battery->status.cell_voltages_mV[97] < 4300)) { - datalayer_battery->info.number_of_cells = 98; - 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.total_capacity_Wh = 64000; - } else { - datalayer_battery->info.number_of_cells = 90; - datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_90S_DV; - datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV; - datalayer_battery->info.total_capacity_Wh = 40000; - } + // 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) && + (datalayer_battery->status.cell_voltages_mV[97] < 4500)) { + datalayer_battery->info.number_of_cells = 98; + 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.total_capacity_Wh = 64000; + } else { + datalayer_battery->info.number_of_cells = 90; + datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_90S_DV; + datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV; + datalayer_battery->info.total_capacity_Wh = 40000; } } From 7e3eb74e0696a581efdd8f93a897249e5298d34c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96ster?= Date: Thu, 19 Jun 2025 00:04:16 +0300 Subject: [PATCH 25/32] Add group check to sixth datarow --- .../src/battery/KIA-HYUNDAI-64-BATTERY.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp index 3e9d35ba..f2380559 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp @@ -362,16 +362,18 @@ void KiaHyundai64Battery::handle_incoming_can_frame(CAN_frame rx_frame) { } break; case 0x26: //Sixth datarow in PID group - //We have read all cells, check that content is valid: - for (uint8_t i = 85; i < 97; ++i) { - if (cellvoltages_mv[i] < 300) { // Zero the value if it's below 300 - cellvoltages_mv[i] = 0; // Some packs incorrectly report the last unpopulated cells as 20-60mV + if (poll_data_pid == 5) { + //We have read all cells, check that content is valid: + for (uint8_t i = 85; i < 97; ++i) { + if (cellvoltages_mv[i] < 300) { // Zero the value if it's below 300 + cellvoltages_mv[i] = 0; // Some packs incorrectly report the last unpopulated cells as 20-60mV + } } + //Map all cell voltages to the global array + memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_mv, 98 * sizeof(uint16_t)); + //Update number of cells + update_number_of_cells(); } - //Map all cell voltages to the global array - memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_mv, 98 * sizeof(uint16_t)); - //Update number of cells - update_number_of_cells(); break; case 0x27: //Seventh datarow in PID group if (poll_data_pid == 1) { From 8ba7d09ec7a28f391a10c5eb99b509a01041ca11 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Thu, 19 Jun 2025 17:20:35 +0300 Subject: [PATCH 26/32] Remove redudant code --- Software/src/communication/nvm/comm_nvm.cpp | 26 +-------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 4ce46a17..a4a9b9d1 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -7,14 +7,10 @@ Preferences settings; // Store user settings // Initialization functions -static void begin() { - settings.begin("batterySettings", false); -} - void init_stored_settings() { static uint32_t temp = 0; // ATTENTION ! The maximum length for settings keys is 15 characters - begin(); + settings.begin("batterySettings", false); // Always get the equipment stop status datalayer.system.settings.equipment_stop_active = settings.getBool("EQUIPMENT_STOP", false); @@ -142,23 +138,3 @@ void store_settings() { settings.end(); // Close preferences handle } - -void store_uint(const char* key, uint32_t value) { - begin(); - settings.putUInt(key, value); -} - -void store_bool(const char* key, bool value) { - begin(); - settings.putBool(key, value); -} - -uint32_t get_uint(const char* key, uint32_t defaultValue) { - begin(); - return settings.getUInt(key, defaultValue); -} - -bool get_bool(const char* key) { - begin(); - return settings.getBool(key, false); -} From 1525f8a26fb3b6c9db424be6bbb3d3cff3422d7a Mon Sep 17 00:00:00 2001 From: freddanastrom Date: Thu, 19 Jun 2025 21:18:18 +0200 Subject: [PATCH 27/32] Mapping unknown polls to support charged/discharged energy --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 67 +++++++++++---------- Software/src/battery/BYD-ATTO-3-BATTERY.h | 9 +-- Software/src/battery/BYD-ATTO-3-HTML.h | 8 +-- Software/src/datalayer/datalayer_extended.h | 8 +-- 4 files changed, 48 insertions(+), 44 deletions(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index d8893204..edc4f85a 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -24,10 +24,10 @@ After battery has been unlocked, you can remove the "USE_ESTIMATED_SOC" from the #define POLL_MAX_CHARGE_POWER 0x000A #define UNKNOWN_POLL_3 0x000B //0x00B1 (177 interesting!) #define UNKNOWN_POLL_4 0x000E //0x0B27 (2855 interesting!) -#define UNKNOWN_POLL_5 0x000F //0x00237B (9083 interesting!) -#define UNKNOWN_POLL_6 0x0010 //0x00231B (8987 interesting!) -#define UNKNOWN_POLL_7 0x0011 //0x0E4E (3662 interesting!) -#define UNKNOWN_POLL_8 0x0012 //0x0E27 (3623 interesting) +#define POLL_TOTAL_CHARGED_AH 0x000F +#define POLL_TOTAL_DISCHARGED_AH 0x0010 +#define POLL_TOTAL_CHARGED_KWH 0x0011 +#define POLL_TOTAL_DISCHARGED_KWH 0x0012 #define UNKNOWN_POLL_9 0x0004 //0x0034 (52 interesting!) #define UNKNOWN_POLL_10 0x002A //0x5B #define UNKNOWN_POLL_11 0x002E //0x08 (probably module number, or cell number?) @@ -181,6 +181,9 @@ void BydAttoBattery:: 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 memcpy(datalayer_battery->status.cell_voltages_mV, battery_cellvoltages, CELLCOUNT_EXTENDED * sizeof(uint16_t)); @@ -277,10 +280,10 @@ void BydAttoBattery:: datalayer_bydatto->chargePower = BMS_allowed_charge_power; datalayer_bydatto->unknown3 = BMS_unknown3; datalayer_bydatto->unknown4 = BMS_unknown4; - datalayer_bydatto->unknown5 = BMS_unknown5; - datalayer_bydatto->unknown6 = BMS_unknown6; - datalayer_bydatto->unknown7 = BMS_unknown7; - datalayer_bydatto->unknown8 = BMS_unknown8; + datalayer_bydatto->total_charged_ah = BMS_total_charged_ah; + datalayer_bydatto->total_discharged_ah = BMS_total_discharged_ah; + datalayer_bydatto->total_charged_kwh = BMS_total_charged_kwh; + datalayer_bydatto->total_discharged_kwh = BMS_total_discharged_kwh; datalayer_bydatto->unknown9 = BMS_unknown9; datalayer_bydatto->unknown10 = BMS_unknown10; datalayer_bydatto->unknown11 = BMS_unknown11; @@ -443,17 +446,17 @@ void BydAttoBattery::handle_incoming_can_frame(CAN_frame rx_frame) { case UNKNOWN_POLL_4: BMS_unknown4 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; break; - case UNKNOWN_POLL_5: - BMS_unknown5 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; + case POLL_TOTAL_CHARGED_AH: + BMS_total_charged_ah = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; break; - case UNKNOWN_POLL_6: - BMS_unknown6 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; + case POLL_TOTAL_DISCHARGED_AH: + BMS_total_discharged_ah = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; break; - case UNKNOWN_POLL_7: - BMS_unknown7 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; + case POLL_TOTAL_CHARGED_KWH: + BMS_total_charged_kwh = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; break; - case UNKNOWN_POLL_8: - BMS_unknown8 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; + case POLL_TOTAL_DISCHARGED_KWH: + BMS_total_discharged_kwh = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; break; case UNKNOWN_POLL_9: BMS_unknown9 = (rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]; @@ -623,26 +626,26 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) { case UNKNOWN_POLL_4: 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); - poll_state = UNKNOWN_POLL_5; + poll_state = POLL_TOTAL_CHARGED_AH; break; - case UNKNOWN_POLL_5: - ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_5 & 0xFF00) >> 8); - ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_5 & 0x00FF); - poll_state = UNKNOWN_POLL_6; + case POLL_TOTAL_CHARGED_AH: + ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_TOTAL_CHARGED_AH & 0xFF00) >> 8); + ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_TOTAL_CHARGED_AH & 0x00FF); + poll_state = POLL_TOTAL_DISCHARGED_AH; break; - case UNKNOWN_POLL_6: - ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_6 & 0xFF00) >> 8); - ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_6 & 0x00FF); - poll_state = UNKNOWN_POLL_7; + case POLL_TOTAL_DISCHARGED_AH: + ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_TOTAL_DISCHARGED_AH & 0xFF00) >> 8); + ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_TOTAL_DISCHARGED_AH & 0x00FF); + poll_state = POLL_TOTAL_CHARGED_KWH; break; - case UNKNOWN_POLL_7: - ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_7 & 0xFF00) >> 8); - ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_7 & 0x00FF); - poll_state = UNKNOWN_POLL_8; + case POLL_TOTAL_CHARGED_KWH: + ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_TOTAL_CHARGED_KWH & 0xFF00) >> 8); + ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_TOTAL_CHARGED_KWH & 0x00FF); + poll_state = POLL_TOTAL_DISCHARGED_KWH; break; - case UNKNOWN_POLL_8: - ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((UNKNOWN_POLL_8 & 0xFF00) >> 8); - ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(UNKNOWN_POLL_8 & 0x00FF); + case POLL_TOTAL_DISCHARGED_KWH: + ATTO_3_7E7_POLL.data.u8[2] = (uint8_t)((POLL_TOTAL_DISCHARGED_KWH & 0xFF00) >> 8); + ATTO_3_7E7_POLL.data.u8[3] = (uint8_t)(POLL_TOTAL_DISCHARGED_KWH & 0x00FF); poll_state = UNKNOWN_POLL_9; break; case UNKNOWN_POLL_9: diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.h b/Software/src/battery/BYD-ATTO-3-BATTERY.h index a5aa9043..a3621863 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.h +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.h @@ -53,6 +53,7 @@ class BydAttoBattery : public CanBattery { virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + bool supports_charged_energy() { return true; } bool supports_reset_crash() { return true; } void reset_crash() { datalayer_bydatto->UserRequestCrashReset = true; } @@ -114,10 +115,10 @@ class BydAttoBattery : public CanBattery { uint16_t BMS_allowed_charge_power = 0; uint16_t BMS_unknown3 = 0; uint16_t BMS_unknown4 = 0; - uint16_t BMS_unknown5 = 0; - uint16_t BMS_unknown6 = 0; - uint16_t BMS_unknown7 = 0; - uint16_t BMS_unknown8 = 0; + uint16_t BMS_total_charged_ah = 0; + uint16_t BMS_total_discharged_ah = 0; + uint16_t BMS_total_charged_kwh = 0; + uint16_t BMS_total_discharged_kwh = 0; uint16_t BMS_unknown9 = 0; uint8_t BMS_unknown10 = 0; uint8_t BMS_unknown11 = 0; diff --git a/Software/src/battery/BYD-ATTO-3-HTML.h b/Software/src/battery/BYD-ATTO-3-HTML.h index b133db84..2a344522 100644 --- a/Software/src/battery/BYD-ATTO-3-HTML.h +++ b/Software/src/battery/BYD-ATTO-3-HTML.h @@ -34,10 +34,10 @@ class BydAtto3HtmlRenderer : public BatteryHtmlRenderer { content += "

Charge power raw: " + String(byd_datalayer->chargePower) + "

"; content += "

Unknown3: " + String(byd_datalayer->unknown3) + "

"; content += "

Unknown4: " + String(byd_datalayer->unknown4) + "

"; - content += "

Unknown5: " + String(byd_datalayer->unknown5) + "

"; - content += "

Unknown6: " + String(byd_datalayer->unknown6) + "

"; - content += "

Unknown7: " + String(byd_datalayer->unknown7) + "

"; - content += "

Unknown8: " + String(byd_datalayer->unknown8) + "

"; + content += "

Total charged Ah: " + String(byd_datalayer->total_charged_ah) + "

"; + content += "

Total discharged Ah: " + String(byd_datalayer->total_discharged_ah) + "

"; + content += "

Total charged kWh: " + String(byd_datalayer->total_charged_kwh) + "

"; + content += "

Total discharged kWh: " + String(byd_datalayer->total_discharged_kwh) + "

"; content += "

Unknown9: " + String(byd_datalayer->unknown9) + "

"; content += "

Unknown10: " + String(byd_datalayer->unknown10) + "

"; content += "

Unknown11: " + String(byd_datalayer->unknown11) + "

"; diff --git a/Software/src/datalayer/datalayer_extended.h b/Software/src/datalayer/datalayer_extended.h index b9456887..9c10deab 100644 --- a/Software/src/datalayer/datalayer_extended.h +++ b/Software/src/datalayer/datalayer_extended.h @@ -197,10 +197,10 @@ typedef struct { uint16_t chargePower = 0; uint16_t unknown3 = 0; uint16_t unknown4 = 0; - uint16_t unknown5 = 0; - uint16_t unknown6 = 0; - uint16_t unknown7 = 0; - uint16_t unknown8 = 0; + uint16_t total_charged_ah = 0; + uint16_t total_discharged_ah = 0; + uint16_t total_charged_kwh = 0; + uint16_t total_discharged_kwh = 0; uint16_t unknown9 = 0; uint8_t unknown10 = 0; uint8_t unknown11 = 0; From 44cdedb6310838a289f67a419ae12d1fa78e2081 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 19:55:51 +0000 Subject: [PATCH 28/32] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- Software/src/battery/BYD-ATTO-3-BATTERY.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index edc4f85a..e38a3f25 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -22,8 +22,8 @@ After battery has been unlocked, you can remove the "USE_ESTIMATED_SOC" from the #define UNKNOWN_POLL_0 0x1FFE //0x64 19 C4 3B #define UNKNOWN_POLL_1 0x1FFC //0x72 1F C4 3B #define POLL_MAX_CHARGE_POWER 0x000A -#define UNKNOWN_POLL_3 0x000B //0x00B1 (177 interesting!) -#define UNKNOWN_POLL_4 0x000E //0x0B27 (2855 interesting!) +#define UNKNOWN_POLL_3 0x000B //0x00B1 (177 interesting!) +#define UNKNOWN_POLL_4 0x000E //0x0B27 (2855 interesting!) #define POLL_TOTAL_CHARGED_AH 0x000F #define POLL_TOTAL_DISCHARGED_AH 0x0010 #define POLL_TOTAL_CHARGED_KWH 0x0011 From 86503ff485334adb675a29d8bacd50039742c844 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Fri, 20 Jun 2025 10:00:28 +0300 Subject: [PATCH 29/32] Inverter indicates whether it controls contactors at run-time --- .../contactorcontrol/comm_contactorcontrol.cpp | 7 ++++--- Software/src/inverter/InverterProtocol.h | 5 +++++ Software/src/inverter/SMA-BYD-H-CAN.h | 4 ++++ Software/src/inverter/SMA-BYD-HVS-CAN.h | 4 ++++ Software/src/inverter/SMA-TRIPOWER-CAN.h | 4 ++++ 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index ccc898db..7fd2c506 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -154,9 +154,10 @@ static void dbg_contactors(const char* state) { // Main functions of the handle_contactors include checking if inverter allows for closing, checking battery 2, checking BMS power output, and actual contactor closing/precharge via GPIO void handle_contactors() { // TODO: This must be determined at run-time! -#if defined(SMA_BYD_H_CAN) || defined(SMA_BYD_HVS_CAN) || defined(SMA_TRIPOWER_CAN) - datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN); -#endif + + if (inverter && inverter->controls_contactor()) { + datalayer.system.status.inverter_allows_contactor_closing = inverter->allows_contactor_closing(); + } #ifdef BMS_POWER handle_BMSpower(); // Some batteries need to be periodically power cycled diff --git a/Software/src/inverter/InverterProtocol.h b/Software/src/inverter/InverterProtocol.h index bd786ff3..46705e59 100644 --- a/Software/src/inverter/InverterProtocol.h +++ b/Software/src/inverter/InverterProtocol.h @@ -40,6 +40,11 @@ class InverterProtocol { // This function maps all the values fetched from battery to the correct battery emulator data structures virtual void update_values() = 0; + + // If true, this inverter supports a signal to control contactor (allows_contactor_closing) + virtual bool controls_contactor() { return false; } + + virtual bool allows_contactor_closing() { return false; } }; extern InverterProtocol* inverter; diff --git a/Software/src/inverter/SMA-BYD-H-CAN.h b/Software/src/inverter/SMA-BYD-H-CAN.h index bf18265e..1f9c3268 100644 --- a/Software/src/inverter/SMA-BYD-H-CAN.h +++ b/Software/src/inverter/SMA-BYD-H-CAN.h @@ -3,6 +3,7 @@ #include "../include.h" #include "CanInverterProtocol.h" +#include "src/devboard/hal/hal.h" #ifdef SMA_BYD_H_CAN #define SELECTED_INVERTER_CLASS SmaBydHInverter @@ -16,6 +17,9 @@ class SmaBydHInverter : public CanInverterProtocol { void map_can_frame_to_variable(CAN_frame rx_frame); static constexpr char* Name = "BYD over SMA CAN"; + virtual bool controls_contactor() { return true; } + virtual bool allows_contactor_closing() { return digitalRead(INVERTER_CONTACTOR_ENABLE_PIN) == 1; } + private: static const int READY_STATE = 0x03; static const int STOP_STATE = 0x02; diff --git a/Software/src/inverter/SMA-BYD-HVS-CAN.h b/Software/src/inverter/SMA-BYD-HVS-CAN.h index 8e761559..bb537e09 100644 --- a/Software/src/inverter/SMA-BYD-HVS-CAN.h +++ b/Software/src/inverter/SMA-BYD-HVS-CAN.h @@ -3,6 +3,7 @@ #include "../include.h" #include "CanInverterProtocol.h" +#include "src/devboard/hal/hal.h" #ifdef SMA_BYD_HVS_CAN #define SELECTED_INVERTER_CLASS SmaBydHvsInverter @@ -16,6 +17,9 @@ class SmaBydHvsInverter : public CanInverterProtocol { void map_can_frame_to_variable(CAN_frame rx_frame); static constexpr char* Name = "BYD Battery-Box HVS over SMA CAN"; + virtual bool controls_contactor() { return true; } + virtual bool allows_contactor_closing() { return digitalRead(INVERTER_CONTACTOR_ENABLE_PIN) == 1; } + private: static const int READY_STATE = 0x03; static const int STOP_STATE = 0x02; diff --git a/Software/src/inverter/SMA-TRIPOWER-CAN.h b/Software/src/inverter/SMA-TRIPOWER-CAN.h index fcd7f099..9c19607c 100644 --- a/Software/src/inverter/SMA-TRIPOWER-CAN.h +++ b/Software/src/inverter/SMA-TRIPOWER-CAN.h @@ -3,6 +3,7 @@ #include "../include.h" #include "CanInverterProtocol.h" +#include "src/devboard/hal/hal.h" #ifdef SMA_TRIPOWER_CAN #define SELECTED_INVERTER_CLASS SmaTripowerInverter @@ -16,6 +17,9 @@ class SmaTripowerInverter : public CanInverterProtocol { void map_can_frame_to_variable(CAN_frame rx_frame); static constexpr char* Name = "SMA Tripower CAN"; + virtual bool controls_contactor() { return true; } + virtual bool allows_contactor_closing() { return digitalRead(INVERTER_CONTACTOR_ENABLE_PIN) == 1; } + private: const int READY_STATE = 0x03; const int STOP_STATE = 0x02; From 62b413bf377ec1fd5130373d382f9c4446cfbe16 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Fri, 20 Jun 2025 10:00:55 +0300 Subject: [PATCH 30/32] Remove TODO --- .../communication/contactorcontrol/comm_contactorcontrol.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp index 7fd2c506..61417439 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -153,8 +153,6 @@ static void dbg_contactors(const char* state) { // Main functions of the handle_contactors include checking if inverter allows for closing, checking battery 2, checking BMS power output, and actual contactor closing/precharge via GPIO void handle_contactors() { - // TODO: This must be determined at run-time! - if (inverter && inverter->controls_contactor()) { datalayer.system.status.inverter_allows_contactor_closing = inverter->allows_contactor_closing(); } From d4aaa2911dcfc6db1f6a349c74987945518e15ed Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Mon, 23 Jun 2025 20:23:37 +0300 Subject: [PATCH 31/32] Fix null-pointer reference --- Software/src/battery/BMW-I3-BATTERY.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Software/src/battery/BMW-I3-BATTERY.h b/Software/src/battery/BMW-I3-BATTERY.h index 9a60a0ce..b1b34d5f 100644 --- a/Software/src/battery/BMW-I3-BATTERY.h +++ b/Software/src/battery/BMW-I3-BATTERY.h @@ -20,7 +20,6 @@ class BmwI3Battery : public CanBattery { contactor_closing_allowed = contactor_closing_allowed_ptr; allows_contactor_closing = nullptr; wakeup_pin = wakeup; - *allows_contactor_closing = true; //Init voltage to 0 to allow contactor check to operate without fear of default values colliding battery_volts = 0; From 6e9e7605cc26e5365c39700dfb23ad9f60d4d7b4 Mon Sep 17 00:00:00 2001 From: Jaakko Haakana Date: Fri, 27 Jun 2025 21:57:57 +0300 Subject: [PATCH 32/32] Fix Volvo Spa info page by adding missing renderer. --- Software/src/battery/VOLVO-SPA-BATTERY.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.h b/Software/src/battery/VOLVO-SPA-BATTERY.h index 4ea009ed..a1becbf4 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-BATTERY.h @@ -25,7 +25,11 @@ class VolvoSpaBattery : public CanBattery { bool supports_reset_BECM() { return true; } void reset_BECM() { datalayer_extended.VolvoPolestar.UserRequestBECMecuReset = true; } + BatteryHtmlRenderer& get_status_renderer() { return renderer; } + private: + VolvoSpaHtmlRenderer renderer; + void readCellVoltages(); static const int MAX_PACK_VOLTAGE_108S_DV = 4540;