mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 02:09:30 +02:00
Merge pull request #1247 from kyberias/common-final
Experimental common image
This commit is contained in:
commit
247cf1f784
138 changed files with 1995 additions and 1231 deletions
|
@ -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<BatteryType> supported_battery_types() {
|
||||
std::vector<BatteryType> 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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 **/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
6
Software/src/battery/Battery.cpp
Normal file
6
Software/src/battery/Battery.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include "Battery.h"
|
||||
#include "../datalayer/datalayer.h"
|
||||
|
||||
float Battery::get_voltage() {
|
||||
return static_cast<float>(datalayer.battery.status.voltage_dV) / 10.0;
|
||||
}
|
|
@ -1,9 +1,56 @@
|
|||
#ifndef BATTERY_H
|
||||
#define BATTERY_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include <vector>
|
||||
#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<BatteryType> 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<float>(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; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#ifndef CHADEMO_SHUNTS_H
|
||||
#define CHADEMO_SHUNTS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../devboard/utils/types.h"
|
||||
|
||||
uint16_t get_measured_voltage();
|
||||
uint16_t get_measured_current();
|
||||
void ISA_handleFrame(CAN_frame* frame);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
8
Software/src/battery/CanBattery.cpp
Normal file
8
Software/src/battery/CanBattery.cpp
Normal file
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#include "../include.h"
|
||||
#ifdef DALY_BMS
|
||||
#include "DALY-BMS.h"
|
||||
#include <cstdint>
|
||||
#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
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
#include "../include.h"
|
||||
#ifdef MEB_BATTERY
|
||||
#include "MEB-BATTERY.h"
|
||||
#include <algorithm> // 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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#include "RENAULT-TWIZY.h"
|
||||
#include <cstdint>
|
||||
#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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -2,8 +2,42 @@
|
|||
|
||||
CanCharger* charger = nullptr;
|
||||
|
||||
ChargerType user_selected_charger_type = ChargerType::None;
|
||||
|
||||
std::vector<ChargerType> supported_charger_types() {
|
||||
std::vector<ChargerType> 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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<ChargerType> supported_charger_types();
|
||||
extern const char* name_for_charger_type(ChargerType type);
|
||||
|
||||
// Generic base class for all chargers
|
||||
class Charger {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define _CANRECEIVER_H
|
||||
|
||||
#include "src/devboard/utils/types.h"
|
||||
#include "src/include.h"
|
||||
|
||||
class CanReceiver {
|
||||
public:
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,9 +1,72 @@
|
|||
#include "settings_html.h"
|
||||
#include <Arduino.h>
|
||||
#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 <typename E>
|
||||
constexpr auto to_underlying(E e) noexcept {
|
||||
return static_cast<std::underlying_type_t<E>>(e);
|
||||
}
|
||||
|
||||
template <typename EnumType>
|
||||
std::vector<EnumType> enum_values() {
|
||||
static_assert(std::is_enum_v<EnumType>, "Template argument must be an enum type.");
|
||||
|
||||
constexpr auto count = to_underlying(EnumType::Highest);
|
||||
std::vector<EnumType> values;
|
||||
for (int i = 1; i < count; ++i) {
|
||||
values.push_back(static_cast<EnumType>(i));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
template <typename EnumType, typename Func>
|
||||
std::vector<std::pair<String, EnumType>> enum_values_and_names(Func name_for_type) {
|
||||
auto values = enum_values<EnumType>();
|
||||
|
||||
std::vector<std::pair<String, EnumType>> 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 <typename TEnum, typename Func>
|
||||
String options_for_enum(TEnum selected, Func name_for_type) {
|
||||
String options;
|
||||
auto values = enum_values_and_names<TEnum>(name_for_type);
|
||||
for (const auto& [name, type] : values) {
|
||||
options +=
|
||||
("<option value=\"" + String(static_cast<int>(type)) + "\"" + (selected == type ? " selected" : "") + ">");
|
||||
options += name;
|
||||
options += "</option>";
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
void render_checkbox(String& content, const char* label, bool enabled, const char* name) {
|
||||
content += "<label>" + String(label) + "</label>";
|
||||
content += "<input id='" + String(name) + "' name='" + String(name) +
|
||||
"' type='checkbox' "
|
||||
"style=\"margin-left: 0;\"";
|
||||
content += (enabled ? " checked" : "");
|
||||
content += " value='on'/>";
|
||||
}
|
||||
|
||||
String settings_processor(const String& var) {
|
||||
if (var == "X") {
|
||||
String content = "";
|
||||
|
@ -28,11 +91,58 @@ String settings_processor(const String& var) {
|
|||
"<h4 style='color: white;'>Password: ######## <span id='Password'></span> <button "
|
||||
"onclick='editPassword()'>Edit</button></h4>";
|
||||
|
||||
#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 +=
|
||||
"<h4 style='color: white;'>Battery interface: <span id='Battery'>" + battery->interface_name() + "</span></h4>";
|
||||
"<div style='background-color: #404E47; padding: 10px; margin-bottom: 10px;border-radius: 50px;'><div "
|
||||
"style='max-width: 500px;'>";
|
||||
content +=
|
||||
"<form action='saveSettings' method='post' style='display: grid; grid-template-columns: 1fr 2fr; gap: 10px; "
|
||||
"align-items: center;'>";
|
||||
content += "<label>Battery: </label><select style='max-width: 250px;' name='battery'>";
|
||||
|
||||
content +=
|
||||
options_for_enum((BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None), name_for_battery_type);
|
||||
content += "</select>";
|
||||
content += "<label>Inverter protocol: </label><select style='max-width: 250px;' name='inverter'>";
|
||||
content += options_for_enum((InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None),
|
||||
name_for_inverter_type);
|
||||
content += "</select>";
|
||||
content += "<label>Charger: </label><select style='max-width: 250px;' name='charger'>";
|
||||
content +=
|
||||
options_for_enum((ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None), name_for_charger_type);
|
||||
content += "</select>";
|
||||
|
||||
// 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 +=
|
||||
"<div style='grid-column: span 2; text-align: center; padding-top: 10px;'><button "
|
||||
"type='submit'>Save</button></div>";
|
||||
|
||||
if (settingsUpdated) {
|
||||
content += "<p>Settings saved. Reboot to take the settings into use.</p>";
|
||||
}
|
||||
|
||||
content += "</form></div></div>";
|
||||
#endif
|
||||
|
||||
if (battery) {
|
||||
content += "<h4 style='color: white;'>Battery interface: <span id='Battery'>" + battery->interface_name() +
|
||||
"</span></h4>";
|
||||
}
|
||||
|
||||
if (battery2) {
|
||||
content += "<h4 style='color: white;'>Battery #2 interface: <span id='Battery'>" + battery->interface_name() +
|
||||
content += "<h4 style='color: white;'>Battery #2 interface: <span id='Battery'>" + battery2->interface_name() +
|
||||
"</span></h4>";
|
||||
}
|
||||
|
||||
|
@ -41,10 +151,10 @@ String settings_processor(const String& var) {
|
|||
String(inverter->interface_name()) + "</span></h4>";
|
||||
}
|
||||
|
||||
#ifdef CAN_SHUNT_SELECTED
|
||||
content += "<h4 style='color: white;'>Shunt Interface: <span id='Shunt'>" +
|
||||
String(getCANInterfaceName(can_config.shunt)) + "</span></h4>";
|
||||
#endif //CAN_SHUNT_SELECTED
|
||||
if (shunt) {
|
||||
content +=
|
||||
"<h4 style='color: white;'>Shunt Interface: <span id='Shunt'>" + shunt->interface_name() + "</span></h4>";
|
||||
}
|
||||
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
|
@ -90,14 +200,14 @@ String settings_processor(const String& var) {
|
|||
// Close the block
|
||||
content += "</div>";
|
||||
|
||||
if (battery->supports_set_fake_voltage()) {
|
||||
if (battery && battery->supports_set_fake_voltage()) {
|
||||
content += "<div style='background-color: #2E37AD; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
||||
content += "<h4 style='color: white;'>Fake battery voltage: " + String(battery->get_voltage(), 1) +
|
||||
" V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>";
|
||||
content += "</div>";
|
||||
}
|
||||
|
||||
if (battery->supports_manual_balancing()) {
|
||||
if (battery && battery->supports_manual_balancing()) {
|
||||
// Start a new block with grey background color
|
||||
content += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
|
||||
|
||||
|
@ -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 "
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#include "webserver.h"
|
||||
#include <Preferences.h>
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
#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<BoolSetting> 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<InverterProtocolType>(atoi(p->value().c_str()));
|
||||
settings.saveUInt("INVTYPE", (int)type);
|
||||
} else if (p->name() == "battery") {
|
||||
auto type = static_cast<BatteryType>(atoi(p->value().c_str()));
|
||||
settings.saveUInt("BATTTYPE", (int)type);
|
||||
} else if (p->name() == "charger") {
|
||||
auto type = static_cast<ChargerType>(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 += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px; border-radius: 50px'>";
|
||||
content += "<h4>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 += "</div>";
|
||||
|
||||
// Start a new block with a specific background color
|
||||
content += "<div style='background-color: #333; padding: 10px; margin-bottom: 10px; border-radius: 50px'>";
|
||||
if (inverter || battery || shunt || charger) {
|
||||
// Start a new block with a specific background color
|
||||
content += "<div style='background-color: #333; padding: 10px; margin-bottom: 10px; border-radius: 50px'>";
|
||||
|
||||
// Display which components are used
|
||||
content += "<h4 style='color: white;'>Inverter protocol: ";
|
||||
content += datalayer.system.info.inverter_protocol;
|
||||
content += " ";
|
||||
content += datalayer.system.info.inverter_brand;
|
||||
content += "</h4>";
|
||||
content += "<h4 style='color: white;'>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 += "</h4>";
|
||||
|
||||
#ifdef CAN_SHUNT_SELECTED
|
||||
content += "<h4 style='color: white;'>Shunt protocol: ";
|
||||
content += datalayer.system.info.shunt_protocol;
|
||||
content += "</h4>";
|
||||
#endif
|
||||
|
||||
if (charger) {
|
||||
content += "<h4 style='color: white;'>Charger protocol: ";
|
||||
content += charger->name();
|
||||
content += "</h4>";
|
||||
}
|
||||
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
|
||||
if (battery2) {
|
||||
// Start a new block with a specific background color. Color changes depending on BMS status
|
||||
content += "<div style='display: flex; width: 100%;'>";
|
||||
content += "<div style='flex: 1; background-color: ";
|
||||
} else {
|
||||
// Start a new block with a specific background color. Color changes depending on system status
|
||||
content += "<div style='background-color: ";
|
||||
}
|
||||
|
||||
switch (led_get_color()) {
|
||||
case led_color::GREEN:
|
||||
content += "#2D3F2F;";
|
||||
break;
|
||||
case led_color::YELLOW:
|
||||
content += "#F5CC00;";
|
||||
break;
|
||||
case led_color::BLUE:
|
||||
content += "#2B35AF;"; // Blue in test mode
|
||||
break;
|
||||
case led_color::RED:
|
||||
content += "#A70107;";
|
||||
break;
|
||||
default: // Some new color, make background green
|
||||
content += "#2D3F2F;";
|
||||
break;
|
||||
}
|
||||
|
||||
// Add the common style properties
|
||||
content += "padding: 10px; margin-bottom: 10px; border-radius: 50px;'>";
|
||||
|
||||
// Display battery statistics within this block
|
||||
float socRealFloat =
|
||||
static_cast<float>(datalayer.battery.status.real_soc) / 100.0; // Convert to float and divide by 100
|
||||
float socScaledFloat =
|
||||
static_cast<float>(datalayer.battery.status.reported_soc) / 100.0; // Convert to float and divide by 100
|
||||
float sohFloat =
|
||||
static_cast<float>(datalayer.battery.status.soh_pptt) / 100.0; // Convert to float and divide by 100
|
||||
float voltageFloat =
|
||||
static_cast<float>(datalayer.battery.status.voltage_dV) / 10.0; // Convert to float and divide by 10
|
||||
float currentFloat =
|
||||
static_cast<float>(datalayer.battery.status.current_dA) / 10.0; // Convert to float and divide by 10
|
||||
float powerFloat = static_cast<float>(datalayer.battery.status.active_power_W); // Convert to float
|
||||
float tempMaxFloat = static_cast<float>(datalayer.battery.status.temperature_max_dC) / 10.0; // Convert to float
|
||||
float tempMinFloat = static_cast<float>(datalayer.battery.status.temperature_min_dC) / 10.0; // Convert to float
|
||||
float maxCurrentChargeFloat =
|
||||
static_cast<float>(datalayer.battery.status.max_charge_current_dA) / 10.0; // Convert to float
|
||||
float maxCurrentDischargeFloat =
|
||||
static_cast<float>(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 += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) +
|
||||
"% (real: " + String(socRealFloat, 2) + "%)</h4>";
|
||||
else
|
||||
content += "<h4 style='color: white;'>SOC: " + String(socRealFloat, 2) + "%</h4>";
|
||||
|
||||
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "%</h4>";
|
||||
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) +
|
||||
" V Current: " + String(currentFloat, 1) + " A</h4>";
|
||||
content += formatPowerValue("Power", powerFloat, "", 1);
|
||||
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled total capacity: " +
|
||||
formatPowerValue(datalayer.battery.info.reported_total_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery.info.total_capacity_Wh, "h", 1) + ")</h4>";
|
||||
else
|
||||
content += formatPowerValue("Total capacity", datalayer.battery.info.total_capacity_Wh, "h", 1);
|
||||
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled remaining capacity: " +
|
||||
formatPowerValue(datalayer.battery.status.reported_remaining_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery.status.remaining_capacity_Wh, "h", 1) + ")</h4>";
|
||||
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 += "<h4 style='color: red;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
||||
content += "<h4 style='color: red;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
||||
} 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 += "<h4 style='color: white;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A";
|
||||
if (datalayer.battery.settings.user_settings_limit_discharge) {
|
||||
content += " (Manual)</h4>";
|
||||
} else {
|
||||
content += " (BMS)</h4>";
|
||||
// Display which components are used
|
||||
if (inverter) {
|
||||
content += "<h4 style='color: white;'>Inverter protocol: ";
|
||||
content += datalayer.system.info.inverter_protocol;
|
||||
content += " ";
|
||||
content += datalayer.system.info.inverter_brand;
|
||||
content += "</h4>";
|
||||
}
|
||||
content += "<h4 style='color: white;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A";
|
||||
if (datalayer.battery.settings.user_settings_limit_charge) {
|
||||
content += " (Manual)</h4>";
|
||||
} else {
|
||||
content += " (BMS)</h4>";
|
||||
}
|
||||
}
|
||||
|
||||
content += "<h4>Cell min/max: " + String(datalayer.battery.status.cell_min_voltage_mV) + " mV / " +
|
||||
String(datalayer.battery.status.cell_max_voltage_mV) + " mV</h4>";
|
||||
if (cell_delta_mv > datalayer.battery.info.max_cell_voltage_deviation_mV) {
|
||||
content += "<h4 style='color: red;'>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
} else {
|
||||
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
}
|
||||
content +=
|
||||
"<h4>Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) + " °C</h4>";
|
||||
|
||||
content += "<h4>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 += "</h4>";
|
||||
|
||||
if (battery->supports_real_BMS_status()) {
|
||||
content += "<h4>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 += "</h4>";
|
||||
}
|
||||
|
||||
if (datalayer.battery.status.current_dA == 0) {
|
||||
content += "<h4>Battery idle</h4>";
|
||||
} else if (datalayer.battery.status.current_dA < 0) {
|
||||
content += "<h4>Battery discharging!";
|
||||
if (datalayer.battery.settings.inverter_limits_discharge) {
|
||||
content += " (Inverter limiting)</h4>";
|
||||
} else {
|
||||
if (datalayer.battery.settings.user_settings_limit_discharge) {
|
||||
content += " (Settings limiting)</h4>";
|
||||
} else {
|
||||
content += " (Battery limiting)</h4>";
|
||||
if (battery) {
|
||||
content += "<h4 style='color: white;'>Battery protocol: ";
|
||||
content += datalayer.system.info.battery_protocol;
|
||||
if (battery2) {
|
||||
content += " (Double battery)";
|
||||
}
|
||||
}
|
||||
content += "</h4>";
|
||||
} else { // > 0 , positive current
|
||||
content += "<h4>Battery charging!";
|
||||
if (datalayer.battery.settings.inverter_limits_charge) {
|
||||
content += " (Inverter limiting)</h4>";
|
||||
} else {
|
||||
if (datalayer.battery.settings.user_settings_limit_charge) {
|
||||
content += " (Settings limiting)</h4>";
|
||||
} else {
|
||||
content += " (Battery limiting)</h4>";
|
||||
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
|
||||
content += " (LFP)";
|
||||
}
|
||||
content += "</h4>";
|
||||
}
|
||||
|
||||
if (shunt) {
|
||||
content += "<h4 style='color: white;'>Shunt protocol: ";
|
||||
content += datalayer.system.info.shunt_protocol;
|
||||
content += "</h4>";
|
||||
}
|
||||
|
||||
if (charger) {
|
||||
content += "<h4 style='color: white;'>Charger protocol: ";
|
||||
content += charger->name();
|
||||
content += "</h4>";
|
||||
}
|
||||
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
}
|
||||
|
||||
content += "<h4>Battery allows contactor closing: ";
|
||||
if (datalayer.system.status.battery_allows_contactor_closing == true) {
|
||||
content += "<span>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
if (battery) {
|
||||
if (battery2) {
|
||||
// Start a new block with a specific background color. Color changes depending on BMS status
|
||||
content += "<div style='display: flex; width: 100%;'>";
|
||||
content += "<div style='flex: 1; background-color: ";
|
||||
} else {
|
||||
// Start a new block with a specific background color. Color changes depending on system status
|
||||
content += "<div style='background-color: ";
|
||||
}
|
||||
|
||||
content += " Inverter allows contactor closing: ";
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
||||
content += "<span>✓</span></h4>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span></h4>";
|
||||
}
|
||||
if (emulator_pause_status == NORMAL)
|
||||
content += "<h4>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
else
|
||||
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
content += "<h4>Contactors controlled by emulator, state: ";
|
||||
if (datalayer.system.status.contactors_engaged) {
|
||||
content += "<span style='color: green;'>ON</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>OFF</span>";
|
||||
}
|
||||
content += "</h4>";
|
||||
|
||||
content += "<h4>Precharge: (";
|
||||
content += PRECHARGE_TIME_MS;
|
||||
content += " ms) Cont. Neg.: ";
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
if (datalayer.system.status.contactors_engaged) {
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
|
||||
#else // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded
|
||||
if (digitalRead(NEGATIVE_CONTACTOR_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
|
||||
content += " Cont. Pos.: ";
|
||||
if (digitalRead(POSITIVE_CONTACTOR_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
#endif //no PWM_CONTACTOR_CONTROL
|
||||
content += "</h4>";
|
||||
#endif
|
||||
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
|
||||
if (battery2) {
|
||||
content += "<div style='flex: 1; background-color: ";
|
||||
switch (datalayer.battery.status.bms_status) {
|
||||
case ACTIVE:
|
||||
switch (led_get_color()) {
|
||||
case led_color::GREEN:
|
||||
content += "#2D3F2F;";
|
||||
break;
|
||||
case FAULT:
|
||||
case led_color::YELLOW:
|
||||
content += "#F5CC00;";
|
||||
break;
|
||||
case led_color::BLUE:
|
||||
content += "#2B35AF;"; // Blue in test mode
|
||||
break;
|
||||
case led_color::RED:
|
||||
content += "#A70107;";
|
||||
break;
|
||||
default:
|
||||
default: // Some new color, make background green
|
||||
content += "#2D3F2F;";
|
||||
break;
|
||||
}
|
||||
|
||||
// Add the common style properties
|
||||
content += "padding: 10px; margin-bottom: 10px; border-radius: 50px;'>";
|
||||
|
||||
// Display battery statistics within this block
|
||||
socRealFloat =
|
||||
static_cast<float>(datalayer.battery2.status.real_soc) / 100.0; // Convert to float and divide by 100
|
||||
//socScaledFloat; // Same value used for bat2
|
||||
sohFloat = static_cast<float>(datalayer.battery2.status.soh_pptt) / 100.0; // Convert to float and divide by 100
|
||||
voltageFloat =
|
||||
static_cast<float>(datalayer.battery2.status.voltage_dV) / 10.0; // Convert to float and divide by 10
|
||||
currentFloat =
|
||||
static_cast<float>(datalayer.battery2.status.current_dA) / 10.0; // Convert to float and divide by 10
|
||||
powerFloat = static_cast<float>(datalayer.battery2.status.active_power_W); // Convert to float
|
||||
tempMaxFloat = static_cast<float>(datalayer.battery2.status.temperature_max_dC) / 10.0; // Convert to float
|
||||
tempMinFloat = static_cast<float>(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<float>(datalayer.battery.status.real_soc) / 100.0; // Convert to float and divide by 100
|
||||
float socScaledFloat =
|
||||
static_cast<float>(datalayer.battery.status.reported_soc) / 100.0; // Convert to float and divide by 100
|
||||
float sohFloat =
|
||||
static_cast<float>(datalayer.battery.status.soh_pptt) / 100.0; // Convert to float and divide by 100
|
||||
float voltageFloat =
|
||||
static_cast<float>(datalayer.battery.status.voltage_dV) / 10.0; // Convert to float and divide by 10
|
||||
float currentFloat =
|
||||
static_cast<float>(datalayer.battery.status.current_dA) / 10.0; // Convert to float and divide by 10
|
||||
float powerFloat = static_cast<float>(datalayer.battery.status.active_power_W); // Convert to float
|
||||
float tempMaxFloat = static_cast<float>(datalayer.battery.status.temperature_max_dC) / 10.0; // Convert to float
|
||||
float tempMinFloat = static_cast<float>(datalayer.battery.status.temperature_min_dC) / 10.0; // Convert to float
|
||||
float maxCurrentChargeFloat =
|
||||
static_cast<float>(datalayer.battery.status.max_charge_current_dA) / 10.0; // Convert to float
|
||||
float maxCurrentDischargeFloat =
|
||||
static_cast<float>(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 += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) +
|
||||
|
@ -1260,110 +1121,334 @@ String processor(const String& var) {
|
|||
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled total capacity: " +
|
||||
formatPowerValue(datalayer.battery2.info.reported_total_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery2.info.total_capacity_Wh, "h", 1) + ")</h4>";
|
||||
formatPowerValue(datalayer.battery.info.reported_total_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery.info.total_capacity_Wh, "h", 1) + ")</h4>";
|
||||
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 += "<h4 style='color: white;'>Scaled remaining capacity: " +
|
||||
formatPowerValue(datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery2.status.remaining_capacity_Wh, "h", 1) + ")</h4>";
|
||||
formatPowerValue(datalayer.battery.status.reported_remaining_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery.status.remaining_capacity_Wh, "h", 1) + ")</h4>";
|
||||
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 += "<h4 style='color: red;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
||||
content += "<h4 style='color: red;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
||||
} 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 +=
|
||||
"<h4 style='color: white;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
||||
content += "<h4 style='color: white;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
||||
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 += "<h4 style='color: white;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A";
|
||||
if (datalayer.battery.settings.user_settings_limit_discharge) {
|
||||
content += " (Manual)</h4>";
|
||||
} else {
|
||||
content += " (BMS)</h4>";
|
||||
}
|
||||
content += "<h4 style='color: white;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A";
|
||||
if (datalayer.battery.settings.user_settings_limit_charge) {
|
||||
content += " (Manual)</h4>";
|
||||
} else {
|
||||
content += " (BMS)</h4>";
|
||||
}
|
||||
}
|
||||
|
||||
content += "<h4>Cell min/max: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV / " +
|
||||
String(datalayer.battery2.status.cell_max_voltage_mV) + " mV</h4>";
|
||||
if (cell_delta_mv > datalayer.battery2.info.max_cell_voltage_deviation_mV) {
|
||||
content += "<h4>Cell min/max: " + String(datalayer.battery.status.cell_min_voltage_mV) + " mV / " +
|
||||
String(datalayer.battery.status.cell_max_voltage_mV) + " mV</h4>";
|
||||
if (cell_delta_mv > datalayer.battery.info.max_cell_voltage_deviation_mV) {
|
||||
content += "<h4 style='color: red;'>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
} else {
|
||||
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
}
|
||||
content += "<h4>Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) +
|
||||
" °C</h4>";
|
||||
if (datalayer.battery.status.bms_status == ACTIVE) {
|
||||
content += "<h4>System status: OK </h4>";
|
||||
} else if (datalayer.battery.status.bms_status == UPDATING) {
|
||||
content += "<h4>System status: UPDATING </h4>";
|
||||
} else {
|
||||
content += "<h4>System status: FAULT </h4>";
|
||||
|
||||
content += "<h4>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 += "<h4>Battery idle</h4>";
|
||||
} else if (datalayer.battery2.status.current_dA < 0) {
|
||||
content += "<h4>Battery discharging!</h4>";
|
||||
} else { // > 0
|
||||
content += "<h4>Battery charging!</h4>";
|
||||
content += "</h4>";
|
||||
|
||||
if (battery && battery->supports_real_BMS_status()) {
|
||||
content += "<h4>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 += "</h4>";
|
||||
}
|
||||
|
||||
content += "<h4>Automatic contactor closing allowed:</h4>";
|
||||
content += "<h4>Battery: ";
|
||||
if (datalayer.system.status.battery2_allowed_contactor_closing == true) {
|
||||
if (datalayer.battery.status.current_dA == 0) {
|
||||
content += "<h4>Battery idle</h4>";
|
||||
} else if (datalayer.battery.status.current_dA < 0) {
|
||||
content += "<h4>Battery discharging!";
|
||||
if (datalayer.battery.settings.inverter_limits_discharge) {
|
||||
content += " (Inverter limiting)</h4>";
|
||||
} else {
|
||||
if (datalayer.battery.settings.user_settings_limit_discharge) {
|
||||
content += " (Settings limiting)</h4>";
|
||||
} else {
|
||||
content += " (Battery limiting)</h4>";
|
||||
}
|
||||
}
|
||||
content += "</h4>";
|
||||
} else { // > 0 , positive current
|
||||
content += "<h4>Battery charging!";
|
||||
if (datalayer.battery.settings.inverter_limits_charge) {
|
||||
content += " (Inverter limiting)</h4>";
|
||||
} else {
|
||||
if (datalayer.battery.settings.user_settings_limit_charge) {
|
||||
content += " (Settings limiting)</h4>";
|
||||
} else {
|
||||
content += " (Battery limiting)</h4>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content += "<h4>Battery allows contactor closing: ";
|
||||
if (datalayer.system.status.battery_allows_contactor_closing == true) {
|
||||
content += "<span>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
|
||||
content += " Inverter: ";
|
||||
content += " Inverter allows contactor closing: ";
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
||||
content += "<span>✓</span></h4>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span></h4>";
|
||||
}
|
||||
|
||||
if (emulator_pause_status == NORMAL)
|
||||
content += "<h4>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
else
|
||||
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
|
||||
#ifdef CONTACTOR_CONTROL
|
||||
content += "<h4>Contactors controlled by emulator, state: ";
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>ON</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>OFF</span>";
|
||||
if (contactor_control_enabled) {
|
||||
content += "<h4>Contactors controlled by emulator, state: ";
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>ON</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>OFF</span>";
|
||||
}
|
||||
content += "</h4>";
|
||||
if (contactor_control_enabled_double_battery) {
|
||||
if (pwm_contactor_control) {
|
||||
content += "<h4>Cont. Neg.: ";
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
} else { // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded
|
||||
content += "<h4>Cont. Neg.: ";
|
||||
if (digitalRead(SECOND_BATTERY_CONTACTORS_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
} //no PWM_CONTACTOR_CONTROL
|
||||
content += "</h4>";
|
||||
}
|
||||
}
|
||||
content += "</h4>";
|
||||
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
#ifdef PWM_CONTACTOR_CONTROL
|
||||
content += "<h4>Cont. Neg.: ";
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
#else // No PWM_CONTACTOR_CONTROL , we can read the pin and see feedback. Helpful if channel overloaded
|
||||
content += "<h4>Cont. Neg.: ";
|
||||
if (digitalRead(SECOND_BATTERY_CONTACTORS_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
#endif //no PWM_CONTACTOR_CONTROL
|
||||
content += "</h4>";
|
||||
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
|
||||
#endif // CONTACTOR_CONTROL
|
||||
|
||||
// Close the block
|
||||
content += "</div>";
|
||||
content += "</div>";
|
||||
|
||||
if (battery2) {
|
||||
content += "<div style='flex: 1; background-color: ";
|
||||
switch (datalayer.battery.status.bms_status) {
|
||||
case ACTIVE:
|
||||
content += "#2D3F2F;";
|
||||
break;
|
||||
case FAULT:
|
||||
content += "#A70107;";
|
||||
break;
|
||||
default:
|
||||
content += "#2D3F2F;";
|
||||
break;
|
||||
}
|
||||
// Add the common style properties
|
||||
content += "padding: 10px; margin-bottom: 10px; border-radius: 50px;'>";
|
||||
|
||||
// Display battery statistics within this block
|
||||
socRealFloat =
|
||||
static_cast<float>(datalayer.battery2.status.real_soc) / 100.0; // Convert to float and divide by 100
|
||||
//socScaledFloat; // Same value used for bat2
|
||||
sohFloat =
|
||||
static_cast<float>(datalayer.battery2.status.soh_pptt) / 100.0; // Convert to float and divide by 100
|
||||
voltageFloat =
|
||||
static_cast<float>(datalayer.battery2.status.voltage_dV) / 10.0; // Convert to float and divide by 10
|
||||
currentFloat =
|
||||
static_cast<float>(datalayer.battery2.status.current_dA) / 10.0; // Convert to float and divide by 10
|
||||
powerFloat = static_cast<float>(datalayer.battery2.status.active_power_W); // Convert to float
|
||||
tempMaxFloat = static_cast<float>(datalayer.battery2.status.temperature_max_dC) / 10.0; // Convert to float
|
||||
tempMinFloat = static_cast<float>(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 += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) +
|
||||
"% (real: " + String(socRealFloat, 2) + "%)</h4>";
|
||||
else
|
||||
content += "<h4 style='color: white;'>SOC: " + String(socRealFloat, 2) + "%</h4>";
|
||||
|
||||
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "%</h4>";
|
||||
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) +
|
||||
" V Current: " + String(currentFloat, 1) + " A</h4>";
|
||||
content += formatPowerValue("Power", powerFloat, "", 1);
|
||||
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled total capacity: " +
|
||||
formatPowerValue(datalayer.battery2.info.reported_total_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery2.info.total_capacity_Wh, "h", 1) + ")</h4>";
|
||||
else
|
||||
content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 1);
|
||||
|
||||
if (datalayer.battery.settings.soc_scaling_active)
|
||||
content += "<h4 style='color: white;'>Scaled remaining capacity: " +
|
||||
formatPowerValue(datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1) +
|
||||
" (real: " + formatPowerValue(datalayer.battery2.status.remaining_capacity_Wh, "h", 1) + ")</h4>";
|
||||
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 +=
|
||||
"<h4 style='color: red;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
||||
content += "<h4 style='color: red;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
||||
} 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 +=
|
||||
"<h4 style='color: white;'>Max discharge current: " + String(maxCurrentDischargeFloat, 1) + " A</h4>";
|
||||
content += "<h4 style='color: white;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
|
||||
}
|
||||
|
||||
content += "<h4>Cell min/max: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV / " +
|
||||
String(datalayer.battery2.status.cell_max_voltage_mV) + " mV</h4>";
|
||||
if (cell_delta_mv > datalayer.battery2.info.max_cell_voltage_deviation_mV) {
|
||||
content += "<h4 style='color: red;'>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
} else {
|
||||
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||
}
|
||||
content += "<h4>Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) +
|
||||
" °C</h4>";
|
||||
if (datalayer.battery.status.bms_status == ACTIVE) {
|
||||
content += "<h4>System status: OK </h4>";
|
||||
} else if (datalayer.battery.status.bms_status == UPDATING) {
|
||||
content += "<h4>System status: UPDATING </h4>";
|
||||
} else {
|
||||
content += "<h4>System status: FAULT </h4>";
|
||||
}
|
||||
if (datalayer.battery2.status.current_dA == 0) {
|
||||
content += "<h4>Battery idle</h4>";
|
||||
} else if (datalayer.battery2.status.current_dA < 0) {
|
||||
content += "<h4>Battery discharging!</h4>";
|
||||
} else { // > 0
|
||||
content += "<h4>Battery charging!</h4>";
|
||||
}
|
||||
|
||||
content += "<h4>Automatic contactor closing allowed:</h4>";
|
||||
content += "<h4>Battery: ";
|
||||
if (datalayer.system.status.battery2_allowed_contactor_closing == true) {
|
||||
content += "<span>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
|
||||
content += " Inverter: ";
|
||||
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
|
||||
content += "<span>✓</span></h4>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span></h4>";
|
||||
}
|
||||
|
||||
if (emulator_pause_status == NORMAL)
|
||||
content += "<h4>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
else
|
||||
content += "<h4 style='color: red;'>Power status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||
|
||||
if (contactor_control_enabled) {
|
||||
content += "<h4>Contactors controlled by emulator, state: ";
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>ON</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>OFF</span>";
|
||||
}
|
||||
content += "</h4>";
|
||||
|
||||
if (contactor_control_enabled_double_battery) {
|
||||
content += "<h4>Cont. Neg.: ";
|
||||
if (pwm_contactor_control) {
|
||||
if (datalayer.system.status.contactors_battery2_engaged) {
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: green;'>Economized</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
content += " Cont. Pos.: ";
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
} 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 += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
|
||||
content += " Cont. Pos.: ";
|
||||
if (digitalRead(SECOND_POSITIVE_CONTACTOR_PIN) == HIGH) {
|
||||
content += "<span style='color: green;'>✓</span>";
|
||||
} else {
|
||||
content += "<span style='color: red;'>✕</span>";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
content += "</h4>";
|
||||
}
|
||||
}
|
||||
content += "</div>";
|
||||
content += "</div>";
|
||||
}
|
||||
}
|
||||
|
||||
if (charger) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue