diff --git a/.github/workflows/compile-common-image-lilygo.yml b/.github/workflows/compile-common-image-lilygo.yml new file mode 100644 index 00000000..40cf16fc --- /dev/null +++ b/.github/workflows/compile-common-image-lilygo.yml @@ -0,0 +1,57 @@ +# This is the name of the workflow, visible on GitHub UI. +name: 🔋 Compile Common Image for Lilygo + +# Here we tell GitHub when to run the workflow. +on: + # The workflow is run when a commit is pushed or for a + # Pull Request. + - push + - pull_request + +# This is the list of jobs that will be run concurrently. +jobs: + # This pre-job is run to skip workflows in case a workflow is already run, i.e. because the workflow is triggered by both push and pull_request + skip-duplicate-actions: + runs-on: ubuntu-latest + # Map a step output to a job output + outputs: + should_skip: ${{ steps.skip_check.outputs.should_skip }} + steps: + - id: skip_check + uses: fkirc/skip-duplicate-actions@v5 + with: + # All of these options are optional, so you can remove them if you are happy with the defaults + concurrent_skipping: 'never' + skip_after_successful_duplicate: 'true' + do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]' + + build-common-image: + # This is the platform GitHub will use to run our workflow. + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + # Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h + - name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h + run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h + + # We use the `arduino/setup-arduino-cli` action to install and + # configure the Arduino CLI on the system. + - name: Setup Arduino CLI + uses: arduino/setup-arduino-cli@v2 + + # We then install the platform. + - name: Install platform + run: | + arduino-cli core update-index + arduino-cli core install esp32:esp32 + + - name: Compile Sketch + 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: Software.ino.bin 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; diff --git a/Software/src/battery/BATTERIES.cpp b/Software/src/battery/BATTERIES.cpp index bf6b3580..185c7540 100644 --- a/Software/src/battery/BATTERIES.cpp +++ b/Software/src/battery/BATTERIES.cpp @@ -4,16 +4,233 @@ #include "CanBattery.h" #include "RS485Battery.h" -// These functions adapt the old C-style global functions battery-API to the -// object-oriented battery API. - -// The instantiated class is defined by the pre-compiler define -// to support battery class selection at compile-time -#ifdef SELECTED_BATTERY_CLASS +#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; +std::vector supported_battery_types() { + std::vector types; + + for (int i = 0; i < (int)BatteryType::Highest; i++) { + types.push_back((BatteryType)i); + } + + return types; +} + +extern const char* name_for_battery_type(BatteryType type) { + switch (type) { + case BatteryType::None: + return "None"; + case BatteryType::BmwI3: + return BmwI3Battery::Name; + case BatteryType::BmwIx: + return BmwIXBattery::Name; + case BatteryType::BoltAmpera: + return BoltAmperaBattery::Name; + case BatteryType::BydAtto3: + 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: + 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::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::TestFake: + return TestFakeBattery::Name; + case BatteryType::VolvoSpa: + return VolvoSpaBattery::Name; + case BatteryType::VolvoSpaHybrid: + return VolvoSpaHybridBattery::Name; + default: + return nullptr; + } +} + +#ifdef COMMON_IMAGE +#ifdef SELECTED_BATTERY_CLASS +#error "Compile time SELECTED_BATTERY_CLASS should not be defined with COMMON_IMAGE" +#endif + +BatteryType user_selected_battery_type = BatteryType::NissanLeaf; +bool user_selected_second_battery = false; + +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(); +#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: + 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; + } + + battery = create_battery(user_selected_battery_type); + + if (battery) { + battery->setup(); + } + + if (user_selected_second_battery && !battery2) { + switch (user_selected_battery_type) { + case BatteryType::NissanLeaf: + battery2 = new NissanLeafBattery(&datalayer.battery2, nullptr, can_config.battery_double); + break; + case BatteryType::BmwI3: + battery2 = new BmwI3Battery(&datalayer.battery2, &datalayer.system.status.battery2_allowed_contactor_closing, + can_config.battery_double, WUP_PIN2); + break; + case BatteryType::KiaHyundai64: + battery2 = new KiaHyundai64Battery(&datalayer.battery2, &datalayer_extended.KiaHyundai64_2, + &datalayer.system.status.battery2_allowed_contactor_closing, + can_config.battery_double); + case BatteryType::SantaFePhev: + battery2 = new SantaFePhevBattery(&datalayer.battery2, can_config.battery_double); + break; + case BatteryType::TestFake: + battery2 = new TestFakeBattery(&datalayer.battery2, can_config.battery_double); + break; + } + + if (battery2) { + battery2->setup(); + } + } +} +#else // Battery selection has been made at build-time + void setup_battery() { // Instantiate the battery only once just in case this function gets called multiple times. if (battery == nullptr) { @@ -40,5 +257,4 @@ void setup_battery() { battery2->setup(); #endif } - #endif diff --git a/Software/src/battery/BATTERIES.h b/Software/src/battery/BATTERIES.h index a1e38da3..caf4b898 100644 --- a/Software/src/battery/BATTERIES.h +++ b/Software/src/battery/BATTERIES.h @@ -11,151 +11,45 @@ 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" -#endif -#ifdef CHADEMO_BATTERY #include "CHADEMO-BATTERY.h" #include "CHADEMO-SHUNTS.h" -#endif -#ifdef CMFA_EV_BATTERY #include "CMFA-EV-BATTERY.h" -#endif - -#ifdef FOXESS_BATTERY -#include "FOXESS-BATTERY.h" -#endif - -#ifdef GEELY_GEOMETRY_C_BATTERY -#include "GEELY-GEOMETRY-C-BATTERY.h" -#endif - -#ifdef ORION_BMS -#include "ORION-BMS.h" -#endif - -#ifdef SONO_BATTERY -#include "SONO-BATTERY.h" -#endif - -#ifdef STELLANTIS_ECMP_BATTERY -#include "ECMP-BATTERY.h" -#endif - -#ifdef IMIEV_CZERO_ION_BATTERY -#include "IMIEV-CZERO-ION-BATTERY.h" -#endif - -#ifdef JAGUAR_IPACE_BATTERY -#include "JAGUAR-IPACE-BATTERY.h" -#endif - -#ifdef KIA_E_GMP_BATTERY -#include "KIA-E-GMP-BATTERY.h" -#endif - -#ifdef KIA_HYUNDAI_64_BATTERY -#include "KIA-HYUNDAI-64-BATTERY.h" -#endif - -#ifdef KIA_HYUNDAI_HYBRID_BATTERY -#include "KIA-HYUNDAI-HYBRID-BATTERY.h" -#endif - -#ifdef MEB_BATTERY -#include "MEB-BATTERY.h" -#endif - -#ifdef MG_5_BATTERY -#include "MG-5-BATTERY.h" -#endif - -#ifdef NISSAN_LEAF_BATTERY -#include "NISSAN-LEAF-BATTERY.h" -#endif - -#ifdef PYLON_BATTERY -#include "PYLON-BATTERY.h" -#endif - -#ifdef DALY_BMS #include "DALY-BMS.h" -#endif - -#ifdef RJXZS_BMS -#include "RJXZS-BMS.h" -#endif - -#ifdef RANGE_ROVER_PHEV_BATTERY +#include "ECMP-BATTERY.h" +#include "FOXESS-BATTERY.h" +#include "GEELY-GEOMETRY-C-BATTERY.h" +#include "IMIEV-CZERO-ION-BATTERY.h" +#include "JAGUAR-IPACE-BATTERY.h" +#include "KIA-E-GMP-BATTERY.h" +#include "KIA-HYUNDAI-64-BATTERY.h" +#include "KIA-HYUNDAI-HYBRID-BATTERY.h" +#include "MEB-BATTERY.h" +#include "MG-5-BATTERY.h" +#include "NISSAN-LEAF-BATTERY.h" +#include "ORION-BMS.h" +#include "PYLON-BATTERY.h" #include "RANGE-ROVER-PHEV-BATTERY.h" -#endif - -#ifdef RENAULT_KANGOO_BATTERY #include "RENAULT-KANGOO-BATTERY.h" -#endif - -#ifdef RENAULT_TWIZY_BATTERY #include "RENAULT-TWIZY.h" -#endif - -#ifdef RENAULT_ZOE_GEN1_BATTERY #include "RENAULT-ZOE-GEN1-BATTERY.h" -#endif - -#ifdef RENAULT_ZOE_GEN2_BATTERY #include "RENAULT-ZOE-GEN2-BATTERY.h" -#endif - -#ifdef SANTA_FE_PHEV_BATTERY +#include "RJXZS-BMS.h" #include "SANTA-FE-PHEV-BATTERY.h" -#endif - -#ifdef SIMPBMS_BATTERY #include "SIMPBMS-BATTERY.h" -#endif - -#if defined(TESLA_MODEL_SX_BATTERY) || defined(TESLA_MODEL_3Y_BATTERY) -#define TESLA_BATTERY +#include "SONO-BATTERY.h" #include "TESLA-BATTERY.h" -#endif - -#ifdef TEST_FAKE_BATTERY #include "TEST-FAKE-BATTERY.h" -#endif - -#ifdef VOLVO_SPA_BATTERY #include "VOLVO-SPA-BATTERY.h" -#endif - -#ifdef VOLVO_SPA_HYBRID_BATTERY #include "VOLVO-SPA-HYBRID-BATTERY.h" -#endif void setup_battery(void); diff --git a/Software/src/battery/BMW-I3-BATTERY.cpp b/Software/src/battery/BMW-I3-BATTERY.cpp index 3d4a1871..a02d64a6 100644 --- a/Software/src/battery/BMW-I3-BATTERY.cpp +++ b/Software/src/battery/BMW-I3-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef BMW_I3_BATTERY +#include "BMW-I3-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" -#include "BMW-I3-BATTERY.h" +#include "../include.h" /* Do not change code below unless you are sure what you are doing */ @@ -34,7 +33,7 @@ static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_ return crc; } -static uint8_t increment_alive_counter(uint8_t counter) { +uint8_t BmwI3Battery::increment_alive_counter(uint8_t counter) { counter++; if (counter > ALIVE_MAX_VALUE) { counter = 0; @@ -509,7 +508,7 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) { } void BmwI3Battery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "BMW i3", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; //Before we have started up and detected which battery is in use, use 60AH values @@ -525,5 +524,3 @@ void BmwI3Battery::setup(void) { // Performs one time setup at startup pinMode(wakeup_pin, OUTPUT); digitalWrite(wakeup_pin, HIGH); // Wake up the battery } - -#endif diff --git a/Software/src/battery/BMW-I3-BATTERY.h b/Software/src/battery/BMW-I3-BATTERY.h index b1b34d5f..42230805 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: @@ -37,6 +38,7 @@ class BmwI3Battery : 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 = "BMW i3"; BatteryHtmlRenderer& get_status_renderer() { return renderer; } @@ -78,7 +80,9 @@ class BmwI3Battery : public CanBattery { unsigned long previousMillis5000 = 0; // will store last time a 5000ms CAN Message was send unsigned long previousMillis10000 = 0; // will store last time a 10000ms CAN Message was send -#define ALIVE_MAX_VALUE 14 // BMW CAN messages contain alive counter, goes from 0...14 + static const int ALIVE_MAX_VALUE = 14; // BMW CAN messages contain alive counter, goes from 0...14 + + uint8_t increment_alive_counter(uint8_t counter); enum BatterySize { BATTERY_60AH, BATTERY_94AH, BATTERY_120AH }; BatterySize detectedBattery = BATTERY_60AH; diff --git a/Software/src/battery/BMW-IX-BATTERY.cpp b/Software/src/battery/BMW-IX-BATTERY.cpp index f48ea5e1..74797509 100644 --- a/Software/src/battery/BMW-IX-BATTERY.cpp +++ b/Software/src/battery/BMW-IX-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef BMW_IX_BATTERY +#include "BMW-IX-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" -#include "BMW-IX-BATTERY.h" +#include "../include.h" // Function to check if a value has gone stale over a specified time period bool BmwIXBattery::isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime) { @@ -475,7 +474,7 @@ void BmwIXBattery::transmit_can(unsigned long currentMillis) { } void BmwIXBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "BMW iX and i4-7 platform", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; //Reset Battery at bootup @@ -696,5 +695,3 @@ void BmwIXBattery::HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms) { } } } - -#endif // BMW_IX_BATTERY diff --git a/Software/src/battery/BMW-IX-BATTERY.h b/Software/src/battery/BMW-IX-BATTERY.h index 68935fad..0b039dda 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: @@ -21,6 +22,8 @@ class BmwIXBattery : public CanBattery { void request_open_contactors() { datalayer_extended.bmwix.UserRequestContactorOpen = true; } void request_close_contactors() { datalayer_extended.bmwix.UserRequestContactorClose = true; } + static constexpr char* Name = "BMW iX and i4-7 platform"; + private: BmwIXHtmlRenderer renderer; static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V diff --git a/Software/src/battery/BMW-PHEV-BATTERY.cpp b/Software/src/battery/BMW-PHEV-BATTERY.cpp index f321f67e..aea4c3f1 100644 --- a/Software/src/battery/BMW-PHEV-BATTERY.cpp +++ b/Software/src/battery/BMW-PHEV-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef BMW_PHEV_BATTERY +#include "BMW-PHEV-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" -#include "BMW-PHEV-BATTERY.h" +#include "../include.h" const unsigned char crc8_table[256] = { // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies @@ -714,5 +713,3 @@ void BmwPhevBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.system.status.battery_allows_contactor_closing = true; } - -#endif 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.cpp b/Software/src/battery/BMW-SBOX.cpp index ed045fe6..69760107 100644 --- a/Software/src/battery/BMW-SBOX.cpp +++ b/Software/src/battery/BMW-SBOX.cpp @@ -1,8 +1,7 @@ -#include "../include.h" -#ifdef BMW_SBOX +#include "BMW-SBOX.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" -#include "BMW-SBOX.h" +#include "../include.h" uint8_t reverse_bits(uint8_t byte) { uint8_t reversed = 0; @@ -179,7 +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'; } -#endif diff --git a/Software/src/battery/BMW-SBOX.h b/Software/src/battery/BMW-SBOX.h index 97ef3cd5..ea2bd72c 100644 --- a/Software/src/battery/BMW-SBOX.h +++ b/Software/src/battery/BMW-SBOX.h @@ -2,8 +2,9 @@ #define BMW_SBOX_CONTROL_H #include "../include.h" -#define CAN_SHUNT_SELECTED +#ifdef BMW_SBOX #define SELECTED_SHUNT_CLASS BmwSbox +#endif #include "Shunt.h" @@ -12,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/BOLT-AMPERA-BATTERY.cpp b/Software/src/battery/BOLT-AMPERA-BATTERY.cpp index eec3f6ea..2cb13d95 100644 --- a/Software/src/battery/BOLT-AMPERA-BATTERY.cpp +++ b/Software/src/battery/BOLT-AMPERA-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef BOLT_AMPERA_BATTERY +#include "BOLT-AMPERA-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" -#include "BOLT-AMPERA-BATTERY.h" +#include "../include.h" /* TODOs left for this implementation @@ -645,7 +644,7 @@ void BoltAmperaBattery::transmit_can(unsigned long currentMillis) { } void BoltAmperaBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Chevrolet Bolt EV/Opel Ampera-e", 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; @@ -655,5 +654,3 @@ void BoltAmperaBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.system.status.battery_allows_contactor_closing = true; } - -#endif diff --git a/Software/src/battery/BOLT-AMPERA-BATTERY.h b/Software/src/battery/BOLT-AMPERA-BATTERY.h index 57fdba84..19a8e07b 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: @@ -16,6 +17,8 @@ class BoltAmperaBattery : public CanBattery { virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Chevrolet Bolt EV/Opel Ampera-e"; + BatteryHtmlRenderer& get_status_renderer() { return renderer; } private: diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp index e38a3f25..d06529e1 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.cpp +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef BYD_ATTO_3_BATTERY +#include "BYD-ATTO-3-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" -#include "BYD-ATTO-3-BATTERY.h" +#include "../include.h" /* Notes SOC% by default is now ESTIMATED. @@ -685,7 +684,7 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) { } void BydAttoBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "BYD Atto 3", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer_battery->info.number_of_cells = CELLCOUNT_STANDARD; datalayer_battery->info.chemistry = battery_chemistry_enum::LFP; @@ -700,5 +699,3 @@ void BydAttoBattery::setup(void) { // Performs one time setup at startup SOC_method = MEASURED; #endif } - -#endif diff --git a/Software/src/battery/BYD-ATTO-3-BATTERY.h b/Software/src/battery/BYD-ATTO-3-BATTERY.h index a3621863..5fdf102d 100644 --- a/Software/src/battery/BYD-ATTO-3-BATTERY.h +++ b/Software/src/battery/BYD-ATTO-3-BATTERY.h @@ -18,18 +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 - -#define CELLCOUNT_EXTENDED 126 -#define CELLCOUNT_STANDARD 104 -#define MAX_PACK_VOLTAGE_EXTENDED_DV 4410 //Extended range -#define MIN_PACK_VOLTAGE_EXTENDED_DV 3800 //Extended range -#define MAX_PACK_VOLTAGE_STANDARD_DV 3640 //Standard range -#define MIN_PACK_VOLTAGE_STANDARD_DV 3136 //Standard range -#define MAX_CELL_DEVIATION_MV 230 -#define MAX_CELL_VOLTAGE_MV 3650 //Charging stops if one cell exceeds this value -#define MIN_CELL_VOLTAGE_MV 2800 //Discharging stops if one cell goes below this value +#endif class BydAttoBattery : public CanBattery { public: @@ -53,6 +44,8 @@ class BydAttoBattery : public CanBattery { virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "BYD Atto 3"; + bool supports_charged_energy() { return true; } bool supports_reset_crash() { return true; } @@ -68,6 +61,16 @@ class BydAttoBattery : public CanBattery { BatteryHtmlRenderer& get_status_renderer() { return renderer; } private: + static const int CELLCOUNT_EXTENDED = 126; + static const int CELLCOUNT_STANDARD = 104; + static const int MAX_PACK_VOLTAGE_EXTENDED_DV = 4410; //Extended range + static const int MIN_PACK_VOLTAGE_EXTENDED_DV = 3800; //Extended range + static const int MAX_PACK_VOLTAGE_STANDARD_DV = 3640; //Standard range + static const int MIN_PACK_VOLTAGE_STANDARD_DV = 3136; //Standard range + static const int MAX_CELL_DEVIATION_MV = 230; + static const int MAX_CELL_VOLTAGE_MV = 3650; //Charging stops if one cell exceeds this value + static const int MIN_CELL_VOLTAGE_MV = 2800; //Discharging stops if one cell goes below this value + BydAtto3HtmlRenderer renderer; DATALAYER_BATTERY_TYPE* datalayer_battery; DATALAYER_INFO_BYDATTO3* datalayer_bydatto; 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 0c0b3b40..187b29d0 100644 --- a/Software/src/battery/Battery.h +++ b/Software/src/battery/Battery.h @@ -1,9 +1,56 @@ #ifndef BATTERY_H #define BATTERY_H -#include "../datalayer/datalayer.h" +#include #include "src/devboard/webserver/BatteryHtmlRenderer.h" +enum class BatteryType { + None = 0, + 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 +}; + +extern std::vector supported_battery_types(); +extern const char* name_for_battery_type(BatteryType type); + +extern BatteryType user_selected_battery_type; +extern bool user_selected_second_battery; + // Abstract base class for next-generation battery implementations. // Defines the interface to call battery specific functionality. class Battery { @@ -48,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/CELLPOWER-BMS.cpp b/Software/src/battery/CELLPOWER-BMS.cpp index 4eaf23d1..5eccbf45 100644 --- a/Software/src/battery/CELLPOWER-BMS.cpp +++ b/Software/src/battery/CELLPOWER-BMS.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef CELLPOWER_BMS +#include "CELLPOWER-BMS.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../devboard/utils/events.h" -#include "CELLPOWER-BMS.h" +#include "../include.h" void CellPowerBms::update_values() { @@ -230,7 +229,7 @@ void CellPowerBms::transmit_can(unsigned long currentMillis) { } void CellPowerBms::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Cellpower BMS", 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; @@ -238,5 +237,3 @@ void CellPowerBms::setup(void) { // Performs one time setup at startup datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; } - -#endif // CELLPOWER_BMS diff --git a/Software/src/battery/CELLPOWER-BMS.h b/Software/src/battery/CELLPOWER-BMS.h index 15a4662f..719ada23 100644 --- a/Software/src/battery/CELLPOWER-BMS.h +++ b/Software/src/battery/CELLPOWER-BMS.h @@ -5,8 +5,9 @@ #include "CELLPOWER-HTML.h" #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef CELLPOWER_BMS #define SELECTED_BATTERY_CLASS CellPowerBms +#endif class CellPowerBms : public CanBattery { public: @@ -15,6 +16,8 @@ class CellPowerBms : public CanBattery { virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Cellpower BMS"; + BatteryHtmlRenderer& get_status_renderer() { return renderer; } private: diff --git a/Software/src/battery/CHADEMO-BATTERY.cpp b/Software/src/battery/CHADEMO-BATTERY.cpp index 833989cb..719f8298 100644 --- a/Software/src/battery/CHADEMO-BATTERY.cpp +++ b/Software/src/battery/CHADEMO-BATTERY.cpp @@ -1,10 +1,11 @@ -#include "../include.h" -#ifdef CHADEMO_BATTERY +#include "CHADEMO-BATTERY.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "CHADEMO-BATTERY.h" +#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 @@ -945,7 +946,7 @@ void ChademoBattery::setup(void) { // Performs one time setup at startup pinMode(CHADEMO_PIN_4, INPUT); pinMode(CHADEMO_PIN_7, INPUT); - strncpy(datalayer.system.info.battery_protocol, "Chademo V2X mode", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; CHADEMO_Status = CHADEMO_IDLE; @@ -994,4 +995,5 @@ void ChademoBattery::setup(void) { // Performs one time setup at startup setupMillis = millis(); } + #endif diff --git a/Software/src/battery/CHADEMO-BATTERY.h b/Software/src/battery/CHADEMO-BATTERY.h index 5f19df83..10269b27 100644 --- a/Software/src/battery/CHADEMO-BATTERY.h +++ b/Software/src/battery/CHADEMO-BATTERY.h @@ -4,15 +4,12 @@ #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 BATTERY_SELECTED -#define SELECTED_BATTERY_CLASS ChademoBattery +#endif class ChademoBattery : public CanBattery { public: @@ -21,6 +18,8 @@ class ChademoBattery : public CanBattery { virtual void update_values(); virtual void transmit_can(unsigned long currentMillis); + static constexpr char* Name = "Chademo V2X mode"; + private: void process_vehicle_charging_minimums(CAN_frame rx_frame); void process_vehicle_charging_maximums(CAN_frame rx_frame); diff --git a/Software/src/battery/CHADEMO-SHUNTS.cpp b/Software/src/battery/CHADEMO-SHUNTS.cpp index 1f336e4c..7702c8f5 100644 --- a/Software/src/battery/CHADEMO-SHUNTS.cpp +++ b/Software/src/battery/CHADEMO-SHUNTS.cpp @@ -18,12 +18,11 @@ * by NJbubo * */ -#include "../include.h" -#ifdef CHADEMO_BATTERY +#include "CHADEMO-SHUNTS.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" +#include "../include.h" #include "CHADEMO-BATTERY.h" -#include "CHADEMO-SHUNTS.h" /* Initial frames received from ISA shunts provide invalid during initialization */ static int framecount = 0; @@ -429,4 +428,3 @@ void ISA_getINFO(uint8_t i) { transmit_can_frame(&outframe, can_config.battery); } -#endif 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 e9eddf64..34d47059 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.cpp +++ b/Software/src/battery/CMFA-EV-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef CMFA_EV_BATTERY +#include "CMFA-EV-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" -#include "CMFA-EV-BATTERY.h" +#include "../include.h" /* The raw SOC value sits at 90% when the battery is full, so we should report back 100% once this value is reached Same goes for low point, when 10% is reached we report 0% */ @@ -941,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; @@ -951,5 +950,3 @@ void CmfaEvBattery::setup(void) { // Performs one time setup at startup 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 diff --git a/Software/src/battery/CMFA-EV-BATTERY.h b/Software/src/battery/CMFA-EV-BATTERY.h index 0455838e..b1dcb3da 100644 --- a/Software/src/battery/CMFA-EV-BATTERY.h +++ b/Software/src/battery/CMFA-EV-BATTERY.h @@ -5,8 +5,9 @@ #include "CMFA-EV-HTML.h" #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef CMFA_EV_BATTERY #define SELECTED_BATTERY_CLASS CmfaEvBattery +#endif class CmfaEvBattery : public CanBattery { public: @@ -14,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 0b96fcd5..d949445a 100644 --- a/Software/src/battery/DALY-BMS.cpp +++ b/Software/src/battery/DALY-BMS.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef DALY_BMS +#include "DALY-BMS.h" #include #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "DALY-BMS.h" +#include "../include.h" /* Do not change code below unless you are sure what you are doing */ @@ -61,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; @@ -207,5 +206,3 @@ void DalyBms::receive() { } } } - -#endif diff --git a/Software/src/battery/DALY-BMS.h b/Software/src/battery/DALY-BMS.h index a8902f85..1c5a2015 100644 --- a/Software/src/battery/DALY-BMS.h +++ b/Software/src/battery/DALY-BMS.h @@ -3,20 +3,9 @@ #include "RS485Battery.h" -/* Tweak these according to your battery build */ -#define CELL_COUNT 14 -#define MAX_PACK_VOLTAGE_DV 580 //580 = 58.0V -#define MIN_PACK_VOLTAGE_DV 460 //480 = 48.0V -#define MAX_CELL_VOLTAGE_MV 4200 //Battery is put into emergency stop if one cell goes over this value -#define MIN_CELL_VOLTAGE_MV 3200 //Battery is put into emergency stop if one cell goes below this value -#define POWER_PER_PERCENT 50 // below 20% and above 80% limit power to 50W * SOC (i.e. 150W at 3%, 500W at 10%, ...) -#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 +#ifdef DALY_BMS #define SELECTED_BATTERY_CLASS DalyBms +#endif class DalyBms : public RS485Battery { public: @@ -24,8 +13,20 @@ 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 */ + static const int CELL_COUNT = 14; + static const int MAX_PACK_VOLTAGE_DV = 580; //580 = 58.0V + static const int MIN_PACK_VOLTAGE_DV = 460; //480 = 48.0V + static const int MAX_CELL_VOLTAGE_MV = 4200; //Battery is put into emergency stop if one cell goes over this value + static const int MIN_CELL_VOLTAGE_MV = 3200; //Battery is put into emergency stop if one cell goes below this value + static const int POWER_PER_PERCENT = + 50; // below 20% and above 80% limit power to 50W * SOC (i.e. 150W at 3%, 500W at 10%, ...) + static const int POWER_PER_DEGREE_C = 60; // max power added/removed per degree above/below 0°C + static const int POWER_AT_0_DEGREE_C = 800; // power at 0°C + int baud_rate() { return 9600; } }; diff --git a/Software/src/battery/ECMP-BATTERY.cpp b/Software/src/battery/ECMP-BATTERY.cpp index dbab7c0f..7f6d3d59 100644 --- a/Software/src/battery/ECMP-BATTERY.cpp +++ b/Software/src/battery/ECMP-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef STELLANTIS_ECMP_BATTERY +#include "ECMP-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For More Battery Info page #include "../devboard/utils/events.h" -#include "ECMP-BATTERY.h" +#include "../include.h" /* TODO: This integration is still ongoing. Here is what still needs to be done in order to use this battery type @@ -1517,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; @@ -1527,5 +1526,3 @@ void EcmpBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.system.status.battery_allows_contactor_closing = true; } - -#endif diff --git a/Software/src/battery/ECMP-BATTERY.h b/Software/src/battery/ECMP-BATTERY.h index c2065587..c6f0f451 100644 --- a/Software/src/battery/ECMP-BATTERY.h +++ b/Software/src/battery/ECMP-BATTERY.h @@ -6,8 +6,9 @@ #include "CanBattery.h" #include "ECMP-HTML.h" -#define BATTERY_SELECTED +#ifdef STELLANTIS_ECMP_BATTERY #define SELECTED_BATTERY_CLASS EcmpBattery +#endif class EcmpBattery : public CanBattery { public: @@ -15,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; } @@ -37,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; @@ -142,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 5c78dc08..4225d509 100644 --- a/Software/src/battery/FOXESS-BATTERY.cpp +++ b/Software/src/battery/FOXESS-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef FOXESS_BATTERY +#include "FOXESS-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "FOXESS-BATTERY.h" +#include "../include.h" /* Can bus @ 500k - all Extended ID, little endian @@ -573,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; @@ -583,5 +582,3 @@ void FoxessBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.system.status.battery_allows_contactor_closing = true; } - -#endif diff --git a/Software/src/battery/FOXESS-BATTERY.h b/Software/src/battery/FOXESS-BATTERY.h index b5102b8e..e5143dc6 100644 --- a/Software/src/battery/FOXESS-BATTERY.h +++ b/Software/src/battery/FOXESS-BATTERY.h @@ -5,8 +5,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef FOXESS_BATTERY #define SELECTED_BATTERY_CLASS FoxessBattery +#endif class FoxessBattery : public CanBattery { public: @@ -14,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 a71338a5..ede65130 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef GEELY_GEOMETRY_C_BATTERY +#include "GEELY-GEOMETRY-C-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../devboard/utils/events.h" -#include "GEELY-GEOMETRY-C-BATTERY.h" +#include "../include.h" /* TODO - Contactor closing: CAN log needed from complete H-CAN of Geely Geometry C vehicle. We are not sure what needs to be sent towards the battery yet to get contactor closing working. DTC readout complains that a "Power CAN BUS Data Missing" message is still missing @@ -661,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 @@ -671,5 +670,3 @@ void GeelyGeometryCBattery::setup(void) { // Performs one time setup at startup datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; } - -#endif diff --git a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h index 03734ce7..abba1220 100644 --- a/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h +++ b/Software/src/battery/GEELY-GEOMETRY-C-BATTERY.h @@ -6,29 +6,9 @@ #include "CanBattery.h" #include "GEELY-GEOMETRY-C-HTML.h" -#define BATTERY_SELECTED +#ifdef GEELY_GEOMETRY_C_BATTERY #define SELECTED_BATTERY_CLASS GeelyGeometryCBattery - -#define POLL_SOC 0x4B35 -#define POLL_CC2_VOLTAGE 0x4BCF -#define POLL_CELL_MAX_VOLTAGE_NUMBER 0x4B1E -#define POLL_CELL_MIN_VOLTAGE_NUMBER 0x4B20 -#define POLL_AMOUNT_CELLS 0x4B07 -#define POLL_SPECIFICIAL_VOLTAGE 0x4B05 -#define POLL_UNKNOWN_1 0x4BDA //245 on two batteries -#define POLL_RAW_SOC_MAX 0x4BC3 -#define POLL_RAW_SOC_MIN 0x4BC4 -#define POLL_UNKNOWN_4 0xDF00 //144 (the other battery 143) -#define POLL_CAPACITY_MODULE_MAX 0x4B3D -#define POLL_CAPACITY_MODULE_MIN 0x4B3E -#define POLL_UNKNOWN_7 0x4B24 //1 (the other battery 23) -#define POLL_UNKNOWN_8 0x4B26 //16 (the other battery 33) -#define POLL_MULTI_TEMPS 0x4B0F -#define POLL_MULTI_UNKNOWN_2 0x4B10 -#define POLL_MULTI_UNKNOWN_3 0x4B53 -#define POLL_MULTI_UNKNOWN_4 0x4B54 -#define POLL_MULTI_HARDWARE_VERSION 0x4B6B -#define POLL_MULTI_SOFTWARE_VERSION 0x4B6C +#endif class GeelyGeometryCBattery : public CanBattery { public: @@ -36,10 +16,32 @@ 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; } private: + static const int POLL_SOC = 0x4B35; + static const int POLL_CC2_VOLTAGE = 0x4BCF; + static const int POLL_CELL_MAX_VOLTAGE_NUMBER = 0x4B1E; + static const int POLL_CELL_MIN_VOLTAGE_NUMBER = 0x4B20; + static const int POLL_AMOUNT_CELLS = 0x4B07; + static const int POLL_SPECIFICIAL_VOLTAGE = 0x4B05; + static const int POLL_UNKNOWN_1 = 0x4BDA; //245 on two batteries + static const int POLL_RAW_SOC_MAX = 0x4BC3; + static const int POLL_RAW_SOC_MIN = 0x4BC4; + static const int POLL_UNKNOWN_4 = 0xDF00; //144 (the other battery 143) + static const int POLL_CAPACITY_MODULE_MAX = 0x4B3D; + static const int POLL_CAPACITY_MODULE_MIN = 0x4B3E; + static const int POLL_UNKNOWN_7 = 0x4B24; //1 (the other battery 23) + static const int POLL_UNKNOWN_8 = 0x4B26; //16 (the other battery 33) + static const int POLL_MULTI_TEMPS = 0x4B0F; + static const int POLL_MULTI_UNKNOWN_2 = 0x4B10; + static const int POLL_MULTI_UNKNOWN_3 = 0x4B53; + static const int POLL_MULTI_UNKNOWN_4 = 0x4B54; + static const int POLL_MULTI_HARDWARE_VERSION = 0x4B6B; + static const int POLL_MULTI_SOFTWARE_VERSION = 0x4B6C; + GeelyGeometryCHtmlRenderer renderer; static const int MAX_PACK_VOLTAGE_70_DV = 4420; //70kWh diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp index ad070bcf..69e4478e 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef IMIEV_CZERO_ION_BATTERY +#include "IMIEV-CZERO-ION-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "IMIEV-CZERO-ION-BATTERY.h" +#include "../include.h" //Code still work in progress, TODO: //Figure out if CAN messages need to be sent to keep the system happy? @@ -191,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; @@ -200,5 +199,3 @@ void ImievCZeroIonBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.system.status.battery_allows_contactor_closing = true; } - -#endif diff --git a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h index 7ed242a4..1c6dc127 100644 --- a/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h +++ b/Software/src/battery/IMIEV-CZERO-ION-BATTERY.h @@ -5,8 +5,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef IMIEV_CZERO_ION_BATTERY #define SELECTED_BATTERY_CLASS ImievCZeroIonBattery +#endif class ImievCZeroIonBattery : public CanBattery { public: @@ -14,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.cpp b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp index 78f68c31..4516b86a 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef JAGUAR_IPACE_BATTERY +#include "JAGUAR-IPACE-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "JAGUAR-IPACE-BATTERY.h" +#include "../include.h" /* Do not change code below unless you are sure what you are doing */ static unsigned long previousMillisKeepAlive = 0; @@ -57,7 +56,7 @@ CAN_frame ipace_keep_alive = {.FD = false, .ID = 0x59e, .data = {0x9E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};*/ -void print_units(char* header, int value, char* units) { +static void print_units(char* header, int value, char* units) { logging.print(header); logging.print(value); logging.print(units); @@ -237,5 +236,3 @@ void JaguarIpaceBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.system.status.battery_allows_contactor_closing = true; } - -#endif diff --git a/Software/src/battery/JAGUAR-IPACE-BATTERY.h b/Software/src/battery/JAGUAR-IPACE-BATTERY.h index f91444a0..e97ccab5 100644 --- a/Software/src/battery/JAGUAR-IPACE-BATTERY.h +++ b/Software/src/battery/JAGUAR-IPACE-BATTERY.h @@ -3,14 +3,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef JAGUAR_IPACE_BATTERY #define SELECTED_BATTERY_CLASS JaguarIpaceBattery - -#define MAX_PACK_VOLTAGE_DV 4546 //5000 = 500.0V -#define MIN_PACK_VOLTAGE_DV 3370 -#define MAX_CELL_DEVIATION_MV 250 -#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value -#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value +#endif class JaguarIpaceBattery : public CanBattery { public: @@ -18,6 +13,14 @@ 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 + static const int MIN_PACK_VOLTAGE_DV = 3370; + static const int MAX_CELL_DEVIATION_MV = 250; + static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value + static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value }; #endif diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.cpp b/Software/src/battery/KIA-E-GMP-BATTERY.cpp index 405f30b5..43576212 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.cpp +++ b/Software/src/battery/KIA-E-GMP-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef KIA_E_GMP_BATTERY +#include "KIA-E-GMP-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" +#include "../include.h" #include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h" -#include "KIA-E-GMP-BATTERY.h" const unsigned char crc8_table[256] = { // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies @@ -1091,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 @@ -1101,5 +1100,3 @@ void KiaEGmpBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; } - -#endif diff --git a/Software/src/battery/KIA-E-GMP-BATTERY.h b/Software/src/battery/KIA-E-GMP-BATTERY.h index 13e8b6eb..faabeb27 100644 --- a/Software/src/battery/KIA-E-GMP-BATTERY.h +++ b/Software/src/battery/KIA-E-GMP-BATTERY.h @@ -9,8 +9,9 @@ extern ACAN2517FD canfd; #define ESTIMATE_SOC_FROM_CELLVOLTAGE -#define BATTERY_SELECTED +#ifdef KIA_E_GMP_BATTERY #define SELECTED_BATTERY_CLASS KiaEGmpBattery +#endif class KiaEGmpBattery : public CanBattery { public: @@ -18,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 f2380559..1e6e1521 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef KIA_HYUNDAI_64_BATTERY +#include "KIA-HYUNDAI-64-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" -#include "KIA-HYUNDAI-64-BATTERY.h" +#include "../include.h" void KiaHyundai64Battery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus @@ -464,7 +463,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 @@ -475,5 +474,3 @@ void KiaHyundai64Battery::setup(void) { // Performs one time setup at startup *allows_contactor_closing = true; } } - -#endif diff --git a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h index 62dbbde0..943dfa53 100644 --- a/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-64-BATTERY.h @@ -7,8 +7,9 @@ #include "CanBattery.h" #include "KIA-HYUNDAI-64-HTML.h" -#define BATTERY_SELECTED +#ifdef KIA_HYUNDAI_64_BATTERY #define SELECTED_BATTERY_CLASS KiaHyundai64Battery +#endif class KiaHyundai64Battery : public CanBattery { public: @@ -34,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 29867742..1372dd8f 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef KIA_HYUNDAI_HYBRID_BATTERY +#include "KIA-HYUNDAI-HYBRID-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "KIA-HYUNDAI-HYBRID-BATTERY.h" +#include "../include.h" /* TODO: - The HEV battery seems to turn off after 1 minute of use. When this happens SOC% stops updating. @@ -215,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 @@ -224,5 +223,3 @@ void KiaHyundaiHybridBattery::setup(void) { // Performs one time setup at start datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; } - -#endif diff --git a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h index 915fdcef..35503754 100644 --- a/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h +++ b/Software/src/battery/KIA-HYUNDAI-HYBRID-BATTERY.h @@ -5,8 +5,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef KIA_HYUNDAI_HYBRID_BATTERY #define SELECTED_BATTERY_CLASS KiaHyundaiHybridBattery +#endif class KiaHyundaiHybridBattery : public CanBattery { public: @@ -14,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 6dac2b9b..0ac01e26 100644 --- a/Software/src/battery/MEB-BATTERY.cpp +++ b/Software/src/battery/MEB-BATTERY.cpp @@ -1,12 +1,11 @@ -#include "../include.h" -#ifdef MEB_BATTERY +#include "MEB-BATTERY.h" #include // For std::min and std::max #include "../communication/can/comm_can.h" #include "../communication/can/obd.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../devboard/utils/events.h" -#include "MEB-BATTERY.h" +#include "../include.h" /* TODO list @@ -2036,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 @@ -2045,5 +2044,3 @@ void MebBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; } - -#endif diff --git a/Software/src/battery/MEB-BATTERY.h b/Software/src/battery/MEB-BATTERY.h index 7895247c..33e93434 100644 --- a/Software/src/battery/MEB-BATTERY.h +++ b/Software/src/battery/MEB-BATTERY.h @@ -5,8 +5,9 @@ #include "CanBattery.h" #include "MEB-HTML.h" -#define BATTERY_SELECTED +#ifdef MEB_BATTERY #define SELECTED_BATTERY_CLASS MebBattery +#endif class MebBattery : public CanBattery { public: @@ -16,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.cpp b/Software/src/battery/MG-5-BATTERY.cpp index f304c7c3..5b1070b8 100644 --- a/Software/src/battery/MG-5-BATTERY.cpp +++ b/Software/src/battery/MG-5-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef MG_5_BATTERY_H +#include "MG-5-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "MG-5-BATTERY.h" +#include "../include.h" /* TODO: - Get contactor closing working @@ -122,5 +121,3 @@ void Mg5Battery::setup(void) { // Performs one time setup at startup datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; } - -#endif diff --git a/Software/src/battery/MG-5-BATTERY.h b/Software/src/battery/MG-5-BATTERY.h index ee34249a..2c343bdf 100644 --- a/Software/src/battery/MG-5-BATTERY.h +++ b/Software/src/battery/MG-5-BATTERY.h @@ -5,8 +5,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef MG_5_BATTERY #define SELECTED_BATTERY_CLASS Mg5Battery +#endif class Mg5Battery : public CanBattery { public: @@ -14,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/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 7af6f702..36fc92c3 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -1,6 +1,5 @@ -#include "../include.h" -#ifdef NISSAN_LEAF_BATTERY #include "NISSAN-LEAF-BATTERY.h" +#include "../include.h" #ifdef MQTT #include "../devboard/mqtt/mqtt.h" #endif @@ -970,7 +969,7 @@ void decodeChallengeData(unsigned int incomingChallenge, unsigned char* solvedCh } void NissanLeafBattery::setup(void) { // Performs one time setup at startup - strncpy(datalayer.system.info.battery_protocol, "Nissan LEAF battery", 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; @@ -979,5 +978,3 @@ void NissanLeafBattery::setup(void) { // Performs one time setup at startup datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; } - -#endif //NISSAN_LEAF_BATTERY diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.h b/Software/src/battery/NISSAN-LEAF-BATTERY.h index 24e98abb..21030560 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.h +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.h @@ -7,14 +7,9 @@ #include "CanBattery.h" #include "NISSAN-LEAF-HTML.h" -#define BATTERY_SELECTED +#ifdef NISSAN_LEAF_BATTERY #define SELECTED_BATTERY_CLASS NissanLeafBattery - -#define MAX_PACK_VOLTAGE_DV 4040 //5000 = 500.0V -#define MIN_PACK_VOLTAGE_DV 2600 -#define MAX_CELL_DEVIATION_MV 150 -#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value -#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value +#endif class NissanLeafBattery : public CanBattery { public: @@ -51,8 +46,15 @@ class NissanLeafBattery : public CanBattery { } BatteryHtmlRenderer& get_status_renderer() { return renderer; } + static constexpr char* Name = "Nissan LEAF battery"; private: + static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V + static const int MIN_PACK_VOLTAGE_DV = 2600; + static const int MAX_CELL_DEVIATION_MV = 150; + static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value + static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value + NissanLeafHtmlRenderer renderer; bool is_message_corrupt(CAN_frame rx_frame); diff --git a/Software/src/battery/ORION-BMS.cpp b/Software/src/battery/ORION-BMS.cpp index 8d15a532..a8688b56 100644 --- a/Software/src/battery/ORION-BMS.cpp +++ b/Software/src/battery/ORION-BMS.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef ORION_BMS +#include "ORION-BMS.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "ORION-BMS.h" +#include "../include.h" void findMinMaxCellvoltages(const uint16_t arr[], size_t size, uint16_t& Minimum_Cell_Voltage, uint16_t& Maximum_Cell_Voltage) { @@ -115,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; @@ -124,5 +123,3 @@ void OrionBms::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.system.status.battery_allows_contactor_closing = true; } - -#endif diff --git a/Software/src/battery/ORION-BMS.h b/Software/src/battery/ORION-BMS.h index e2785059..6fa17fc3 100644 --- a/Software/src/battery/ORION-BMS.h +++ b/Software/src/battery/ORION-BMS.h @@ -5,8 +5,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef ORION_BMS #define SELECTED_BATTERY_CLASS OrionBms +#endif class OrionBms : public CanBattery { public: @@ -14,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.cpp b/Software/src/battery/PYLON-BATTERY.cpp index 02a8c96b..469f9a7a 100644 --- a/Software/src/battery/PYLON-BATTERY.cpp +++ b/Software/src/battery/PYLON-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef PYLON_BATTERY +#include "PYLON-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "PYLON-BATTERY.h" +#include "../include.h" void PylonBattery::update_values() { @@ -144,5 +143,3 @@ void PylonBattery::setup(void) { // Performs one time setup at startup *allows_contactor_closing = true; } } - -#endif diff --git a/Software/src/battery/PYLON-BATTERY.h b/Software/src/battery/PYLON-BATTERY.h index cf21aeb5..9b5bb88d 100644 --- a/Software/src/battery/PYLON-BATTERY.h +++ b/Software/src/battery/PYLON-BATTERY.h @@ -6,8 +6,9 @@ #include "../include.h" #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef PYLON_BATTERY #define SELECTED_BATTERY_CLASS PylonBattery +#endif class PylonBattery : public CanBattery { public: @@ -30,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 e653b673..bfa50b6d 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef RANGE_ROVER_PHEV_BATTERY +#include "RANGE-ROVER-PHEV-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "RANGE-ROVER-PHEV-BATTERY.h" +#include "../include.h" /* TODO - LOG files from vehicle needed to determine CAN content needed to send towards battery! @@ -208,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; @@ -216,5 +215,3 @@ void RangeRoverPhevBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; } - -#endif //RANGE_ROVER_PHEV_BATTERY diff --git a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h index d1f1b13a..d3d4da37 100644 --- a/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h +++ b/Software/src/battery/RANGE-ROVER-PHEV-BATTERY.h @@ -5,8 +5,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef RANGE_ROVER_PHEV_BATTERY #define SELECTED_BATTERY_CLASS RangeRoverPhevBattery +#endif class RangeRoverPhevBattery : public CanBattery { public: @@ -14,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 6c6606b2..db43b95b 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef RENAULT_KANGOO_BATTERY +#include "RENAULT-KANGOO-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "RENAULT-KANGOO-BATTERY.h" +#include "../include.h" /* TODO: There seems to be some values on the Kangoo that differ between the 22/33 kWh version @@ -182,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; @@ -191,5 +190,3 @@ void RenaultKangooBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; } - -#endif diff --git a/Software/src/battery/RENAULT-KANGOO-BATTERY.h b/Software/src/battery/RENAULT-KANGOO-BATTERY.h index 535f0369..3a6f6732 100644 --- a/Software/src/battery/RENAULT-KANGOO-BATTERY.h +++ b/Software/src/battery/RENAULT-KANGOO-BATTERY.h @@ -5,8 +5,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef RENAULT_KANGOO_BATTERY #define SELECTED_BATTERY_CLASS RenaultKangooBattery +#endif class RenaultKangooBattery : public CanBattery { public: @@ -14,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 1dffca86..21aa8a46 100644 --- a/Software/src/battery/RENAULT-TWIZY.cpp +++ b/Software/src/battery/RENAULT-TWIZY.cpp @@ -1,9 +1,8 @@ +#include "RENAULT-TWIZY.h" #include -#include "../include.h" -#ifdef RENAULT_TWIZY_BATTERY #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "RENAULT-TWIZY.h" +#include "../include.h" int16_t max_value(int16_t* entries, size_t len) { int result = INT16_MIN; @@ -119,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; @@ -129,5 +128,3 @@ void RenaultTwizyBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.total_capacity_Wh = 6600; datalayer.system.status.battery_allows_contactor_closing = true; } - -#endif diff --git a/Software/src/battery/RENAULT-TWIZY.h b/Software/src/battery/RENAULT-TWIZY.h index f4ab501c..f2388a4f 100644 --- a/Software/src/battery/RENAULT-TWIZY.h +++ b/Software/src/battery/RENAULT-TWIZY.h @@ -3,8 +3,9 @@ #include "../include.h" #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef RENAULT_TWIZY_BATTERY #define SELECTED_BATTERY_CLASS RenaultTwizyBattery +#endif class RenaultTwizyBattery : public CanBattery { public: @@ -12,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 55823db5..fa64d9f4 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef RENAULT_ZOE_GEN1_BATTERY +#include "RENAULT-ZOE-GEN1-BATTERY.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" #include "../devboard/utils/events.h" -#include "RENAULT-ZOE-GEN1-BATTERY.h" +#include "../include.h" void transmit_can_frame(CAN_frame* tx_frame, int interface); @@ -514,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; @@ -524,5 +523,3 @@ void RenaultZoeGen1Battery::setup(void) { // Performs one time setup at startup datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; } - -#endif diff --git a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h index 9243c94a..8b15d164 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN1-BATTERY.h @@ -4,14 +4,9 @@ #include "CanBattery.h" #include "RENAULT-ZOE-GEN1-HTML.h" -#define BATTERY_SELECTED +#ifdef RENAULT_ZOE_GEN1_BATTERY #define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery - -#define MAX_PACK_VOLTAGE_DV 4200 //5000 = 500.0V -#define MIN_PACK_VOLTAGE_DV 3000 -#define MAX_CELL_DEVIATION_MV 150 -#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value -#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value +#endif class RenaultZoeGen1Battery : public CanBattery { public: @@ -36,10 +31,17 @@ 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; } private: + static const int MAX_PACK_VOLTAGE_DV = 4200; //5000 = 500.0V + static const int MIN_PACK_VOLTAGE_DV = 3000; + static const int MAX_CELL_DEVIATION_MV = 150; + static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value + static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value + RenaultZoeGen1HtmlRenderer renderer; DATALAYER_BATTERY_TYPE* datalayer_battery; diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp index 462825bf..4970f7bd 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef RENAULT_ZOE_GEN2_BATTERY +#include "RENAULT-ZOE-GEN2-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../devboard/utils/events.h" -#include "RENAULT-ZOE-GEN2-BATTERY.h" +#include "../include.h" /* TODO - Add //NVROL Reset @@ -705,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; @@ -788,5 +787,3 @@ void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) { break; } } - -#endif diff --git a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h index 0133d639..2d908345 100644 --- a/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h +++ b/Software/src/battery/RENAULT-ZOE-GEN2-BATTERY.h @@ -5,8 +5,9 @@ #include "CanBattery.h" #include "RENAULT-ZOE-GEN2-HTML.h" -#define BATTERY_SELECTED +#ifdef RENAULT_ZOE_GEN2_BATTERY #define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery +#endif class RenaultZoeGen2Battery : public CanBattery { public: @@ -14,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 745015ea..fda2c42b 100644 --- a/Software/src/battery/RJXZS-BMS.cpp +++ b/Software/src/battery/RJXZS-BMS.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef RJXZS_BMS +#include "RJXZS-BMS.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "RJXZS-BMS.h" +#include "../include.h" void RjxzsBms::update_values() { @@ -517,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; @@ -525,5 +524,3 @@ void RjxzsBms::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.system.status.battery_allows_contactor_closing = true; } - -#endif // RJXZS_BMS diff --git a/Software/src/battery/RJXZS-BMS.h b/Software/src/battery/RJXZS-BMS.h index baabbbb5..569c62e5 100644 --- a/Software/src/battery/RJXZS-BMS.h +++ b/Software/src/battery/RJXZS-BMS.h @@ -5,8 +5,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef RJXZS_BMS #define SELECTED_BATTERY_CLASS RjxzsBms +#endif class RjxzsBms : public CanBattery { public: @@ -14,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 e012efbf..783e42b4 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef SANTA_FE_PHEV_BATTERY +#include "SANTA-FE-PHEV-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "SANTA-FE-PHEV-BATTERY.h" +#include "../include.h" /* Credits go to maciek16c for these findings! https://github.com/maciek16c/hyundai-santa-fe-phev-battery @@ -320,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; @@ -333,5 +332,3 @@ void SantaFePhevBattery::setup(void) { // Performs one time setup at startup *allows_contactor_closing = true; } } - -#endif diff --git a/Software/src/battery/SANTA-FE-PHEV-BATTERY.h b/Software/src/battery/SANTA-FE-PHEV-BATTERY.h index f73e80e3..c926a883 100644 --- a/Software/src/battery/SANTA-FE-PHEV-BATTERY.h +++ b/Software/src/battery/SANTA-FE-PHEV-BATTERY.h @@ -5,8 +5,9 @@ #include "../include.h" #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef SANTA_FE_PHEV_BATTERY #define SELECTED_BATTERY_CLASS SantaFePhevBattery +#endif class SantaFePhevBattery : public CanBattery { public: @@ -26,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 8c4ad9b7..3d6e8379 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.cpp +++ b/Software/src/battery/SIMPBMS-BATTERY.cpp @@ -1,8 +1,7 @@ -#include "../include.h" -#ifdef SIMPBMS_BATTERY +#include "SIMPBMS-BATTERY.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "SIMPBMS-BATTERY.h" +#include "../include.h" void SimpBmsBattery::update_values() { @@ -93,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; @@ -102,5 +101,3 @@ void SimpBmsBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.system.status.battery_allows_contactor_closing = true; } - -#endif diff --git a/Software/src/battery/SIMPBMS-BATTERY.h b/Software/src/battery/SIMPBMS-BATTERY.h index ac33580d..4d415147 100644 --- a/Software/src/battery/SIMPBMS-BATTERY.h +++ b/Software/src/battery/SIMPBMS-BATTERY.h @@ -5,8 +5,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef SIMPBMS_BATTERY #define SELECTED_BATTERY_CLASS SimpBmsBattery +#endif class SimpBmsBattery : public CanBattery { public: @@ -14,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 a5d7095e..47631cc2 100644 --- a/Software/src/battery/SONO-BATTERY.cpp +++ b/Software/src/battery/SONO-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef SONO_BATTERY +#include "SONO-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../devboard/utils/events.h" -#include "SONO-BATTERY.h" +#include "../include.h" void SonoBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus @@ -141,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; @@ -152,5 +151,3 @@ void SonoBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; } - -#endif diff --git a/Software/src/battery/SONO-BATTERY.h b/Software/src/battery/SONO-BATTERY.h index 47907f5e..b1e366ad 100644 --- a/Software/src/battery/SONO-BATTERY.h +++ b/Software/src/battery/SONO-BATTERY.h @@ -5,8 +5,9 @@ #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef SONO_BATTERY #define SELECTED_BATTERY_CLASS SonoBattery +#endif class SonoBattery : public CanBattery { public: @@ -14,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/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/battery/Shunts.cpp b/Software/src/battery/Shunts.cpp index f572a201..35a0b98c 100644 --- a/Software/src/battery/Shunts.cpp +++ b/Software/src/battery/Shunts.cpp @@ -4,6 +4,10 @@ CanShunt* shunt = nullptr; void setup_can_shunt() { + if (shunt) { + return; + } + #if defined(SELECTED_SHUNT_CLASS) shunt = new SELECTED_SHUNT_CLASS(); if (shunt) { diff --git a/Software/src/battery/TESLA-BATTERY.cpp b/Software/src/battery/TESLA-BATTERY.cpp index e7f7e7a2..65215b63 100644 --- a/Software/src/battery/TESLA-BATTERY.cpp +++ b/Software/src/battery/TESLA-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef TESLA_BATTERY +#include "TESLA-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For Advanced Battery Insights webpage #include "../devboard/utils/events.h" -#include "TESLA-BATTERY.h" +#include "../include.h" /* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */ @@ -1764,8 +1763,7 @@ void TeslaModel3YBattery::setup(void) { // Performs one time setup at startup *allows_contactor_closing = true; } -#ifdef TESLA_MODEL_3Y_BATTERY // Model 3/Y can be either LFP or NCM/A - strncpy(datalayer.system.info.battery_protocol, "Tesla Model 3/Y", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; #ifdef LFP_CHEMISTRY datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; @@ -1781,7 +1779,6 @@ void TeslaModel3YBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM; #endif // !LFP_CHEMISTRY -#endif // TESLA_MODEL_3Y_BATTERY } void TeslaModelSXBattery::setup(void) { @@ -1789,7 +1786,7 @@ void TeslaModelSXBattery::setup(void) { *allows_contactor_closing = true; } - strncpy(datalayer.system.info.battery_protocol, "Tesla Model S/X", 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_SX_NCMA; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA; @@ -1797,5 +1794,3 @@ void TeslaModelSXBattery::setup(void) { datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM; } - -#endif // TESLA_BATTERY diff --git a/Software/src/battery/TESLA-BATTERY.h b/Software/src/battery/TESLA-BATTERY.h index 7b60cb91..fab9ba65 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 @@ -514,12 +513,14 @@ class TeslaModel3YBattery : public TeslaBattery { operate_contactors = true; #endif } + static constexpr char* Name = "Tesla Model 3/Y"; virtual void setup(void); }; class TeslaModelSXBattery : public TeslaBattery { public: TeslaModelSXBattery() { operate_contactors = true; } + static constexpr char* Name = "Tesla Model S/X"; virtual void setup(void); }; diff --git a/Software/src/battery/TEST-FAKE-BATTERY.cpp b/Software/src/battery/TEST-FAKE-BATTERY.cpp index 845ecbed..c3c91f7a 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.cpp +++ b/Software/src/battery/TEST-FAKE-BATTERY.cpp @@ -1,9 +1,8 @@ -#include "../include.h" -#ifdef TEST_FAKE_BATTERY -#include "../datalayer/datalayer.h" #include "TEST-FAKE-BATTERY.h" +#include "../datalayer/datalayer.h" +#include "../include.h" -void print_units(char* header, int value, char* units) { +static void print_units(char* header, int value, char* units) { logging.print(header); logging.print(value); logging.print(units); @@ -75,7 +74,7 @@ void TestFakeBattery::transmit_can(unsigned long currentMillis) { void TestFakeBattery::setup(void) { // Performs one time setup at startup randomSeed(analogRead(0)); - strncpy(datalayer.system.info.battery_protocol, "Fake battery for testing purposes", 63); + strncpy(datalayer.system.info.battery_protocol, Name, 63); datalayer.system.info.battery_protocol[63] = '\0'; datalayer_battery->info.max_design_voltage_dV = @@ -87,5 +86,3 @@ void TestFakeBattery::setup(void) { // Performs one time setup at startup *allows_contactor_closing = true; } } - -#endif diff --git a/Software/src/battery/TEST-FAKE-BATTERY.h b/Software/src/battery/TEST-FAKE-BATTERY.h index ed79c8b1..ed2ee462 100644 --- a/Software/src/battery/TEST-FAKE-BATTERY.h +++ b/Software/src/battery/TEST-FAKE-BATTERY.h @@ -4,8 +4,9 @@ #include "../include.h" #include "CanBattery.h" -#define BATTERY_SELECTED +#ifdef TEST_FAKE_BATTERY #define SELECTED_BATTERY_CLASS TestFakeBattery +#endif class TestFakeBattery : public CanBattery { public: @@ -21,6 +22,8 @@ class TestFakeBattery : public CanBattery { allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing; } + static constexpr char* Name = "Fake battery for testing purposes"; + virtual void setup(); virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void update_values(); diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.cpp b/Software/src/battery/VOLVO-SPA-BATTERY.cpp index 25aeb02f..fe27647c 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef VOLVO_SPA_BATTERY +#include "VOLVO-SPA-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../devboard/utils/events.h" -#include "VOLVO-SPA-BATTERY.h" +#include "../include.h" void VolvoSpaBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter @@ -378,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) @@ -388,4 +387,3 @@ void VolvoSpaBattery::setup(void) { // Performs one time setup at startup datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; } -#endif diff --git a/Software/src/battery/VOLVO-SPA-BATTERY.h b/Software/src/battery/VOLVO-SPA-BATTERY.h index 4ea009ed..1e90237c 100644 --- a/Software/src/battery/VOLVO-SPA-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-BATTERY.h @@ -6,8 +6,9 @@ #include "CanBattery.h" #include "VOLVO-SPA-HTML.h" -#define BATTERY_SELECTED +#ifdef VOLVO_SPA_BATTERY #define SELECTED_BATTERY_CLASS VolvoSpaBattery +#endif class VolvoSpaBattery : public CanBattery { public: @@ -15,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 f210acd6..19507a7c 100644 --- a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.cpp @@ -1,10 +1,9 @@ -#include "../include.h" -#ifdef VOLVO_SPA_HYBRID_BATTERY +#include "VOLVO-SPA-HYBRID-BATTERY.h" #include "../communication/can/comm_can.h" #include "../datalayer/datalayer.h" #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../devboard/utils/events.h" -#include "VOLVO-SPA-HYBRID-BATTERY.h" +#include "../include.h" void VolvoSpaHybridBattery:: update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter @@ -553,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; @@ -563,4 +562,3 @@ void VolvoSpaHybridBattery::setup(void) { // datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; } -#endif diff --git a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h index 70b58cb8..e31c9967 100644 --- a/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h +++ b/Software/src/battery/VOLVO-SPA-HYBRID-BATTERY.h @@ -6,8 +6,9 @@ #include "CanBattery.h" #include "VOLVO-SPA-HYBRID-HTML.h" -#define BATTERY_SELECTED +#ifdef VOLVO_SPA_HYBRID_BATTERY #define SELECTED_BATTERY_CLASS VolvoSpaHybridBattery +#endif class VolvoSpaHybridBattery : public CanBattery { public: @@ -15,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/charger/CHARGERS.cpp b/Software/src/charger/CHARGERS.cpp index 9924459a..6ba343b9 100644 --- a/Software/src/charger/CHARGERS.cpp +++ b/Software/src/charger/CHARGERS.cpp @@ -2,8 +2,42 @@ CanCharger* charger = nullptr; +ChargerType user_selected_charger_type = ChargerType::None; + +std::vector supported_charger_types() { + std::vector types; + + for (int i = 0; i < (int)ChargerType::Highest; i++) { + types.push_back((ChargerType)i); + } + + return types; +} + +extern const char* name_for_charger_type(ChargerType type) { + switch (type) { + case ChargerType::ChevyVolt: + return ChevyVoltCharger::Name; + case ChargerType::NissanLeaf: + return NissanLeafCharger::Name; + case ChargerType::None: + return "None"; + } + + return nullptr; +} + void setup_charger() { +#ifdef COMMON_IMAGE + switch (user_selected_charger_type) { + case ChargerType::ChevyVolt: + charger = new ChevyVoltCharger(); + case ChargerType::NissanLeaf: + charger = new NissanLeafCharger(); + } +#else #ifdef SELECTED_CHARGER_CLASS charger = new SELECTED_CHARGER_CLASS(); #endif +#endif } diff --git a/Software/src/charger/CHEVY-VOLT-CHARGER.h b/Software/src/charger/CHEVY-VOLT-CHARGER.h index d7f11668..1b7ad4d0 100644 --- a/Software/src/charger/CHEVY-VOLT-CHARGER.h +++ b/Software/src/charger/CHEVY-VOLT-CHARGER.h @@ -13,7 +13,8 @@ class ChevyVoltCharger : public CanCharger { public: ChevyVoltCharger() : CanCharger(ChargerType::ChevyVolt) {} - const char* name() { return "Chevy Volt Gen1 Charger"; } + const char* name() { return Name; } + static constexpr char* Name = "Chevy Volt Gen1 Charger"; void map_can_frame_to_variable(CAN_frame rx_frame); void transmit_can(unsigned long currentMillis); diff --git a/Software/src/charger/CanCharger.h b/Software/src/charger/CanCharger.h index 7e75391f..5f8efed4 100644 --- a/Software/src/charger/CanCharger.h +++ b/Software/src/charger/CanCharger.h @@ -7,7 +7,12 @@ #include "src/communication/Transmitter.h" #include "src/communication/can/CanReceiver.h" -enum class ChargerType { NissanLeaf, ChevyVolt }; +enum class ChargerType { None, NissanLeaf, ChevyVolt, Highest }; + +extern ChargerType user_selected_charger_type; + +extern std::vector supported_charger_types(); +extern const char* name_for_charger_type(ChargerType type); // Generic base class for all chargers class Charger { diff --git a/Software/src/charger/NISSAN-LEAF-CHARGER.h b/Software/src/charger/NISSAN-LEAF-CHARGER.h index d4aba6ba..1fa6f74d 100644 --- a/Software/src/charger/NISSAN-LEAF-CHARGER.h +++ b/Software/src/charger/NISSAN-LEAF-CHARGER.h @@ -13,7 +13,9 @@ class NissanLeafCharger : public CanCharger { public: NissanLeafCharger() : CanCharger(ChargerType::NissanLeaf) {} - const char* name() { return "Nissan LEAF 2013-2024 PDM charger"; } + const char* name() { return Name; } + static constexpr char* Name = "Nissan LEAF 2013-2024 PDM charger"; + void map_can_frame_to_variable(CAN_frame rx_frame); void transmit_can(unsigned long currentMillis); 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 5180f884..870163ab 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.cpp @@ -1,19 +1,50 @@ #include "comm_contactorcontrol.h" #include "../../include.h" -// Parameters -#ifndef CONTACTOR_CONTROL -#ifdef PWM_CONTACTOR_CONTROL -#error CONTACTOR_CONTROL needs to be enabled for PWM_CONTACTOR_CONTROL -#endif -#endif - #ifdef CONTACTOR_CONTROL +const bool contactor_control_enabled_default = true; +#else +const bool contactor_control_enabled_default = false; +#endif +bool contactor_control_enabled = contactor_control_enabled_default; + +#ifdef PWM_CONTACTOR_CONTROL +const bool pwn_contactor_control_default = true; +#else +const bool pwn_contactor_control_default = false; +#endif +bool pwm_contactor_control = pwn_contactor_control_default; + +#ifdef PERIODIC_BMS_RESET +const bool periodic_bms_reset_default = true; +#else +const bool periodic_bms_reset_default = false; +#endif +bool periodic_bms_reset = periodic_bms_reset_default; + +#ifdef REMOTE_BMS_RESET +const bool remote_bms_reset_default = true; +#else +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 + enum State { DISCONNECTED, START_PRECHARGE, PRECHARGE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED }; State contactorStatus = DISCONNECTED; -#define ON 1 -#define OFF 0 +const int ON = 1; +const int OFF = 0; #ifdef NC_CONTACTORS //Normally closed contactors use inverted logic #undef ON @@ -34,11 +65,10 @@ State contactorStatus = DISCONNECTED; #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; -#endif unsigned long currentTime = 0; unsigned long lastPowerRemovalTime = 0; unsigned long bmsPowerOnTime = 0; @@ -47,12 +77,12 @@ const unsigned long powerRemovalDuration = 30000; // 30 seconds i const unsigned long bmsWarmupDuration = 3000; void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) { -#ifdef PWM_CONTACTOR_CONTROL - if (pwm_freq != 0xFFFF) { - ledcWrite(pin, pwm_freq); - return; + if (pwm_contactor_control) { + if (pwm_freq != 0xFFFF) { + ledcWrite(pin, pwm_freq); + return; + } } -#endif if (direction == 1) { digitalWrite(pin, HIGH); } else { // 0 @@ -64,36 +94,39 @@ void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) { void init_contactors() { // Init contactor pins -#ifdef CONTACTOR_CONTROL -#ifdef PWM_CONTACTOR_CONTROL - // Setup PWM Channel Frequency and Resolution - ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Positive_Channel); - ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Negative_Channel); - // Set all pins OFF (0% PWM) - ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY); - ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY); -#else //Normal CONTACTOR_CONTROL - pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT); - set(POSITIVE_CONTACTOR_PIN, OFF); - pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT); - set(NEGATIVE_CONTACTOR_PIN, OFF); -#endif // Precharge never has PWM regardless of setting - pinMode(PRECHARGE_PIN, OUTPUT); - set(PRECHARGE_PIN, OFF); -#endif // CONTACTOR_CONTROL -#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY - pinMode(SECOND_BATTERY_CONTACTORS_PIN, OUTPUT); - set(SECOND_BATTERY_CONTACTORS_PIN, OFF); -#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY + if (contactor_control_enabled) { + if (pwm_contactor_control) { + // Setup PWM Channel Frequency and Resolution + ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Positive_Channel); + ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Negative_Channel); + // Set all pins OFF (0% PWM) + ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY); + ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY); + } else { //Normal CONTACTOR_CONTROL + pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT); + set(POSITIVE_CONTACTOR_PIN, OFF); + pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT); + set(NEGATIVE_CONTACTOR_PIN, OFF); + } // Precharge never has PWM regardless of setting + pinMode(PRECHARGE_PIN, OUTPUT); + set(PRECHARGE_PIN, OFF); + } + if (contactor_control_enabled_double_battery) { + pinMode(SECOND_BATTERY_CONTACTORS_PIN, OUTPUT); + set(SECOND_BATTERY_CONTACTORS_PIN, OFF); + } // 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); -#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); -#endif //PERIODIC_BMS_RESET +#endif // HW with dedicated BMS pins + +#ifdef BMS_POWER + if (periodic_bms_reset || remote_bms_reset) { + pinMode(BMS_POWER, OUTPUT); + digitalWrite(BMS_POWER, HIGH); + } +#endif } static void dbg_contactors(const char* state) { @@ -107,110 +140,114 @@ 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() { -#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 +#endif #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY handle_contactors_battery2(); #endif // CONTACTOR_CONTROL_DOUBLE_BATTERY -#ifdef CONTACTOR_CONTROL - // First check if we have any active errors, incase we do, turn off the battery - if (datalayer.battery.status.bms_status == FAULT) { - timeSpentInFaultedMode++; - } else { - timeSpentInFaultedMode = 0; - } + if (contactor_control_enabled) { + // First check if we have any active errors, incase we do, turn off the battery + if (datalayer.battery.status.bms_status == FAULT) { + timeSpentInFaultedMode++; + } else { + timeSpentInFaultedMode = 0; + } - //handle contactor control SHUTDOWN_REQUESTED - if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) { - contactorStatus = SHUTDOWN_REQUESTED; - } + //handle contactor control SHUTDOWN_REQUESTED + if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) { + contactorStatus = SHUTDOWN_REQUESTED; + } - if (contactorStatus == SHUTDOWN_REQUESTED) { - set(PRECHARGE_PIN, OFF); - set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); - set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); - set_event(EVENT_ERROR_OPEN_CONTACTOR, 0); - datalayer.system.status.contactors_engaged = false; - return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured) - } + if (contactorStatus == SHUTDOWN_REQUESTED) { + set(PRECHARGE_PIN, OFF); + set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); + set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); + set_event(EVENT_ERROR_OPEN_CONTACTOR, 0); + datalayer.system.status.contactors_engaged = false; + return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured) + } - // After that, check if we are OK to start turning on the battery - if (contactorStatus == DISCONNECTED) { - set(PRECHARGE_PIN, OFF); - set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); - set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); - datalayer.system.status.contactors_engaged = false; + // After that, check if we are OK to start turning on the battery + if (contactorStatus == DISCONNECTED) { + set(PRECHARGE_PIN, OFF); + set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); + set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); + datalayer.system.status.contactors_engaged = false; - if (datalayer.system.status.battery_allows_contactor_closing && - datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.settings.equipment_stop_active) { - contactorStatus = START_PRECHARGE; + if (datalayer.system.status.battery_allows_contactor_closing && + datalayer.system.status.inverter_allows_contactor_closing && + !datalayer.system.settings.equipment_stop_active) { + contactorStatus = START_PRECHARGE; + } + } + + // In case the inverter requests contactors to open, set the state accordingly + if (contactorStatus == COMPLETED) { + //Incase inverter (or estop) requests contactors to open, make state machine jump to Disconnected state (recoverable) + if (!datalayer.system.status.inverter_allows_contactor_closing || + datalayer.system.settings.equipment_stop_active) { + contactorStatus = DISCONNECTED; + } + // Skip running the state machine below if it has already completed + return; + } + + currentTime = millis(); + + if (currentTime < INTERVAL_10_S) { + // Skip running the state machine before system has started up. + // Gives the system some time to detect any faults from battery before blindly just engaging the contactors + return; + } + + // Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge + switch (contactorStatus) { + case START_PRECHARGE: + set(NEGATIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY); + dbg_contactors("NEGATIVE"); + prechargeStartTime = currentTime; + contactorStatus = PRECHARGE; + break; + + case PRECHARGE: + if (currentTime - prechargeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) { + set(PRECHARGE_PIN, ON); + dbg_contactors("PRECHARGE"); + negativeStartTime = currentTime; + contactorStatus = POSITIVE; + } + break; + + case POSITIVE: + if (currentTime - negativeStartTime >= PRECHARGE_TIME_MS) { + set(POSITIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY); + dbg_contactors("POSITIVE"); + prechargeCompletedTime = currentTime; + contactorStatus = PRECHARGE_OFF; + } + break; + + case PRECHARGE_OFF: + if (currentTime - prechargeCompletedTime >= PRECHARGE_COMPLETED_TIME_MS) { + set(PRECHARGE_PIN, OFF); + set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY); + set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY); + dbg_contactors("PRECHARGE_OFF"); + contactorStatus = COMPLETED; + datalayer.system.status.contactors_engaged = true; + } + break; + default: + break; } } - - // In case the inverter requests contactors to open, set the state accordingly - if (contactorStatus == COMPLETED) { - //Incase inverter (or estop) requests contactors to open, make state machine jump to Disconnected state (recoverable) - if (!datalayer.system.status.inverter_allows_contactor_closing || datalayer.system.settings.equipment_stop_active) { - contactorStatus = DISCONNECTED; - } - // Skip running the state machine below if it has already completed - return; - } - - currentTime = millis(); - - if (currentTime < INTERVAL_10_S) { - // Skip running the state machine before system has started up. - // Gives the system some time to detect any faults from battery before blindly just engaging the contactors - return; - } - - // Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge - switch (contactorStatus) { - case START_PRECHARGE: - set(NEGATIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY); - dbg_contactors("NEGATIVE"); - prechargeStartTime = currentTime; - contactorStatus = PRECHARGE; - break; - - case PRECHARGE: - if (currentTime - prechargeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) { - set(PRECHARGE_PIN, ON); - dbg_contactors("PRECHARGE"); - negativeStartTime = currentTime; - contactorStatus = POSITIVE; - } - break; - - case POSITIVE: - if (currentTime - negativeStartTime >= PRECHARGE_TIME_MS) { - set(POSITIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY); - dbg_contactors("POSITIVE"); - prechargeCompletedTime = currentTime; - contactorStatus = PRECHARGE_OFF; - } - break; - - case PRECHARGE_OFF: - if (currentTime - prechargeCompletedTime >= PRECHARGE_COMPLETED_TIME_MS) { - set(PRECHARGE_PIN, OFF); - set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY); - set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY); - dbg_contactors("PRECHARGE_OFF"); - contactorStatus = COMPLETED; - datalayer.system.status.contactors_engaged = true; - } - break; - default: - break; - } -#endif // CONTACTOR_CONTROL } #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY @@ -223,7 +260,7 @@ void handle_contactors_battery2() { datalayer.system.status.contactors_battery2_engaged = false; } } -#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY +#endif /* PERIODIC_BMS_RESET - Once every 24 hours we remove power from the BMS_power pin for 30 seconds. REMOTE_BMS_RESET - Allows the user to remotely powercycle the BMS by sending a command to the emulator via MQTT. @@ -232,58 +269,62 @@ 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 defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET) - // Get current time - currentTime = millis(); + if (periodic_bms_reset || remote_bms_reset) { + // Get current time + currentTime = millis(); -#ifdef PERIODIC_BMS_RESET - // Check if 24 hours have passed since the last power removal - if ((currentTime + bmsResetTimeOffset) - lastPowerRemovalTime >= powerRemovalInterval) { - start_bms_reset(); - } -#endif //PERIODIC_BMS_RESET + if (periodic_bms_reset) { + // Check if 24 hours have passed since the last power removal + if ((currentTime + bmsResetTimeOffset) - lastPowerRemovalTime >= powerRemovalInterval) { + start_bms_reset(); + } + } - // If power has been removed for 30 seconds, restore the power - if (datalayer.system.status.BMS_reset_in_progress && currentTime - lastPowerRemovalTime >= powerRemovalDuration) { - // Reapply power to the BMS - digitalWrite(BMS_POWER, HIGH); + // If power has been removed for 30 seconds, restore the power + if (datalayer.system.status.BMS_reset_in_progress && currentTime - lastPowerRemovalTime >= powerRemovalDuration) { + // Reapply power to the BMS + digitalWrite(BMS_POWER, HIGH); #ifdef BMS_2_POWER - digitalWrite(BMS_2_POWER, HIGH); // Same for battery 2 + digitalWrite(BMS_2_POWER, HIGH); // Same for battery 2 #endif - bmsPowerOnTime = currentTime; - datalayer.system.status.BMS_reset_in_progress = false; // Reset the power removal flag - datalayer.system.status.BMS_startup_in_progress = true; // Set the BMS warmup flag - } - //if power has been restored we need to wait a couple of seconds to unpause the battery - if (datalayer.system.status.BMS_startup_in_progress && currentTime - bmsPowerOnTime >= bmsWarmupDuration) { + bmsPowerOnTime = currentTime; + datalayer.system.status.BMS_reset_in_progress = false; // Reset the power removal flag + datalayer.system.status.BMS_startup_in_progress = true; // Set the BMS warmup flag + } + //if power has been restored we need to wait a couple of seconds to unpause the battery + if (datalayer.system.status.BMS_startup_in_progress && currentTime - bmsPowerOnTime >= bmsWarmupDuration) { - setBatteryPause(false, false, false, false); + setBatteryPause(false, false, false, false); - datalayer.system.status.BMS_startup_in_progress = false; // Reset the BMS warmup removal flag - set_event(EVENT_PERIODIC_BMS_RESET, 0); + datalayer.system.status.BMS_startup_in_progress = false; // Reset the BMS warmup removal flag + set_event(EVENT_PERIODIC_BMS_RESET, 0); + } } -#endif //defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET) } +#endif void start_bms_reset() { -#if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET) - if (!datalayer.system.status.BMS_reset_in_progress) { - lastPowerRemovalTime = currentTime; // Record the time when BMS reset was started - // we are now resetting at the correct time. We don't need to offset anymore - bmsResetTimeOffset = 0; - // Set a flag to let the rest of the system know we are cutting power to the BMS. - // The battery CAN sending routine will then know not to try guto send anything towards battery while active - datalayer.system.status.BMS_reset_in_progress = true; + if (periodic_bms_reset || remote_bms_reset) { + if (!datalayer.system.status.BMS_reset_in_progress) { + lastPowerRemovalTime = currentTime; // Record the time when BMS reset was started + // we are now resetting at the correct time. We don't need to offset anymore + bmsResetTimeOffset = 0; + // Set a flag to let the rest of the system know we are cutting power to the BMS. + // The battery CAN sending routine will then know not to try guto send anything towards battery while active + datalayer.system.status.BMS_reset_in_progress = true; - // Set emulator state to paused (Max Charge/Discharge = 0 & CAN = stop) - // We try to keep contactors engaged during this pause, and just ramp power down to 0. - setBatteryPause(true, false, false, false); + // Set emulator state to paused (Max Charge/Discharge = 0 & CAN = stop) + // We try to keep contactors engaged during this pause, and just ramp power down to 0. + setBatteryPause(true, false, false, false); - digitalWrite(BMS_POWER, LOW); // Remove power by setting the BMS power pin to LOW -#ifdef BMS_2_POWER - digitalWrite(BMS_2_POWER, LOW); // Same for battery 2 +#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 + } } -#endif //defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET) } diff --git a/Software/src/communication/contactorcontrol/comm_contactorcontrol.h b/Software/src/communication/contactorcontrol/comm_contactorcontrol.h index febfd118..e18e8431 100644 --- a/Software/src/communication/contactorcontrol/comm_contactorcontrol.h +++ b/Software/src/communication/contactorcontrol/comm_contactorcontrol.h @@ -6,6 +6,13 @@ #include "../../datalayer/datalayer.h" #include "../../devboard/utils/events.h" +// 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; + /** * @brief Handle BMS power output * diff --git a/Software/src/communication/nvm/comm_nvm.cpp b/Software/src/communication/nvm/comm_nvm.cpp index 03afbee0..a4a9b9d1 100644 --- a/Software/src/communication/nvm/comm_nvm.cpp +++ b/Software/src/communication/nvm/comm_nvm.cpp @@ -1,5 +1,6 @@ #include "comm_nvm.h" #include "../../include.h" +#include "../contactorcontrol/comm_contactorcontrol.h" // Parameters Preferences settings; // Store user settings @@ -69,6 +70,19 @@ void init_stored_settings() { datalayer.battery.settings.max_user_set_discharge_voltage_dV = temp; } datalayer.battery.settings.user_set_voltage_limits_active = settings.getBool("USEVOLTLIMITS", false); + +#ifdef COMMON_IMAGE + user_selected_battery_type = (BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None); + user_selected_inverter_protocol = (InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None); + 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); +#endif + settings.end(); } @@ -121,5 +135,6 @@ void store_settings() { if (!settings.putUInt("TARGETDISCHVOLT", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) { set_event(EVENT_PERSISTENT_SAVE_INFO, 11); } + settings.end(); // Close preferences handle } diff --git a/Software/src/communication/nvm/comm_nvm.h b/Software/src/communication/nvm/comm_nvm.h index eb624e89..023a757b 100644 --- a/Software/src/communication/nvm/comm_nvm.h +++ b/Software/src/communication/nvm/comm_nvm.h @@ -34,4 +34,28 @@ 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(bool readOnly = false) { + if (!settings.begin("batterySettings", readOnly)) { + set_event(EVENT_PERSISTENT_SAVE_INFO, 0); + } + } + + ~BatteryEmulatorSettingsStore() { settings.end(); } + + uint32_t getUInt(const char* name, uint32_t defaultValue) { return settings.getUInt(name, defaultValue); } + + void saveUInt(const char* name, uint32_t value) { settings.putUInt(name, value); } + + bool getBool(const char* name) { return settings.getBool(name, false); } + + void saveBool(const char* name, bool value) { settings.putBool(name, value); } + + private: + Preferences settings; +}; + #endif 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 diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index a00b06d5..bf818dec 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -320,17 +320,14 @@ typedef struct { bool BMS_reset_in_progress = false; /** True if the BMS is starting up */ bool BMS_startup_in_progress = false; -#ifdef PRECHARGE_CONTROL + /** State of automatic precharge sequence */ PrechargeState precharge_status = AUTO_PRECHARGE_IDLE; -#endif } DATALAYER_SYSTEM_STATUS_TYPE; typedef struct { bool equipment_stop_active = false; -#ifdef PRECHARGE_CONTROL bool start_precharging = false; -#endif } DATALAYER_SYSTEM_SETTINGS_TYPE; typedef struct { diff --git a/Software/src/devboard/safety/safety.cpp b/Software/src/devboard/safety/safety.cpp index af2a0317..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->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 4892bdd6..6dedcb97 100644 --- a/Software/src/devboard/webserver/settings_html.cpp +++ b/Software/src/devboard/webserver/settings_html.cpp @@ -1,9 +1,72 @@ #include "settings_html.h" #include +#include "../../../src/communication/contactorcontrol/comm_contactorcontrol.h" #include "../../charger/CHARGERS.h" +#include "../../communication/nvm/comm_nvm.h" #include "../../datalayer/datalayer.h" #include "../../include.h" +extern bool settingsUpdated; + +template +constexpr auto to_underlying(E e) noexcept { + return static_cast>(e); +} + +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) { + pairs.push_back(std::pair(String(name), type)); + } + } + + 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; +} + +template +String options_for_enum(TEnum selected, Func name_for_type) { + String options; + auto values = enum_values_and_names(name_for_type); + for (const auto& [name, type] : values) { + options += + (""; + } + return options; +} + +void render_checkbox(String& content, const char* label, bool enabled, const char* name) { + content += ""; + content += "Password: ######## "; +#ifdef COMMON_IMAGE + BatteryEmulatorSettingsStore settings; + + // It's important that we read/write settings directly to settings store instead of the run-time values + // since the run-time values may have direct effect on operation. content += - "

Battery interface: " + battery->interface_name() + "

"; + "
"; + 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"), "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"); + + content += + "
"; + + if (settingsUpdated) { + content += "

Settings saved. Reboot to take the settings into use.

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

Battery interface: " + battery->interface_name() + + "

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

Battery #2 interface: " + battery->interface_name() + + content += "

Battery #2 interface: " + battery2->interface_name() + "

"; } @@ -41,10 +151,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 += ""; @@ -90,14 +200,14 @@ String settings_processor(const String& var) { // Close the block content += ""; - if (battery->supports_set_fake_voltage()) { + if (battery && battery->supports_set_fake_voltage()) { content += "
"; content += "

Fake battery voltage: " + String(battery->get_voltage(), 1) + " V

"; content += "
"; } - if (battery->supports_manual_balancing()) { + if (battery && battery->supports_manual_balancing()) { // Start a new block with grey background color content += "
"; @@ -282,7 +392,7 @@ String settings_processor(const String& var) { "BalMaxDevCellV?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value " "between 300 and 600');}}}"; - if (battery->supports_set_fake_voltage()) { + if (battery && battery->supports_set_fake_voltage()) { content += "function editFakeBatteryVoltage(){var value=prompt('Enter new fake battery " "voltage');if(value!==null){if(value>=0&&value<=5000){var xhr=new " diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 46f3981e..492b2d9d 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1,9 +1,12 @@ #include "webserver.h" #include #include +#include #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" #include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h" @@ -38,6 +41,9 @@ 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 that need a reboot to be effective. +bool settingsUpdated = false; + CAN_frame currentFrame = {.FD = true, .ext_ID = false, .DLC = 64, .ID = 0x12F, .data = {0}}; void handleFileUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, @@ -382,6 +388,69 @@ 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, [boolSettingNames](AsyncWebServerRequest* request) { + BatteryEmulatorSettingsStore settings; + + 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())); + settings.saveUInt("INVTYPE", (int)type); + } else if (p->name() == "battery") { + auto type = static_cast(atoi(p->value().c_str())); + settings.saveUInt("BATTTYPE", (int)type); + } else if (p->name() == "charger") { + auto type = static_cast(atoi(p->value().c_str())); + settings.saveUInt("CHGTYPE", (int)type); + } /*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") { + settings.saveBool("CNTCTRLDBL", p->value() == "on"); + } else if (p->name() == "pwmcontctrl") { + settings.saveBool("PWMCNTCTRL", p->value() == "on"); + } else if (p->name() == "PERBMSRESET") { + 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); + } + } + + settingsUpdated = true; + request->redirect("/settings"); + }); +#endif + // Route for editing SSID server.on("/updateSSID", HTTP_GET, [](AsyncWebServerRequest* request) { if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) @@ -899,6 +968,10 @@ String processor(const String& var) { // Start content block content += "
"; content += "

