More common image

This commit is contained in:
Jaakko Haakana 2025-06-16 08:25:08 +03:00
commit 901c89e03f
95 changed files with 1030 additions and 579 deletions

View file

@ -58,6 +58,7 @@ jobs:
- CMFA_EV_BATTERY - CMFA_EV_BATTERY
- DALY_BMS - DALY_BMS
- FOXESS_BATTERY - FOXESS_BATTERY
- GEELY_GEOMETRY_C_BATTERY
- IMIEV_CZERO_ION_BATTERY - IMIEV_CZERO_ION_BATTERY
- JAGUAR_IPACE_BATTERY - JAGUAR_IPACE_BATTERY
- KIA_E_GMP_BATTERY - KIA_E_GMP_BATTERY

View file

@ -0,0 +1,52 @@
# This is the name of the workflow, visible on GitHub UI.
name: 🔋 Compile Common Image for Lilygo
# Here we tell GitHub when to run the workflow.
on:
# The workflow is run when a commit is pushed or for a
# Pull Request.
- push
- pull_request
# This is the list of jobs that will be run concurrently.
jobs:
# This pre-job is run to skip workflows in case a workflow is already run, i.e. because the workflow is triggered by both push and pull_request
skip-duplicate-actions:
runs-on: ubuntu-latest
# Map a step output to a job output
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
uses: fkirc/skip-duplicate-actions@v5
with:
# All of these options are optional, so you can remove them if you are happy with the defaults
concurrent_skipping: 'never'
skip_after_successful_duplicate: 'true'
do_not_skip: '["pull_request", "workflow_dispatch", "schedule"]'
build-common-image:
# This is the platform GitHub will use to run our workflow.
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h
- name: Copy USER_SECRETS.TEMPLATE.h to USER_SECRETS.h
run: cp ./Software/USER_SECRETS.TEMPLATE.h ./Software/USER_SECRETS.h
# We use the `arduino/setup-arduino-cli` action to install and
# configure the Arduino CLI on the system.
- name: Setup Arduino CLI
uses: arduino/setup-arduino-cli@v2
# We then install the platform.
- name: Install platform
run: |
arduino-cli core update-index
arduino-cli core install esp32:esp32
- name: Compile Sketch
run: arduino-cli compile --fqbn esp32:esp32:esp32 --build-property build.partitions=min_spiffs --build-property upload.maximum_size=1966080 --build-property "build.extra_flags=-Wall -Wextra -Wpedantic -Werror -DESP32 -DCOMMON_IMAGE -DHW_LILYGO" ./Software

View file

@ -4,16 +4,134 @@
#include "CanBattery.h" #include "CanBattery.h"
#include "RS485Battery.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
Battery* battery = nullptr; Battery* battery = nullptr;
Battery* battery2 = 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_type(BatteryType type) {
switch (type) {
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;
case BatteryType::Chademo:
return ChademoBattery::Name;
case BatteryType::NissanLeaf:
return NissanLeafBattery::Name;
case BatteryType::TestFake:
return TestFakeBattery::Name;
case BatteryType::TeslaModel3Y:
return TeslaModel3YBattery::Name;
case BatteryType::TeslaModelSX:
return TeslaModelSXBattery::Name;
case BatteryType::None:
return "None";
}
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;
void setup_battery() {
if (battery) {
// Let's not create the battery again.
return;
}
switch (user_selected_battery_type) {
case BatteryType::BmwI3:
battery = new BmwI3Battery();
break;
case BatteryType::BmwIx:
battery = new BmwIXBattery();
break;
case BatteryType::BoltAmpera:
battery = new BoltAmperaBattery();
break;
case BatteryType::BydAtto3:
battery = new BydAttoBattery();
break;
case BatteryType::CellPowerBms:
battery = new CellPowerBms();
break;
case BatteryType::Chademo:
battery = new ChademoBattery();
break;
case BatteryType::CmfaEv:
battery = new CmfaEvBattery();
break;
case BatteryType::DalyBms:
battery = new DalyBms();
break;
case BatteryType::NissanLeaf:
battery = new NissanLeafBattery();
break;
case BatteryType::TeslaModel3Y:
battery = new TeslaModel3YBattery();
break;
case BatteryType::TeslaModelSX:
battery = new TeslaModelSXBattery();
break;
case BatteryType::TestFake:
battery = new TestFakeBattery();
break;
}
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() { void setup_battery() {
// Instantiate the battery only once just in case this function gets called multiple times. // Instantiate the battery only once just in case this function gets called multiple times.
if (battery == nullptr) { if (battery == nullptr) {
@ -40,5 +158,4 @@ void setup_battery() {
battery2->setup(); battery2->setup();
#endif #endif
} }
#endif #endif

View file

@ -17,128 +17,39 @@ void setup_can_shunt();
#include "BMW-SBOX.h" #include "BMW-SBOX.h"
#include "BOLT-AMPERA-BATTERY.h" #include "BOLT-AMPERA-BATTERY.h"
#include "BYD-ATTO-3-BATTERY.h" #include "BYD-ATTO-3-BATTERY.h"
#ifdef CELLPOWER_BMS
#include "CELLPOWER-BMS.h" #include "CELLPOWER-BMS.h"
#endif
#ifdef CHADEMO_BATTERY
#include "CHADEMO-BATTERY.h" #include "CHADEMO-BATTERY.h"
#include "CHADEMO-SHUNTS.h" #include "CHADEMO-SHUNTS.h"
#endif
#ifdef CMFA_EV_BATTERY
#include "CMFA-EV-BATTERY.h" #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" #include "DALY-BMS.h"
#endif #include "ECMP-BATTERY.h"
#include "FOXESS-BATTERY.h"
#ifdef RJXZS_BMS #include "GEELY-GEOMETRY-C-BATTERY.h"
#include "RJXZS-BMS.h" #include "IMIEV-CZERO-ION-BATTERY.h"
#endif #include "JAGUAR-IPACE-BATTERY.h"
#include "KIA-E-GMP-BATTERY.h"
#ifdef RANGE_ROVER_PHEV_BATTERY #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" #include "RANGE-ROVER-PHEV-BATTERY.h"
#endif
#ifdef RENAULT_KANGOO_BATTERY
#include "RENAULT-KANGOO-BATTERY.h" #include "RENAULT-KANGOO-BATTERY.h"
#endif
#ifdef RENAULT_TWIZY_BATTERY
#include "RENAULT-TWIZY.h" #include "RENAULT-TWIZY.h"
#endif
#ifdef RENAULT_ZOE_GEN1_BATTERY
#include "RENAULT-ZOE-GEN1-BATTERY.h" #include "RENAULT-ZOE-GEN1-BATTERY.h"
#endif
#ifdef RENAULT_ZOE_GEN2_BATTERY
#include "RENAULT-ZOE-GEN2-BATTERY.h" #include "RENAULT-ZOE-GEN2-BATTERY.h"
#endif #include "RJXZS-BMS.h"
#ifdef SANTA_FE_PHEV_BATTERY
#include "SANTA-FE-PHEV-BATTERY.h" #include "SANTA-FE-PHEV-BATTERY.h"
#endif
#ifdef SIMPBMS_BATTERY
#include "SIMPBMS-BATTERY.h" #include "SIMPBMS-BATTERY.h"
#endif #include "SONO-BATTERY.h"
#if defined(TESLA_MODEL_SX_BATTERY) || defined(TESLA_MODEL_3Y_BATTERY)
#define TESLA_BATTERY
#include "TESLA-BATTERY.h" #include "TESLA-BATTERY.h"
#endif
#ifdef TEST_FAKE_BATTERY
#include "TEST-FAKE-BATTERY.h" #include "TEST-FAKE-BATTERY.h"
#endif
#ifdef VOLVO_SPA_BATTERY
#include "VOLVO-SPA-BATTERY.h" #include "VOLVO-SPA-BATTERY.h"
#endif
#ifdef VOLVO_SPA_HYBRID_BATTERY
#include "VOLVO-SPA-HYBRID-BATTERY.h" #include "VOLVO-SPA-HYBRID-BATTERY.h"
#endif
void setup_battery(void); void setup_battery(void);

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "BMW-I3-BATTERY.h"
#ifdef BMW_I3_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" #include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.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 */ /* 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; return crc;
} }
static uint8_t increment_alive_counter(uint8_t counter) { uint8_t BmwI3Battery::increment_alive_counter(uint8_t counter) {
counter++; counter++;
if (counter > ALIVE_MAX_VALUE) { if (counter > ALIVE_MAX_VALUE) {
counter = 0; counter = 0;
@ -509,7 +508,7 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
} }
void BmwI3Battery::setup(void) { // Performs one time setup at startup 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'; datalayer.system.info.battery_protocol[63] = '\0';
//Before we have started up and detected which battery is in use, use 60AH values //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); pinMode(wakeup_pin, OUTPUT);
digitalWrite(wakeup_pin, HIGH); // Wake up the battery digitalWrite(wakeup_pin, HIGH); // Wake up the battery
} }
#endif

View file

@ -39,6 +39,7 @@ class BmwI3Battery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values(); virtual void update_values();
virtual void transmit_can(unsigned long currentMillis); virtual void transmit_can(unsigned long currentMillis);
static constexpr char* Name = "BMW i3";
BatteryHtmlRenderer& get_status_renderer() { return renderer; } BatteryHtmlRenderer& get_status_renderer() { return renderer; }
@ -80,7 +81,9 @@ class BmwI3Battery : public CanBattery {
unsigned long previousMillis5000 = 0; // will store last time a 5000ms CAN Message was send 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 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 }; enum BatterySize { BATTERY_60AH, BATTERY_94AH, BATTERY_120AH };
BatterySize detectedBattery = BATTERY_60AH; BatterySize detectedBattery = BATTERY_60AH;

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "BMW-IX-BATTERY.h"
#ifdef BMW_IX_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" #include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.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 // 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) { 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 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'; datalayer.system.info.battery_protocol[63] = '\0';
//Reset Battery at bootup //Reset Battery at bootup
@ -696,5 +695,3 @@ void BmwIXBattery::HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms) {
} }
} }
} }
#endif // BMW_IX_BATTERY

