Merge branch 'main' into mqtt-cleanup

This commit is contained in:
Brett Christensen 2024-02-21 12:20:09 +11:00 committed by GitHub
commit ff18c2fc4a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
60 changed files with 1371 additions and 1344 deletions

View file

@ -49,6 +49,7 @@ jobs:
# - LUNA2000_MODBUS # - LUNA2000_MODBUS
# - PYLON_CAN # - PYLON_CAN
# - SMA_CAN # - SMA_CAN
# - SMA_TRIPOWER_CAN
# - SOFAR_CAN # - SOFAR_CAN
# - SOLAX_CAN # - SOLAX_CAN

View file

@ -52,8 +52,10 @@ jobs:
- LUNA2000_MODBUS - LUNA2000_MODBUS
- PYLON_CAN - PYLON_CAN
- SMA_CAN - SMA_CAN
- SMA_TRIPOWER_CAN
- SOFAR_CAN - SOFAR_CAN
- SOLAX_CAN - SOLAX_CAN
- SERIAL_LINK_TRANSMITTER
# This is the platform GitHub will use to run our workflow. # This is the platform GitHub will use to run our workflow.
runs-on: ubuntu-latest runs-on: ubuntu-latest

View file

@ -47,8 +47,10 @@ jobs:
- LUNA2000_MODBUS - LUNA2000_MODBUS
- PYLON_CAN - PYLON_CAN
- SMA_CAN - SMA_CAN
- SMA_TRIPOWER_CAN
- SOFAR_CAN - SOFAR_CAN
- SOLAX_CAN - SOLAX_CAN
- SERIAL_LINK_TRANSMITTER
# This is the platform GitHub will use to run our workflow. # This is the platform GitHub will use to run our workflow.
runs-on: ubuntu-latest runs-on: ubuntu-latest

View file

