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/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "src/charger/CHARGERS.h" #include "src/charger/CHARGERS.h"
#include "src/datalayer/datalayer.h"
#include "src/devboard/utils/events.h" #include "src/devboard/utils/events.h"
#include "src/devboard/utils/led_handler.h" #include "src/devboard/utils/led_handler.h"
#include "src/devboard/utils/value_mapping.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/CAN_config.h"
#include "src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" #include "src/lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "src/datalayer/datalayer.h"
#ifdef WIFI #ifdef WIFI
#include "src/devboard/wifi/wifi.h" #include "src/devboard/wifi/wifi.h"
#ifdef WEBSERVER #ifdef WEBSERVER
@ -48,9 +47,13 @@
#endif #endif
#endif #endif
#ifdef EQUIPMENT_STOP_BUTTON
#include "src/devboard/utils/debounce_button.h"
#endif
Preferences settings; // Store user settings Preferences settings; // Store user settings
// The current software version, shown on webserver // The current software version, shown on webserver
const char* version_number = "7.4.0"; const char* version_number = "7.5.dev";
// Interval settings // Interval settings
uint16_t intervalUpdateValues = INTERVAL_5_S; // Interval at which to update inverter values / Modbus registers 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 #ifdef DUAL_CAN
#include "src/lib/pierremolinaro-acan2515/ACAN2515.h" #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); ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);
static ACAN2515_Buffer16 gBuffer; static ACAN2515_Buffer16 gBuffer;
#endif #endif
@ -135,6 +138,14 @@ unsigned long negativeStartTime = 0;
unsigned long timeSpentInFaultedMode = 0; unsigned long timeSpentInFaultedMode = 0;
#endif #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 main_loop_task;
TaskHandle_t connectivity_loop_task; TaskHandle_t connectivity_loop_task;
@ -163,6 +174,9 @@ void setup() {
init_battery(); init_battery();
#ifdef EQUIPMENT_STOP_BUTTON
init_equipment_stop_button();
#endif
// BOOT button at runtime is used as an input for various things // BOOT button at runtime is used as an input for various things
pinMode(0, INPUT_PULLUP); pinMode(0, INPUT_PULLUP);
@ -235,6 +249,10 @@ void core_loop(void* task_time_us) {
while (true) { while (true) {
START_TIME_MEASUREMENT(all); START_TIME_MEASUREMENT(all);
START_TIME_MEASUREMENT(comm); START_TIME_MEASUREMENT(comm);
#ifdef EQUIPMENT_STOP_BUTTON
monitor_equipment_stop_button();
#endif
// Input, Runs as fast as possible // Input, Runs as fast as possible
receive_can_native(); // Receive CAN messages from native CAN port receive_can_native(); // Receive CAN messages from native CAN port
#ifdef CAN_FD #ifdef CAN_FD
@ -334,10 +352,21 @@ void init_serial() {
} }
void init_stored_settings() { void init_stored_settings() {
static uint32_t temp = 0;
settings.begin("batterySettings", false); 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 #ifndef LOAD_SAVED_SETTINGS_ON_BOOT
settings.clear(); // If this clear function is executed, no settings will be read from storage 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 #endif
#ifdef WIFI #ifdef WIFI
@ -356,7 +385,6 @@ void init_stored_settings() {
} }
#endif #endif
static uint32_t temp = 0;
temp = settings.getUInt("BATTERY_WH_MAX", false); temp = settings.getUInt("BATTERY_WH_MAX", false);
if (temp != 0) { if (temp != 0) {
datalayer.battery.info.total_capacity_Wh = temp; datalayer.battery.info.total_capacity_Wh = temp;
@ -547,6 +575,43 @@ void init_battery() {
#endif #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 #ifdef CAN_FD
// Functions // Functions
#ifdef DEBUG_CANFD_DATA #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 //message incoming, pass it on to the handler
receive_can(&rx_frame, CAN_ADDON_FD_MCP2518); receive_can(&rx_frame, CAN_ADDON_FD_MCP2518);
receive_can(&rx_frame, CANFD_NATIVE);
} }
} }
#endif #endif
@ -672,9 +738,14 @@ void handle_contactors() {
timeSpentInFaultedMode = 0; 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; contactorStatus = SHUTDOWN_REQUESTED;
} }
if (contactorStatus == SHUTDOWN_REQUESTED && !datalayer.system.settings.equipment_stop_active) {
contactorStatus = DISCONNECTED;
}
if (contactorStatus == SHUTDOWN_REQUESTED) { if (contactorStatus == SHUTDOWN_REQUESTED) {
digitalWrite(PRECHARGE_PIN, LOW); digitalWrite(PRECHARGE_PIN, LOW);
#ifndef PWM_CONTACTOR_CONTROL #ifndef PWM_CONTACTOR_CONTROL
@ -846,6 +917,12 @@ void init_serialDataLink() {
#endif #endif
} }
void store_settings_equipment_stop() {
settings.begin("batterySettings", false);
settings.putBool("EQUIPMENT_STOP", datalayer.system.settings.equipment_stop_active);
settings.end();
}
void storeSettings() { void storeSettings() {
settings.begin("batterySettings", false); settings.begin("batterySettings", false);
#ifdef WIFI #ifdef WIFI

View file

@ -48,6 +48,13 @@ const char* mqtt_password = "REDACTED"; // Set NULL for no password
#endif // USE_MQTT #endif // USE_MQTT
#endif // WIFI #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) */ /* 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_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 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 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 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 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 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 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) //#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 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 //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
#define WEBSERVER_AUTH_REQUIRED \ #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 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 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 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 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 */ /* MQTT options */
// #define MQTT // Enable this line to enable MQTT // #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_HV_enabled;
extern bool charger_aux12V_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 #ifdef WIFICONFIG
extern IPAddress local_IP; extern IPAddress local_IP;
// Set your Gateway IP address // 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.temperature_max_dC = battery2_temperature_max * 10; // Add a decimal
datalayer.battery2.status.cell_min_voltage_mV = datalayer.battery2.status.cell_voltages_mV[0]; if (battery2_info_available) {
datalayer.battery2.status.cell_max_voltage_mV = datalayer.battery2.status.cell_voltages_mV[1]; // 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 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) { if (detectedBattery == BATTERY_60AH) {
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH; 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.min_design_voltage_dV = MIN_PACK_VOLTAGE_60AH;
if (datalayer.battery.status.cell_max_voltage_mV >= MAX_CELL_VOLTAGE_60AH) { datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_60AH;
set_event(EVENT_CELL_OVER_VOLTAGE, 0); datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_60AH;
}
if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_60AH) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
} else if (detectedBattery == BATTERY_94AH) { } else if (detectedBattery == BATTERY_94AH) {
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_94AH;
datalayer.battery.info.min_design_voltage_dV = MIN_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) { datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_94AH;
set_event(EVENT_CELL_OVER_VOLTAGE, 0); datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_94AH;
}
if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_94AH) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
} else { // BATTERY_120AH } else { // BATTERY_120AH
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_120AH;
datalayer.battery.info.min_design_voltage_dV = MIN_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) { datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_120AH;
set_event(EVENT_CELL_OVER_VOLTAGE, 0); datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_120AH;
}
if (datalayer.battery.status.cell_min_voltage_mV <= MIN_CELL_VOLTAGE_120AH) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
}
} }
} }
@ -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 //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.max_design_voltage_dV = MAX_PACK_VOLTAGE_60AH;
datalayer.battery.info.min_design_voltage_dV = MIN_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; datalayer.system.status.battery_allows_contactor_closing = true;
#ifdef DOUBLE_BATTERY #ifdef DOUBLE_BATTERY
Serial.println("Another BMW i3 battery also selected!"); 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.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.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 = datalayer.battery2.status.voltage_dV =
0; //Init voltage to 0 to allow contactor check to operate without fear of default values colliding 0; //Init voltage to 0 to allow contactor check to operate without fear of default values colliding
#endif #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) { 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 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 case 0x244: //00,00,00,04,41,0F,20,8B - Static, values never changes between logs
break; break;
case 0x245: //01,00,02,19,3A,25,90,F4 Seems to have a mux in frame0 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 //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 //03,C6,88,12,FD,48,90,5C
//04,00,FF,FF,00,00,90,6D //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 #ifdef DEBUG_VIA_USB
Serial.println("BYD Atto 3 battery selected"); Serial.println("BYD Atto 3 battery selected");
#endif #endif
datalayer.battery.info.number_of_cells = 126;
datalayer.battery.info.max_design_voltage_dV = 4410; // Over this charging is not possible datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
datalayer.battery.info.min_design_voltage_dV = 3800; // 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;
datalayer.battery.info.max_cell_voltage_deviation_mV = MAX_CELL_DEVIATION_MV;
} }
#endif #endif