View file

@ -22,6 +22,8 @@ class BmwIXBattery : public CanBattery {
void request_open_contactors() { datalayer_extended.bmwix.UserRequestContactorOpen = true; } void request_open_contactors() { datalayer_extended.bmwix.UserRequestContactorOpen = true; }
void request_close_contactors() { datalayer_extended.bmwix.UserRequestContactorClose = true; } void request_close_contactors() { datalayer_extended.bmwix.UserRequestContactorClose = true; }
static constexpr char* Name = "BMW iX and i4-7 platform";
private: private:
BmwIXHtmlRenderer renderer; BmwIXHtmlRenderer renderer;
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "BMW-PHEV-BATTERY.h"
#ifdef BMW_PHEV_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" #include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "BMW-PHEV-BATTERY.h" #include "../include.h"
const unsigned char crc8_table[256] = const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies { // 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.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }
#endif

View file

@ -1,8 +1,7 @@
#include "../include.h" #include "BMW-SBOX.h"
#ifdef BMW_SBOX
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "BMW-SBOX.h" #include "../include.h"
uint8_t reverse_bits(uint8_t byte) { uint8_t reverse_bits(uint8_t byte) {
uint8_t reversed = 0; uint8_t reversed = 0;
@ -182,4 +181,3 @@ void BmwSbox::setup() {
strncpy(datalayer.system.info.shunt_protocol, "BMW SBOX", 63); strncpy(datalayer.system.info.shunt_protocol, "BMW SBOX", 63);
datalayer.system.info.shunt_protocol[63] = '\0'; datalayer.system.info.shunt_protocol[63] = '\0';
} }
#endif

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "BOLT-AMPERA-BATTERY.h"
#ifdef BOLT_AMPERA_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" #include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "BOLT-AMPERA-BATTERY.h" #include "../include.h"
/* /*
TODOs left for this implementation 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 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.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; 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.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }
#endif

View file

@ -17,6 +17,8 @@ class BoltAmperaBattery : public CanBattery {
virtual void update_values(); virtual void update_values();
virtual void transmit_can(unsigned long currentMillis); virtual void transmit_can(unsigned long currentMillis);
static constexpr char* Name = "Chevrolet Bolt EV/Opel Ampera-e";
BatteryHtmlRenderer& get_status_renderer() { return renderer; } BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private: private:

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "BYD-ATTO-3-BATTERY.h"
#ifdef BYD_ATTO_3_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" #include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "BYD-ATTO-3-BATTERY.h" #include "../include.h"
/* Notes /* Notes
SOC% by default is now ESTIMATED. SOC% by default is now ESTIMATED.
@ -681,7 +680,7 @@ void BydAttoBattery::transmit_can(unsigned long currentMillis) {
} }
void BydAttoBattery::setup(void) { // Performs one time setup at startup 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.system.info.battery_protocol[63] = '\0';
datalayer_battery->info.number_of_cells = CELLCOUNT_STANDARD; datalayer_battery->info.number_of_cells = CELLCOUNT_STANDARD;
datalayer_battery->info.chemistry = battery_chemistry_enum::LFP; datalayer_battery->info.chemistry = battery_chemistry_enum::LFP;
@ -696,5 +695,3 @@ void BydAttoBattery::setup(void) { // Performs one time setup at startup
SOC_method = MEASURED; SOC_method = MEASURED;
#endif #endif
} }
#endif

View file

@ -22,16 +22,6 @@
#define SELECTED_BATTERY_CLASS BydAttoBattery #define SELECTED_BATTERY_CLASS BydAttoBattery
#endif #endif
#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
class BydAttoBattery : public CanBattery { class BydAttoBattery : public CanBattery {
public: public:
// Use this constructor for the second battery. // Use this constructor for the second battery.
@ -54,6 +44,8 @@ class BydAttoBattery : public CanBattery {
virtual void update_values(); virtual void update_values();
virtual void transmit_can(unsigned long currentMillis); virtual void transmit_can(unsigned long currentMillis);
static constexpr char* Name = "BYD Atto 3";
bool supports_reset_crash() { return true; } bool supports_reset_crash() { return true; }
void reset_crash() { datalayer_bydatto->UserRequestCrashReset = true; } void reset_crash() { datalayer_bydatto->UserRequestCrashReset = true; }
@ -68,6 +60,16 @@ class BydAttoBattery : public CanBattery {
BatteryHtmlRenderer& get_status_renderer() { return renderer; } BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private: 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; BydAtto3HtmlRenderer renderer;
DATALAYER_BATTERY_TYPE* datalayer_battery; DATALAYER_BATTERY_TYPE* datalayer_battery;
DATALAYER_INFO_BYDATTO3* datalayer_bydatto; DATALAYER_INFO_BYDATTO3* datalayer_bydatto;

View file

@ -4,6 +4,53 @@
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h" #include "src/devboard/webserver/BatteryHtmlRenderer.h"
enum class BatteryType {
None = 0,
BmwSbox,
BmwI3,
BmwIx,
BoltAmpera,
BydAtto3,
CellPowerBms,
Chademo,
CmfaEv,
Foxess,
GeelyGeometryC,
OrionBms,
Sono,
StellantisEcmp,
ImievCZeroIon,
JaguarIpace,
KiaEGmp,
KiaHyundai64,
KiaHyundaiHybrid,
Meb,
Mg5,
NissanLeaf,
Pylon,
DalyBms,
RjxzsBms,
RangeRoverPhev,
RenaultKangoo,
RenaultTwizy,
RenaultZoe1,
RenaultZoe2,
SantaFePhev,
SimpBms,
TeslaModel3Y,
TeslaModelSX,
TestFake,
VolvoSpa,
VolvoSpaHybrid,
Highest
};
extern std::vector<BatteryType> supported_battery_types();
extern const char* name_for_type(BatteryType type);
extern BatteryType user_selected_battery_type;
extern bool user_selected_second_battery;
// Abstract base class for next-generation battery implementations. // Abstract base class for next-generation battery implementations.
// Defines the interface to call battery specific functionality. // Defines the interface to call battery specific functionality.
class Battery { class Battery {

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "CELLPOWER-BMS.h"
#ifdef CELLPOWER_BMS
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "CELLPOWER-BMS.h" #include "../include.h"
void CellPowerBms::update_values() { 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 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.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; 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.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
} }
#endif // CELLPOWER_BMS

View file

@ -16,6 +16,8 @@ class CellPowerBms : public CanBattery {
virtual void update_values(); virtual void update_values();
virtual void transmit_can(unsigned long currentMillis); virtual void transmit_can(unsigned long currentMillis);
static constexpr char* Name = "Cellpower BMS";
BatteryHtmlRenderer& get_status_renderer() { return renderer; } BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private: private:

View file

@ -1,8 +1,7 @@
#include "../include.h" #include "CHADEMO-BATTERY.h"
#ifdef CHADEMO_BATTERY
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "CHADEMO-BATTERY.h" #include "../include.h"
#include "CHADEMO-SHUNTS.h" #include "CHADEMO-SHUNTS.h"
/* CHADEMO handling runs at 6.25 times the rate of most other code, so, rather than the /* CHADEMO handling runs at 6.25 times the rate of most other code, so, rather than the
@ -939,7 +938,7 @@ void ChademoBattery::setup(void) { // Performs one time setup at startup
pinMode(CHADEMO_PIN_4, INPUT); pinMode(CHADEMO_PIN_4, INPUT);
pinMode(CHADEMO_PIN_7, 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'; datalayer.system.info.battery_protocol[63] = '\0';
CHADEMO_Status = CHADEMO_IDLE; CHADEMO_Status = CHADEMO_IDLE;
@ -988,4 +987,3 @@ void ChademoBattery::setup(void) { // Performs one time setup at startup
setupMillis = millis(); setupMillis = millis();
} }
#endif

View file

@ -9,10 +9,6 @@
//Contactor control is required for CHADEMO support //Contactor control is required for CHADEMO support
#define CONTACTOR_CONTROL #define CONTACTOR_CONTROL
//ISA shunt is currently required for CHADEMO support
// other measurement sources may be added in the future
#define ISA_SHUNT
#endif #endif
class ChademoBattery : public CanBattery { class ChademoBattery : public CanBattery {
@ -22,6 +18,8 @@ class ChademoBattery : public CanBattery {
virtual void update_values(); virtual void update_values();
virtual void transmit_can(unsigned long currentMillis); virtual void transmit_can(unsigned long currentMillis);
static constexpr char* Name = "Chademo V2X mode";
private: private:
void process_vehicle_charging_minimums(CAN_frame rx_frame); void process_vehicle_charging_minimums(CAN_frame rx_frame);
void process_vehicle_charging_maximums(CAN_frame rx_frame); void process_vehicle_charging_maximums(CAN_frame rx_frame);

View file

@ -18,12 +18,11 @@
* by NJbubo * by NJbubo
* *
*/ */
#include "../include.h" #include "CHADEMO-SHUNTS.h"
#ifdef CHADEMO_BATTERY
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "../include.h"
#include "CHADEMO-BATTERY.h" #include "CHADEMO-BATTERY.h"
#include "CHADEMO-SHUNTS.h"
/* Initial frames received from ISA shunts provide invalid during initialization */ /* Initial frames received from ISA shunts provide invalid during initialization */
static int framecount = 0; static int framecount = 0;
@ -429,4 +428,3 @@ void ISA_getINFO(uint8_t i) {
transmit_can_frame(&outframe, can_config.battery); transmit_can_frame(&outframe, can_config.battery);
} }
#endif

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "CMFA-EV-BATTERY.h"
#ifdef CMFA_EV_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" #include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.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 /* 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% */ Same goes for low point, when 10% is reached we report 0% */
@ -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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
} }
#endif //CMFA_EV_BATTERY

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "DALY-BMS.h"
#ifdef DALY_BMS
#include <cstdint> #include <cstdint>
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.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 */ /* Do not change code below unless you are sure what you are doing */
@ -207,5 +206,3 @@ void DalyBms::receive() {
} }
} }
} }
#endif

