Merge branch 'main' into feature/ioniq-28kWh

This commit is contained in:
Daniel Öster 2025-07-01 14:34:44 +03:00
commit e817741097
142 changed files with 2147 additions and 1337 deletions

View file

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

View file

@ -2,6 +2,7 @@
#define __USER_SETTINGS_H__
#include <WiFi.h>
#include <stdint.h>
#include "src/devboard/utils/types.h"
/* This file contains all the battery/inverter protocol settings Battery-Emulator software */
/* To switch between batteries/inverters, uncomment a line to enable, comment out to disable. */
@ -139,7 +140,7 @@
#define HA_AUTODISCOVERY // Enable this line to send Home Assistant autodiscovery messages. If not enabled manual configuration of Home Assitant is required
/* Battery settings */
// Predefined total energy capacity of the battery in Watt-hours
// Predefined total energy capacity of the battery in Watt-hours (updates automatically from battery data when available)
#define BATTERY_WH_MAX 30000
// Increases battery life. If true will rescale SOC between the configured min/max-percentage
#define BATTERY_USE_SCALED_SOC true
@ -174,7 +175,6 @@
/* Do not change any code below this line */
/* Only change battery specific settings above and in "USER_SETTINGS.cpp" */
typedef enum { CAN_NATIVE = 0, CANFD_NATIVE = 1, CAN_ADDON_MCP2515 = 2, CANFD_ADDON_MCP2518 = 3 } CAN_Interface;
typedef struct {
CAN_Interface battery;
CAN_Interface inverter;
@ -182,7 +182,6 @@ typedef struct {
CAN_Interface charger;
CAN_Interface shunt;
} CAN_Configuration;
extern const char* getCANInterfaceName(CAN_Interface interface);
extern volatile CAN_Configuration can_config;
extern volatile uint8_t AccessPointEnabled;
extern const uint8_t wifi_channel;

View file

@ -4,16 +4,233 @@
#include "CanBattery.h"
#include "RS485Battery.h"
// These functions adapt the old C-style global functions battery-API to the
// object-oriented battery API.
// The instantiated class is defined by the pre-compiler define
// to support battery class selection at compile-time
#ifdef SELECTED_BATTERY_CLASS
#if !defined(COMMON_IMAGE) && !defined(SELECTED_BATTERY_CLASS)
#error No battery selected! Choose one from the USER_SETTINGS.h file
#endif
Battery* battery = nullptr;
Battery* battery2 = nullptr;
std::vector<BatteryType> supported_battery_types() {
std::vector<BatteryType> types;
for (int i = 0; i < (int)BatteryType::Highest; i++) {
types.push_back((BatteryType)i);
}
return types;
}
extern const char* name_for_battery_type(BatteryType type) {
switch (type) {
case BatteryType::None:
return "None";
case BatteryType::BmwI3:
return BmwI3Battery::Name;
case BatteryType::BmwIx:
return BmwIXBattery::Name;
case BatteryType::BoltAmpera:
return BoltAmperaBattery::Name;
case BatteryType::BydAtto3:
return BydAttoBattery::Name;
case BatteryType::CellPowerBms:
return CellPowerBms::Name;
#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms
case BatteryType::Chademo:
return ChademoBattery::Name;
#endif
case BatteryType::CmfaEv:
return CmfaEvBattery::Name;
case BatteryType::Foxess:
return FoxessBattery::Name;
case BatteryType::GeelyGeometryC:
return GeelyGeometryCBattery::Name;
case BatteryType::OrionBms:
return OrionBms::Name;
case BatteryType::Sono:
return SonoBattery::Name;
case BatteryType::StellantisEcmp:
return EcmpBattery::Name;
case BatteryType::ImievCZeroIon:
return ImievCZeroIonBattery::Name;
case BatteryType::JaguarIpace:
return JaguarIpaceBattery::Name;
case BatteryType::KiaEGmp:
return KiaEGmpBattery::Name;
case BatteryType::KiaHyundai64:
return KiaHyundai64Battery::Name;
case BatteryType::KiaHyundaiHybrid:
return KiaHyundaiHybridBattery::Name;
case BatteryType::Meb:
return MebBattery::Name;
case BatteryType::Mg5:
return Mg5Battery::Name;
case BatteryType::NissanLeaf:
return NissanLeafBattery::Name;
case BatteryType::Pylon:
return PylonBattery::Name;
case BatteryType::DalyBms:
return DalyBms::Name;
case BatteryType::RjxzsBms:
return RjxzsBms::Name;
case BatteryType::RangeRoverPhev:
return RangeRoverPhevBattery::Name;
case BatteryType::RenaultKangoo:
return RenaultKangooBattery::Name;
case BatteryType::RenaultTwizy:
return RenaultTwizyBattery::Name;
case BatteryType::RenaultZoe1:
return RenaultZoeGen1Battery::Name;
case BatteryType::RenaultZoe2:
return RenaultZoeGen2Battery::Name;
case BatteryType::SantaFePhev:
return SantaFePhevBattery::Name;
case BatteryType::SimpBms:
return SimpBmsBattery::Name;
case BatteryType::TeslaModel3Y:
return TeslaModel3YBattery::Name;
case BatteryType::TeslaModelSX:
return TeslaModelSXBattery::Name;
case BatteryType::TestFake:
return TestFakeBattery::Name;
case BatteryType::VolvoSpa:
return VolvoSpaBattery::Name;
case BatteryType::VolvoSpaHybrid:
return VolvoSpaHybridBattery::Name;
default:
return nullptr;
}
}
#ifdef COMMON_IMAGE
#ifdef SELECTED_BATTERY_CLASS
#error "Compile time SELECTED_BATTERY_CLASS should not be defined with COMMON_IMAGE"
#endif
BatteryType user_selected_battery_type = BatteryType::NissanLeaf;
bool user_selected_second_battery = false;
Battery* create_battery(BatteryType type) {
switch (type) {
case BatteryType::None:
return nullptr;
case BatteryType::BmwI3:
return new BmwI3Battery();
case BatteryType::BmwIx:
return new BmwIXBattery();
case BatteryType::BoltAmpera:
return new BoltAmperaBattery();
case BatteryType::BydAtto3:
return new BydAttoBattery();
case BatteryType::CellPowerBms:
return new CellPowerBms();
#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms
case BatteryType::Chademo:
return new ChademoBattery();
#endif
case BatteryType::CmfaEv:
return new CmfaEvBattery();
case BatteryType::Foxess:
return new FoxessBattery();
case BatteryType::GeelyGeometryC:
return new GeelyGeometryCBattery();
case BatteryType::OrionBms:
return new OrionBms();
case BatteryType::Sono:
return new SonoBattery();
case BatteryType::StellantisEcmp:
return new EcmpBattery();
case BatteryType::ImievCZeroIon:
return new ImievCZeroIonBattery();
case BatteryType::JaguarIpace:
return new JaguarIpaceBattery();
case BatteryType::KiaEGmp:
return new KiaEGmpBattery();
case BatteryType::KiaHyundai64:
return new KiaHyundai64Battery();
case BatteryType::KiaHyundaiHybrid:
return new KiaHyundaiHybridBattery();
case BatteryType::Meb:
return new MebBattery();
case BatteryType::Mg5:
return new Mg5Battery();
case BatteryType::NissanLeaf:
return new NissanLeafBattery();
case BatteryType::Pylon:
return new PylonBattery();
case BatteryType::DalyBms:
return new DalyBms();
case BatteryType::RjxzsBms:
return new RjxzsBms();
case BatteryType::RangeRoverPhev:
return new RangeRoverPhevBattery();
case BatteryType::RenaultKangoo:
return new RenaultKangooBattery();
case BatteryType::RenaultTwizy:
return new RenaultTwizyBattery();
case BatteryType::RenaultZoe1:
return new RenaultZoeGen1Battery();
case BatteryType::RenaultZoe2:
return new RenaultZoeGen2Battery();
case BatteryType::SantaFePhev:
return new SantaFePhevBattery();
case BatteryType::SimpBms:
return new SimpBmsBattery();
case BatteryType::TeslaModel3Y:
return new TeslaModel3YBattery();
case BatteryType::TeslaModelSX:
return new TeslaModelSXBattery();
case BatteryType::TestFake:
return new TestFakeBattery();
case BatteryType::VolvoSpa:
return new VolvoSpaBattery();
case BatteryType::VolvoSpaHybrid:
return new VolvoSpaHybridBattery();
default:
return nullptr;
}
}
void setup_battery() {
if (battery) {
// Let's not create the battery again.
return;
}
battery = create_battery(user_selected_battery_type);
if (battery) {
battery->setup();
}
if (user_selected_second_battery && !battery2) {
switch (user_selected_battery_type) {
case BatteryType::NissanLeaf:
battery2 = new NissanLeafBattery(&datalayer.battery2, nullptr, can_config.battery_double);
break;
case BatteryType::BmwI3:
battery2 = new BmwI3Battery(&datalayer.battery2, &datalayer.system.status.battery2_allowed_contactor_closing,
can_config.battery_double, WUP_PIN2);
break;
case BatteryType::KiaHyundai64:
battery2 = new KiaHyundai64Battery(&datalayer.battery2, &datalayer_extended.KiaHyundai64_2,
&datalayer.system.status.battery2_allowed_contactor_closing,
can_config.battery_double);
case BatteryType::SantaFePhev:
battery2 = new SantaFePhevBattery(&datalayer.battery2, can_config.battery_double);
break;
case BatteryType::TestFake:
battery2 = new TestFakeBattery(&datalayer.battery2, can_config.battery_double);
break;
}
if (battery2) {
battery2->setup();
}
}
}
#else // Battery selection has been made at build-time
void setup_battery() {
// Instantiate the battery only once just in case this function gets called multiple times.
if (battery == nullptr) {
@ -40,5 +257,4 @@ void setup_battery() {
battery2->setup();
#endif
}
#endif

View file

@ -11,155 +11,46 @@ extern Battery* battery2;
void setup_can_shunt();
#ifdef BMW_SBOX
#include "BMW-SBOX.h"
#endif
#ifdef BMW_I3_BATTERY
#include "BMW-I3-BATTERY.h"
#endif
#ifdef BMW_IX_BATTERY
#include "BMW-IX-BATTERY.h"
#endif
#ifdef BMW_PHEV_BATTERY
#include "BMW-PHEV-BATTERY.h"
#endif
#ifdef BOLT_AMPERA_BATTERY
#include "BMW-SBOX.h"
#include "BOLT-AMPERA-BATTERY.h"
#endif
#ifdef BYD_ATTO_3_BATTERY
#include "BYD-ATTO-3-BATTERY.h"
#endif
#ifdef CELLPOWER_BMS
#include "CELLPOWER-BMS.h"
#endif
#ifdef CHADEMO_BATTERY
#include "CHADEMO-BATTERY.h"
#include "CHADEMO-SHUNTS.h"
#endif
#ifdef CMFA_EV_BATTERY
#include "CMFA-EV-BATTERY.h"
#endif
#ifdef FOXESS_BATTERY
#include "FOXESS-BATTERY.h"
#endif
#ifdef GEELY_GEOMETRY_C_BATTERY
#include "GEELY-GEOMETRY-C-BATTERY.h"
#endif
#ifdef HYUNDAI_IONIQ_28_BATTERY
#include "HYUNDAI-IONIQ-28-BATTERY.h"
#endif
#ifdef ORION_BMS
#include "ORION-BMS.h"
#endif
#ifdef SONO_BATTERY
#include "SONO-BATTERY.h"
#endif
#ifdef STELLANTIS_ECMP_BATTERY
#include "ECMP-BATTERY.h"
#endif
#ifdef IMIEV_CZERO_ION_BATTERY
#include "IMIEV-CZERO-ION-BATTERY.h"
#endif
#ifdef JAGUAR_IPACE_BATTERY
#include "JAGUAR-IPACE-BATTERY.h"
#endif
#ifdef KIA_E_GMP_BATTERY
#include "KIA-E-GMP-BATTERY.h"
#endif
#ifdef KIA_HYUNDAI_64_BATTERY
#include "KIA-HYUNDAI-64-BATTERY.h"
#endif
#ifdef KIA_HYUNDAI_HYBRID_BATTERY
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
#endif
#ifdef MEB_BATTERY
#include "MEB-BATTERY.h"
#endif
#ifdef MG_5_BATTERY
#include "MG-5-BATTERY.h"
#endif
#ifdef NISSAN_LEAF_BATTERY
#include "NISSAN-LEAF-BATTERY.h"
#endif
#ifdef PYLON_BATTERY
#include "PYLON-BATTERY.h"
#endif
#ifdef DALY_BMS
#include "DALY-BMS.h"
#endif
#ifdef RJXZS_BMS
#include "RJXZS-BMS.h"
#endif
#ifdef RANGE_ROVER_PHEV_BATTERY
#include "ECMP-BATTERY.h"
#include "FOXESS-BATTERY.h"
#include "GEELY-GEOMETRY-C-BATTERY.h"
#include "HYUNDAI-IONIQ-28-BATTERY.h"
#include "IMIEV-CZERO-ION-BATTERY.h"
#include "JAGUAR-IPACE-BATTERY.h"
#include "KIA-E-GMP-BATTERY.h"
#include "KIA-HYUNDAI-64-BATTERY.h"
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
#include "MEB-BATTERY.h"
#include "MG-5-BATTERY.h"
#include "NISSAN-LEAF-BATTERY.h"
#include "ORION-BMS.h"
#include "PYLON-BATTERY.h"
#include "RANGE-ROVER-PHEV-BATTERY.h"
#endif
#ifdef RENAULT_KANGOO_BATTERY
#include "RENAULT-KANGOO-BATTERY.h"
#endif
#ifdef RENAULT_TWIZY_BATTERY
#include "RENAULT-TWIZY.h"
#endif
#ifdef RENAULT_ZOE_GEN1_BATTERY
#include "RENAULT-ZOE-GEN1-BATTERY.h"
#endif
#ifdef RENAULT_ZOE_GEN2_BATTERY
#include "RENAULT-ZOE-GEN2-BATTERY.h"
#endif
#ifdef SANTA_FE_PHEV_BATTERY
#include "RJXZS-BMS.h"
#include "SANTA-FE-PHEV-BATTERY.h"
#endif
#ifdef SIMPBMS_BATTERY
#include "SIMPBMS-BATTERY.h"
#endif
#if defined(TESLA_MODEL_SX_BATTERY) || defined(TESLA_MODEL_3Y_BATTERY)
#define TESLA_BATTERY
#include "SONO-BATTERY.h"
#include "TESLA-BATTERY.h"
#endif
#ifdef TEST_FAKE_BATTERY
#include "TEST-FAKE-BATTERY.h"
#endif
#ifdef VOLVO_SPA_BATTERY
#include "VOLVO-SPA-BATTERY.h"
#endif
#ifdef VOLVO_SPA_HYBRID_BATTERY
#include "VOLVO-SPA-HYBRID-BATTERY.h"
#endif
void setup_battery(void);

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef BMW_I3_BATTERY
#include "BMW-I3-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "BMW-I3-BATTERY.h"
#include "../include.h"
/* Do not change code below unless you are sure what you are doing */
@ -34,7 +33,7 @@ static uint8_t calculateCRC(CAN_frame rx_frame, uint8_t length, uint8_t initial_
return crc;
}
static uint8_t increment_alive_counter(uint8_t counter) {
uint8_t BmwI3Battery::increment_alive_counter(uint8_t counter) {
counter++;
if (counter > ALIVE_MAX_VALUE) {
counter = 0;
@ -509,7 +508,7 @@ void BmwI3Battery::transmit_can(unsigned long currentMillis) {
}
void BmwI3Battery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "BMW i3", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
//Before we have started up and detected which battery is in use, use 60AH values
@ -525,5 +524,3 @@ void BmwI3Battery::setup(void) { // Performs one time setup at startup
pinMode(wakeup_pin, OUTPUT);
digitalWrite(wakeup_pin, HIGH); // Wake up the battery
}
#endif

View file

@ -7,8 +7,9 @@
#include "BMW-I3-HTML.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef BMW_I3_BATTERY
#define SELECTED_BATTERY_CLASS BmwI3Battery
#endif
class BmwI3Battery : public CanBattery {
public:
@ -37,6 +38,7 @@ class BmwI3Battery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "BMW i3";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
@ -78,7 +80,9 @@ class BmwI3Battery : public CanBattery {
unsigned long previousMillis5000 = 0; // will store last time a 5000ms CAN Message was send
unsigned long previousMillis10000 = 0; // will store last time a 10000ms CAN Message was send
#define ALIVE_MAX_VALUE 14 // BMW CAN messages contain alive counter, goes from 0...14
static const int ALIVE_MAX_VALUE = 14; // BMW CAN messages contain alive counter, goes from 0...14
uint8_t increment_alive_counter(uint8_t counter);
enum BatterySize { BATTERY_60AH, BATTERY_94AH, BATTERY_120AH };
BatterySize detectedBattery = BATTERY_60AH;

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef BMW_IX_BATTERY
#include "BMW-IX-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "BMW-IX-BATTERY.h"
#include "../include.h"
// Function to check if a value has gone stale over a specified time period
bool BmwIXBattery::isStale(int16_t currentValue, uint16_t& lastValue, unsigned long& lastChangeTime) {
@ -475,7 +474,7 @@ void BmwIXBattery::transmit_can(unsigned long currentMillis) {
}
void BmwIXBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "BMW iX and i4-7 platform", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
//Reset Battery at bootup
@ -696,5 +695,3 @@ void BmwIXBattery::HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms) {
}
}
}
#endif // BMW_IX_BATTERY

View file

@ -5,8 +5,9 @@
#include "BMW-IX-HTML.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef BMW_IX_BATTERY
#define SELECTED_BATTERY_CLASS BmwIXBattery
#endif
class BmwIXBattery : public CanBattery {
public:
@ -21,6 +22,8 @@ class BmwIXBattery : public CanBattery {
void request_open_contactors() { datalayer_extended.bmwix.UserRequestContactorOpen = true; }
void request_close_contactors() { datalayer_extended.bmwix.UserRequestContactorClose = true; }
static constexpr const char* Name = "BMW iX and i4-7 platform";
private:
BmwIXHtmlRenderer renderer;
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef BMW_PHEV_BATTERY
#include "BMW-PHEV-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "BMW-PHEV-BATTERY.h"
#include "../include.h"
const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
@ -714,5 +713,3 @@ void BmwPhevBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
}
#endif

View file

@ -5,8 +5,9 @@
#include "BMW-PHEV-HTML.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef BMW_PHEV_BATTERY
#define SELECTED_BATTERY_CLASS BmwPhevBattery
#endif
class BmwPhevBattery : public CanBattery {
public:

View file

@ -1,8 +1,7 @@
#include "../include.h"
#ifdef BMW_SBOX
#include "BMW-SBOX.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "BMW-SBOX.h"
#include "../include.h"
uint8_t reverse_bits(uint8_t byte) {
uint8_t reversed = 0;
@ -179,7 +178,6 @@ void BmwSbox::transmit_can(unsigned long currentMillis) {
}
void BmwSbox::setup() {
strncpy(datalayer.system.info.shunt_protocol, "BMW SBOX", 63);
strncpy(datalayer.system.info.shunt_protocol, Name, 63);
datalayer.system.info.shunt_protocol[63] = '\0';
}
#endif

View file

@ -2,8 +2,9 @@
#define BMW_SBOX_CONTROL_H
#include "../include.h"
#define CAN_SHUNT_SELECTED
#ifdef BMW_SBOX
#define SELECTED_SHUNT_CLASS BmwSbox
#endif
#include "Shunt.h"
@ -12,6 +13,7 @@ class BmwSbox : public CanShunt {
void setup();
void transmit_can(unsigned long currentMillis);
void handle_incoming_can_frame(CAN_frame rx_frame);
static constexpr const char* Name = "BMW SBOX";
private:
/** Minimum input voltage required to enable relay control **/

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef BOLT_AMPERA_BATTERY
#include "BOLT-AMPERA-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "BOLT-AMPERA-BATTERY.h"
#include "../include.h"
/*
TODOs left for this implementation
@ -645,7 +644,7 @@ void BoltAmperaBattery::transmit_can(unsigned long currentMillis) {
}
void BoltAmperaBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Chevrolet Bolt EV/Opel Ampera-e", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -655,5 +654,3 @@ void BoltAmperaBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
}
#endif

View file

@ -6,8 +6,9 @@
#include "BOLT-AMPERA-HTML.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef BOLT_AMPERA_BATTERY
#define SELECTED_BATTERY_CLASS BoltAmperaBattery
#endif
class BoltAmperaBattery : public CanBattery {
public:
@ -16,6 +17,8 @@ class BoltAmperaBattery : public CanBattery {
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Chevrolet Bolt EV/Opel Ampera-e";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private:

View file

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

View file

@ -18,18 +18,9 @@
//#define SKIP_TEMPERATURE_SENSOR_NUMBER 1
/* Do not modify the rows below */
#define BATTERY_SELECTED
#ifdef BYD_ATTO_3_BATTERY
#define SELECTED_BATTERY_CLASS BydAttoBattery
#define CELLCOUNT_EXTENDED 126
#define CELLCOUNT_STANDARD 104
#define MAX_PACK_VOLTAGE_EXTENDED_DV 4410 //Extended range
#define MIN_PACK_VOLTAGE_EXTENDED_DV 3800 //Extended range
#define MAX_PACK_VOLTAGE_STANDARD_DV 3640 //Standard range
#define MIN_PACK_VOLTAGE_STANDARD_DV 3136 //Standard range
#define MAX_CELL_DEVIATION_MV 230
#define MAX_CELL_VOLTAGE_MV 3650 //Charging stops if one cell exceeds this value
#define MIN_CELL_VOLTAGE_MV 2800 //Discharging stops if one cell goes below this value
#endif
class BydAttoBattery : public CanBattery {
public:
@ -53,6 +44,8 @@ class BydAttoBattery : public CanBattery {
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "BYD Atto 3";
bool supports_charged_energy() { return true; }
bool supports_reset_crash() { return true; }
@ -68,6 +61,16 @@ class BydAttoBattery : public CanBattery {
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private:
static const int CELLCOUNT_EXTENDED = 126;
static const int CELLCOUNT_STANDARD = 104;
static const int MAX_PACK_VOLTAGE_EXTENDED_DV = 4410; //Extended range
static const int MIN_PACK_VOLTAGE_EXTENDED_DV = 3800; //Extended range
static const int MAX_PACK_VOLTAGE_STANDARD_DV = 3640; //Standard range
static const int MIN_PACK_VOLTAGE_STANDARD_DV = 3136; //Standard range
static const int MAX_CELL_DEVIATION_MV = 230;
static const int MAX_CELL_VOLTAGE_MV = 3650; //Charging stops if one cell exceeds this value
static const int MIN_CELL_VOLTAGE_MV = 2800; //Discharging stops if one cell goes below this value
BydAtto3HtmlRenderer renderer;
DATALAYER_BATTERY_TYPE* datalayer_battery;
DATALAYER_INFO_BYDATTO3* datalayer_bydatto;

View file

@ -0,0 +1,6 @@
#include "Battery.h"
#include "../datalayer/datalayer.h"
float Battery::get_voltage() {
return static_cast<float>(datalayer.battery.status.voltage_dV) / 10.0;
}

View file

@ -1,9 +1,56 @@
#ifndef BATTERY_H
#define BATTERY_H
#include "../datalayer/datalayer.h"
#include <vector>
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
enum class BatteryType {
None = 0,
BmwSbox = 1,
BmwI3 = 2,
BmwIx = 3,
BoltAmpera = 4,
BydAtto3 = 5,
CellPowerBms = 6,
Chademo = 7,
CmfaEv = 8,
Foxess = 9,
GeelyGeometryC = 10,
OrionBms = 11,
Sono = 12,
StellantisEcmp = 13,
ImievCZeroIon = 14,
JaguarIpace = 15,
KiaEGmp = 16,
KiaHyundai64 = 17,
KiaHyundaiHybrid = 18,
Meb = 19,
Mg5 = 20,
NissanLeaf = 21,
Pylon = 22,
DalyBms = 23,
RjxzsBms = 24,
RangeRoverPhev = 25,
RenaultKangoo = 26,
RenaultTwizy = 27,
RenaultZoe1 = 28,
RenaultZoe2 = 29,
SantaFePhev = 30,
SimpBms = 31,
TeslaModel3Y = 32,
TeslaModelSX = 33,
TestFake = 34,
VolvoSpa = 35,
VolvoSpaHybrid = 36,
Highest
};
extern std::vector<BatteryType> supported_battery_types();
extern const char* name_for_battery_type(BatteryType type);
extern BatteryType user_selected_battery_type;
extern bool user_selected_second_battery;
// Abstract base class for next-generation battery implementations.
// Defines the interface to call battery specific functionality.
class Battery {
@ -48,7 +95,7 @@ class Battery {
virtual void set_factory_mode() {}
virtual void set_fake_voltage(float v) {}
virtual float get_voltage() { return static_cast<float>(datalayer.battery.status.voltage_dV) / 10.0; }
virtual float get_voltage();
// This allows for battery specific SOC plausibility calculations to be performed.
virtual bool soc_plausible() { return true; }

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef CELLPOWER_BMS
#include "CELLPOWER-BMS.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "CELLPOWER-BMS.h"
#include "../include.h"
void CellPowerBms::update_values() {
@ -230,7 +229,7 @@ void CellPowerBms::transmit_can(unsigned long currentMillis) {
}
void CellPowerBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Cellpower BMS", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -238,5 +237,3 @@ void CellPowerBms::setup(void) { // Performs one time setup at startup
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
}
#endif // CELLPOWER_BMS

View file

@ -5,8 +5,9 @@
#include "CELLPOWER-HTML.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef CELLPOWER_BMS
#define SELECTED_BATTERY_CLASS CellPowerBms
#endif
class CellPowerBms : public CanBattery {
public:
@ -15,6 +16,8 @@ class CellPowerBms : public CanBattery {
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Cellpower BMS";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private:

View file

@ -1,10 +1,11 @@
#include "../include.h"
#ifdef CHADEMO_BATTERY
#include "CHADEMO-BATTERY.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "CHADEMO-BATTERY.h"
#include "../include.h"
#include "CHADEMO-SHUNTS.h"
#ifdef CHADEMO_PIN_2 // Only support chademo for certain platforms
/* CHADEMO handling runs at 6.25 times the rate of most other code, so, rather than the
* default value of 12 (for 12 iterations of the 5s value update loop) * 5 for a 60s timeout,
* instead use 75 for 75*0.8s = 60s
@ -945,7 +946,7 @@ void ChademoBattery::setup(void) { // Performs one time setup at startup
pinMode(CHADEMO_PIN_4, INPUT);
pinMode(CHADEMO_PIN_7, INPUT);
strncpy(datalayer.system.info.battery_protocol, "Chademo V2X mode", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
CHADEMO_Status = CHADEMO_IDLE;
@ -994,4 +995,5 @@ void ChademoBattery::setup(void) { // Performs one time setup at startup
setupMillis = millis();
}
#endif

View file

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

View file

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

View file

@ -1,6 +1,9 @@
#ifndef CHADEMO_SHUNTS_H
#define CHADEMO_SHUNTS_H
#include <stdint.h>
#include "../devboard/utils/types.h"
uint16_t get_measured_voltage();
uint16_t get_measured_current();
void ISA_handleFrame(CAN_frame* frame);

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef CMFA_EV_BATTERY
#include "CMFA-EV-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "CMFA-EV-BATTERY.h"
#include "../include.h"
/* The raw SOC value sits at 90% when the battery is full, so we should report back 100% once this value is reached
Same goes for low point, when 10% is reached we report 0% */
@ -28,68 +27,68 @@ uint16_t CmfaEvBattery::rescale_raw_SOC(uint32_t raw_SOC) {
void CmfaEvBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
datalayer.battery.status.soh_pptt = (SOH * 100);
datalayer_battery->status.soh_pptt = (SOH * 100);
datalayer.battery.status.real_soc = rescale_raw_SOC(SOC_raw);
datalayer_battery->status.real_soc = rescale_raw_SOC(SOC_raw);
datalayer.battery.status.current_dA = current * 10;
datalayer_battery->status.current_dA = current * 10;
datalayer.battery.status.voltage_dV = average_voltage_of_cells / 100;
datalayer_battery->status.voltage_dV = average_voltage_of_cells / 100;
datalayer.battery.info.total_capacity_Wh = 27000;
datalayer_battery->info.total_capacity_Wh = 27000;
//Calculate the remaining Wh amount from SOC% and max Wh value.
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
datalayer_battery->status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer_battery->status.real_soc) / 10000) * datalayer_battery->info.total_capacity_Wh);
datalayer.battery.status.max_discharge_power_W = discharge_power_w;
datalayer_battery->status.max_discharge_power_W = discharge_power_w;
datalayer.battery.status.max_charge_power_W = charge_power_w;
datalayer_battery->status.max_charge_power_W = charge_power_w;
datalayer.battery.status.temperature_min_dC = (lowest_cell_temperature * 10);
datalayer_battery->status.temperature_min_dC = (lowest_cell_temperature * 10);
datalayer.battery.status.temperature_max_dC = (highest_cell_temperature * 10);
datalayer_battery->status.temperature_max_dC = (highest_cell_temperature * 10);
datalayer.battery.status.cell_min_voltage_mV = lowest_cell_voltage_mv;
datalayer_battery->status.cell_min_voltage_mV = lowest_cell_voltage_mv;
datalayer.battery.status.cell_max_voltage_mV = highest_cell_voltage_mv;
datalayer_battery->status.cell_max_voltage_mV = highest_cell_voltage_mv;
//Map all cell voltages to the global array
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_mv, 72 * sizeof(uint16_t));
memcpy(datalayer_battery->status.cell_voltages_mV, cellvoltages_mv, 72 * sizeof(uint16_t));
if (lead_acid_voltage < 11000) { //11.000V
set_event(EVENT_12V_LOW, lead_acid_voltage);
}
// Update webserver datalayer
datalayer_extended.CMFAEV.soc_u = soc_u;
datalayer_extended.CMFAEV.soc_z = soc_z;
datalayer_extended.CMFAEV.lead_acid_voltage = lead_acid_voltage;
datalayer_extended.CMFAEV.highest_cell_voltage_number = highest_cell_voltage_number;
datalayer_extended.CMFAEV.lowest_cell_voltage_number = lowest_cell_voltage_number;
datalayer_extended.CMFAEV.max_regen_power = max_regen_power;
datalayer_extended.CMFAEV.max_discharge_power = max_discharge_power;
datalayer_extended.CMFAEV.average_temperature = average_temperature;
datalayer_extended.CMFAEV.minimum_temperature = minimum_temperature;
datalayer_extended.CMFAEV.maximum_temperature = maximum_temperature;
datalayer_extended.CMFAEV.maximum_charge_power = maximum_charge_power;
datalayer_extended.CMFAEV.SOH_available_power = SOH_available_power;
datalayer_extended.CMFAEV.SOH_generated_power = SOH_generated_power;
datalayer_extended.CMFAEV.cumulative_energy_when_discharging = cumulative_energy_when_discharging;
datalayer_extended.CMFAEV.cumulative_energy_when_charging = cumulative_energy_when_charging;
datalayer_extended.CMFAEV.cumulative_energy_in_regen = cumulative_energy_in_regen;
datalayer_extended.CMFAEV.soh_average = soh_average;
datalayer_cmfa->soc_u = soc_u;
datalayer_cmfa->soc_z = soc_z;
datalayer_cmfa->lead_acid_voltage = lead_acid_voltage;
datalayer_cmfa->highest_cell_voltage_number = highest_cell_voltage_number;
datalayer_cmfa->lowest_cell_voltage_number = lowest_cell_voltage_number;
datalayer_cmfa->max_regen_power = max_regen_power;
datalayer_cmfa->max_discharge_power = max_discharge_power;
datalayer_cmfa->average_temperature = average_temperature;
datalayer_cmfa->minimum_temperature = minimum_temperature;
datalayer_cmfa->maximum_temperature = maximum_temperature;
datalayer_cmfa->maximum_charge_power = maximum_charge_power;
datalayer_cmfa->SOH_available_power = SOH_available_power;
datalayer_cmfa->SOH_generated_power = SOH_generated_power;
datalayer_cmfa->cumulative_energy_when_discharging = cumulative_energy_when_discharging;
datalayer_cmfa->cumulative_energy_when_charging = cumulative_energy_when_charging;
datalayer_cmfa->cumulative_energy_in_regen = cumulative_energy_in_regen;
datalayer_cmfa->soh_average = soh_average;
}
void CmfaEvBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (rx_frame.ID) { //These frames are transmitted by the battery
case 0x127: //10ms , Same structure as old Zoe 0x155 message!
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
current = (((((rx_frame.data.u8[1] & 0x0F) << 8) | rx_frame.data.u8[2]) * 0.25) - 500);
SOC_raw = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
break;
case 0x3D6: //100ms, Same structure as old Zoe 0x424 message!
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
charge_power_w = rx_frame.data.u8[2] * 500;
discharge_power_w = rx_frame.data.u8[3] * 500;
lowest_cell_temperature = (rx_frame.data.u8[4] - 40);
@ -98,34 +97,34 @@ void CmfaEvBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
highest_cell_temperature = (rx_frame.data.u8[7] - 40);
break;
case 0x3D7: //100ms
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
pack_voltage = ((rx_frame.data.u8[6] << 4 | (rx_frame.data.u8[5] & 0x0F)));
break;
case 0x3D8: //100ms
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
//counter_3D8 = rx_frame.data.u8[3]; //?
//CRC_3D8 = rx_frame.data.u8[4]; //?
break;
case 0x43C: //100ms
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
heartbeat2 = rx_frame.data.u8[2]; //Alternates between 0x55 and 0xAA every 5th frame
break;
case 0x431: //100ms
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
//byte0 9C always
//byte1 40 always
break;
case 0x5A9:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x5AB:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x5C8:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x5E1:
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
datalayer_battery->status.CAN_battery_still_alive = CAN_STILL_ALIVE;
break;
case 0x7BB: // Reply from battery
if (rx_frame.data.u8[0] == 0x10) { //PID header
@ -941,15 +940,13 @@ void CmfaEvBattery::transmit_can(unsigned long currentMillis) {
}
void CmfaEvBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "CMFA platform, 27 kWh battery", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 72;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer_battery->info.number_of_cells = 72;
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
datalayer_battery->info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
}
#endif //CMFA_EV_BATTERY

View file

@ -5,21 +5,46 @@
#include "CMFA-EV-HTML.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef CMFA_EV_BATTERY
#define SELECTED_BATTERY_CLASS CmfaEvBattery
#endif
class CmfaEvBattery : public CanBattery {
public:
// Use this constructor for the second battery.
CmfaEvBattery(DATALAYER_BATTERY_TYPE* datalayer_ptr, DATALAYER_INFO_CMFAEV* extended, CAN_Interface targetCan)
: CanBattery(targetCan) {
datalayer_battery = datalayer_ptr;
allows_contactor_closing = nullptr;
datalayer_cmfa = extended;
average_voltage_of_cells = 0;
}
// Use the default constructor to create the first or single battery.
CmfaEvBattery() {
datalayer_battery = &datalayer.battery;
allows_contactor_closing = &datalayer.system.status.battery_allows_contactor_closing;
datalayer_cmfa = &datalayer_extended.CMFAEV;
}
virtual void setup(void);
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "CMFA platform, 27 kWh battery";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private:
CmfaEvHtmlRenderer renderer;
DATALAYER_BATTERY_TYPE* datalayer_battery;
DATALAYER_INFO_CMFAEV* datalayer_cmfa;
// If not null, this battery decides when the contactor can be closed and writes the value here.
bool* allows_contactor_closing;
uint16_t rescale_raw_SOC(uint32_t raw_SOC);
static const int MAX_PACK_VOLTAGE_DV = 3040; // 5000 = 500.0V

View file

@ -0,0 +1,8 @@
#include "CanBattery.h"
#include "../../src/include.h"
CanBattery::CanBattery() {
can_interface = can_config.battery;
register_transmitter(this);
register_can_receiver(this, can_interface);
}

View file

@ -22,11 +22,7 @@ class CanBattery : public Battery, Transmitter, CanReceiver {
protected:
CAN_Interface can_interface;
CanBattery() {
can_interface = can_config.battery;
register_transmitter(this);
register_can_receiver(this, can_interface);
}
CanBattery();
CanBattery(CAN_Interface interface) {
can_interface = interface;

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef DALY_BMS
#include "DALY-BMS.h"
#include <cstdint>
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "DALY-BMS.h"
#include "../include.h"
/* Do not change code below unless you are sure what you are doing */
@ -61,7 +60,7 @@ void DalyBms::update_values() {
}
void DalyBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "DALY RS485", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = CELL_COUNT;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -207,5 +206,3 @@ void DalyBms::receive() {
}
}
}
#endif

View file

@ -3,20 +3,9 @@
#include "RS485Battery.h"
/* Tweak these according to your battery build */
#define CELL_COUNT 14
#define MAX_PACK_VOLTAGE_DV 580 //580 = 58.0V
#define MIN_PACK_VOLTAGE_DV 460 //480 = 48.0V
#define MAX_CELL_VOLTAGE_MV 4200 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 3200 //Battery is put into emergency stop if one cell goes below this value
#define POWER_PER_PERCENT 50 // below 20% and above 80% limit power to 50W * SOC (i.e. 150W at 3%, 500W at 10%, ...)
#define POWER_PER_DEGREE_C 60 // max power added/removed per degree above/below 0°C
#define POWER_AT_0_DEGREE_C 800 // power at 0°C
/* Do not modify any rows below*/
#define BATTERY_SELECTED
#define RS485_BATTERY_SELECTED
#ifdef DALY_BMS
#define SELECTED_BATTERY_CLASS DalyBms
#endif
class DalyBms : public RS485Battery {
public:
@ -24,8 +13,20 @@ class DalyBms : public RS485Battery {
void update_values();
void transmit_rs485(unsigned long currentMillis);
void receive();
static constexpr const char* Name = "DALY RS485";
private:
/* Tweak these according to your battery build */
static const int CELL_COUNT = 14;
static const int MAX_PACK_VOLTAGE_DV = 580; //580 = 58.0V
static const int MIN_PACK_VOLTAGE_DV = 460; //480 = 48.0V
static const int MAX_CELL_VOLTAGE_MV = 4200; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 3200; //Battery is put into emergency stop if one cell goes below this value
static const int POWER_PER_PERCENT =
50; // below 20% and above 80% limit power to 50W * SOC (i.e. 150W at 3%, 500W at 10%, ...)
static const int POWER_PER_DEGREE_C = 60; // max power added/removed per degree above/below 0°C
static const int POWER_AT_0_DEGREE_C = 800; // power at 0°C
int baud_rate() { return 9600; }
};

View file

@ -1,15 +1,13 @@
#include "../include.h"
#ifdef STELLANTIS_ECMP_BATTERY
#include "ECMP-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For More Battery Info page
#include "../devboard/utils/events.h"
#include "ECMP-BATTERY.h"
#include "../include.h"
/* TODO:
This integration is still ongoing. Here is what still needs to be done in order to use this battery type
- Disable the isolation resistance requirement that opens contactors after 30s
- Battery says it might need 37E and 485, but no logs of this?
- Disable the isolation resistance requirement that opens contactors after 30s under load. Factory mode?
*/
/* Do not change code below unless you are sure what you are doing */
@ -21,7 +19,7 @@ void EcmpBattery::update_values() {
datalayer.battery.status.voltage_dV = battery_voltage * 10;
datalayer.battery.status.current_dA = battery_current * 10;
datalayer.battery.status.current_dA = -(battery_current * 10);
datalayer.battery.status.active_power_W = //Power in watts, Negative = charging batt
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
@ -375,19 +373,31 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
if (datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring) {
if ((rx_frame.data.u8[0] == 0x06) && (rx_frame.data.u8[1] == 0x50) && (rx_frame.data.u8[2] == 0x03)) {
//06,50,03,00,C8,00,14,00,
DisableIsoMonitoringStatemachine = 2; //Send ECMP_FACTORY_MODE_ACTIVATION next loop
DisableIsoMonitoringStatemachine = 2; //Send ECMP_ACK_MESSAGE (02 3e 00)
}
//if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F) && (rx_frame.data.u8[2] == 0x2E)) {
if (DisableIsoMonitoringStatemachine == 3) {
DisableIsoMonitoringStatemachine = 4;
if ((rx_frame.data.u8[0] == 0x02) && (rx_frame.data.u8[1] == 0x7E) && (rx_frame.data.u8[2] == 0x00)) {
//Expected 02,7E,00
DisableIsoMonitoringStatemachine = 4; //Send ECMP_FACTORY_MODE_ACTIVATION next loop
}
//if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F) && (rx_frame.data.u8[2] == 0x2E)) {
if (DisableIsoMonitoringStatemachine == 5) {
DisableIsoMonitoringStatemachine = 6;
if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x6E) && (rx_frame.data.u8[2] == 0xD9)) {
//Factory mode ENTRY: 2E.D9.00.01
DisableIsoMonitoringStatemachine = 6; //Send ECMP_DISABLE_ISOLATION_REQ next loop
}
//if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F) && (rx_frame.data.u8[2] == 0x31)) {
if (DisableIsoMonitoringStatemachine == 7) {
//UNKNOWN? 03,7F,31,24 (or 7F?)
if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F) && (rx_frame.data.u8[2] == 0x2E)) {
//Factory mode fails to enter with 7F
set_event(EVENT_PID_FAILED, rx_frame.data.u8[2]);
DisableIsoMonitoringStatemachine =
6; //Send ECMP_DISABLE_ISOLATION_REQ next loop (pointless, since it will fail)
}
if ((rx_frame.data.u8[0] == 0x04) && (rx_frame.data.u8[1] == 0x31) && (rx_frame.data.u8[2] == 0x02)) {
//Disable isolation successful 04 31 02 df e1
DisableIsoMonitoringStatemachine = COMPLETED_STATE;
datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring = false;
timeSpentDisableIsoMonitoring = COMPLETED_STATE;
}
if ((rx_frame.data.u8[0] == 0x03) && (rx_frame.data.u8[1] == 0x7F) && (rx_frame.data.u8[2] == 0x31)) {
//Disable Isolation fails to enter with 7F
set_event(EVENT_PID_FAILED, rx_frame.data.u8[2]);
DisableIsoMonitoringStatemachine = COMPLETED_STATE;
datalayer_extended.stellantisECMP.UserRequestDisableIsoMonitoring = false;
timeSpentDisableIsoMonitoring = COMPLETED_STATE;
@ -535,10 +545,10 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
pid_avg_cell_voltage = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
break;
case PID_CURRENT:
pid_current = (((rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16) | (rx_frame.data.u8[6] << 8) |
rx_frame.data.u8[7]) -
76800) *
10;
pid_current = -(((rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16) | (rx_frame.data.u8[6] << 8) |
rx_frame.data.u8[7]) -
76800) *
20;
break;
case PID_INSULATION_NEG:
pid_insulation_res_neg = ((rx_frame.data.u8[4] << 24) | (rx_frame.data.u8[5] << 16) |
@ -579,7 +589,7 @@ void EcmpBattery::handle_incoming_can_frame(CAN_frame rx_frame) {
pid_lowest_cell_voltage_num = (rx_frame.data.u8[4]);
break;
case PID_SUM_OF_CELLS:
pid_sum_of_cells = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
pid_sum_of_cells = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 2;
break;
case PID_CELL_MIN_CAPACITY:
pid_cell_min_capacity = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
@ -846,15 +856,15 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
DisableIsoMonitoringStatemachine = 1;
}
if (DisableIsoMonitoringStatemachine == 2) {
transmit_can_frame(&ECMP_FACTORY_MODE_ACTIVATION_NEW, can_config.battery);
transmit_can_frame(&ECMP_ACK_MESSAGE, can_config.battery);
DisableIsoMonitoringStatemachine = 3;
}
if (DisableIsoMonitoringStatemachine == 4) {
transmit_can_frame(&ECMP_DISABLE_ISOLATION_REQ, can_config.battery);
transmit_can_frame(&ECMP_FACTORY_MODE_ACTIVATION, can_config.battery);
DisableIsoMonitoringStatemachine = 5;
}
if (DisableIsoMonitoringStatemachine == 6) {
transmit_can_frame(&ECMP_FACTORY_MODE_ACTIVATION, can_config.battery);
transmit_can_frame(&ECMP_DISABLE_ISOLATION_REQ, can_config.battery);
DisableIsoMonitoringStatemachine = 7;
}
timeSpentDisableIsoMonitoring++;
@ -1322,9 +1332,11 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
transmit_can_frame(&ECMP_0C5, can_config.battery); //DC2_0C5
transmit_can_frame(&ECMP_17B, can_config.battery); //VCU_PCANInfo_17B
transmit_can_frame(&ECMP_0F2, can_config.battery); //CtrlMCU1_0F2
transmit_can_frame(&ECMP_111, can_config.battery);
transmit_can_frame(&ECMP_110, can_config.battery);
transmit_can_frame(&ECMP_114, can_config.battery);
if (simulateEntireCar) {
transmit_can_frame(&ECMP_111, can_config.battery);
transmit_can_frame(&ECMP_110, can_config.battery);
transmit_can_frame(&ECMP_114, can_config.battery);
}
}
// Send 20ms periodic CAN Message simulating the car still being attached
@ -1356,7 +1368,7 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
ECMP_27A.data = {0x4F, 0x58, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00};
}
transmit_can_frame(&ECMP_230, can_config.battery); //OBC3_230
transmit_can_frame(&ECMP_27A, can_config.battery); //PSA specific VCU message (VCU_BSI_Wakeup_27A)
transmit_can_frame(&ECMP_27A, can_config.battery); //VCU_BSI_Wakeup_27A
}
// Send 100ms periodic CAN Message simulating the car still being attached
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
@ -1464,20 +1476,23 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
transmit_can_frame(&ECMP_345, can_config.battery); //DC1_345
transmit_can_frame(&ECMP_3A2, can_config.battery); //OBC2_3A2
transmit_can_frame(&ECMP_3A3, can_config.battery); //OBC1_3A3
transmit_can_frame(&ECMP_31E, can_config.battery);
transmit_can_frame(&ECMP_383, can_config.battery);
transmit_can_frame(&ECMP_010, can_config.battery);
transmit_can_frame(&ECMP_0A6, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_37F, can_config.battery); //Seems to be temperatures of some sort
transmit_can_frame(&ECMP_372, can_config.battery);
transmit_can_frame(&ECMP_351, can_config.battery);
transmit_can_frame(&ECMP_31D, can_config.battery);
transmit_can_frame(&ECMP_010, can_config.battery); //VCU_BCM_Crash
if (simulateEntireCar) {
transmit_can_frame(&ECMP_31E, can_config.battery);
transmit_can_frame(&ECMP_383, can_config.battery);
transmit_can_frame(&ECMP_0A6, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_37F, can_config.battery); //Seems to be temperatures of some sort
transmit_can_frame(&ECMP_372, can_config.battery);
transmit_can_frame(&ECMP_351, can_config.battery);
transmit_can_frame(&ECMP_31D, can_config.battery);
}
}
// Send 500ms periodic CAN Message simulating the car still being attached
if (currentMillis - previousMillis500 >= INTERVAL_500_MS) {
previousMillis500 = currentMillis;
transmit_can_frame(&ECMP_0AE, can_config.battery);
if (simulateEntireCar) {
transmit_can_frame(&ECMP_0AE, can_config.battery);
}
}
// Send 1s CAN Message
if (currentMillis - previousMillis1000 >= INTERVAL_1_S) {
@ -1500,24 +1515,27 @@ void EcmpBattery::transmit_can(unsigned long currentMillis) {
ECMP_552.data.u8[2] = ((ticks_552 & 0x0000FF00) >> 8);
ECMP_552.data.u8[3] = (ticks_552 & 0x000000FF);
transmit_can_frame(&ECMP_439, can_config.battery); //PSA Specific? Not in all logs
transmit_can_frame(&ECMP_486, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_041, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_786, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_591, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_439, can_config.battery); //OBC4
transmit_can_frame(&ECMP_552, can_config.battery); //VCU_552 timetracking
transmit_can_frame(&ECMP_794, can_config.battery); //Not in all logs
if (simulateEntireCar) {
transmit_can_frame(&ECMP_486, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_041, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_786, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_591, can_config.battery); //Not in all logs
transmit_can_frame(&ECMP_794, can_config.battery); //Not in all logs
}
}
// Send 5s periodic CAN Message simulating the car still being attached
if (currentMillis - previousMillis5000 >= INTERVAL_5_S) {
previousMillis5000 = currentMillis;
transmit_can_frame(&ECMP_55F, can_config.battery);
if (simulateEntireCar) {
transmit_can_frame(&ECMP_55F, can_config.battery);
}
}
}
void EcmpBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Stellantis ECMP battery", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 108;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
@ -1527,5 +1545,3 @@ void EcmpBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
datalayer.system.status.battery_allows_contactor_closing = true;
}
#endif

View file

@ -6,8 +6,9 @@
#include "CanBattery.h"
#include "ECMP-HTML.h"
#define BATTERY_SELECTED
#ifdef STELLANTIS_ECMP_BATTERY
#define SELECTED_BATTERY_CLASS EcmpBattery
#endif
class EcmpBattery : public CanBattery {
public:
@ -15,6 +16,7 @@ class EcmpBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Stellantis ECMP battery";
bool supports_clear_isolation() { return true; }
void clear_isolation() { datalayer_extended.stellantisECMP.UserRequestIsolationReset = true; }
@ -37,8 +39,10 @@ class EcmpBattery : public CanBattery {
static const int MAX_CELL_DEVIATION_MV = 100;
static const int MAX_CELL_VOLTAGE_MV = 4250;
static const int MIN_CELL_VOLTAGE_MV = 2700;
#define NOT_SAMPLED_YET 255
#define COMPLETED_STATE 0
bool simulateEntireCar =
false; //Set this to true to simulate the whole car (useful for when using external diagnostic tools)
static const int NOT_SAMPLED_YET = 255;
static const int COMPLETED_STATE = 0;
bool battery_RelayOpenRequest = false;
bool battery_InterlockOpen = false;
uint8_t ContactorResetStatemachine = 0;
@ -142,82 +146,82 @@ class EcmpBattery : public CanBattery {
unsigned long previousMillis500 = 0; // will store last time a 500ms CAN Message was sent
unsigned long previousMillis1000 = 0; // will store last time a 1000ms CAN Message was sent
unsigned long previousMillis5000 = 0; // will store last time a 1000ms CAN Message was sent
#define PID_WELD_CHECK 0xD814
#define PID_CONT_REASON_OPEN 0xD812
#define PID_CONTACTOR_STATUS 0xD813
#define PID_NEG_CONT_CONTROL 0xD44F
#define PID_NEG_CONT_STATUS 0xD453
#define PID_POS_CONT_CONTROL 0xD44E
#define PID_POS_CONT_STATUS 0xD452
#define PID_CONTACTOR_NEGATIVE 0xD44C
#define PID_CONTACTOR_POSITIVE 0xD44D
#define PID_PRECHARGE_RELAY_CONTROL 0xD44B
#define PID_PRECHARGE_RELAY_STATUS 0xD451
#define PID_RECHARGE_STATUS 0xD864
#define PID_DELTA_TEMPERATURE 0xD878
#define PID_COLDEST_MODULE 0xD446
#define PID_LOWEST_TEMPERATURE 0xD87D
#define PID_AVERAGE_TEMPERATURE 0xD877
#define PID_HIGHEST_TEMPERATURE 0xD817
#define PID_HOTTEST_MODULE 0xD445
#define PID_AVG_CELL_VOLTAGE 0xD43D
#define PID_CURRENT 0xD816
#define PID_INSULATION_NEG 0xD87C
#define PID_INSULATION_POS 0xD87B
#define PID_MAX_CURRENT_10S 0xD876
#define PID_MAX_DISCHARGE_10S 0xD873
#define PID_MAX_DISCHARGE_30S 0xD874
#define PID_MAX_CHARGE_10S 0xD871
#define PID_MAX_CHARGE_30S 0xD872
#define PID_ENERGY_CAPACITY 0xD860
#define PID_HIGH_CELL_NUM 0xD43B
#define PID_LOW_CELL_NUM 0xD43C
#define PID_SUM_OF_CELLS 0xD438
#define PID_CELL_MIN_CAPACITY 0xD413
#define PID_CELL_VOLTAGE_MEAS_STATUS 0xD48A
#define PID_INSULATION_RES 0xD47A
#define PID_PACK_VOLTAGE 0xD815
#define PID_HIGH_CELL_VOLTAGE 0xD870
#define PID_ALL_CELL_VOLTAGES 0xD440 //Multi-frame
#define PID_LOW_CELL_VOLTAGE 0xD86F
#define PID_BATTERY_ENERGY 0xD865
#define PID_BATTERY_ENERGY 0xD865
#define PID_CELLBALANCE_STATUS 0xD46F //Multi-frame?
#define PID_CELLBALANCE_HWERR_MASK 0xD470 //Multi-frame
#define PID_CRASH_COUNTER 0xD42F
#define PID_WIRE_CRASH 0xD87F
#define PID_CAN_CRASH 0xD48D
#define PID_HISTORY_DATA 0xD465
#define PID_LOWSOC_COUNTER 0xD492 //Not supported on all batteris
#define PID_LAST_CAN_FAILURE_DETAIL 0xD89E //Not supported on all batteris
#define PID_HW_VERSION_NUM 0xF193 //Not supported on all batteris
#define PID_SW_VERSION_NUM 0xF195 //Not supported on all batteris
#define PID_FACTORY_MODE_CONTROL 0xD900
#define PID_BATTERY_SERIAL 0xD901
#define PID_ALL_CELL_SOH 0xD4B5 //Very long message reply, too much data for this integration
#define PID_AUX_FUSE_STATE 0xD86C
#define PID_BATTERY_STATE 0xD811
#define PID_PRECHARGE_SHORT_CIRCUIT 0xD4D8
#define PID_ESERVICE_PLUG_STATE 0xD86A
#define PID_MAINFUSE_STATE 0xD86B
#define PID_MOST_CRITICAL_FAULT 0xD481
#define PID_CURRENT_TIME 0xD47F
#define PID_TIME_SENT_BY_CAR 0xD4CA
#define PID_12V 0xD822
#define PID_12V_ABNORMAL 0xD42B
#define PID_HVIL_IN_VOLTAGE 0xD46B
#define PID_HVIL_OUT_VOLTAGE 0xD46A
#define PID_HVIL_STATE 0xD869
#define PID_BMS_STATE 0xD45A
#define PID_VEHICLE_SPEED 0xD802
#define PID_TIME_SPENT_OVER_55C 0xE082
#define PID_CONTACTOR_CLOSING_COUNTER 0xD416
#define PID_DATE_OF_MANUFACTURE 0xF18B
static const uint16_t PID_WELD_CHECK = 0xD814;
static const uint16_t PID_CONT_REASON_OPEN = 0xD812;
static const uint16_t PID_CONTACTOR_STATUS = 0xD813;
static const uint16_t PID_NEG_CONT_CONTROL = 0xD44F;
static const uint16_t PID_NEG_CONT_STATUS = 0xD453;
static const uint16_t PID_POS_CONT_CONTROL = 0xD44E;
static const uint16_t PID_POS_CONT_STATUS = 0xD452;
static const uint16_t PID_CONTACTOR_NEGATIVE = 0xD44C;
static const uint16_t PID_CONTACTOR_POSITIVE = 0xD44D;
static const uint16_t PID_PRECHARGE_RELAY_CONTROL = 0xD44B;
static const uint16_t PID_PRECHARGE_RELAY_STATUS = 0xD451;
static const uint16_t PID_RECHARGE_STATUS = 0xD864;
static const uint16_t PID_DELTA_TEMPERATURE = 0xD878;
static const uint16_t PID_COLDEST_MODULE = 0xD446;
static const uint16_t PID_LOWEST_TEMPERATURE = 0xD87D;
static const uint16_t PID_AVERAGE_TEMPERATURE = 0xD877;
static const uint16_t PID_HIGHEST_TEMPERATURE = 0xD817;
static const uint16_t PID_HOTTEST_MODULE = 0xD445;
static const uint16_t PID_AVG_CELL_VOLTAGE = 0xD43D;
static const uint16_t PID_CURRENT = 0xD816;
static const uint16_t PID_INSULATION_NEG = 0xD87C;
static const uint16_t PID_INSULATION_POS = 0xD87B;
static const uint16_t PID_MAX_CURRENT_10S = 0xD876;
static const uint16_t PID_MAX_DISCHARGE_10S = 0xD873;
static const uint16_t PID_MAX_DISCHARGE_30S = 0xD874;
static const uint16_t PID_MAX_CHARGE_10S = 0xD871;
static const uint16_t PID_MAX_CHARGE_30S = 0xD872;
static const uint16_t PID_ENERGY_CAPACITY = 0xD860;
static const uint16_t PID_HIGH_CELL_NUM = 0xD43B;
static const uint16_t PID_LOW_CELL_NUM = 0xD43C;
static const uint16_t PID_SUM_OF_CELLS = 0xD438;
static const uint16_t PID_CELL_MIN_CAPACITY = 0xD413;
static const uint16_t PID_CELL_VOLTAGE_MEAS_STATUS = 0xD48A;
static const uint16_t PID_INSULATION_RES = 0xD47A;
static const uint16_t PID_PACK_VOLTAGE = 0xD815;
static const uint16_t PID_HIGH_CELL_VOLTAGE = 0xD870;
static const uint16_t PID_ALL_CELL_VOLTAGES = 0xD440; //Multi-frame
static const uint16_t PID_LOW_CELL_VOLTAGE = 0xD86F;
static const uint16_t PID_BATTERY_ENERGY = 0xD865;
static const uint16_t PID_CELLBALANCE_STATUS = 0xD46F; //Multi-frame?
static const uint16_t PID_CELLBALANCE_HWERR_MASK = 0xD470; //Multi-frame
static const uint16_t PID_CRASH_COUNTER = 0xD42F;
static const uint16_t PID_WIRE_CRASH = 0xD87F;
static const uint16_t PID_CAN_CRASH = 0xD48D;
static const uint16_t PID_HISTORY_DATA = 0xD465;
static const uint16_t PID_LOWSOC_COUNTER = 0xD492; //Not supported on all batteris
static const uint16_t PID_LAST_CAN_FAILURE_DETAIL = 0xD89E; //Not supported on all batteris
static const uint16_t PID_HW_VERSION_NUM = 0xF193; //Not supported on all batteris
static const uint16_t PID_SW_VERSION_NUM = 0xF195; //Not supported on all batteris
static const uint16_t PID_FACTORY_MODE_CONTROL = 0xD900;
static const uint16_t PID_BATTERY_SERIAL = 0xD901;
static const uint16_t PID_ALL_CELL_SOH = 0xD4B5; //Very long message reply, too much data for this integration
static const uint16_t PID_AUX_FUSE_STATE = 0xD86C;
static const uint16_t PID_BATTERY_STATE = 0xD811;
static const uint16_t PID_PRECHARGE_SHORT_CIRCUIT = 0xD4D8;
static const uint16_t PID_ESERVICE_PLUG_STATE = 0xD86A;
static const uint16_t PID_MAINFUSE_STATE = 0xD86B;
static const uint16_t PID_MOST_CRITICAL_FAULT = 0xD481;
static const uint16_t PID_CURRENT_TIME = 0xD47F;
static const uint16_t PID_TIME_SENT_BY_CAR = 0xD4CA;
static const uint16_t PID_12V = 0xD822;
static const uint16_t PID_12V_ABNORMAL = 0xD42B;
static const uint16_t PID_HVIL_IN_VOLTAGE = 0xD46B;
static const uint16_t PID_HVIL_OUT_VOLTAGE = 0xD46A;
static const uint16_t PID_HVIL_STATE = 0xD869;
static const uint16_t PID_BMS_STATE = 0xD45A;
static const uint16_t PID_VEHICLE_SPEED = 0xD802;
static const uint16_t PID_TIME_SPENT_OVER_55C = 0xE082;
static const uint16_t PID_CONTACTOR_CLOSING_COUNTER = 0xD416;
static const uint16_t PID_DATE_OF_MANUFACTURE = 0xF18B;
uint16_t poll_state = PID_WELD_CHECK;
uint16_t incoming_poll = 0;
CAN_frame ECMP_010 = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x010, .data = {0xB4}};
CAN_frame ECMP_010 = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x010, .data = {0xB4}}; //VCU_BCM_Crash 100ms
CAN_frame ECMP_041 = {.FD = false, .ext_ID = false, .DLC = 1, .ID = 0x041, .data = {0x00}};
CAN_frame ECMP_0A6 = {.FD = false,
.ext_ID = false,
@ -333,7 +337,7 @@ class EcmpBattery : public CanBattery {
.DLC = 8,
.ID = 0x3A3,
.data = {0x4A, 0x4A, 0x40, 0x00, 0x00, 0x08, 0x00, 0x0F}};
CAN_frame ECMP_439 = {.FD = false, //??? 1s periodic (Perfectly emulated in Battery-Emulator)
CAN_frame ECMP_439 = {.FD = false, //OBC4 1s periodic (Perfectly emulated in Battery-Emulator)
.ext_ID = false, //Same content always, fully static
.DLC = 8,
.ID = 0x439,
@ -412,16 +416,12 @@ class EcmpBattery : public CanBattery {
.DLC = 5,
.ID = 0x6B4,
.data = {0x04, 0x2E, 0xD9, 0x00, 0x01}};
CAN_frame ECMP_FACTORY_MODE_ACTIVATION_NEW = {.FD = false,
.ext_ID = false,
.DLC = 4,
.ID = 0x6B4,
.data = {0x04, 0x2E, 0x19, 0x01}};
CAN_frame ECMP_DISABLE_ISOLATION_REQ = {.FD = false,
.ext_ID = false,
.DLC = 5,
.ID = 0x6B4,
.data = {0x04, 0x31, 0x02, 0xDF, 0xE1}};
CAN_frame ECMP_ACK_MESSAGE = {.FD = false, .ext_ID = false, .DLC = 3, .ID = 0x6B4, .data = {0x02, 0x3E, 0x00}};
uint8_t data_010_CRC[8] = {0xB4, 0x96, 0x78, 0x5A, 0x3C, 0x1E, 0xF0, 0xD2};
uint8_t data_3A2_CRC[16] = {0x0C, 0x1B, 0x2A, 0x39, 0x48, 0x57,
0x66, 0x75, 0x84, 0x93, 0xA2, 0xB1}; // NOTE. Changes on BMS state

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef FOXESS_BATTERY
#include "FOXESS-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "FOXESS-BATTERY.h"
#include "../include.h"
/*
Can bus @ 500k - all Extended ID, little endian
@ -573,7 +572,7 @@ void FoxessBattery::transmit_can(unsigned long currentMillis) {
}
void FoxessBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "FoxESS HV2600/ECS4100 OEM battery", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 0; //Startup with no cells, populates later when we know packsize
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -583,5 +582,3 @@ void FoxessBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
}
#endif

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef FOXESS_BATTERY
#define SELECTED_BATTERY_CLASS FoxessBattery
#endif
class FoxessBattery : public CanBattery {
public:
@ -14,6 +15,7 @@ class FoxessBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "FoxESS HV2600/ECS4100 OEM battery";
private:
static const int MAX_PACK_VOLTAGE_DV = 4672; //467.2V for HS20.8 (used during startup, refined later)

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef GEELY_GEOMETRY_C_BATTERY
#include "GEELY-GEOMETRY-C-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "GEELY-GEOMETRY-C-BATTERY.h"
#include "../include.h"
/* TODO
- Contactor closing: CAN log needed from complete H-CAN of Geely Geometry C vehicle. We are not sure what needs to be sent towards the battery yet to get contactor closing working. DTC readout complains that a "Power CAN BUS Data Missing" message is still missing
@ -661,7 +660,7 @@ void GeelyGeometryCBattery::transmit_can(unsigned long currentMillis) {
}
void GeelyGeometryCBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Geely Geometry C", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer_battery->info.number_of_cells = 102; //70kWh pack has 102S, startup in this mode
@ -671,5 +670,3 @@ void GeelyGeometryCBattery::setup(void) { // Performs one time setup at startup
datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
}
#endif

View file

@ -6,29 +6,9 @@
#include "CanBattery.h"
#include "GEELY-GEOMETRY-C-HTML.h"
#define BATTERY_SELECTED
#ifdef GEELY_GEOMETRY_C_BATTERY
#define SELECTED_BATTERY_CLASS GeelyGeometryCBattery
#define POLL_SOC 0x4B35
#define POLL_CC2_VOLTAGE 0x4BCF
#define POLL_CELL_MAX_VOLTAGE_NUMBER 0x4B1E
#define POLL_CELL_MIN_VOLTAGE_NUMBER 0x4B20
#define POLL_AMOUNT_CELLS 0x4B07
#define POLL_SPECIFICIAL_VOLTAGE 0x4B05
#define POLL_UNKNOWN_1 0x4BDA //245 on two batteries
#define POLL_RAW_SOC_MAX 0x4BC3
#define POLL_RAW_SOC_MIN 0x4BC4
#define POLL_UNKNOWN_4 0xDF00 //144 (the other battery 143)
#define POLL_CAPACITY_MODULE_MAX 0x4B3D
#define POLL_CAPACITY_MODULE_MIN 0x4B3E
#define POLL_UNKNOWN_7 0x4B24 //1 (the other battery 23)
#define POLL_UNKNOWN_8 0x4B26 //16 (the other battery 33)
#define POLL_MULTI_TEMPS 0x4B0F
#define POLL_MULTI_UNKNOWN_2 0x4B10
#define POLL_MULTI_UNKNOWN_3 0x4B53
#define POLL_MULTI_UNKNOWN_4 0x4B54
#define POLL_MULTI_HARDWARE_VERSION 0x4B6B
#define POLL_MULTI_SOFTWARE_VERSION 0x4B6C
#endif
class GeelyGeometryCBattery : public CanBattery {
public:
@ -36,10 +16,32 @@ class GeelyGeometryCBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Geely Geometry C";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private:
static const int POLL_SOC = 0x4B35;
static const int POLL_CC2_VOLTAGE = 0x4BCF;
static const int POLL_CELL_MAX_VOLTAGE_NUMBER = 0x4B1E;
static const int POLL_CELL_MIN_VOLTAGE_NUMBER = 0x4B20;
static const int POLL_AMOUNT_CELLS = 0x4B07;
static const int POLL_SPECIFICIAL_VOLTAGE = 0x4B05;
static const int POLL_UNKNOWN_1 = 0x4BDA; //245 on two batteries
static const int POLL_RAW_SOC_MAX = 0x4BC3;
static const int POLL_RAW_SOC_MIN = 0x4BC4;
static const int POLL_UNKNOWN_4 = 0xDF00; //144 (the other battery 143)
static const int POLL_CAPACITY_MODULE_MAX = 0x4B3D;
static const int POLL_CAPACITY_MODULE_MIN = 0x4B3E;
static const int POLL_UNKNOWN_7 = 0x4B24; //1 (the other battery 23)
static const int POLL_UNKNOWN_8 = 0x4B26; //16 (the other battery 33)
static const int POLL_MULTI_TEMPS = 0x4B0F;
static const int POLL_MULTI_UNKNOWN_2 = 0x4B10;
static const int POLL_MULTI_UNKNOWN_3 = 0x4B53;
static const int POLL_MULTI_UNKNOWN_4 = 0x4B54;
static const int POLL_MULTI_HARDWARE_VERSION = 0x4B6B;
static const int POLL_MULTI_SOFTWARE_VERSION = 0x4B6C;
GeelyGeometryCHtmlRenderer renderer;
static const int MAX_PACK_VOLTAGE_70_DV = 4420; //70kWh

View file

@ -368,7 +368,7 @@ void HyundaiIoniq28Battery::transmit_can(unsigned long currentMillis) {
}
void HyundaiIoniq28Battery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Hyundai Ioniq Electric 28kWh", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer_battery->info.total_capacity_Wh = 28000;
datalayer_battery->info.number_of_cells = 96;

View file

@ -34,6 +34,7 @@ class HyundaiIoniq28Battery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Hyundai Ioniq Electric 28kWh";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef IMIEV_CZERO_ION_BATTERY
#include "IMIEV-CZERO-ION-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "IMIEV-CZERO-ION-BATTERY.h"
#include "../include.h"
//Code still work in progress, TODO:
//Figure out if CAN messages need to be sent to keep the system happy?
@ -191,7 +190,7 @@ void ImievCZeroIonBattery::transmit_can(unsigned long currentMillis) {
}
void ImievCZeroIonBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "I-Miev / C-Zero / Ion Triplet", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
@ -200,5 +199,3 @@ void ImievCZeroIonBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
}
#endif

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef IMIEV_CZERO_ION_BATTERY
#define SELECTED_BATTERY_CLASS ImievCZeroIonBattery
#endif
class ImievCZeroIonBattery : public CanBattery {
public:
@ -14,6 +15,7 @@ class ImievCZeroIonBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "I-Miev / C-Zero / Ion Triplet";
private:
static const int MAX_PACK_VOLTAGE_DV = 3696; //5000 = 500.0V

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef JAGUAR_IPACE_BATTERY
#include "JAGUAR-IPACE-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "JAGUAR-IPACE-BATTERY.h"
#include "../include.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillisKeepAlive = 0;
@ -57,7 +56,7 @@ CAN_frame ipace_keep_alive = {.FD = false,
.ID = 0x59e,
.data = {0x9E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};*/
void print_units(char* header, int value, char* units) {
static void print_units(char* header, int value, char* units) {
logging.print(header);
logging.print(value);
logging.print(units);
@ -237,5 +236,3 @@ void JaguarIpaceBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
}
#endif

View file

@ -3,14 +3,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef JAGUAR_IPACE_BATTERY
#define SELECTED_BATTERY_CLASS JaguarIpaceBattery
#define MAX_PACK_VOLTAGE_DV 4546 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3370
#define MAX_CELL_DEVIATION_MV 250
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value
#endif
class JaguarIpaceBattery : public CanBattery {
public:
@ -18,6 +13,14 @@ class JaguarIpaceBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Jaguar I-PACE";
private:
static const int MAX_PACK_VOLTAGE_DV = 4546; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 3370;
static const int MAX_CELL_DEVIATION_MV = 250;
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
};
#endif

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef KIA_E_GMP_BATTERY
#include "KIA-E-GMP-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "KIA-E-GMP-BATTERY.h"
const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
@ -1091,7 +1090,7 @@ void KiaEGmpBattery::transmit_can(unsigned long currentMillis) {
}
void KiaEGmpBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai EGMP platform", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 192; // TODO: will vary depending on battery
@ -1101,5 +1100,3 @@ void KiaEGmpBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
}
#endif

View file

@ -9,8 +9,9 @@ extern ACAN2517FD canfd;
#define ESTIMATE_SOC_FROM_CELLVOLTAGE
#define BATTERY_SELECTED
#ifdef KIA_E_GMP_BATTERY
#define SELECTED_BATTERY_CLASS KiaEGmpBattery
#endif
class KiaEGmpBattery : public CanBattery {
public:
@ -18,6 +19,7 @@ class KiaEGmpBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Kia/Hyundai EGMP platform";
private:
uint16_t estimateSOC(uint16_t packVoltage, uint16_t cellCount, int16_t currentAmps);

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef KIA_HYUNDAI_64_BATTERY
#include "KIA-HYUNDAI-64-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-64-BATTERY.h"
#include "../include.h"
void KiaHyundai64Battery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
@ -464,7 +463,7 @@ void KiaHyundai64Battery::transmit_can(unsigned long currentMillis) {
}
void KiaHyundai64Battery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai 64/40kWh battery", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV; //Start with 98S value. Precised later
datalayer_battery->info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV; //Start with 90S value. Precised later
@ -475,5 +474,3 @@ void KiaHyundai64Battery::setup(void) { // Performs one time setup at startup
*allows_contactor_closing = true;
}
}
#endif

View file

@ -7,8 +7,9 @@
#include "CanBattery.h"
#include "KIA-HYUNDAI-64-HTML.h"
#define BATTERY_SELECTED
#ifdef KIA_HYUNDAI_64_BATTERY
#define SELECTED_BATTERY_CLASS KiaHyundai64Battery
#endif
class KiaHyundai64Battery : public CanBattery {
public:
@ -34,6 +35,7 @@ class KiaHyundai64Battery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Kia/Hyundai 64/40kWh battery";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef KIA_HYUNDAI_HYBRID_BATTERY
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "KIA-HYUNDAI-HYBRID-BATTERY.h"
#include "../include.h"
/* TODO:
- The HEV battery seems to turn off after 1 minute of use. When this happens SOC% stops updating.
@ -215,7 +214,7 @@ void KiaHyundaiHybridBattery::transmit_can(unsigned long currentMillis) {
}
void KiaHyundaiHybridBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Kia/Hyundai Hybrid", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 56; // HEV , TODO: Make dynamic according to HEV/PHEV
@ -224,5 +223,3 @@ void KiaHyundaiHybridBattery::setup(void) { // Performs one time setup at start
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
}
#endif

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef KIA_HYUNDAI_HYBRID_BATTERY
#define SELECTED_BATTERY_CLASS KiaHyundaiHybridBattery
#endif
class KiaHyundaiHybridBattery : public CanBattery {
public:
@ -14,6 +15,7 @@ class KiaHyundaiHybridBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Kia/Hyundai Hybrid";
private:
static const int MAX_PACK_VOLTAGE_DV = 2550; //5000 = 500.0V

View file

@ -1,12 +1,11 @@
#include "../include.h"
#ifdef MEB_BATTERY
#include "MEB-BATTERY.h"
#include <algorithm> // For std::min and std::max
#include "../communication/can/comm_can.h"
#include "../communication/can/obd.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "MEB-BATTERY.h"
#include "../include.h"
/*
TODO list
@ -2036,7 +2035,7 @@ void MebBattery::transmit_can(unsigned long currentMillis) {
}
void MebBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Volkswagen Group MEB platform via CAN-FD", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 108; //Startup in 108S mode. We figure out the actual count later.
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_108S_DV; //Defined later to correct pack size
@ -2045,5 +2044,3 @@ void MebBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
}
#endif

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#include "MEB-HTML.h"
#define BATTERY_SELECTED
#ifdef MEB_BATTERY
#define SELECTED_BATTERY_CLASS MebBattery
#endif
class MebBattery : public CanBattery {
public:
@ -16,6 +17,7 @@ class MebBattery : public CanBattery {
virtual void transmit_can(unsigned long currentMillis);
bool supports_real_BMS_status() { return true; }
bool supports_charged_energy() { return true; }
static constexpr const char* Name = "Volkswagen Group MEB platform via CAN-FD";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef MG_5_BATTERY_H
#include "MG-5-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "MG-5-BATTERY.h"
#include "../include.h"
/* TODO:
- Get contactor closing working
@ -122,5 +121,3 @@ void Mg5Battery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
}
#endif

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef MG_5_BATTERY
#define SELECTED_BATTERY_CLASS Mg5Battery
#endif
class Mg5Battery : public CanBattery {
public:
@ -14,6 +15,7 @@ class Mg5Battery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "MG 5 battery";
private:
static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V

View file

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

View file

@ -7,14 +7,9 @@
#include "CanBattery.h"
#include "NISSAN-LEAF-HTML.h"
#define BATTERY_SELECTED
#ifdef NISSAN_LEAF_BATTERY
#define SELECTED_BATTERY_CLASS NissanLeafBattery
#define MAX_PACK_VOLTAGE_DV 4040 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2600
#define MAX_CELL_DEVIATION_MV 150
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value
#endif
class NissanLeafBattery : public CanBattery {
public:
@ -51,8 +46,15 @@ class NissanLeafBattery : public CanBattery {
}
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
static constexpr const char* Name = "Nissan LEAF battery";
private:
static const int MAX_PACK_VOLTAGE_DV = 4040; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 2600;
static const int MAX_CELL_DEVIATION_MV = 150;
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
NissanLeafHtmlRenderer renderer;
bool is_message_corrupt(CAN_frame rx_frame);

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef ORION_BMS
#include "ORION-BMS.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "ORION-BMS.h"
#include "../include.h"
void findMinMaxCellvoltages(const uint16_t arr[], size_t size, uint16_t& Minimum_Cell_Voltage,
uint16_t& Maximum_Cell_Voltage) {
@ -115,7 +114,7 @@ void OrionBms::transmit_can(unsigned long currentMillis) {
}
void OrionBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "DIY battery with Orion BMS (Victron setting)", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = NUMBER_OF_CELLS;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -124,5 +123,3 @@ void OrionBms::setup(void) { // Performs one time setup at startup
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
}
#endif

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef ORION_BMS
#define SELECTED_BATTERY_CLASS OrionBms
#endif
class OrionBms : public CanBattery {
public:
@ -14,6 +15,7 @@ class OrionBms : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "DIY battery with Orion BMS (Victron setting)";
private:
/* Change the following to suit your battery */

View file

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

View file

@ -6,8 +6,9 @@
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef PYLON_BATTERY
#define SELECTED_BATTERY_CLASS PylonBattery
#endif
class PylonBattery : public CanBattery {
public:
@ -30,6 +31,7 @@ class PylonBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Pylon compatible battery";
private:
/* Change the following to suit your battery */

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef RANGE_ROVER_PHEV_BATTERY
#include "RANGE-ROVER-PHEV-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RANGE-ROVER-PHEV-BATTERY.h"
#include "../include.h"
/* TODO
- LOG files from vehicle needed to determine CAN content needed to send towards battery!
@ -208,7 +207,7 @@ void RangeRoverPhevBattery::transmit_can(unsigned long currentMillis) {
}
void RangeRoverPhevBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Range Rover 13kWh PHEV battery (L494/L405)", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -216,5 +215,3 @@ void RangeRoverPhevBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
}
#endif //RANGE_ROVER_PHEV_BATTERY

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef RANGE_ROVER_PHEV_BATTERY
#define SELECTED_BATTERY_CLASS RangeRoverPhevBattery
#endif
class RangeRoverPhevBattery : public CanBattery {
public:
@ -14,6 +15,7 @@ class RangeRoverPhevBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Range Rover 13kWh PHEV battery (L494/L405)";
private:
/* Change the following to suit your battery */

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef RENAULT_KANGOO_BATTERY
#include "RENAULT-KANGOO-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RENAULT-KANGOO-BATTERY.h"
#include "../include.h"
/* TODO:
There seems to be some values on the Kangoo that differ between the 22/33 kWh version
@ -182,7 +181,7 @@ void RenaultKangooBattery::transmit_can(unsigned long currentMillis) {
}
void RenaultKangooBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Renault Kangoo", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -191,5 +190,3 @@ void RenaultKangooBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
}
#endif

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef RENAULT_KANGOO_BATTERY
#define SELECTED_BATTERY_CLASS RenaultKangooBattery
#endif
class RenaultKangooBattery : public CanBattery {
public:
@ -14,6 +15,7 @@ class RenaultKangooBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Renault Kangoo";
private:
static const int MAX_PACK_VOLTAGE_DV = 4150; //5000 = 500.0V

View file

@ -1,9 +1,8 @@
#include "RENAULT-TWIZY.h"
#include <cstdint>
#include "../include.h"
#ifdef RENAULT_TWIZY_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RENAULT-TWIZY.h"
#include "../include.h"
int16_t max_value(int16_t* entries, size_t len) {
int result = INT16_MIN;
@ -119,7 +118,7 @@ void RenaultTwizyBattery::transmit_can(unsigned long currentMillis) {
}
void RenaultTwizyBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Renault Twizy", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 14;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -129,5 +128,3 @@ void RenaultTwizyBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.total_capacity_Wh = 6600;
datalayer.system.status.battery_allows_contactor_closing = true;
}
#endif

View file

@ -3,8 +3,9 @@
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef RENAULT_TWIZY_BATTERY
#define SELECTED_BATTERY_CLASS RenaultTwizyBattery
#endif
class RenaultTwizyBattery : public CanBattery {
public:
@ -12,6 +13,7 @@ class RenaultTwizyBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Renault Twizy";
private:
static const int MAX_PACK_VOLTAGE_DV = 579; // 57.9V at 100% SOC (with 70% SOH, new one might be higher)

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef RENAULT_ZOE_GEN1_BATTERY
#include "RENAULT-ZOE-GEN1-BATTERY.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h"
#include "../devboard/utils/events.h"
#include "RENAULT-ZOE-GEN1-BATTERY.h"
#include "../include.h"
void transmit_can_frame(CAN_frame* tx_frame, int interface);
@ -167,7 +166,7 @@ void RenaultZoeGen1Battery::handle_incoming_can_frame(CAN_frame rx_frame) {
switch (frame0) {
case 0x10: //PID HEADER, datarow 0
requested_poll = rx_frame.data.u8[3];
transmit_can_frame(&ZOE_ACK_79B, can_config.battery);
transmit_can_frame(&ZOE_ACK_79B, can_interface);
if (requested_poll == GROUP1_CELLVOLTAGES_1_POLL) {
cellvoltages[0] = (rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5];
@ -470,7 +469,7 @@ void RenaultZoeGen1Battery::transmit_can(unsigned long currentMillis) {
// Send 100ms CAN Message
if (currentMillis - previousMillis100 >= INTERVAL_100_MS) {
previousMillis100 = currentMillis;
transmit_can_frame(&ZOE_423, can_config.battery);
transmit_can_frame(&ZOE_423, can_interface);
if ((counter_423 / 5) % 2 == 0) { // Alternate every 5 messages between these two
ZOE_423.data.u8[4] = 0xB2;
@ -509,12 +508,12 @@ void RenaultZoeGen1Battery::transmit_can(unsigned long currentMillis) {
ZOE_POLL_79B.data.u8[2] = current_poll;
transmit_can_frame(&ZOE_POLL_79B, can_config.battery);
transmit_can_frame(&ZOE_POLL_79B, can_interface);
}
}
void RenaultZoeGen1Battery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen1 22/40kWh", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer_battery->info.number_of_cells = 96;
@ -524,5 +523,3 @@ void RenaultZoeGen1Battery::setup(void) { // Performs one time setup at startup
datalayer_battery->info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer_battery->info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
}
#endif

View file

@ -4,14 +4,9 @@
#include "CanBattery.h"
#include "RENAULT-ZOE-GEN1-HTML.h"
#define BATTERY_SELECTED
#ifdef RENAULT_ZOE_GEN1_BATTERY
#define SELECTED_BATTERY_CLASS RenaultZoeGen1Battery
#define MAX_PACK_VOLTAGE_DV 4200 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3000
#define MAX_CELL_DEVIATION_MV 150
#define MAX_CELL_VOLTAGE_MV 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2700 //Battery is put into emergency stop if one cell goes below this value
#endif
class RenaultZoeGen1Battery : public CanBattery {
public:
@ -36,10 +31,17 @@ class RenaultZoeGen1Battery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Renault Zoe Gen1 22/40kWh";
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private:
static const int MAX_PACK_VOLTAGE_DV = 4200; //5000 = 500.0V
static const int MIN_PACK_VOLTAGE_DV = 3000;
static const int MAX_CELL_DEVIATION_MV = 150;
static const int MAX_CELL_VOLTAGE_MV = 4250; //Battery is put into emergency stop if one cell goes over this value
static const int MIN_CELL_VOLTAGE_MV = 2700; //Battery is put into emergency stop if one cell goes below this value
RenaultZoeGen1HtmlRenderer renderer;
DATALAYER_BATTERY_TYPE* datalayer_battery;

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef RENAULT_ZOE_GEN2_BATTERY
#include "RENAULT-ZOE-GEN2-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "RENAULT-ZOE-GEN2-BATTERY.h"
#include "../include.h"
/* TODO
- Add //NVROL Reset
@ -705,7 +704,7 @@ void RenaultZoeGen2Battery::transmit_can(unsigned long currentMillis) {
}
void RenaultZoeGen2Battery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Renault Zoe Gen2 50kWh", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 96;
@ -788,5 +787,3 @@ void RenaultZoeGen2Battery::transmit_reset_nvrol_frames(void) {
break;
}
}
#endif

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#include "RENAULT-ZOE-GEN2-HTML.h"
#define BATTERY_SELECTED
#ifdef RENAULT_ZOE_GEN2_BATTERY
#define SELECTED_BATTERY_CLASS RenaultZoeGen2Battery
#endif
class RenaultZoeGen2Battery : public CanBattery {
public:
@ -14,6 +15,7 @@ class RenaultZoeGen2Battery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Renault Zoe Gen2 50kWh";
bool supports_reset_NVROL() { return true; }

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef RJXZS_BMS
#include "RJXZS-BMS.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "RJXZS-BMS.h"
#include "../include.h"
void RjxzsBms::update_values() {
@ -517,7 +516,7 @@ void RjxzsBms::transmit_can(unsigned long currentMillis) {
}
void RjxzsBms::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "RJXZS BMS, DIY battery", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
@ -525,5 +524,3 @@ void RjxzsBms::setup(void) { // Performs one time setup at startup
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
}
#endif // RJXZS_BMS

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef RJXZS_BMS
#define SELECTED_BATTERY_CLASS RjxzsBms
#endif
class RjxzsBms : public CanBattery {
public:
@ -14,6 +15,7 @@ class RjxzsBms : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "RJXZS BMS, DIY battery";
private:
/* Tweak these according to your battery build */

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef SANTA_FE_PHEV_BATTERY
#include "SANTA-FE-PHEV-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SANTA-FE-PHEV-BATTERY.h"
#include "../include.h"
/* Credits go to maciek16c for these findings!
https://github.com/maciek16c/hyundai-santa-fe-phev-battery
@ -320,7 +319,7 @@ void SantaFePhevBattery::transmit_can(unsigned long currentMillis) {
}
void SantaFePhevBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Santa Fe PHEV", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer_battery->info.number_of_cells = 96;
datalayer_battery->info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -333,5 +332,3 @@ void SantaFePhevBattery::setup(void) { // Performs one time setup at startup
*allows_contactor_closing = true;
}
}
#endif

View file

@ -5,8 +5,9 @@
#include "../include.h"
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef SANTA_FE_PHEV_BATTERY
#define SELECTED_BATTERY_CLASS SantaFePhevBattery
#endif
class SantaFePhevBattery : public CanBattery {
public:
@ -26,6 +27,7 @@ class SantaFePhevBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Santa Fe PHEV";
private:
DATALAYER_BATTERY_TYPE* datalayer_battery;

View file

@ -1,8 +1,7 @@
#include "../include.h"
#ifdef SIMPBMS_BATTERY
#include "SIMPBMS-BATTERY.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SIMPBMS-BATTERY.h"
#include "../include.h"
void SimpBmsBattery::update_values() {
@ -93,7 +92,7 @@ void SimpBmsBattery::transmit_can(unsigned long currentMillis) {
}
void SimpBmsBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "SIMPBMS battery", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = CELL_COUNT;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -102,5 +101,3 @@ void SimpBmsBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
}
#endif

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef SIMPBMS_BATTERY
#define SELECTED_BATTERY_CLASS SimpBmsBattery
#endif
class SimpBmsBattery : public CanBattery {
public:
@ -14,6 +15,7 @@ class SimpBmsBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "SIMPBMS battery";
private:
/* DEFAULT VALUES BMS will send configured */

View file

@ -1,9 +1,8 @@
#include "../include.h"
#ifdef SONO_BATTERY
#include "SONO-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SONO-BATTERY.h"
#include "../include.h"
void SonoBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
@ -141,7 +140,7 @@ void SonoBattery::transmit_can(unsigned long currentMillis) {
}
void SonoBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Sono Motors Sion 64kWh LFP ", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 96;
datalayer.system.status.battery_allows_contactor_closing = true;
@ -152,5 +151,3 @@ void SonoBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
}
#endif

View file

@ -5,8 +5,9 @@
#include "CanBattery.h"
#define BATTERY_SELECTED
#ifdef SONO_BATTERY
#define SELECTED_BATTERY_CLASS SonoBattery
#endif
class SonoBattery : public CanBattery {
public:
@ -14,6 +15,7 @@ class SonoBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Sono Motors Sion 64kWh LFP ";
private:
static const int MAX_PACK_VOLTAGE_DV = 5000; //5000 = 500.0V

View file

@ -11,6 +11,9 @@ class CanShunt : public Transmitter, CanReceiver {
virtual void transmit_can(unsigned long currentMillis) = 0;
virtual void handle_incoming_can_frame(CAN_frame rx_frame) = 0;
// The name of the comm interface the shunt is using.
virtual String interface_name() { return getCANInterfaceName(can_config.shunt); }
void transmit(unsigned long currentMillis) {
if (allowed_to_send_CAN) {
transmit_can(currentMillis);

View file

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

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef TESLA_BATTERY
#include "TESLA-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For Advanced Battery Insights webpage
#include "../devboard/utils/events.h"
#include "TESLA-BATTERY.h"
#include "../include.h"
/* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */
@ -1764,8 +1763,7 @@ void TeslaModel3YBattery::setup(void) { // Performs one time setup at startup
*allows_contactor_closing = true;
}
#ifdef TESLA_MODEL_3Y_BATTERY // Model 3/Y can be either LFP or NCM/A
strncpy(datalayer.system.info.battery_protocol, "Tesla Model 3/Y", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
#ifdef LFP_CHEMISTRY
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
@ -1781,7 +1779,6 @@ void TeslaModel3YBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
#endif // !LFP_CHEMISTRY
#endif // TESLA_MODEL_3Y_BATTERY
}
void TeslaModelSXBattery::setup(void) {
@ -1789,7 +1786,7 @@ void TeslaModelSXBattery::setup(void) {
*allows_contactor_closing = true;
}
strncpy(datalayer.system.info.battery_protocol, "Tesla Model S/X", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA;
@ -1797,5 +1794,3 @@ void TeslaModelSXBattery::setup(void) {
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
}
#endif // TESLA_BATTERY

View file

@ -5,7 +5,6 @@
#include "CanBattery.h"
#include "TESLA-HTML.h"
#define BATTERY_SELECTED
#ifdef TESLA_MODEL_3Y_BATTERY
#define SELECTED_BATTERY_CLASS TeslaModel3YBattery
#endif
@ -514,12 +513,14 @@ class TeslaModel3YBattery : public TeslaBattery {
operate_contactors = true;
#endif
}
static constexpr const char* Name = "Tesla Model 3/Y";
virtual void setup(void);
};
class TeslaModelSXBattery : public TeslaBattery {
public:
TeslaModelSXBattery() { operate_contactors = true; }
static constexpr const char* Name = "Tesla Model S/X";
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 "../datalayer/datalayer.h"
#include "../include.h"
void print_units(char* header, int value, char* units) {
static void print_units(char* header, int value, char* units) {
logging.print(header);
logging.print(value);
logging.print(units);
@ -75,7 +74,7 @@ void TestFakeBattery::transmit_can(unsigned long currentMillis) {
void TestFakeBattery::setup(void) { // Performs one time setup at startup
randomSeed(analogRead(0));
strncpy(datalayer.system.info.battery_protocol, "Fake battery for testing purposes", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer_battery->info.max_design_voltage_dV =
@ -87,5 +86,3 @@ void TestFakeBattery::setup(void) { // Performs one time setup at startup
*allows_contactor_closing = true;
}
}
#endif

View file

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

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef VOLVO_SPA_BATTERY
#include "VOLVO-SPA-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "VOLVO-SPA-BATTERY.h"
#include "../include.h"
void VolvoSpaBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
@ -378,7 +377,7 @@ void VolvoSpaBattery::transmit_can(unsigned long currentMillis) {
}
void VolvoSpaBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Volvo / Polestar 69/78kWh SPA battery", 63);
strncpy(datalayer.system.info.battery_protocol, Name, 63);
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 0; // Initializes when all cells have been read
datalayer.battery.info.total_capacity_Wh = 78200; //Startout in 78kWh mode (This value used for SOC calc)
@ -388,4 +387,3 @@ void VolvoSpaBattery::setup(void) { // Performs one time setup at startup
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
}
#endif

View file

@ -6,8 +6,9 @@
#include "CanBattery.h"
#include "VOLVO-SPA-HTML.h"
#define BATTERY_SELECTED
#ifdef VOLVO_SPA_BATTERY
#define SELECTED_BATTERY_CLASS VolvoSpaBattery
#endif
class VolvoSpaBattery : public CanBattery {
public:
@ -15,6 +16,7 @@ class VolvoSpaBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Volvo / Polestar 69/78kWh SPA battery";
bool supports_reset_DTC() { return true; }
void reset_DTC() { datalayer_extended.VolvoPolestar.UserRequestDTCreset = true; }
@ -25,7 +27,11 @@ class VolvoSpaBattery : public CanBattery {
bool supports_reset_BECM() { return true; }
void reset_BECM() { datalayer_extended.VolvoPolestar.UserRequestBECMecuReset = true; }
BatteryHtmlRenderer& get_status_renderer() { return renderer; }
private:
VolvoSpaHtmlRenderer renderer;
void readCellVoltages();
static const int MAX_PACK_VOLTAGE_108S_DV = 4540;

View file

@ -1,10 +1,9 @@
#include "../include.h"
#ifdef VOLVO_SPA_HYBRID_BATTERY
#include "VOLVO-SPA-HYBRID-BATTERY.h"
#include "../communication/can/comm_can.h"
#include "../datalayer/datalayer.h"
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "VOLVO-SPA-HYBRID-BATTERY.h"
#include "../include.h"
void VolvoSpaHybridBattery::
update_values() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
@ -553,8 +552,8 @@ void VolvoSpaHybridBattery::transmit_can(unsigned long currentMillis) {
}
}
void VolvoSpaHybridBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, "Volvo PHEV battery", 63); //changed
void VolvoSpaHybridBattery::setup(void) { // Performs one time setup at startup
strncpy(datalayer.system.info.battery_protocol, Name, 63); //changed
datalayer.system.info.battery_protocol[63] = '\0';
datalayer.battery.info.number_of_cells = 102; //was 108, changed
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
@ -563,4 +562,3 @@ void VolvoSpaHybridBattery::setup(void) { //
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
}
#endif

View file

@ -6,8 +6,9 @@
#include "CanBattery.h"
#include "VOLVO-SPA-HYBRID-HTML.h"
#define BATTERY_SELECTED
#ifdef VOLVO_SPA_HYBRID_BATTERY
#define SELECTED_BATTERY_CLASS VolvoSpaHybridBattery
#endif
class VolvoSpaHybridBattery : public CanBattery {
public:
@ -15,6 +16,7 @@ class VolvoSpaHybridBattery : public CanBattery {
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
virtual void update_values();
virtual void transmit_can(unsigned long currentMillis);
static constexpr const char* Name = "Volvo PHEV battery";
bool supports_reset_DTC() { return true; }
void reset_DTC() { datalayer_extended.VolvoHybrid.UserRequestDTCreset = true; }

View file

@ -2,8 +2,42 @@
CanCharger* charger = nullptr;
ChargerType user_selected_charger_type = ChargerType::None;
std::vector<ChargerType> supported_charger_types() {
std::vector<ChargerType> types;
for (int i = 0; i < (int)ChargerType::Highest; i++) {
types.push_back((ChargerType)i);
}
return types;
}
extern const char* name_for_charger_type(ChargerType type) {
switch (type) {
case ChargerType::ChevyVolt:
return ChevyVoltCharger::Name;
case ChargerType::NissanLeaf:
return NissanLeafCharger::Name;
case ChargerType::None:
return "None";
}
return nullptr;
}
void setup_charger() {
#ifdef COMMON_IMAGE
switch (user_selected_charger_type) {
case ChargerType::ChevyVolt:
charger = new ChevyVoltCharger();
case ChargerType::NissanLeaf:
charger = new NissanLeafCharger();
}
#else
#ifdef SELECTED_CHARGER_CLASS
charger = new SELECTED_CHARGER_CLASS();
#endif
#endif
}

View file

@ -13,7 +13,8 @@ class ChevyVoltCharger : public CanCharger {
public:
ChevyVoltCharger() : CanCharger(ChargerType::ChevyVolt) {}
const char* name() { return "Chevy Volt Gen1 Charger"; }
const char* name() { return Name; }
static constexpr const char* Name = "Chevy Volt Gen1 Charger";
void map_can_frame_to_variable(CAN_frame rx_frame);
void transmit_can(unsigned long currentMillis);

View file

@ -7,7 +7,12 @@
#include "src/communication/Transmitter.h"
#include "src/communication/can/CanReceiver.h"
enum class ChargerType { NissanLeaf, ChevyVolt };
enum class ChargerType { None, NissanLeaf, ChevyVolt, Highest };
extern ChargerType user_selected_charger_type;
extern std::vector<ChargerType> supported_charger_types();
extern const char* name_for_charger_type(ChargerType type);
// Generic base class for all chargers
class Charger {

View file

@ -13,7 +13,9 @@ class NissanLeafCharger : public CanCharger {
public:
NissanLeafCharger() : CanCharger(ChargerType::NissanLeaf) {}
const char* name() { return "Nissan LEAF 2013-2024 PDM charger"; }
const char* name() { return Name; }
static constexpr const char* Name = "Nissan LEAF 2013-2024 PDM charger";
void map_can_frame_to_variable(CAN_frame rx_frame);
void transmit_can(unsigned long currentMillis);

View file

@ -2,7 +2,6 @@
#define _CANRECEIVER_H
#include "src/devboard/utils/types.h"
#include "src/include.h"
class CanReceiver {
public:

View file

@ -1,19 +1,50 @@
#include "comm_contactorcontrol.h"
#include "../../include.h"
// Parameters
#ifndef CONTACTOR_CONTROL
#ifdef PWM_CONTACTOR_CONTROL
#error CONTACTOR_CONTROL needs to be enabled for PWM_CONTACTOR_CONTROL
#endif
#endif
#ifdef CONTACTOR_CONTROL
const bool contactor_control_enabled_default = true;
#else
const bool contactor_control_enabled_default = false;
#endif
bool contactor_control_enabled = contactor_control_enabled_default;
#ifdef PWM_CONTACTOR_CONTROL
const bool pwn_contactor_control_default = true;
#else
const bool pwn_contactor_control_default = false;
#endif
bool pwm_contactor_control = pwn_contactor_control_default;
#ifdef PERIODIC_BMS_RESET
const bool periodic_bms_reset_default = true;
#else
const bool periodic_bms_reset_default = false;
#endif
bool periodic_bms_reset = periodic_bms_reset_default;
#ifdef REMOTE_BMS_RESET
const bool remote_bms_reset_default = true;
#else
const bool remote_bms_reset_default = true;
#endif
bool remote_bms_reset = remote_bms_reset_default;
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
const bool contactor_control_enabled_double_battery_default = true;
#else
const bool contactor_control_enabled_double_battery_default = false;
#endif
bool contactor_control_enabled_double_battery = contactor_control_enabled_double_battery_default;
// TODO: Ensure valid values at run-time
// Parameters
enum State { DISCONNECTED, START_PRECHARGE, PRECHARGE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
State contactorStatus = DISCONNECTED;
#define ON 1
#define OFF 0
const int ON = 1;
const int OFF = 0;
#ifdef NC_CONTACTORS //Normally closed contactors use inverted logic
#undef ON
@ -34,11 +65,10 @@ State contactorStatus = DISCONNECTED;
#define PWM_ON_DUTY 1023
#define PWM_Positive_Channel 0
#define PWM_Negative_Channel 1
unsigned long prechargeStartTime = 0;
static unsigned long prechargeStartTime = 0;
unsigned long negativeStartTime = 0;
unsigned long prechargeCompletedTime = 0;
unsigned long timeSpentInFaultedMode = 0;
#endif
unsigned long currentTime = 0;
unsigned long lastPowerRemovalTime = 0;
unsigned long bmsPowerOnTime = 0;
@ -47,12 +77,12 @@ const unsigned long powerRemovalDuration = 30000; // 30 seconds i
const unsigned long bmsWarmupDuration = 3000;
void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) {
#ifdef PWM_CONTACTOR_CONTROL
if (pwm_freq != 0xFFFF) {
ledcWrite(pin, pwm_freq);
return;
if (pwm_contactor_control) {
if (pwm_freq != 0xFFFF) {
ledcWrite(pin, pwm_freq);
return;
}
}
#endif
if (direction == 1) {
digitalWrite(pin, HIGH);
} else { // 0
@ -64,36 +94,39 @@ void set(uint8_t pin, bool direction, uint32_t pwm_freq = 0xFFFF) {
void init_contactors() {
// Init contactor pins
#ifdef CONTACTOR_CONTROL
#ifdef PWM_CONTACTOR_CONTROL
// Setup PWM Channel Frequency and Resolution
ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Positive_Channel);
ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Negative_Channel);
// Set all pins OFF (0% PWM)
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
#else //Normal CONTACTOR_CONTROL
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
set(POSITIVE_CONTACTOR_PIN, OFF);
pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT);
set(NEGATIVE_CONTACTOR_PIN, OFF);
#endif // Precharge never has PWM regardless of setting
pinMode(PRECHARGE_PIN, OUTPUT);
set(PRECHARGE_PIN, OFF);
#endif // CONTACTOR_CONTROL
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
pinMode(SECOND_BATTERY_CONTACTORS_PIN, OUTPUT);
set(SECOND_BATTERY_CONTACTORS_PIN, OFF);
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
if (contactor_control_enabled) {
if (pwm_contactor_control) {
// Setup PWM Channel Frequency and Resolution
ledcAttachChannel(POSITIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Positive_Channel);
ledcAttachChannel(NEGATIVE_CONTACTOR_PIN, PWM_Freq, PWM_Res, PWM_Negative_Channel);
// Set all pins OFF (0% PWM)
ledcWrite(POSITIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
ledcWrite(NEGATIVE_CONTACTOR_PIN, PWM_OFF_DUTY);
} else { //Normal CONTACTOR_CONTROL
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
set(POSITIVE_CONTACTOR_PIN, OFF);
pinMode(NEGATIVE_CONTACTOR_PIN, OUTPUT);
set(NEGATIVE_CONTACTOR_PIN, OFF);
} // Precharge never has PWM regardless of setting
pinMode(PRECHARGE_PIN, OUTPUT);
set(PRECHARGE_PIN, OFF);
}
if (contactor_control_enabled_double_battery) {
pinMode(SECOND_BATTERY_CONTACTORS_PIN, OUTPUT);
set(SECOND_BATTERY_CONTACTORS_PIN, OFF);
}
// Init BMS contactor
#if defined HW_STARK || defined HW_3LB // This hardware has dedicated pin, always enable on start
pinMode(BMS_POWER, OUTPUT); //LilyGo is omitted from this, only enabled if user selects PERIODIC_BMS_RESET
digitalWrite(BMS_POWER, HIGH);
#endif // HW with dedicated BMS pins
#if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET) // User has enabled BMS reset, turn on output on start
pinMode(BMS_POWER, OUTPUT);
digitalWrite(BMS_POWER, HIGH);
#endif //PERIODIC_BMS_RESET
#endif // HW with dedicated BMS pins
#ifdef BMS_POWER
if (periodic_bms_reset || remote_bms_reset) {
pinMode(BMS_POWER, OUTPUT);
digitalWrite(BMS_POWER, HIGH);
}
#endif
}
static void dbg_contactors(const char* state) {
@ -107,110 +140,114 @@ static void dbg_contactors(const char* state) {
// Main functions of the handle_contactors include checking if inverter allows for closing, checking battery 2, checking BMS power output, and actual contactor closing/precharge via GPIO
void handle_contactors() {
#if defined(SMA_BYD_H_CAN) || defined(SMA_BYD_HVS_CAN) || defined(SMA_TRIPOWER_CAN)
datalayer.system.status.inverter_allows_contactor_closing = digitalRead(INVERTER_CONTACTOR_ENABLE_PIN);
#endif
if (inverter && inverter->controls_contactor()) {
datalayer.system.status.inverter_allows_contactor_closing = inverter->allows_contactor_closing();
}
#ifdef BMS_POWER
handle_BMSpower(); // Some batteries need to be periodically power cycled
#endif
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
handle_contactors_battery2();
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
#ifdef CONTACTOR_CONTROL
// First check if we have any active errors, incase we do, turn off the battery
if (datalayer.battery.status.bms_status == FAULT) {
timeSpentInFaultedMode++;
} else {
timeSpentInFaultedMode = 0;
}
if (contactor_control_enabled) {
// First check if we have any active errors, incase we do, turn off the battery
if (datalayer.battery.status.bms_status == FAULT) {
timeSpentInFaultedMode++;
} else {
timeSpentInFaultedMode = 0;
}
//handle contactor control SHUTDOWN_REQUESTED
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) {
contactorStatus = SHUTDOWN_REQUESTED;
}
//handle contactor control SHUTDOWN_REQUESTED
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) {
contactorStatus = SHUTDOWN_REQUESTED;
}
if (contactorStatus == SHUTDOWN_REQUESTED) {
set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set_event(EVENT_ERROR_OPEN_CONTACTOR, 0);
datalayer.system.status.contactors_engaged = false;
return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured)
}
if (contactorStatus == SHUTDOWN_REQUESTED) {
set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set_event(EVENT_ERROR_OPEN_CONTACTOR, 0);
datalayer.system.status.contactors_engaged = false;
return; // A fault scenario latches the contactor control. It is not possible to recover without a powercycle (and investigation why fault occured)
}
// After that, check if we are OK to start turning on the battery
if (contactorStatus == DISCONNECTED) {
set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
datalayer.system.status.contactors_engaged = false;
// After that, check if we are OK to start turning on the battery
if (contactorStatus == DISCONNECTED) {
set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
set(POSITIVE_CONTACTOR_PIN, OFF, PWM_OFF_DUTY);
datalayer.system.status.contactors_engaged = false;
if (datalayer.system.status.battery_allows_contactor_closing &&
datalayer.system.status.inverter_allows_contactor_closing && !datalayer.system.settings.equipment_stop_active) {
contactorStatus = START_PRECHARGE;
if (datalayer.system.status.battery_allows_contactor_closing &&
datalayer.system.status.inverter_allows_contactor_closing &&
!datalayer.system.settings.equipment_stop_active) {
contactorStatus = START_PRECHARGE;
}
}
// In case the inverter requests contactors to open, set the state accordingly
if (contactorStatus == COMPLETED) {
//Incase inverter (or estop) requests contactors to open, make state machine jump to Disconnected state (recoverable)
if (!datalayer.system.status.inverter_allows_contactor_closing ||
datalayer.system.settings.equipment_stop_active) {
contactorStatus = DISCONNECTED;
}
// Skip running the state machine below if it has already completed
return;
}
currentTime = millis();
if (currentTime < INTERVAL_10_S) {
// Skip running the state machine before system has started up.
// Gives the system some time to detect any faults from battery before blindly just engaging the contactors
return;
}
// Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge
switch (contactorStatus) {
case START_PRECHARGE:
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
dbg_contactors("NEGATIVE");
prechargeStartTime = currentTime;
contactorStatus = PRECHARGE;
break;
case PRECHARGE:
if (currentTime - prechargeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) {
set(PRECHARGE_PIN, ON);
dbg_contactors("PRECHARGE");
negativeStartTime = currentTime;
contactorStatus = POSITIVE;
}
break;
case POSITIVE:
if (currentTime - negativeStartTime >= PRECHARGE_TIME_MS) {
set(POSITIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
dbg_contactors("POSITIVE");
prechargeCompletedTime = currentTime;
contactorStatus = PRECHARGE_OFF;
}
break;
case PRECHARGE_OFF:
if (currentTime - prechargeCompletedTime >= PRECHARGE_COMPLETED_TIME_MS) {
set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
dbg_contactors("PRECHARGE_OFF");
contactorStatus = COMPLETED;
datalayer.system.status.contactors_engaged = true;
}
break;
default:
break;
}
}
// In case the inverter requests contactors to open, set the state accordingly
if (contactorStatus == COMPLETED) {
//Incase inverter (or estop) requests contactors to open, make state machine jump to Disconnected state (recoverable)
if (!datalayer.system.status.inverter_allows_contactor_closing || datalayer.system.settings.equipment_stop_active) {
contactorStatus = DISCONNECTED;
}
// Skip running the state machine below if it has already completed
return;
}
currentTime = millis();
if (currentTime < INTERVAL_10_S) {
// Skip running the state machine before system has started up.
// Gives the system some time to detect any faults from battery before blindly just engaging the contactors
return;
}
// Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge
switch (contactorStatus) {
case START_PRECHARGE:
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
dbg_contactors("NEGATIVE");
prechargeStartTime = currentTime;
contactorStatus = PRECHARGE;
break;
case PRECHARGE:
if (currentTime - prechargeStartTime >= NEGATIVE_CONTACTOR_TIME_MS) {
set(PRECHARGE_PIN, ON);
dbg_contactors("PRECHARGE");
negativeStartTime = currentTime;
contactorStatus = POSITIVE;
}
break;
case POSITIVE:
if (currentTime - negativeStartTime >= PRECHARGE_TIME_MS) {
set(POSITIVE_CONTACTOR_PIN, ON, PWM_ON_DUTY);
dbg_contactors("POSITIVE");
prechargeCompletedTime = currentTime;
contactorStatus = PRECHARGE_OFF;
}
break;
case PRECHARGE_OFF:
if (currentTime - prechargeCompletedTime >= PRECHARGE_COMPLETED_TIME_MS) {
set(PRECHARGE_PIN, OFF);
set(NEGATIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
set(POSITIVE_CONTACTOR_PIN, ON, PWM_HOLD_DUTY);
dbg_contactors("PRECHARGE_OFF");
contactorStatus = COMPLETED;
datalayer.system.status.contactors_engaged = true;
}
break;
default:
break;
}
#endif // CONTACTOR_CONTROL
}
#ifdef CONTACTOR_CONTROL_DOUBLE_BATTERY
@ -223,7 +260,7 @@ void handle_contactors_battery2() {
datalayer.system.status.contactors_battery2_engaged = false;
}
}
#endif // CONTACTOR_CONTROL_DOUBLE_BATTERY
#endif
/* PERIODIC_BMS_RESET - Once every 24 hours we remove power from the BMS_power pin for 30 seconds.
REMOTE_BMS_RESET - Allows the user to remotely powercycle the BMS by sending a command to the emulator via MQTT.
@ -232,58 +269,62 @@ This makes the BMS recalculate all SOC% and avoid memory leaks
During that time we also set the emulator state to paused in order to not try and send CAN messages towards the battery
Feature is only used if user has enabled PERIODIC_BMS_RESET in the USER_SETTINGS */
#ifdef BMS_POWER
void handle_BMSpower() {
#if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET)
// Get current time
currentTime = millis();
if (periodic_bms_reset || remote_bms_reset) {
// Get current time
currentTime = millis();
#ifdef PERIODIC_BMS_RESET
// Check if 24 hours have passed since the last power removal
if ((currentTime + bmsResetTimeOffset) - lastPowerRemovalTime >= powerRemovalInterval) {
start_bms_reset();
}
#endif //PERIODIC_BMS_RESET
if (periodic_bms_reset) {
// Check if 24 hours have passed since the last power removal
if ((currentTime + bmsResetTimeOffset) - lastPowerRemovalTime >= powerRemovalInterval) {
start_bms_reset();
}
}
// If power has been removed for 30 seconds, restore the power
if (datalayer.system.status.BMS_reset_in_progress && currentTime - lastPowerRemovalTime >= powerRemovalDuration) {
// Reapply power to the BMS
digitalWrite(BMS_POWER, HIGH);
// If power has been removed for 30 seconds, restore the power
if (datalayer.system.status.BMS_reset_in_progress && currentTime - lastPowerRemovalTime >= powerRemovalDuration) {
// Reapply power to the BMS
digitalWrite(BMS_POWER, HIGH);
#ifdef BMS_2_POWER
digitalWrite(BMS_2_POWER, HIGH); // Same for battery 2
digitalWrite(BMS_2_POWER, HIGH); // Same for battery 2
#endif
bmsPowerOnTime = currentTime;
datalayer.system.status.BMS_reset_in_progress = false; // Reset the power removal flag
datalayer.system.status.BMS_startup_in_progress = true; // Set the BMS warmup flag
}
//if power has been restored we need to wait a couple of seconds to unpause the battery
if (datalayer.system.status.BMS_startup_in_progress && currentTime - bmsPowerOnTime >= bmsWarmupDuration) {
bmsPowerOnTime = currentTime;
datalayer.system.status.BMS_reset_in_progress = false; // Reset the power removal flag
datalayer.system.status.BMS_startup_in_progress = true; // Set the BMS warmup flag
}
//if power has been restored we need to wait a couple of seconds to unpause the battery
if (datalayer.system.status.BMS_startup_in_progress && currentTime - bmsPowerOnTime >= bmsWarmupDuration) {
setBatteryPause(false, false, false, false);
setBatteryPause(false, false, false, false);
datalayer.system.status.BMS_startup_in_progress = false; // Reset the BMS warmup removal flag
set_event(EVENT_PERIODIC_BMS_RESET, 0);
datalayer.system.status.BMS_startup_in_progress = false; // Reset the BMS warmup removal flag
set_event(EVENT_PERIODIC_BMS_RESET, 0);
}
}
#endif //defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET)
}
#endif
void start_bms_reset() {
#if defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET)
if (!datalayer.system.status.BMS_reset_in_progress) {
lastPowerRemovalTime = currentTime; // Record the time when BMS reset was started
// we are now resetting at the correct time. We don't need to offset anymore
bmsResetTimeOffset = 0;
// Set a flag to let the rest of the system know we are cutting power to the BMS.
// The battery CAN sending routine will then know not to try guto send anything towards battery while active
datalayer.system.status.BMS_reset_in_progress = true;
if (periodic_bms_reset || remote_bms_reset) {
if (!datalayer.system.status.BMS_reset_in_progress) {
lastPowerRemovalTime = currentTime; // Record the time when BMS reset was started
// we are now resetting at the correct time. We don't need to offset anymore
bmsResetTimeOffset = 0;
// Set a flag to let the rest of the system know we are cutting power to the BMS.
// The battery CAN sending routine will then know not to try guto send anything towards battery while active
datalayer.system.status.BMS_reset_in_progress = true;
// Set emulator state to paused (Max Charge/Discharge = 0 & CAN = stop)
// We try to keep contactors engaged during this pause, and just ramp power down to 0.
setBatteryPause(true, false, false, false);
// Set emulator state to paused (Max Charge/Discharge = 0 & CAN = stop)
// We try to keep contactors engaged during this pause, and just ramp power down to 0.
setBatteryPause(true, false, false, false);
digitalWrite(BMS_POWER, LOW); // Remove power by setting the BMS power pin to LOW
#ifdef BMS_2_POWER
digitalWrite(BMS_2_POWER, LOW); // Same for battery 2
#ifdef BMS_POWER
digitalWrite(BMS_POWER, LOW); // Remove power by setting the BMS power pin to LOW
#endif
#ifdef BMS_2_POWER
digitalWrite(BMS_2_POWER, LOW); // Same for battery 2
#endif
}
}
#endif //defined(PERIODIC_BMS_RESET) || defined(REMOTE_BMS_RESET)
}

View file

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

View file

@ -1,5 +1,6 @@
#include "comm_nvm.h"
#include "../../include.h"
#include "../contactorcontrol/comm_contactorcontrol.h"
// Parameters
Preferences settings; // Store user settings
@ -69,6 +70,19 @@ void init_stored_settings() {
datalayer.battery.settings.max_user_set_discharge_voltage_dV = temp;
}
datalayer.battery.settings.user_set_voltage_limits_active = settings.getBool("USEVOLTLIMITS", false);
#ifdef COMMON_IMAGE
user_selected_battery_type = (BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None);
user_selected_inverter_protocol = (InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None);
user_selected_charger_type = (ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None);
user_selected_second_battery = settings.getBool("DBLBTR", false);
contactor_control_enabled = settings.getBool("CNTCTRL", false);
contactor_control_enabled_double_battery = settings.getBool("CNTCTRLDBL", false);
pwm_contactor_control = settings.getBool("PWMCNTCTRL", false);
periodic_bms_reset = settings.getBool("PERBMSRESET", false);
remote_bms_reset = settings.getBool("REMBMSRESET", false);
#endif
settings.end();
}
@ -121,5 +135,6 @@ void store_settings() {
if (!settings.putUInt("TARGETDISCHVOLT", datalayer.battery.settings.max_user_set_discharge_voltage_dV)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 11);
}
settings.end(); // Close preferences handle
}

View file

@ -34,4 +34,28 @@ void store_settings_equipment_stop();
*/
void store_settings();
// Wraps the Preferences object begin/end calls, so that the scope of this object
// runs them automatically (via constructor/destructor).
class BatteryEmulatorSettingsStore {
public:
BatteryEmulatorSettingsStore(bool readOnly = false) {
if (!settings.begin("batterySettings", readOnly)) {
set_event(EVENT_PERSISTENT_SAVE_INFO, 0);
}
}
~BatteryEmulatorSettingsStore() { settings.end(); }
uint32_t getUInt(const char* name, uint32_t defaultValue) { return settings.getUInt(name, defaultValue); }
void saveUInt(const char* name, uint32_t value) { settings.putUInt(name, value); }
bool getBool(const char* name) { return settings.getBool(name, false); }
void saveBool(const char* name, bool value) { settings.putBool(name, value); }
private:
Preferences settings;
};
#endif

View file

@ -18,9 +18,9 @@
#define ON 1
#define OFF 0
#endif
unsigned long prechargeStartTime = 0;
static unsigned long prechargeStartTime = 0;
static uint32_t freq = Precharge_default_PWM_Freq;
uint16_t delta_freq = 1;
static uint16_t delta_freq = 1;
static int32_t prev_external_voltage = 20000;
// Initialization functions

View file

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

View file

@ -55,161 +55,166 @@ void update_machineryprotection() {
}
// Start checking that the battery is within reason. Incase we see any funny business, raise an event!
// Don't check any battery issues if battery is not configured
if (battery) {
// Pause function is on OR we have a critical fault event active
if (emulator_pause_request_ON || (datalayer.battery.status.bms_status == FAULT)) {
datalayer.battery.status.max_discharge_power_W = 0;
datalayer.battery.status.max_charge_power_W = 0;
}
// Battery is overheated!
if (datalayer.battery.status.temperature_max_dC > BATTERY_MAXTEMPERATURE) {
set_event(EVENT_BATTERY_OVERHEAT, datalayer.battery.status.temperature_max_dC);
} else {
clear_event(EVENT_BATTERY_OVERHEAT);
}
// Battery is frozen!
if (datalayer.battery.status.temperature_min_dC < BATTERY_MINTEMPERATURE) {
set_event(EVENT_BATTERY_FROZEN, datalayer.battery.status.temperature_min_dC);
} else {
clear_event(EVENT_BATTERY_FROZEN);
}
if (labs(datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC) >
BATTERY_MAX_TEMPERATURE_DEVIATION) {
set_event_latched(EVENT_BATTERY_TEMP_DEVIATION_HIGH,
datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC);
} else {
clear_event(EVENT_BATTERY_TEMP_DEVIATION_HIGH);
}
// Battery voltage is over designed max voltage!
if (datalayer.battery.status.voltage_dV > datalayer.battery.info.max_design_voltage_dV) {
set_event(EVENT_BATTERY_OVERVOLTAGE, datalayer.battery.status.voltage_dV);
datalayer.battery.status.max_charge_power_W = 0;
} else {
clear_event(EVENT_BATTERY_OVERVOLTAGE);
}
// Battery voltage is under designed min voltage!
if (datalayer.battery.status.voltage_dV < datalayer.battery.info.min_design_voltage_dV) {
set_event(EVENT_BATTERY_UNDERVOLTAGE, datalayer.battery.status.voltage_dV);
datalayer.battery.status.max_discharge_power_W = 0;
} else {
clear_event(EVENT_BATTERY_UNDERVOLTAGE);
}
// Cell overvoltage, further charging not possible. Battery might be imbalanced.
if (datalayer.battery.status.cell_max_voltage_mV >= datalayer.battery.info.max_cell_voltage_mV) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
datalayer.battery.status.max_charge_power_W = 0;
}
// Cell CRITICAL overvoltage, critical latching error without automatic reset. Requires user action to inspect battery.
if (datalayer.battery.status.cell_max_voltage_mV >= (datalayer.battery.info.max_cell_voltage_mV + CELL_CRITICAL_MV)) {
set_event(EVENT_CELL_CRITICAL_OVER_VOLTAGE, 0);
}
// Cell undervoltage. Further discharge not possible. Battery might be imbalanced.
if (datalayer.battery.status.cell_min_voltage_mV <= datalayer.battery.info.min_cell_voltage_mV) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
datalayer.battery.status.max_discharge_power_W = 0;
}
//Cell CRITICAL undervoltage. critical latching error without automatic reset. Requires user action to inspect battery.
if (datalayer.battery.status.cell_min_voltage_mV <= (datalayer.battery.info.min_cell_voltage_mV - CELL_CRITICAL_MV)) {
set_event(EVENT_CELL_CRITICAL_UNDER_VOLTAGE, 0);
}
// Battery is fully charged. Dont allow any more power into it
// Normally the BMS will send 0W allowed, but this acts as an additional layer of safety
if (datalayer.battery.status.reported_soc == 10000) //Scaled SOC% value is 100.00%
{
if (!battery_full_event_fired) {
set_event(EVENT_BATTERY_FULL, 0);
battery_full_event_fired = true;
// Pause function is on OR we have a critical fault event active
if (emulator_pause_request_ON || (datalayer.battery.status.bms_status == FAULT)) {
datalayer.battery.status.max_discharge_power_W = 0;
datalayer.battery.status.max_charge_power_W = 0;
}
datalayer.battery.status.max_charge_power_W = 0;
} else {
clear_event(EVENT_BATTERY_FULL);
battery_full_event_fired = false;
}
// Battery is empty. Do not allow further discharge.
// Normally the BMS will send 0W allowed, but this acts as an additional layer of safety
if (datalayer.battery.status.bms_status == ACTIVE) {
if (datalayer.battery.status.reported_soc == 0) { //Scaled SOC% value is 0.00%
if (!battery_empty_event_fired) {
set_event(EVENT_BATTERY_EMPTY, 0);
battery_empty_event_fired = true;
}
// Battery is overheated!
if (datalayer.battery.status.temperature_max_dC > BATTERY_MAXTEMPERATURE) {
set_event(EVENT_BATTERY_OVERHEAT, datalayer.battery.status.temperature_max_dC);
} else {
clear_event(EVENT_BATTERY_OVERHEAT);
}
// Battery is frozen!
if (datalayer.battery.status.temperature_min_dC < BATTERY_MINTEMPERATURE) {
set_event(EVENT_BATTERY_FROZEN, datalayer.battery.status.temperature_min_dC);
} else {
clear_event(EVENT_BATTERY_FROZEN);
}
if (labs(datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC) >
BATTERY_MAX_TEMPERATURE_DEVIATION) {
set_event_latched(EVENT_BATTERY_TEMP_DEVIATION_HIGH,
datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC);
} else {
clear_event(EVENT_BATTERY_TEMP_DEVIATION_HIGH);
}
// Battery voltage is over designed max voltage!
if (datalayer.battery.status.voltage_dV > datalayer.battery.info.max_design_voltage_dV) {
set_event(EVENT_BATTERY_OVERVOLTAGE, datalayer.battery.status.voltage_dV);
datalayer.battery.status.max_charge_power_W = 0;
} else {
clear_event(EVENT_BATTERY_OVERVOLTAGE);
}
// Battery voltage is under designed min voltage!
if (datalayer.battery.status.voltage_dV < datalayer.battery.info.min_design_voltage_dV) {
set_event(EVENT_BATTERY_UNDERVOLTAGE, datalayer.battery.status.voltage_dV);
datalayer.battery.status.max_discharge_power_W = 0;
} else {
clear_event(EVENT_BATTERY_EMPTY);
battery_empty_event_fired = false;
clear_event(EVENT_BATTERY_UNDERVOLTAGE);
}
}
// Battery is extremely degraded, not fit for secondlifestorage!
if (datalayer.battery.status.soh_pptt < 2500) {
set_event(EVENT_SOH_LOW, datalayer.battery.status.soh_pptt);
} else {
clear_event(EVENT_SOH_LOW);
}
// Cell overvoltage, further charging not possible. Battery might be imbalanced.
if (datalayer.battery.status.cell_max_voltage_mV >= datalayer.battery.info.max_cell_voltage_mV) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
datalayer.battery.status.max_charge_power_W = 0;
}
// Cell CRITICAL overvoltage, critical latching error without automatic reset. Requires user action to inspect battery.
if (datalayer.battery.status.cell_max_voltage_mV >=
(datalayer.battery.info.max_cell_voltage_mV + CELL_CRITICAL_MV)) {
set_event(EVENT_CELL_CRITICAL_OVER_VOLTAGE, 0);
}
if (!battery->soc_plausible()) {
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc);
}
// Cell undervoltage. Further discharge not possible. Battery might be imbalanced.
if (datalayer.battery.status.cell_min_voltage_mV <= datalayer.battery.info.min_cell_voltage_mV) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
datalayer.battery.status.max_discharge_power_W = 0;
}
//Cell CRITICAL undervoltage. critical latching error without automatic reset. Requires user action to inspect battery.
if (datalayer.battery.status.cell_min_voltage_mV <=
(datalayer.battery.info.min_cell_voltage_mV - CELL_CRITICAL_MV)) {
set_event(EVENT_CELL_CRITICAL_UNDER_VOLTAGE, 0);
}
// Check diff between highest and lowest cell
cell_deviation_mV =
std::abs(datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV);
if (cell_deviation_mV > datalayer.battery.info.max_cell_voltage_deviation_mV) {
set_event(EVENT_CELL_DEVIATION_HIGH, (cell_deviation_mV / 20));
} else {
clear_event(EVENT_CELL_DEVIATION_HIGH);
}
// Inverter is charging with more power than battery wants!
if (datalayer.battery.status.active_power_W > 0) { // Charging
if (datalayer.battery.status.active_power_W > (datalayer.battery.status.max_charge_power_W + 2000)) {
if (charge_limit_failures > MAX_CHARGE_DISCHARGE_LIMIT_FAILURES) {
set_event(EVENT_CHARGE_LIMIT_EXCEEDED, 0); // Alert when 2kW over requested max
} else {
charge_limit_failures++;
// Battery is fully charged. Dont allow any more power into it
// Normally the BMS will send 0W allowed, but this acts as an additional layer of safety
if (datalayer.battery.status.reported_soc == 10000) //Scaled SOC% value is 100.00%
{
if (!battery_full_event_fired) {
set_event(EVENT_BATTERY_FULL, 0);
battery_full_event_fired = true;
}
datalayer.battery.status.max_charge_power_W = 0;
} else {
clear_event(EVENT_CHARGE_LIMIT_EXCEEDED);
charge_limit_failures = 0;
clear_event(EVENT_BATTERY_FULL);
battery_full_event_fired = false;
}
}
// Inverter is pulling too much power from battery!
if (datalayer.battery.status.active_power_W < 0) { // Discharging
if (-datalayer.battery.status.active_power_W > (datalayer.battery.status.max_discharge_power_W + 2000)) {
if (discharge_limit_failures > MAX_CHARGE_DISCHARGE_LIMIT_FAILURES) {
set_event(EVENT_DISCHARGE_LIMIT_EXCEEDED, 0); // Alert when 2kW over requested max
// Battery is empty. Do not allow further discharge.
// Normally the BMS will send 0W allowed, but this acts as an additional layer of safety
if (datalayer.battery.status.bms_status == ACTIVE) {
if (datalayer.battery.status.reported_soc == 0) { //Scaled SOC% value is 0.00%
if (!battery_empty_event_fired) {
set_event(EVENT_BATTERY_EMPTY, 0);
battery_empty_event_fired = true;
}
datalayer.battery.status.max_discharge_power_W = 0;
} else {
discharge_limit_failures++;
clear_event(EVENT_BATTERY_EMPTY);
battery_empty_event_fired = false;
}
} else {
clear_event(EVENT_DISCHARGE_LIMIT_EXCEEDED);
discharge_limit_failures = 0;
}
}
// Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error
if (!datalayer.battery.status.CAN_battery_still_alive) {
set_event(EVENT_CAN_BATTERY_MISSING, can_config.battery);
} else {
datalayer.battery.status.CAN_battery_still_alive--;
clear_event(EVENT_CAN_BATTERY_MISSING);
}
// Battery is extremely degraded, not fit for secondlifestorage!
if (datalayer.battery.status.soh_pptt < 2500) {
set_event(EVENT_SOH_LOW, datalayer.battery.status.soh_pptt);
} else {
clear_event(EVENT_SOH_LOW);
}
// Too many malformed CAN messages recieved!
if (datalayer.battery.status.CAN_error_counter > MAX_CAN_FAILURES) {
set_event(EVENT_CAN_CORRUPTED_WARNING, can_config.battery);
} else {
clear_event(EVENT_CAN_CORRUPTED_WARNING);
if (battery && !battery->soc_plausible()) {
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc);
}
// Check diff between highest and lowest cell
cell_deviation_mV =
std::abs(datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV);
if (cell_deviation_mV > datalayer.battery.info.max_cell_voltage_deviation_mV) {
set_event(EVENT_CELL_DEVIATION_HIGH, (cell_deviation_mV / 20));
} else {
clear_event(EVENT_CELL_DEVIATION_HIGH);
}
// Inverter is charging with more power than battery wants!
if (datalayer.battery.status.active_power_W > 0) { // Charging
if (datalayer.battery.status.active_power_W > (datalayer.battery.status.max_charge_power_W + 2000)) {
if (charge_limit_failures > MAX_CHARGE_DISCHARGE_LIMIT_FAILURES) {
set_event(EVENT_CHARGE_LIMIT_EXCEEDED, 0); // Alert when 2kW over requested max
} else {
charge_limit_failures++;
}
} else {
clear_event(EVENT_CHARGE_LIMIT_EXCEEDED);
charge_limit_failures = 0;
}
}
// Inverter is pulling too much power from battery!
if (datalayer.battery.status.active_power_W < 0) { // Discharging
if (-datalayer.battery.status.active_power_W > (datalayer.battery.status.max_discharge_power_W + 2000)) {
if (discharge_limit_failures > MAX_CHARGE_DISCHARGE_LIMIT_FAILURES) {
set_event(EVENT_DISCHARGE_LIMIT_EXCEEDED, 0); // Alert when 2kW over requested max
} else {
discharge_limit_failures++;
}
} else {
clear_event(EVENT_DISCHARGE_LIMIT_EXCEEDED);
discharge_limit_failures = 0;
}
}
// Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error
if (!datalayer.battery.status.CAN_battery_still_alive) {
set_event(EVENT_CAN_BATTERY_MISSING, can_config.battery);
} else {
datalayer.battery.status.CAN_battery_still_alive--;
clear_event(EVENT_CAN_BATTERY_MISSING);
}
// Too many malformed CAN messages recieved!
if (datalayer.battery.status.CAN_error_counter > MAX_CAN_FAILURES) {
set_event(EVENT_CAN_CORRUPTED_WARNING, can_config.battery);
} else {
clear_event(EVENT_CAN_CORRUPTED_WARNING);
}
}
if (inverter && inverter->interface_type() == InverterInterfaceType::Can) {

View file

@ -121,6 +121,7 @@ void init_events(void) {
events.entries[EVENT_RJXZS_LOG].level = EVENT_LEVEL_INFO;
events.entries[EVENT_PAUSE_BEGIN].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_PAUSE_END].level = EVENT_LEVEL_INFO;
events.entries[EVENT_PID_FAILED].level = EVENT_LEVEL_INFO;
events.entries[EVENT_WIFI_CONNECT].level = EVENT_LEVEL_INFO;
events.entries[EVENT_WIFI_DISCONNECT].level = EVENT_LEVEL_INFO;
events.entries[EVENT_MQTT_CONNECT].level = EVENT_LEVEL_INFO;
@ -351,6 +352,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
return "The emulator is trying to pause the battery.";
case EVENT_PAUSE_END:
return "The emulator is attempting to resume battery operation from pause.";
case EVENT_PID_FAILED:
return "Failed to write PID request to battery";
case EVENT_WIFI_CONNECT:
return "Wifi connected.";
case EVENT_WIFI_DISCONNECT:

View file

@ -95,6 +95,7 @@
XX(EVENT_RJXZS_LOG) \
XX(EVENT_PAUSE_BEGIN) \
XX(EVENT_PAUSE_END) \
XX(EVENT_PID_FAILED) \
XX(EVENT_WIFI_CONNECT) \
XX(EVENT_WIFI_DISCONNECT) \
XX(EVENT_MQTT_CONNECT) \

Some files were not shown because too many files have changed in this diff Show more