View file

@ -4,7 +4,11 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #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_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 setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface); void transmit_can(CAN_frame* tx_frame, int interface);

View file

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

View file

@ -8,8 +8,6 @@
//Figure out if CAN messages need to be sent to keep the system happy? //Figure out if CAN messages need to be sent to keep the system happy?
/* Do not change code below unless you are sure what you are doing */ /* Do not change code below unless you are sure what you are doing */
#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 errorCode = 0; //stores if we have an error code active from battery control logic
static uint8_t BMU_Detected = 0; static uint8_t BMU_Detected = 0;
static uint8_t CMU_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); 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) { if (!BMU_Detected) {
#ifdef DEBUG_VIA_USB #ifdef DEBUG_VIA_USB
Serial.println("BMU not detected, check wiring!"); 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 #ifdef DEBUG_VIA_USB
Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected"); Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected");
#endif #endif
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.max_design_voltage_dV = 3696; // 369.6V datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = 3160; // 316.0V 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 #endif

View file

@ -4,7 +4,11 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #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_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 setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface); 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 #endif
datalayer.battery.info.number_of_cells = 108; datalayer.battery.info.number_of_cells = 108;
datalayer.battery.info.max_design_voltage_dV = 4546; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = 3370; 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; datalayer.system.status.battery_allows_contactor_closing = true;
} }