View file

@ -3,17 +3,9 @@
#include "RS485Battery.h" #include "RS485Battery.h"
/* Tweak these according to your battery build */ #ifdef DALY_BMS
#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
#define SELECTED_BATTERY_CLASS DalyBms #define SELECTED_BATTERY_CLASS DalyBms
#endif
class DalyBms : public RS485Battery { class DalyBms : public RS485Battery {
public: public:
@ -23,6 +15,17 @@ class DalyBms : public RS485Battery {
void receive(); void receive();
private: 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; } int baud_rate() { return 9600; }
}; };

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "ECMP-BATTERY.h"
#ifdef STELLANTIS_ECMP_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For More Battery Info page #include "../datalayer/datalayer_extended.h" //For More Battery Info page
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "ECMP-BATTERY.h" #include "../include.h"
/* TODO: /* TODO:
This integration is still ongoing. Here is what still needs to be done in order to use this battery type This integration is still ongoing. Here is what still needs to be done in order to use this battery type
@ -331,5 +330,3 @@ void EcmpBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }
#endif

View file

@ -6,7 +6,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#include "ECMP-HTML.h" #include "ECMP-HTML.h"
#ifdef STELLANTIS_ECMP_BATTERY
#define SELECTED_BATTERY_CLASS EcmpBattery #define SELECTED_BATTERY_CLASS EcmpBattery
#endif
class EcmpBattery : public CanBattery { class EcmpBattery : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "FOXESS-BATTERY.h"
#ifdef FOXESS_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "FOXESS-BATTERY.h" #include "../include.h"
/* /*
Can bus @ 500k - all Extended ID, little endian Can bus @ 500k - all Extended ID, little endian
@ -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.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }
#endif

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "GEELY-GEOMETRY-C-BATTERY.h"
#ifdef GEELY_GEOMETRY_C_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "GEELY-GEOMETRY-C-BATTERY.h" #include "../include.h"
/* TODO /* 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 - 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
@ -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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
} }
#endif

View file

@ -10,27 +10,6 @@
#define SELECTED_BATTERY_CLASS GeelyGeometryCBattery #define SELECTED_BATTERY_CLASS GeelyGeometryCBattery
#endif #endif
#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
class GeelyGeometryCBattery : public CanBattery { class GeelyGeometryCBattery : public CanBattery {
public: public:
virtual void setup(void); virtual void setup(void);
@ -41,13 +20,38 @@ class GeelyGeometryCBattery : public CanBattery {
BatteryHtmlRenderer& get_status_renderer() { return renderer; } BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private: 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; GeelyGeometryCHtmlRenderer renderer;
const int MAX_PACK_VOLTAGE_70_DV 4420 //70kWh
const int MIN_PACK_VOLTAGE_70_DV 2860 const int MAX_PACK_VOLTAGE_53_DV 4160 //53kWh static const int MAX_PACK_VOLTAGE_70_DV = 4420; //70kWh
const int MIN_PACK_VOLTAGE_53_DV 2700 const int MAX_CELL_DEVIATION_MV 150 const int static const int MIN_PACK_VOLTAGE_70_DV = 2860;
MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value static const int MAX_PACK_VOLTAGE_53_DV = 4160; //53kWh
const int MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value static const int MIN_PACK_VOLTAGE_53_DV = 2700;
DATALAYER_BATTERY_TYPE* datalayer_battery; 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
DATALAYER_BATTERY_TYPE* datalayer_battery;
DATALAYER_INFO_GEELY_GEOMETRY_C* datalayer_geometryc; DATALAYER_INFO_GEELY_GEOMETRY_C* datalayer_geometryc;
CAN_frame GEELY_191 = {.FD = false, //PAS_APA_Status , 10ms CAN_frame GEELY_191 = {.FD = false, //PAS_APA_Status , 10ms
@ -214,10 +218,7 @@ class GeelyGeometryCBattery : public CanBattery {
uint16_t poll_unknown7 = 0; uint16_t poll_unknown7 = 0;
uint16_t poll_unknown8 = 0; uint16_t poll_unknown8 = 0;
int16_t poll_temperature[6] = {0}; int16_t poll_temperature[6] = {0};
static const int TEMP_OFFSET = 30; //TODO, not calibrated yet, best guess
//TODO, not calibrated yet, best guess
static const int TEMP_OFFSET = 30;
uint8_t poll_software_version[16] = {0}; uint8_t poll_software_version[16] = {0};
uint8_t poll_hardware_version[16] = {0}; uint8_t poll_hardware_version[16] = {0};
}; };

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "IMIEV-CZERO-ION-BATTERY.h"
#ifdef IMIEV_CZERO_ION_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "IMIEV-CZERO-ION-BATTERY.h" #include "../include.h"
//Code still work in progress, TODO: //Code still work in progress, TODO:
//Figure out if CAN messages need to be sent to keep the system happy? //Figure out if CAN messages need to be sent to keep the system happy?
@ -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.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }
#endif

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#ifdef IMIEV_CZERO_ION_BATTERY
#define SELECTED_BATTERY_CLASS ImievCZeroIonBattery #define SELECTED_BATTERY_CLASS ImievCZeroIonBattery
#endif
class ImievCZeroIonBattery : public CanBattery { class ImievCZeroIonBattery : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "JAGUAR-IPACE-BATTERY.h"
#ifdef JAGUAR_IPACE_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.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 */ /* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillisKeepAlive = 0; static unsigned long previousMillisKeepAlive = 0;
@ -57,7 +56,7 @@ CAN_frame ipace_keep_alive = {.FD = false,
.ID = 0x59e, .ID = 0x59e,
.data = {0x9E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};*/ .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(header);
logging.print(value); logging.print(value);
logging.print(units); 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.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }
#endif

View file

@ -3,13 +3,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#ifdef JAGUAR_IPACE_BATTERY
#define SELECTED_BATTERY_CLASS JaguarIpaceBattery #define SELECTED_BATTERY_CLASS JaguarIpaceBattery
#endif
#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
class JaguarIpaceBattery : public CanBattery { class JaguarIpaceBattery : public CanBattery {
public: public:
@ -17,6 +13,13 @@ class JaguarIpaceBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values(); virtual void update_values();
virtual void transmit_can(unsigned long currentMillis); virtual void transmit_can(unsigned long currentMillis);
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 #endif

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "KIA-E-GMP-BATTERY.h"
#ifdef KIA_E_GMP_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "../include.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h" #include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "KIA-E-GMP-BATTERY.h"
const unsigned char crc8_table[256] = const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies { // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
@ -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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
} }
#endif

View file

@ -9,7 +9,9 @@ extern ACAN2517FD canfd;
#define ESTIMATE_SOC_FROM_CELLVOLTAGE #define ESTIMATE_SOC_FROM_CELLVOLTAGE
#ifdef KIA_E_GMP_BATTERY
#define SELECTED_BATTERY_CLASS KiaEGmpBattery #define SELECTED_BATTERY_CLASS KiaEGmpBattery
#endif
class KiaEGmpBattery : public CanBattery { class KiaEGmpBattery : public CanBattery {
public: public:

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "KIA-HYUNDAI-64-BATTERY.h"
#ifdef KIA_HYUNDAI_64_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" #include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-64-BATTERY.h" #include "../include.h"
void KiaHyundai64Battery:: void KiaHyundai64Battery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
@ -475,5 +474,3 @@ void KiaHyundai64Battery::setup(void) { // Performs one time setup at startup
*allows_contactor_closing = true; *allows_contactor_closing = true;
} }
} }
#endif

View file