@ -23,7 +23,7 @@
#endif #endif
Preferences settings; // Store user settings Preferences settings; // Store user settings
const char* version_number = "5.3.0"; // The current software version, shown on webserver const char* version_number = "5.3.RC"; // The current software version, shown on webserver
// Interval settings // Interval settings
int intervalUpdateValues = 4800; // Interval at which to update inverter values / Modbus registers int intervalUpdateValues = 4800; // Interval at which to update inverter values / Modbus registers
const int interval10 = 10; // Interval for 10ms tasks const int interval10 = 10; // Interval for 10ms tasks
@ -54,26 +54,27 @@ uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
ModbusServerRTU MBserver(Serial2, 2000); ModbusServerRTU MBserver(Serial2, 2000);
#endif #endif
// Common inverter parameters. Batteries map their values to these variables // Common system parameters. Batteries map their values to these variables
uint16_t max_voltage = 5000; //V+1, 0-500.0 (0-5000) uint32_t system_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-150000Wh
uint16_t min_voltage = 2500; //V+1, 0-500.0 (0-5000) uint32_t system_remaining_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-150000Wh
uint16_t battery_voltage = 3700; //V+1, 0-500.0 (0-5000) int16_t system_temperature_max_dC = 0; //C+1, -50.0 - 50.0
uint16_t battery_current = 0; int16_t system_temperature_min_dC = 0; //C+1, -50.0 - 50.0
uint16_t SOC = 5000; //SOC%, 0-100.00 (0-10000) int16_t system_active_power_W = 0; //Watts, -32000 to 32000
uint16_t StateOfHealth = 9900; //SOH%, 0-100.00 (0-10000) int16_t system_battery_current_dA = 0; //A+1, -1000 - 1000
uint16_t capacity_Wh = BATTERY_WH_MAX; //Wh, 0-60000 uint16_t system_battery_voltage_dV = 3700; //V+1, 0-500.0 (0-5000)
uint16_t remaining_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-60000 uint16_t system_max_design_voltage_dV = 5000; //V+1, 0-500.0 (0-5000)
uint16_t max_target_discharge_power = 0; // 0W (0W > restricts to no discharge), Updates later on from CAN uint16_t system_min_design_voltage_dV = 2500; //V+1, 0-500.0 (0-5000)
uint16_t max_target_charge_power = 4312; // Init to 4.3kW, Updates later on from CAN uint16_t system_scaled_SOC_pptt = 5000; //SOC%, 0-100.00 (0-10000)
uint16_t temperature_max = 50; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) uint16_t system_real_SOC_pptt = 5000; //SOC%, 0-100.00 (0-10000)
uint16_t temperature_min = 60; // Reads from battery later uint16_t system_SOH_pptt = 9900; //SOH%, 0-100.00 (0-10000)
uint8_t bms_status = ACTIVE; // ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING] uint16_t system_max_discharge_power_W = 0; //Watts, 0 to 65535
uint16_t stat_batt_power = 0; // Power going in/out of battery uint16_t system_max_charge_power_W = 4312; //Watts, 0 to 65535
uint16_t cell_max_voltage = 3700; // Stores the highest cell voltage value in the system uint16_t system_cell_max_voltage_mV = 3700; //mV, 0-5000 , Stores the highest cell millivolt value
uint16_t cell_min_voltage = 3700; // Stores the minimum cell voltage value in the system uint16_t system_cell_min_voltage_mV = 3700; //mV, 0-5000, Stores the minimum cell millivolt value
uint16_t cellvoltages[120]; // Stores all cell voltages uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV. Oversized to accomodate all setups
uint8_t nof_cellvoltages = 0; // Total number of cell voltages, set by each battery. uint8_t system_bms_status = ACTIVE; //ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
bool LFP_Chemistry = false; uint8_t system_number_of_cells = 0; //Total number of cell voltages, set by each battery
bool system_LFP_Chemistry = false; //Set to true or false depending on cell chemistry
// Common charger parameters // Common charger parameters
volatile float charger_setpoint_HV_VDC = 0.0f; volatile float charger_setpoint_HV_VDC = 0.0f;
@ -185,6 +186,7 @@ void loop() {
if (millis() - previousMillisUpdateVal >= intervalUpdateValues) // Every 4.8s if (millis() - previousMillisUpdateVal >= intervalUpdateValues) // Every 4.8s
{ {
previousMillisUpdateVal = millis(); previousMillisUpdateVal = millis();
update_SOC(); // Check if real or calculated SOC% value should be sent
update_values(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus. update_values(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus.
if (DUMMY_EVENT_ENABLED) { if (DUMMY_EVENT_ENABLED) {
set_event(EVENT_DUMMY_ERROR, (uint8_t)millis()); set_event(EVENT_DUMMY_ERROR, (uint8_t)millis());
@ -220,7 +222,7 @@ void init_stored_settings() {
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
#endif #endif
static uint16_t temp = 0; 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) {
BATTERY_WH_MAX = temp; BATTERY_WH_MAX = temp;
@ -240,7 +242,10 @@ void init_stored_settings() {
temp = settings.getUInt("MAXDISCHARGEAMP", false); temp = settings.getUInt("MAXDISCHARGEAMP", false);
if (temp != 0) { if (temp != 0) {
MAXDISCHARGEAMP = temp; MAXDISCHARGEAMP = temp;
} temp = settings.getBool("USE_SCALED_SOC", false);
USE_SCALED_SOC = temp; //This bool needs to be checked inside the temp!= block
} // No way to know if it wasnt reset otherwise
settings.end(); settings.end();
} }
@ -357,9 +362,7 @@ void inform_user_on_inverter() {
void init_battery() { void init_battery() {
// Inform user what battery is used and perform setup // Inform user what battery is used and perform setup
setup_battery(); setup_battery();
#ifdef SERIAL_LINK_RECEIVER
Serial.println("SERIAL_DATA_LINK_RECEIVER selected");
#endif
#ifndef BATTERY_SELECTED #ifndef BATTERY_SELECTED
#error No battery selected! Choose one from the USER_SETTINGS.h file #error No battery selected! Choose one from the USER_SETTINGS.h file
#endif #endif
@ -371,9 +374,11 @@ void receive_can() { // This section checks if we have a complete CAN message i
CAN_frame_t rx_frame; CAN_frame_t rx_frame;
if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) { if (xQueueReceive(CAN_cfg.rx_queue, &rx_frame, 3 * portTICK_PERIOD_MS) == pdTRUE) {
if (rx_frame.FIR.B.FF == CAN_frame_std) { if (rx_frame.FIR.B.FF == CAN_frame_std) {
//printf("New standard frame"); //printf("New standard frame");
// Battery // Battery
#ifndef SERIAL_LINK_RECEIVER
receive_can_battery(rx_frame); receive_can_battery(rx_frame);
#endif
// Inverter // Inverter
#ifdef BYD_CAN #ifdef BYD_CAN
receive_can_byd(rx_frame); receive_can_byd(rx_frame);
@ -517,7 +522,7 @@ void handle_LED_state() {
#ifdef CONTACTOR_CONTROL #ifdef CONTACTOR_CONTROL
void handle_contactors() { void handle_contactors() {
// First check if we have any active errors, incase we do, turn off the battery // First check if we have any active errors, incase we do, turn off the battery
if (bms_status == FAULT) { if (system_bms_status == FAULT) {
timeSpentInFaultedMode++; timeSpentInFaultedMode++;
} else { } else {
timeSpentInFaultedMode = 0; timeSpentInFaultedMode = 0;
@ -600,6 +605,23 @@ void handle_contactors() {
} }
#endif #endif
void update_SOC() {
if (USE_SCALED_SOC) { //User has configred a SOC window. Calculate a SOC% to send towards inverter
static int16_t CalculatedSOC = 0;
CalculatedSOC = system_real_SOC_pptt;
CalculatedSOC = (10000) * (CalculatedSOC - (MINPERCENTAGE * 10)) / (MAXPERCENTAGE * 10 - MINPERCENTAGE * 10);
if (CalculatedSOC < 0) { //We are in the real SOC% range of 0-MINPERCENTAGE%
CalculatedSOC = 0;
}
if (CalculatedSOC > 10000) { //We are in the real SOC% range of MAXPERCENTAGE-100%
CalculatedSOC = 10000;
}
system_scaled_SOC_pptt = CalculatedSOC;
} else { // No SOC window wanted. Set scaled to same as real.
system_scaled_SOC_pptt = system_real_SOC_pptt;
}
}
void update_values() { void update_values() {
// Battery // Battery
update_values_battery(); // Map the fake values to the correct registers update_values_battery(); // Map the fake values to the correct registers
@ -630,23 +652,22 @@ void update_values() {
#endif #endif
} }
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
void runSerialDataLink() { void runSerialDataLink() {
static unsigned long updateTime = 0; static unsigned long updateTime = 0;
unsigned long currentMillis = millis(); unsigned long currentMillis = millis();
#ifdef SERIAL_LINK_RECEIVER
if ((currentMillis - updateTime) > 1) { //Every 2ms
updateTime = currentMillis;
manageSerialLinkReceiver();
}
#endif
#ifdef SERIAL_LINK_TRANSMITTER
if ((currentMillis - updateTime) > 1) { //Every 2ms if ((currentMillis - updateTime) > 1) { //Every 2ms
updateTime = currentMillis; updateTime = currentMillis;
manageSerialLinkTransmitter(); #ifdef SERIAL_LINK_RECEIVER
} manageSerialLinkReceiver();
#endif #endif
#ifdef SERIAL_LINK_TRANSMITTER
manageSerialLinkTransmitter();
#endif
}
} }
#endif
void init_serialDataLink() { void init_serialDataLink() {
#if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER) #if defined(SERIAL_LINK_RECEIVER) || defined(SERIAL_LINK_TRANSMITTER)
@ -661,5 +682,7 @@ void storeSettings() {
settings.putUInt("MINPERCENTAGE", MINPERCENTAGE); settings.putUInt("MINPERCENTAGE", MINPERCENTAGE);
settings.putUInt("MAXCHARGEAMP", MAXCHARGEAMP); settings.putUInt("MAXCHARGEAMP", MAXCHARGEAMP);
settings.putUInt("MAXDISCHARGEAMP", MAXDISCHARGEAMP); settings.putUInt("MAXDISCHARGEAMP", MAXDISCHARGEAMP);
settings.putBool("USE_SCALED_SOC", USE_SCALED_SOC);
settings.end(); settings.end();
} }

View file

@ -4,16 +4,17 @@
/* They can be defined here, or later on in the WebUI */ /* They can be defined here, or later on in the WebUI */
/* Battery settings */ /* Battery settings */
volatile uint16_t BATTERY_WH_MAX = volatile bool USE_SCALED_SOC =
30000; //Battery size in Wh (Maximum value for most inverters is 65000 [65kWh], you can use larger batteries but do not set value over 65000! true; //Increases battery life. If true will rescale SOC between the configured min/max-percentage
volatile uint32_t BATTERY_WH_MAX = 30000; //Battery size in Wh
volatile uint16_t MAXPERCENTAGE = volatile uint16_t MAXPERCENTAGE =
800; //80.0% , Max percentage the battery will charge to (App will show 100% once this value is reached) 800; //80.0% , Max percentage the battery will charge to (Inverter gets 100% when reached)
volatile uint16_t MINPERCENTAGE = volatile uint16_t MINPERCENTAGE =
200; //20.0% , Min percentage the battery will discharge to (App will show 0% once this value is reached) 200; //20.0% , Min percentage the battery will discharge to (Inverter gets 0% when reached)
volatile uint16_t MAXCHARGEAMP = volatile uint16_t MAXCHARGEAMP =
300; //30.0A , BYD CAN specific setting, Max charge speed in Amp (Some inverters needs to be artificially limited) 300; //30.0A , BYD CAN specific setting, Max charge in Amp (Some inverters needs to be limited)
volatile uint16_t MAXDISCHARGEAMP = volatile uint16_t MAXDISCHARGEAMP =
300; //30.0A , BYD CAN specific setting, Max discharge speed in Amp (Some inverters needs to be artificially limited) 300; //30.0A , BYD CAN specific setting, Max discharge in Amp (Some inverters needs to be limited)
/* Charger settings (Optional, when generator charging) */ /* Charger settings (Optional, when generator charging) */
/* Charger settings */ /* Charger settings */

View file

@ -30,7 +30,7 @@
//#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus //#define SOLAX_CAN //Enable this line to emulate a "SolaX Triple Power LFP" over CAN bus
/* Other options */ /* Other options */
#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs //#define DEBUG_VIA_USB //Enable this line to have the USB port output serial diagnostic data while program runs
//#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting //#define INTERLOCK_REQUIRED //Nissan LEAF specific setting, if enabled requires both high voltage conenctors to be seated before starting
//#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 logic for contactors, which lower power consumption and heat generation //#define PWM_CONTACTOR_CONTROL //Enable this line to use PWM logic for contactors, which lower power consumption and heat generation
@ -38,7 +38,7 @@
//#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)
//#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery) //#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery)
#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 LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot //#define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot (overrides any battery settings set in USER_SETTINGS.cpp)
/* MQTT options */ /* MQTT options */
// #define MQTT // Enable this line to enable MQTT // #define MQTT // Enable this line to enable MQTT
@ -55,12 +55,13 @@
//#define NISSANLEAF_CHARGER //Enable this line to control a Nissan LEAF PDM connected to battery - for example, when generator charging //#define NISSANLEAF_CHARGER //Enable this line to control a Nissan LEAF PDM connected to battery - for example, when generator charging
/* Battery limits: These are set in the USER_SETTINGS.cpp file, or later on via the Webserver */ /* Battery limits: These are set in the USER_SETTINGS.cpp file, or later on via the Webserver */
extern volatile uint16_t BATTERY_WH_MAX; extern volatile uint32_t BATTERY_WH_MAX;
extern volatile uint16_t MAXPERCENTAGE; extern volatile uint16_t MAXPERCENTAGE;
extern volatile uint16_t MINPERCENTAGE; extern volatile uint16_t MINPERCENTAGE;
extern volatile uint16_t MAXCHARGEAMP; extern volatile uint16_t MAXCHARGEAMP;
extern volatile uint16_t MAXDISCHARGEAMP; extern volatile uint16_t MAXDISCHARGEAMP;
extern volatile uint8_t AccessPointEnabled; extern volatile uint8_t AccessPointEnabled;
extern volatile bool USE_SCALED_SOC;
/* Charger limits (Optional): Set in the USER_SETTINGS.cpp or later in the webserver */ /* Charger limits (Optional): Set in the USER_SETTINGS.cpp or later in the webserver */
extern volatile float charger_setpoint_HV_VDC; extern volatile float charger_setpoint_HV_VDC;

View file

@ -47,11 +47,13 @@
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h" //See this file for more Serial-battery settings #include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h" //See this file for more Serial-battery settings
#endif #endif
#ifndef SERIAL_LINK_RECEIVER // The serial thing does its thing #ifdef SERIAL_LINK_RECEIVER // The serial thing does its thing
void update_values_battery(); void receive_can_battery();
#else
void receive_can_battery(CAN_frame_t rx_frame); void receive_can_battery(CAN_frame_t rx_frame);
#endif
void update_values_battery();
void send_can_battery(); void send_can_battery();
void setup_battery(void); void setup_battery(void);
#endif
#endif #endif

View file

@ -16,9 +16,6 @@ static const int interval20 = 20; // interval (ms) at which send CAN
static const int interval600 = 600; // interval (ms) at which send CAN Messages static const int interval600 = 600; // interval (ms) at which send CAN Messages
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
#define LB_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Inverter
#define LB_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Inverter
CAN_frame_t BMW_10B = {.FIR = {.B = CAN_frame_t BMW_10B = {.FIR = {.B =
{ {
.DLC = 3, .DLC = 3,
@ -58,47 +55,38 @@ static int16_t Battery_Power = 0;
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
//Calculate the SOC% value to send to inverter //Calculate the SOC% value to send to inverter
Calculated_SOC = (Display_SOC * 10); //Increase decimal amount system_real_SOC_pptt = (Display_SOC * 100); //increase Display_SOC range from 0-100 -> 100.00
Calculated_SOC =
LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (Calculated_SOC - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
if (Calculated_SOC < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to inverter as 0%
Calculated_SOC = 0;
}
if (Calculated_SOC > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to inverter as 100%
Calculated_SOC = 1000;
}
SOC = (Calculated_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
battery_voltage = Battery_Volts; //Unit V+1 (5000 = 500.0V) system_battery_voltage_dV = Battery_Volts; //Unit V+1 (5000 = 500.0V)
battery_current = Battery_Current; system_battery_current_dA = Battery_Current;
capacity_Wh = BATTERY_WH_MAX; system_capacity_Wh = BATTERY_WH_MAX;
remaining_capacity_Wh = (Battery_Capacity_kWh * 1000); system_remaining_capacity_Wh = (Battery_Capacity_kWh * 1000);
if (SOC > 9900) //If Soc is over 99%, stop charging if (system_scaled_SOC_pptt > 9900) //If Soc is over 99%, stop charging
{ {
max_target_charge_power = 0; system_max_charge_power_W = 0;
} else { } else {
max_target_charge_power = 5000; //Hardcoded value for testing. TODO: read real value from battery when discovered system_max_charge_power_W = 5000; //Hardcoded value for testing. TODO: read real value from battery when discovered
} }
if (SOC < 500) //If Soc is under 5%, stop dicharging if (system_scaled_SOC_pptt < 500) //If Soc is under 5%, stop dicharging
{ {
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
} else { } else {
max_target_discharge_power = system_max_discharge_power_W =
5000; //Hardcoded value for testing. TODO: read real value from battery when discovered 5000; //Hardcoded value for testing. TODO: read real value from battery when discovered
} }
Battery_Power = (Battery_Current * (Battery_Volts / 10)); Battery_Power = (Battery_Current * (Battery_Volts / 10));
stat_batt_power = Battery_Power; //TODO:, is mapping OK? system_active_power_W = Battery_Power; //TODO:, is mapping OK?
temperature_min; //hardcoded to 5*C in startup, TODO:, find from battery CAN later system_temperature_min_dC; //hardcoded to 5*C in startup, TODO:, find from battery CAN later
temperature_max; //hardcoded to 6*C in startup, TODO:, find from battery CAN later system_temperature_max_dC; //hardcoded to 6*C in startup, TODO:, find from battery CAN later
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/ /* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) { if (!CANstillAlive) {
@ -112,15 +100,15 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.print("SOC% battery: "); Serial.print("SOC% battery: ");
Serial.print(Display_SOC); Serial.print(Display_SOC);
Serial.print(" SOC% sent to inverter: "); Serial.print(" SOC% sent to inverter: ");
Serial.print(SOC); Serial.print(system_scaled_SOC_pptt);
Serial.print(" Battery voltage: "); Serial.print(" Battery voltage: ");
Serial.print(battery_voltage); Serial.print(system_battery_voltage_dV);
Serial.print(" Remaining Wh: "); Serial.print(" Remaining Wh: ");
Serial.print(remaining_capacity_Wh); Serial.print(system_remaining_capacity_Wh);
Serial.print(" Max charge power: "); Serial.print(" Max charge power: ");
Serial.print(max_target_charge_power); Serial.print(system_max_charge_power_W);
Serial.print(" Max discharge power: "); Serial.print(" Max discharge power: ");
Serial.print(max_target_discharge_power); Serial.print(system_max_discharge_power_W);
#endif #endif
} }
@ -196,8 +184,8 @@ void send_can_battery() {
void setup_battery(void) { // Performs one time setup at startup void setup_battery(void) { // Performs one time setup at startup
Serial.println("BMW i3 battery selected"); Serial.println("BMW i3 battery selected");
max_voltage = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge) system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
min_voltage = 3100; // 310.0V under this, discharging further is disabled system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
} }
#endif #endif

View file

@ -8,24 +8,26 @@
#define BATTERY_SELECTED #define BATTERY_SELECTED
// These parameters need to be mapped for the inverter // These parameters need to be mapped for the inverter
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery. extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
void setup_battery(void); void setup_battery(void);

View file

@ -93,16 +93,16 @@ uint8_t HighVoltageControlStatus = 0;
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for the inverter
SOC = ChargingRate; system_real_SOC_pptt = ChargingRate;
max_target_discharge_power = (MaximumDischargeCurrent * MaximumBatteryVoltage); //In Watts, Convert A to P system_max_discharge_power_W = (MaximumDischargeCurrent * MaximumBatteryVoltage); //In Watts, Convert A to P
battery_voltage = TargetBatteryVoltage; //TODO: scaling? system_battery_voltage_dV = TargetBatteryVoltage; //TODO: scaling?
capacity_Wh = ((RatedBatteryCapacity / 0.11) * system_capacity_Wh = ((RatedBatteryCapacity / 0.11) *
1000); //(Added in CHAdeMO v1.0.1), maybe handle hardcoded on lower protocol version? 1000); //(Added in CHAdeMO v1.0.1), maybe handle hardcoded on lower protocol version?
remaining_capacity_Wh = (SOC / 100) * capacity_Wh; system_remaining_capacity_Wh = (system_real_SOC_pptt / 100) * system_capacity_Wh;
/* Check if the Vehicle is still sending CAN messages. If we go 60s without messages we raise an error*/ /* Check if the Vehicle is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) { if (!CANstillAlive) {
@ -119,19 +119,19 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.println(errorCode); Serial.println(errorCode);
} }
Serial.print("BMS Status (3=OK): "); Serial.print("BMS Status (3=OK): ");
Serial.println(bms_status); Serial.println(system_bms_status);
Serial.print("Max discharge power: "); Serial.print("Max discharge power: ");
Serial.println(max_target_discharge_power); Serial.println(system_max_discharge_power_W);
Serial.print("Max charge power: "); Serial.print("Max charge power: ");
Serial.println(max_target_charge_power); Serial.println(system_max_charge_power_W);
Serial.print("SOH%: "); Serial.print("SOH%: ");
Serial.println(StateOfHealth); Serial.println(system_SOH_pptt);
Serial.print("SOC% to Inverter: "); Serial.print("SOC% to Inverter: ");
Serial.println(SOC); Serial.println(system_scaled_SOC_pptt);
Serial.print("Temperature Min: "); Serial.print("Temperature Min: ");
Serial.println(temperature_min); Serial.println(system_temperature_min_dC);
Serial.print("Temperature Max: "); Serial.print("Temperature Max: ");
Serial.println(temperature_max); Serial.println(system_temperature_max_dC);
#endif #endif
} }
@ -211,7 +211,7 @@ void send_can_battery() {
void setup_battery(void) { // Performs one time setup at startup void setup_battery(void) { // Performs one time setup at startup
Serial.println("Chademo battery selected"); Serial.println("Chademo battery selected");
max_voltage = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge) system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
min_voltage = 2000; // 200.0V under this, discharging further is disabled system_min_design_voltage_dV = 2000; // 200.0V under this, discharging further is disabled
} }
#endif #endif

View file

@ -8,24 +8,26 @@
#define BATTERY_SELECTED #define BATTERY_SELECTED
// These parameters need to be mapped for the inverter // These parameters need to be mapped for the inverter
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery. extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
void setup_battery(void); void setup_battery(void);

View file

@ -9,8 +9,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 BMU_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to inverter
#define BMU_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to inverter
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
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;
@ -43,30 +41,30 @@ static double max_temp_cel = 20.00;
static double min_temp_cel = 19.00; static double min_temp_cel = 19.00;
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
SOC = (uint16_t)(BMU_SOC * 100); //increase BMU_SOC range from 0-100 -> 100.00 system_real_SOC_pptt = (uint16_t)(BMU_SOC * 100); //increase BMU_SOC range from 0-100 -> 100.00
battery_voltage = (uint16_t)(BMU_PackVoltage * 10); // Multiply by 10 and cast to uint16_t system_battery_voltage_dV = (uint16_t)(BMU_PackVoltage * 10); // Multiply by 10 and cast to uint16_t
battery_current = (BMU_Current * 10); //Todo, scaling? system_battery_current_dA = (BMU_Current * 10); //Todo, scaling?
capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value system_capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value
remaining_capacity_Wh = (uint16_t)((SOC / 10000) * capacity_Wh); system_remaining_capacity_Wh = (uint16_t)((system_real_SOC_pptt / 10000) * system_capacity_Wh);
//We do not know if the max charge power is sent by the battery. So we estimate the value based on SOC% //We do not know if the max charge power is sent by the battery. So we estimate the value based on SOC%
if (SOC == 10000) { //100.00% if (system_scaled_SOC_pptt == 10000) { //100.00%
max_target_charge_power = 0; //When battery is 100% full, set allowed charge W to 0 system_max_charge_power_W = 0; //When battery is 100% full, set allowed charge W to 0
} else { } else {
max_target_charge_power = 10000; //Otherwise we can push 10kW into the pack! system_max_charge_power_W = 10000; //Otherwise we can push 10kW into the pack!
} }
if (SOC < 200) { //2.00% if (system_scaled_SOC_pptt < 200) { //2.00%
max_target_discharge_power = 0; //When battery is empty (below 2%), set allowed discharge W to 0 system_max_discharge_power_W = 0; //When battery is empty (below 2%), set allowed discharge W to 0
} else { } else {
max_target_discharge_power = 10000; //Otherwise we can discharge 10kW from the pack! system_max_discharge_power_W = 10000; //Otherwise we can discharge 10kW from the pack!
} }
stat_batt_power = BMU_Power; //TODO: Scaling? system_active_power_W = BMU_Power; //TODO: Scaling?
static int n = sizeof(cell_voltages) / sizeof(cell_voltages[0]); static int n = sizeof(cell_voltages) / sizeof(cell_voltages[0]);
max_volt_cel = cell_voltages[0]; // Initialize max with the first element of the array max_volt_cel = cell_voltages[0]; // Initialize max with the first element of the array
@ -98,13 +96,13 @@ void update_values_battery() { //This function maps all the values fetched via
} }
} }
cell_max_voltage = (uint16_t)(max_volt_cel * 1000); system_cell_max_voltage_mV = (uint16_t)(max_volt_cel * 1000);
cell_min_voltage = (uint16_t)(min_volt_cel * 1000); system_cell_min_voltage_mV = (uint16_t)(min_volt_cel * 1000);
temperature_min = (uint16_t)(min_temp_cel * 1000); system_temperature_min_dC = (int16_t)(min_temp_cel * 1000);
temperature_max = (uint16_t)(max_temp_cel * 1000); system_temperature_min_dC = (int16_t)(max_temp_cel * 1000);
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/ /* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) { if (!CANstillAlive) {
@ -140,25 +138,25 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.println("Values sent to inverter"); Serial.println("Values sent to inverter");
Serial.print("SOC% (0-100.00): "); Serial.print("SOC% (0-100.00): ");
Serial.print(SOC); Serial.print(system_scaled_SOC_pptt);
Serial.print(" Voltage (0-400.0): "); Serial.print(" Voltage (0-400.0): ");
Serial.print(battery_voltage); Serial.print(system_battery_voltage_dV);
Serial.print(" Capacity WH full (0-60000): "); Serial.print(" Capacity WH full (0-60000): ");
Serial.print(capacity_Wh); Serial.print(system_capacity_Wh);
Serial.print(" Capacity WH remain (0-60000): "); Serial.print(" Capacity WH remain (0-60000): ");
Serial.print(remaining_capacity_Wh); Serial.print(system_remaining_capacity_Wh);
Serial.print(" Max charge power W (0-10000): "); Serial.print(" Max charge power W (0-10000): ");
Serial.print(max_target_charge_power); Serial.print(system_max_charge_power_W);
Serial.print(" Max discharge power W (0-10000): "); Serial.print(" Max discharge power W (0-10000): ");
Serial.print(max_target_discharge_power); Serial.print(system_max_discharge_power_W);
Serial.print(" Temp max "); Serial.print(" Temp max ");
Serial.print(temperature_max); Serial.print(system_temperature_max_dC);
Serial.print(" Temp min "); Serial.print(" Temp min ");
Serial.print(temperature_min); Serial.print(system_temperature_min_dC);
Serial.print(" Cell mV max "); Serial.print(" Cell mV max ");
Serial.print(cell_max_voltage); Serial.print(system_cell_max_voltage_mV);
Serial.print(" Cell mV min "); Serial.print(" Cell mV min ");
Serial.print(cell_min_voltage); Serial.print(system_cell_min_voltage_mV);
#endif #endif
} }
@ -230,8 +228,8 @@ void send_can_battery() {
void setup_battery(void) { // Performs one time setup at startup void setup_battery(void) { // Performs one time setup at startup
Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected"); Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected");
max_voltage = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge) system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
min_voltage = 3100; // 310.0V under this, discharging further is disabled system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
} }
#endif #endif

View file

@ -7,25 +7,27 @@
#define BATTERY_SELECTED #define BATTERY_SELECTED
// These parameters need to be mapped for the Inverter // These parameters need to be mapped for the inverter
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery. extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
void setup_battery(void); void setup_battery(void);

View file

@ -12,8 +12,6 @@ static const int interval100 = 100; // interval (ms) at which send CAN
static const int interval10ms = 10; // interval (ms) at which send CAN Messages static const int interval10ms = 10; // interval (ms) at which send CAN Messages
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
#define MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Inverter
#define MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Inverter
#define MAX_CELL_VOLTAGE 4250 //Battery is put into emergency stop if one cell goes over this value #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 #define MIN_CELL_VOLTAGE 2950 //Battery is put into emergency stop if one cell goes below this value
#define MAX_CELL_DEVIATION 150 //LED turns yellow on the board if mv delta exceeds this value #define MAX_CELL_DEVIATION 150 //LED turns yellow on the board if mv delta exceeds this value
@ -150,52 +148,43 @@ CAN_frame_t KIA64_7E4_ack = {
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
//Calculate the SOC% value to send to inverter system_real_SOC_pptt = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
soc_calculated = SOC_Display;
soc_calculated = MIN_SOC + (MAX_SOC - MIN_SOC) * (soc_calculated - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
if (soc_calculated < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Inverter as 0%
soc_calculated = 0;
}
if (soc_calculated > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Inverter as 100%
soc_calculated = 1000;
}
SOC = (soc_calculated * 10); //increase SOC range from 0-100.0 -> 100.00
StateOfHealth = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00% system_SOH_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
battery_voltage = batteryVoltage; //value is *10 (3700 = 370.0) system_battery_voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0)
battery_current = convertToUnsignedInt16(batteryAmps); //value is *10 (150 = 15.0) system_battery_current_dA = batteryAmps; //value is *10 (150 = 15.0)
capacity_Wh = BATTERY_WH_MAX; system_capacity_Wh = BATTERY_WH_MAX;
remaining_capacity_Wh = static_cast<int>((static_cast<double>(SOC) / 10000) * BATTERY_WH_MAX); system_remaining_capacity_Wh = static_cast<int>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
//max_target_charge_power = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts //system_max_charge_power_W = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts
//The allowed charge power is not available. We estimate this value //The allowed charge power is not available. We estimate this value
if (SOC == 10000) { // When scaled SOC is 100%, set allowed charge power to 0 if (system_scaled_SOC_pptt == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
max_target_charge_power = 0; system_max_charge_power_W = 0;
} else { // No limits, max charging power allowed } else { // No limits, max charging power allowed
max_target_charge_power = MAXCHARGEPOWERALLOWED; system_max_charge_power_W = MAXCHARGEPOWERALLOWED;
} }
//max_target_discharge_power = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts //system_max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
if (SOC < 100) { // When scaled SOC is <1%, set allowed charge power to 0 if (system_scaled_SOC_pptt < 100) { // When scaled SOC is <1%, set allowed charge power to 0
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
} else { // No limits, max charging power allowed } else { // No limits, max charging power allowed
max_target_discharge_power = MAXDISCHARGEPOWERALLOWED; system_max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
} }
powerWatt = ((batteryVoltage * batteryAmps) / 100); powerWatt = ((batteryVoltage * batteryAmps) / 100);
stat_batt_power = convertToUnsignedInt16(powerWatt); //Power in watts, Negative = charging batt system_active_power_W = powerWatt; //Power in watts, Negative = charging batt
temperature_min = convertToUnsignedInt16((int8_t)temperatureMin * 10); //Increase decimals, 17C -> 17.0C system_temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C
temperature_max = convertToUnsignedInt16((int8_t)temperatureMax * 10); //Increase decimals, 18C -> 18.0C system_temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C
cell_max_voltage = CellVoltMax_mV; system_cell_max_voltage_mV = CellVoltMax_mV;
cell_min_voltage = CellVoltMin_mV; system_cell_min_voltage_mV = CellVoltMin_mV;
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/ /* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) { if (!CANstillAlive) {
@ -214,12 +203,12 @@ void update_values_battery() { //This function maps all the values fetched via
} }
// Check if cell voltages are within allowed range // Check if cell voltages are within allowed range
cell_deviation_mV = (cell_max_voltage - cell_min_voltage); cell_deviation_mV = (system_cell_max_voltage_mV - system_cell_min_voltage_mV);
if (cell_max_voltage >= MAX_CELL_VOLTAGE) { if (CellVoltMax_mV >= MAX_CELL_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0); set_event(EVENT_CELL_OVER_VOLTAGE, 0);
} }
if (cell_min_voltage <= MIN_CELL_VOLTAGE) { if (CellVoltMin_mV <= MIN_CELL_VOLTAGE) {
set_event(EVENT_CELL_UNDER_VOLTAGE, 0); set_event(EVENT_CELL_UNDER_VOLTAGE, 0);
} }
if (cell_deviation_mV > MAX_CELL_DEVIATION) { if (cell_deviation_mV > MAX_CELL_DEVIATION) {
@ -228,9 +217,9 @@ void update_values_battery() { //This function maps all the values fetched via
clear_event(EVENT_CELL_DEVIATION_HIGH); clear_event(EVENT_CELL_DEVIATION_HIGH);
} }
if (bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
max_target_charge_power = 0; system_max_charge_power_W = 0;
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
} }
/* Safeties verified. Perform USB serial printout if configured to do so */ /* Safeties verified. Perform USB serial printout if configured to do so */
@ -249,7 +238,7 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.print(" Amps | "); Serial.print(" Amps | ");
Serial.print((uint16_t)batteryVoltage / 10.0, 1); Serial.print((uint16_t)batteryVoltage / 10.0, 1);
Serial.print(" Volts | "); Serial.print(" Volts | ");
Serial.print((int16_t)stat_batt_power); Serial.print((int16_t)system_active_power_W);
Serial.println(" Watts"); Serial.println(" Watts");
Serial.print("Allowed Charge "); Serial.print("Allowed Charge ");
Serial.print((uint16_t)allowedChargePower * 10); Serial.print((uint16_t)allowedChargePower * 10);
@ -364,53 +353,53 @@ void receive_can_battery(CAN_frame_t rx_frame) {
allowedDischargePower = ((rx_frame.data.u8[5] << 8) + rx_frame.data.u8[6]); allowedDischargePower = ((rx_frame.data.u8[5] << 8) + rx_frame.data.u8[6]);
batteryRelay = rx_frame.data.u8[7]; batteryRelay = rx_frame.data.u8[7];
} else if (poll_data_pid == 2) { } else if (poll_data_pid == 2) {
cellvoltages[0] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[0] = (rx_frame.data.u8[2] * 20);
cellvoltages[1] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[1] = (rx_frame.data.u8[3] * 20);
cellvoltages[2] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[2] = (rx_frame.data.u8[4] * 20);
cellvoltages[3] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[3] = (rx_frame.data.u8[5] * 20);
cellvoltages[4] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[4] = (rx_frame.data.u8[6] * 20);
cellvoltages[5] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[5] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 3) { } else if (poll_data_pid == 3) {
cellvoltages[32] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[32] = (rx_frame.data.u8[2] * 20);
cellvoltages[33] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[33] = (rx_frame.data.u8[3] * 20);
cellvoltages[34] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[34] = (rx_frame.data.u8[4] * 20);
cellvoltages[35] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[35] = (rx_frame.data.u8[5] * 20);
cellvoltages[36] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[36] = (rx_frame.data.u8[6] * 20);
cellvoltages[37] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[37] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 4) { } else if (poll_data_pid == 4) {
cellvoltages[64] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[64] = (rx_frame.data.u8[2] * 20);
cellvoltages[65] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[65] = (rx_frame.data.u8[3] * 20);
cellvoltages[66] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[66] = (rx_frame.data.u8[4] * 20);
cellvoltages[67] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[67] = (rx_frame.data.u8[5] * 20);
cellvoltages[68] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[68] = (rx_frame.data.u8[6] * 20);
cellvoltages[69] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[69] = (rx_frame.data.u8[7] * 20);
} }
break; break;
case 0x22: //Second datarow in PID group case 0x22: //Second datarow in PID group
if (poll_data_pid == 2) { if (poll_data_pid == 2) {
cellvoltages[6] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[6] = (rx_frame.data.u8[1] * 20);
cellvoltages[7] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[7] = (rx_frame.data.u8[2] * 20);
cellvoltages[8] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[8] = (rx_frame.data.u8[3] * 20);
cellvoltages[9] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[9] = (rx_frame.data.u8[4] * 20);
cellvoltages[10] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[10] = (rx_frame.data.u8[5] * 20);
cellvoltages[11] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[11] = (rx_frame.data.u8[6] * 20);
cellvoltages[12] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[12] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 3) { } else if (poll_data_pid == 3) {
cellvoltages[38] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[38] = (rx_frame.data.u8[1] * 20);
cellvoltages[39] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[39] = (rx_frame.data.u8[2] * 20);
cellvoltages[40] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[40] = (rx_frame.data.u8[3] * 20);
cellvoltages[41] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[41] = (rx_frame.data.u8[4] * 20);
cellvoltages[42] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[42] = (rx_frame.data.u8[5] * 20);
cellvoltages[43] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[43] = (rx_frame.data.u8[6] * 20);
cellvoltages[44] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[44] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 4) { } else if (poll_data_pid == 4) {
cellvoltages[70] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[70] = (rx_frame.data.u8[1] * 20);
cellvoltages[71] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[71] = (rx_frame.data.u8[2] * 20);
cellvoltages[72] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[72] = (rx_frame.data.u8[3] * 20);
cellvoltages[73] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[73] = (rx_frame.data.u8[4] * 20);
cellvoltages[74] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[74] = (rx_frame.data.u8[5] * 20);
cellvoltages[75] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[75] = (rx_frame.data.u8[6] * 20);
cellvoltages[76] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[76] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 6) { } else if (poll_data_pid == 6) {
batteryManagementMode = rx_frame.data.u8[5]; batteryManagementMode = rx_frame.data.u8[5];
} }
@ -420,29 +409,29 @@ void receive_can_battery(CAN_frame_t rx_frame) {
temperature_water_inlet = rx_frame.data.u8[6]; temperature_water_inlet = rx_frame.data.u8[6];
CellVoltMax_mV = (rx_frame.data.u8[7] * 20); //(volts *50) *20 =mV CellVoltMax_mV = (rx_frame.data.u8[7] * 20); //(volts *50) *20 =mV
} else if (poll_data_pid == 2) { } else if (poll_data_pid == 2) {
cellvoltages[13] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[13] = (rx_frame.data.u8[1] * 20);
cellvoltages[14] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[14] = (rx_frame.data.u8[2] * 20);
cellvoltages[15] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[15] = (rx_frame.data.u8[3] * 20);
cellvoltages[16] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[16] = (rx_frame.data.u8[4] * 20);
cellvoltages[17] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[17] = (rx_frame.data.u8[5] * 20);
cellvoltages[18] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[18] = (rx_frame.data.u8[6] * 20);
cellvoltages[19] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[19] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 3) { } else if (poll_data_pid == 3) {
cellvoltages[45] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[45] = (rx_frame.data.u8[1] * 20);
cellvoltages[46] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[46] = (rx_frame.data.u8[2] * 20);
cellvoltages[47] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[47] = (rx_frame.data.u8[3] * 20);
cellvoltages[48] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[48] = (rx_frame.data.u8[4] * 20);
cellvoltages[49] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[49] = (rx_frame.data.u8[5] * 20);
cellvoltages[50] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[50] = (rx_frame.data.u8[6] * 20);
cellvoltages[51] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[51] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 4) { } else if (poll_data_pid == 4) {
cellvoltages[77] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[77] = (rx_frame.data.u8[1] * 20);
cellvoltages[78] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[78] = (rx_frame.data.u8[2] * 20);
cellvoltages[79] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[79] = (rx_frame.data.u8[3] * 20);
cellvoltages[80] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[80] = (rx_frame.data.u8[4] * 20);
cellvoltages[81] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[81] = (rx_frame.data.u8[5] * 20);
cellvoltages[82] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[82] = (rx_frame.data.u8[6] * 20);
cellvoltages[83] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[83] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 5) { } else if (poll_data_pid == 5) {
heatertemp = rx_frame.data.u8[7]; heatertemp = rx_frame.data.u8[7];
} }
@ -453,56 +442,56 @@ void receive_can_battery(CAN_frame_t rx_frame) {
CellVminNo = rx_frame.data.u8[3]; CellVminNo = rx_frame.data.u8[3];
CellVoltMin_mV = (rx_frame.data.u8[2] * 20); //(volts *50) *20 =mV CellVoltMin_mV = (rx_frame.data.u8[2] * 20); //(volts *50) *20 =mV
} else if (poll_data_pid == 2) { } else if (poll_data_pid == 2) {
cellvoltages[20] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[20] = (rx_frame.data.u8[1] * 20);
cellvoltages[21] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[21] = (rx_frame.data.u8[2] * 20);
cellvoltages[22] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[22] = (rx_frame.data.u8[3] * 20);
cellvoltages[23] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[23] = (rx_frame.data.u8[4] * 20);
cellvoltages[24] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[24] = (rx_frame.data.u8[5] * 20);
cellvoltages[25] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[25] = (rx_frame.data.u8[6] * 20);
cellvoltages[26] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[26] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 3) { } else if (poll_data_pid == 3) {
cellvoltages[52] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[52] = (rx_frame.data.u8[1] * 20);
cellvoltages[53] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[53] = (rx_frame.data.u8[2] * 20);
cellvoltages[54] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[54] = (rx_frame.data.u8[3] * 20);
cellvoltages[55] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[55] = (rx_frame.data.u8[4] * 20);
cellvoltages[56] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[56] = (rx_frame.data.u8[5] * 20);
cellvoltages[57] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[57] = (rx_frame.data.u8[6] * 20);
cellvoltages[58] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[58] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 4) { } else if (poll_data_pid == 4) {
cellvoltages[84] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[84] = (rx_frame.data.u8[1] * 20);
cellvoltages[85] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[85] = (rx_frame.data.u8[2] * 20);
cellvoltages[86] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[86] = (rx_frame.data.u8[3] * 20);
cellvoltages[87] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[87] = (rx_frame.data.u8[4] * 20);
cellvoltages[88] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[88] = (rx_frame.data.u8[5] * 20);
cellvoltages[89] = (rx_frame.data.u8[6] * 20); system_cellvoltages_mV[89] = (rx_frame.data.u8[6] * 20);
cellvoltages[90] = (rx_frame.data.u8[7] * 20); system_cellvoltages_mV[90] = (rx_frame.data.u8[7] * 20);
} else if (poll_data_pid == 5) { } else if (poll_data_pid == 5) {
batterySOH = ((rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3]); batterySOH = ((rx_frame.data.u8[2] << 8) + rx_frame.data.u8[3]);
} }
break; break;
case 0x25: //Fifth datarow in PID group case 0x25: //Fifth datarow in PID group
if (poll_data_pid == 2) { if (poll_data_pid == 2) {
cellvoltages[27] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[27] = (rx_frame.data.u8[1] * 20);
cellvoltages[28] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[28] = (rx_frame.data.u8[2] * 20);
cellvoltages[29] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[29] = (rx_frame.data.u8[3] * 20);
cellvoltages[30] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[30] = (rx_frame.data.u8[4] * 20);
cellvoltages[31] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[31] = (rx_frame.data.u8[5] * 20);
} else if (poll_data_pid == 3) { } else if (poll_data_pid == 3) {
cellvoltages[59] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[59] = (rx_frame.data.u8[1] * 20);
cellvoltages[60] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[60] = (rx_frame.data.u8[2] * 20);
cellvoltages[61] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[61] = (rx_frame.data.u8[3] * 20);
cellvoltages[62] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[62] = (rx_frame.data.u8[4] * 20);
cellvoltages[63] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[63] = (rx_frame.data.u8[5] * 20);
} else if (poll_data_pid == 4) { } else if (poll_data_pid == 4) {
cellvoltages[91] = (rx_frame.data.u8[1] * 20); system_cellvoltages_mV[91] = (rx_frame.data.u8[1] * 20);
cellvoltages[92] = (rx_frame.data.u8[2] * 20); system_cellvoltages_mV[92] = (rx_frame.data.u8[2] * 20);
cellvoltages[93] = (rx_frame.data.u8[3] * 20); system_cellvoltages_mV[93] = (rx_frame.data.u8[3] * 20);
cellvoltages[94] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[94] = (rx_frame.data.u8[4] * 20);
cellvoltages[95] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[95] = (rx_frame.data.u8[5] * 20);
} else if (poll_data_pid == 5) { } else if (poll_data_pid == 5) {
cellvoltages[96] = (rx_frame.data.u8[4] * 20); system_cellvoltages_mV[96] = (rx_frame.data.u8[4] * 20);
cellvoltages[97] = (rx_frame.data.u8[5] * 20); system_cellvoltages_mV[97] = (rx_frame.data.u8[5] * 20);
nof_cellvoltages = 98; system_number_of_cells = 98;
} }
break; break;
case 0x26: //Sixth datarow in PID group case 0x26: //Sixth datarow in PID group
@ -591,19 +580,11 @@ void send_can_battery() {
} }
} }
uint16_t convertToUnsignedInt16(int16_t signed_value) {
if (signed_value < 0) {
return (65535 + signed_value);
} else {
return (uint16_t)signed_value;
}
}
void setup_battery(void) { // Performs one time setup at startup void setup_battery(void) { // Performs one time setup at startup
Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected"); Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected");
max_voltage = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge) system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
min_voltage = 3100; // 310.0V under this, discharging further is disabled system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
} }
#endif #endif

View file

@ -11,27 +11,28 @@
#define MAXDISCHARGEPOWERALLOWED 10000 #define MAXDISCHARGEPOWERALLOWED 10000
// These parameters need to be mapped for the inverter // These parameters need to be mapped for the inverter
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery. extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
uint16_t convertToUnsignedInt16(int16_t signed_value);
void setup_battery(void); void setup_battery(void);
#endif #endif

View file

@ -97,13 +97,10 @@ static uint8_t LEAF_Battery_Type = ZE0_BATTERY;
#define MIN_CELL_VOLTAGE 2700 //Battery is put into emergency stop if one cell goes below this value #define MIN_CELL_VOLTAGE 2700 //Battery is put into emergency stop if one cell goes below this value
#define MAX_CELL_DEVIATION 500 //LED turns yellow on the board if mv delta exceeds this value #define MAX_CELL_DEVIATION 500 //LED turns yellow on the board if mv delta exceeds 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
#define LB_MAX_SOC 1000 //LEAF BMS never goes over this value. We use this info to rescale SOC% sent to Fronius
#define LB_MIN_SOC 0 //LEAF BMS never goes below this value. We use this info to rescale SOC% sent to Fronius
static uint16_t LB_Discharge_Power_Limit = 0; //Limit in kW static uint16_t LB_Discharge_Power_Limit = 0; //Limit in kW
static uint16_t LB_Charge_Power_Limit = 0; //Limit in kW static uint16_t LB_Charge_Power_Limit = 0; //Limit in kW
static int16_t LB_MAX_POWER_FOR_CHARGER = 0; //Limit in kW static int16_t LB_MAX_POWER_FOR_CHARGER = 0; //Limit in kW
static int16_t LB_SOC = 500; //0 - 100.0 % (0-1000) The real SOC% in the battery static int16_t LB_SOC = 500; //0 - 100.0 % (0-1000) The real SOC% in the battery
static int16_t CalculatedSOC = 0; // Temporary value used for calculating SOC
static uint16_t LB_TEMP = 0; //Temporary value used in status checks static uint16_t LB_TEMP = 0; //Temporary value used in status checks
static uint16_t LB_Wh_Remaining = 0; //Amount of energy in battery, in Wh static uint16_t LB_Wh_Remaining = 0; //Amount of energy in battery, in Wh
static uint16_t LB_GIDS = 273; //Startup in 24kWh mode static uint16_t LB_GIDS = 273; //Startup in 24kWh mode
@ -170,94 +167,84 @@ void print_with_units(char* header, int value, char* units) {
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 */
/* Start with mapping all values */ /* Start with mapping all values */
StateOfHealth = (LB_StateOfHealth * 100); //Increase range from 99% -> 99.00% system_SOH_pptt = (LB_StateOfHealth * 100); //Increase range from 99% -> 99.00%
//Calculate the SOC% value to send to Fronius system_real_SOC_pptt = (LB_SOC * 10);
CalculatedSOC = LB_SOC;
CalculatedSOC =
LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (CalculatedSOC - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
if (CalculatedSOC < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Fronius as 0%
CalculatedSOC = 0;
}
if (CalculatedSOC > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Fronius as 100%
CalculatedSOC = 1000;
}
SOC = (CalculatedSOC * 10); //increase CalculatedSOC range from 0-100.0 -> 100.00
battery_voltage = (LB_Total_Voltage2 * 5); //0.5V /bit, multiply by 5 to get Voltage+1decimal (350.5V = 701) system_battery_voltage_dV = (LB_Total_Voltage2 * 5); //0.5V/bit, multiply by 5 to get Voltage+1decimal (350.5V = 701)
battery_current = convert2unsignedint16((LB_Current2 * 5)); //0.5A/bit, multiply by 5 to get Amp+1decimal (5,5A = 11) system_battery_current_dA = (LB_Current2 * 5); //0.5A/bit, multiply by 5 to get Amp+1decimal (5,5A = 11)
capacity_Wh = (LB_Max_GIDS * WH_PER_GID); system_capacity_Wh = (LB_Max_GIDS * WH_PER_GID);
remaining_capacity_Wh = LB_Wh_Remaining; system_remaining_capacity_Wh = LB_Wh_Remaining;
LB_Power = LB_Power =
((LB_Total_Voltage2 * LB_Current2) / 4); //P = U * I (Both values are 0.5 per bit so the math is non-intuitive) ((LB_Total_Voltage2 * LB_Current2) / 4); //P = U * I (Both values are 0.5 per bit so the math is non-intuitive)
stat_batt_power = convert2unsignedint16(LB_Power); //add sign if needed
system_active_power_W = LB_Power;
//Update temperature readings. Method depends on which generation LEAF battery is used //Update temperature readings. Method depends on which generation LEAF battery is used
if (LEAF_Battery_Type == ZE0_BATTERY) { if (LEAF_Battery_Type == ZE0_BATTERY) {
//Since we only have average value, send the minimum as -1.0 degrees below average //Since we only have average value, send the minimum as -1.0 degrees below average
temperature_min = system_temperature_min_dC = ((LB_AverageTemperature * 10) - 10); //Increase range from C to C+1, remove 1.0C
convert2unsignedint16((LB_AverageTemperature * 10) - 10); //add sign if negative and increase range system_temperature_max_dC = (LB_AverageTemperature * 10); //Increase range from C to C+1
temperature_max = convert2unsignedint16((LB_AverageTemperature * 10));
} else if (LEAF_Battery_Type == AZE0_BATTERY) { } else if (LEAF_Battery_Type == AZE0_BATTERY) {
//Use the value sent constantly via CAN in 5C0 (only available on AZE0) //Use the value sent constantly via CAN in 5C0 (only available on AZE0)
temperature_min = system_temperature_min_dC = (LB_HistData_Temperature_MIN * 10); //Increase range from C to C+1
convert2unsignedint16((LB_HistData_Temperature_MIN * 10)); //add sign if negative and increase range system_temperature_max_dC = (LB_HistData_Temperature_MAX * 10); //Increase range from C to C+1
temperature_max = convert2unsignedint16((LB_HistData_Temperature_MAX * 10));
} else { // ZE1 (TODO: Once the muxed value in 5C0 becomes known, switch to using that instead of this complicated polled value) } else { // ZE1 (TODO: Once the muxed value in 5C0 becomes known, switch to using that instead of this complicated polled value)
if (temp_raw_min != 0) //We have a polled value available if (temp_raw_min != 0) //We have a polled value available
{ {
temp_polled_min = ((Temp_fromRAW_to_F(temp_raw_min) - 320) * 5) / 9; //Convert from F to C temp_polled_min = ((Temp_fromRAW_to_F(temp_raw_min) - 320) * 5) / 9; //Convert from F to C
temp_polled_max = ((Temp_fromRAW_to_F(temp_raw_max) - 320) * 5) / 9; //Convert from F to C temp_polled_max = ((Temp_fromRAW_to_F(temp_raw_max) - 320) * 5) / 9; //Convert from F to C
if (temp_polled_min < temp_polled_max) { //Catch any edge cases from Temp_fromRAW_to_F function if (temp_polled_min < temp_polled_max) { //Catch any edge cases from Temp_fromRAW_to_F function
temperature_min = convert2unsignedint16((temp_polled_min)); system_temperature_min_dC = temp_polled_min;
temperature_max = convert2unsignedint16((temp_polled_max)); system_temperature_max_dC = temp_polled_max;
} else { } else {
temperature_min = convert2unsignedint16((temp_polled_max)); system_temperature_min_dC = temp_polled_max;
temperature_max = convert2unsignedint16((temp_polled_min)); system_temperature_max_dC = temp_polled_min;
} }
} }
} }
// Define power able to be discharged from battery // Define power able to be discharged from battery
if (LB_Discharge_Power_Limit > 30) { //if >30kW can be pulled from battery if (LB_Discharge_Power_Limit > 60) { //if >60kW can be pulled from battery
max_target_discharge_power = 30000; //cap value so we don't go over the Fronius limits system_max_discharge_power_W = 60000; //cap value so we don't go over uint16 value
} else { } else {
max_target_discharge_power = (LB_Discharge_Power_Limit * 1000); //kW to W system_max_discharge_power_W = (LB_Discharge_Power_Limit * 1000); //kW to W
} }
if (SOC == 0) { //Scaled SOC% value is 0.00%, we should not discharge battery further if (system_scaled_SOC_pptt == 0) { //Scaled SOC% value is 0.00%, we should not discharge battery further
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
} }
// Define power able to be put into the battery // Define power able to be put into the battery
if (LB_Charge_Power_Limit > 30) { //if >30kW can be put into the battery if (LB_Charge_Power_Limit > 60) { //if >60kW can be put into the battery
max_target_charge_power = 30000; //cap value so we don't go over the Fronius limits system_max_charge_power_W = 60000; //cap value so we don't go over uint16 value
} else { } else {
max_target_charge_power = (LB_Charge_Power_Limit * 1000); //kW to W system_max_charge_power_W = (LB_Charge_Power_Limit * 1000); //kW to W
} }
if (SOC == 10000) //Scaled SOC% value is 100.00% if (system_scaled_SOC_pptt == 10000) //Scaled SOC% value is 100.00%
{ {
max_target_charge_power = 0; //No need to charge further, set max power to 0 system_max_charge_power_W = 0; //No need to charge further, set max power to 0
} }
//Map all cell voltages to the global array //Map all cell voltages to the global array
for (int i = 0; i < 96; ++i) { for (int i = 0; i < 96; ++i) {
cellvoltages[i] = cell_voltages[i]; system_cellvoltages_mV[i] = cell_voltages[i];
} }
/*Extra safety functions below*/ /*Extra safety functions below*/
if (LB_GIDS < 6) //500Wh left in battery if (LB_GIDS < 10) //700Wh left in battery!
{ //Battery is running abnormally low, some discharge logic might have failed. Zero it all out. { //Battery is running abnormally low, some discharge logic might have failed. Zero it all out.
set_event(EVENT_BATTERY_EMPTY, 0); set_event(EVENT_BATTERY_EMPTY, 0);
SOC = 0; system_real_SOC_pptt = 0;
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
} }
//Check if SOC% is plausible //Check if SOC% is plausible
if (battery_voltage > (max_voltage - 100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT if (system_battery_voltage_dV >
(system_max_design_voltage_dV - 100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
if (LB_SOC < 650) { if (LB_SOC < 650) {
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, LB_SOC / 10); // Set event with the SOC as data set_event(EVENT_SOC_PLAUSIBILITY_ERROR, LB_SOC / 10); // Set event with the SOC as data
} else { } else {
@ -267,14 +254,14 @@ void update_values_battery() { /* This function maps all the values fetched via
if (LB_Full_CHARGE_flag) { //Battery reports that it is fully charged stop all further charging incase it hasn't already if (LB_Full_CHARGE_flag) { //Battery reports that it is fully charged stop all further charging incase it hasn't already
set_event(EVENT_BATTERY_FULL, 0); set_event(EVENT_BATTERY_FULL, 0);
max_target_charge_power = 0; system_max_charge_power_W = 0;
} else { } else {
clear_event(EVENT_BATTERY_FULL); clear_event(EVENT_BATTERY_FULL);
} }
if (LB_Capacity_Empty) { //Battery reports that it is fully discharged. Stop all further discharging incase it hasn't already if (LB_Capacity_Empty) { //Battery reports that it is fully discharged. Stop all further discharging incase it hasn't already
set_event(EVENT_BATTERY_EMPTY, 0); set_event(EVENT_BATTERY_EMPTY, 0);
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
} else { } else {
clear_event(EVENT_BATTERY_EMPTY); clear_event(EVENT_BATTERY_EMPTY);
} }
@ -285,8 +272,8 @@ void update_values_battery() { /* This function maps all the values fetched via
#endif #endif
//Note, this is sometimes triggered during the night while idle, and the BMS recovers after a while. Removed latching from this scenario //Note, this is sometimes triggered during the night while idle, and the BMS recovers after a while. Removed latching from this scenario
errorCode = 1; errorCode = 1;
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
max_target_charge_power = 0; system_max_charge_power_W = 0;
} }
if (LB_Failsafe_Status > 0) // 0 is normal, start charging/discharging if (LB_Failsafe_Status > 0) // 0 is normal, start charging/discharging
@ -367,9 +354,9 @@ void update_values_battery() { /* This function maps all the values fetched via
set_event(EVENT_CAN_RX_WARNING, 0); set_event(EVENT_CAN_RX_WARNING, 0);
} }
if (bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
max_target_charge_power = 0; system_max_charge_power_W = 0;
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
} }
/*Finally print out values to serial if configured to do so*/ /*Finally print out values to serial if configured to do so*/
@ -379,16 +366,16 @@ void update_values_battery() { /* This function maps all the values fetched via
Serial.println(errorCode); Serial.println(errorCode);
} }
Serial.println("Values going to inverter"); Serial.println("Values going to inverter");
print_with_units("SOH%: ", (StateOfHealth * 0.01), "% "); print_with_units("SOH%: ", (system_SOH_pptt * 0.01), "% ");
print_with_units(", SOC% scaled: ", (SOC * 0.01), "% "); print_with_units(", SOC% scaled: ", (system_scaled_SOC_pptt * 0.01), "% ");
print_with_units(", Voltage: ", (battery_voltage * 0.1), "V "); print_with_units(", Voltage: ", (system_battery_voltage_dV * 0.1), "V ");
print_with_units(", Max discharge power: ", max_target_discharge_power, "W "); print_with_units(", Max discharge power: ", system_max_discharge_power_W, "W ");
print_with_units(", Max charge power: ", max_target_charge_power, "W "); print_with_units(", Max charge power: ", system_max_charge_power_W, "W ");
print_with_units(", Max temp: ", ((int16_t)temperature_max * 0.1), "°C "); print_with_units(", Max temp: ", (system_temperature_max_dC * 0.1), "°C ");
print_with_units(", Min temp: ", ((int16_t)temperature_min * 0.1), "°C "); print_with_units(", Min temp: ", (system_temperature_min_dC * 0.1), "°C ");
Serial.println(""); Serial.println("");
Serial.print("BMS Status: "); Serial.print("BMS Status: ");
if (bms_status == 3) { if (system_bms_status == 3) {
Serial.print("Active, "); Serial.print("Active, ");
} else { } else {
Serial.print("FAULT, "); Serial.print("FAULT, ");
@ -584,8 +571,8 @@ void receive_can_battery(CAN_frame_t rx_frame) {
cell_deviation_mV = (min_max_voltage[1] - min_max_voltage[0]); cell_deviation_mV = (min_max_voltage[1] - min_max_voltage[0]);
cell_max_voltage = min_max_voltage[1]; system_cell_max_voltage_mV = min_max_voltage[1];
cell_min_voltage = min_max_voltage[0]; system_cell_min_voltage_mV = min_max_voltage[0];
if (cell_deviation_mV > MAX_CELL_DEVIATION) { if (cell_deviation_mV > MAX_CELL_DEVIATION) {
set_event(EVENT_CELL_DEVIATION_HIGH, 0); set_event(EVENT_CELL_DEVIATION_HIGH, 0);
@ -861,14 +848,6 @@ void send_can_battery() {
} }
} }
uint16_t convert2unsignedint16(int16_t signed_value) {
if (signed_value < 0) {
return (65535 + signed_value);
} else {
return (uint16_t)signed_value;
}
}
bool is_message_corrupt(CAN_frame_t rx_frame) { bool is_message_corrupt(CAN_frame_t rx_frame) {
uint8_t crc = 0; uint8_t crc = 0;
for (uint8_t j = 0; j < 7; j++) { for (uint8_t j = 0; j < 7; j++) {
@ -911,9 +890,9 @@ uint16_t Temp_fromRAW_to_F(uint16_t temperature) { //This function feels horrib
void setup_battery(void) { // Performs one time setup at startup void setup_battery(void) { // Performs one time setup at startup
Serial.println("Nissan LEAF battery selected"); Serial.println("Nissan LEAF battery selected");
nof_cellvoltages = 96; system_number_of_cells = 96;
max_voltage = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge) system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
min_voltage = 2450; // 245.0V under this, discharging further is disabled system_min_design_voltage_dV = 2600; // 260.0V under this, discharging further is disabled
} }
#endif #endif

View file

@ -8,26 +8,27 @@
#define BATTERY_SELECTED #define BATTERY_SELECTED
// These parameters need to be mapped for the inverter // These parameters need to be mapped for the inverter
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery. extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
uint16_t convert2unsignedint16(int16_t signed_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_t rx_frame); bool is_message_corrupt(CAN_frame_t rx_frame);
void setup_battery(void); void setup_battery(void);

View file

@ -6,16 +6,12 @@
#include "RENAULT-KANGOO-BATTERY.h" #include "RENAULT-KANGOO-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */ /* Do not change code below unless you are sure what you are doing */
#define LB_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Fronius
#define LB_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Fronius
static uint32_t LB_Battery_Voltage = 3700; static uint32_t LB_Battery_Voltage = 3700;
static uint32_t LB_Charge_Power_Limit_Watts = 0; static uint32_t LB_Charge_Power_Limit_Watts = 0;
static uint32_t LB_Discharge_Power_Limit_Watts = 0; static uint32_t LB_Discharge_Power_Limit_Watts = 0;
static int32_t LB_Current = 0; static int32_t LB_Current = 0;
static int16_t LB_MAX_TEMPERATURE = 0; static int16_t LB_MAX_TEMPERATURE = 0;
static int16_t LB_MIN_TEMPERATURE = 0; static int16_t LB_MIN_TEMPERATURE = 0;
static uint16_t soc_calculated = 0;
static uint16_t LB_SOC = 0; static uint16_t LB_SOC = 0;
static uint16_t LB_SOH = 0; static uint16_t LB_SOH = 0;
static uint16_t LB_Discharge_Power_Limit = 0; static uint16_t LB_Discharge_Power_Limit = 0;
@ -60,68 +56,56 @@ static const int interval100 = 100; // interval (ms) at which send CAN Messag
static const int interval1000 = 1000; // interval (ms) at which send CAN Messages static const int interval1000 = 1000; // interval (ms) at which send CAN Messages
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
StateOfHealth = (LB_SOH * 100); //Increase range from 99% -> 99.00%
//Calculate the SOC% value to send to Fronius system_real_SOC_pptt = (LB_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
soc_calculated = LB_SOC;
soc_calculated =
LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (soc_calculated - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
if (soc_calculated < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Inverter as 0%
soc_calculated = 0;
}
if (soc_calculated > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Inverter as 100%
soc_calculated = 1000;
}
SOC = (soc_calculated * 10); //increase LB_SOC range from 0-100.0 -> 100.00
battery_voltage = LB_Battery_Voltage; system_SOH_pptt = (LB_SOH * 100); //Increase range from 99% -> 99.00%
battery_current = LB_Current; system_battery_voltage_dV = LB_Battery_Voltage;
capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value system_battery_current_dA = LB_Current;
remaining_capacity_Wh = (uint16_t)((SOC / 10000) * capacity_Wh); system_capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value
system_remaining_capacity_Wh = (uint16_t)((system_real_SOC_pptt / 10000) * system_capacity_Wh);
LB_Discharge_Power_Limit_Watts = (LB_Discharge_Power_Limit * 500); //Convert value fetched from battery to watts LB_Discharge_Power_Limit_Watts = (LB_Discharge_Power_Limit * 500); //Convert value fetched from battery to watts
/* Define power able to be discharged from battery */ /* Define power able to be discharged from battery */
if (LB_Discharge_Power_Limit_Watts > 30000) //if >30kW can be pulled from battery if (LB_Discharge_Power_Limit_Watts > 60000) //if >60kW can be pulled from battery
{ {
max_target_discharge_power = 30000; //cap value so we don't go over the Fronius limits system_max_discharge_power_W = 60000; //cap value so we don't go over the uint16 limit
} else { } else {
max_target_discharge_power = LB_Discharge_Power_Limit_Watts; system_max_discharge_power_W = LB_Discharge_Power_Limit_Watts;
} }
if (SOC == 0) //Scaled SOC% value is 0.00%, we should not discharge battery further if (system_scaled_SOC_pptt == 0) //Scaled SOC% value is 0.00%, we should not discharge battery further
{ {
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
} }
LB_Charge_Power_Limit_Watts = (LB_Charge_Power_Limit * 500); //Convert value fetched from battery to watts LB_Charge_Power_Limit_Watts = (LB_Charge_Power_Limit * 500); //Convert value fetched from battery to watts
/* Define power able to be put into the battery */ /* Define power able to be put into the battery */
if (LB_Charge_Power_Limit_Watts > 30000) //if >30kW can be put into the battery if (LB_Charge_Power_Limit_Watts > 60000) //if >60kW can be put into the battery
{ {
max_target_charge_power = 30000; //cap value so we don't go over the Fronius limits system_max_charge_power_W = 60000; //cap value so we don't go over the uint16 limit
}
if (LB_Charge_Power_Limit_Watts < 0) {
max_target_charge_power = 0; //cap calue so we dont do under the Fronius limits
} else { } else {
max_target_charge_power = LB_Charge_Power_Limit_Watts; system_max_charge_power_W = LB_Charge_Power_Limit_Watts;
} }
if (SOC == 10000) //Scaled SOC% value is 100.00% if (system_scaled_SOC_pptt == 10000) //Scaled SOC% value is 100.00%
{ {
max_target_charge_power = 0; //No need to charge further, set max power to 0 system_max_charge_power_W = 0; //No need to charge further, set max power to 0
} }
stat_batt_power = (battery_voltage * LB_Current); //TODO: check if scaling is OK system_active_power_W = (system_battery_voltage_dV * LB_Current); //TODO: check if scaling is OK
temperature_min = convert2uint16(LB_MIN_TEMPERATURE * 10); system_temperature_min_dC = (LB_MIN_TEMPERATURE * 10);
temperature_max = convert2uint16(LB_MAX_TEMPERATURE * 10); system_temperature_max_dC = (LB_MAX_TEMPERATURE * 10);
cell_min_voltage = LB_Cell_Min_Voltage; system_cell_min_voltage_mV = LB_Cell_Min_Voltage;
cell_max_voltage = LB_Cell_Max_Voltage; system_cell_max_voltage_mV = LB_Cell_Max_Voltage;
cell_deviation_mV = (cell_max_voltage - cell_min_voltage); cell_deviation_mV = (system_temperature_max_dC - system_temperature_min_dC);
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/ /* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) { if (!CANstillAlive) {
@ -146,21 +130,21 @@ void update_values_battery() { //This function maps all the values fetched via
#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%: ");
Serial.print(StateOfHealth); Serial.print(system_SOH_pptt);
Serial.print(", SOC% scaled: "); Serial.print(", SOC% scaled: ");
Serial.print(SOC); Serial.print(system_scaled_SOC_pptt);
Serial.print(", Voltage: "); Serial.print(", Voltage: ");
Serial.print(battery_voltage); Serial.print(system_battery_voltage_dV);
Serial.print(", Max discharge power: "); Serial.print(", Max discharge power: ");
Serial.print(max_target_discharge_power); Serial.print(system_max_discharge_power_W);
Serial.print(", Max charge power: "); Serial.print(", Max charge power: ");
Serial.print(max_target_charge_power); Serial.print(system_max_charge_power_W);
Serial.print(", Max temp: "); Serial.print(", Max temp: ");
Serial.print(temperature_max); Serial.print(system_temperature_max_dC);
Serial.print(", Min temp: "); Serial.print(", Min temp: ");
Serial.print(temperature_min); Serial.print(system_temperature_min_dC);
Serial.print(", BMS Status (3=OK): "); Serial.print(", BMS Status (3=OK): ");
Serial.print(bms_status); Serial.print(system_bms_status);
Serial.println("Battery values: "); Serial.println("Battery values: ");
Serial.print("Real SOC: "); Serial.print("Real SOC: ");
@ -230,7 +214,7 @@ void receive_can_battery(CAN_frame_t rx_frame) //GKOE reworked
} }
if (rx_frame.data.u8[0] == 0x24) { //5th response Bytes 24-31 if (rx_frame.data.u8[0] == 0x24) { //5th response Bytes 24-31
LB_Discharge_Power_Limit = word(LB_Discharge_Power_Limit_Byte1, rx_frame.data.u8[1]) * 100; //OK! LB_Discharge_Power_Limit = word(LB_Discharge_Power_Limit_Byte1, rx_frame.data.u8[1]) * 100; //OK!
LB_Battery_Voltage = word(rx_frame.data.u8[2], rx_frame.data.u8[3]) * 10; //OK! LB_Battery_Voltage = word(rx_frame.data.u8[2], rx_frame.data.u8[3]) / 10; //OK!
GVB_79B_Continue = false; GVB_79B_Continue = false;
} }
break; break;
@ -262,19 +246,11 @@ void send_can_battery() {
} }
} }
uint16_t convert2uint16(int16_t signed_value) {
if (signed_value < 0) {
return (65535 + signed_value);
} else {
return (uint16_t)signed_value;
}
}
void setup_battery(void) { // Performs one time setup at startup void setup_battery(void) { // Performs one time setup at startup
Serial.println("Renault Kangoo battery selected"); Serial.println("Renault Kangoo battery selected");
max_voltage = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge) system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
min_voltage = 3100; // 310.0V under this, discharging further is disabled system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
} }
#endif #endif

View file

@ -14,26 +14,28 @@
#define MAX_CELL_DEVIATION_MV 500 //LED turns yellow on the board if mv delta exceeds this value #define MAX_CELL_DEVIATION_MV 500 //LED turns yellow on the board if mv delta exceeds this value
// These parameters need to be mapped for the inverter // These parameters need to be mapped for the inverter
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t CANerror; extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, true/false
uint16_t convert2uint16(int16_t signed_value);
void setup_battery(void); void setup_battery(void);
#endif #endif

View file

@ -6,13 +6,9 @@
#include "RENAULT-ZOE-BATTERY.h" #include "RENAULT-ZOE-BATTERY.h"
/* Do not change code below unless you are sure what you are doing */ /* Do not change code below unless you are sure what you are doing */
#define LB_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Fronius
#define LB_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Fronius
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
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 uint16_t LB_SOC = 50; static uint16_t LB_SOC = 50;
static uint16_t soc_calculated = 0;
static uint16_t LB_SOH = 99; static uint16_t LB_SOH = 99;
static int16_t LB_MIN_TEMPERATURE = 0; static int16_t LB_MIN_TEMPERATURE = 0;
static int16_t LB_MAX_TEMPERATURE = 0; static int16_t LB_MAX_TEMPERATURE = 0;
@ -45,44 +41,34 @@ static const int interval100 = 100; // interval (ms) at which send CAN Messag
static const int interval1000 = 1000; // interval (ms) at which send CAN Messages static const int interval1000 = 1000; // interval (ms) at which send CAN Messages
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
StateOfHealth = (LB_SOH * 100); //Increase range from 99% -> 99.00% system_SOH_pptt = (LB_SOH * 100); //Increase range from 99% -> 99.00%
//Calculate the SOC% value to send to Fronius system_real_SOC_pptt = (LB_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
soc_calculated = LB_SOC;
soc_calculated =
LB_MIN_SOC + (LB_MAX_SOC - LB_MIN_SOC) * (soc_calculated - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
if (soc_calculated < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Inverter as 0%
soc_calculated = 0;
}
if (soc_calculated > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Inverter as 100%
soc_calculated = 1000;
}
SOC = (soc_calculated * 10); //increase LB_SOC range from 0-100.0 -> 100.00
battery_voltage = LB_Battery_Voltage; system_battery_voltage_dV = LB_Battery_Voltage;
battery_current = LB_Current; system_battery_current_dA = LB_Current;
capacity_Wh = BATTERY_WH_MAX; //Use the configured value to avoid overflows system_capacity_Wh = BATTERY_WH_MAX; //Use the configured value to avoid overflows
//Calculate the remaining Wh amount from SOC% and max Wh value. //Calculate the remaining Wh amount from SOC% and max Wh value.
remaining_capacity_Wh = static_cast<int>((static_cast<double>(SOC) / 10000) * BATTERY_WH_MAX); system_remaining_capacity_Wh = static_cast<int>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
max_target_discharge_power; system_max_discharge_power_W;
max_target_charge_power; system_max_charge_power_W;
stat_batt_power; system_active_power_W;
temperature_min; system_temperature_min_dC;
temperature_max; system_temperature_max_dC;
cell_min_voltage; system_cell_min_voltage_mV;
cell_max_voltage; system_cell_max_voltage_mV;
cell_deviation_mV = (cell_max_voltage - cell_min_voltage); cell_deviation_mV = (system_cell_max_voltage_mV - system_cell_min_voltage_mV);
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/ /* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) { if (!CANstillAlive) {
@ -107,21 +93,21 @@ void update_values_battery() { //This function maps all the values fetched via
#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%: ");
Serial.print(StateOfHealth); Serial.print(system_SOH_pptt);
Serial.print(", SOC% scaled: "); Serial.print(", SOC% scaled: ");
Serial.print(SOC); Serial.print(system_scaled_SOC_pptt);
Serial.print(", Voltage: "); Serial.print(", Voltage: ");
Serial.print(battery_voltage); Serial.print(system_battery_voltage_dV);
Serial.print(", Max discharge power: "); Serial.print(", Max discharge power: ");
Serial.print(max_target_discharge_power); Serial.print(system_max_discharge_power_W);
Serial.print(", Max charge power: "); Serial.print(", Max charge power: ");
Serial.print(max_target_charge_power); Serial.print(system_max_charge_power_W);
Serial.print(", Max temp: "); Serial.print(", Max temp: ");
Serial.print(temperature_max); Serial.print(system_temperature_max_dC);
Serial.print(", Min temp: "); Serial.print(", Min temp: ");
Serial.print(temperature_min); Serial.print(system_temperature_min_dC);
Serial.print(", BMS Status (3=OK): "); Serial.print(", BMS Status (3=OK): ");
Serial.print(bms_status); Serial.print(system_bms_status);
Serial.println("Battery values: "); Serial.println("Battery values: ");
Serial.print("Real SOC: "); Serial.print("Real SOC: ");
@ -169,8 +155,8 @@ void send_can_battery() {
void setup_battery(void) { // Performs one time setup at startup void setup_battery(void) { // Performs one time setup at startup
Serial.println("Renault Zoe battery selected"); Serial.println("Renault Zoe battery selected");
max_voltage = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge) system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
min_voltage = 3100; // 310.0V under this, discharging further is disabled system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
} }
#endif #endif

View file

@ -14,23 +14,26 @@
#define MAX_CELL_DEVIATION_MV 500 //LED turns yellow on the board if mv delta exceeds this value #define MAX_CELL_DEVIATION_MV 500 //LED turns yellow on the board if mv delta exceeds this value
// These parameters need to be mapped for the inverter // These parameters need to be mapped for the inverter
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t CANerror; extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
void setup_battery(void); void setup_battery(void);

View file

@ -21,9 +21,6 @@ static const int interval10 = 10; // interval (ms) at which send CAN
static const int interval100 = 100; // interval (ms) at which send CAN Messages static const int interval100 = 100; // interval (ms) at which send CAN Messages
static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive static uint8_t CANstillAlive = 12; //counter for checking if CAN is still alive
#define LB_MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to Inverter
#define LB_MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to Inverter
static int SOC_1 = 0; static int SOC_1 = 0;
static int SOC_2 = 0; static int SOC_2 = 0;
static int SOC_3 = 0; static int SOC_3 = 0;
@ -61,25 +58,25 @@ CAN_frame_t SANTAFE_523 = {.FIR = {.B =
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
SOC; system_real_SOC_pptt;
battery_voltage; system_battery_voltage_dV;
battery_current; system_battery_current_dA;
capacity_Wh = BATTERY_WH_MAX; system_capacity_Wh = BATTERY_WH_MAX;
remaining_capacity_Wh; system_remaining_capacity_Wh;
max_target_discharge_power; system_max_discharge_power_W;
max_target_charge_power; system_max_charge_power_W;
stat_batt_power; system_active_power_W;
temperature_min; system_temperature_min_dC;
temperature_max; system_temperature_max_dC;
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/ /* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) { if (!CANstillAlive) {
@ -180,8 +177,8 @@ uint8_t CalculateCRC8(CAN_frame_t rx_frame) {
void setup_battery(void) { // Performs one time setup at startup 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");
max_voltage = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge) system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
min_voltage = 3100; // 310.0V under this, discharging further is disabled system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
} }
#endif #endif

View file

@ -7,24 +7,27 @@
#define BATTERY_SELECTED #define BATTERY_SELECTED
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) // These parameters need to be mapped for the inverter
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery. extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
uint8_t CalculateCRC8(CAN_frame_t rx_frame); uint8_t CalculateCRC8(CAN_frame_t rx_frame);
void setup_battery(void); void setup_battery(void);

View file

@ -1,8 +1,8 @@
// SERIAL-LINK-RECEIVER-FROM-BATTERY.cpp #include "BATTERIES.h"
#ifdef SERIAL_LINK_RECEIVER
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h"
#include <Arduino.h> #include <Arduino.h>
#include "../devboard/utils/events.h" #include "../devboard/utils/events.h"
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h"
#define INVERTER_SEND_NUM_VARIABLES 1 #define INVERTER_SEND_NUM_VARIABLES 1
#define INVERTER_RECV_NUM_VARIABLES 16 #define INVERTER_RECV_NUM_VARIABLES 16
@ -28,25 +28,25 @@ SerialDataLink dataLinkReceive(SerialReceiver, 0, 0x01, sendingNumVariables,
static bool batteryFault = false; // used locally - mainly to indicate Battery CAN failure static bool batteryFault = false; // used locally - mainly to indicate Battery CAN failure
void __getData() { void __getData() {
SOC = (uint16_t)dataLinkReceive.getReceivedData(0); system_real_SOC_pptt = (uint16_t)dataLinkReceive.getReceivedData(0);
StateOfHealth = (uint16_t)dataLinkReceive.getReceivedData(1); system_SOH_pptt = (uint16_t)dataLinkReceive.getReceivedData(1);
battery_voltage = (uint16_t)dataLinkReceive.getReceivedData(2); system_battery_voltage_dV = (uint16_t)dataLinkReceive.getReceivedData(2);
battery_current = (uint16_t)dataLinkReceive.getReceivedData(3); system_battery_current_dA = (int16_t)dataLinkReceive.getReceivedData(3);
capacity_Wh = (uint16_t)dataLinkReceive.getReceivedData(4); system_capacity_Wh = (uint32_t)dataLinkReceive.getReceivedData(4);
remaining_capacity_Wh = (uint16_t)dataLinkReceive.getReceivedData(5); system_remaining_capacity_Wh = (uint32_t)dataLinkReceive.getReceivedData(5);
max_target_discharge_power = (uint16_t)dataLinkReceive.getReceivedData(6); system_max_discharge_power_W = (uint16_t)dataLinkReceive.getReceivedData(6);
max_target_charge_power = (uint16_t)dataLinkReceive.getReceivedData(7); system_max_charge_power_W = (uint16_t)dataLinkReceive.getReceivedData(7);
uint16_t _bms_status = (uint16_t)dataLinkReceive.getReceivedData(8); uint16_t _system_bms_status = (uint16_t)dataLinkReceive.getReceivedData(8);
stat_batt_power = (uint16_t)dataLinkReceive.getReceivedData(9); system_active_power_W = (uint16_t)dataLinkReceive.getReceivedData(9);
temperature_min = (uint16_t)dataLinkReceive.getReceivedData(10); system_temperature_min_dC = (int16_t)dataLinkReceive.getReceivedData(10);
temperature_max = (uint16_t)dataLinkReceive.getReceivedData(11); system_temperature_max_dC = (int16_t)dataLinkReceive.getReceivedData(11);
cell_max_voltage = (uint16_t)dataLinkReceive.getReceivedData(12); system_cell_max_voltage_mV = (uint16_t)dataLinkReceive.getReceivedData(12);
cell_min_voltage = (uint16_t)dataLinkReceive.getReceivedData(13); system_cell_min_voltage_mV = (uint16_t)dataLinkReceive.getReceivedData(13);
LFP_Chemistry = (bool)dataLinkReceive.getReceivedData(14); system_LFP_Chemistry = (bool)dataLinkReceive.getReceivedData(14);
batteryAllowsContactorClosing = (uint16_t)dataLinkReceive.getReceivedData(15); batteryAllowsContactorClosing = (bool)dataLinkReceive.getReceivedData(15);
batteryFault = false; batteryFault = false;
if (_bms_status == FAULT) { if (_system_bms_status == FAULT) {
batteryFault = true; batteryFault = true;
set_event(EVENT_SERIAL_TRANSMITTER_FAILURE, 0); set_event(EVENT_SERIAL_TRANSMITTER_FAILURE, 0);
} }
@ -99,8 +99,8 @@ void manageSerialLinkReceiver() {
{ {
__getData(); __getData();
reads++; reads++;
lastGoodMaxCharge = max_target_charge_power; lastGoodMaxCharge = system_max_charge_power_W;
lastGoodMaxDischarge = max_target_discharge_power; lastGoodMaxDischarge = system_max_discharge_power_W;
//--- if BatteryFault then assume Data is stale //--- if BatteryFault then assume Data is stale
if (!batteryFault) if (!batteryFault)
lastGood = currentTime; lastGood = currentTime;
@ -116,13 +116,13 @@ void manageSerialLinkReceiver() {
if (minutesLost > 0 && lastGood > 0) { if (minutesLost > 0 && lastGood > 0) {
// lose 25% each minute of data loss // lose 25% each minute of data loss
if (minutesLost < 4) { if (minutesLost < 4) {
max_target_charge_power = (lastGoodMaxCharge * (4 - minutesLost)) / 4; system_max_charge_power_W = (lastGoodMaxCharge * (4 - minutesLost)) / 4;
max_target_discharge_power = (lastGoodMaxDischarge * (4 - minutesLost)) / 4; system_max_discharge_power_W = (lastGoodMaxDischarge * (4 - minutesLost)) / 4;
set_event(EVENT_SERIAL_RX_WARNING, minutesLost); set_event(EVENT_SERIAL_RX_WARNING, minutesLost);
} else { } else {
// Times Up - // Times Up -
max_target_charge_power = 0; system_max_charge_power_W = 0;
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
set_event(EVENT_SERIAL_RX_FAILURE, uint8_t(min(minutesLost, 255uL))); set_event(EVENT_SERIAL_RX_FAILURE, uint8_t(min(minutesLost, 255uL)));
//----- Throw Error //----- Throw Error
} }
@ -137,9 +137,9 @@ void manageSerialLinkReceiver() {
} }
Serial.print(minutesLost); Serial.print(minutesLost);
Serial.print(", max Charge = "); Serial.print(", max Charge = ");
Serial.print(max_target_charge_power); Serial.print(system_max_charge_power_W);
Serial.print(", max Discharge = "); Serial.print(", max Discharge = ");
Serial.println(max_target_discharge_power); Serial.println(system_max_discharge_power_W);
} }
} }
@ -176,35 +176,35 @@ void manageSerialLinkReceiver() {
void update_values_serial_link() { void update_values_serial_link() {
Serial.println("Values from battery: "); Serial.println("Values from battery: ");
Serial.print("SOC: "); Serial.print("SOC: ");
Serial.print(SOC); Serial.print(system_real_SOC_pptt);
Serial.print(" SOH: "); Serial.print(" SOH: ");
Serial.print(StateOfHealth); Serial.print(system_SOH_pptt);
Serial.print(" Voltage: "); Serial.print(" Voltage: ");
Serial.print(battery_voltage); Serial.print(system_battery_voltage_dV);
Serial.print(" Current: "); Serial.print(" Current: ");
Serial.print(battery_current); Serial.print(system_battery_current_dA);
Serial.print(" Capacity: "); Serial.print(" Capacity: ");
Serial.print(capacity_Wh); Serial.print(system_capacity_Wh);
Serial.print(" Remain cap: "); Serial.print(" Remain cap: ");
Serial.print(remaining_capacity_Wh); Serial.print(system_remaining_capacity_Wh);
Serial.print(" Max discharge W: "); Serial.print(" Max discharge W: ");
Serial.print(max_target_discharge_power); Serial.print(system_max_discharge_power_W);
Serial.print(" Max charge W: "); Serial.print(" Max charge W: ");
Serial.print(max_target_charge_power); Serial.print(system_max_charge_power_W);
Serial.print(" BMS status: "); Serial.print(" BMS status: ");
Serial.print(bms_status); Serial.print(system_bms_status);
Serial.print(" Power: "); Serial.print(" Power: ");
Serial.print(stat_batt_power); Serial.print(system_active_power_W);
Serial.print(" Temp min: "); Serial.print(" Temp min: ");
Serial.print(temperature_min); Serial.print(system_temperature_min_dC);
Serial.print(" Temp max: "); Serial.print(" Temp max: ");
Serial.print(temperature_max); Serial.print(system_temperature_max_dC);
Serial.print(" Cell max: "); Serial.print(" Cell max: ");
Serial.print(cell_max_voltage); Serial.print(system_cell_max_voltage_mV);
Serial.print(" Cell min: "); Serial.print(" Cell min: ");
Serial.print(cell_min_voltage); Serial.print(system_cell_min_voltage_mV);
Serial.print(" LFP : "); Serial.print(" LFP : ");
Serial.print(LFP_Chemistry); Serial.print(system_LFP_Chemistry);
Serial.print(" batteryAllowsContactorClosing: "); Serial.print(" batteryAllowsContactorClosing: ");
Serial.print(batteryAllowsContactorClosing); Serial.print(batteryAllowsContactorClosing);
Serial.print(" inverterAllowsContactorClosing: "); Serial.print(" inverterAllowsContactorClosing: ");
@ -212,3 +212,11 @@ void update_values_serial_link() {
Serial.println(""); Serial.println("");
} }
void setup_battery(void) {
Serial.println("SERIAL_DATA_LINK_RECEIVER selected");
}
void update_values_battery() {}
void send_can_battery() {}
#endif

View file

@ -3,6 +3,8 @@
#ifndef SERIAL_LINK_RECEIVER_FROM_BATTERY_H #ifndef SERIAL_LINK_RECEIVER_FROM_BATTERY_H
#define SERIAL_LINK_RECEIVER_FROM_BATTERY_H #define SERIAL_LINK_RECEIVER_FROM_BATTERY_H
#define BATTERY_SELECTED
#include <Arduino.h> #include <Arduino.h>
#include "../../USER_SETTINGS.h" #include "../../USER_SETTINGS.h"
#include "../devboard/config.h" // Needed for all defines #include "../devboard/config.h" // Needed for all defines
@ -10,27 +12,24 @@
// https://github.com/mackelec/SerialDataLink // https://github.com/mackelec/SerialDataLink
// These parameters need to be mapped for the inverter // These parameters need to be mapped on the battery side
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint8_t bms_status; //Enum, 0-5 extern uint8_t system_bms_status; //Enum 0-5
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern bool system_LFP_Chemistry; //Set to true or false depending on cell chemistry
extern bool LFP_Chemistry;
extern uint16_t CANerror;
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
// Parameters to send to the transmitter // Parameters to send to the transmitter
@ -38,5 +37,6 @@ extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
void manageSerialLinkReceiver(); void manageSerialLinkReceiver();
void update_values_serial_link(); void update_values_serial_link();
void setup_battery(void);
#endif #endif

View file

@ -60,7 +60,6 @@ static uint16_t output_current = 0;
static uint16_t soc_min = 0; static uint16_t soc_min = 0;
static uint16_t soc_max = 0; static uint16_t soc_max = 0;
static uint16_t soc_vi = 0; static uint16_t soc_vi = 0;
static uint16_t soc_calculated = 0;
static uint16_t soc_ave = 0; static uint16_t soc_ave = 0;
static uint16_t cell_max_v = 3700; static uint16_t cell_max_v = 3700;
static uint16_t cell_min_v = 3700; static uint16_t cell_min_v = 3700;
@ -153,8 +152,6 @@ static const char* hvilStatusState[] = {"NOT OK",
"UNKNOWN(14)", "UNKNOWN(14)",
"UNKNOWN(15)"}; "UNKNOWN(15)"};
#define MAX_SOC 1000 //BMS never goes over this value. We use this info to rescale SOC% sent to inverter
#define MIN_SOC 0 //BMS never goes below this value. We use this info to rescale SOC% sent to inverter
#define MAX_CELL_VOLTAGE_NCA_NCM 4250 //Battery is put into emergency stop if one cell goes over 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 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_DEVIATION_NCA_NCM 500 //LED turns yellow on the board if mv delta exceeds this value
@ -169,66 +166,58 @@ void update_values_battery() { //This function maps all the values fetched via
//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
//Calculate the SOH% to send to inverter //Calculate the SOH% to send to inverter
if (bat_beginning_of_life != 0) { //div/0 safeguard if (bat_beginning_of_life != 0) { //div/0 safeguard
StateOfHealth = system_SOH_pptt =
static_cast<uint16_t>((static_cast<double>(nominal_full_pack_energy) / bat_beginning_of_life) * 10000.0); static_cast<uint16_t>((static_cast<double>(nominal_full_pack_energy) / bat_beginning_of_life) * 10000.0);
} }
//If the calculation went wrong, set SOH to 100% //If the calculation went wrong, set SOH to 100%
if (StateOfHealth > 10000) { if (system_SOH_pptt > 10000) {
StateOfHealth = 10000; system_SOH_pptt = 10000;
} }
//If the value is unavailable, set SOH to 99% //If the value is unavailable, set SOH to 99%
if (nominal_full_pack_energy < REASONABLE_ENERGYAMOUNT) { if (nominal_full_pack_energy < REASONABLE_ENERGYAMOUNT) {
StateOfHealth = 9900; system_SOH_pptt = 9900;
} }
//Calculate the SOC% value to send to inverter system_real_SOC_pptt = (soc_vi * 10); //increase SOC range from 0-100.0 -> 100.00
soc_calculated = soc_vi;
soc_calculated = MIN_SOC + (MAX_SOC - MIN_SOC) * (soc_calculated - MINPERCENTAGE) / (MAXPERCENTAGE - MINPERCENTAGE);
if (soc_calculated < 0) { //We are in the real SOC% range of 0-20%, always set SOC sent to Inverter as 0%
soc_calculated = 0;
}
if (soc_calculated > 1000) { //We are in the real SOC% range of 80-100%, always set SOC sent to Inverter as 100%
soc_calculated = 1000;
}
SOC = (soc_calculated * 10); //increase SOC range from 0-100.0 -> 100.00
battery_voltage = (volts * 10); //One more decimal needed (370 -> 3700) system_battery_voltage_dV = (volts * 10); //One more decimal needed (370 -> 3700)
battery_current = convert2unsignedInt16(amps); //13.0A system_battery_current_dA = amps; //13.0A
capacity_Wh = BATTERY_WH_MAX; //Use the configured value to avoid overflows system_capacity_Wh = BATTERY_WH_MAX; //Use the configured value to avoid overflows
//Calculate the remaining Wh amount from SOC% and max Wh value. //Calculate the remaining Wh amount from SOC% and max Wh value.
remaining_capacity_Wh = static_cast<uint16_t>((static_cast<double>(SOC) / 10000) * BATTERY_WH_MAX); system_remaining_capacity_Wh =
static_cast<uint32_t>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
// Define the allowed discharge power // Define the allowed discharge power
max_target_discharge_power = (max_discharge_current * volts); system_max_discharge_power_W = (max_discharge_current * volts);
// Cap the allowed discharge power if battery is empty, or discharge power is higher than the maximum discharge power allowed // Cap the allowed discharge power if battery is empty, or discharge power is higher than the maximum discharge power allowed
if (SOC == 0) { if (system_scaled_SOC_pptt == 0) {
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
} else if (max_target_discharge_power > MAXDISCHARGEPOWERALLOWED) { } else if (system_max_discharge_power_W > MAXDISCHARGEPOWERALLOWED) {
max_target_discharge_power = MAXDISCHARGEPOWERALLOWED; system_max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
} }
//The allowed charge power behaves strangely. We instead estimate this value //The allowed charge power behaves strangely. We instead estimate this value
if (SOC == 10000) { // When scaled SOC is 100%, set allowed charge power to 0 if (system_scaled_SOC_pptt == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
max_target_charge_power = 0; system_max_charge_power_W = 0;
} else if (soc_vi > 950) { // When real SOC is between 95-99.99%, ramp the value between Max<->0 } else if (soc_vi > 950) { // When real SOC is between 95-99.99%, ramp the value between Max<->0
max_target_charge_power = MAXCHARGEPOWERALLOWED * (1 - (soc_vi - 950) / 50.0); system_max_charge_power_W = MAXCHARGEPOWERALLOWED * (1 - (soc_vi - 950) / 50.0);
} else { // No limits, max charging power allowed } else { // No limits, max charging power allowed
max_target_charge_power = MAXCHARGEPOWERALLOWED; system_max_charge_power_W = MAXCHARGEPOWERALLOWED;
} }
power = ((volts / 10) * amps); power = ((volts / 10) * amps);
stat_batt_power = convert2unsignedInt16(power); system_active_power_W = power;
temperature_min = convert2unsignedInt16(min_temp); system_temperature_min_dC = min_temp;
temperature_max = convert2unsignedInt16(max_temp); system_temperature_max_dC = max_temp;
cell_max_voltage = cell_max_v; system_cell_max_voltage_mV = cell_max_v;
cell_min_voltage = cell_min_v; system_cell_min_voltage_mV = cell_min_v;
/* Value mapping is completed. Start to check all safeties */ /* Value mapping is completed. Start to check all safeties */
@ -251,30 +240,31 @@ void update_values_battery() { //This function maps all the values fetched via
//Determine which chemistry battery pack is using (crude method, TODO: replace with real CAN identifier later) //Determine which chemistry battery pack is using (crude method, TODO: replace with real CAN identifier later)
if (soc_vi > 900) { //When SOC% is over 90.0%, we can use max cell voltage to estimate what chemistry is used if (soc_vi > 900) { //When SOC% is over 90.0%, we can use max cell voltage to estimate what chemistry is used
if (cell_max_v < 3450) { if (cell_max_v < 3450) {
LFP_Chemistry = true; system_LFP_Chemistry = true;
} }
if (cell_max_v > 3700) { if (cell_max_v > 3700) {
LFP_Chemistry = false; system_LFP_Chemistry = false;
} }
} }
// An even better way is to check how many cells are in the pack. NCM/A batteries have 96s, LFP has 102-106s // An even better way is to check how many cells are in the pack. NCM/A batteries have 96s, LFP has 102-106s
if (nof_cellvoltages > 101) { if (system_number_of_cells > 101) {
LFP_Chemistry = true; system_LFP_Chemistry = true;
} }
//Once cell chemistry is determined, set maximum and minimum total pack voltage safety limits //Once cell chemistry is determined, set maximum and minimum total pack voltage safety limits
if (LFP_Chemistry) { if (system_LFP_Chemistry) {
max_voltage = 3880; system_max_design_voltage_dV = 3880;
min_voltage = 2968; system_min_design_voltage_dV = 2968;
} else { // NCM/A chemistry } else { // NCM/A chemistry
max_voltage = 4030; system_max_design_voltage_dV = 4030;
min_voltage = 3100; system_min_design_voltage_dV = 3100;
} }
//Check if SOC% is plausible //Check if SOC% is plausible
if (battery_voltage > (max_voltage - 20)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT if (system_battery_voltage_dV >
if (SOC < 5000) { //When SOC is less than 50.00% when approaching max voltage (system_max_design_voltage_dV - 20)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, SOC / 100); if (system_real_SOC_pptt < 5000) { //When SOC is less than 50.00% when approaching max voltage
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, system_real_SOC_pptt / 100);
} }
} }
@ -288,7 +278,7 @@ void update_values_battery() { //This function maps all the values fetched via
set_event(EVENT_KWH_PLAUSIBILITY_ERROR, nominal_full_pack_energy); set_event(EVENT_KWH_PLAUSIBILITY_ERROR, nominal_full_pack_energy);
} }
if (LFP_Chemistry) { //LFP limits used for voltage safeties if (system_LFP_Chemistry) { //LFP limits used for voltage safeties
if (cell_max_v >= MAX_CELL_VOLTAGE_LFP) { if (cell_max_v >= MAX_CELL_VOLTAGE_LFP) {
set_event(EVENT_CELL_OVER_VOLTAGE, (cell_max_v - MAX_CELL_VOLTAGE_LFP)); set_event(EVENT_CELL_OVER_VOLTAGE, (cell_max_v - MAX_CELL_VOLTAGE_LFP));
} }
@ -314,9 +304,9 @@ void update_values_battery() { //This function maps all the values fetched via
} }
} }
if (bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
max_target_charge_power = 0; system_max_charge_power_W = 0;
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
} }
/* Safeties verified. Perform USB serial printout if configured to do so */ /* Safeties verified. Perform USB serial printout if configured to do so */
@ -350,10 +340,9 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.print("YES, "); Serial.print("YES, ");
else else
Serial.print("NO, "); Serial.print("NO, ");
if (LFP_Chemistry) { if (system_LFP_Chemistry) {
Serial.print("LFP chemistry detected!"); Serial.print("LFP chemistry detected!");
} }
Serial.print(nof_cellvoltages);
Serial.println(""); Serial.println("");
Serial.print("Cellstats, Max: "); Serial.print("Cellstats, Max: ");
Serial.print(cell_max_v); Serial.print(cell_max_v);
@ -375,14 +364,14 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.println(""); Serial.println("");
Serial.println("Values passed to the inverter: "); Serial.println("Values passed to the inverter: ");
print_SOC(" SOC: ", SOC); print_SOC(" SOC: ", system_scaled_SOC_pptt);
print_int_with_units(" Max discharge power: ", max_target_discharge_power, "W"); print_int_with_units(" Max discharge power: ", system_max_discharge_power_W, "W");
Serial.print(", "); Serial.print(", ");
print_int_with_units(" Max charge power: ", max_target_charge_power, "W"); print_int_with_units(" Max charge power: ", system_max_charge_power_W, "W");
Serial.println(""); Serial.println("");
print_int_with_units(" Max temperature: ", ((int16_t)temperature_max * 0.1), "°C"); print_int_with_units(" Max temperature: ", ((int16_t)system_temperature_min_dC * 0.1), "°C");
Serial.print(", "); Serial.print(", ");
print_int_with_units(" Min temperature: ", ((int16_t)temperature_min * 0.1), "°C"); print_int_with_units(" Min temperature: ", ((int16_t)system_temperature_max_dC * 0.1), "°C");
Serial.println(""); Serial.println("");
#endif #endif
} }
@ -478,11 +467,11 @@ void receive_can_battery(CAN_frame_t rx_frame) {
{ {
// Example, frame3=0x89,frame2=0x1D = 35101 / 10 = 3510mV // Example, frame3=0x89,frame2=0x1D = 35101 / 10 = 3510mV
volts = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) / 10; volts = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) / 10;
cellvoltages[mux * 3] = volts; system_cellvoltages_mV[mux * 3] = volts;
volts = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) / 10; volts = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) / 10;
cellvoltages[1 + mux * 3] = volts; system_cellvoltages_mV[1 + mux * 3] = volts;
volts = ((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) / 10; volts = ((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) / 10;
cellvoltages[2 + mux * 3] = volts; system_cellvoltages_mV[2 + mux * 3] = volts;
// Track the max value of mux. If we've seen two 0 values for mux, we've probably gathered all // Track the max value of mux. If we've seen two 0 values for mux, we've probably gathered all
// cell voltages. Then, 2 + mux_max * 3 + 1 is the number of cell voltages. // cell voltages. Then, 2 + mux_max * 3 + 1 is the number of cell voltages.
@ -491,7 +480,7 @@ void receive_can_battery(CAN_frame_t rx_frame) {
mux_zero_counter++; mux_zero_counter++;
if (mux_zero_counter == 2u) { if (mux_zero_counter == 2u) {
// The max index will be 2 + mux_max * 3 (see above), so "+ 1" for the number of cells // The max index will be 2 + mux_max * 3 (see above), so "+ 1" for the number of cells
nof_cellvoltages = 2 + 3 * mux_max + 1; system_number_of_cells = 2 + 3 * mux_max + 1;
// Increase the counter arbitrarily another time to make the initial if-statement evaluate to false // Increase the counter arbitrarily another time to make the initial if-statement evaluate to false
mux_zero_counter++; mux_zero_counter++;
} }
@ -591,12 +580,12 @@ the first, for a few cycles, then stop all messages which causes the contactor
previousMillis30 = currentMillis; previousMillis30 = currentMillis;
if (inverterAllowsContactorClosing == 1) { if (inverterAllowsContactorClosing == 1) {
if (bms_status == ACTIVE) { if (system_bms_status == ACTIVE) {
send221still = 50; send221still = 50;
batteryAllowsContactorClosing = true; batteryAllowsContactorClosing = true;
ESP32Can.CANWriteFrame(&TESLA_221_1); ESP32Can.CANWriteFrame(&TESLA_221_1);
ESP32Can.CANWriteFrame(&TESLA_221_2); ESP32Can.CANWriteFrame(&TESLA_221_2);
} else { //bms_status == FAULT or inverter requested opening contactors } else { //system_bms_status == FAULT or inverter requested opening contactors
if (send221still > 0) { if (send221still > 0) {
batteryAllowsContactorClosing = false; batteryAllowsContactorClosing = false;
ESP32Can.CANWriteFrame(&TESLA_221_1); ESP32Can.CANWriteFrame(&TESLA_221_1);
@ -606,13 +595,6 @@ the first, for a few cycles, then stop all messages which causes the contactor
} }
} }
} }
uint16_t convert2unsignedInt16(int16_t signed_value) {
if (signed_value < 0) {
return (65535 + signed_value);
} else {
return (uint16_t)signed_value;
}
}
void print_int_with_units(char* header, int value, char* units) { void print_int_with_units(char* header, int value, char* units) {
Serial.print(header); Serial.print(header);
@ -708,8 +690,8 @@ void printDebugIfActive(uint8_t symbol, const char* message) {
void setup_battery(void) { // Performs one time setup at startup void setup_battery(void) { // Performs one time setup at startup
Serial.println("Tesla Model 3 battery selected"); Serial.println("Tesla Model 3 battery selected");
max_voltage = 4030; // 403.0V, over this, charging is not possible (goes into forced discharge) system_max_design_voltage_dV = 4030; // 403.0V, over this, charging is not possible (goes into forced discharge)
min_voltage = 3100; // 310.0V under this, discharging further is disabled system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
} }
#endif #endif

View file

@ -12,32 +12,33 @@
60000 // 60000W we need to cap this value to max 60kW, most inverters overflow otherwise 60000 // 60000W we need to cap this value to max 60kW, most inverters overflow otherwise
// These parameters need to be mapped for the inverter // These parameters need to be mapped for the inverter
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery. extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
extern bool LFP_Chemistry; extern bool system_LFP_Chemistry; //Bool, 1=true, 0=false
void printFaultCodesIfActive(); void printFaultCodesIfActive();
void printDebugIfActive(uint8_t symbol, const char* message); void printDebugIfActive(uint8_t symbol, const char* message);
void print_int_with_units(char* header, int value, char* units); void print_int_with_units(char* header, int value, char* units);
void print_SOC(char* header, int SOC); void print_SOC(char* header, int SOC);
uint16_t convert2unsignedInt16(int16_t signed_value);
void setup_battery(void); void setup_battery(void);
#endif #endif

View file

@ -19,48 +19,48 @@ void print_units(char* header, int value, char* units) {
} }
void update_values_battery() { /* This function puts fake values onto the parameters sent towards the inverter */ void update_values_battery() { /* This function puts fake values onto the parameters sent towards the inverter */
SOC = 5000; // 50.00% system_real_SOC_pptt = 5000; // 50.00%
StateOfHealth = 9900; // 99.00% system_SOH_pptt = 9900; // 99.00%
//battery_voltage = 3700; // 370.0V , value set in startup in .ino file, editable via webUI //system_battery_voltage_dV = 3700; // 370.0V , value set in startup in .ino file, editable via webUI
battery_current = 0; // 0 A system_battery_current_dA = 0; // 0 A
capacity_Wh = 30000; // 30kWh system_capacity_Wh = 30000; // 30kWh
remaining_capacity_Wh = 15000; // 15kWh system_remaining_capacity_Wh = 15000; // 15kWh
cell_max_voltage = 3596; system_cell_max_voltage_mV = 3596;
cell_min_voltage = 3500; system_cell_min_voltage_mV = 3500;
stat_batt_power = 0; // 0W system_active_power_W = 0; // 0W
temperature_min = 50; // 5.0*C system_temperature_min_dC = 50; // 5.0*C
temperature_max = 60; // 6.0*C system_temperature_max_dC = 60; // 6.0*C
max_target_discharge_power = 5000; // 5kW system_max_discharge_power_W = 5000; // 5kW
max_target_charge_power = 5000; // 5kW system_max_charge_power_W = 5000; // 5kW
for (int i = 0; i < 97; ++i) { for (int i = 0; i < 97; ++i) {
cellvoltages[i] = 3500 + i; system_cellvoltages_mV[i] = 3500 + i;
} }
/*Finally print out values to serial if configured to do so*/ /*Finally print out values to serial if configured to do so*/
#ifdef DEBUG_VIA_USB #ifdef DEBUG_VIA_USB
Serial.println("FAKE Values going to inverter"); Serial.println("FAKE Values going to inverter");
print_units("SOH%: ", (StateOfHealth * 0.01), "% "); print_units("SOH%: ", (system_SOH_pptt * 0.01), "% ");
print_units(", SOC%: ", (SOC * 0.01), "% "); print_units(", SOC%: ", (system_scaled_SOC_pptt * 0.01), "% ");
print_units(", Voltage: ", (battery_voltage * 0.1), "V "); print_units(", Voltage: ", (system_battery_voltage_dV * 0.1), "V ");
print_units(", Max discharge power: ", max_target_discharge_power, "W "); print_units(", Max discharge power: ", system_max_discharge_power_W, "W ");
print_units(", Max charge power: ", max_target_charge_power, "W "); print_units(", Max charge power: ", system_max_charge_power_W, "W ");
print_units(", Max temp: ", (temperature_max * 0.1), "°C "); print_units(", Max temp: ", (system_temperature_max_dC * 0.1), "°C ");
print_units(", Min temp: ", (temperature_min * 0.1), "°C "); print_units(", Min temp: ", (system_temperature_min_dC * 0.1), "°C ");
print_units(", Max cell voltage: ", cell_max_voltage, "mV "); print_units(", Max cell voltage: ", system_cell_max_voltage_mV, "mV ");
print_units(", Min cell voltage: ", cell_min_voltage, "mV "); print_units(", Min cell voltage: ", system_cell_min_voltage_mV, "mV ");
Serial.println(""); Serial.println("");
#endif #endif
} }
@ -85,8 +85,8 @@ void send_can_battery() {
void setup_battery(void) { // Performs one time setup at startup void setup_battery(void) { // Performs one time setup at startup
Serial.println("Test mode with fake battery selected"); Serial.println("Test mode with fake battery selected");
max_voltage = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge) system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
min_voltage = 2450; // 245.0V under this, discharging further is disabled system_min_design_voltage_dV = 2450; // 245.0V under this, discharging further is disabled
} }
#endif #endif

View file

@ -8,25 +8,27 @@
#define BATTERY_SELECTED #define BATTERY_SELECTED
// These parameters need to be mapped for the inverter // These parameters need to be mapped for the inverter
extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint8_t bms_status; //Enum, 0-5 extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint16_t cellvoltages[120]; //mV 0-5000 per cell extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, true/false
void setup_battery(void); void setup_battery(void);

View file

@ -227,12 +227,12 @@ void send_can_nissanleaf_charger() {
} }
// if actual battery_voltage is less than setpoint got to max power set from web ui // if actual battery_voltage is less than setpoint got to max power set from web ui
if (battery_voltage < (CHARGER_SET_HV * 10)) { //battery_voltage = V+1, 0-500.0 (0-5000) if (system_battery_voltage_dV < (CHARGER_SET_HV * 10)) { //system_battery_voltage_dV = V+1, 0-500.0 (0-5000)
OBCpower = OBCpowerSetpoint; OBCpower = OBCpowerSetpoint;
} }
// decrement charger power if volt setpoint is reached // decrement charger power if volt setpoint is reached
if (battery_voltage >= (CHARGER_SET_HV * 10)) { if (system_battery_voltage_dV >= (CHARGER_SET_HV * 10)) {
if (OBCpower > 0x64) { if (OBCpower > 0x64) {
OBCpower--; OBCpower--;
} }

View file

@ -4,7 +4,7 @@
#include "../../USER_SETTINGS.h" #include "../../USER_SETTINGS.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" #include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
void send_can_nissanleaf_charger(); void send_can_nissanleaf_charger();
void receive_can_nissanleaf_charger(CAN_frame_t rx_frame); void receive_can_nissanleaf_charger(CAN_frame_t rx_frame);

View file

@ -37,7 +37,9 @@ static void publish_cell_voltages(void) {
static JsonDocument doc; static JsonDocument doc;
static const char* hostname = WiFi.getHostname(); static const char* hostname = WiFi.getHostname();
static String state_topic = String("battery-emulator_") + String(hostname) + "/spec_data"; static String state_topic = String("battery-emulator_") + String(hostname) + "/spec_data";
if (nof_cellvoltages == 0u) {
// If the cell voltage number isn't initialized...
if (system_number_of_cells == 0u) {
return; return;
} }
@ -45,7 +47,7 @@ static void publish_cell_voltages(void) {
mqtt_first_transmission = false; mqtt_first_transmission = false;
String topic = "homeassistant/sensor/battery-emulator/cell_voltage"; String topic = "homeassistant/sensor/battery-emulator/cell_voltage";
for (int i = 0; i < nof_cellvoltages; i++) { for (int i = 0; i < system_number_of_cells; i++) {
int cellNumber = i + 1; int cellNumber = i + 1;
doc["name"] = "Battery Cell Voltage " + String(cellNumber); doc["name"] = "Battery Cell Voltage " + String(cellNumber);
doc["object_id"] = "battery_voltage_cell" + String(cellNumber); doc["object_id"] = "battery_voltage_cell" + String(cellNumber);
@ -72,13 +74,13 @@ static void publish_cell_voltages(void) {
doc.clear(); // clear after sending autoconfig doc.clear(); // clear after sending autoconfig
} else { } else {
// If cell voltages haven't been populated... // If cell voltages haven't been populated...
if (nof_cellvoltages == 0u) { if (system_number_of_cells == 0u) {
return; return;
} }
JsonArray cell_voltages = doc["cell_voltages"].to<JsonArray>(); JsonArray cell_voltages = doc["cell_voltages"].to<JsonArray>();
for (size_t i = 0; i < nof_cellvoltages; ++i) { for (size_t i = 0; i < system_number_of_cells; ++i) {
cell_voltages.add(((float)cellvoltages[i]) / 1000.0); cell_voltages.add(((float)system_cellvoltages_mV[i]) / 1000.0);
} }
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg)); serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
@ -145,15 +147,16 @@ static void publish_common_info(void) {
} }
doc.clear(); doc.clear();
} else { } else {
doc["SOC"] = ((float)SOC) / 100.0; doc["SOC"] = ((float)system_scaled_SOC_pptt) / 100.0;
doc["state_of_health"] = ((float)StateOfHealth) / 100.0; doc["SOC_real"] = ((float)system_real_SOC_pptt) / 100.0;
doc["temperature_min"] = ((float)((int16_t)temperature_min)) / 10.0; doc["state_of_health"] = ((float)system_SOH_pptt) / 100.0;
doc["temperature_max"] = ((float)((int16_t)temperature_max)) / 10.0; doc["temperature_min"] = ((float)((int16_t)system_temperature_min_dC)) / 10.0;
doc["stat_batt_power"] = ((float)((int16_t)stat_batt_power)); doc["temperature_max"] = ((float)((int16_t)system_temperature_max_dC)) / 10.0;
doc["battery_current"] = ((float)((int16_t)battery_current)) / 10.0; doc["stat_batt_power"] = ((float)((int16_t)system_active_power_W));
doc["cell_max_voltage"] = ((float)cell_max_voltage) / 1000.0; doc["battery_current"] = ((float)((int16_t)system_battery_current_dA)) / 10.0;
doc["cell_min_voltage"] = ((float)cell_min_voltage) / 1000.0; doc["cell_max_voltage"] = ((float)system_cell_max_voltage_mV) / 1000.0;
doc["battery_voltage"] = ((float)battery_voltage) / 10.0; doc["cell_min_voltage"] = ((float)system_cell_min_voltage_mV) / 1000.0;
doc["battery_voltage"] = ((float)system_battery_voltage_dV) / 10.0;
serializeJson(doc, mqtt_msg); serializeJson(doc, mqtt_msg);
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) { if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {

View file

@ -41,17 +41,18 @@
extern const char* version_number; // The current software version, used for mqtt extern const char* version_number; // The current software version, used for mqtt
extern uint16_t SOC; extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t StateOfHealth; extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery. extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000 , Stores the highest cell millivolt value
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint16_t stat_batt_power; extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern const char* mqtt_user; extern const char* mqtt_user;
extern const char* mqtt_password; extern const char* mqtt_password;

View file

@ -137,6 +137,7 @@ void init_events(void) {
events.entries[EVENT_LOW_SOH].level = EVENT_LEVEL_ERROR; events.entries[EVENT_LOW_SOH].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_HVIL_FAILURE].level = EVENT_LEVEL_ERROR; events.entries[EVENT_HVIL_FAILURE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_INTERNAL_OPEN_FAULT].level = EVENT_LEVEL_ERROR; events.entries[EVENT_INTERNAL_OPEN_FAULT].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_INVERTER_OPEN_CONTACTOR].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CELL_UNDER_VOLTAGE].level = EVENT_LEVEL_ERROR; events.entries[EVENT_CELL_UNDER_VOLTAGE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CELL_OVER_VOLTAGE].level = EVENT_LEVEL_ERROR; events.entries[EVENT_CELL_OVER_VOLTAGE].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_CELL_DEVIATION_HIGH].level = EVENT_LEVEL_WARNING; events.entries[EVENT_CELL_DEVIATION_HIGH].level = EVENT_LEVEL_WARNING;
@ -210,6 +211,8 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
"disabled!"; "disabled!";
case EVENT_INTERNAL_OPEN_FAULT: case EVENT_INTERNAL_OPEN_FAULT:
return "ERROR: High voltage cable removed while battery running. Opening contactors!"; return "ERROR: High voltage cable removed while battery running. Opening contactors!";
case EVENT_INVERTER_OPEN_CONTACTOR:
return "ERROR: Inverter requested contactors to open. Opening contactors!";
case EVENT_CELL_UNDER_VOLTAGE: case EVENT_CELL_UNDER_VOLTAGE:
return "ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!"; return "ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!";
case EVENT_CELL_OVER_VOLTAGE: case EVENT_CELL_OVER_VOLTAGE:
@ -299,13 +302,13 @@ static void update_bms_status(void) {
case EVENT_LEVEL_INFO: case EVENT_LEVEL_INFO:
case EVENT_LEVEL_WARNING: case EVENT_LEVEL_WARNING:
case EVENT_LEVEL_DEBUG: case EVENT_LEVEL_DEBUG:
bms_status = ACTIVE; system_bms_status = ACTIVE;
break; break;
case EVENT_LEVEL_UPDATE: case EVENT_LEVEL_UPDATE:
bms_status = UPDATING; system_bms_status = UPDATING;
break; break;
case EVENT_LEVEL_ERROR: case EVENT_LEVEL_ERROR:
bms_status = FAULT; system_bms_status = FAULT;
break; break;
default: default:
break; break;

View file

@ -36,6 +36,7 @@
XX(EVENT_LOW_SOH) \ XX(EVENT_LOW_SOH) \
XX(EVENT_HVIL_FAILURE) \ XX(EVENT_HVIL_FAILURE) \
XX(EVENT_INTERNAL_OPEN_FAULT) \ XX(EVENT_INTERNAL_OPEN_FAULT) \
XX(EVENT_INVERTER_OPEN_CONTACTOR) \
XX(EVENT_CELL_UNDER_VOLTAGE) \ XX(EVENT_CELL_UNDER_VOLTAGE) \
XX(EVENT_CELL_OVER_VOLTAGE) \ XX(EVENT_CELL_OVER_VOLTAGE) \
XX(EVENT_CELL_DEVIATION_HIGH) \ XX(EVENT_CELL_DEVIATION_HIGH) \
@ -99,6 +100,6 @@ void run_event_handling(void);
void run_sequence_on_target(void); void run_sequence_on_target(void);
extern uint8_t bms_status; //Enum, 0-5 extern uint8_t system_bms_status; //Enum 0-5
#endif // __MYTIMER_H__ #endif // __MYTIMER_H__

View file

@ -26,8 +26,8 @@ void run_sequence_on_target(void) {
timer.set_interval(10000); timer.set_interval(10000);
events_test_state = ETOT_FIRST_WAIT; events_test_state = ETOT_FIRST_WAIT;
Serial.println("Events test: initialized"); Serial.println("Events test: initialized");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
break; break;
case ETOT_FIRST_WAIT: case ETOT_FIRST_WAIT:
if (timer.elapsed()) { if (timer.elapsed()) {
@ -36,8 +36,8 @@ void run_sequence_on_target(void) {
set_event(EVENT_DUMMY_INFO, 123); set_event(EVENT_DUMMY_INFO, 123);
set_event(EVENT_DUMMY_INFO, 234); // 234 should show, occurrence 1 set_event(EVENT_DUMMY_INFO, 234); // 234 should show, occurrence 1
Serial.println("Events test: info event set, data: 234"); Serial.println("Events test: info event set, data: 234");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
} }
break; break;
case ETOT_INFO: case ETOT_INFO:
@ -46,8 +46,8 @@ void run_sequence_on_target(void) {
clear_event(EVENT_DUMMY_INFO); clear_event(EVENT_DUMMY_INFO);
events_test_state = ETOT_INFO_CLEAR; events_test_state = ETOT_INFO_CLEAR;
Serial.println("Events test : info event cleared"); Serial.println("Events test : info event cleared");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
} }
break; break;
case ETOT_INFO_CLEAR: case ETOT_INFO_CLEAR:
@ -57,8 +57,8 @@ void run_sequence_on_target(void) {
set_event(EVENT_DUMMY_DEBUG, 111); set_event(EVENT_DUMMY_DEBUG, 111);
set_event(EVENT_DUMMY_DEBUG, 222); // 222 should show, occurrence 1 set_event(EVENT_DUMMY_DEBUG, 222); // 222 should show, occurrence 1
Serial.println("Events test : debug event set, data: 222"); Serial.println("Events test : debug event set, data: 222");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
} }
break; break;
case ETOT_DEBUG: case ETOT_DEBUG:
@ -67,8 +67,8 @@ void run_sequence_on_target(void) {
clear_event(EVENT_DUMMY_DEBUG); clear_event(EVENT_DUMMY_DEBUG);
events_test_state = ETOT_DEBUG_CLEAR; events_test_state = ETOT_DEBUG_CLEAR;
Serial.println("Events test : info event cleared"); Serial.println("Events test : info event cleared");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
} }
break; break;
case ETOT_DEBUG_CLEAR: case ETOT_DEBUG_CLEAR:
@ -78,8 +78,8 @@ void run_sequence_on_target(void) {
set_event(EVENT_DUMMY_WARNING, 234); set_event(EVENT_DUMMY_WARNING, 234);
set_event(EVENT_DUMMY_WARNING, 121); // 121 should show, occurrence 1 set_event(EVENT_DUMMY_WARNING, 121); // 121 should show, occurrence 1
Serial.println("Events test : warning event set, data: 121"); Serial.println("Events test : warning event set, data: 121");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
} }
break; break;
case ETOT_WARNING: case ETOT_WARNING:
@ -88,8 +88,8 @@ void run_sequence_on_target(void) {
clear_event(EVENT_DUMMY_WARNING); clear_event(EVENT_DUMMY_WARNING);
events_test_state = ETOT_WARNING_CLEAR; events_test_state = ETOT_WARNING_CLEAR;
Serial.println("Events test : warning event cleared"); Serial.println("Events test : warning event cleared");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
} }
break; break;
case ETOT_WARNING_CLEAR: case ETOT_WARNING_CLEAR:
@ -99,8 +99,8 @@ void run_sequence_on_target(void) {
set_event(EVENT_DUMMY_ERROR, 221); set_event(EVENT_DUMMY_ERROR, 221);
set_event(EVENT_DUMMY_ERROR, 133); // 133 should show, occurrence 1 set_event(EVENT_DUMMY_ERROR, 133); // 133 should show, occurrence 1
Serial.println("Events test : error event set, data: 133"); Serial.println("Events test : error event set, data: 133");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
} }
break; break;
case ETOT_ERROR: case ETOT_ERROR:
@ -109,8 +109,8 @@ void run_sequence_on_target(void) {
clear_event(EVENT_DUMMY_ERROR); clear_event(EVENT_DUMMY_ERROR);
events_test_state = ETOT_ERROR_CLEAR; events_test_state = ETOT_ERROR_CLEAR;
Serial.println("Events test : error event cleared"); Serial.println("Events test : error event cleared");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
} }
break; break;
case ETOT_ERROR_CLEAR: case ETOT_ERROR_CLEAR:
@ -120,8 +120,8 @@ void run_sequence_on_target(void) {
set_event_latched(EVENT_DUMMY_ERROR, 221); set_event_latched(EVENT_DUMMY_ERROR, 221);
set_event_latched(EVENT_DUMMY_ERROR, 133); // 133 should show, occurrence 1 set_event_latched(EVENT_DUMMY_ERROR, 133); // 133 should show, occurrence 1
Serial.println("Events test : latched error event set, data: 133"); Serial.println("Events test : latched error event set, data: 133");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
} }
break; break;
case ETOT_ERROR_LATCHED: case ETOT_ERROR_LATCHED:
@ -130,8 +130,8 @@ void run_sequence_on_target(void) {
clear_event(EVENT_DUMMY_ERROR); clear_event(EVENT_DUMMY_ERROR);
events_test_state = ETOT_DONE; events_test_state = ETOT_DONE;
Serial.println("Events test : latched error event cleared?"); Serial.println("Events test : latched error event cleared?");
Serial.print("bms_status: "); Serial.print("system_bms_status: ");
Serial.println(bms_status); Serial.println(system_bms_status);
} }
break; break;
case ETOT_DONE: case ETOT_DONE:

View file

@ -18,9 +18,9 @@ String cellmonitor_processor(const String& var) {
// Display max, min, and deviation voltage values // Display max, min, and deviation voltage values
content += "<div class='voltage-values'>"; content += "<div class='voltage-values'>";
content += "Max Voltage: " + String(cell_max_voltage) + " mV<br>"; content += "Max Voltage: " + String(system_cell_max_voltage_mV) + " mV<br>";
content += "Min Voltage: " + String(cell_min_voltage) + " mV<br>"; content += "Min Voltage: " + String(system_cell_min_voltage_mV) + " mV<br>";
int deviation = cell_max_voltage - cell_min_voltage; int deviation = system_cell_max_voltage_mV - system_cell_min_voltage_mV;
content += "Voltage Deviation: " + String(deviation) + " mV"; content += "Voltage Deviation: " + String(deviation) + " mV";
content += "</div>"; content += "</div>";
@ -28,14 +28,14 @@ String cellmonitor_processor(const String& var) {
content += "<div class='container'>"; content += "<div class='container'>";
for (int i = 0; i < 120; ++i) { for (int i = 0; i < 120; ++i) {
// Skip empty values // Skip empty values
if (cellvoltages[i] == 0) { if (system_cellvoltages_mV[i] == 0) {
continue; continue;
} }
String cellContent = "Cell " + String(i + 1) + "<br>" + String(cellvoltages[i]) + " mV"; String cellContent = "Cell " + String(i + 1) + "<br>" + String(system_cellvoltages_mV[i]) + " mV";
// Check if the cell voltage is below 3000, apply red color // Check if the cell voltage is below 3000, apply red color
if (cellvoltages[i] < 3000) { if (system_cellvoltages_mV[i] < 3000) {
cellContent = "<span class='low-voltage'>" + cellContent + "</span>"; cellContent = "<span class='low-voltage'>" + cellContent + "</span>";
} }

View file

@ -4,9 +4,9 @@
#include <Arduino.h> #include <Arduino.h>
#include <stdint.h> #include <stdint.h>
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
/** /**
* @brief Replaces placeholder with content section in web page * @brief Replaces placeholder with content section in web page

View file

@ -15,6 +15,8 @@ String settings_processor(const String& var) {
// Show current settings with edit buttons and input fields // Show current settings with edit buttons and input fields
content += "<h4 style='color: white;'>Battery capacity: <span id='BATTERY_WH_MAX'>" + String(BATTERY_WH_MAX) + content += "<h4 style='color: white;'>Battery capacity: <span id='BATTERY_WH_MAX'>" + String(BATTERY_WH_MAX) +
" Wh </span> <button onclick='editWh()'>Edit</button></h4>"; " Wh </span> <button onclick='editWh()'>Edit</button></h4>";
content += "<h4 style='color: white;'>Rescale SOC: <span id='USE_SCALED_SOC'>" + String(USE_SCALED_SOC) +
"</span> <button onclick='editUseScaledSOC()'>Edit</button></h4>";
content += "<h4 style='color: white;'>SOC max percentage: " + String(MAXPERCENTAGE / 10.0, 1) + content += "<h4 style='color: white;'>SOC max percentage: " + String(MAXPERCENTAGE / 10.0, 1) +
" </span> <button onclick='editSocMax()'>Edit</button></h4>"; " </span> <button onclick='editSocMax()'>Edit</button></h4>";
content += "<h4 style='color: white;'>SOC min percentage: " + String(MINPERCENTAGE / 10.0, 1) + content += "<h4 style='color: white;'>SOC min percentage: " + String(MINPERCENTAGE / 10.0, 1) +
@ -29,7 +31,7 @@ String settings_processor(const String& var) {
#ifdef TEST_FAKE_BATTERY #ifdef TEST_FAKE_BATTERY
// Start a new block with blue background color // Start a new block with blue background color
content += "<div style='background-color: #2E37AD; padding: 10px; margin-bottom: 10px;border-radius: 50px'>"; content += "<div style='background-color: #2E37AD; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
float voltageFloat = static_cast<float>(battery_voltage) / 10.0; // Convert to float and divide by 10 float voltageFloat = static_cast<float>(system_battery_voltage_dV) / 10.0; // Convert to float and divide by 10
content += "<h4 style='color: white;'>Fake battery voltage: " + String(voltageFloat, 1) + content += "<h4 style='color: white;'>Fake battery voltage: " + String(voltageFloat, 1) +
" V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>"; " V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>";
@ -69,14 +71,26 @@ String settings_processor(const String& var) {
content += "<script>"; content += "<script>";
content += "function editWh() {"; content += "function editWh() {";
content += "var value = prompt('How much energy the battery can store. Enter new Wh value (1-65000):');"; content += "var value = prompt('How much energy the battery can store. Enter new Wh value (1-120000):');";
content += "if (value !== null) {"; content += "if (value !== null) {";
content += " if (value >= 1 && value <= 65000) {"; content += " if (value >= 1 && value <= 120000) {";
content += " var xhr = new XMLHttpRequest();"; content += " var xhr = new XMLHttpRequest();";
content += " xhr.open('GET', '/updateBatterySize?value=' + value, true);"; content += " xhr.open('GET', '/updateBatterySize?value=' + value, true);";
content += " xhr.send();"; content += " xhr.send();";
content += " } else {"; content += " } else {";
content += " alert('Invalid value. Please enter a value between 1 and 65000.');"; content += " alert('Invalid value. Please enter a value between 1 and 120000.');";
content += " }";
content += "}";
content += "}";
content += "function editUseScaledSOC() {";
content += "var value = prompt('Should SOC% be scaled? (0 = No, 1 = Yes):');";
content += "if (value !== null) {";
content += " if (value == 0 || value == 1) {";
content += " var xhr = new XMLHttpRequest();";
content += " xhr.open('GET', '/updateUseScaledSOC?value=' + value, true);";
content += " xhr.send();";
content += " } else {";
content += " alert('Invalid value. Please enter a value between 0 and 1.');";
content += " }"; content += " }";
content += "}"; content += "}";
content += "}"; content += "}";

View file

@ -4,7 +4,7 @@
#include <Arduino.h> #include <Arduino.h>
#include "../../../USER_SETTINGS.h" // Needed for WiFi ssid and password #include "../../../USER_SETTINGS.h" // Needed for WiFi ssid and password
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
/** /**
* @brief Replaces placeholder with content section in web page * @brief Replaces placeholder with content section in web page

View file

@ -74,6 +74,18 @@ void init_webserver() {
} }
}); });
// Route for editing USE_SCALED_SOC
server.on("/updateUseScaledSOC", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
USE_SCALED_SOC = value.toInt();
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
request->send(400, "text/plain", "Bad Request");
}
});
// Route for editing SOCMax // Route for editing SOCMax
server.on("/updateSocMax", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateSocMax", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) { if (request->hasParam("value")) {
@ -132,7 +144,7 @@ void init_webserver() {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
float val = value.toFloat(); float val = value.toFloat();
battery_voltage = val * 10; system_battery_voltage_dV = val * 10;
request->send(200, "text/plain", "Updated successfully"); request->send(200, "text/plain", "Updated successfully");
}); });
@ -480,58 +492,41 @@ String processor(const String& var) {
} }
// Display battery statistics within this block // Display battery statistics within this block
float socFloat = static_cast<float>(SOC) / 100.0; // Convert to float and divide by 100 float socRealFloat = static_cast<float>(system_real_SOC_pptt) / 100.0; // Convert to float and divide by 100
float sohFloat = static_cast<float>(StateOfHealth) / 100.0; // Convert to float and divide by 100 float socScaledFloat = static_cast<float>(system_scaled_SOC_pptt) / 100.0; // Convert to float and divide by 100
float voltageFloat = static_cast<float>(battery_voltage) / 10.0; // Convert to float and divide by 10 float sohFloat = static_cast<float>(system_SOH_pptt) / 100.0; // Convert to float and divide by 100
float currentFloat = 0; float voltageFloat = static_cast<float>(system_battery_voltage_dV) / 10.0; // Convert to float and divide by 10
if (battery_current > 32767) { //Handle negative values on this unsigned value float currentFloat = static_cast<float>(system_battery_current_dA) / 10.0; // Convert to float and divide by 10
currentFloat = static_cast<float>(-(65535 - battery_current)) / 10.0; // Convert to float and divide by 10 float powerFloat = static_cast<float>(system_active_power_W); // Convert to float
} else { float tempMaxFloat = static_cast<float>(system_temperature_max_dC) / 10.0; // Convert to float
currentFloat = static_cast<float>(battery_current) / 10.0; // Convert to float and divide by 10 float tempMinFloat = static_cast<float>(system_temperature_min_dC) / 10.0; // Convert to float
}
float powerFloat = 0; content += "<h4 style='color: white;'>Real SOC: " + String(socRealFloat, 2) + "</h4>";
if (stat_batt_power > 32767) { //Handle negative values on this unsigned value content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) + "</h4>";
powerFloat = static_cast<float>(-(65535 - stat_batt_power));
} else {
powerFloat = static_cast<float>(stat_batt_power);
}
float tempMaxFloat = 0;
float tempMinFloat = 0;
if (temperature_max > 32767) { //Handle negative values on this unsigned value
tempMaxFloat = static_cast<float>(-(65536 - temperature_max)) / 10.0; // Convert to float and divide by 10
} else {
tempMaxFloat = static_cast<float>(temperature_max) / 10.0; // Convert to float and divide by 10
}
if (temperature_min > 32767) { //Handle negative values on this unsigned value
tempMinFloat = static_cast<float>(-(65536 - temperature_min)) / 10.0; // Convert to float and divide by 10
} else {
tempMinFloat = static_cast<float>(temperature_min) / 10.0; // Convert to float and divide by 10
}
content += "<h4 style='color: white;'>SOC: " + String(socFloat, 2) + "</h4>";
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "</h4>"; content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "</h4>";
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) + " V</h4>"; content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) + " V</h4>";
content += "<h4 style='color: white;'>Current: " + String(currentFloat, 1) + " A</h4>"; content += "<h4 style='color: white;'>Current: " + String(currentFloat, 1) + " A</h4>";
content += formatPowerValue("Power", powerFloat, "", 1); content += formatPowerValue("Power", powerFloat, "", 1);
content += formatPowerValue("Total capacity", capacity_Wh, "h", 0); content += formatPowerValue("Total capacity", system_capacity_Wh, "h", 0);
content += formatPowerValue("Remaining capacity", remaining_capacity_Wh, "h", 1); content += formatPowerValue("Remaining capacity", system_remaining_capacity_Wh, "h", 1);
content += formatPowerValue("Max discharge power", max_target_discharge_power, "", 1); content += formatPowerValue("Max discharge power", system_max_discharge_power_W, "", 1);
content += formatPowerValue("Max charge power", max_target_charge_power, "", 1); content += formatPowerValue("Max charge power", system_max_charge_power_W, "", 1);
content += "<h4>Cell max: " + String(cell_max_voltage) + " mV</h4>"; content += "<h4>Cell max: " + String(system_cell_max_voltage_mV) + " mV</h4>";
content += "<h4>Cell min: " + String(cell_min_voltage) + " mV</h4>"; content += "<h4>Cell min: " + String(system_cell_min_voltage_mV) + " mV</h4>";
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " C</h4>"; content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " C</h4>";
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " C</h4>"; content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " C</h4>";
if (bms_status == ACTIVE) { if (system_bms_status == ACTIVE) {
content += "<h4>BMS Status: OK </h4>"; content += "<h4>BMS Status: OK </h4>";
} else if (bms_status == UPDATING) { } else if (system_bms_status == UPDATING) {
content += "<h4>BMS Status: UPDATING </h4>"; content += "<h4>BMS Status: UPDATING </h4>";
} else { } else {
content += "<h4>BMS Status: FAULT </h4>"; content += "<h4>BMS Status: FAULT </h4>";
} }
if (battery_current == 0) { if (system_battery_current_dA == 0) {
content += "<h4>Battery idle</h4>"; content += "<h4>Battery idle</h4>";
} else if (battery_current > 32767) { } else if (system_battery_current_dA < 0) {
content += "<h4>Battery discharging!</h4>"; content += "<h4>Battery discharging!</h4>";
} else { // between 1-32767 } else { // > 0
content += "<h4>Battery charging!</h4>"; content += "<h4>Battery charging!</h4>";
} }
content += "<h4>Automatic contactor closing allowed:</h4>"; content += "<h4>Automatic contactor closing allowed:</h4>";
@ -683,7 +678,7 @@ template <typename T> // This function makes power values appear as W when unde
String formatPowerValue(String label, T value, String unit, int precision) { String formatPowerValue(String label, T value, String unit, int precision) {
String result = "<h4 style='color: white;'>" + label + ": "; String result = "<h4 style='color: white;'>" + label + ": ";
if (std::is_same<T, float>::value || std::is_same<T, uint16_t>::value) { if (std::is_same<T, float>::value || std::is_same<T, uint16_t>::value || std::is_same<T, uint32_t>::value) {
float convertedValue = static_cast<float>(value); float convertedValue = static_cast<float>(value);
if (convertedValue >= 1000.0 || convertedValue <= -1000.0) { if (convertedValue >= 1000.0 || convertedValue <= -1000.0) {

View file

@ -17,21 +17,25 @@
#endif #endif
extern const char* version_number; // The current software version, shown on webserver extern const char* version_number; // The current software version, shown on webserver
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t capacity_Wh; //Wh, 0-60000 extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint8_t bms_status; //Enum, 0-5 extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cellvoltages[120]; //mV 0-4350 per cell extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000 , Stores the highest cell millivolt value
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern uint8_t LEDcolor; //Enum, 0-10 extern uint8_t LEDcolor; //Enum, 0-10
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false

View file

@ -106,28 +106,26 @@ CAN_frame_t BYD_210 = {.FIR = {.B =
.MsgID = 0x210, .MsgID = 0x210,
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; .data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
static int discharge_current = 0; static uint16_t discharge_current = 0;
static int charge_current = 0; static uint16_t charge_current = 0;
static int initialDataSent = 0;
static int16_t temperature_average = 0; static int16_t temperature_average = 0;
static int16_t temp_min = 0; static uint16_t inverter_voltage = 0;
static int16_t temp_max = 0; static uint16_t inverter_SOC = 0;
static int inverter_voltage = 0;
static int inverter_SOC = 0;
static long inverter_timestamp = 0; static long inverter_timestamp = 0;
static bool initialDataSent = 0;
void update_values_can_byd() { //This function maps all the values fetched from battery CAN to the correct CAN messages void update_values_can_byd() { //This function maps all the values fetched from battery CAN to the correct CAN messages
//Calculate values //Calculate values
charge_current = charge_current = ((system_max_charge_power_W * 10) /
((max_target_charge_power * 10) / max_voltage); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
//The above calculation results in (30 000*10)/3700=81A //The above calculation results in (30 000*10)/3700=81A
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
if (charge_current > MAXCHARGEAMP) { if (charge_current > MAXCHARGEAMP) {
charge_current = MAXCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. charge_current = MAXCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
} }
discharge_current = ((max_target_discharge_power * 10) / discharge_current = ((system_max_discharge_power_W * 10) /
max_voltage); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
//The above calculation results in (30 000*10)/3700=81A //The above calculation results in (30 000*10)/3700=81A
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
if (discharge_current > MAXDISCHARGEAMP) { if (discharge_current > MAXDISCHARGEAMP) {
@ -135,17 +133,15 @@ void update_values_can_byd() { //This function maps all the values fetched from
MAXDISCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values. MAXDISCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
} }
temp_min = temperature_min; //Convert from unsigned to signed temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
temp_max = temperature_max;
temperature_average = ((temp_max + temp_min) / 2);
//Map values to CAN messages //Map values to CAN messages
//Maxvoltage (eg 400.0V = 4000 , 16bits long) //Maxvoltage (eg 400.0V = 4000 , 16bits long)
BYD_110.data.u8[0] = (max_voltage >> 8); BYD_110.data.u8[0] = (system_max_design_voltage_dV >> 8);
BYD_110.data.u8[1] = (max_voltage & 0x00FF); BYD_110.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
//Minvoltage (eg 300.0V = 3000 , 16bits long) //Minvoltage (eg 300.0V = 3000 , 16bits long)
BYD_110.data.u8[2] = (min_voltage >> 8); BYD_110.data.u8[2] = (system_min_design_voltage_dV >> 8);
BYD_110.data.u8[3] = (min_voltage & 0x00FF); BYD_110.data.u8[3] = (system_min_design_voltage_dV & 0x00FF);
//Maximum discharge power allowed (Unit: A+1) //Maximum discharge power allowed (Unit: A+1)
BYD_110.data.u8[4] = (discharge_current >> 8); BYD_110.data.u8[4] = (discharge_current >> 8);
BYD_110.data.u8[5] = (discharge_current & 0x00FF); BYD_110.data.u8[5] = (discharge_current & 0x00FF);
@ -154,11 +150,11 @@ void update_values_can_byd() { //This function maps all the values fetched from
BYD_110.data.u8[7] = (charge_current & 0x00FF); BYD_110.data.u8[7] = (charge_current & 0x00FF);
//SOC (100.00%) //SOC (100.00%)
BYD_150.data.u8[0] = (SOC >> 8); BYD_150.data.u8[0] = (system_scaled_SOC_pptt >> 8);
BYD_150.data.u8[1] = (SOC & 0x00FF); BYD_150.data.u8[1] = (system_scaled_SOC_pptt & 0x00FF);
//StateOfHealth (100.00%) //StateOfHealth (100.00%)
BYD_150.data.u8[2] = (StateOfHealth >> 8); BYD_150.data.u8[2] = (system_SOH_pptt >> 8);
BYD_150.data.u8[3] = (StateOfHealth & 0x00FF); BYD_150.data.u8[3] = (system_SOH_pptt & 0x00FF);
//Maximum charge power allowed (Unit: A+1) //Maximum charge power allowed (Unit: A+1)
BYD_150.data.u8[4] = (charge_current >> 8); BYD_150.data.u8[4] = (charge_current >> 8);
BYD_150.data.u8[5] = (charge_current & 0x00FF); BYD_150.data.u8[5] = (charge_current & 0x00FF);
@ -167,21 +163,21 @@ void update_values_can_byd() { //This function maps all the values fetched from
BYD_150.data.u8[7] = (discharge_current & 0x00FF); BYD_150.data.u8[7] = (discharge_current & 0x00FF);
//Voltage (ex 370.0) //Voltage (ex 370.0)
BYD_1D0.data.u8[0] = (battery_voltage >> 8); BYD_1D0.data.u8[0] = (system_battery_voltage_dV >> 8);
BYD_1D0.data.u8[1] = (battery_voltage & 0x00FF); BYD_1D0.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
//Current (ex 81.0A) //Current (ex 81.0A)
BYD_1D0.data.u8[2] = (battery_current >> 8); BYD_1D0.data.u8[2] = (system_battery_current_dA >> 8);
BYD_1D0.data.u8[3] = (battery_current & 0x00FF); BYD_1D0.data.u8[3] = (system_battery_current_dA & 0x00FF);
//Temperature average //Temperature average
BYD_1D0.data.u8[4] = (temperature_average >> 8); BYD_1D0.data.u8[4] = (temperature_average >> 8);
BYD_1D0.data.u8[5] = (temperature_average & 0x00FF); BYD_1D0.data.u8[5] = (temperature_average & 0x00FF);
//Temperature max //Temperature max
BYD_210.data.u8[0] = (temperature_max >> 8); BYD_210.data.u8[0] = (system_temperature_max_dC >> 8);
BYD_210.data.u8[1] = (temperature_max & 0x00FF); BYD_210.data.u8[1] = (system_temperature_max_dC & 0x00FF);
//Temperature min //Temperature min
BYD_210.data.u8[2] = (temperature_min >> 8); BYD_210.data.u8[2] = (system_temperature_min_dC >> 8);
BYD_210.data.u8[3] = (temperature_min & 0x00FF); BYD_210.data.u8[3] = (system_temperature_min_dC & 0x00FF);
#ifdef DEBUG_VIA_USB #ifdef DEBUG_VIA_USB
if (char1_151 != 0) { if (char1_151 != 0) {

View file

@ -5,22 +5,28 @@
#include "../devboard/config.h" // Needed for all defines #include "../devboard/config.h" // Needed for all defines
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" #include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
extern uint16_t SOC; // These parameters need to be mapped for the inverter
extern uint16_t StateOfHealth; extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t battery_voltage; extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t battery_current; extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t capacity_Wh; extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t remaining_capacity_Wh; extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t max_target_discharge_power; extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t max_target_charge_power; extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t stat_batt_power; extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t temperature_min; extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t temperature_max; extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t CANerror; extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t min_voltage; extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t max_voltage; extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_max_charge_power_W; //W, 0-65000
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, true/false
void update_values_can_byd(); void update_values_can_byd();
void send_can_byd(); void send_can_byd();

View file

@ -32,15 +32,19 @@ void handle_update_data_modbusp201_byd() {
static uint16_t system_data[13]; static uint16_t system_data[13];
system_data[0] = 0; // Id.: p201 Value.: 0 Scaled value.: 0 Comment.: Always 0 system_data[0] = 0; // Id.: p201 Value.: 0 Scaled value.: 0 Comment.: Always 0
system_data[1] = 0; // Id.: p202 Value.: 0 Scaled value.: 0 Comment.: Always 0 system_data[1] = 0; // Id.: p202 Value.: 0 Scaled value.: 0 Comment.: Always 0
if (system_capacity_Wh > 60000) {
system_data[2] = 60000;
} else {
system_data[2] = system_data[2] =
(capacity_Wh); // Id.: p203 Value.: 32000 Scaled value.: 32kWh Comment.: Capacity rated, maximum value is 60000 (60kWh) (system_capacity_Wh); // Id.: p203 Value.: 32000 Scaled value.: 32kWh Comment.: Capacity rated, maximum value is 60000 (60kWh)
}
system_data[3] = MAX_POWER; // Id.: p204 Value.: 32000 Scaled value.: 32kWh Comment.: Nominal capacity system_data[3] = MAX_POWER; // Id.: p204 Value.: 32000 Scaled value.: 32kWh Comment.: Nominal capacity
system_data[4] = system_data[4] =
MAX_POWER; // Id.: p205 Value.: 14500 Scaled value.: 30,42kW Comment.: Max Charge/Discharge Power=10.24kW (lowest value of 204 and 205 will be enforced by Gen24) MAX_POWER; // Id.: p205 Value.: 14500 Scaled value.: 30,42kW Comment.: Max Charge/Discharge Power=10.24kW (lowest value of 204 and 205 will be enforced by Gen24)
system_data[5] = system_data[5] =
(max_voltage); // Id.: p206 Value.: 3667 Scaled value.: 362,7VDC Comment.: Max Voltage, if higher charging is not possible (goes into forced discharge) (system_max_design_voltage_dV); // Id.: p206 Value.: 3667 Scaled value.: 362,7VDC Comment.: Max Voltage, if higher charging is not possible (goes into forced discharge)
system_data[6] = system_data[6] =
(min_voltage); // Id.: p207 Value.: 2776 Scaled value.: 277,6VDC Comment.: Min Voltage, if lower Gen24 disables battery (system_min_design_voltage_dV); // Id.: p207 Value.: 2776 Scaled value.: 277,6VDC Comment.: Min Voltage, if lower Gen24 disables battery
system_data[7] = system_data[7] =
53248; // Id.: p208 Value.: 53248 Scaled value.: 53248 Comment.: Always 53248 for this BYD, Peak Charge power? 53248; // Id.: p208 Value.: 53248 Scaled value.: 53248 Comment.: Always 53248 for this BYD, Peak Charge power?
system_data[8] = 10; // Id.: p209 Value.: 10 Scaled value.: 10 Comment.: Always 10 system_data[8] = 10; // Id.: p209 Value.: 10 Scaled value.: 10 Comment.: Always 10
@ -58,42 +62,60 @@ void handle_update_data_modbusp301_byd() {
static uint16_t battery_data[24]; static uint16_t battery_data[24];
static uint8_t bms_char_dis_status = STANDBY; static uint8_t bms_char_dis_status = STANDBY;
if (battery_current == 0) { if (system_battery_current_dA == 0) {
bms_char_dis_status = STANDBY; bms_char_dis_status = STANDBY;
} else if (battery_current > 32767) { //Negative value = Discharging } else if (system_battery_current_dA < 0) { //Negative value = Discharging
bms_char_dis_status = DISCHARGING; bms_char_dis_status = DISCHARGING;
} else { //Positive value = Charging } else { //Positive value = Charging
bms_char_dis_status = CHARGING; bms_char_dis_status = CHARGING;
} }
if (bms_status == ACTIVE) { if (system_bms_status == ACTIVE) {
battery_data[8] = battery_data[8] =
battery_voltage; // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V system_battery_voltage_dV; // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V
} else { } else {
battery_data[8] = 0; battery_data[8] = 0;
} }
battery_data[0] = battery_data[0] =
bms_status; // Id.: p301 Value.: 3 Scaled value.: 3 Comment.: status(*): ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING] system_bms_status; // Id.: p301 Value.: 3 Scaled value.: 3 Comment.: status(*): ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
battery_data[1] = 0; // Id.: p302 Value.: 0 Scaled value.: 0 Comment.: always 0 battery_data[1] = 0; // Id.: p302 Value.: 0 Scaled value.: 0 Comment.: always 0
battery_data[2] = 128 + bms_char_dis_status; // Id.: p303 Value.: 130 Scaled value.: 130 Comment.: mode(*): normal battery_data[2] = 128 + bms_char_dis_status; // Id.: p303 Value.: 130 Scaled value.: 130 Comment.: mode(*): normal
battery_data[3] = SOC; // Id.: p304 Value.: 1700 Scaled value.: 50% Comment.: SOC: (50% would equal 5000) battery_data[3] =
battery_data[4] = capacity_Wh; // Id.: p305 Value.: 32000 Scaled value.: 32kWh Comment.: tot cap: system_scaled_SOC_pptt; // Id.: p304 Value.: 1700 Scaled value.: 50% Comment.: SOC: (50% would equal 5000)
if (system_capacity_Wh > 60000) {
battery_data[4] = 60000;
} else {
battery_data[4] = system_capacity_Wh; // Id.: p305 Value.: 32000 Scaled value.: 32kWh Comment.: tot cap:
}
if (system_remaining_capacity_Wh > 60000) {
battery_data[5] = 60000;
} else {
battery_data[5] = battery_data[5] =
remaining_capacity_Wh; // Id.: p306 Value.: 13260 Scaled value.: 13,26kWh Comment.: remaining cap: 7.68kWh system_remaining_capacity_Wh; // Id.: p306 Value.: 13260 Scaled value.: 13,26kWh Comment.: remaining cap: 7.68kWh
}
if (system_max_discharge_power_W > 30000) {
battery_data[6] = 30000;
} else {
battery_data[6] = battery_data[6] =
max_target_discharge_power; // Id.: p307 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target discharge power: 0W (0W > restricts to no discharge) system_max_discharge_power_W; // Id.: p307 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target discharge power: 0W (0W > restricts to no discharge)
}
if (system_max_charge_power_W > 30000) {
battery_data[7] = 30000;
} else {
battery_data[7] = battery_data[7] =
max_target_charge_power; // Id.: p308 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target charge power: 4.3kW (during charge), both 307&308 can be set (>0) at the same time system_max_charge_power_W; // Id.: p308 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target charge power: 4.3kW (during charge), both 307&308 can be set (>0) at the same time
}
//Battery_data[8] set previously in function // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V //Battery_data[8] set previously in function // Id.: p309 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage outer (0 if status !=3, maybe a contactor closes when active): 173.4V
battery_data[9] = battery_data[9] =
2000; // Id.: p310 Value.: 64121 Scaled value.: 6412,1W Comment.: Current Power to API: if>32768... -(65535-61760)=3775W 2000; // Id.: p310 Value.: 64121 Scaled value.: 6412,1W Comment.: Current Power to API: if>32768... -(65535-61760)=3775W
battery_data[10] = battery_data[10] =
battery_voltage; // Id.: p311 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage inner: 173.2V (LEAF voltage is in whole volts, need to add a decimal) system_battery_voltage_dV; // Id.: p311 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage inner: 173.2V
battery_data[11] = 2000; // Id.: p312 Value.: 64121 Scaled value.: 6412,1W Comment.: p310 battery_data[11] = 2000; // Id.: p312 Value.: 64121 Scaled value.: 6412,1W Comment.: p310
battery_data[12] = battery_data[12] =
temperature_min; // Id.: p313 Value.: 75 Scaled value.: 7,5 Comment.: temp min: 7 degrees (if below 0....65535-t) system_temperature_min_dC; // Id.: p313 Value.: 75 Scaled value.: 7,5 Comment.: temp min: 7 degrees (if below 0....65535-t)
battery_data[13] = battery_data[13] =
temperature_max; // Id.: p314 Value.: 95 Scaled value.: 9,5 Comment.: temp max: 9 degrees (if below 0....65535-t) system_temperature_max_dC; // Id.: p314 Value.: 95 Scaled value.: 9,5 Comment.: temp max: 9 degrees (if below 0....65535-t)
battery_data[14] = 0; // Id.: p315 Value.: 0 Scaled value.: 0 Comment.: always 0 battery_data[14] = 0; // Id.: p315 Value.: 0 Scaled value.: 0 Comment.: always 0
battery_data[15] = 0; // Id.: p316 Value.: 0 Scaled value.: 0 Comment.: always 0 battery_data[15] = 0; // Id.: p316 Value.: 0 Scaled value.: 0 Comment.: always 0
battery_data[16] = 16; // Id.: p317 Value.: 0 Scaled value.: 0 Comment.: counter charge hi battery_data[16] = 16; // Id.: p317 Value.: 0 Scaled value.: 0 Comment.: counter charge hi
@ -105,27 +127,27 @@ void handle_update_data_modbusp301_byd() {
battery_data[21] = battery_data[21] =
52064; // Id.: p322 Value.: 0 Scaled value.: 0 Comment.: counter discharge lo....65536*92+7448 = 6036760 Wh? 52064; // Id.: p322 Value.: 0 Scaled value.: 0 Comment.: counter discharge lo....65536*92+7448 = 6036760 Wh?
battery_data[22] = 230; // Id.: p323 Value.: 0 Scaled value.: 0 Comment.: device temperature (23 degrees) battery_data[22] = 230; // Id.: p323 Value.: 0 Scaled value.: 0 Comment.: device temperature (23 degrees)
battery_data[23] = StateOfHealth; // Id.: p324 Value.: 9900 Scaled value.: 99% Comment.: SOH battery_data[23] = system_SOH_pptt; // Id.: p324 Value.: 9900 Scaled value.: 99% Comment.: SOH
static uint16_t i = 300; static uint16_t i = 300;
memcpy(&mbPV[i], battery_data, sizeof(battery_data)); memcpy(&mbPV[i], battery_data, sizeof(battery_data));
} }
void verify_temperature_modbus() { void verify_temperature_modbus() {
if (LFP_Chemistry) { if (system_LFP_Chemistry) {
return; // Skip the following section return; // Skip the following section
} }
// This section checks if the battery temperature is negative, and incase it falls between -9.0 and -20.0C degrees // This section checks if the battery temperature is negative, and incase it falls between -9.0 and -20.0C degrees
// The Fronius Gen24 (and other Fronius inverters also affected), will stop charge/discharge if the battery gets colder than -10°C. // The Fronius Gen24 (and other Fronius inverters also affected), will stop charge/discharge if the battery gets colder than -10°C.
// This is due to the original battery pack (BYD HVM), is a lithium iron phosphate battery, that cannot be charged in cold weather. // This is due to the original battery pack (BYD HVM), is a lithium iron phosphate battery, that cannot be charged in cold weather.
// When using EV packs with NCM/LMO/NCA chemsitry, this is not a problem, since these chemistries are OK for outdoor cold use. // When using EV packs with NCM/LMO/NCA chemsitry, this is not a problem, since these chemistries are OK for outdoor cold use.
if (temperature_min > 32768) { // Signed value on negative side if (system_temperature_min_dC < 0) {
if (temperature_min < 65445 && temperature_min > 65335) { // Between -9.0 and -20.0C degrees if (system_temperature_min_dC < -90 && system_temperature_min_dC > -200) { // Between -9.0 and -20.0C degrees
temperature_min = 65445; //Cap value to -9.0C system_temperature_min_dC = -90; //Cap value to -9.0C
} }
} }
if (temperature_max > 32768) { // Signed value on negative side if (system_temperature_max_dC < 0) { // Signed value on negative side
if (temperature_max < 65445 && temperature_max > 65335) { // Between -9.0 and -20.0C degrees if (system_temperature_max_dC < -90 && system_temperature_max_dC > -200) { // Between -9.0 and -20.0C degrees
temperature_max = 65445; //Cap value to -9.0C system_temperature_max_dC = -90; //Cap value to -9.0C
} }
} }
} }

View file

@ -7,24 +7,24 @@
#define MAX_POWER 40960 //BYD Modbus specific value #define MAX_POWER 40960 //BYD Modbus specific value
extern uint16_t mbPV[MB_RTU_NUM_VALUES]; extern uint16_t mbPV[MB_RTU_NUM_VALUES];
extern uint16_t SOC; extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t StateOfHealth; extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t battery_voltage; extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t battery_current; extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t capacity_Wh; extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t remaining_capacity_Wh; extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t max_target_discharge_power; extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint8_t bms_status; extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t stat_batt_power; extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t temperature_max; extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_power; extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t max_voltage; extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t min_voltage; extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern bool inverterAllowsContactorClosing; //Bool, true/false
extern bool LFP_Chemistry; extern bool system_LFP_Chemistry; //Bool, true/false
void handle_static_data_modbus_byd(); void handle_static_data_modbus_byd();
void verify_temperature_modbus(); void verify_temperature_modbus();

View file

@ -14,9 +14,9 @@ void handle_update_data_modbus32051() {
system_data[2] = 110; //Goes between 110- -107 [NOTE, SIGNED VALUE] system_data[2] = 110; //Goes between 110- -107 [NOTE, SIGNED VALUE]
system_data[3] = 0; //Goes between 0 and -1 [NOTE, SIGNED VALUE] system_data[3] = 0; //Goes between 0 and -1 [NOTE, SIGNED VALUE]
system_data[4] = 616; //Goes between 616- -520 [NOTE, SIGNED VALUE] system_data[4] = 616; //Goes between 616- -520 [NOTE, SIGNED VALUE]
system_data[5] = temperature_max; //Temperature max? system_data[5] = system_temperature_max_dC; //Temperature max?
system_data[6] = temperature_min; //Temperature min? system_data[6] = system_temperature_min_dC; //Temperature min?
system_data[7] = (SOC / 100); //SOC 0-100%, no decimals system_data[7] = (system_scaled_SOC_pptt / 100); //SOC 0-100%, no decimals
system_data[8] = 98; //Always 98 in logs system_data[8] = 98; //Always 98 in logs
static uint16_t i = 2051; static uint16_t i = 2051;
memcpy(&mbPV[i], system_data, sizeof(system_data)); memcpy(&mbPV[i], system_data, sizeof(system_data));
@ -26,17 +26,17 @@ void handle_update_data_modbus39500() {
// Store the data into the array // Store the data into the array
static uint16_t system_data[26]; static uint16_t system_data[26];
system_data[0] = 0; system_data[0] = 0;
system_data[1] = capacity_Wh; //Capacity? 5000 with 5kWh battery system_data[1] = system_capacity_Wh; //Capacity? 5000 with 5kWh battery
system_data[2] = 0; system_data[2] = 0;
system_data[3] = capacity_Wh; //Capacity? 5000 with 5kWh battery system_data[3] = system_capacity_Wh; //Capacity? 5000 with 5kWh battery
system_data[4] = 0; system_data[4] = 0;
system_data[5] = 2500; //??? system_data[5] = 2500; //???
system_data[6] = 0; system_data[6] = 0;
system_data[7] = 2500; //??? system_data[7] = 2500; //???
system_data[8] = (SOC / 100); //SOC 0-100%, no decimals system_data[8] = (system_scaled_SOC_pptt / 100); //SOC 0-100%, no decimals
system_data[9] = system_data[9] =
1; //Running status, equiv to register 37762, 0 = Offline, 1 = Standby,2 = Running, 3 = Fault, 4 = sleep mode 1; //Running status, equiv to register 37762, 0 = Offline, 1 = Standby,2 = Running, 3 = Fault, 4 = sleep mode
system_data[10] = battery_voltage; //Battery bus voltage (766.5V = 7665) system_data[10] = system_battery_voltage_dV; //Battery bus voltage (766.5V = 7665)
system_data[11] = 9; //TODO: GOES LOWER WITH LOW SOC system_data[11] = 9; //TODO: GOES LOWER WITH LOW SOC
system_data[12] = 0; system_data[12] = 0;
system_data[13] = 699; //TODO: GOES LOWER WITH LOW SOC system_data[13] = 699; //TODO: GOES LOWER WITH LOW SOC
@ -50,7 +50,7 @@ void handle_update_data_modbus39500() {
system_data[21] = 0; system_data[21] = 0;
system_data[22] = 0; system_data[22] = 0;
system_data[23] = 0; system_data[23] = 0;
system_data[24] = (SOC / 10); //SOC 0-100.0%, 1x decimal system_data[24] = (system_scaled_SOC_pptt / 10); //SOC 0-100.0%, 1x decimal
system_data[25] = 0; system_data[25] = 0;
system_data[26] = 1; //Always 1 in logs system_data[26] = 1; //Always 1 in logs
static uint16_t i = 9500; static uint16_t i = 9500;

View file

@ -6,22 +6,27 @@
#define MB_RTU_NUM_VALUES 30000 #define MB_RTU_NUM_VALUES 30000
extern uint16_t mbPV[MB_RTU_NUM_VALUES]; extern uint16_t mbPV[MB_RTU_NUM_VALUES];
extern uint16_t SOC; extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t StateOfHealth; extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t battery_voltage; extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_current; extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t capacity_Wh; extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t remaining_capacity_Wh; extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t max_target_discharge_power; extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t stat_batt_power; extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t temperature_min; extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_max; extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t max_power; extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t max_voltage; extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t min_voltage; extern uint16_t system_max_charge_power_W; //W, 0-65000
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, true/false
void update_modbus_registers_luna2000(); void update_modbus_registers_luna2000();
void handle_update_data_modbus32051(); void handle_update_data_modbus32051();

View file

@ -182,53 +182,53 @@ void update_values_can_pylon() { //This function maps all the values fetched fr
PYLON_4281.data.u8[3] = 0; PYLON_4281.data.u8[3] = 0;
//Voltage (370.0) //Voltage (370.0)
PYLON_4210.data.u8[0] = (battery_voltage >> 8); PYLON_4210.data.u8[0] = (system_battery_voltage_dV >> 8);
PYLON_4210.data.u8[1] = (battery_voltage & 0x00FF); PYLON_4210.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
PYLON_4211.data.u8[0] = (battery_voltage >> 8); PYLON_4211.data.u8[0] = (system_battery_voltage_dV >> 8);
PYLON_4211.data.u8[1] = (battery_voltage & 0x00FF); PYLON_4211.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
//Current (TODO: SIGNED? Or looks like it could be just offset, in that case the below line wont work) //Current (15.0)
PYLON_4210.data.u8[2] = (battery_current >> 8); PYLON_4210.data.u8[2] = (system_battery_current_dA >> 8);
PYLON_4210.data.u8[3] = (battery_current & 0x00FF); PYLON_4210.data.u8[3] = (system_battery_current_dA & 0x00FF);
PYLON_4211.data.u8[2] = (battery_current >> 8); PYLON_4211.data.u8[2] = (system_battery_current_dA >> 8);
PYLON_4211.data.u8[3] = (battery_current & 0x00FF); PYLON_4211.data.u8[3] = (system_battery_current_dA & 0x00FF);
//SOC (100.00%) //SOC (100.00%)
PYLON_4210.data.u8[6] = (SOC * 0.01); //Remove decimals PYLON_4210.data.u8[6] = (system_scaled_SOC_pptt * 0.01); //Remove decimals
PYLON_4211.data.u8[6] = (SOC * 0.01); //Remove decimals PYLON_4211.data.u8[6] = (system_scaled_SOC_pptt * 0.01); //Remove decimals
//StateOfHealth (100.00%) //StateOfHealth (100.00%)
PYLON_4210.data.u8[7] = (StateOfHealth * 0.01); PYLON_4210.data.u8[7] = (system_SOH_pptt * 0.01);
PYLON_4211.data.u8[7] = (StateOfHealth * 0.01); PYLON_4211.data.u8[7] = (system_SOH_pptt * 0.01);
#ifdef INVERT_VOLTAGE //Useful for Sofar inverters \ #ifdef INVERT_VOLTAGE //Useful for Sofar inverters \
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Discharge Cutoff Voltage //Maxvoltage (eg 400.0V = 4000 , 16bits long) Discharge Cutoff Voltage
PYLON_4220.data.u8[0] = (max_voltage & 0x00FF); PYLON_4220.data.u8[0] = (system_max_design_voltage_dV & 0x00FF);
PYLON_4220.data.u8[1] = (max_voltage >> 8); PYLON_4220.data.u8[1] = (system_max_design_voltage_dV >> 8);
PYLON_4221.data.u8[0] = (max_voltage & 0x00FF); PYLON_4221.data.u8[0] = (system_max_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[1] = (max_voltage >> 8); PYLON_4221.data.u8[1] = (system_max_design_voltage_dV >> 8);
//Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage //Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage
PYLON_4220.data.u8[2] = (min_voltage & 0x00FF); PYLON_4220.data.u8[2] = (system_min_design_voltage_dV & 0x00FF);
PYLON_4220.data.u8[3] = (min_voltage >> 8); PYLON_4220.data.u8[3] = (system_min_design_voltage_dV >> 8);
PYLON_4221.data.u8[2] = (min_voltage & 0x00FF); PYLON_4221.data.u8[2] = (system_min_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[3] = (min_voltage >> 8); PYLON_4221.data.u8[3] = (system_min_design_voltage_dV >> 8);
#else #else
//Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage //Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage
PYLON_4220.data.u8[0] = (min_voltage >> 8); PYLON_4220.data.u8[0] = (system_min_design_voltage_dV >> 8);
PYLON_4220.data.u8[1] = (min_voltage & 0x00FF); PYLON_4220.data.u8[1] = (system_min_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[0] = (min_voltage >> 8); PYLON_4221.data.u8[0] = (system_min_design_voltage_dV >> 8);
PYLON_4221.data.u8[1] = (min_voltage & 0x00FF); PYLON_4221.data.u8[1] = (system_min_design_voltage_dV & 0x00FF);
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Discharge Cutoff Voltage //Maxvoltage (eg 400.0V = 4000 , 16bits long) Discharge Cutoff Voltage
PYLON_4220.data.u8[2] = (max_voltage >> 8); PYLON_4220.data.u8[2] = (system_max_design_voltage_dV >> 8);
PYLON_4220.data.u8[3] = (max_voltage & 0x00FF); PYLON_4220.data.u8[3] = (system_max_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[2] = (max_voltage >> 8); PYLON_4221.data.u8[2] = (system_max_design_voltage_dV >> 8);
PYLON_4221.data.u8[3] = (max_voltage & 0x00FF); PYLON_4221.data.u8[3] = (system_max_design_voltage_dV & 0x00FF);
#endif #endif
//In case we run into any errors/faults, we can set charge / discharge forbidden //In case we run into any errors/faults, we can set charge / discharge forbidden
if (bms_status == FAULT) { if (system_bms_status == FAULT) {
PYLON_4280.data.u8[0] = 0xAA; PYLON_4280.data.u8[0] = 0xAA;
PYLON_4280.data.u8[1] = 0xAA; PYLON_4280.data.u8[1] = 0xAA;
PYLON_4280.data.u8[2] = 0xAA; PYLON_4280.data.u8[2] = 0xAA;

View file

@ -5,23 +5,27 @@
#include "../devboard/config.h" // Needed for all defines #include "../devboard/config.h" // Needed for all defines
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" #include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
extern uint16_t SOC; extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t StateOfHealth; extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t battery_voltage; extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_current; extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t capacity_Wh; extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t remaining_capacity_Wh; extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t max_target_discharge_power; extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint8_t bms_status; extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t stat_batt_power; extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_max; extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t CANerror; extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t min_voltage; extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t max_voltage; extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, true/false
void update_values_can_pylon(); void update_values_can_pylon();
void receive_can_pylon(CAN_frame_t rx_frame); void receive_can_pylon(CAN_frame_t rx_frame);

View file

@ -108,8 +108,8 @@ void manageSerialLinkTransmitter() {
Serial.println("SerialDataLink : max_target_discharge_power = 0"); Serial.println("SerialDataLink : max_target_discharge_power = 0");
Serial.println("SerialDataLink : max_target_charge_power = 0"); Serial.println("SerialDataLink : max_target_charge_power = 0");
max_target_discharge_power = 0; system_max_discharge_power_W = 0;
max_target_charge_power = 0; system_max_charge_power_W = 0;
set_event(EVENT_SERIAL_TX_FAILURE, 0); set_event(EVENT_SERIAL_TX_FAILURE, 0);
// throw error // throw error
} }
@ -129,21 +129,21 @@ void manageSerialLinkTransmitter() {
if (currentTime - updateDataTime > 999) { if (currentTime - updateDataTime > 999) {
updateDataTime = currentTime; updateDataTime = currentTime;
dataLinkTransmit.updateData(0, SOC); dataLinkTransmit.updateData(0, system_real_SOC_pptt);
dataLinkTransmit.updateData(1, StateOfHealth); dataLinkTransmit.updateData(1, system_SOH_pptt);
dataLinkTransmit.updateData(2, battery_voltage); dataLinkTransmit.updateData(2, system_battery_voltage_dV);
dataLinkTransmit.updateData(3, battery_current); dataLinkTransmit.updateData(3, system_battery_current_dA);
dataLinkTransmit.updateData(4, capacity_Wh); dataLinkTransmit.updateData(4, system_capacity_Wh);
dataLinkTransmit.updateData(5, remaining_capacity_Wh); dataLinkTransmit.updateData(5, system_remaining_capacity_Wh);
dataLinkTransmit.updateData(6, max_target_discharge_power); dataLinkTransmit.updateData(6, system_max_discharge_power_W);
dataLinkTransmit.updateData(7, max_target_charge_power); dataLinkTransmit.updateData(7, system_max_charge_power_W);
dataLinkTransmit.updateData(8, bms_status); dataLinkTransmit.updateData(8, system_bms_status);
dataLinkTransmit.updateData(9, stat_batt_power); dataLinkTransmit.updateData(9, system_active_power_W);
dataLinkTransmit.updateData(10, temperature_min); dataLinkTransmit.updateData(10, system_temperature_min_dC);
dataLinkTransmit.updateData(11, temperature_max); dataLinkTransmit.updateData(11, system_temperature_max_dC);
dataLinkTransmit.updateData(12, cell_max_voltage); dataLinkTransmit.updateData(12, system_cell_max_voltage_mV);
dataLinkTransmit.updateData(13, cell_min_voltage); dataLinkTransmit.updateData(13, system_cell_min_voltage_mV);
dataLinkTransmit.updateData(14, (int16_t)LFP_Chemistry); dataLinkTransmit.updateData(14, (int16_t)system_LFP_Chemistry);
dataLinkTransmit.updateData(15, batteryAllowsContactorClosing); dataLinkTransmit.updateData(15, batteryAllowsContactorClosing);
} }
} }
@ -152,35 +152,35 @@ void manageSerialLinkTransmitter() {
void printSendingValues() { void printSendingValues() {
Serial.println("Values from battery: "); Serial.println("Values from battery: ");
Serial.print("SOC: "); Serial.print("SOC: ");
Serial.print(SOC); Serial.print(system_real_SOC_pptt);
Serial.print(" SOH: "); Serial.print(" SOH: ");
Serial.print(StateOfHealth); Serial.print(system_SOH_pptt);
Serial.print(" Voltage: "); Serial.print(" Voltage: ");
Serial.print(battery_voltage); Serial.print(system_battery_voltage_dV);
Serial.print(" Current: "); Serial.print(" Current: ");
Serial.print(battery_current); Serial.print(system_battery_current_dA);
Serial.print(" Capacity: "); Serial.print(" Capacity: ");
Serial.print(capacity_Wh); Serial.print(system_capacity_Wh);
Serial.print(" Remain cap: "); Serial.print(" Remain cap: ");
Serial.print(remaining_capacity_Wh); Serial.print(system_remaining_capacity_Wh);
Serial.print(" Max discharge W: "); Serial.print(" Max discharge W: ");
Serial.print(max_target_discharge_power); Serial.print(system_max_discharge_power_W);
Serial.print(" Max charge W: "); Serial.print(" Max charge W: ");
Serial.print(max_target_charge_power); Serial.print(system_max_charge_power_W);
Serial.print(" BMS status: "); Serial.print(" BMS status: ");
Serial.print(bms_status); Serial.print(system_bms_status);
Serial.print(" Power: "); Serial.print(" Power: ");
Serial.print(stat_batt_power); Serial.print(system_active_power_W);
Serial.print(" Temp min: "); Serial.print(" Temp min: ");
Serial.print(temperature_min); Serial.print(system_temperature_min_dC);
Serial.print(" Temp max: "); Serial.print(" Temp max: ");
Serial.print(temperature_max); Serial.print(system_temperature_max_dC);
Serial.print(" Cell max: "); Serial.print(" Cell max: ");
Serial.print(cell_max_voltage); Serial.print(system_cell_max_voltage_mV);
Serial.print(" Cell min: "); Serial.print(" Cell min: ");
Serial.print(cell_min_voltage); Serial.print(system_cell_min_voltage_mV);
Serial.print(" LFP : "); Serial.print(" LFP : ");
Serial.print(LFP_Chemistry); Serial.print(system_LFP_Chemistry);
Serial.print(" batteryAllowsContactorClosing: "); Serial.print(" batteryAllowsContactorClosing: ");
Serial.print(batteryAllowsContactorClosing); Serial.print(batteryAllowsContactorClosing);
Serial.print(" inverterAllowsContactorClosing: "); Serial.print(" inverterAllowsContactorClosing: ");

View file

@ -1,5 +1,3 @@
//SERIAL-LINK-TRANSMITTER-INVERTER.h
#ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H #ifndef SERIAL_LINK_TRANSMITTER_INVERTER_H
#define SERIAL_LINK_TRANSMITTER_INVERTER_H #define SERIAL_LINK_TRANSMITTER_INVERTER_H
@ -9,27 +7,25 @@
#include "../lib/mackelec-SerialDataLink/SerialDataLink.h" #include "../lib/mackelec-SerialDataLink/SerialDataLink.h"
// These parameters need to be mapped for the inverter // These parameters need to be mapped for the inverter
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t capacity_Wh; //Wh, 0-60000 extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint8_t bms_status; //Enum, 0-5 extern uint8_t system_bms_status; //Enum 0-5
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool system_LFP_Chemistry; //Bool, true/false
extern bool LFP_Chemistry;
extern uint16_t CANerror;
// parameters received from receiver // parameters received from receiver
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern bool inverterAllowsContactorClosing; //Bool, true/false
void manageSerialLinkTransmitter(); void manageSerialLinkTransmitter();

View file

@ -99,36 +99,33 @@ CAN_frame_t SMA_158 = {.FIR = {.B =
static int16_t discharge_current = 0; static int16_t discharge_current = 0;
static int16_t charge_current = 0; static int16_t charge_current = 0;
static int16_t temperature_average = 0; static int16_t temperature_average = 0;
static int16_t temp_min = 0;
static int16_t temp_max = 0;
static uint16_t ampere_hours_remaining = 0; static uint16_t ampere_hours_remaining = 0;
void update_values_can_sma() { //This function maps all the values fetched from battery CAN to the correct CAN messages void update_values_can_sma() { //This function maps all the values fetched from battery CAN to the correct CAN messages
//Calculate values //Calculate values
charge_current = charge_current = ((system_max_charge_power_W * 10) /
((max_target_charge_power * 10) / max_voltage); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
//The above calculation results in (30 000*10)/3700=81A //The above calculation results in (30 000*10)/3700=81A
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
discharge_current = ((max_target_discharge_power * 10) / discharge_current = ((system_max_discharge_power_W * 10) /
max_voltage); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
//The above calculation results in (30 000*10)/3700=81A //The above calculation results in (30 000*10)/3700=81A
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
temp_min = temperature_min; //Convert from unsigned to signed temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
temp_max = temperature_max;
temperature_average = ((temp_max + temp_min) / 2);
ampere_hours_remaining = ampere_hours_remaining =
((remaining_capacity_Wh / battery_voltage) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah) ((system_remaining_capacity_Wh / system_battery_voltage_dV) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
//Map values to CAN messages //Map values to CAN messages
//Maxvoltage (eg 400.0V = 4000 , 16bits long) //Maxvoltage (eg 400.0V = 4000 , 16bits long)
SMA_358.data.u8[0] = (max_voltage >> 8); SMA_358.data.u8[0] = (system_max_design_voltage_dV >> 8);
SMA_358.data.u8[1] = (max_voltage & 0x00FF); SMA_358.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
//Minvoltage (eg 300.0V = 3000 , 16bits long) //Minvoltage (eg 300.0V = 3000 , 16bits long)
SMA_358.data.u8[2] = (min_voltage >> 8); //Minvoltage behaves strange on SMA, cuts out at 56% of the set value? SMA_358.data.u8[2] =
SMA_358.data.u8[3] = (min_voltage & 0x00FF); (system_min_design_voltage_dV >> 8); //Minvoltage behaves strange on SMA, cuts out at 56% of the set value?
SMA_358.data.u8[3] = (system_min_design_voltage_dV & 0x00FF);
//Discharge limited current, 500 = 50A, (0.1, A) //Discharge limited current, 500 = 50A, (0.1, A)
SMA_358.data.u8[4] = (discharge_current >> 8); SMA_358.data.u8[4] = (discharge_current >> 8);
SMA_358.data.u8[5] = (discharge_current & 0x00FF); SMA_358.data.u8[5] = (discharge_current & 0x00FF);
@ -137,26 +134,26 @@ void update_values_can_sma() { //This function maps all the values fetched from
SMA_358.data.u8[7] = (charge_current & 0x00FF); SMA_358.data.u8[7] = (charge_current & 0x00FF);
//SOC (100.00%) //SOC (100.00%)
SMA_3D8.data.u8[0] = (SOC >> 8); SMA_3D8.data.u8[0] = (system_scaled_SOC_pptt >> 8);
SMA_3D8.data.u8[1] = (SOC & 0x00FF); SMA_3D8.data.u8[1] = (system_scaled_SOC_pptt & 0x00FF);
//StateOfHealth (100.00%) //StateOfHealth (100.00%)
SMA_3D8.data.u8[2] = (StateOfHealth >> 8); SMA_3D8.data.u8[2] = (system_SOH_pptt >> 8);
SMA_3D8.data.u8[3] = (StateOfHealth & 0x00FF); SMA_3D8.data.u8[3] = (system_SOH_pptt & 0x00FF);
//State of charge (AH, 0.1) //State of charge (AH, 0.1)
SMA_3D8.data.u8[4] = (ampere_hours_remaining >> 8); SMA_3D8.data.u8[4] = (ampere_hours_remaining >> 8);
SMA_3D8.data.u8[5] = (ampere_hours_remaining & 0x00FF); SMA_3D8.data.u8[5] = (ampere_hours_remaining & 0x00FF);
//Voltage (370.0) //Voltage (370.0)
SMA_4D8.data.u8[0] = (battery_voltage >> 8); SMA_4D8.data.u8[0] = (system_battery_voltage_dV >> 8);
SMA_4D8.data.u8[1] = (battery_voltage & 0x00FF); SMA_4D8.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
//Current (TODO: signed OK?) //Current (TODO: signed OK?)
SMA_4D8.data.u8[2] = (battery_current >> 8); SMA_4D8.data.u8[2] = (system_battery_current_dA >> 8);
SMA_4D8.data.u8[3] = (battery_current & 0x00FF); SMA_4D8.data.u8[3] = (system_battery_current_dA & 0x00FF);
//Temperature average //Temperature average
SMA_4D8.data.u8[4] = (temperature_average >> 8); SMA_4D8.data.u8[4] = (temperature_average >> 8);
SMA_4D8.data.u8[5] = (temperature_average & 0x00FF); SMA_4D8.data.u8[5] = (temperature_average & 0x00FF);
//Battery ready //Battery ready
if (bms_status == ACTIVE) { if (system_bms_status == ACTIVE) {
SMA_4D8.data.u8[6] = READY_STATE; SMA_4D8.data.u8[6] = READY_STATE;
} else { } else {
SMA_4D8.data.u8[6] = STOP_STATE; SMA_4D8.data.u8[6] = STOP_STATE;

View file

@ -5,24 +5,27 @@
#include "../devboard/config.h" // Needed for all defines #include "../devboard/config.h" // Needed for all defines
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" #include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t capacity_Wh; //Wh, 0-60000 extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint8_t bms_status; extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t min_voltage; extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t max_voltage; extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, true/false
#define READY_STATE 0x03 #define READY_STATE 0x03
#define STOP_STATE 0x02 #define STOP_STATE 0x02

View file

@ -161,21 +161,22 @@ InvInitState invInitState = SYSTEM_FREQUENCY;
void update_values_can_sma_tripower() { //This function maps all the values fetched from battery CAN to the inverter CAN void update_values_can_sma_tripower() { //This function maps all the values fetched from battery CAN to the inverter CAN
//Calculate values //Calculate values
charge_current = charge_current = ((system_max_charge_power_W * 10) /
((max_target_charge_power * 10) / max_voltage); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
//The above calculation results in (30 000*10)/3700=81A //The above calculation results in (30 000*10)/3700=81A
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
discharge_current = ((max_target_discharge_power * 10) / discharge_current = ((system_max_discharge_power_W * 10) /
max_voltage); //Charge power in W , max volt in V+1decimal (P=UI, solve for I) system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
//The above calculation results in (30 000*10)/3700=81A //The above calculation results in (30 000*10)/3700=81A
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A) discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
temperature_average = ((temperature_max + temperature_min) / 2); temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
ampere_hours_remaining = ampere_hours_remaining =
((remaining_capacity_Wh / battery_voltage) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah) ((system_remaining_capacity_Wh / system_battery_voltage_dV) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
ampere_hours_max = ((capacity_Wh / battery_voltage) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah) ampere_hours_max =
((system_capacity_Wh / system_battery_voltage_dV) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
batteryState = OPERATE; batteryState = OPERATE;
inverterControlFlags = INVERTER_STAY_ON; inverterControlFlags = INVERTER_STAY_ON;
@ -183,11 +184,11 @@ void update_values_can_sma_tripower() { //This function maps all the values fet
//Map values to CAN messages //Map values to CAN messages
// Battery Limits // Battery Limits
//Battery Max Charge Voltage (eg 400.0V = 4000 , 16bits long) //Battery Max Charge Voltage (eg 400.0V = 4000 , 16bits long)
SMA_00D.data.u8[0] = (max_voltage >> 8); SMA_00D.data.u8[0] = (system_max_design_voltage_dV >> 8);
SMA_00D.data.u8[1] = (max_voltage & 0x00FF); SMA_00D.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
//Battery Min Discharge Voltage (eg 300.0V = 3000 , 16bits long) //Battery Min Discharge Voltage (eg 300.0V = 3000 , 16bits long)
SMA_00D.data.u8[2] = (min_voltage >> 8); SMA_00D.data.u8[2] = (system_min_design_voltage_dV >> 8);
SMA_00D.data.u8[3] = (min_voltage & 0x00FF); SMA_00D.data.u8[3] = (system_min_design_voltage_dV & 0x00FF);
//Discharge limited current, 500 = 50A, (0.1, A) //Discharge limited current, 500 = 50A, (0.1, A)
SMA_00D.data.u8[4] = (discharge_current >> 8); SMA_00D.data.u8[4] = (discharge_current >> 8);
SMA_00D.data.u8[5] = (discharge_current & 0x00FF); SMA_00D.data.u8[5] = (discharge_current & 0x00FF);
@ -197,11 +198,11 @@ void update_values_can_sma_tripower() { //This function maps all the values fet
// Battery State // Battery State
//SOC (100.00%) //SOC (100.00%)
SMA_00F.data.u8[0] = (SOC >> 8); SMA_00F.data.u8[0] = (system_scaled_SOC_pptt >> 8);
SMA_00F.data.u8[1] = (SOC & 0x00FF); SMA_00F.data.u8[1] = (system_scaled_SOC_pptt & 0x00FF);
//StateOfHealth (100.00%) //StateOfHealth (100.00%)
SMA_00F.data.u8[2] = (StateOfHealth >> 8); SMA_00F.data.u8[2] = (system_SOH_pptt >> 8);
SMA_00F.data.u8[3] = (StateOfHealth & 0x00FF); SMA_00F.data.u8[3] = (system_SOH_pptt & 0x00FF);
//State of charge (AH, 0.1) //State of charge (AH, 0.1)
SMA_00F.data.u8[4] = (ampere_hours_remaining >> 8); SMA_00F.data.u8[4] = (ampere_hours_remaining >> 8);
SMA_00F.data.u8[5] = (ampere_hours_remaining & 0x00FF); SMA_00F.data.u8[5] = (ampere_hours_remaining & 0x00FF);
@ -223,11 +224,11 @@ void update_values_can_sma_tripower() { //This function maps all the values fet
// Battery Measurements // Battery Measurements
//Voltage (370.0) //Voltage (370.0)
SMA_013.data.u8[0] = (battery_voltage >> 8); SMA_013.data.u8[0] = (system_battery_voltage_dV >> 8);
SMA_013.data.u8[1] = (battery_voltage & 0x00FF); SMA_013.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
//Current (TODO: signed OK?) //Current (TODO: signed OK?)
SMA_013.data.u8[2] = (battery_current >> 8); SMA_013.data.u8[2] = (system_battery_current_dA >> 8);
SMA_013.data.u8[3] = (battery_current & 0x00FF); SMA_013.data.u8[3] = (system_battery_current_dA & 0x00FF);
//Temperature average //Temperature average
SMA_013.data.u8[4] = (temperature_average >> 8); SMA_013.data.u8[4] = (temperature_average >> 8);
SMA_013.data.u8[5] = (temperature_average & 0x00FF); SMA_013.data.u8[5] = (temperature_average & 0x00FF);
@ -237,11 +238,11 @@ void update_values_can_sma_tripower() { //This function maps all the values fet
// Battery Temperature and Cellvoltages // Battery Temperature and Cellvoltages
// Battery max temperature // Battery max temperature
SMA_014.data.u8[0] = (temperature_max >> 8); SMA_014.data.u8[0] = (system_temperature_max_dC >> 8);
SMA_014.data.u8[1] = (temperature_max & 0x00FF); SMA_014.data.u8[1] = (system_temperature_max_dC & 0x00FF);
// Battery min temperature // Battery min temperature
SMA_014.data.u8[2] = (temperature_min >> 8); SMA_014.data.u8[2] = (system_temperature_min_dC >> 8);
SMA_014.data.u8[3] = (temperature_min & 0x00FF); SMA_014.data.u8[3] = (system_temperature_min_dC & 0x00FF);
// Battery Cell Voltage (sum) // Battery Cell Voltage (sum)
//SMA_014.data.u8[4] = (??? >> 8); //TODO scaling? //SMA_014.data.u8[4] = (??? >> 8); //TODO scaling?
//SMA_014.data.u8[5] = (??? & 0x00FF); //TODO scaling? //SMA_014.data.u8[5] = (??? & 0x00FF); //TODO scaling?

View file

@ -5,24 +5,27 @@
#include "../devboard/config.h" // Needed for all defines #include "../devboard/config.h" // Needed for all defines
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" #include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t capacity_Wh; //Wh, 0-60000 extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint8_t bms_char_dis_status; //Enum, 0-2 extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t min_voltage; extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t max_voltage; extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, true/false
void update_values_can_sma_tripower(); void update_values_can_sma_tripower();
void send_can_sma_tripower(); void send_can_sma_tripower();

View file

@ -281,29 +281,29 @@ CAN_frame_t SOFAR_7C0 = {.FIR = {.B =
void update_values_can_sofar() { //This function maps all the values fetched from battery CAN to the correct CAN messages void update_values_can_sofar() { //This function maps all the values fetched from battery CAN to the correct CAN messages
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage //Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage
SOFAR_351.data.u8[0] = (max_voltage >> 8); SOFAR_351.data.u8[0] = (system_max_design_voltage_dV >> 8);
SOFAR_351.data.u8[1] = (max_voltage & 0x00FF); SOFAR_351.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
//SOFAR_351.data.u8[2] = DC charge current limitation (Default 25.0A) //SOFAR_351.data.u8[2] = DC charge current limitation (Default 25.0A)
//SOFAR_351.data.u8[3] = DC charge current limitation //SOFAR_351.data.u8[3] = DC charge current limitation
//SOFAR_351.data.u8[4] = DC discharge current limitation (Default 25.0A) //SOFAR_351.data.u8[4] = DC discharge current limitation (Default 25.0A)
//SOFAR_351.data.u8[5] = DC discharge current limitation //SOFAR_351.data.u8[5] = DC discharge current limitation
//Minvoltage (eg 300.0V = 3000 , 16bits long) Discharge Cutoff Voltage //Minvoltage (eg 300.0V = 3000 , 16bits long) Discharge Cutoff Voltage
SOFAR_351.data.u8[6] = (min_voltage >> 8); SOFAR_351.data.u8[6] = (system_min_design_voltage_dV >> 8);
SOFAR_351.data.u8[7] = (min_voltage & 0x00FF); SOFAR_351.data.u8[7] = (system_min_design_voltage_dV & 0x00FF);
//SOC //SOC
SOFAR_355.data.u8[0] = (SOC / 100); SOFAR_355.data.u8[0] = (system_scaled_SOC_pptt / 100);
SOFAR_355.data.u8[2] = (StateOfHealth / 100); SOFAR_355.data.u8[2] = (system_SOH_pptt / 100);
//SOFAR_355.data.u8[6] = (AH_remaining >> 8); //SOFAR_355.data.u8[6] = (AH_remaining >> 8);
//SOFAR_355.data.u8[7] = (AH_remaining & 0x00FF); //SOFAR_355.data.u8[7] = (AH_remaining & 0x00FF);
//Voltage (370.0) //Voltage (370.0)
SOFAR_356.data.u8[0] = (battery_voltage >> 8); SOFAR_356.data.u8[0] = (system_battery_voltage_dV >> 8);
SOFAR_356.data.u8[1] = (battery_voltage & 0x00FF); SOFAR_356.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
SOFAR_356.data.u8[2] = (battery_current >> 8); SOFAR_356.data.u8[2] = (system_battery_current_dA >> 8);
SOFAR_356.data.u8[3] = (battery_current & 0x00FF); SOFAR_356.data.u8[3] = (system_battery_current_dA & 0x00FF);
SOFAR_356.data.u8[2] = (temperature_max >> 8); SOFAR_356.data.u8[2] = (system_temperature_max_dC >> 8);
SOFAR_356.data.u8[3] = (temperature_max & 0x00FF); SOFAR_356.data.u8[3] = (system_temperature_max_dC & 0x00FF);
} }
void receive_can_sofar(CAN_frame_t rx_frame) { void receive_can_sofar(CAN_frame_t rx_frame) {

View file

@ -6,21 +6,27 @@
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" #include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
// These parameters need to be mapped for the inverter // These parameters need to be mapped for the inverter
extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t capacity_Wh; //Wh, 0-60000 extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t max_target_discharge_power; //W, 0-60000 extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; //W, 0-60000 extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t cell_max_voltage; //mV, 0-4350 extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t cell_min_voltage; //mV, 0-4350 extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_max_charge_power_W; //W, 0-65000
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, true/false
extern uint16_t min_voltage; extern uint16_t min_voltage;
extern uint16_t max_voltage; extern uint16_t max_voltage;

View file

@ -1,12 +1,15 @@
#include "SOLAX-CAN.h" #include "SOLAX-CAN.h"
#include "../devboard/utils/events.h"
/* Do not change code below unless you are sure what you are doing */ /* Do not change code below unless you are sure what you are doing */
static uint16_t max_charge_rate_amp = 0; static uint16_t max_charge_rate_amp = 0;
static uint16_t max_discharge_rate_amp = 0; static uint16_t max_discharge_rate_amp = 0;
static uint16_t temperature_average = 0; static int16_t temperature_average = 0;
static int STATE = BATTERY_ANNOUNCE; static uint8_t STATE = BATTERY_ANNOUNCE;
static unsigned long LastFrameTime = 0; static unsigned long LastFrameTime = 0;
static int number_of_batteries = 1; static uint8_t number_of_batteries = 1;
static uint16_t capped_capacity_Wh;
static uint16_t capped_remaining_capacity_Wh;
//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
@ -122,64 +125,77 @@ void update_values_can_solax() { //This function maps all the values fetched fr
STATE = BATTERY_ANNOUNCE; STATE = BATTERY_ANNOUNCE;
} }
//Calculate the required values //Calculate the required values
temperature_average = ((temperature_max + temperature_min) / 2); temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
//max_target_charge_power (30000W max) //system_max_charge_power_W (30000W max)
if (SOC > 9999) //99.99% if (system_scaled_SOC_pptt > 9999) //99.99%
{ //Additional safety incase SOC% is 100, then do not charge battery further { //Additional safety incase SOC% is 100, then do not charge battery further
max_charge_rate_amp = 0; max_charge_rate_amp = 0;
} else { //We can pass on the battery charge rate (in W) to the inverter (that takes A) } else { //We can pass on the battery charge rate (in W) to the inverter (that takes A)
if (max_target_charge_power >= 30000) { if (system_max_charge_power_W >= 30000) {
max_charge_rate_amp = 75; //Incase battery can take over 30kW, cap value to 75A max_charge_rate_amp = 75; //Incase battery can take over 30kW, cap value to 75A
} else { //Calculate the W value into A } else { //Calculate the W value into A
max_charge_rate_amp = (max_target_charge_power / (battery_voltage * 0.1)); // P/U = I max_charge_rate_amp = (system_max_charge_power_W / (system_battery_voltage_dV * 0.1)); // P/U = I
} }
} }
//max_target_discharge_power (30000W max) //system_max_discharge_power_W (30000W max)
if (SOC < 100) //1.00% if (system_scaled_SOC_pptt < 100) //1.00%
{ //Additional safety incase SOC% is below 1, then do not charge battery further { //Additional safety incase SOC% is below 1, then do not charge battery further
max_discharge_rate_amp = 0; max_discharge_rate_amp = 0;
} else { //We can pass on the battery discharge rate to the inverter } else { //We can pass on the battery discharge rate to the inverter
if (max_target_discharge_power >= 30000) { if (system_max_discharge_power_W >= 30000) {
max_discharge_rate_amp = 75; //Incase battery can be charged with over 30kW, cap value to 75A max_discharge_rate_amp = 75; //Incase battery can be charged with over 30kW, cap value to 75A
} else { //Calculate the W value into A } else { //Calculate the W value into A
max_discharge_rate_amp = (max_target_discharge_power / (battery_voltage * 0.1)); // P/U = I max_discharge_rate_amp = (system_max_discharge_power_W / (system_battery_voltage_dV * 0.1)); // P/U = I
} }
} }
// Batteries might be larger than uint16_t value can take
if (system_capacity_Wh > 65000) {
capped_capacity_Wh = 65000;
} else {
capped_capacity_Wh = system_capacity_Wh;
}
// Batteries might be larger than uint16_t value can take
if (system_remaining_capacity_Wh > 65000) {
capped_remaining_capacity_Wh = 65000;
} else {
capped_remaining_capacity_Wh = system_remaining_capacity_Wh;
}
//Put the values into the CAN messages //Put the values into the CAN messages
//BMS_Limits //BMS_Limits
SOLAX_1872.data.u8[0] = (uint8_t)max_voltage; //TODO: scaling OK? SOLAX_1872.data.u8[0] = (uint8_t)system_max_design_voltage_dV;
SOLAX_1872.data.u8[1] = (max_voltage >> 8); SOLAX_1872.data.u8[1] = (system_max_design_voltage_dV >> 8);
SOLAX_1872.data.u8[2] = (uint8_t)min_voltage; //TODO: scaling OK? SOLAX_1872.data.u8[2] = (uint8_t)system_min_design_voltage_dV;
SOLAX_1872.data.u8[3] = (min_voltage >> 8); SOLAX_1872.data.u8[3] = (system_min_design_voltage_dV >> 8);
SOLAX_1872.data.u8[4] = (uint8_t)(max_charge_rate_amp * 10); //TODO: scaling OK? SOLAX_1872.data.u8[4] = (uint8_t)(max_charge_rate_amp * 10);
SOLAX_1872.data.u8[5] = ((max_charge_rate_amp * 10) >> 8); SOLAX_1872.data.u8[5] = ((max_charge_rate_amp * 10) >> 8);
SOLAX_1872.data.u8[6] = (uint8_t)(max_discharge_rate_amp * 10); //TODO: scaling OK? SOLAX_1872.data.u8[6] = (uint8_t)(max_discharge_rate_amp * 10);
SOLAX_1872.data.u8[7] = ((max_discharge_rate_amp * 10) >> 8); SOLAX_1872.data.u8[7] = ((max_discharge_rate_amp * 10) >> 8);
//BMS_PackData //BMS_PackData
SOLAX_1873.data.u8[0] = (uint8_t)battery_voltage; // OK SOLAX_1873.data.u8[0] = (uint8_t)system_battery_voltage_dV; // OK
SOLAX_1873.data.u8[1] = (battery_voltage >> 8); SOLAX_1873.data.u8[1] = (system_battery_voltage_dV >> 8);
SOLAX_1873.data.u8[2] = (int8_t)battery_current; // OK, Signed (Active current in Amps x 10) SOLAX_1873.data.u8[2] = (int8_t)system_battery_current_dA; // OK, Signed (Active current in Amps x 10)
SOLAX_1873.data.u8[3] = (battery_current >> 8); SOLAX_1873.data.u8[3] = (system_battery_current_dA >> 8);
SOLAX_1873.data.u8[4] = (uint8_t)(SOC / 100); //SOC (100.00%) SOLAX_1873.data.u8[4] = (uint8_t)(system_scaled_SOC_pptt / 100); //SOC (100.00%)
//SOLAX_1873.data.u8[5] = //Seems like this is not required? Or shall we put SOC decimals here? //SOLAX_1873.data.u8[5] = //Seems like this is not required? Or shall we put SOC decimals here?
SOLAX_1873.data.u8[6] = (uint8_t)(remaining_capacity_Wh / 100); //TODO: scaling OK? SOLAX_1873.data.u8[6] = (uint8_t)(capped_remaining_capacity_Wh / 100); //TODO: scaling OK?
SOLAX_1873.data.u8[7] = ((remaining_capacity_Wh / 100) >> 8); SOLAX_1873.data.u8[7] = ((capped_remaining_capacity_Wh / 100) >> 8);
//BMS_CellData //BMS_CellData
SOLAX_1874.data.u8[0] = (uint8_t)temperature_max; SOLAX_1874.data.u8[0] = (int8_t)system_temperature_max_dC;
SOLAX_1874.data.u8[1] = (temperature_max >> 8); SOLAX_1874.data.u8[1] = (system_temperature_max_dC >> 8);
SOLAX_1874.data.u8[2] = (uint8_t)temperature_min; SOLAX_1874.data.u8[2] = (int8_t)system_temperature_min_dC;
SOLAX_1874.data.u8[3] = (temperature_min >> 8); SOLAX_1874.data.u8[3] = (system_temperature_min_dC >> 8);
SOLAX_1874.data.u8[4] = SOLAX_1874.data.u8[4] =
(uint8_t)(cell_max_voltage); //TODO: scaling OK? Supposed to be alarm trigger absolute cell max? (uint8_t)(system_cell_max_voltage_mV); //TODO: scaling OK? Supposed to be alarm trigger absolute cell max?
SOLAX_1874.data.u8[5] = (cell_max_voltage >> 8); SOLAX_1874.data.u8[5] = (system_cell_max_voltage_mV >> 8);
SOLAX_1874.data.u8[6] = SOLAX_1874.data.u8[6] =
(uint8_t)(cell_min_voltage); //TODO: scaling OK? Supposed to be alarm trigger absolute cell min? (uint8_t)(system_cell_min_voltage_mV); //TODO: scaling OK? Supposed to be alarm trigger absolute cell min?
SOLAX_1874.data.u8[7] = (cell_min_voltage >> 8); SOLAX_1874.data.u8[7] = (system_cell_min_voltage_mV >> 8);
//BMS_Status //BMS_Status
SOLAX_1875.data.u8[0] = (uint8_t)temperature_average; SOLAX_1875.data.u8[0] = (uint8_t)temperature_average;
@ -188,11 +204,11 @@ void update_values_can_solax() { //This function maps all the values fetched fr
SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on. SOLAX_1875.data.u8[4] = (uint8_t)0; // Contactor Status 0=off, 1=on.
//BMS_PackTemps (strange name, since it has voltages?) //BMS_PackTemps (strange name, since it has voltages?)
SOLAX_1876.data.u8[2] = (uint8_t)cell_max_voltage; //TODO: scaling OK? SOLAX_1876.data.u8[2] = (uint8_t)system_cell_max_voltage_mV; //TODO: scaling OK?
SOLAX_1876.data.u8[3] = (cell_max_voltage >> 8); SOLAX_1876.data.u8[3] = (system_cell_max_voltage_mV >> 8);
SOLAX_1876.data.u8[6] = (uint8_t)cell_min_voltage; //TODO: scaling OK? SOLAX_1876.data.u8[6] = (uint8_t)system_cell_min_voltage_mV; //TODO: scaling OK?
SOLAX_1876.data.u8[7] = (cell_min_voltage >> 8); SOLAX_1876.data.u8[7] = (system_cell_min_voltage_mV >> 8);
//Unknown //Unknown
SOLAX_1877.data.u8[4] = (uint8_t)0x50; // Battery type SOLAX_1877.data.u8[4] = (uint8_t)0x50; // Battery type
@ -201,11 +217,11 @@ void update_values_can_solax() { //This function maps all the values fetched fr
(uint8_t)0x02; // The above firmware version applies to:02 = Master BMS, 10 = S1, 20 = S2, 30 = S3, 40 = S4 (uint8_t)0x02; // The above firmware version applies to:02 = Master BMS, 10 = S1, 20 = S2, 30 = S3, 40 = S4
//BMS_PackStats //BMS_PackStats
SOLAX_1878.data.u8[0] = (uint8_t)(battery_voltage); //TODO: should this be max or current voltage? SOLAX_1878.data.u8[0] = (uint8_t)(system_battery_voltage_dV); //TODO: should this be max or current voltage?
SOLAX_1878.data.u8[1] = ((battery_voltage) >> 8); SOLAX_1878.data.u8[1] = ((system_battery_voltage_dV) >> 8);
SOLAX_1878.data.u8[4] = (uint8_t)capacity_Wh; //TODO: scaling OK? SOLAX_1878.data.u8[4] = (uint8_t)capped_capacity_Wh; //TODO: scaling OK?
SOLAX_1878.data.u8[5] = (capacity_Wh >> 8); SOLAX_1878.data.u8[5] = (capped_capacity_Wh >> 8);
// BMS_Answer // BMS_Answer
SOLAX_1801.data.u8[0] = 2; SOLAX_1801.data.u8[0] = 2;
@ -264,8 +280,10 @@ void receive_can_solax(CAN_frame_t rx_frame) {
CAN_WriteFrame(&SOLAX_1878); CAN_WriteFrame(&SOLAX_1878);
// Message from the inverter to open contactor // Message from the inverter to open contactor
// Byte 4 changes from 1 to 0 // Byte 4 changes from 1 to 0
if (rx_frame.data.u64 == Contactor_Open_Payload) if (rx_frame.data.u64 == Contactor_Open_Payload) {
set_event(EVENT_INVERTER_OPEN_CONTACTOR, 0);
STATE = BATTERY_ANNOUNCE; STATE = BATTERY_ANNOUNCE;
}
break; break;
} }
} }

View file

@ -8,24 +8,27 @@
extern ACAN2515 can; extern ACAN2515 can;
extern uint16_t SOC; extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t StateOfHealth; extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern uint16_t battery_voltage; extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern uint16_t battery_current; extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern uint16_t capacity_Wh; extern int16_t system_active_power_W; //W, -32000 to 32000
extern uint16_t remaining_capacity_Wh; extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t max_target_discharge_power; extern uint16_t system_battery_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t max_target_charge_power; extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t stat_batt_power; extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000)
extern uint16_t temperature_min; extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t temperature_max; extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t CANerror; extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t min_voltage; extern uint16_t system_max_discharge_power_W; //W, 0-65000
extern uint16_t max_voltage; extern uint16_t system_max_charge_power_W; //W, 0-65000
extern uint16_t cell_max_voltage; extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value
extern uint16_t cell_min_voltage; extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, true/false
// Timeout in milliseconds // Timeout in milliseconds
#define SolaxTimeout 2000 #define SolaxTimeout 2000