View file

@ -3,7 +3,11 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #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 setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface); 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 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 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] = const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies { // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF, 0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0, 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); 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 */ /* Safeties verified. Perform USB serial printout if configured to do so */
#ifdef DEBUG_VIA_USB #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.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 192; // TODO: will vary depending on battery datalayer.battery.info.number_of_cells = 192; // TODO: will vary depending on battery
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.max_design_voltage_dV = datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
8064; // TODO: define when battery is known, charging is not possible (goes into forced discharge) datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_design_voltage_dV = datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
4320; // TODO: define when battery is known. discharging further is disabled
} }
#endif #endif

View file

@ -7,7 +7,11 @@
extern ACAN2517FD canfd; extern ACAN2517FD canfd;
#define BATTERY_SELECTED #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_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 MAXCHARGEPOWERALLOWED 10000
#define MAXDISCHARGEPOWERALLOWED 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 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 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_calculated = 0;
static uint16_t SOC_BMS = 0; static uint16_t SOC_BMS = 0;
static uint16_t SOC_Display = 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); 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 */ /* Safeties verified. Perform USB serial printout if configured to do so */
#ifdef DEBUG_VIA_USB #ifdef DEBUG_VIA_USB
@ -223,12 +212,12 @@ void update_number_of_cells() {
// Check if we have 98S or 90S battery // Check if we have 98S or 90S battery
if (datalayer.battery.status.cell_voltages_mV[97] > 0) { if (datalayer.battery.status.cell_voltages_mV[97] > 0) {
datalayer.battery.info.number_of_cells = 98; datalayer.battery.info.number_of_cells = 98;
datalayer.battery.info.max_design_voltage_dV = 4040; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV;
datalayer.battery.info.min_design_voltage_dV = 3100; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_98S_DV;
} else { } else {
datalayer.battery.info.number_of_cells = 90; datalayer.battery.info.number_of_cells = 90;
datalayer.battery.info.max_design_voltage_dV = 3870; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_90S_DV;
datalayer.battery.info.min_design_voltage_dV = 2250; 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 #ifdef DEBUG_VIA_USB
Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected"); Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected");
#endif #endif
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_98S_DV; //Start with 98S value. Precised later
datalayer.battery.info.max_design_voltage_dV = 4040; // 404.0V datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_90S_DV; //Start with 90S value. Precised later
datalayer.battery.info.min_design_voltage_dV = 3100; // 310.0V datalayer.battery.info.max_cell_voltage_mV = MAX_CELL_VOLTAGE_MV;
datalayer.battery.info.min_cell_voltage_mV = MIN_CELL_VOLTAGE_MV;
} }
#endif #endif

View file