@ -7,7 +7,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#include "KIA-HYUNDAI-64-HTML.h" #include "KIA-HYUNDAI-64-HTML.h"
#ifdef KIA_HYUNDAI_64_BATTERY
#define SELECTED_BATTERY_CLASS KiaHyundai64Battery #define SELECTED_BATTERY_CLASS KiaHyundai64Battery
#endif
class KiaHyundai64Battery : public CanBattery { class KiaHyundai64Battery : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "KIA-HYUNDAI-HYBRID-BATTERY.h"
#ifdef KIA_HYUNDAI_HYBRID_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-HYBRID-BATTERY.h" #include "../include.h"
/* TODO: /* TODO:
- The HEV battery seems to turn off after 1 minute of use. When this happens SOC% stops updating. - The HEV battery seems to turn off after 1 minute of use. When this happens SOC% stops updating.
@ -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.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
} }
#endif

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#ifdef KIA_HYUNDAI_HYBRID_BATTERY
#define SELECTED_BATTERY_CLASS KiaHyundaiHybridBattery #define SELECTED_BATTERY_CLASS KiaHyundaiHybridBattery
#endif
class KiaHyundaiHybridBattery : public CanBattery { class KiaHyundaiHybridBattery : public CanBattery {
public: public:

View file

@ -1,12 +1,11 @@
#include "../include.h" #include "MEB-BATTERY.h"
#ifdef MEB_BATTERY
#include <algorithm> // For std::min and std::max #include <algorithm> // For std::min and std::max
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../communication/can/obd.h" #include "../communication/can/obd.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "MEB-BATTERY.h" #include "../include.h"
/* /*
TODO list TODO list
@ -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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
} }
#endif

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#include "MEB-HTML.h" #include "MEB-HTML.h"
#ifdef MEB_BATTERY
#define SELECTED_BATTERY_CLASS MebBattery #define SELECTED_BATTERY_CLASS MebBattery
#endif
class MebBattery : public CanBattery { class MebBattery : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "MG-5-BATTERY.h"
#ifdef MG_5_BATTERY_H
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "MG-5-BATTERY.h" #include "../include.h"
/* TODO: /* TODO:
- Get contactor closing working - 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.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
} }
#endif

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#ifdef MG_5_BATTERY
#define SELECTED_BATTERY_CLASS Mg5Battery #define SELECTED_BATTERY_CLASS Mg5Battery
#endif
class Mg5Battery : public CanBattery { class Mg5Battery : public CanBattery {
public: public:

View file

@ -1,6 +1,5 @@
#include "../include.h"
#ifdef NISSAN_LEAF_BATTERY
#include "NISSAN-LEAF-BATTERY.h" #include "NISSAN-LEAF-BATTERY.h"
#include "../include.h"
#ifdef MQTT #ifdef MQTT
#include "../devboard/mqtt/mqtt.h" #include "../devboard/mqtt/mqtt.h"
#endif #endif
@ -970,7 +969,7 @@ void decodeChallengeData(unsigned int incomingChallenge, unsigned char* solvedCh
} }
void NissanLeafBattery::setup(void) { // Performs one time setup at startup 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.system.info.battery_protocol[63] = '\0';
datalayer_battery->info.number_of_cells = 96; datalayer_battery->info.number_of_cells = 96;
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV; 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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
} }
#endif //NISSAN_LEAF_BATTERY

View file

@ -7,14 +7,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#include "NISSAN-LEAF-HTML.h" #include "NISSAN-LEAF-HTML.h"
#ifdef NISSAN_LEAF_BATTERY
#define SELECTED_BATTERY_CLASS NissanLeafBattery #define SELECTED_BATTERY_CLASS NissanLeafBattery
#define EXTENDED_DATA_PTR (&datalayer_extended.nissanleaf) #endif
#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
class NissanLeafBattery : public CanBattery { class NissanLeafBattery : public CanBattery {
public: public:
@ -51,8 +46,15 @@ class NissanLeafBattery : public CanBattery {
} }
BatteryHtmlRenderer& get_status_renderer() { return renderer; } BatteryHtmlRenderer& get_status_renderer() { return renderer; }
static constexpr char* Name = "Nissan LEAF battery";
private: 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; NissanLeafHtmlRenderer renderer;
bool is_message_corrupt(CAN_frame rx_frame); bool is_message_corrupt(CAN_frame rx_frame);

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "ORION-BMS.h"
#ifdef ORION_BMS
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.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, void findMinMaxCellvoltages(const uint16_t arr[], size_t size, uint16_t& Minimum_Cell_Voltage,
uint16_t& Maximum_Cell_Voltage) { uint16_t& Maximum_Cell_Voltage) {
@ -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.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }
#endif

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#ifdef ORION_BMS
#define SELECTED_BATTERY_CLASS OrionBms #define SELECTED_BATTERY_CLASS OrionBms
#endif
class OrionBms : public CanBattery { class OrionBms : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "PYLON-BATTERY.h"
#ifdef PYLON_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "PYLON-BATTERY.h" #include "../include.h"
void PylonBattery::update_values() { void PylonBattery::update_values() {
@ -144,5 +143,3 @@ void PylonBattery::setup(void) { // Performs one time setup at startup
*allows_contactor_closing = true; *allows_contactor_closing = true;
} }
} }
#endif

View file

@ -6,7 +6,9 @@
#include "../include.h" #include "../include.h"
#include "CanBattery.h" #include "CanBattery.h"
#ifdef PYLON_BATTERY
#define SELECTED_BATTERY_CLASS PylonBattery #define SELECTED_BATTERY_CLASS PylonBattery
#endif
class PylonBattery : public CanBattery { class PylonBattery : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "RANGE-ROVER-PHEV-BATTERY.h"
#ifdef RANGE_ROVER_PHEV_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "RANGE-ROVER-PHEV-BATTERY.h" #include "../include.h"
/* TODO /* TODO
- LOG files from vehicle needed to determine CAN content needed to send towards battery! - LOG files from vehicle needed to determine CAN content needed to send towards battery!
@ -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.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
} }
#endif //RANGE_ROVER_PHEV_BATTERY

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#ifdef RANGE_ROVER_PHEV_BATTERY
#define SELECTED_BATTERY_CLASS RangeRoverPhevBattery #define SELECTED_BATTERY_CLASS RangeRoverPhevBattery
#endif
class RangeRoverPhevBattery : public CanBattery { class RangeRoverPhevBattery : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "RENAULT-KANGOO-BATTERY.h"
#ifdef RENAULT_KANGOO_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "RENAULT-KANGOO-BATTERY.h" #include "../include.h"
/* TODO: /* TODO:
There seems to be some values on the Kangoo that differ between the 22/33 kWh version There seems to be some values on the Kangoo that differ between the 22/33 kWh version
@ -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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
} }
#endif

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#ifdef RENAULT_KANGOO_BATTERY
#define SELECTED_BATTERY_CLASS RenaultKangooBattery #define SELECTED_BATTERY_CLASS RenaultKangooBattery
#endif
class RenaultKangooBattery : public CanBattery { class RenaultKangooBattery : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "RENAULT-TWIZY.h"
#include <cstdint> #include <cstdint>
#include "../include.h"
#ifdef RENAULT_TWIZY_BATTERY
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "RENAULT-TWIZY.h" #include "../include.h"
int16_t max_value(int16_t* entries, size_t len) { int16_t max_value(int16_t* entries, size_t len) {
int result = INT16_MIN; int result = INT16_MIN;
@ -129,5 +128,3 @@ void RenaultTwizyBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.total_capacity_Wh = 6600; datalayer.battery.info.total_capacity_Wh = 6600;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }
#endif

View file

@ -3,7 +3,9 @@
#include "../include.h" #include "../include.h"
#include "CanBattery.h" #include "CanBattery.h"
#ifdef RENAULT_TWIZY_BATTERY
#define SELECTED_BATTERY_CLASS RenaultTwizyBattery #define SELECTED_BATTERY_CLASS RenaultTwizyBattery
#endif
class RenaultTwizyBattery : public CanBattery { class RenaultTwizyBattery : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "RENAULT-ZOE-GEN1-BATTERY.h"
#ifdef RENAULT_ZOE_GEN1_BATTERY
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" #include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.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); void transmit_can_frame(CAN_frame* tx_frame, int interface);
@ -567,5 +566,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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
} }
#endif

View file

@ -4,13 +4,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#include "RENAULT-ZOE-GEN1-HTML.h" #include "RENAULT-ZOE-GEN1-HTML.h"
#ifdef RENAULT_ZOE_GEN1_BATTERY
#define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery #define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery
#endif
#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
class RenaultZoeGen1Battery : public CanBattery { class RenaultZoeGen1Battery : public CanBattery {
public: public:
@ -22,6 +18,12 @@ class RenaultZoeGen1Battery : public CanBattery {
BatteryHtmlRenderer& get_status_renderer() { return renderer; } BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private: 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; RenaultZoeGen1HtmlRenderer renderer;
}; };

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "RENAULT-ZOE-GEN2-BATTERY.h"
#ifdef RENAULT_ZOE_GEN2_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "RENAULT-ZOE-GEN2-BATTERY.h" #include "../include.h"
/* TODO /* TODO
- Add //NVROL Reset - Add //NVROL Reset
@ -688,5 +687,3 @@ void RenaultZoeGen2Battery::wait_ms(int duration_ms) {
// Do nothing - just wait // Do nothing - just wait
} }
} }
#endif

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#include "RENAULT-ZOE-GEN2-HTML.h" #include "RENAULT-ZOE-GEN2-HTML.h"
#ifdef RENAULT_ZOE_GEN2_BATTERY
#define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery #define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery
#endif
class RenaultZoeGen2Battery : public CanBattery { class RenaultZoeGen2Battery : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "RJXZS-BMS.h"
#ifdef RJXZS_BMS
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "RJXZS-BMS.h" #include "../include.h"
void RjxzsBms::update_values() { void RjxzsBms::update_values() {
@ -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.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }
#endif // RJXZS_BMS

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#ifdef RJXZS_BMS
#define SELECTED_BATTERY_CLASS RjxzsBms #define SELECTED_BATTERY_CLASS RjxzsBms
#endif
class RjxzsBms : public CanBattery { class RjxzsBms : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "SANTA-FE-PHEV-BATTERY.h"
#ifdef SANTA_FE_PHEV_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "SANTA-FE-PHEV-BATTERY.h" #include "../include.h"
/* Credits go to maciek16c for these findings! /* Credits go to maciek16c for these findings!
https://github.com/maciek16c/hyundai-santa-fe-phev-battery https://github.com/maciek16c/hyundai-santa-fe-phev-battery
@ -333,5 +332,3 @@ void SantaFePhevBattery::setup(void) { // Performs one time setup at startup
*allows_contactor_closing = true; *allows_contactor_closing = true;
} }
} }
#endif

View file

@ -5,7 +5,9 @@
#include "../include.h" #include "../include.h"
#include "CanBattery.h" #include "CanBattery.h"
#ifdef SANTA_FE_PHEV_BATTERY
#define SELECTED_BATTERY_CLASS SantaFePhevBattery #define SELECTED_BATTERY_CLASS SantaFePhevBattery
#endif
class SantaFePhevBattery : public CanBattery { class SantaFePhevBattery : public CanBattery {
public: public:

View file

@ -1,8 +1,7 @@
#include "../include.h" #include "SIMPBMS-BATTERY.h"
#ifdef SIMPBMS_BATTERY
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "SIMPBMS-BATTERY.h" #include "../include.h"
void SimpBmsBattery::update_values() { void SimpBmsBattery::update_values() {
@ -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.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
} }
#endif

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#ifdef SIMPBMS_BATTERY
#define SELECTED_BATTERY_CLASS SimpBmsBattery #define SELECTED_BATTERY_CLASS SimpBmsBattery
#endif
class SimpBmsBattery : public CanBattery { class SimpBmsBattery : public CanBattery {
public: public:

View file

@ -1,9 +1,8 @@
#include "../include.h" #include "SONO-BATTERY.h"
#ifdef SONO_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "SONO-BATTERY.h" #include "../include.h"
void SonoBattery:: void SonoBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
@ -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.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
} }
#endif

View file

@ -5,7 +5,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#ifdef SONO_BATTERY
#define SELECTED_BATTERY_CLASS SonoBattery #define SELECTED_BATTERY_CLASS SonoBattery
#endif
class SonoBattery : public CanBattery { class SonoBattery : public CanBattery {
public: public:

View file

@ -4,6 +4,10 @@
CanShunt* shunt = nullptr; CanShunt* shunt = nullptr;
void setup_can_shunt() { void setup_can_shunt() {
if (shunt) {
return;
}
#if defined(SELECTED_SHUNT_CLASS) #if defined(SELECTED_SHUNT_CLASS)
shunt = new SELECTED_SHUNT_CLASS(); shunt = new SELECTED_SHUNT_CLASS();
if (shunt) { if (shunt) {

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "TESLA-BATTERY.h"
#ifdef TESLA_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For Advanced Battery Insights webpage #include "../datalayer/datalayer_extended.h" //For Advanced Battery Insights webpage
#include "../devboard/utils/events.h" #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/) */ /* 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; *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, Name, 63);
strncpy(datalayer.system.info.battery_protocol, "Tesla Model 3/Y", 63);
datalayer.system.info.battery_protocol[63] = '\0'; datalayer.system.info.battery_protocol[63] = '\0';
#ifdef LFP_CHEMISTRY #ifdef LFP_CHEMISTRY
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP; 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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
#endif // !LFP_CHEMISTRY #endif // !LFP_CHEMISTRY
#endif // TESLA_MODEL_3Y_BATTERY
} }
void TeslaModelSXBattery::setup(void) { void TeslaModelSXBattery::setup(void) {
@ -1789,7 +1786,7 @@ void TeslaModelSXBattery::setup(void) {
*allows_contactor_closing = true; *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.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA;
datalayer.battery.info.min_design_voltage_dV = MIN_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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
} }
#endif // TESLA_BATTERY

View file

@ -513,12 +513,14 @@ class TeslaModel3YBattery : public TeslaBattery {
operate_contactors = true; operate_contactors = true;
#endif #endif
} }
static constexpr char* Name = "Tesla Model 3/Y";
virtual void setup(void); virtual void setup(void);
}; };
class TeslaModelSXBattery : public TeslaBattery { class TeslaModelSXBattery : public TeslaBattery {
public: public:
TeslaModelSXBattery() { operate_contactors = true; } TeslaModelSXBattery() { operate_contactors = true; }
static constexpr char* Name = "Tesla Model S/X";
virtual void setup(void); virtual void setup(void);
}; };

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef TEST_FAKE_BATTERY
#include "../datalayer/datalayer.h"
#include "TEST-FAKE-BATTERY.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(header);
logging.print(value); logging.print(value);
logging.print(units); logging.print(units);
@ -75,7 +74,7 @@ void TestFakeBattery::transmit_can(unsigned long currentMillis) {
void TestFakeBattery::setup(void) { // Performs one time setup at startup void TestFakeBattery::setup(void) { // Performs one time setup at startup
randomSeed(analogRead(0)); 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.system.info.battery_protocol[63] = '\0';
datalayer_battery->info.max_design_voltage_dV = 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; *allows_contactor_closing = true;
} }
} }
#endif

View file

@ -4,7 +4,9 @@
#include "../include.h" #include "../include.h"
#include "CanBattery.h" #include "CanBattery.h"
#ifdef TEST_FAKE_BATTERY
#define SELECTED_BATTERY_CLASS TestFakeBattery #define SELECTED_BATTERY_CLASS TestFakeBattery
#endif
class TestFakeBattery : public CanBattery { class TestFakeBattery : public CanBattery {
public: public:
@ -20,6 +22,8 @@ class TestFakeBattery : public CanBattery {
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing; allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
} }
static constexpr char* Name = "Fake battery for testing purposes";
virtual void setup(); virtual void setup();
virtual void handle_incoming_can_frame(CAN_frame rx_frame); virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values(); virtual void update_values();

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "VOLVO-SPA-BATTERY.h"
#ifdef VOLVO_SPA_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "VOLVO-SPA-BATTERY.h" #include "../include.h"
void VolvoSpaBattery:: void VolvoSpaBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
@ -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.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
} }
#endif

View file

@ -6,7 +6,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#include "VOLVO-SPA-HTML.h" #include "VOLVO-SPA-HTML.h"
#ifdef VOLVO_SPA_BATTERY
#define SELECTED_BATTERY_CLASS VolvoSpaBattery #define SELECTED_BATTERY_CLASS VolvoSpaBattery
#endif
class VolvoSpaBattery : public CanBattery { class VolvoSpaBattery : public CanBattery {
public: public:

View file

@ -1,10 +1,9 @@
#include "../include.h" #include "VOLVO-SPA-HYBRID-BATTERY.h"
#ifdef VOLVO_SPA_HYBRID_BATTERY
#include "../communication/can/comm_can.h" #include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h" #include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage #include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "VOLVO-SPA-HYBRID-BATTERY.h" #include "../include.h"
void VolvoSpaHybridBattery:: void VolvoSpaHybridBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
@ -563,4 +562,3 @@ void VolvoSpaHybridBattery::setup(void) { //
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV; datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV; datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
} }
#endif

View file

@ -6,7 +6,9 @@
#include "CanBattery.h" #include "CanBattery.h"
#include "VOLVO-SPA-HYBRID-HTML.h" #include "VOLVO-SPA-HYBRID-HTML.h"
#ifdef VOLVO_SPA_HYBRID_BATTERY
#define SELECTED_BATTERY_CLASS VolvoSpaHybridBattery #define SELECTED_BATTERY_CLASS VolvoSpaHybridBattery
#endif
class VolvoSpaHybridBattery : public CanBattery { class VolvoSpaHybridBattery : public CanBattery {
public: public:

View file

@ -2,8 +2,42 @@
CanCharger* charger = nullptr; 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_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() { 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 #ifdef SELECTED_CHARGER_CLASS
charger = new SELECTED_CHARGER_CLASS(); charger = new SELECTED_CHARGER_CLASS();
#endif #endif
#endif
} }

View file

@ -13,7 +13,8 @@ class ChevyVoltCharger : public CanCharger {
public: public:
ChevyVoltCharger() : CanCharger(ChargerType::ChevyVolt) {} 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 map_can_frame_to_variable(CAN_frame rx_frame);
void transmit_can(unsigned long currentMillis); void transmit_can(unsigned long currentMillis);

View file

@ -7,7 +7,12 @@
#include "src/communication/Transmitter.h" #include "src/communication/Transmitter.h"
#include "src/communication/can/CanReceiver.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_type(ChargerType type);
// Generic base class for all chargers // Generic base class for all chargers
class Charger { class Charger {

View file

@ -13,7 +13,9 @@ class NissanLeafCharger : public CanCharger {
public: public:
NissanLeafCharger() : CanCharger(ChargerType::NissanLeaf) {} 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 map_can_frame_to_variable(CAN_frame rx_frame);
void transmit_can(unsigned long currentMillis); void transmit_can(unsigned long currentMillis);

View file

@ -1,19 +1,43 @@
#include "comm_contactorcontrol.h" #include "comm_contactorcontrol.h"
#include "../../include.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 #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;
// TODO: Ensure valid values at run-time
// Parameters
enum State { DISCONNECTED, START_PRECHARGE, PRECHARGE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED }; enum State { DISCONNECTED, START_PRECHARGE, PRECHARGE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
State contactorStatus = DISCONNECTED; State contactorStatus = DISCONNECTED;
#define ON 1 const int ON = 1;
#define OFF 0 const int OFF = 0;
#ifdef NC_CONTACTORS //Normally closed contactors use inverted logic #ifdef NC_CONTACTORS //Normally closed contactors use inverted logic
#undef ON #undef ON
@ -38,7 +62,6 @@ unsigned long prechargeStartTime = 0;
unsigned long negativeStartTime = 0; unsigned long negativeStartTime = 0;
unsigned long prechargeCompletedTime = 0; unsigned long prechargeCompletedTime = 0;
unsigned long timeSpentInFaultedMode = 0; unsigned long timeSpentInFaultedMode = 0;
#endif
unsigned long currentTime = 0; unsigned long currentTime = 0;
unsigned long lastPowerRemovalTime = 0; unsigned long lastPowerRemovalTime = 0;
unsigned long bmsPowerOnTime = 0; unsigned long bmsPowerOnTime = 0;
@ -47,12 +70,12 @@ const unsigned long powerRemovalDuration = 30000; // 30 seconds i
const unsigned long bmsWarmupDuration = 3000; const unsigned long bmsWarmupDuration = 3000;
void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) { void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) {
#ifdef PWM_CONTACTOR_CONTROL if (pwm_contactor_control) {
if (pwm_freq != 0xFFFF) { if (pwm_freq != 0xFFFF) {
ledcWrite(pin, pwm_freq); ledcWrite(pin, pwm_freq);
return; return;
}
} }
#endif
if (direction == 1) { if (direction == 1) {
digitalWrite(pin, HIGH); digitalWrite(pin, HIGH);
} else { // 0 } else { // 0
@ -64,23 +87,24 @@ void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) {
void init_contactors() { void init_contactors() {
// Init contactor pins // Init contactor pins
#ifdef CONTACTOR_CONTROL if (contactor_control_enabled) {
#ifdef PWM_CONTACTOR_CONTROL if (pwm_contactor_control) {
// Setup PWM Channel Frequency and Resolution // Setup PWM Channel Frequency and Resolution
ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Positive_Channel); ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Positive_Channel);
ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Negative_Channel); ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Negative_Channel);
// Set all pins OFF (0% PWM) // Set all pins OFF (0% PWM)
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY); ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY); ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
#else //Normal CONTACTOR_CONTROL } else {
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT); pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
set(POSITIVE_CONTACTOR_PIN, OFF); set(POSITIVE_CONTACTOR_PIN, OFF);
pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT); pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT);
set(NEGATIVE_CONTACTOR_PIN, OFF); set(NEGATIVE_CONTACTOR_PIN, OFF);
#endif // Precharge never has PWM regardless of setting } // Precharge never has PWM regardless of setting
pinMode(PRECHARGE_PIN, OUTPUT); pinMode(PRECHARGE_PIN, OUTPUT);
set(PRECHARGE_PIN, OFF); set(PRECHARGE_PIN, OFF);
#endif // CONTACTOR_CONTROL }
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
pinMode(SECOND_POSITIVE_CONTACTOR_PIN, OUTPUT); pinMode(SECOND_POSITIVE_CONTACTOR_PIN, OUTPUT);
set(SECOND_POSITIVE_CONTACTOR_PIN, OFF); set(SECOND_POSITIVE_CONTACTOR_PIN, OFF);
@ -94,16 +118,16 @@ void init_contactors() {
#ifdef BMS_2_POWER //Hardware supports 2x BMS #ifdef BMS_2_POWER //Hardware supports 2x BMS
pinMode(BMS_2_POWER, OUTPUT); pinMode(BMS_2_POWER, OUTPUT);
digitalWrite(BMS_2_POWER, HIGH); digitalWrite(BMS_2_POWER, HIGH);
#endif //BMS_2_POWER
#endif // HW with dedicated BMS pins
#if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET) // User has enabled BMS reset, turn on output on start
pinMode(BMS_POWER, OUTPUT);
digitalWrite(BMS_POWER, HIGH);
#ifdef BMS_2_POWER //Hardware supports 2x BMS
pinMode(BMS_2_POWER, OUTPUT);
digitalWrite(BMS_2_POWER, HIGH);
#endif //BMS_2_POWER #endif //BMS_2_POWER
#endif //PERIODIC_BMS_RESET #endif // HW with dedicated BMS pins
if (periodic_bms_reset || remote_bms_reset) {
pinMode(BMS_POWER, OUTPUT);
digitalWrite(BMS_POWER, HIGH);
#ifdef BMS_2_POWER //Hardware supports 2x BMS
pinMode(BMS_2_POWER, OUTPUT);
digitalWrite(BMS_2_POWER, HIGH);
#endif //BMS_2_POWER
}
} }
static void dbg_contactors(const char* state) { static void dbg_contactors(const char* state) {
@ -117,6 +141,7 @@ 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 // 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() { void handle_contactors() {
// TODO: This must be determined at run-time!
#if defined(SMA_BYD_H_CAN) || defined(SMA_BYD_HVS_CAN) || defined(SMA_TRIPOWER_CAN) #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); datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN);
#endif #endif
@ -127,100 +152,102 @@ void handle_contactors() {
handle_contactors_battery2(); handle_contactors_battery2();
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY #endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
#ifdef CONTACTOR_CONTROL if (contactor_control_enabled) {
// First check if we have any active errors, incase we do, turn off the battery // First check if we have any active errors, incase we do, turn off the battery
if (datalayer.battery.status.bms_status == FAULT) { if (datalayer.battery.status.bms_status == FAULT) {
timeSpentInFaultedMode++; timeSpentInFaultedMode++;
} else { } else {
timeSpentInFaultedMode = 0; timeSpentInFaultedMode = 0;
} }
//handle contactor control SHUTDOWN_REQUESTED //handle contactor control SHUTDOWN_REQUESTED
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) { if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) {
contactorStatus = SHUTDOWN_REQUESTED; contactorStatus = SHUTDOWN_REQUESTED;
} }
if (contactorStatus == SHUTDOWN_REQUESTED) { if (contactorStatus == SHUTDOWN_REQUESTED) {
set(PRECHARGE_PIN, OFF); set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set_event(EVENT_ERROR_OPEN_CONTACTOR, 0); set_event(EVENT_ERROR_OPEN_CONTACTOR, 0);
datalayer.system.status.contactors_engaged = false; 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) 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 // After that, check if we are OK to start turning on the battery
if (contactorStatus == DISCONNECTED) { if (contactorStatus == DISCONNECTED) {
set(PRECHARGE_PIN, OFF); set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY); set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
datalayer.system.status.contactors_engaged = false; datalayer.system.status.contactors_engaged = false;
if (datalayer.system.status.battery_allows_contactor_closing && if (datalayer.system.status.battery_allows_contactor_closing &&
datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.settings.equipment_stop_active) { datalayer.system.status.inverter_allows_contactor_closing &&
contactorStatus = START_PRECHARGE; !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 #ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
@ -235,7 +262,7 @@ void handle_contactors_battery2() {
datalayer.system.status.contactors_battery2_engaged = false; 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. /* 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. REMOTE_BMS_RESET - Allows the user to remotely powercycle the BMS by sending a command to the emulator via MQTT.
@ -245,57 +272,57 @@ During that time we also set the emulator state to paused in order to not try an
Feature is only used if user has enabled PERIODIC_BMS_RESET in the USER_SETTINGS */ Feature is only used if user has enabled PERIODIC_BMS_RESET in the USER_SETTINGS */
void handle_BMSpower() { void handle_BMSpower() {
#if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET) if (periodic_bms_reset || remote_bms_reset) {
// Get current time // Get current time
currentTime = millis(); currentTime = millis();
#ifdef PERIODIC_BMS_RESET if (periodic_bms_reset) {
// Check if 24 hours have passed since the last power removal // Check if 24 hours have passed since the last power removal
if ((currentTime + bmsResetTimeOffset) - lastPowerRemovalTime >= powerRemovalInterval) { if ((currentTime + bmsResetTimeOffset) - lastPowerRemovalTime >= powerRemovalInterval) {
start_bms_reset(); start_bms_reset();
} }
#endif //PERIODIC_BMS_RESET }
// If power has been removed for 30 seconds, restore the power // If power has been removed for 30 seconds, restore the power
if (datalayer.system.status.BMS_reset_in_progress && currentTime - lastPowerRemovalTime >= powerRemovalDuration) { if (datalayer.system.status.BMS_reset_in_progress && currentTime - lastPowerRemovalTime >= powerRemovalDuration) {
// Reapply power to the BMS // Reapply power to the BMS
digitalWrite(BMS_POWER, HIGH); digitalWrite(BMS_POWER, HIGH);
#ifdef BMS_2_POWER #ifdef BMS_2_POWER
digitalWrite(BMS_2_POWER, HIGH); // Same for battery 2 digitalWrite(BMS_2_POWER, HIGH); // Same for battery 2
#endif #endif
bmsPowerOnTime = currentTime; bmsPowerOnTime = currentTime;
datalayer.system.status.BMS_reset_in_progress = false; // Reset the power removal flag 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 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 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) { 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 datalayer.system.status.BMS_startup_in_progress = false; // Reset the BMS warmup removal flag
set_event(EVENT_PERIODIC_BMS_RESET, 0); set_event(EVENT_PERIODIC_BMS_RESET, 0);
}
} }
#endif //defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET)
} }
void start_bms_reset() { void start_bms_reset() {
#if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET) if (periodic_bms_reset || remote_bms_reset) {
if (!datalayer.system.status.BMS_reset_in_progress) { if (!datalayer.system.status.BMS_reset_in_progress) {
lastPowerRemovalTime = currentTime; // Record the time when BMS reset was started 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 // we are now resetting at the correct time. We don't need to offset anymore
bmsResetTimeOffset = 0; bmsResetTimeOffset = 0;
// Set a flag to let the rest of the system know we are cutting power to the BMS. // 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 // 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; datalayer.system.status.BMS_reset_in_progress = true;
// Set emulator state to paused (Max Charge/Discharge = 0 & CAN = stop) // 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. // We try to keep contactors engaged during this pause, and just ramp power down to 0.
setBatteryPause(true, false, false, false); setBatteryPause(true, false, false, false);
digitalWrite(BMS_POWER, LOW); // Remove power by setting the BMS power pin to LOW digitalWrite(BMS_POWER, LOW); // Remove power by setting the BMS power pin to LOW
#ifdef BMS_2_POWER #ifdef BMS_2_POWER
digitalWrite(BMS_2_POWER, LOW); // Same for battery 2 digitalWrite(BMS_2_POWER, LOW); // Same for battery 2
#endif #endif
}
} }
#endif //defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET)
} }

View file

@ -6,6 +6,12 @@
#include "../../datalayer/datalayer.h" #include "../../datalayer/datalayer.h"
#include "../../devboard/utils/events.h" #include "../../devboard/utils/events.h"
// Settings that can be changed at run-time
extern bool contactor_control_enabled;
extern bool pwm_contactor_control;
extern bool periodic_bms_reset;
extern bool remote_bms_reset;
/** /**
* @brief Handle BMS power output * @brief Handle BMS power output
* *

View file

@ -1,15 +1,20 @@
#include "comm_nvm.h" #include "comm_nvm.h"
#include "../../include.h" #include "../../include.h"
#include "../contactorcontrol/comm_contactorcontrol.h"
// Parameters // Parameters
Preferences settings; // Store user settings Preferences settings; // Store user settings
// Initialization functions // Initialization functions
static void begin() {
settings.begin("batterySettings", false);
}
void init_stored_settings() { void init_stored_settings() {
static uint32_t temp = 0; static uint32_t temp = 0;
// ATTENTION ! The maximum length for settings keys is 15 characters // ATTENTION ! The maximum length for settings keys is 15 characters
settings.begin("batterySettings", false); begin();
// Always get the equipment stop status // Always get the equipment stop status
datalayer.system.settings.equipment_stop_active = settings.getBool("EQUIPMENT_STOP", false); datalayer.system.settings.equipment_stop_active = settings.getBool("EQUIPMENT_STOP", false);
@ -69,6 +74,18 @@ void init_stored_settings() {
datalayer.battery.settings.max_user_set_discharge_voltage_dV = temp; datalayer.battery.settings.max_user_set_discharge_voltage_dV = temp;
} }
datalayer.battery.settings.user_set_voltage_limits_active = settings.getBool("USEVOLTLIMITS", false); 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);
pwm_contactor_control = settings.getBool("PWMCNTCTRL", false);
periodic_bms_reset = settings.getBool("PERBMSRESET", false);
remote_bms_reset = settings.getBool("REMBMSRESET", false);
#endif
settings.end(); settings.end();
} }
@ -121,5 +138,26 @@ void store_settings() {
if (!settings.putUInt("TARGETDISCHVOLT", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) { if (!settings.putUInt("TARGETDISCHVOLT", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 11); set_event(EVENT_PERSISTENT_SAVE_INFO, 11);
} }
settings.end(); // Close preferences handle settings.end(); // Close preferences handle
} }
void store_uint(const char* key, uint32_t value) {
begin();
settings.putUInt(key, value);
}
void store_bool(const char* key, bool value) {
begin();
settings.putBool(key, value);
}
uint32_t get_uint(const char* key, uint32_t defaultValue) {
begin();
return settings.getUInt(key, defaultValue);
}
bool get_bool(const char* key) {
begin();
return settings.getBool(key, false);
}

View file

@ -34,4 +34,24 @@ void store_settings_equipment_stop();
*/ */
void store_settings(); void store_settings();
class BatteryEmulatorSettingsStore {
public:
BatteryEmulatorSettingsStore() { settings.begin("batterySettings", false); }
~BatteryEmulatorSettingsStore() { settings.end(); }
BatteryType get_batterytype() { return (BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None); }
void set_batterytype(BatteryType type) { settings.putUInt("BATTTYPE", (int)type); }
InverterProtocolType get_invertertype() {
return (InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None);
}
void set_invertertype(InverterProtocolType type) { settings.putUInt("INVTYPE", (int)type); }
private:
Preferences settings;
};
#endif #endif

