Merge branch 'main' into feature/foxess-inverter

This commit is contained in:
Daniel Öster 2024-10-07 21:59:40 +03:00 committed by GitHub
commit adcf145109
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
49 changed files with 584 additions and 294 deletions

View file

@ -11,6 +11,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "src/charger/CHARGERS.h"
#include "src/datalayer/datalayer.h"
#include "src/devboard/utils/events.h"
#include "src/devboard/utils/led_handler.h"
#include "src/devboard/utils/value_mapping.h"
@ -23,8 +24,6 @@
#include "src/lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "src/datalayer/datalayer.h"
#ifdef WIFI
#include "src/devboard/wifi/wifi.h"
#ifdef WEBSERVER
@ -48,9 +47,13 @@
#endif
#endif
#ifdef EQUIPMENT_STOP_BUTTON
#include "src/devboard/utils/debounce_button.h"
#endif
Preferences settings; // Store user settings
// The current software version, shown on webserver
const char* version_number = "7.4.0";
const char* version_number = "7.5.dev";
// Interval settings
uint16_t intervalUpdateValues = INTERVAL_5_S; // Interval at which to update inverter values / Modbus registers
@ -64,7 +67,7 @@ volatile bool send_ok = 0;
#ifdef DUAL_CAN
#include "src/lib/pierremolinaro-acan2515/ACAN2515.h"
static const uint32_t QUARTZ_FREQUENCY = 8UL * 1000UL * 1000UL; // 8 MHz
static const uint32_t QUARTZ_FREQUENCY = CRYSTAL_FREQUENCY_MHZ * 1000000UL; //MHZ configured in USER_SETTINGS.h
ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);
static ACAN2515_Buffer16 gBuffer;
#endif
@ -135,6 +138,14 @@ unsigned long negativeStartTime = 0;
unsigned long timeSpentInFaultedMode = 0;
#endif
#ifdef EQUIPMENT_STOP_BUTTON
const unsigned long equipment_button_long_press_duration =
15000; // 15 seconds for long press in case of MOMENTARY_SWITCH
const unsigned long equipment_button_debounce_duration = 200; // 250ms for debouncing the button
unsigned long timeSincePress = 0; // Variable to store the time since the last press
DebouncedButton equipment_stop_button; // Debounced button object
#endif
TaskHandle_t main_loop_task;
TaskHandle_t connectivity_loop_task;
@ -163,6 +174,9 @@ void setup() {
init_battery();
#ifdef EQUIPMENT_STOP_BUTTON
init_equipment_stop_button();
#endif
// BOOT button at runtime is used as an input for various things
pinMode(0, INPUT_PULLUP);
@ -235,6 +249,10 @@ void core_loop(void* task_time_us) {
while (true) {
START_TIME_MEASUREMENT(all);
START_TIME_MEASUREMENT(comm);
#ifdef EQUIPMENT_STOP_BUTTON
monitor_equipment_stop_button();
#endif
// Input, Runs as fast as possible
receive_can_native(); // Receive CAN messages from native CAN port
#ifdef CAN_FD
@ -334,10 +352,21 @@ void init_serial() {
}
void init_stored_settings() {
static uint32_t temp = 0;
settings.begin("batterySettings", false);
// Always get the equipment stop status
datalayer.system.settings.equipment_stop_active = settings.getBool("EQUIPMENT_STOP", false);
if (datalayer.system.settings.equipment_stop_active) {
set_event(EVENT_EQUIPMENT_STOP, 1);
}
#ifndef LOAD_SAVED_SETTINGS_ON_BOOT
settings.clear(); // If this clear function is executed, no settings will be read from storage
//always save the equipment stop status
settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active);
#endif
#ifdef WIFI
@ -356,7 +385,6 @@ void init_stored_settings() {
}
#endif
static uint32_t temp = 0;
temp = settings.getUInt("BATTERY_WH_MAX", false);
if (temp != 0) {
datalayer.battery.info.total_capacity_Wh = temp;
@ -547,6 +575,43 @@ void init_battery() {
#endif
}
#ifdef EQUIPMENT_STOP_BUTTON
void monitor_equipment_stop_button() {
ButtonState changed_state = debounceButton(equipment_stop_button, timeSincePress);
if (equipment_stop_behavior == LATCHING_SWITCH) {
if (changed_state == PRESSED) {
// Changed to ON initiating equipment stop.
setBatteryPause(true, true, true);
} else if (changed_state == RELEASED) {
// Changed to OFF ending equipment stop.
setBatteryPause(false, false, false);
}
} else if (equipment_stop_behavior == MOMENTARY_SWITCH) {
if (changed_state == RELEASED) { // button is released
if (timeSincePress < equipment_button_long_press_duration) {
// Short press detected, trigger equipment stop
setBatteryPause(true, true, true);
} else {
// Long press detected, reset equipment stop state
setBatteryPause(false, false, false);
}
}
}
}
void init_equipment_stop_button() {
//using external pullup resistors NC
pinMode(EQUIPMENT_STOP_PIN, INPUT);
// Initialize the debounced button with NC switch type and equipment_button_debounce_duration debounce time
initDebouncedButton(equipment_stop_button, EQUIPMENT_STOP_PIN, NC, equipment_button_debounce_duration);
}
#endif
#ifdef CAN_FD
// Functions
#ifdef DEBUG_CANFD_DATA
@ -581,6 +646,7 @@ void receive_canfd() { // This section checks if we have a complete CAN-FD mess
}
//message incoming, pass it on to the handler
receive_can(&rx_frame, CAN_ADDON_FD_MCP2518);
receive_can(&rx_frame, CANFD_NATIVE);
}
}
#endif
@ -672,9 +738,14 @@ void handle_contactors() {
timeSpentInFaultedMode = 0;
}
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS) {
//handle contactor control SHUTDOWN_REQUESTED vs DISCONNECTED
if (timeSpentInFaultedMode > MAX_ALLOWED_FAULT_TICKS ||
(datalayer.system.settings.equipment_stop_active && contactorStatus != SHUTDOWN_REQUESTED)) {
contactorStatus = SHUTDOWN_REQUESTED;
}
if (contactorStatus == SHUTDOWN_REQUESTED && !datalayer.system.settings.equipment_stop_active) {
contactorStatus = DISCONNECTED;
}
if (contactorStatus == SHUTDOWN_REQUESTED) {
digitalWrite(PRECHARGE_PIN, LOW);
#ifndef PWM_CONTACTOR_CONTROL
@ -846,6 +917,12 @@ void init_serialDataLink() {
#endif
}
void store_settings_equipment_stop() {
settings.begin("batterySettings", false);
settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active);
settings.end();
}
void storeSettings() {
settings.begin("batterySettings", false);
#ifdef WIFI

View file

@ -48,6 +48,13 @@ const char* mqtt_password = "REDACTED"; // Set NULL for no password
#endif // USE_MQTT
#endif // WIFI
#ifdef EQUIPMENT_STOP_BUTTON
// Equipment stop button behavior. Use NC button for safety reasons.
//LATCHING_SWITCH - Normally closed (NC), latching switch. When pressed it activates e-stop
//MOMENTARY_SWITCH - Short press to activate e-stop, long 15s press to deactivate. E-stop is persistent between reboots
volatile STOP_BUTTON_BEHAVIOR equipment_stop_behavior = LATCHING_SWITCH;
#endif
/* Charger settings (Optional, when using generator charging) */
volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack
volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger

View file

@ -53,6 +53,7 @@
//#define CONTACTOR_CONTROL //Enable this line to have pins 25,32,33 handle automatic precharge/contactor+/contactor- closing sequence
//#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM for CONTACTOR_CONTROL, which lowers power consumption and heat generation. CONTACTOR_CONTROL must be enabled.
//#define DUAL_CAN //Enable this line to activate an isolated secondary CAN Bus using add-on MCP2515 chip (Needed for some inverters / double battery)
#define CRYSTAL_FREQUENCY_MHZ 8 //DUAL_CAN option, what is your MCP2515 add-on boards crystal frequency?
//#define CAN_FD //Enable this line to activate an isolated secondary CAN-FD bus using add-on MCP2518FD chip / Native CANFD on Stark board
//#define USE_CANFD_INTERFACE_AS_CLASSIC_CAN // Enable this line if you intend to use the CANFD as normal CAN
//#define SERIAL_LINK_RECEIVER //Enable this line to receive battery data over RS485 pins from another Lilygo (This LilyGo interfaces with inverter)
@ -61,11 +62,12 @@
//#define WIFICONFIG //Enable this line to set a static IP address / gateway /subnet mask for the device. see USER_SETTINGS.cpp for the settings
#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
#define WEBSERVER_AUTH_REQUIRED \
false //Enable this line to enable webserver authentication. See USER_SETTINGS.cpp setting the credentials.
false //Set this line to true to activate webserver authentication (this line must not be commented). Refer to USER_SETTINGS.cpp for setting the credentials.
#define WIFIAP //Disable this line to permanently disable WIFI AP mode (make sure to hardcode ssid and password of you home wifi network). When enabled WIFI AP can still be disabled by a setting in the future.
#define MDNSRESPONDER //Enable this line to enable MDNS, allows battery monitor te be found by .local address. Requires WEBSERVER to be enabled.
#define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot (overrides Wifi/battery settings set below)
//#define FUNCTION_TIME_MEASUREMENT // Enable this to record execution times and present them in the web UI (WARNING, raises CPU load, do not use for production)
//#define EQUIPMENT_STOP_BUTTON // Enable this to allow an equipment stop button connected to the Battery-Emulator to disengage the battery
/* MQTT options */
// #define MQTT // Enable this line to enable MQTT
@ -120,6 +122,11 @@ extern volatile float CHARGER_END_A;
extern bool charger_HV_enabled;
extern bool charger_aux12V_enabled;
#ifdef EQUIPMENT_STOP_BUTTON
typedef enum { LATCHING_SWITCH = 0, MOMENTARY_SWITCH = 1 } STOP_BUTTON_BEHAVIOR;
extern volatile STOP_BUTTON_BEHAVIOR equipment_stop_behavior;
#endif
#ifdef WIFICONFIG
extern IPAddress local_IP;
// Set your Gateway IP address

View file

@ -395,8 +395,37 @@ void update_values_battery2() { //This function maps all the values fetched via
datalayer.battery2.status.temperature_max_dC = battery2_temperature_max * 10; // Add a decimal
datalayer.battery2.status.cell_min_voltage_mV = datalayer.battery2.status.cell_voltages_mV[0];
datalayer.battery2.status.cell_max_voltage_mV = datalayer.battery2.status.cell_voltages_mV[1];
if (battery2_info_available) {
// Start checking safeties. First up, cellvoltages!
if (detectedBattery == BATTERY_60AH) {
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_60AH;
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_60AH;
} else if (detectedBattery == BATTERY_94AH) {
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_94AH;
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_94AH;
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_94AH;
} else { // BATTERY_120AH
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_120AH;
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_120AH;
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_120AH;
}
}
// Perform other safety checks
if (battery2_status_error_locking == 2) { // HVIL seated?
set_event(EVENT_HVIL_FAILURE, 2);
} else {
clear_event(EVENT_HVIL_FAILURE);
}
if (battery2_status_precharge_locked == 2) { // Capacitor seated?
set_event(EVENT_PRECHARGE_FAILURE, 2);
} else {
clear_event(EVENT_PRECHARGE_FAILURE);
}
}
void update_values_battery() { //This function maps all the values fetched via CAN to the battery datalayer
@ -433,30 +462,18 @@ void update_values_battery() { //This function maps all the values fetched via
if (detectedBattery == BATTERY_60AH) {
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_60AH) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_60AH) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_60AH;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_60AH;
} else if (detectedBattery == BATTERY_94AH) {
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_94AH;
if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_94AH) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_94AH) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_94AH;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_94AH;
} else { // BATTERY_120AH
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_120AH;
if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_120AH) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_120AH) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_120AH;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_120AH;
}
}
@ -1124,13 +1141,14 @@ void setup_battery(void) { // Performs one time setup at startup
//Before we have started up and detected which battery is in use, use 60AH values
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
datalayer.system.status.battery_allows_contactor_closing = true;
#ifdef DOUBLE_BATTERY
Serial.println("Another BMW i3 battery also selected!");
datalayer.battery2.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV;
datalayer.battery2.info.min_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV;
datalayer.battery2.info.max_cell_voltage_deviation_mV = datalayer.battery.info.max_cell_voltage_deviation_mV;
datalayer.battery2.status.voltage_dV =
0; //Init voltage to 0 to allow contactor check to operate without fear of default values colliding
#endif

View file

@ -135,11 +135,11 @@ void update_values_battery() { //This function maps all the values fetched via
}
void receive_can_battery(CAN_frame rx_frame) {
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
switch (rx_frame.ID) { //Log values taken with 422V from battery
case 0x244: //00,00,00,04,41,0F,20,8B - Static, values never changes between logs
break;
case 0x245: //01,00,02,19,3A,25,90,F4 Seems to have a mux in frame0
datalayer.battery.status.CAN_battery_still_alive = CAN_STILL_ALIVE;
//02,00,90,01,79,79,90,EA // Point of interest, went from 7E,75 to 7B,7C when discharging
//03,C6,88,12,FD,48,90,5C
//04,00,FF,FF,00,00,90,6D
@ -371,9 +371,13 @@ void setup_battery(void) { // Performs one time setup at startup
#ifdef DEBUG_VIA_USB
Serial.println("BYD Atto 3 battery selected");
#endif
datalayer.battery.info.max_design_voltage_dV = 4410; // Over this charging is not possible
datalayer.battery.info.min_design_voltage_dV = 3800; // Under this discharging is disabled
datalayer.battery.info.number_of_cells = 126;
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
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

View file

@ -4,7 +4,11 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4410 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3800
#define MAX_CELL_DEVIATION_MV 150
#define MAX_CELL_VOLTAGE_MV 3800 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2800 //Battery is put into emergency stop if one cell goes below this value
void setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -4,7 +4,6 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_CELL_DEVIATION_MV 9999
//Contactor control is required for CHADEMO support
#define CONTACTOR_CONTROL

View file

@ -8,8 +8,6 @@
//Figure out if CAN messages need to be sent to keep the system happy?
/* Do not change code below unless you are sure what you are doing */
#define MAX_CELL_VOLTAGE 4150
#define MIN_CELL_VOLTAGE 2750
static uint8_t errorCode = 0; //stores if we have an error code active from battery control logic
static uint8_t BMU_Detected = 0;
static uint8_t CMU_Detected = 0;
@ -106,14 +104,6 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.temperature_max_dC = (int16_t)(max_temp_cel * 10);
}
//Check safeties
if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, datalayer.battery.status.cell_max_voltage_mV);
}
if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, datalayer.battery.status.cell_min_voltage_mV);
}
if (!BMU_Detected) {
#ifdef DEBUG_VIA_USB
Serial.println("BMU not detected, check wiring!");
@ -239,9 +229,11 @@ void setup_battery(void) { // Performs one time setup at startup
#ifdef DEBUG_VIA_USB
Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected");
#endif
datalayer.battery.info.max_design_voltage_dV = 3696; // 369.6V
datalayer.battery.info.min_design_voltage_dV = 3160; // 316.0V
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

View file

@ -4,7 +4,11 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 3696 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3160
#define MAX_CELL_DEVIATION_MV 500
#define MAX_CELL_VOLTAGE_MV 4150 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_MV 2750 //Battery is put into emergency stop if one cell goes below this value
void setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -263,8 +263,11 @@ void setup_battery(void) { // Performs one time setup at startup
#endif
datalayer.battery.info.number_of_cells = 108;
datalayer.battery.info.max_design_voltage_dV = 4546;
datalayer.battery.info.min_design_voltage_dV = 3370;
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.system.status.battery_allows_contactor_closing = true;
}

View file

@ -3,7 +3,11 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_CELL_DEVIATION_MV 9999 // TODO is this ok ?
#define MAX_PACK_VOLTAGE_DV 4546 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3370
#define MAX_CELL_DEVIATION_MV 500
#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
void setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -15,9 +15,6 @@ static unsigned long previousMillis500ms = 0; // will store last time a 500ms C
static unsigned long previousMillis1s = 0; // will store last time a 1s CAN Message was send
static unsigned long previousMillis10s = 0; // will store last time a 10s CAN Message was send
#define MAX_CELL_VOLTAGE 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE 2950 //Battery is put into emergency stop if one cell goes below this value
const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0,
@ -864,14 +861,6 @@ void update_values_battery() { //This function maps all the values fetched via
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
}
// Check if cell voltages are within allowed range
if (CellVoltMax_mV >= MAX_CELL_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (CellVoltMin_mV <= MIN_CELL_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
/* Safeties verified. Perform USB serial printout if configured to do so */
#ifdef DEBUG_VIA_USB
@ -1239,11 +1228,10 @@ void setup_battery(void) { // Performs one time setup at startup
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 192; // TODO: will vary depending on battery
datalayer.battery.info.max_design_voltage_dV =
8064; // TODO: define when battery is known, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV =
4320; // TODO: define when battery is known. discharging further is disabled
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;
}
#endif

View file

@ -7,7 +7,11 @@
extern ACAN2517FD canfd;
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 8064 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 4320
#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 2950 //Battery is put into emergency stop if one cell goes below this value
#define MAXCHARGEPOWERALLOWED 10000
#define MAXDISCHARGEPOWERALLOWED 10000

View file

@ -8,9 +8,6 @@
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis10 = 0; // will store last time a 10s CAN Message was send
#define MAX_CELL_VOLTAGE 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE 2950 //Battery is put into emergency stop if one cell goes below this value
static uint16_t soc_calculated = 0;
static uint16_t SOC_BMS = 0;
static uint16_t SOC_Display = 0;
@ -147,14 +144,6 @@ void update_values_battery() { //This function maps all the values fetched via
set_event(EVENT_12V_LOW, leadAcidBatteryVoltage);
}
// Check if cell voltages are within allowed range
if (CellVoltMax_mV >= MAX_CELL_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (CellVoltMin_mV <= MIN_CELL_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
/* Safeties verified. Perform USB serial printout if configured to do so */
#ifdef DEBUG_VIA_USB
@ -223,12 +212,12 @@ void update_number_of_cells() {
// Check if we have 98S or 90S battery
if (datalayer.battery.status.cell_voltages_mV[97] > 0) {
datalayer.battery.info.number_of_cells = 98;
datalayer.battery.info.max_design_voltage_dV = 4040;
datalayer.battery.info.min_design_voltage_dV = 3100;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_98S_DV;
} else {
datalayer.battery.info.number_of_cells = 90;
datalayer.battery.info.max_design_voltage_dV = 3870;
datalayer.battery.info.min_design_voltage_dV = 2250;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_90S_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV;
}
}
}
@ -550,9 +539,10 @@ void setup_battery(void) { // Performs one time setup at startup
#ifdef DEBUG_VIA_USB
Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected");
#endif
datalayer.battery.info.max_design_voltage_dV = 4040; // 404.0V
datalayer.battery.info.min_design_voltage_dV = 3100; // 310.0V
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
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

@ -4,7 +4,13 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_98S_DV 4070 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_98S_DV 2800
#define MAX_PACK_VOLTAGE_90S_DV 3870
#define MIN_PACK_VOLTAGE_90S_DV 2250
#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 2950 //Battery is put into emergency stop if one cell goes below this value
void setup_battery(void);
void update_number_of_cells();

View file

@ -265,8 +265,10 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Kia/Hyundai Hybrid battery selected");
#endif
datalayer.battery.info.number_of_cells = 56; // HEV , TODO: Make dynamic according to HEV/PHEV
datalayer.battery.info.max_design_voltage_dV = 2550; //TODO: Values OK?
datalayer.battery.info.min_design_voltage_dV = 1700;
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;
}
#endif

View file

@ -4,7 +4,11 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 2550 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 1700
#define MAX_CELL_DEVIATION_MV 100
#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
void setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -141,8 +141,10 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("MG 5 battery selected");
#endif
datalayer.battery.info.max_design_voltage_dV = 4040; // Over this charging is not possible
datalayer.battery.info.min_design_voltage_dV = 3100; // Under this discharging is disabled
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;
}
#endif

View file

@ -4,7 +4,11 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4040 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3100
#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
void setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -75,8 +75,6 @@ static uint8_t crctable[256] = {
#define ZE1_BATTERY 2
static uint8_t LEAF_battery_Type = ZE0_BATTERY;
static bool battery_can_alive = false;
#define MAX_CELL_VOLTAGE 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE 2700 //Battery is put into emergency stop if one cell goes below this value
#define WH_PER_GID 77 //One GID is this amount of Watt hours
static uint16_t battery_Discharge_Power_Limit = 0; //Limit in kW
static uint16_t battery_Charge_Power_Limit = 0; //Limit in kW
@ -634,12 +632,6 @@ void receive_can_battery2(CAN_frame rx_frame) {
datalayer.battery2.status.cell_max_voltage_mV = battery2_min_max_voltage[1];
datalayer.battery2.status.cell_min_voltage_mV = battery2_min_max_voltage[0];
if (battery2_min_max_voltage[1] >= MAX_CELL_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (battery2_min_max_voltage[0] <= MIN_CELL_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
break;
}
@ -884,12 +876,6 @@ void receive_can_battery(CAN_frame rx_frame) {
datalayer.battery.status.cell_max_voltage_mV = battery_min_max_voltage[1];
datalayer.battery.status.cell_min_voltage_mV = battery_min_max_voltage[0];
if (battery_min_max_voltage[1] >= MAX_CELL_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (battery_min_max_voltage[0] <= MIN_CELL_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
break;
}
@ -1221,13 +1207,19 @@ void setup_battery(void) { // Performs one time setup at startup
#endif
datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.max_design_voltage_dV = 4040; // 404.4V
datalayer.battery.info.min_design_voltage_dV = 2600; // 260.0V
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;
#ifdef DOUBLE_BATTERY
datalayer.battery2.info.number_of_cells = datalayer.battery.info.number_of_cells;
datalayer.battery2.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV;
datalayer.battery2.info.min_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV;
datalayer.battery2.info.max_cell_voltage_mV = datalayer.battery.info.max_cell_voltage_mV;
datalayer.battery2.info.min_cell_voltage_mV = datalayer.battery.info.min_cell_voltage_mV;
datalayer.battery2.info.max_cell_voltage_deviation_mV = datalayer.battery.info.max_cell_voltage_deviation_mV;
#endif //DOUBLE_BATTERY
}

View file

@ -4,7 +4,11 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4040 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2600
#define MAX_CELL_DEVIATION_MV 500
#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
uint16_t Temp_fromRAW_to_F(uint16_t temperature);
bool is_message_corrupt(CAN_frame rx_frame);

View file

@ -4,10 +4,6 @@
#include "../devboard/utils/events.h"
#include "PYLON-BATTERY.h"
/* Change the following to suit your battery */
#define MAX_PACK_VOLTAGE 5000 //5000 = 500.0V
#define MIN_PACK_VOLTAGE 1500
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis1000 = 0; // will store last time a 1s CAN Message was sent
@ -183,8 +179,10 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Pylon battery selected");
#endif
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE;
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;
}
#endif

View file

@ -4,7 +4,13 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_CELL_DEVIATION_MV 9999
/* Change the following to suit your battery */
#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 1500
#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
#define MAX_CELL_DEVIATION_MV 500
void setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -106,13 +106,6 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.cell_max_voltage_mV = LB_Cell_Max_Voltage;
if (LB_Cell_Max_Voltage >= ABSOLUTE_CELL_MAX_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, (LB_Cell_Max_Voltage / 20));
}
if (LB_Cell_Min_Voltage <= ABSOLUTE_CELL_MIN_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, (LB_Cell_Min_Voltage / 20));
}
#ifdef DEBUG_VIA_USB
Serial.println("Values going to inverter:");
Serial.print("SOH%: ");
@ -248,9 +241,11 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Renault Kangoo battery selected");
#endif
datalayer.battery.info.max_design_voltage_dV =
4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
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

View file

@ -4,10 +4,11 @@
#include "../include.h"
#define BATTERY_SELECTED
#define ABSOLUTE_CELL_MAX_VOLTAGE 4150 // If cellvoltage goes over this mV, we go into FAULT mode
#define ABSOLUTE_CELL_MIN_VOLTAGE 2500 // If cellvoltage goes under this mV, we go into FAULT mode
#define MAX_CELL_DEVIATION_MV 500 // If cell mV delta exceeds this, we go into WARNING mode
#define MAX_PACK_VOLTAGE_DV 4150 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2500
#define MAX_CELL_DEVIATION_MV 500
#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
#define MAX_CHARGE_POWER_W 5000 // Battery can be charged with this amount of power
void setup_battery(void);

View file

@ -148,15 +148,7 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.cell_min_voltage_mV = min_cell_mv_value;
datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value;
datalayer.battery.status.voltage_dV =
static_cast<uint32_t>((calculated_total_pack_voltage_mV / 100)); // Convert from mV to dV
if (datalayer.battery.status.cell_max_voltage_mV >= ABSOLUTE_CELL_MAX_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (datalayer.battery.status.cell_min_voltage_mV <= ABSOLUTE_CELL_MIN_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
datalayer.battery.status.voltage_dV = static_cast<uint32_t>((calculated_total_pack_voltage_mV / 100)); // mV to dV
#ifdef DEBUG_VIA_USB
@ -535,8 +527,11 @@ void setup_battery(void) { // Performs one time setup at startup
#endif
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.max_design_voltage_dV = 4040;
datalayer.battery.info.min_design_voltage_dV = 2700;
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

View file

@ -3,10 +3,12 @@
#include "../include.h"
#define BATTERY_SELECTED
#define ABSOLUTE_CELL_MAX_VOLTAGE 4200
#define ABSOLUTE_CELL_MIN_VOLTAGE 3000
#define MAX_CELL_DEVIATION_MV 500
#define MAX_PACK_VOLTAGE_DV 4200 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3000
#define MAX_CELL_DEVIATION_MV 500
#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
void setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -59,13 +59,6 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.cell_max_voltage_mV;
if (LB_Cell_Max_Voltage >= ABSOLUTE_CELL_MAX_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (LB_Cell_Min_Voltage <= ABSOLUTE_CELL_MIN_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
#ifdef DEBUG_VIA_USB
#endif
@ -99,9 +92,13 @@ void setup_battery(void) { // Performs one time setup at startup
#ifdef DEBUG_VIA_USB
Serial.println("Renault Zoe 50kWh battery selected");
#endif
datalayer.battery.info.max_design_voltage_dV = 4040;
datalayer.battery.info.min_design_voltage_dV = 3100;
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 96;
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

View file

@ -3,10 +3,11 @@
#include "../include.h"
#define BATTERY_SELECTED
#define ABSOLUTE_CELL_MAX_VOLTAGE 4100
#define ABSOLUTE_CELL_MIN_VOLTAGE 3000
#define MAX_PACK_VOLTAGE_DV 4100 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 3000
#define MAX_CELL_DEVIATION_MV 500
#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
void setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -545,8 +545,10 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("RJXZS BMS selected");
#endif
datalayer.battery.info.max_design_voltage_dV = 5000;
datalayer.battery.info.min_design_voltage_dV = 1000;
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;
}
#endif // RJXZS_BMS

View file

@ -4,6 +4,10 @@
#include "../include.h"
/* Tweak these according to your battery build */
#define MAX_PACK_VOLTAGE_DV 5000 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 1500
#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
#define MAX_CELL_DEVIATION_MV 250
#define MAX_DISCHARGE_POWER_ALLOWED_W 5000
#define MAX_CHARGE_POWER_ALLOWED_W 5000

View file

@ -410,8 +410,10 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Hyundai Santa Fe PHEV battery selected");
#endif
datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.max_design_voltage_dV = 4040;
datalayer.battery.info.min_design_voltage_dV = 2880;
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;
}
#endif

View file

@ -4,7 +4,11 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4040 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2880
#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
uint8_t CalculateCRC8(CAN_frame rx_frame);
void setup_battery(void);

View file

@ -4,9 +4,6 @@
#define SERIAL_LINK_RECEIVER_FROM_BATTERY_H
#define BATTERY_SELECTED
#ifndef MAX_CELL_DEVIATION_MV
#define MAX_CELL_DEVIATION_MV 9999
#endif
#include "../include.h"
#include "../lib/mackelec-SerialDataLink/SerialDataLink.h"

View file

@ -253,16 +253,6 @@ static const char* hvilStatusState[] = {"NOT OK",
"UNKNOWN(14)",
"UNKNOWN(15)"};
#define MAX_CELL_VOLTAGE_NCA_NCM 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_NCA_NCM 2950 //Battery is put into emergency stop if one cell goes below this value
#define MAX_CELL_DEVIATION_NCA_NCM 500 //LED turns yellow on the board if mv delta exceeds this value
#define MAX_CELL_VOLTAGE_LFP 3550 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_LFP 2800 //Battery is put into emergency stop if one cell goes below this value
#define MAX_CELL_DEVIATION_LFP 200 //LED turns yellow on the board if mv delta exceeds this value
#define REASONABLE_ENERGYAMOUNT 20 //When the BMS stops making sense on some values, they are always <20
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
//After values are mapped, we perform some safety checks, and do some serial printouts
@ -316,6 +306,8 @@ void update_values_battery() { //This function maps all the values fetched via
datalayer.battery.status.cell_min_voltage_mV = battery_cell_min_v;
battery_cell_deviation_mV = (battery_cell_max_v - battery_cell_min_v);
/* Value mapping is completed. Start to check all safeties */
if (battery_hvil_status ==
@ -325,8 +317,6 @@ void update_values_battery() { //This function maps all the values fetched via
clear_event(EVENT_INTERNAL_OPEN_FAULT);
}
battery_cell_deviation_mV = (battery_cell_max_v - battery_cell_min_v);
#ifdef TESLA_MODEL_3Y_BATTERY
// Autodetect algoritm for chemistry on 3/Y packs.
// NCM/A batteries have 96s, LFP has 102-106s
@ -339,49 +329,18 @@ void update_values_battery() { //This function maps all the values fetched via
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_LFP;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_LFP;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_LFP;
} else { // NCM/A chemistry
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
}
#endif // TESLA_MODEL_3Y_BATTERY
//Check if SOC% is plausible
if (datalayer.battery.status.voltage_dV >
(datalayer.battery.info.max_design_voltage_dV -
20)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
if (datalayer.battery.status.real_soc < 5000) { //When SOC is less than 50.00% when approaching max voltage
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc / 100);
}
}
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { //LFP limits used for voltage safeties
if (battery_cell_max_v >= MAX_CELL_VOLTAGE_LFP) {
set_event(EVENT_CELL_OVER_VOLTAGE, (battery_cell_max_v - MAX_CELL_VOLTAGE_LFP));
}
if (battery_cell_min_v <= MIN_CELL_VOLTAGE_LFP) {
set_event(EVENT_CELL_UNDER_VOLTAGE, (MIN_CELL_VOLTAGE_LFP - battery_cell_min_v));
}
if (battery_cell_deviation_mV > MAX_CELL_DEVIATION_LFP) {
set_event(EVENT_CELL_DEVIATION_HIGH, battery_cell_deviation_mV);
} else {
clear_event(EVENT_CELL_DEVIATION_HIGH);
}
} else { //NCA/NCM limits used
if (battery_cell_max_v >= MAX_CELL_VOLTAGE_NCA_NCM) {
set_event(EVENT_CELL_OVER_VOLTAGE, (battery_cell_max_v - MAX_CELL_VOLTAGE_NCA_NCM));
}
if (battery_cell_min_v <= MIN_CELL_VOLTAGE_NCA_NCM) {
set_event(EVENT_CELL_UNDER_VOLTAGE, (MIN_CELL_VOLTAGE_NCA_NCM - battery_cell_min_v));
}
if (battery_cell_deviation_mV > MAX_CELL_DEVIATION_NCA_NCM) {
set_event(EVENT_CELL_DEVIATION_HIGH, battery_cell_deviation_mV);
} else {
clear_event(EVENT_CELL_DEVIATION_HIGH);
}
}
/* Safeties verified. Perform USB serial printout if configured to do so */
#ifdef DEBUG_VIA_USB
printFaultCodesIfActive();
@ -898,6 +857,8 @@ void update_values_battery2() { //This function maps all the values fetched via
datalayer.battery2.status.cell_min_voltage_mV = battery2_cell_min_v;
battery2_cell_deviation_mV = (battery2_cell_max_v - battery2_cell_min_v);
/* Value mapping is completed. Start to check all safeties */
if (battery2_hvil_status ==
@ -907,8 +868,6 @@ void update_values_battery2() { //This function maps all the values fetched via
clear_event(EVENT_INTERNAL_OPEN_FAULT);
}
battery2_cell_deviation_mV = (battery2_cell_max_v - battery2_cell_min_v);
#ifdef TESLA_MODEL_3Y_BATTERY
// Autodetect algoritm for chemistry on 3/Y packs.
// NCM/A batteries have 96s, LFP has 102-106s
@ -921,57 +880,18 @@ void update_values_battery2() { //This function maps all the values fetched via
if (datalayer.battery2.info.chemistry == battery_chemistry_enum::LFP) {
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP;
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_LFP;
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_LFP;
datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_LFP;
} else { // NCM/A chemistry
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA;
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM;
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
}
#endif // TESLA_MODEL_3Y_BATTERY
//Check if SOC% is plausible
if (datalayer.battery2.status.voltage_dV >
(datalayer.battery2.info.max_design_voltage_dV -
20)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
if (datalayer.battery2.status.real_soc < 5000) { //When SOC is less than 50.00% when approaching max voltage
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery2.status.real_soc / 100);
}
}
//Check if BMS is in need of recalibration
if (battery2_nominal_full_pack_energy > 1 && battery2_nominal_full_pack_energy < REASONABLE_ENERGYAMOUNT) {
set_event(EVENT_KWH_PLAUSIBILITY_ERROR, battery2_nominal_full_pack_energy);
} else if (battery2_nominal_full_pack_energy <= 1) {
set_event(EVENT_KWH_PLAUSIBILITY_ERROR, battery2_nominal_full_pack_energy);
}
if (datalayer.battery2.info.chemistry == battery_chemistry_enum::LFP) { //LFP limits used for voltage safeties
if (battery2_cell_max_v >= MAX_CELL_VOLTAGE_LFP) {
set_event(EVENT_CELL_OVER_VOLTAGE, (battery2_cell_max_v - MAX_CELL_VOLTAGE_LFP));
}
if (battery2_cell_min_v <= MIN_CELL_VOLTAGE_LFP) {
set_event(EVENT_CELL_UNDER_VOLTAGE, (MIN_CELL_VOLTAGE_LFP - battery2_cell_min_v));
}
if (battery2_cell_deviation_mV > MAX_CELL_DEVIATION_LFP) {
set_event(EVENT_CELL_DEVIATION_HIGH, battery2_cell_deviation_mV);
} else {
clear_event(EVENT_CELL_DEVIATION_HIGH);
}
} else { //NCA/NCM limits used
if (battery2_cell_max_v >= MAX_CELL_VOLTAGE_NCA_NCM) {
set_event(EVENT_CELL_OVER_VOLTAGE, (battery2_cell_max_v - MAX_CELL_VOLTAGE_NCA_NCM));
}
if (battery2_cell_min_v <= MIN_CELL_VOLTAGE_NCA_NCM) {
set_event(EVENT_CELL_UNDER_VOLTAGE, (MIN_CELL_VOLTAGE_NCA_NCM - battery2_cell_min_v));
}
if (battery2_cell_deviation_mV > MAX_CELL_DEVIATION_NCA_NCM) {
set_event(EVENT_CELL_DEVIATION_HIGH, battery2_cell_deviation_mV);
} else {
clear_event(EVENT_CELL_DEVIATION_HIGH);
}
}
/* Safeties verified. Perform USB serial printout if configured to do so */
#ifdef DEBUG_VIA_USB
printFaultCodesIfActive_battery2();
@ -1254,9 +1174,15 @@ void setup_battery(void) { // Performs one time setup at startup
#ifdef TESLA_MODEL_SX_BATTERY // Always use NCM/A mode on S/X packs
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
#ifdef DOUBLE_BATTERY
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA;
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM;
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
#endif // DOUBLE_BATTERY
#endif // TESLA_MODEL_SX_BATTERY
@ -1265,17 +1191,29 @@ void setup_battery(void) { // Performs one time setup at startup
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_LFP;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_LFP;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_LFP;
#ifdef DOUBLE_BATTERY
datalayer.battery2.info.chemistry = battery_chemistry_enum::LFP;
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP;
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_LFP;
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_LFP;
datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_LFP;
#endif // DOUBLE_BATTERY
#else // Startup in NCM/A mode
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA;
datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
#ifdef DOUBLE_BATTERY
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA;
datalayer.battery2.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_NCA_NCM;
datalayer.battery2.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_NCA_NCM;
datalayer.battery2.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_NCA_NCM;
#endif // DOUBLE_BATTERY
#endif // !LFP_CHEMISTRY
#endif // TESLA_MODEL_3Y_BATTERY

View file

@ -21,7 +21,12 @@
#define MIN_PACK_VOLTAGE_3Y_NCMA 3100 // V+1, if pack voltage goes below this, discharge stops
#define MAX_PACK_VOLTAGE_3Y_LFP 3880 // V+1, if pack voltage goes over this, charge stops
#define MIN_PACK_VOLTAGE_3Y_LFP 2968 // V+1, if pack voltage goes below this, discharge stops
#define MAX_CELL_DEVIATION_MV 9999 // Handled inside the Tesla.cpp file, just for compilation
#define MAX_CELL_DEVIATION_NCA_NCM 500 //LED turns yellow on the board if mv delta exceeds this value
#define MAX_CELL_DEVIATION_LFP 200 //LED turns yellow on the board if mv delta exceeds this value
#define MAX_CELL_VOLTAGE_NCA_NCM 4250 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_NCA_NCM 2950 //Battery is put into emergency stop if one cell goes below this value
#define MAX_CELL_VOLTAGE_LFP 3550 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE_LFP 2800 //Battery is put into emergency stop if one cell goes below this value
void printFaultCodesIfActive();
void printDebugIfActive(uint8_t symbol, const char* message);

View file

@ -8,9 +8,6 @@
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
static unsigned long previousMillis60s = 0; // will store last time a 60s CAN Message was send
#define MAX_CELL_VOLTAGE 4210 //Battery is put into emergency stop if one cell goes over this value
#define MIN_CELL_VOLTAGE 2700 //Battery is put into emergency stop if one cell goes below this value
static float BATT_U = 0; //0x3A
static float MAX_U = 0; //0x3A
static float MIN_U = 0; //0x3A
@ -22,8 +19,8 @@ static float BATT_T_MIN = 0; //0x413
static float BATT_T_AVG = 0; //0x413
static uint16_t SOC_BMS = 0; //0X37D
static uint16_t SOC_CALC = 0;
static uint16_t CELL_U_MAX = 0; //0x37D
static uint16_t CELL_U_MIN = 0; //0x37D
static uint16_t CELL_U_MAX = 3700; //0x37D
static uint16_t CELL_U_MIN = 3700; //0x37D
static uint8_t CELL_ID_U_MAX = 0; //0x37D
static uint16_t HvBattPwrLimDchaSoft = 0; //0x369
static uint8_t batteryModuleNumber = 0x10; // First battery module
@ -288,12 +285,6 @@ void receive_can_battery(CAN_frame rx_frame) {
min_max_voltage[1] = cell_voltages[cellcounter];
}
if (min_max_voltage[1] >= MAX_CELL_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
if (min_max_voltage[0] <= MIN_CELL_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
transmit_can(&VOLVO_SOH_Req, can_config.battery); //Send SOH read request
}
rxConsecutiveFrames = 0;
@ -347,8 +338,10 @@ void setup_battery(void) { // Performs one time setup at startup
#endif
datalayer.battery.info.number_of_cells = 108;
datalayer.battery.info.max_design_voltage_dV =
4540; // 454.0V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 2938; // 293.8V under this, discharging further is disabled
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

View file

@ -4,7 +4,11 @@
#include "../include.h"
#define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4540 //5000 = 500.0V
#define MIN_PACK_VOLTAGE_DV 2938
#define MAX_CELL_DEVIATION_MV 250
#define MAX_CELL_VOLTAGE_MV 4210 //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
void setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -13,6 +13,12 @@ typedef struct {
uint16_t max_design_voltage_dV = 5000;
/** The minimum intended packvoltage, in deciVolt. 3300 = 330.0 V */
uint16_t min_design_voltage_dV = 2500;
/** The maximum cellvoltage before shutting down, in milliVolt. 4300 = 4.250 V */
uint16_t max_cell_voltage_mV = 4300;
/** The minimum cellvoltage before shutting down, in milliVolt. 2700 = 2.700 V */
uint16_t min_cell_voltage_mV = 2700;
/** The maxumum allowed deviation between cells, in milliVolt. 500 = 0.500 V */
uint16_t max_cell_voltage_deviation_mV = 500;
/** BYD CAN specific setting, max charge in deciAmpere. 300 = 30.0 A */
uint16_t max_charge_amp_dA = BATTERY_MAX_CHARGE_AMP;
/** BYD CAN specific setting, max discharge in deciAmpere. 300 = 30.0 A */
@ -172,6 +178,7 @@ typedef struct {
} DATALAYER_SYSTEM_STATUS_TYPE;
typedef struct {
bool equipment_stop_active = false;
} DATALAYER_SYSTEM_SETTINGS_TYPE;
typedef struct {

View file

@ -65,6 +65,9 @@
#define LED_PIN 4
#define LED_MAX_BRIGHTNESS 40
// Equipment stop pin
#define EQUIPMENT_STOP_PIN 35
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
#ifndef HW_CONFIGURED
#define HW_CONFIGURED
@ -78,6 +81,18 @@
#endif
#endif
#ifdef EQUIPMENT_STOP_BUTTON
#ifdef DUAL_CAN
#error EQUIPMENT_STOP_BUTTON and DUAL_CAN cannot coexist due to overlapping GPIO pin usage
#endif
#ifdef CAN_FD
#error EQUIPMENT_STOP_BUTTON and CAN_FD cannot coexist due to overlapping GPIO pin usage
#endif
#ifdef CHADEMO_BATTERY
#error EQUIPMENT_STOP_BUTTON and CHADEMO_BATTERY cannot coexist due to overlapping GPIO pin usage
#endif
#endif
#ifdef BMW_I3_BATTERY
#ifdef CONTACTOR_CONTROL
#error GPIO PIN 25 cannot be used for both BMWi3 Wakeup and contactor control. Disable CONTACTOR_CONTROL

View file

@ -58,6 +58,9 @@ GPIOs on extra header
#define LED_PIN 4
#define LED_MAX_BRIGHTNESS 40
// Equipment stop pin
#define EQUIPMENT_STOP_PIN 2
/* ----- Error checks below, don't change (can't be moved to separate file) ----- */
#ifndef HW_CONFIGURED
#define HW_CONFIGURED

View file

@ -54,6 +54,15 @@ void update_machineryprotection() {
clear_event(EVENT_BATTERY_UNDERVOLTAGE);
}
// Cell overvoltage, critical latching error without automatic reset. Requires user action.
if (datalayer.battery.status.cell_max_voltage_mV >= datalayer.battery.info.max_cell_voltage_mV) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
// Cell undervoltage, critical latching error without automatic reset. Requires user action.
if (datalayer.battery.status.cell_min_voltage_mV <= datalayer.battery.info.min_cell_voltage_mV) {
set_event(EVENT_CELL_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%
@ -103,7 +112,7 @@ void update_machineryprotection() {
// Check diff between highest and lowest cell
cell_deviation_mV = (datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV);
if (cell_deviation_mV > MAX_CELL_DEVIATION_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);
@ -175,6 +184,23 @@ void update_machineryprotection() {
clear_event(EVENT_CAN_RX_WARNING);
}
// Cell overvoltage, critical latching error without automatic reset. Requires user action.
if (datalayer.battery2.status.cell_max_voltage_mV >= datalayer.battery2.info.max_cell_voltage_mV) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
}
// Cell undervoltage, critical latching error without automatic reset. Requires user action.
if (datalayer.battery2.status.cell_min_voltage_mV <= datalayer.battery2.info.min_cell_voltage_mV) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
// Check diff between highest and lowest cell
cell_deviation_mV = (datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV);
if (cell_deviation_mV > datalayer.battery2.info.max_cell_voltage_deviation_mV) {
set_event(EVENT_CELL_DEVIATION_HIGH, (cell_deviation_mV / 20));
} else {
clear_event(EVENT_CELL_DEVIATION_HIGH);
}
// Check if SOH% between the packs is too large
if ((datalayer.battery.status.soh_pptt != 9900) && (datalayer.battery2.status.soh_pptt != 9900)) {
// Both values available, check diff
@ -196,7 +222,23 @@ void update_machineryprotection() {
}
//battery pause status begin
void setBatteryPause(bool pause_battery, bool pause_CAN) {
void setBatteryPause(bool pause_battery, bool pause_CAN, bool equipment_stop, bool store_settings) {
// First handle equipment stop / resume
if (equipment_stop && !datalayer.system.settings.equipment_stop_active) {
datalayer.system.settings.equipment_stop_active = true;
if (store_settings) {
store_settings_equipment_stop();
}
set_event(EVENT_EQUIPMENT_STOP, 1);
} else if (!equipment_stop && datalayer.system.settings.equipment_stop_active) {
datalayer.system.settings.equipment_stop_active = false;
if (store_settings) {
store_settings_equipment_stop();
}
clear_event(EVENT_EQUIPMENT_STOP);
}
emulator_pause_CAN_send_ON = pause_CAN;
@ -228,6 +270,7 @@ void setBatteryPause(bool pause_battery, bool pause_CAN) {
/// @brief handle emulator pause status
/// @return true if CAN messages should be sent to battery, false if not
void emulator_pause_state_send_CAN_battery() {
bool previous_allowed_to_send_CAN = allowed_to_send_CAN;
if (emulator_pause_status == NORMAL) {
allowed_to_send_CAN = true;
@ -245,6 +288,14 @@ void emulator_pause_state_send_CAN_battery() {
}
allowed_to_send_CAN = (!emulator_pause_CAN_send_ON || emulator_pause_status == NORMAL);
if (previous_allowed_to_send_CAN && !allowed_to_send_CAN) {
//completely force stop the CAN communication
ESP32Can.CANStop();
} else if (!previous_allowed_to_send_CAN && allowed_to_send_CAN) {
//resume CAN communication
ESP32Can.CANInit();
}
}
std::string get_emulator_pause_status() {

View file

@ -2,6 +2,7 @@
#define SAFETY_H
#include <Arduino.h>
#include <string>
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#define MAX_CAN_FAILURES 50
@ -15,10 +16,12 @@ extern battery_pause_status emulator_pause_status;
extern bool allowed_to_send_CAN;
//battery pause status end
extern void store_settings_equipment_stop();
void update_machineryprotection();
//battery pause status begin
void setBatteryPause(bool pause_battery, bool pause_CAN);
void setBatteryPause(bool pause_battery, bool pause_CAN, bool equipment_stop = false, bool store_settings = true);
void emulator_pause_state_send_CAN_battery();
std::string get_emulator_pause_status();
//battery pause status end

View file

@ -0,0 +1,56 @@
#include "debounce_button.h"
// Function to initialize the debounced button with pin, switch type, and debounce delay
void initDebouncedButton(DebouncedButton& button, int pin, SwitchType type, unsigned long debounceDelay) {
button.pin = pin;
button.debounceDelay = debounceDelay;
button.lastDebounceTime = 0;
button.lastButtonState = (type == NC) ? HIGH : LOW; // NC starts HIGH, NO starts LOW
button.buttonState = button.lastButtonState;
button.switchType = type;
pinMode(pin, INPUT); // Setup pin mode
}
ButtonState debounceButton(DebouncedButton& button, unsigned long& timeSincePress) {
int reading = digitalRead(button.pin);
// If the button state has changed due to noise or a press
if (reading != button.lastButtonState) {
// Reset debounce timer
button.lastDebounceTime = millis();
}
// Check if the state change is stable for the debounce delay
if ((millis() - button.lastDebounceTime) > button.debounceDelay) {
if (reading != button.buttonState) {
button.buttonState = reading;
// Adjust button logic based on switch type (NC or NO)
if (button.switchType == NC) {
if (button.buttonState == LOW) {
// Button pressed for NC, record the press time
button.ulPressTime = millis();
return PRESSED;
} else {
// Button released for NC, calculate the time since last press
timeSincePress = millis() - button.ulPressTime;
return RELEASED;
}
} else { // NO type
if (button.buttonState == HIGH) {
// Button pressed for NO, record the press time
button.ulPressTime = millis();
return PRESSED;
} else {
// Button released for NO, calculate the time since last press
timeSincePress = millis() - button.ulPressTime;
return RELEASED;
}
}
}
}
// Remember the last button state
button.lastButtonState = reading;
return NONE; // No change in button state
}

View file

@ -0,0 +1,36 @@
#ifndef DEBOUNCE_H
#define DEBOUNCE_H
#include <Arduino.h>
// Enum to define switch type (Normally Closed - NC, Normally Open - NO)
enum SwitchType {
NC, // Normally Closed
NO // Normally Open
};
// Enum to define button state
enum ButtonState {
NONE, // No change in button state
PRESSED, // Button is pressed down
RELEASED // Button is released
};
// Struct to hold button state and debounce parameters
struct DebouncedButton {
int pin; // GPIO pin number
unsigned long lastDebounceTime; // Time of last state change
unsigned long debounceDelay; // Debounce delay time
unsigned long ulPressTime; // Time when the button was last pressed
bool lastButtonState; // Previous button state
bool buttonState; // Current button state
SwitchType switchType; // Switch type (NC or NO)
};
// Function to initialize the debounced button
void initDebouncedButton(DebouncedButton& button, int pin, SwitchType type, unsigned long debounceDelay = 50);
// Function to debounce button and return the button state (PRESSED, RELEASED, or NONE)
ButtonState debounceButton(DebouncedButton& button, unsigned long& timeSincePress);
#endif // DEBOUNCE_H

View file

@ -149,7 +149,7 @@ void init_events(void) {
events.entries[EVENT_CANFD_RX_FAILURE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CAN_RX_WARNING].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_CAN_TX_FAILURE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CAN_INVERTER_MISSING].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CAN_INVERTER_MISSING].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_WATER_INGRESS].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO;
events.entries[EVENT_DISCHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO;
@ -215,6 +215,7 @@ void init_events(void) {
events.entries[EVENT_WIFI_DISCONNECT].level = EVENT_LEVEL_INFO;
events.entries[EVENT_MQTT_CONNECT].level = EVENT_LEVEL_INFO;
events.entries[EVENT_MQTT_DISCONNECT].level = EVENT_LEVEL_INFO;
events.entries[EVENT_EQUIPMENT_STOP].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_EEPROM_WRITE].log = false; // Don't log the logger...
@ -278,7 +279,7 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
case EVENT_CAN_TX_FAILURE:
return "ERROR: CAN messages failed to transmit, or no one on the bus to ACK the message!";
case EVENT_CAN_INVERTER_MISSING:
return "ERROR: Inverter missing on CAN bus! Check wiring!";
return "Warning: Inverter not sending messages on CAN bus. Check wiring!";
case EVENT_CHARGE_LIMIT_EXCEEDED:
return "Info: Inverter is charging faster than battery is allowing.";
case EVENT_DISCHARGE_LIMIT_EXCEEDED:
@ -417,6 +418,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
return "Info: MQTT connected.";
case EVENT_MQTT_DISCONNECT:
return "Info: MQTT disconnected.";
case EVENT_EQUIPMENT_STOP:
return "ERROR: EQUIPMENT STOP ACTIVATED!!!";
default:
return "";
}

View file

@ -103,6 +103,7 @@
XX(EVENT_WIFI_DISCONNECT) \
XX(EVENT_MQTT_CONNECT) \
XX(EVENT_MQTT_DISCONNECT) \
XX(EVENT_EQUIPMENT_STOP) \
XX(EVENT_NOF_EVENTS)
typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE;

View file

@ -166,6 +166,23 @@ void init_webserver() {
}
});
// Route for equipment stop/resume
server.on("/equipmentStop", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("stop")) {
String valueStr = request->getParam("stop")->value();
if (valueStr == "true" || valueStr == "1") {
setBatteryPause(true, true, true);
} else {
setBatteryPause(false, false, false);
}
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
}
});
// Route for editing SOCMin
server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
@ -327,7 +344,10 @@ void init_webserver() {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
request->send(200, "text/plain", "Rebooting server...");
//TODO: Should we handle contactors gracefully? Ifdef CONTACTOR_CONTROL then what?
//Equipment STOP without persisting the equipment state before restart
// Max Charge/Discharge = 0; CAN = stop; contactors = open
setBatteryPause(true, true, true, false);
delay(1000);
ESP.restart();
});
@ -834,7 +854,10 @@ String processor(const String& var) {
if (emulator_pause_request_ON)
content += "<button onclick='PauseBattery(false)'>Resume charge/discharge</button>";
else
content += "<button onclick='PauseBattery(true)'>Pause charge/discharge</button>";
content +=
"<button onclick=\"if(confirm('Are you sure you want to pause charging and discharging? This will set the "
"maximum charge and discharge values to zero, preventing any further power flow.')) { PauseBattery(true); "
"}\">Pause charge/discharge</button>";
content += " ";
content += "<button onclick='OTA()'>Perform OTA update</button>";
content += " ";
@ -847,6 +870,21 @@ String processor(const String& var) {
content += "<button onclick='askReboot()'>Reboot Emulator</button>";
if (WEBSERVER_AUTH_REQUIRED)
content += "<button onclick='logout()'>Logout</button>";
if (!datalayer.system.settings.equipment_stop_active)
content +=
"<br/><br/><button style=\"background:red;color:white;cursor:pointer;\""
" onclick=\""
"if(confirm('This action will open contactors on the battery and stop all CAN communications. Are you "
"sure?')) { estop(true); }\""
">Open Contactors</button><br/>";
else
content +=
"<br/><br/><button style=\"background:green;color:white;cursor:pointer;\""
"20px;font-size:16px;font-weight:bold;cursor:pointer;border-radius:5px; margin:10px;"
" onclick=\""
"if(confirm('This action will restore the battery state. Are you sure?')) { estop(false); }\""
">Close Contactors</button><br/>";
content += "<script>";
content += "function OTA() { window.location.href = '/update'; }";
content += "function Cellmon() { window.location.href = '/cellmonitor'; }";
@ -875,7 +913,12 @@ String processor(const String& var) {
"XMLHttpRequest();xhr.onload=function() { "
"window.location.reload();};xhr.open('GET','/pause?p='+pause,true);xhr.send();";
content += "}";
content += "function estop(stop){";
content +=
"var xhr=new "
"XMLHttpRequest();xhr.onload=function() { "
"window.location.reload();};xhr.open('GET','/equipmentStop?stop='+stop,true);xhr.send();";
content += "}";
content += "</script>";
//Script for refreshing page
@ -899,9 +942,6 @@ void onOTAStart() {
clear_event(EVENT_OTA_UPDATE_TIMEOUT);
ota_active = true;
//completely force stop the CAN communication
ESP32Can.CANStop();
ota_timeout_timer.reset();
}
@ -924,6 +964,9 @@ void onOTAEnd(bool success) {
// Log when OTA has finished
if (success) {
//Equipment STOP without persisting the equipment state before restart
// Max Charge/Discharge = 0; CAN = stop; contactors = open
setBatteryPause(true, true, true, false);
// a reboot will be done by the OTA library. no need to do anything here
#ifdef DEBUG_VIA_USB
Serial.println("OTA update finished successfully!");
@ -934,8 +977,6 @@ void onOTAEnd(bool success) {
#endif // DEBUG_VIA_USB
//try to Resume the battery pause and CAN communication
setBatteryPause(false, false);
//resume CAN communication
ESP32Can.CANInit();
}
}

View file

@ -18,6 +18,7 @@ static unsigned long LastFrameTime = 0;
static uint8_t number_of_batteries = 1;
static uint16_t capped_capacity_Wh;
static uint16_t capped_remaining_capacity_Wh;
static uint16_t inverter_missing_on_can = 0;
//CAN message translations from this amazing repository: https://github.com/rand12345/solax_can_bus
@ -88,6 +89,12 @@ void update_values_can_inverter() { //This function maps all the values fetched
if (millis() - LastFrameTime >= SolaxTimeout) {
datalayer.system.status.inverter_allows_contactor_closing = false;
STATE = BATTERY_ANNOUNCE;
inverter_missing_on_can++;
if (inverter_missing_on_can > CAN_STILL_ALIVE) {
set_event(EVENT_CAN_INVERTER_MISSING, 0);
} else {
clear_event(EVENT_CAN_INVERTER_MISSING);
}
}
//Calculate the required values
temperature_average =
@ -129,6 +136,14 @@ void update_values_can_inverter() { //This function maps all the values fetched
}
}
//Cap the value according to user settings. Some inverters cannot handle large values.
if ((max_charge_rate_amp * 10) > datalayer.battery.info.max_charge_amp_dA) {
max_charge_rate_amp = (datalayer.battery.info.max_charge_amp_dA / 10);
}
if ((max_discharge_rate_amp * 10) > datalayer.battery.info.max_discharge_amp_dA) {
max_discharge_rate_amp = (datalayer.battery.info.max_discharge_amp_dA / 10);
}
// Batteries might be larger than uint16_t value can take
if (datalayer.battery.info.total_capacity_Wh > 65000) {
capped_capacity_Wh = 65000;