Software: " + String(version_number); + +#ifdef COMMON_IMAGE + content += " (Common image) "; +#endif // Show hardware used: #ifdef HW_LILYGO content += " Hardware: LilyGo T-CAN485"; @@ -942,310 +1015,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 - content += "

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

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

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

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

"; -#endif - - 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)

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

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

"; } - 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.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

"; - - 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->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)"; + if (battery) { + content += "

Battery protocol: "; + content += datalayer.system.info.battery_protocol; + if (battery2) { + content += " (Double battery)"; } - } - 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)"; + 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 += "
"; } - content += "

Battery allows contactor closing: "; - if (datalayer.system.status.battery_allows_contactor_closing == true) { - content += ""; - } else { - content += ""; - } + if (battery) { + if (battery2) { + // Start a new block with a specific background color. Color changes depending on BMS status + content += "
"; + content += "
"; - 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) + @@ -1260,110 +1121,334 @@ 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"; + 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) { + if (pwm_contactor_control) { + content += "

Cont. Neg.: "; + 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 + content += "

Cont. Neg.: "; + if (digitalRead(SECOND_BATTERY_CONTACTORS_PIN) == HIGH) { + content += ""; + } else { + content += ""; + } + } //no PWM_CONTACTOR_CONTROL + content += "

"; + } } - content += ""; -#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY -#ifdef PWM_CONTACTOR_CONTROL - content += "

Cont. Neg.: "; - 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 - content += "

Cont. Neg.: "; - if (digitalRead(SECOND_BATTERY_CONTACTORS_PIN) == HIGH) { - content += ""; - } else { - content += ""; - } -#endif //no PWM_CONTACTOR_CONTROL - content += "

"; -#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY -#endif // CONTACTOR_CONTROL + // Close the block content += "
"; - 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) { diff --git a/Software/src/include.h b/Software/src/include.h index d06d89dd..6a72f9b4 100644 --- a/Software/src/include.h +++ b/Software/src/include.h @@ -39,10 +39,6 @@ #endif #endif -#ifndef BATTERY_SELECTED -#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 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/BYD-CAN.cpp b/Software/src/inverter/BYD-CAN.cpp index a2619172..915eae71 100644 --- a/Software/src/inverter/BYD-CAN.cpp +++ b/Software/src/inverter/BYD-CAN.cpp @@ -171,6 +171,6 @@ void BydCanInverter::send_initial_data() { } void BydCanInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "BYD Battery-Box Premium HVS 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/BYD-CAN.h b/Software/src/inverter/BYD-CAN.h index fa1c3aa4..2b86ca62 100644 --- a/Software/src/inverter/BYD-CAN.h +++ b/Software/src/inverter/BYD-CAN.h @@ -14,6 +14,7 @@ class BydCanInverter : 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 = "BYD Battery-Box Premium HVS over CAN Bus"; private: void send_initial_data(); diff --git a/Software/src/inverter/BYD-MODBUS.cpp b/Software/src/inverter/BYD-MODBUS.cpp index c245e1f3..26d704dc 100644 --- a/Software/src/inverter/BYD-MODBUS.cpp +++ b/Software/src/inverter/BYD-MODBUS.cpp @@ -146,7 +146,7 @@ void BydModbusInverter::verify_inverter_modbus() { } void BydModbusInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "BYD 11kWh HVM battery over Modbus RTU", 63); + strncpy(datalayer.system.info.inverter_protocol, Name, 63); datalayer.system.info.inverter_protocol[63] = '\0'; // Init Static data to the RTU Modbus diff --git a/Software/src/inverter/BYD-MODBUS.h b/Software/src/inverter/BYD-MODBUS.h index fc1bb454..ba8d6b1c 100644 --- a/Software/src/inverter/BYD-MODBUS.h +++ b/Software/src/inverter/BYD-MODBUS.h @@ -12,6 +12,7 @@ class BydModbusInverter : public ModbusInverterProtocol { public: void setup(); void update_values(); + static constexpr char* Name = "BYD 11kWh HVM battery over Modbus RTU"; private: void handle_static_data(); diff --git a/Software/src/inverter/FERROAMP-CAN.cpp b/Software/src/inverter/FERROAMP-CAN.cpp index 89caa49e..a9dd74b9 100644 --- a/Software/src/inverter/FERROAMP-CAN.cpp +++ b/Software/src/inverter/FERROAMP-CAN.cpp @@ -367,6 +367,6 @@ void FerroampCanInverter::send_system_data() { //System equipment information } void FerroampCanInverter::setup(void) { // Performs one time setup at startup over CAN bus - strncpy(datalayer.system.info.inverter_protocol, "Ferroamp Pylon 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/FERROAMP-CAN.h b/Software/src/inverter/FERROAMP-CAN.h index c05f3270..b8b4e238 100644 --- a/Software/src/inverter/FERROAMP-CAN.h +++ b/Software/src/inverter/FERROAMP-CAN.h @@ -15,6 +15,8 @@ class FerroampCanInverter : public CanInverterProtocol { void transmit_can(unsigned long currentMillis); void map_can_frame_to_variable(CAN_frame rx_frame); + static constexpr char* Name = "Ferroamp Pylon battery over CAN bus"; + private: void send_system_data(); void send_setup_info(); 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 eeed90a0..f242368f 100644 --- a/Software/src/inverter/INVERTERS.cpp +++ b/Software/src/inverter/INVERTERS.cpp @@ -2,6 +2,176 @@ InverterProtocol* inverter = nullptr; +InverterProtocolType user_selected_inverter_protocol = InverterProtocolType::BydModbus; + +std::vector supported_inverter_protocols() { + std::vector types; + + for (int i = 0; i < (int)InverterProtocolType::Highest; i++) { + types.push_back((InverterProtocolType)i); + } + + return types; +} + +extern const char* name_for_inverter_type(InverterProtocolType type) { + switch (type) { + case InverterProtocolType::None: + return "None"; + + 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; +} + +#ifdef COMMON_IMAGE +#ifdef SELECTED_INVERTER_CLASS +#error "Compile time SELECTED_INVERTER_CLASS should not be defined with COMMON_IMAGE" +#endif + +void setup_inverter() { + if (inverter) { + return; + } + + 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) { + inverter->setup(); + } +} + +#else void setup_inverter() { if (inverter) { // The inverter is setup only once. @@ -16,3 +186,4 @@ void setup_inverter() { } #endif } +#endif diff --git a/Software/src/inverter/InverterProtocol.h b/Software/src/inverter/InverterProtocol.h index 8e78a9fc..46705e59 100644 --- a/Software/src/inverter/InverterProtocol.h +++ b/Software/src/inverter/InverterProtocol.h @@ -1,6 +1,34 @@ #ifndef INVERTER_PROTOCOL_H #define INVERTER_PROTOCOL_H +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_inverter_type(InverterProtocolType type); + enum class InverterInterfaceType { Can, Rs485, Modbus }; // The abstract base class for all inverter protocols @@ -12,6 +40,13 @@ 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; + #endif 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..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 @@ -14,6 +15,10 @@ 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"; + + 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; 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..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 @@ -14,6 +15,10 @@ 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"; + + 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; 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..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 @@ -14,6 +15,10 @@ 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"; + + virtual bool controls_contactor() { return true; } + virtual bool allows_contactor_closing() { return digitalRead(INVERTER_CONTACTOR_ENABLE_PIN) == 1; } 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;