View file

@ -320,17 +320,14 @@ typedef struct {
bool BMS_reset_in_progress = false; bool BMS_reset_in_progress = false;
/** True if the BMS is starting up */ /** True if the BMS is starting up */
bool BMS_startup_in_progress = false; bool BMS_startup_in_progress = false;
#ifdef PRECHARGE_CONTROL
/** State of automatic precharge sequence */ /** State of automatic precharge sequence */
PrechargeState precharge_status = AUTO_PRECHARGE_IDLE; PrechargeState precharge_status = AUTO_PRECHARGE_IDLE;
#endif
} DATALAYER_SYSTEM_STATUS_TYPE; } DATALAYER_SYSTEM_STATUS_TYPE;
typedef struct { typedef struct {
bool equipment_stop_active = false; bool equipment_stop_active = false;
#ifdef PRECHARGE_CONTROL
bool start_precharging = false; bool start_precharging = false;
#endif
} DATALAYER_SYSTEM_SETTINGS_TYPE; } DATALAYER_SYSTEM_SETTINGS_TYPE;
typedef struct { typedef struct {

View file

@ -156,7 +156,7 @@ void update_machineryprotection() {
clear_event(EVENT_SOH_LOW); clear_event(EVENT_SOH_LOW);
} }
if (!battery->soc_plausible()) { if (battery && !battery->soc_plausible()) {
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc); set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc);
} }