@ -4,7 +4,13 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #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_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 setup_battery(void);
void update_number_of_cells(); 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"); Serial.println("Kia/Hyundai Hybrid battery selected");
#endif #endif
datalayer.battery.info.number_of_cells = 56; // HEV , TODO: Make dynamic according to HEV/PHEV 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.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = 1700; 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 #endif

View file

@ -4,7 +4,11 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #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_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 setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface); 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"); Serial.println("MG 5 battery selected");
#endif #endif
datalayer.battery.info.max_design_voltage_dV = 4040; // Over this charging is not possible datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = 3100; // Under this discharging is disabled 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 #endif

View file

@ -4,7 +4,11 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #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_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 setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface); void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -75,8 +75,6 @@ static uint8_t crctable[256] = {
#define ZE1_BATTERY 2 #define ZE1_BATTERY 2
static uint8_t LEAF_battery_Type = ZE0_BATTERY; static uint8_t LEAF_battery_Type = ZE0_BATTERY;
static bool battery_can_alive = false; 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 #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_Discharge_Power_Limit = 0; //Limit in kW
static uint16_t battery_Charge_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_max_voltage_mV = battery2_min_max_voltage[1];
datalayer.battery2.status.cell_min_voltage_mV = battery2_min_max_voltage[0]; 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; 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_max_voltage_mV = battery_min_max_voltage[1];
datalayer.battery.status.cell_min_voltage_mV = battery_min_max_voltage[0]; 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; break;
} }
@ -1221,13 +1207,19 @@ void setup_battery(void) { // Performs one time setup at startup
#endif #endif
datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.max_design_voltage_dV = 4040; // 404.4V datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = 2600; // 260.0V 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 #ifdef DOUBLE_BATTERY
datalayer.battery2.info.number_of_cells = datalayer.battery.info.number_of_cells; 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.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.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 #endif //DOUBLE_BATTERY
} }

View file

@ -4,7 +4,11 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #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_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); uint16_t Temp_fromRAW_to_F(uint16_t temperature);
bool is_message_corrupt(CAN_frame rx_frame); bool is_message_corrupt(CAN_frame rx_frame);

View file

@ -4,10 +4,6 @@
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "PYLON-BATTERY.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 */ /* 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 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"); Serial.println("Pylon battery selected");
#endif #endif
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE; 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 #endif

View file

@ -4,7 +4,13 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #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 setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface); 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; 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 #ifdef DEBUG_VIA_USB
Serial.println("Values going to inverter:"); Serial.println("Values going to inverter:");
Serial.print("SOH%: "); Serial.print("SOH%: ");
@ -248,9 +241,11 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Renault Kangoo battery selected"); Serial.println("Renault Kangoo battery selected");
#endif #endif
datalayer.battery.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
4040; // 404.0V, over this, charging is not possible (goes into forced discharge) datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled 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 #endif

View file