View file

@ -1,9 +1,77 @@
#include "settings_html.h" #include "settings_html.h"
#include <Arduino.h> #include <Arduino.h>
#include "../../../src/communication/contactorcontrol/comm_contactorcontrol.h"
#include "../../charger/CHARGERS.h" #include "../../charger/CHARGERS.h"
#include "../../communication/nvm/comm_nvm.h"
#include "../../datalayer/datalayer.h" #include "../../datalayer/datalayer.h"
#include "../../include.h" #include "../../include.h"
extern bool settingsUpdated;
#ifdef COMMON_IMAGE
String battery_options(BatteryType selected) {
String options;
auto batteries = supported_battery_types();
for (BatteryType type : batteries) {
auto name = name_for_type(type);
if (name != nullptr) {
options +=
("<option value=\"" + String(static_cast<int>(type)) + "\"" + (selected == type ? " selected" : "") + ">");
options += name;
options += "</option>";
}
}
return options;
}
String inverter_options(InverterProtocolType selected) {
String options;
auto inverters = supported_inverter_protocols();
for (InverterProtocolType type : inverters) {
auto name = name_for_type(type);
if (name != nullptr) {
options +=
("<option value=\"" + String(static_cast<int>(type)) + "\"" + (selected == type ? "selected" : "") + ">");
options += name;
options += "</option>";
}
}
return options;
}
String charger_options(ChargerType selected) {
String options;
auto chargers = supported_charger_types();
for (ChargerType type : chargers) {
auto name = name_for_type(type);
if (name != nullptr) {
options +=
("<option value=\"" + String(static_cast<int>(type)) + "\"" + (selected == type ? "selected" : "") + ">");
options += name;
options += "</option>";
}
}
return options;
}
#endif
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) { String settings_processor(const String& var) {
if (var == "X") { if (var == "X") {
String content = ""; String content = "";
@ -27,11 +95,59 @@ String settings_processor(const String& var) {
"<h4 style='color: white;'>Password: ######## <span id='Password'></span> <button " "<h4 style='color: white;'>Password: ######## <span id='Password'></span> <button "
"onclick='editPassword()'>Edit</button></h4>"; "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 += 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 += battery_options(settings.get_batterytype());
content += "</select>";
content += "<label>Inverter protocol: </label><select style='max-width: 250px;' name='inverter'>";
content += inverter_options(settings.get_invertertype());
content += "</select>";
content += "<label>Charger: </label><select style='max-width: 250px;' name='charger'>";
content += charger_options((ChargerType)("CHGTYPE", 0));
content += "</select>";
//content += "<label>Double battery:</label>";
// TODO: Generalize settings: define settings in one place and use the definitions to render
// UI and handle load/save
render_checkbox(content, "Double battery", settings.get_doublebattery(), "dblbtr");
render_checkbox(content, "Contactor control", get_bool("CNTCTRL"), "contctrl");
render_checkbox(content, "PWM contactor control", get_bool("PWMCNTCTRL"), "pwmcontctrl");
render_checkbox(content, "Periodic BMS reset", get_bool("PERBMSRESET"), "PERBMSRESET");
render_checkbox(content, "Remote BMS reset", get_bool("REMBMSRESET"), "REMBMSRESET");
/* content +=
"<div style=\"display: flex; justify-content: flex-start;\"><input id='dblbtr' name='dblbtr' type='checkbox' "
"style=\"margin-left: 0;\"";
content += (user_selected_second_battery ? " checked" : "");
content += " value='on'/></div>";*/
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) { 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>"; "</span></h4>";
} }
@ -89,14 +205,14 @@ String settings_processor(const String& var) {
// Close the block // Close the block
content += "</div>"; 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 += "<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) + content += "<h4 style='color: white;'>Fake battery voltage: " + String(battery->get_voltage(), 1) +
" V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>"; " V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>";
content += "</div>"; content += "</div>";
} }
if (battery->supports_manual_balancing()) { if (battery && battery->supports_manual_balancing()) {
// Start a new block with grey background color // Start a new block with grey background color
content += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>"; content += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
@ -281,7 +397,7 @@ String settings_processor(const String& var) {
"BalMaxDevCellV?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value " "BalMaxDevCellV?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
"between 300 and 600');}}}"; "between 300 and 600');}}}";
if (battery->supports_set_fake_voltage()) { if (battery && battery->supports_set_fake_voltage()) {
content += content +=
"function editFakeBatteryVoltage(){var value=prompt('Enter new fake battery " "function editFakeBatteryVoltage(){var value=prompt('Enter new fake battery "
"voltage');if(value!==null){if(value>=0&&value<=5000){var xhr=new " "voltage');if(value!==null){if(value>=0&&value<=5000){var xhr=new "

View file

@ -4,6 +4,7 @@
#include "../../../USER_SECRETS.h" #include "../../../USER_SECRETS.h"
#include "../../battery/BATTERIES.h" #include "../../battery/BATTERIES.h"
#include "../../battery/Battery.h" #include "../../battery/Battery.h"
#include "../../communication/nvm/comm_nvm.h"
#include "../../datalayer/datalayer.h" #include "../../datalayer/datalayer.h"
#include "../../datalayer/datalayer_extended.h" #include "../../datalayer/datalayer_extended.h"
#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h" #include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h"
@ -38,6 +39,9 @@ const char get_firmware_info_html[] = R"rawliteral(%X%)rawliteral";
String importedLogs = ""; // Store the uploaded logfile contents in RAM String importedLogs = ""; // Store the uploaded logfile contents in RAM
bool isReplayRunning = false; // Global flag to track replay state bool isReplayRunning = false; // Global flag to track replay state
// True when user has updated settings and a reboot is needed.
bool settingsUpdated = false;
CAN_frame currentFrame = {.FD = true, .ext_ID = false, .DLC = 64, .ID = 0x12F, .data = {0}}; 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, void handleFileUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len,
@ -382,6 +386,41 @@ void init_webserver() {
request->send(200, "text/html", response); request->send(200, "text/html", response);
}); });
#ifdef COMMON_IMAGE
// Handles the form POST from UI to save certain settings: battery/inverter type and double battery on/off
server.on("/saveSettings", HTTP_POST, [](AsyncWebServerRequest* request) {
int params = request->params();
// dblbtr not present in form content if not checked.
bool secondBattery = false;
for (int i = 0; i < params; i++) {
auto p = request->getParam(i);
if (p->name() == "inverter") {
auto type = static_cast<InverterProtocolType>(atoi(p->value().c_str()));
store_uint("INVTYPE", (int)type);
} else if (p->name() == "battery") {
auto type = static_cast<BatteryType>(atoi(p->value().c_str()));
store_uint("BATTTYPE", (int)type);
} else if (p->name() == "charger") {
auto type = static_cast<ChargerType>(atoi(p->value().c_str()));
store_uint("CHGTYPE", (int)type);
} else if (p->name() == "dblbtr") {
store_bool("DBLBTR", p->value() == "on");
} else if (p->name() == "contctrl") {
store_bool("CNTCTRL", p->value() == "on");
} else if (p->name() == "pwmcontctrl") {
store_bool("PWMCNTCTRL", p->value() == "on");
} else if (p->name() == "PERBMSRESET") {
store_bool("PERBMSRESET", p->value() == "on");
} else if (p->name() == "REMBMSRESET") {
store_bool("REMBMSRESET", p->value() == "on");
}
}
settingsUpdated = true;
request->redirect("/settings");
});
#endif
// Route for editing SSID // Route for editing SSID
server.on("/updateSSID", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateSSID", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
@ -945,20 +984,25 @@ String processor(const String& var) {
content += "<div style='background-color: #333; padding: 10px; margin-bottom: 10px; border-radius: 50px'>"; content += "<div style='background-color: #333; padding: 10px; margin-bottom: 10px; border-radius: 50px'>";
// Display which components are used // Display which components are used
content += "<h4 style='color: white;'>Inverter protocol: "; if (inverter) {
content += datalayer.system.info.inverter_protocol; content += "<h4 style='color: white;'>Inverter protocol: ";
content += " "; content += datalayer.system.info.inverter_protocol;
content += datalayer.system.info.inverter_brand; content += " ";
content += "</h4>"; content += datalayer.system.info.inverter_brand;
content += "<h4 style='color: white;'>Battery protocol: "; content += "</h4>";
content += datalayer.system.info.battery_protocol;
if (battery2) {
content += " (Double battery)";
} }
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
content += " (LFP)"; if (battery) {
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>";
} }
content += "</h4>";
if (shunt) { if (shunt) {
content += "<h4 style='color: white;'>Shunt protocol: "; content += "<h4 style='color: white;'>Shunt protocol: ";
@ -1106,7 +1150,7 @@ String processor(const String& var) {
} }
content += "</h4>"; content += "</h4>";
if (battery->supports_real_BMS_status()) { if (battery && battery->supports_real_BMS_status()) {
content += "<h4>Battery BMS status: "; content += "<h4>Battery BMS status: ";
switch (datalayer.battery.status.real_bms_status) { switch (datalayer.battery.status.real_bms_status) {
case BMS_ACTIVE: case BMS_ACTIVE:

View file

@ -171,6 +171,6 @@ void BydCanInverter::send_initial_data() {
} }
void BydCanInverter::setup(void) { // Performs one time setup at startup over CAN bus void BydCanInverter::setup(void) { // Performs one time setup at startup over CAN bus
strncpy(datalayer.system.info.inverter_protocol, "BYD Battery-Box Premium HVS over CAN Bus", 63); strncpy(datalayer.system.info.inverter_protocol, Name, 63);
datalayer.system.info.inverter_protocol[63] = '\0'; datalayer.system.info.inverter_protocol[63] = '\0';
} }

View file

@ -14,6 +14,7 @@ class BydCanInverter : public CanInverterProtocol {
void transmit_can(unsigned long currentMillis); void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame); void map_can_frame_to_variable(CAN_frame rx_frame);
void update_values(); void update_values();
static constexpr char* Name = "BYD Battery-Box Premium HVS over CAN Bus";
private: private:
void send_initial_data(); void send_initial_data();

View file

@ -146,7 +146,7 @@ void BydModbusInverter::verify_inverter_modbus() {
} }
void BydModbusInverter::setup(void) { // Performs one time setup at startup over CAN bus void BydModbusInverter::setup(void) { // Performs one time setup at startup over CAN bus
strncpy(datalayer.system.info.inverter_protocol, "BYD 11kWh HVM battery over Modbus RTU", 63); strncpy(datalayer.system.info.inverter_protocol, Name, 63);
datalayer.system.info.inverter_protocol[63] = '\0'; datalayer.system.info.inverter_protocol[63] = '\0';
// Init Static data to the RTU Modbus // Init Static data to the RTU Modbus

View file

@ -12,6 +12,7 @@ class BydModbusInverter : public ModbusInverterProtocol {
public: public:
void setup(); void setup();
void update_values(); void update_values();
static constexpr char* Name = "BYD 11kWh HVM battery over Modbus RTU";
private: private:
void handle_static_data(); void handle_static_data();

View file

@ -367,6 +367,6 @@ void FerroampCanInverter::send_system_data() { //System equipment information
} }
void FerroampCanInverter::setup(void) { // Performs one time setup at startup over CAN bus void FerroampCanInverter::setup(void) { // Performs one time setup at startup over CAN bus
strncpy(datalayer.system.info.inverter_protocol, "Ferroamp Pylon battery over CAN bus", 63); strncpy(datalayer.system.info.inverter_protocol, Name, 63);
datalayer.system.info.inverter_protocol[63] = '\0'; datalayer.system.info.inverter_protocol[63] = '\0';
} }

View file

@ -15,6 +15,8 @@ class FerroampCanInverter : public CanInverterProtocol {
void transmit_can(unsigned long currentMillis); void transmit_can(unsigned long currentMillis);
void map_can_frame_to_variable(CAN_frame rx_frame); void map_can_frame_to_variable(CAN_frame rx_frame);
static constexpr char* Name = "Ferroamp Pylon battery over CAN bus";
private: private:
void send_system_data(); void send_system_data();
void send_setup_info(); void send_setup_info();

View file

@ -2,6 +2,65 @@
InverterProtocol* inverter = nullptr; InverterProtocol* inverter = nullptr;
InverterProtocolType user_selected_inverter_protocol = InverterProtocolType::BydModbus;
std::vector<InverterProtocolType> supported_inverter_protocols() {
std::vector<InverterProtocolType> types;
for (int i = 0; i < (int)InverterProtocolType::Highest; i++) {
types.push_back((InverterProtocolType)i);
}
return types;
}
extern const char* name_for_type(InverterProtocolType type) {
switch (type) {
case InverterProtocolType::BydCan:
return BydCanInverter::Name;
break;
case InverterProtocolType::BydModbus:
return BydModbusInverter::Name;
break;
case InverterProtocolType::FerroampCan:
return FerroampCanInverter::Name;
break;
case InverterProtocolType::None:
return "None";
break;
}
return nullptr;
}
#ifdef COMMON_IMAGE
#ifdef SELECTED_INVERTER_CLASS
#error "Compile time SELECTED_INVERTER_CLASS should not be defined with COMMON_IMAGE"
#endif
void setup_inverter() {
if (inverter) {
return;
}
switch (user_selected_inverter_protocol) {
case InverterProtocolType::BydCan:
inverter = new BydCanInverter();
break;
case InverterProtocolType::BydModbus:
inverter = new BydModbusInverter();
break;
case InverterProtocolType::FerroampCan:
inverter = new FerroampCanInverter();
break;
}
if (inverter) {
inverter->setup();
}
}
#else
void setup_inverter() { void setup_inverter() {
if (inverter) { if (inverter) {
// The inverter is setup only once. // The inverter is setup only once.
@ -16,3 +75,4 @@ void setup_inverter() {
} }
#endif #endif
} }
#endif

View file

@ -1,6 +1,13 @@
#ifndef INVERTER_PROTOCOL_H #ifndef INVERTER_PROTOCOL_H
#define INVERTER_PROTOCOL_H #define INVERTER_PROTOCOL_H
enum class InverterProtocolType { None = 0, BydCan, BydModbus, FerroampCan, Highest };
extern InverterProtocolType user_selected_inverter_protocol;
extern std::vector<InverterProtocolType> supported_inverter_protocols();
extern const char* name_for_type(InverterProtocolType type);
enum class InverterInterfaceType { Can, Rs485, Modbus }; enum class InverterInterfaceType { Can, Rs485, Modbus };
// The abstract base class for all inverter protocols // The abstract base class for all inverter protocols
@ -14,4 +21,6 @@ class InverterProtocol {
virtual void update_values() = 0; virtual void update_values() = 0;
}; };
extern InverterProtocol* inverter;
#endif #endif