@ -4,10 +4,11 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4150 //5000 = 500.0V
#define ABSOLUTE_CELL_MAX_VOLTAGE 4150 // If cellvoltage goes over this mV, we go into FAULT mode #define MIN_PACK_VOLTAGE_DV 2500
#define ABSOLUTE_CELL_MIN_VOLTAGE 2500 // If cellvoltage goes under this mV, we go into FAULT mode #define MAX_CELL_DEVIATION_MV 500
#define MAX_CELL_DEVIATION_MV 500 // If cell mV delta exceeds this, we go into WARNING mode #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 #define MAX_CHARGE_POWER_W 5000 // Battery can be charged with this amount of power
void setup_battery(void); 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_min_voltage_mV = min_cell_mv_value;
datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value; datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value;
datalayer.battery.status.voltage_dV = datalayer.battery.status.voltage_dV = static_cast<uint32_t>((calculated_total_pack_voltage_mV / 100)); // mV to 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);
}
#ifdef DEBUG_VIA_USB #ifdef DEBUG_VIA_USB
@ -535,8 +527,11 @@ void setup_battery(void) { // Performs one time setup at startup
#endif #endif
datalayer.system.status.battery_allows_contactor_closing = true; datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.max_design_voltage_dV = 4040; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = 2700; 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 #endif

View file

@ -3,10 +3,12 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #define BATTERY_SELECTED
#define ABSOLUTE_CELL_MAX_VOLTAGE 4200
#define ABSOLUTE_CELL_MIN_VOLTAGE 3000
#define MAX_CELL_DEVIATION_MV 500 #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 setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface); 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; 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 #ifdef DEBUG_VIA_USB
#endif #endif
@ -99,9 +92,13 @@ void setup_battery(void) { // Performs one time setup at startup
#ifdef DEBUG_VIA_USB #ifdef DEBUG_VIA_USB
Serial.println("Renault Zoe 50kWh battery selected"); Serial.println("Renault Zoe 50kWh battery selected");
#endif #endif
datalayer.system.status.battery_allows_contactor_closing = true;
datalayer.battery.info.max_design_voltage_dV = 4040; datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.min_design_voltage_dV = 3100; 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 #endif

View file

@ -3,10 +3,11 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #define BATTERY_SELECTED
#define MAX_PACK_VOLTAGE_DV 4100 //5000 = 500.0V
#define ABSOLUTE_CELL_MAX_VOLTAGE 4100 #define MIN_PACK_VOLTAGE_DV 3000
#define ABSOLUTE_CELL_MIN_VOLTAGE 3000
#define MAX_CELL_DEVIATION_MV 500 #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 setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface); 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"); Serial.println("RJXZS BMS selected");
#endif #endif
datalayer.battery.info.max_design_voltage_dV = 5000; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = 1000; 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 #endif // RJXZS_BMS

View file

@ -4,6 +4,10 @@
#include "../include.h" #include "../include.h"
/* Tweak these according to your battery build */ /* 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_CELL_DEVIATION_MV 250
#define MAX_DISCHARGE_POWER_ALLOWED_W 5000 #define MAX_DISCHARGE_POWER_ALLOWED_W 5000
#define MAX_CHARGE_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"); Serial.println("Hyundai Santa Fe PHEV battery selected");
#endif #endif
datalayer.battery.info.number_of_cells = 96; datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.max_design_voltage_dV = 4040; datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = 2880; 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 #endif

View file

@ -4,7 +4,11 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #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_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); uint8_t CalculateCRC8(CAN_frame rx_frame);
void setup_battery(void); void setup_battery(void);

View file

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

View file

@ -253,16 +253,6 @@ static const char* hvilStatusState[] = {"NOT OK",
"UNKNOWN(14)", "UNKNOWN(14)",
"UNKNOWN(15)"}; "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 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 //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; 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 */ /* Value mapping is completed. Start to check all safeties */
if (battery_hvil_status == 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); clear_event(EVENT_INTERNAL_OPEN_FAULT);
} }
battery_cell_deviation_mV = (battery_cell_max_v - battery_cell_min_v);
#ifdef TESLA_MODEL_3Y_BATTERY #ifdef TESLA_MODEL_3Y_BATTERY
// Autodetect algoritm for chemistry on 3/Y packs. // Autodetect algoritm for chemistry on 3/Y packs.
// NCM/A batteries have 96s, LFP has 102-106s // 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) { if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_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.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 } else { // NCM/A chemistry
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA; 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.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 #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 #ifdef DEBUG_VIA_USB
printFaultCodesIfActive(); 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; 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 */ /* Value mapping is completed. Start to check all safeties */
if (battery2_hvil_status == 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); clear_event(EVENT_INTERNAL_OPEN_FAULT);
} }
battery2_cell_deviation_mV = (battery2_cell_max_v - battery2_cell_min_v);
#ifdef TESLA_MODEL_3Y_BATTERY #ifdef TESLA_MODEL_3Y_BATTERY
// Autodetect algoritm for chemistry on 3/Y packs. // Autodetect algoritm for chemistry on 3/Y packs.
// NCM/A batteries have 96s, LFP has 102-106s // 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) { if (datalayer.battery2.info.chemistry == battery_chemistry_enum::LFP) {
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_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.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 } else { // NCM/A chemistry
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA; 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.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 #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 #ifdef DEBUG_VIA_USB
printFaultCodesIfActive_battery2(); 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 #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.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA; datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA;
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 #ifdef DOUBLE_BATTERY
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA; 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.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 // DOUBLE_BATTERY
#endif // TESLA_MODEL_SX_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.chemistry = battery_chemistry_enum::LFP;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_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.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 #ifdef DOUBLE_BATTERY
datalayer.battery2.info.chemistry = battery_chemistry_enum::LFP; datalayer.battery2.info.chemistry = battery_chemistry_enum::LFP;
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_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.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 #endif // DOUBLE_BATTERY
#else // Startup in NCM/A mode #else // Startup in NCM/A mode
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA; 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.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 #ifdef DOUBLE_BATTERY
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA; 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.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 // DOUBLE_BATTERY
#endif // !LFP_CHEMISTRY #endif // !LFP_CHEMISTRY
#endif // TESLA_MODEL_3Y_BATTERY #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 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 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 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 printFaultCodesIfActive();
void printDebugIfActive(uint8_t symbol, const char* message); 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 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 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 BATT_U = 0; //0x3A
static float MAX_U = 0; //0x3A static float MAX_U = 0; //0x3A
static float MIN_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 float BATT_T_AVG = 0; //0x413
static uint16_t SOC_BMS = 0; //0X37D static uint16_t SOC_BMS = 0; //0X37D
static uint16_t SOC_CALC = 0; static uint16_t SOC_CALC = 0;
static uint16_t CELL_U_MAX = 0; //0x37D static uint16_t CELL_U_MAX = 3700; //0x37D
static uint16_t CELL_U_MIN = 0; //0x37D static uint16_t CELL_U_MIN = 3700; //0x37D
static uint8_t CELL_ID_U_MAX = 0; //0x37D static uint8_t CELL_ID_U_MAX = 0; //0x37D
static uint16_t HvBattPwrLimDchaSoft = 0; //0x369 static uint16_t HvBattPwrLimDchaSoft = 0; //0x369
static uint8_t batteryModuleNumber = 0x10; // First battery module 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]; 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 transmit_can(&VOLVO_SOH_Req, can_config.battery); //Send SOH read request
} }
rxConsecutiveFrames = 0; rxConsecutiveFrames = 0;
@ -347,8 +338,10 @@ void setup_battery(void) { // Performs one time setup at startup
#endif #endif
datalayer.battery.info.number_of_cells = 108; datalayer.battery.info.number_of_cells = 108;
datalayer.battery.info.max_design_voltage_dV = datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
4540; // 454.0V, over this, charging is not possible (goes into forced discharge) datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
datalayer.battery.info.min_design_voltage_dV = 2938; // 293.8V under this, discharging further is disabled 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 #endif

View file

@ -4,7 +4,11 @@
#include "../include.h" #include "../include.h"
#define BATTERY_SELECTED #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_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 setup_battery(void);
void transmit_can(CAN_frame* tx_frame, int interface); void transmit_can(CAN_frame* tx_frame, int interface);

View file

@ -13,6 +13,12 @@ typedef struct {
uint16_t max_design_voltage_dV = 5000; uint16_t max_design_voltage_dV = 5000;
/** The minimum intended packvoltage, in deciVolt. 3300 = 330.0 V */ /** The minimum intended packvoltage, in deciVolt. 3300 = 330.0 V */
uint16_t min_design_voltage_dV = 2500; 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 */ /** BYD CAN specific setting, max charge in deciAmpere. 300 = 30.0 A */
uint16_t max_charge_amp_dA = BATTERY_MAX_CHARGE_AMP; uint16_t max_charge_amp_dA = BATTERY_MAX_CHARGE_AMP;
/** BYD CAN specific setting, max discharge in deciAmpere. 300 = 30.0 A */ /** BYD CAN specific setting, max discharge in deciAmpere. 300 = 30.0 A */
@ -172,6 +178,7 @@ typedef struct {
} DATALAYER_SYSTEM_STATUS_TYPE; } DATALAYER_SYSTEM_STATUS_TYPE;
typedef struct { typedef struct {
bool equipment_stop_active = false;
} DATALAYER_SYSTEM_SETTINGS_TYPE; } DATALAYER_SYSTEM_SETTINGS_TYPE;
typedef struct { typedef struct {

View file

@ -65,6 +65,9 @@
#define LED_PIN 4 #define LED_PIN 4
#define LED_MAX_BRIGHTNESS 40 #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) ----- */ /* ----- Error checks below, don't change (can't be moved to separate file) ----- */
#ifndef HW_CONFIGURED #ifndef HW_CONFIGURED
#define HW_CONFIGURED #define HW_CONFIGURED
@ -78,6 +81,18 @@
#endif #endif
#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 BMW_I3_BATTERY
#ifdef CONTACTOR_CONTROL #ifdef CONTACTOR_CONTROL
#error GPIO PIN 25 cannot be used for both BMWi3 Wakeup and contactor control. Disable 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_PIN 4
#define LED_MAX_BRIGHTNESS 40 #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) ----- */ /* ----- Error checks below, don't change (can't be moved to separate file) ----- */
#ifndef HW_CONFIGURED #ifndef HW_CONFIGURED
#define HW_CONFIGURED #define HW_CONFIGURED

View file

@ -54,6 +54,15 @@ void update_machineryprotection() {
clear_event(EVENT_BATTERY_UNDERVOLTAGE); 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 // 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 // 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 (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 // Check diff between highest and lowest cell
cell_deviation_mV = (datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV); 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)); set_event(EVENT_CELL_DEVIATION_HIGH, (cell_deviation_mV / 20));
} else { } else {
clear_event(EVENT_CELL_DEVIATION_HIGH); clear_event(EVENT_CELL_DEVIATION_HIGH);
@ -175,6 +184,23 @@ void update_machineryprotection() {
clear_event(EVENT_CAN_RX_WARNING); 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 // Check if SOH% between the packs is too large
if ((datalayer.battery.status.soh_pptt != 9900) && (datalayer.battery2.status.soh_pptt != 9900)) { if ((datalayer.battery.status.soh_pptt != 9900) && (datalayer.battery2.status.soh_pptt != 9900)) {
// Both values available, check diff // Both values available, check diff
@ -196,7 +222,23 @@ void update_machineryprotection() {
} }
//battery pause status begin //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; emulator_pause_CAN_send_ON = pause_CAN;
@ -228,6 +270,7 @@ void setBatteryPause(bool pause_battery, bool pause_CAN) {
/// @brief handle emulator pause status /// @brief handle emulator pause status
/// @return true if CAN messages should be sent to battery, false if not /// @return true if CAN messages should be sent to battery, false if not
void emulator_pause_state_send_CAN_battery() { void emulator_pause_state_send_CAN_battery() {
bool previous_allowed_to_send_CAN = allowed_to_send_CAN;
if (emulator_pause_status == NORMAL) { if (emulator_pause_status == NORMAL) {
allowed_to_send_CAN = true; 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); 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() { std::string get_emulator_pause_status() {

View file

@ -2,6 +2,7 @@
#define SAFETY_H #define SAFETY_H
#include <Arduino.h> #include <Arduino.h>
#include <string> #include <string>
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#define MAX_CAN_FAILURES 50 #define MAX_CAN_FAILURES 50
@ -15,10 +16,12 @@ extern battery_pause_status emulator_pause_status;
extern bool allowed_to_send_CAN; extern bool allowed_to_send_CAN;
//battery pause status end //battery pause status end
extern void store_settings_equipment_stop();
void update_machineryprotection(); void update_machineryprotection();
//battery pause status begin //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(); void emulator_pause_state_send_CAN_battery();
std::string get_emulator_pause_status(); std::string get_emulator_pause_status();
//battery pause status end //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_CANFD_RX_FAILURE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CAN_RX_WARNING].level = EVENT_LEVEL_WARNING; events.entries[EVENT_CAN_RX_WARNING].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_CAN_TX_FAILURE].level = EVENT_LEVEL_ERROR; 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_WATER_INGRESS].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO; events.entries[EVENT_CHARGE_LIMIT_EXCEEDED].level = EVENT_LEVEL_INFO;
events.entries[EVENT_DISCHARGE_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_WIFI_DISCONNECT].level = EVENT_LEVEL_INFO;
events.entries[EVENT_MQTT_CONNECT].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_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... 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: case EVENT_CAN_TX_FAILURE:
return "ERROR: CAN messages failed to transmit, or no one on the bus to ACK the message!"; return "ERROR: CAN messages failed to transmit, or no one on the bus to ACK the message!";
case EVENT_CAN_INVERTER_MISSING: 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: case EVENT_CHARGE_LIMIT_EXCEEDED:
return "Info: Inverter is charging faster than battery is allowing."; return "Info: Inverter is charging faster than battery is allowing.";
case EVENT_DISCHARGE_LIMIT_EXCEEDED: case EVENT_DISCHARGE_LIMIT_EXCEEDED:
@ -417,6 +418,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
return "Info: MQTT connected."; return "Info: MQTT connected.";
case EVENT_MQTT_DISCONNECT: case EVENT_MQTT_DISCONNECT:
return "Info: MQTT disconnected."; return "Info: MQTT disconnected.";
case EVENT_EQUIPMENT_STOP:
return "ERROR: EQUIPMENT STOP ACTIVATED!!!";
default: default:
return ""; return "";
} }

View file

@ -103,6 +103,7 @@
XX(EVENT_WIFI_DISCONNECT) \ XX(EVENT_WIFI_DISCONNECT) \
XX(EVENT_MQTT_CONNECT) \ XX(EVENT_MQTT_CONNECT) \
XX(EVENT_MQTT_DISCONNECT) \ XX(EVENT_MQTT_DISCONNECT) \
XX(EVENT_EQUIPMENT_STOP) \
XX(EVENT_NOF_EVENTS) XX(EVENT_NOF_EVENTS)
typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE; 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 // Route for editing SOCMin
server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password)) 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)) if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication(); return request->requestAuthentication();
request->send(200, "text/plain", "Rebooting server..."); 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); delay(1000);
ESP.restart(); ESP.restart();
}); });
@ -834,7 +854,10 @@ String processor(const String& var) {
if (emulator_pause_request_ON) if (emulator_pause_request_ON)
content += "<button onclick='PauseBattery(false)'>Resume charge/discharge</button>"; content += "<button onclick='PauseBattery(false)'>Resume charge/discharge</button>";
else 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 += " ";
content += "<button onclick='OTA()'>Perform OTA update</button>"; content += "<button onclick='OTA()'>Perform OTA update</button>";
content += " "; content += " ";
@ -847,6 +870,21 @@ String processor(const String& var) {
content += "<button onclick='askReboot()'>Reboot Emulator</button>"; content += "<button onclick='askReboot()'>Reboot Emulator</button>";
if (WEBSERVER_AUTH_REQUIRED) if (WEBSERVER_AUTH_REQUIRED)
content += "<button onclick='logout()'>Logout</button>"; 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 += "<script>";
content += "function OTA() { window.location.href = '/update'; }"; content += "function OTA() { window.location.href = '/update'; }";
content += "function Cellmon() { window.location.href = '/cellmonitor'; }"; content += "function Cellmon() { window.location.href = '/cellmonitor'; }";
@ -875,7 +913,12 @@ String processor(const String& var) {
"XMLHttpRequest();xhr.onload=function() { " "XMLHttpRequest();xhr.onload=function() { "
"window.location.reload();};xhr.open('GET','/pause?p='+pause,true);xhr.send();"; "window.location.reload();};xhr.open('GET','/pause?p='+pause,true);xhr.send();";
content += "}"; 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>"; content += "</script>";
//Script for refreshing page //Script for refreshing page
@ -899,9 +942,6 @@ void onOTAStart() {
clear_event(EVENT_OTA_UPDATE_TIMEOUT); clear_event(EVENT_OTA_UPDATE_TIMEOUT);
ota_active = true; ota_active = true;
//completely force stop the CAN communication
ESP32Can.CANStop();
ota_timeout_timer.reset(); ota_timeout_timer.reset();
} }
@ -924,6 +964,9 @@ void onOTAEnd(bool success) {
// Log when OTA has finished // Log when OTA has finished
if (success) { 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 // a reboot will be done by the OTA library. no need to do anything here
#ifdef DEBUG_VIA_USB #ifdef DEBUG_VIA_USB
Serial.println("OTA update finished successfully!"); Serial.println("OTA update finished successfully!");
@ -934,8 +977,6 @@ void onOTAEnd(bool success) {
#endif // DEBUG_VIA_USB #endif // DEBUG_VIA_USB
//try to Resume the battery pause and CAN communication //try to Resume the battery pause and CAN communication
setBatteryPause(false, false); 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 uint8_t number_of_batteries = 1;
static uint16_t capped_capacity_Wh; static uint16_t capped_capacity_Wh;
static uint16_t capped_remaining_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 //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) { if (millis() - LastFrameTime >= SolaxTimeout) {
datalayer.system.status.inverter_allows_contactor_closing = false; datalayer.system.status.inverter_allows_contactor_closing = false;
STATE = BATTERY_ANNOUNCE; 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 //Calculate the required values
temperature_average = 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 // Batteries might be larger than uint16_t value can take
if (datalayer.battery.info.total_capacity_Wh > 65000) { if (datalayer.battery.info.total_capacity_Wh > 65000) {
capped_capacity_Wh = 65000; capped_capacity_Wh = 65000;