Data layer introduction (#254)

* Active power, SOH

* Total and remaining capacity

* pre-commit, baby!

* Typo

* pre-commit wrestling

* Temp min/max

* Voltage and current

* Min/max design voltage

* BMS status, max charge/discharge, cell min/max

* BMS status cleanup

* Pre-commit, we meet again

* Cell voltages and minor fixes

* Cell number

* SOC

* Missed a spot

* Event handling moved, Wh bugs fixed, time measurements improved

* Pre-commit, old friend...

* Battery chemistry

* Update TESLA-MODEL-3-BATTERY.cpp

* Total capacity/Wh max, soem default values

* Good ol' pre-comm

* Some prio/core cleanup, docs...

* Contactor closing variables

* Max charge/discharge amps

* Data layer variable documentation

* USER_SETTINGS comments

* Charger comment

* Update datalayer.h
This commit is contained in:
Cabooman 2024-04-15 22:26:10 +02:00 committed by GitHub
parent e7f14084e3
commit 0fee07313a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
68 changed files with 1097 additions and 1510 deletions

View file

@ -60,28 +60,6 @@ uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
ModbusServerRTU MBserver(Serial2, 2000);
#endif
// Common system parameters. Batteries map their values to these variables
uint32_t system_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-500000 Wh
uint32_t system_remaining_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-500000 Wh
int16_t system_temperature_max_dC = 0; //C+1, -50.0 - 50.0
int16_t system_temperature_min_dC = 0; //C+1, -50.0 - 50.0
int32_t system_active_power_W = 0; //Watts, -200000 to 200000 W
int16_t system_battery_current_dA = 0; //A+1, -1000 - 1000
uint16_t system_battery_voltage_dV = 3700; //V+1, 0-1000.0 (0-10000)
uint16_t system_max_design_voltage_dV = 5000; //V+1, 0-1000.0 (0-10000)
uint16_t system_min_design_voltage_dV = 2500; //V+1, 0-1000.0 (0-10000)
uint16_t system_scaled_SOC_pptt = 5000; //SOC%, 0-100.00 (0-10000)
uint16_t system_real_SOC_pptt = 5000; //SOC%, 0-100.00 (0-10000)
uint16_t system_SOH_pptt = 9900; //SOH%, 0-100.00 (0-10000)
uint32_t system_max_discharge_power_W = 0; //Watts, 0 to 200000
uint32_t system_max_charge_power_W = 4312; //Watts, 0 to 200000
uint16_t system_cell_max_voltage_mV = 3700; //mV, 0-5000 , Stores the highest cell millivolt value
uint16_t system_cell_min_voltage_mV = 3700; //mV, 0-5000, Stores the minimum cell millivolt value
uint16_t system_cellvoltages_mV[MAX_AMOUNT_CELLS]; //Array with all cell voltages. Oversized to accomodate all setups
uint8_t system_bms_status = ACTIVE; //ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
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
volatile float charger_setpoint_HV_VDC = 0.0f;
volatile float charger_setpoint_HV_IDC = 0.0f;
@ -104,6 +82,8 @@ MyTimer core_task_timer_10s(INTERVAL_10_S);
int64_t mqtt_task_time_us;
MyTimer mqtt_task_timer_10s(INTERVAL_10_S);
MyTimer loop_task_timer_10s(INTERVAL_10_S);
// Contactor parameters
#ifdef CONTACTOR_CONTROL
enum State { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
@ -124,11 +104,9 @@ unsigned long prechargeStartTime = 0;
unsigned long negativeStartTime = 0;
unsigned long timeSpentInFaultedMode = 0;
#endif
bool batteryAllowsContactorClosing = false;
bool inverterAllowsContactorClosing = true;
TaskHandle_t main_loop_task;
TaskHandle_t wifi_loop_task;
TaskHandle_t mqtt_loop_task;
// Initialization
void setup() {
@ -140,8 +118,8 @@ void setup() {
init_webserver();
init_mDNS();
#ifdef MQTT
xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, &mqtt_task_time_us, TASK_WIFI_PRIO,
&main_loop_task, WIFI_CORE);
xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, &mqtt_task_time_us, TASK_CONNECTIVITY_PRIO,
&mqtt_loop_task, WIFI_CORE);
#endif
#endif
@ -169,7 +147,16 @@ void setup() {
}
// Perform main program functions
void loop() {}
void loop() {
START_TIME_MEASUREMENT(loop_func);
run_event_handling();
END_TIME_MEASUREMENT_MAX(loop_func, datalayer.system.status.loop_task_10s_max_us);
#ifdef FUNCTION_TIME_MEASUREMENT
if (loop_task_timer_10s.elapsed()) {
datalayer.system.status.loop_task_10s_max_us = 0;
}
#endif
}
#ifdef MQTT
void mqtt_loop(void* task_time_us) {
@ -179,11 +166,11 @@ void mqtt_loop(void* task_time_us) {
while (true) {
START_TIME_MEASUREMENT(mqtt);
mqtt_loop();
END_TIME_MEASUREMENT_MAX(mqtt, datalayer.system.status.time_mqtt_us);
END_TIME_MEASUREMENT_MAX(mqtt, datalayer.system.status.mqtt_task_10s_max_us);
#ifdef FUNCTION_TIME_MEASUREMENT
if (mqtt_task_timer_10s.elapsed()) {
datalayer.system.status.time_mqtt_us = 0;
datalayer.system.status.mqtt_task_10s_max_us = 0;
}
#endif
delay(1);
@ -219,7 +206,7 @@ void core_loop(void* task_time_us) {
START_TIME_MEASUREMENT(wifi_ota);
wifi_monitor();
ElegantOTA.loop();
END_TIME_MEASUREMENT(wifi_ota, datalayer.system.status.time_wifi_us);
END_TIME_MEASUREMENT_MAX(wifi_ota, datalayer.system.status.time_wifi_us);
#endif
START_TIME_MEASUREMENT(time_10ms);
@ -252,21 +239,29 @@ void core_loop(void* task_time_us) {
send_can2();
#endif
END_TIME_MEASUREMENT_MAX(cantx, datalayer.system.status.time_cantx_us);
START_TIME_MEASUREMENT(events);
run_event_handling();
END_TIME_MEASUREMENT_MAX(events, datalayer.system.status.time_events_us);
END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.main_task_10s_max_us);
END_TIME_MEASUREMENT_MAX(all, datalayer.system.status.core_task_10s_max_us);
#ifdef FUNCTION_TIME_MEASUREMENT
datalayer.system.status.main_task_max_us =
MAX(datalayer.system.status.main_task_10s_max_us, datalayer.system.status.main_task_max_us);
if (datalayer.system.status.core_task_10s_max_us > datalayer.system.status.core_task_max_us) {
// Update worst case total time
datalayer.system.status.core_task_max_us = datalayer.system.status.core_task_10s_max_us;
// Record snapshots of task times
datalayer.system.status.time_snap_comm_us = datalayer.system.status.time_comm_us;
datalayer.system.status.time_snap_10ms_us = datalayer.system.status.time_10ms_us;
datalayer.system.status.time_snap_5s_us = datalayer.system.status.time_5s_us;
datalayer.system.status.time_snap_cantx_us = datalayer.system.status.time_cantx_us;
datalayer.system.status.time_snap_wifi_us = datalayer.system.status.time_wifi_us;
}
datalayer.system.status.core_task_max_us =
MAX(datalayer.system.status.core_task_10s_max_us, datalayer.system.status.core_task_max_us);
if (core_task_timer_10s.elapsed()) {
datalayer.system.status.time_wifi_us = 0;
datalayer.system.status.time_comm_us = 0;
datalayer.system.status.time_10ms_us = 0;
datalayer.system.status.time_5s_us = 0;
datalayer.system.status.time_cantx_us = 0;
datalayer.system.status.time_events_us = 0;
datalayer.system.status.main_task_10s_max_us = 0;
datalayer.system.status.core_task_10s_max_us = 0;
}
#endif
vTaskDelayUntil(&xLastWakeTime, xFrequency);
@ -314,25 +309,25 @@ void init_stored_settings() {
static uint32_t temp = 0;
temp = settings.getUInt("BATTERY_WH_MAX", false);
if (temp != 0) {
BATTERY_WH_MAX = temp;
datalayer.battery.info.total_capacity_Wh = temp;
}
temp = settings.getUInt("MAXPERCENTAGE", false);
if (temp != 0) {
MAXPERCENTAGE = temp;
datalayer.battery.settings.max_percentage = temp * 10; // Multiply by 10 for backwards compatibility
}
temp = settings.getUInt("MINPERCENTAGE", false);
if (temp != 0) {
MINPERCENTAGE = temp;
datalayer.battery.settings.min_percentage = temp * 10; // Multiply by 10 for backwards compatibility
}
temp = settings.getUInt("MAXCHARGEAMP", false);
if (temp != 0) {
MAXCHARGEAMP = temp;
datalayer.battery.info.max_charge_amp_dA = temp;
}
temp = settings.getUInt("MAXDISCHARGEAMP", false);
if (temp != 0) {
MAXDISCHARGEAMP = temp;
datalayer.battery.info.max_discharge_amp_dA = temp;
temp = settings.getBool("USE_SCALED_SOC", false);
USE_SCALED_SOC = temp; //This bool needs to be checked inside the temp!= block
datalayer.battery.settings.soc_scaling_active = temp; //This bool needs to be checked inside the temp!= block
} // No way to know if it wasnt reset otherwise
settings.end();
@ -484,7 +479,8 @@ void inform_user_on_inverter() {
#endif
#endif
#ifdef SOLAX_CAN
inverterAllowsContactorClosing = false; // The inverter needs to allow first on this protocol
datalayer.system.status.inverter_allows_contactor_closing =
false; // The inverter needs to allow first on this protocol
intervalUpdateValues = 800; // This protocol also requires the values to be updated faster
#ifdef DEBUG_VIA_USB
Serial.println("SOLAX CAN protocol selected");
@ -618,7 +614,7 @@ void send_can2() {
#ifdef CONTACTOR_CONTROL
void handle_contactors() {
// First check if we have any active errors, incase we do, turn off the battery
if (system_bms_status == FAULT) {
if (datalayer.battery.status.bms_status == FAULT) {
timeSpentInFaultedMode++;
} else {
timeSpentInFaultedMode = 0;
@ -642,14 +638,15 @@ void handle_contactors() {
ledcWrite(NEGATIVE_PWM_Ch, 0);
#endif
if (batteryAllowsContactorClosing && inverterAllowsContactorClosing) {
if (datalayer.system.status.battery_allows_contactor_closing &&
datalayer.system.status.inverter_allows_contactor_closing) {
contactorStatus = PRECHARGE;
}
}
// In case the inverter requests contactors to open, set the state accordingly
if (contactorStatus == COMPLETED) {
if (!inverterAllowsContactorClosing)
if (!datalayer.system.status.inverter_allows_contactor_closing)
contactorStatus = DISCONNECTED;
// Skip running the state machine below if it has already completed
return;
@ -702,19 +699,37 @@ void handle_contactors() {
#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;
if (datalayer.battery.settings.soc_scaling_active) {
/** SOC Scaling
*
* This is essentially a more static version of a stochastic oscillator (https://en.wikipedia.org/wiki/Stochastic_oscillator)
*
* The idea is this:
*
* real_soc - min_percent 3000 - 1000
* ------------------------- = scaled_soc, or ----------- = 0.25
* max_percent - min-percent 8000 - 1000
*
* Because we use integers, we want to account for the scaling:
*
* 10000 * (real_soc - min_percent) 10000 * (3000 - 1000)
* -------------------------------- = scaled_soc, or --------------------- = 2500
* max_percent - min_percent 8000 - 1000
*
* Or as a one-liner: (10000 * (real_soc - min_percentage)) / (max_percentage - min_percentage)
*
* Before we use real_soc, we must make sure that it's within the range of min_percentage and max_percentage.
*/
uint32_t calc_soc;
// Make sure that the SOC starts out between min and max percentages
calc_soc = CONSTRAIN(datalayer.battery.status.real_soc, datalayer.battery.settings.min_percentage,
datalayer.battery.settings.max_percentage);
// Perform scaling
calc_soc = 10000 * (calc_soc - datalayer.battery.settings.min_percentage);
calc_soc = calc_soc / (datalayer.battery.settings.max_percentage - datalayer.battery.settings.min_percentage);
datalayer.battery.status.reported_soc = calc_soc;
} else { // No SOC window wanted. Set scaled to same as real.
system_scaled_SOC_pptt = system_real_SOC_pptt;
datalayer.battery.status.reported_soc = datalayer.battery.status.real_soc;
}
}
@ -773,12 +788,14 @@ void init_serialDataLink() {
void storeSettings() {
settings.begin("batterySettings", false);
settings.putUInt("BATTERY_WH_MAX", BATTERY_WH_MAX);
settings.putUInt("MAXPERCENTAGE", MAXPERCENTAGE);
settings.putUInt("MINPERCENTAGE", MINPERCENTAGE);
settings.putUInt("MAXCHARGEAMP", MAXCHARGEAMP);
settings.putUInt("MAXDISCHARGEAMP", MAXDISCHARGEAMP);
settings.putBool("USE_SCALED_SOC", USE_SCALED_SOC);
settings.putUInt("BATTERY_WH_MAX", datalayer.battery.info.total_capacity_Wh);
settings.putUInt("MAXPERCENTAGE",
datalayer.battery.settings.max_percentage / 10); // Divide by 10 for backwards compatibility
settings.putUInt("MINPERCENTAGE",
datalayer.battery.settings.min_percentage / 10); // Divide by 10 for backwards compatibility
settings.putUInt("MAXCHARGEAMP", datalayer.battery.info.max_charge_amp_dA);
settings.putUInt("MAXDISCHARGEAMP", datalayer.battery.info.max_discharge_amp_dA);
settings.putBool("USE_SCALED_SOC", datalayer.battery.settings.soc_scaling_active);
settings.end();
}

View file

@ -3,21 +3,7 @@
/* This file contains all the battery settings and limits */
/* They can be defined here, or later on in the WebUI */
/* Battery settings */
volatile bool USE_SCALED_SOC =
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 =
800; //80.0% , Max percentage the battery will charge to (Inverter gets 100% when reached)
volatile uint16_t MINPERCENTAGE =
200; //20.0% , Min percentage the battery will discharge to (Inverter gets 0% when reached)
volatile uint16_t MAXCHARGEAMP =
300; //30.0A , BYD CAN specific setting, Max charge in Amp (Some inverters needs to be limited)
volatile uint16_t MAXDISCHARGEAMP =
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 */
/* Charger settings (Optional, when using generator charging) */
volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack
volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger
volatile float CHARGER_MIN_HV = 200; // Min permissible output (VDC) of charger

View file

@ -58,14 +58,23 @@
//#define CHEVYVOLT_CHARGER //Enable this line to control a Chevrolet Volt charger connected to battery - for example, when generator charging or using an inverter without a charging function.
//#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 */
extern volatile uint32_t BATTERY_WH_MAX;
extern volatile uint16_t MAXPERCENTAGE;
extern volatile uint16_t MINPERCENTAGE;
extern volatile uint16_t MAXCHARGEAMP;
extern volatile uint16_t MAXDISCHARGEAMP;
/* Battery settings */
// Predefined total energy capacity of the battery in Watt-hours
#define BATTERY_WH_MAX 30000
// Increases battery life. If true will rescale SOC between the configured min/max-percentage
#define BATTERY_USE_SCALED_SOC true
// 8000 = 80.0% , Max percentage the battery will charge to (Inverter gets 100% when reached)
#define BATTERY_MAXPERCENTAGE 8000
// 2000 = 20.0% , Min percentage the battery will discharge to (Inverter gets 0% when reached)
#define BATTERY_MINPERCENTAGE 2000
// 300 = 30.0A , BYD CAN specific setting, Max charge in Amp (Some inverters needs to be limited)
#define BATTERY_MAX_CHARGE_AMP 300
// 300 = 30.0A , BYD CAN specific setting, Max discharge in Amp (Some inverters needs to be limited)
#define BATTERY_MAX_DISCHARGE_AMP 300
extern volatile uint8_t AccessPointEnabled;
extern volatile bool USE_SCALED_SOC;
extern const uint8_t wifi_channel;
/* Charger limits (Optional): Set in the USER_SETTINGS.cpp or later in the webserver */
extern volatile float charger_setpoint_HV_VDC;
@ -80,6 +89,4 @@ extern volatile float CHARGER_END_A;
extern bool charger_HV_enabled;
extern bool charger_aux12V_enabled;
extern const uint8_t wifi_channel;
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef BMW_I3_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -414,39 +415,37 @@ static uint8_t increment_alive_counter(uint8_t counter) {
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
system_real_SOC_pptt = (battery_HVBatt_SOC * 10);
datalayer.battery.status.real_soc = (battery_HVBatt_SOC * 10);
system_battery_voltage_dV = battery_volts; //Unit V+1 (5000 = 500.0V)
datalayer.battery.status.voltage_dV = battery_volts; //Unit V+1 (5000 = 500.0V)
system_battery_current_dA = battery_current;
datalayer.battery.status.current_dA = battery_current;
system_capacity_Wh = BATTERY_WH_MAX;
datalayer.battery.status.remaining_capacity_Wh = (battery_energy_content_maximum_kWh * 1000); // Convert kWh to Wh
system_remaining_capacity_Wh = (battery_energy_content_maximum_kWh * 1000); // Convert kWh to Wh
system_SOH_pptt = battery_soh * 100;
datalayer.battery.status.soh_pptt = battery_soh * 100;
if (battery_BEV_available_power_longterm_discharge > 65000) {
system_max_discharge_power_W = 65000;
datalayer.battery.status.max_discharge_power_W = 65000;
} else {
system_max_discharge_power_W = battery_BEV_available_power_longterm_discharge;
datalayer.battery.status.max_discharge_power_W = battery_BEV_available_power_longterm_discharge;
}
if (battery_BEV_available_power_longterm_charge > 65000) {
system_max_charge_power_W = 65000;
datalayer.battery.status.max_charge_power_W = 65000;
} else {
system_max_charge_power_W = battery_BEV_available_power_longterm_charge;
datalayer.battery.status.max_charge_power_W = battery_BEV_available_power_longterm_charge;
}
battery_power = (system_battery_current_dA * (system_battery_voltage_dV / 100));
battery_power = (datalayer.battery.status.current_dA * (datalayer.battery.status.voltage_dV / 100));
system_active_power_W = battery_power;
datalayer.battery.status.active_power_W = battery_power;
system_temperature_min_dC = battery_temperature_min * 10; // Add a decimal
datalayer.battery.status.temperature_min_dC = battery_temperature_min * 10; // Add a decimal
system_temperature_max_dC = battery_temperature_max * 10; // Add a decimal
datalayer.battery.status.temperature_max_dC = battery_temperature_max * 10; // Add a decimal
system_cell_min_voltage_mV = system_cellvoltages_mV[0];
system_cell_max_voltage_mV = system_cellvoltages_mV[1];
datalayer.battery.status.cell_min_voltage_mV = datalayer.battery.status.cell_voltages_mV[0];
datalayer.battery.status.cell_max_voltage_mV = datalayer.battery.status.cell_voltages_mV[1];
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) {
@ -464,25 +463,25 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.println(" ");
Serial.print("Values sent to inverter: ");
Serial.print("Real SOC%: ");
Serial.print(system_real_SOC_pptt * 0.01);
Serial.print(datalayer.battery.status.real_soc * 0.01);
Serial.print(" Battery voltage: ");
Serial.print(system_battery_voltage_dV * 0.1);
Serial.print(datalayer.battery.status.voltage_dV * 0.1);
Serial.print(" Battery current: ");
Serial.print(system_battery_current_dA * 0.1);
Serial.print(datalayer.battery.status.current_dA * 0.1);
Serial.print(" Wh when full: ");
Serial.print(system_capacity_Wh);
Serial.print(datalayer.battery.info.total_capacity_Wh);
Serial.print(" Remaining Wh: ");
Serial.print(system_remaining_capacity_Wh);
Serial.print(datalayer.battery.status.remaining_capacity_Wh);
Serial.print(" Max charge power: ");
Serial.print(system_max_charge_power_W);
Serial.print(datalayer.battery.status.max_charge_power_W);
Serial.print(" Max discharge power: ");
Serial.print(system_max_discharge_power_W);
Serial.print(datalayer.battery.status.max_discharge_power_W);
Serial.print(" Active power: ");
Serial.print(system_active_power_W);
Serial.print(datalayer.battery.status.active_power_W);
Serial.print(" Min temp: ");
Serial.print(system_temperature_min_dC * 0.1);
Serial.print(datalayer.battery.status.temperature_min_dC * 0.1);
Serial.print(" Max temp: ");
Serial.print(system_temperature_max_dC * 0.1);
Serial.print(datalayer.battery.status.temperature_max_dC * 0.1);
#endif
}
@ -569,13 +568,13 @@ void receive_can_battery(CAN_frame_t rx_frame) {
case 0x426: // TODO: Figure out how to trigger sending of this. Does the SME require some CAN command?
battery_cellvoltage_mux = rx_frame.data.u8[0];
if (battery_cellvoltage_mux == 0) {
system_cellvoltages_mV[0] = ((rx_frame.data.u8[1] * 10) + 1800);
system_cellvoltages_mV[1] = ((rx_frame.data.u8[2] * 10) + 1800);
system_cellvoltages_mV[2] = ((rx_frame.data.u8[3] * 10) + 1800);
system_cellvoltages_mV[3] = ((rx_frame.data.u8[4] * 10) + 1800);
system_cellvoltages_mV[4] = ((rx_frame.data.u8[5] * 10) + 1800);
system_cellvoltages_mV[5] = ((rx_frame.data.u8[6] * 10) + 1800);
system_cellvoltages_mV[5] = ((rx_frame.data.u8[7] * 10) + 1800);
datalayer.battery.status.cell_voltages_mV[0] = ((rx_frame.data.u8[1] * 10) + 1800);
datalayer.battery.status.cell_voltages_mV[1] = ((rx_frame.data.u8[2] * 10) + 1800);
datalayer.battery.status.cell_voltages_mV[2] = ((rx_frame.data.u8[3] * 10) + 1800);
datalayer.battery.status.cell_voltages_mV[3] = ((rx_frame.data.u8[4] * 10) + 1800);
datalayer.battery.status.cell_voltages_mV[4] = ((rx_frame.data.u8[5] * 10) + 1800);
datalayer.battery.status.cell_voltages_mV[5] = ((rx_frame.data.u8[6] * 10) + 1800);
datalayer.battery.status.cell_voltages_mV[5] = ((rx_frame.data.u8[7] * 10) + 1800);
}
break;
case 0x430: //BMS [1s] - Charging status of high-voltage battery - 2
@ -622,13 +621,13 @@ void receive_can_battery(CAN_frame_t rx_frame) {
switch (cmdState) {
case CELL_VOLTAGE:
if (next_data >= 4) {
system_cellvoltages_mV[0] = (message_data[0] << 8 | message_data[1]);
system_cellvoltages_mV[2] = (message_data[2] << 8 | message_data[3]);
datalayer.battery.status.cell_voltages_mV[0] = (message_data[0] << 8 | message_data[1]);
datalayer.battery.status.cell_voltages_mV[2] = (message_data[2] << 8 | message_data[3]);
}
break;
case CELL_VOLTAGE_AVG:
if (next_data >= 30) {
system_cellvoltages_mV[1] = (message_data[10] << 8 | message_data[11]) / 10;
datalayer.battery.status.cell_voltages_mV[1] = (message_data[10] << 8 | message_data[11]) / 10;
battery_capacity_cah = (message_data[4] << 8 | message_data[5]);
}
break;
@ -669,7 +668,7 @@ void send_can_battery() {
BMW_10B.data.u8[1] = 0x10; // Close contactors
}
if (system_bms_status == FAULT) {
if (datalayer.battery.status.bms_status == FAULT) {
BMW_10B.data.u8[1] = 0x00; // Open contactors (TODO: test if this works)
}
@ -808,8 +807,9 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("BMW i3 battery selected");
#endif
system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 2800; // 280.0V under this, discharging further is disabled
datalayer.battery.info.max_design_voltage_dV =
4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 2800; // 280.0V under this, discharging further is disabled
digitalWrite(WUP_PIN, HIGH); // Wake up the battery
}

View file

@ -7,29 +7,6 @@
#define BATTERY_SELECTED
#define WUP_PIN 25
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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
void setup_battery(void);
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef CHADEMO_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -90,16 +91,19 @@ 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
system_real_SOC_pptt = ChargingRate;
datalayer.battery.status.real_soc = ChargingRate;
system_max_discharge_power_W = (MaximumDischargeCurrent * MaximumBatteryVoltage); //In Watts, Convert A to P
datalayer.battery.status.max_discharge_power_W =
(MaximumDischargeCurrent * MaximumBatteryVoltage); //In Watts, Convert A to P
system_battery_voltage_dV = TargetBatteryVoltage; //TODO: scaling?
datalayer.battery.status.voltage_dV = TargetBatteryVoltage; //TODO: scaling?
system_capacity_Wh = ((RatedBatteryCapacity / 0.11) *
datalayer.battery.info.total_capacity_Wh =
((RatedBatteryCapacity / 0.11) *
1000); //(Added in CHAdeMO v1.0.1), maybe handle hardcoded on lower protocol version?
system_remaining_capacity_Wh = (system_real_SOC_pptt / 100) * system_capacity_Wh;
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
/* Check if the Vehicle is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) {
@ -197,7 +201,8 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Chademo battery selected");
#endif
system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 2000; // 200.0V under this, discharging further is disabled
datalayer.battery.info.max_design_voltage_dV =
4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 2000; // 200.0V under this, discharging further is disabled
}
#endif

View file

@ -6,28 +6,6 @@
#define BATTERY_SELECTED
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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
void setup_battery(void);
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef IMIEV_CZERO_ION_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -39,30 +40,30 @@ static double max_temp_cel = 20.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
system_real_SOC_pptt = (uint16_t)(BMU_SOC * 100); //increase BMU_SOC range from 0-100 -> 100.00
datalayer.battery.status.real_soc = (uint16_t)(BMU_SOC * 100); //increase BMU_SOC range from 0-100 -> 100.00
system_battery_voltage_dV = (uint16_t)(BMU_PackVoltage * 10); // Multiply by 10 and cast to uint16_t
datalayer.battery.status.voltage_dV = (uint16_t)(BMU_PackVoltage * 10); // Multiply by 10 and cast to uint16_t
system_battery_current_dA = (BMU_Current * 10); //Todo, scaling?
datalayer.battery.status.current_dA = (BMU_Current * 10); //Todo, scaling?
system_capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value
system_remaining_capacity_Wh = (uint16_t)((system_real_SOC_pptt / 10000) * system_capacity_Wh);
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
//We do not know if the max charge power is sent by the battery. So we estimate the value based on SOC%
if (system_scaled_SOC_pptt == 10000) { //100.00%
system_max_charge_power_W = 0; //When battery is 100% full, set allowed charge W to 0
if (datalayer.battery.status.reported_soc == 10000) { //100.00%
datalayer.battery.status.max_charge_power_W = 0; //When battery is 100% full, set allowed charge W to 0
} else {
system_max_charge_power_W = 10000; //Otherwise we can push 10kW into the pack!
datalayer.battery.status.max_charge_power_W = 10000; //Otherwise we can push 10kW into the pack!
}
if (system_scaled_SOC_pptt < 200) { //2.00%
system_max_discharge_power_W = 0; //When battery is empty (below 2%), set allowed discharge W to 0
if (datalayer.battery.status.reported_soc < 200) { //2.00%
datalayer.battery.status.max_discharge_power_W =
0; //When battery is empty (below 2%), set allowed discharge W to 0
} else {
system_max_discharge_power_W = 10000; //Otherwise we can discharge 10kW from the pack!
datalayer.battery.status.max_discharge_power_W = 10000; //Otherwise we can discharge 10kW from the pack!
}
system_active_power_W = BMU_Power; //TODO: Scaling?
datalayer.battery.status.active_power_W = BMU_Power; //TODO: Scaling?
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
@ -96,16 +97,16 @@ void update_values_battery() { //This function maps all the values fetched via
//Map all cell voltages to the global array
for (int i = 0; i < 88; ++i) {
system_cellvoltages_mV[i] = (uint16_t)(cell_voltages[i] * 1000);
datalayer.battery.status.cell_voltages_mV[i] = (uint16_t)(cell_voltages[i] * 1000);
}
system_cell_max_voltage_mV = (uint16_t)(max_volt_cel * 1000);
datalayer.battery.status.cell_max_voltage_mV = (uint16_t)(max_volt_cel * 1000);
system_cell_min_voltage_mV = (uint16_t)(min_volt_cel * 1000);
datalayer.battery.status.cell_min_voltage_mV = (uint16_t)(min_volt_cel * 1000);
system_temperature_min_dC = (int16_t)(min_temp_cel * 10);
datalayer.battery.status.temperature_min_dC = (int16_t)(min_temp_cel * 10);
system_temperature_min_dC = (int16_t)(max_temp_cel * 10);
datalayer.battery.status.temperature_min_dC = (int16_t)(max_temp_cel * 10);
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) {
@ -217,8 +218,9 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Mitsubishi i-MiEV / Citroen C-Zero / Peugeot Ion battery selected");
#endif
system_max_design_voltage_dV = 3600; // 360.0V, over this, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 3160; // 316.0V under this, discharging further is disabled
datalayer.battery.info.max_design_voltage_dV =
3600; // 360.0V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 3160; // 316.0V under this, discharging further is disabled
}
#endif

View file

@ -6,29 +6,6 @@
#define BATTERY_SELECTED
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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
void setup_battery(void);
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef KIA_E_GMP_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -50,49 +51,48 @@ CANFDMessage EGMP_7E4_ack;
void set_cell_voltages(CANFDMessage frame, int start, int length, int startCell) {
for (size_t i = 0; i < length; i++) {
system_cellvoltages_mV[startCell + i] = (frame.data[start + i] * 20);
datalayer.battery.status.cell_voltages_mV[startCell + i] = (frame.data[start + i] * 20);
}
}
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
system_real_SOC_pptt = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
system_SOH_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
datalayer.battery.status.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
system_battery_voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0)
datalayer.battery.status.voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0)
system_battery_current_dA = batteryAmps; //value is *10 (150 = 15.0)
datalayer.battery.status.current_dA = batteryAmps; //value is *10 (150 = 15.0)
system_capacity_Wh = BATTERY_WH_MAX;
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
system_remaining_capacity_Wh = static_cast<int>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
//system_max_charge_power_W = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts
//datalayer.battery.status.max_charge_power_W = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts
//The allowed charge power is not available. We estimate this value
if (system_scaled_SOC_pptt == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
system_max_charge_power_W = 0;
if (datalayer.battery.status.reported_soc == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
datalayer.battery.status.max_charge_power_W = 0;
} else { // No limits, max charging power allowed
system_max_charge_power_W = MAXCHARGEPOWERALLOWED;
datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED;
}
//system_max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
if (system_scaled_SOC_pptt < 100) { // When scaled SOC is <1%, set allowed charge power to 0
system_max_discharge_power_W = 0;
//datalayer.battery.status.max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
if (datalayer.battery.status.reported_soc < 100) { // When scaled SOC is <1%, set allowed charge power to 0
datalayer.battery.status.max_discharge_power_W = 0;
} else { // No limits, max charging power allowed
system_max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
}
powerWatt = ((batteryVoltage * batteryAmps) / 100);
system_active_power_W = powerWatt; //Power in watts, Negative = charging batt
datalayer.battery.status.active_power_W = powerWatt; //Power in watts, Negative = charging batt
system_temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C
datalayer.battery.status.temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C
system_temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C
datalayer.battery.status.temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C
system_cell_max_voltage_mV = CellVoltMax_mV;
datalayer.battery.status.cell_max_voltage_mV = CellVoltMax_mV;
system_cell_min_voltage_mV = CellVoltMin_mV;
datalayer.battery.status.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*/
if (!CANstillAlive) {
@ -111,7 +111,7 @@ void update_values_battery() { //This function maps all the values fetched via
}
// Check if cell voltages are within allowed range
cell_deviation_mV = (system_cell_max_voltage_mV - system_cell_min_voltage_mV);
cell_deviation_mV = (datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV);
if (CellVoltMax_mV >= MAX_CELL_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
@ -125,9 +125,10 @@ void update_values_battery() { //This function maps all the values fetched via
clear_event(EVENT_CELL_DEVIATION_HIGH);
}
if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
system_max_charge_power_W = 0;
system_max_discharge_power_W = 0;
if (datalayer.battery.status.bms_status ==
FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
datalayer.battery.status.max_charge_power_W = 0;
datalayer.battery.status.max_discharge_power_W = 0;
}
/* Safeties verified. Perform USB serial printout if configured to do so */
@ -146,7 +147,7 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.print(" Amps | ");
Serial.print((uint16_t)batteryVoltage / 10.0, 1);
Serial.print(" Volts | ");
Serial.print((int16_t)system_active_power_W);
Serial.print((int16_t)datalayer.battery.status.active_power_W);
Serial.println(" Watts");
Serial.print("Allowed Charge ");
Serial.print((uint16_t)allowedChargePower * 10);
@ -329,7 +330,7 @@ void receive_canfd_battery(CANFDMessage frame) {
set_cell_voltages(frame, 1, 5, 187);
//set_cell_count();
} else if (poll_data_pid == 5) {
// system_number_of_cells = 98;
// datalayer.battery.info.number_of_cells = 98;
SOC_Display = frame.data[1] * 5;
}
break;
@ -401,11 +402,12 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Hyundai E-GMP (Electric Global Modular Platform) battery selected");
#endif
system_number_of_cells = 192; // TODO: will vary depending on battery
datalayer.battery.info.number_of_cells = 192; // TODO: will vary depending on battery
system_max_design_voltage_dV =
datalayer.battery.info.max_design_voltage_dV =
8064; // TODO: define when battery is known, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 4320; // TODO: define when battery is known. discharging further is disabled
datalayer.battery.info.min_design_voltage_dV =
4320; // TODO: define when battery is known. discharging further is disabled
EGMP_7E4.id = 0x7E4;
EGMP_7E4.ext = false;

View file

@ -12,29 +12,6 @@ extern ACAN2517FD canfd;
#define MAXCHARGEPOWERALLOWED 10000
#define MAXDISCHARGEPOWERALLOWED 10000
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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 setup_battery(void);
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef KIA_HYUNDAI_64_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -146,42 +147,42 @@ 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
system_real_SOC_pptt = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
datalayer.battery.status.real_soc = (SOC_Display * 10); //increase SOC range from 0-100.0 -> 100.00
system_SOH_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
datalayer.battery.status.soh_pptt = (batterySOH * 10); //Increase decimals from 100.0% -> 100.00%
system_battery_voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0)
datalayer.battery.status.voltage_dV = batteryVoltage; //value is *10 (3700 = 370.0)
system_battery_current_dA = -batteryAmps; //value is *10 (150 = 15.0) , invert the sign
datalayer.battery.status.current_dA = -batteryAmps; //value is *10 (150 = 15.0) , invert the sign
system_capacity_Wh = BATTERY_WH_MAX;
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
system_remaining_capacity_Wh = static_cast<int>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
//system_max_charge_power_W = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts
//datalayer.battery.status.max_charge_power_W = (uint16_t)allowedChargePower * 10; //From kW*100 to Watts
//The allowed charge power is not available. We estimate this value
if (system_scaled_SOC_pptt == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
system_max_charge_power_W = 0;
if (datalayer.battery.status.reported_soc == 10000) { // When scaled SOC is 100%, set allowed charge power to 0
datalayer.battery.status.max_charge_power_W = 0;
} else { // No limits, max charging power allowed
system_max_charge_power_W = MAXCHARGEPOWERALLOWED;
datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED;
}
//system_max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
if (system_scaled_SOC_pptt < 100) { // When scaled SOC is <1%, set allowed charge power to 0
system_max_discharge_power_W = 0;
//datalayer.battery.status.max_discharge_power_W = (uint16_t)allowedDischargePower * 10; //From kW*100 to Watts
if (datalayer.battery.status.reported_soc < 100) { // When scaled SOC is <1%, set allowed charge power to 0
datalayer.battery.status.max_discharge_power_W = 0;
} else { // No limits, max charging power allowed
system_max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
}
//Power in watts, Negative = charging batt
system_active_power_W = ((system_battery_voltage_dV * system_battery_current_dA) / 100);
datalayer.battery.status.active_power_W =
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
system_temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C
datalayer.battery.status.temperature_min_dC = (int8_t)temperatureMin * 10; //Increase decimals, 17C -> 17.0C
system_temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C
datalayer.battery.status.temperature_max_dC = (int8_t)temperatureMax * 10; //Increase decimals, 18C -> 18.0C
system_cell_max_voltage_mV = CellVoltMax_mV;
datalayer.battery.status.cell_max_voltage_mV = CellVoltMax_mV;
system_cell_min_voltage_mV = CellVoltMin_mV;
datalayer.battery.status.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*/
if (!CANstillAlive) {
@ -202,22 +203,22 @@ void update_values_battery() { //This function maps all the values fetched via
//Map all cell voltages to the global array
for (int i = 0; i < 98; ++i) {
if (cellvoltages_mv[i] > 1000) {
system_cellvoltages_mV[i] = cellvoltages_mv[i];
datalayer.battery.status.cell_voltages_mV[i] = cellvoltages_mv[i];
}
}
// Check if we have 98S or 90S battery
if (system_cellvoltages_mV[97] > 0) {
system_number_of_cells = 98;
system_max_design_voltage_dV = 4040;
system_min_design_voltage_dV = 3100;
if (datalayer.battery.status.cell_voltages_mV[97] > 0) {
datalayer.battery.info.number_of_cells = 98;
datalayer.battery.info.max_design_voltage_dV = 4040;
datalayer.battery.info.min_design_voltage_dV = 3100;
} else {
system_number_of_cells = 90;
system_max_design_voltage_dV = 3870;
system_min_design_voltage_dV = 2250;
datalayer.battery.info.number_of_cells = 90;
datalayer.battery.info.max_design_voltage_dV = 3870;
datalayer.battery.info.min_design_voltage_dV = 2250;
}
// Check if cell voltages are within allowed range
cell_deviation_mV = (system_cell_max_voltage_mV - system_cell_min_voltage_mV);
cell_deviation_mV = (datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV);
if (CellVoltMax_mV >= MAX_CELL_VOLTAGE) {
set_event(EVENT_CELL_OVER_VOLTAGE, 0);
@ -231,9 +232,10 @@ void update_values_battery() { //This function maps all the values fetched via
clear_event(EVENT_CELL_DEVIATION_HIGH);
}
if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
system_max_charge_power_W = 0;
system_max_discharge_power_W = 0;
if (datalayer.battery.status.bms_status ==
FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
datalayer.battery.status.max_charge_power_W = 0;
datalayer.battery.status.max_discharge_power_W = 0;
}
/* Safeties verified. Perform USB serial printout if configured to do so */
@ -252,7 +254,7 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.print(" Amps | ");
Serial.print((uint16_t)batteryVoltage / 10.0, 1);
Serial.print(" Volts | ");
Serial.print((int16_t)system_active_power_W);
Serial.print((int16_t)datalayer.battery.status.active_power_W);
Serial.println(" Watts");
Serial.print("Allowed Charge ");
Serial.print((uint16_t)allowedChargePower * 10);
@ -602,8 +604,9 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Kia Niro / Hyundai Kona 64kWh battery selected");
#endif
system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
datalayer.battery.info.max_design_voltage_dV =
4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
}
#endif

View file

@ -9,29 +9,6 @@
#define MAXCHARGEPOWERALLOWED 10000
#define MAXDISCHARGEPOWERALLOWED 10000
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-150000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-150000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -32000 to 32000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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
void setup_battery(void);
#endif

View file

@ -159,84 +159,88 @@ 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 */
/* Start with mapping all values */
system_SOH_pptt = (LB_StateOfHealth * 100); //Increase range from 99% -> 99.00%
datalayer.battery.status.soh_pptt = (LB_StateOfHealth * 100); //Increase range from 99% -> 99.00%
system_real_SOC_pptt = (LB_SOC * 10);
datalayer.battery.status.real_soc = (LB_SOC * 10);
system_battery_voltage_dV = (LB_Total_Voltage2 * 5); //0.5V/bit, multiply by 5 to get Voltage+1decimal (350.5V = 701)
datalayer.battery.status.voltage_dV =
(LB_Total_Voltage2 * 5); //0.5V/bit, multiply by 5 to get Voltage+1decimal (350.5V = 701)
system_battery_current_dA = (LB_Current2 * 5); //0.5A/bit, multiply by 5 to get Amp+1decimal (5,5A = 11)
datalayer.battery.status.current_dA = (LB_Current2 * 5); //0.5A/bit, multiply by 5 to get Amp+1decimal (5,5A = 11)
system_capacity_Wh = (LB_Max_GIDS * WH_PER_GID);
datalayer.battery.info.total_capacity_Wh = (LB_Max_GIDS * WH_PER_GID);
system_remaining_capacity_Wh = LB_Wh_Remaining;
datalayer.battery.status.remaining_capacity_Wh = LB_Wh_Remaining;
LB_Power =
((LB_Total_Voltage2 * LB_Current2) / 4); //P = U * I (Both values are 0.5 per bit so the math is non-intuitive)
system_active_power_W = LB_Power;
datalayer.battery.status.active_power_W = LB_Power;
//Update temperature readings. Method depends on which generation LEAF battery is used
if (LEAF_Battery_Type == ZE0_BATTERY) {
//Since we only have average value, send the minimum as -1.0 degrees below average
system_temperature_min_dC = ((LB_AverageTemperature * 10) - 10); //Increase range from C to C+1, remove 1.0C
system_temperature_max_dC = (LB_AverageTemperature * 10); //Increase range from C to C+1
datalayer.battery.status.temperature_min_dC =
((LB_AverageTemperature * 10) - 10); //Increase range from C to C+1, remove 1.0C
datalayer.battery.status.temperature_max_dC = (LB_AverageTemperature * 10); //Increase range from C to C+1
} else if (LEAF_Battery_Type == AZE0_BATTERY) {
//Use the value sent constantly via CAN in 5C0 (only available on AZE0)
system_temperature_min_dC = (LB_HistData_Temperature_MIN * 10); //Increase range from C to C+1
system_temperature_max_dC = (LB_HistData_Temperature_MAX * 10); //Increase range from C to C+1
datalayer.battery.status.temperature_min_dC = (LB_HistData_Temperature_MIN * 10); //Increase range from C to C+1
datalayer.battery.status.temperature_max_dC = (LB_HistData_Temperature_MAX * 10); //Increase range from C to C+1
} 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
{
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
if (temp_polled_min < temp_polled_max) { //Catch any edge cases from Temp_fromRAW_to_F function
system_temperature_min_dC = temp_polled_min;
system_temperature_max_dC = temp_polled_max;
datalayer.battery.status.temperature_min_dC = temp_polled_min;
datalayer.battery.status.temperature_max_dC = temp_polled_max;
} else {
system_temperature_min_dC = temp_polled_max;
system_temperature_max_dC = temp_polled_min;
datalayer.battery.status.temperature_min_dC = temp_polled_max;
datalayer.battery.status.temperature_max_dC = temp_polled_min;
}
}
}
// Define power able to be discharged from battery
if (LB_Discharge_Power_Limit > 60) { //if >60kW can be pulled from battery
system_max_discharge_power_W = 60000; //cap value so we don't go over uint16 value
datalayer.battery.status.max_discharge_power_W = 60000; //cap value so we don't go over uint16 value
} else {
system_max_discharge_power_W = (LB_Discharge_Power_Limit * 1000); //kW to W
datalayer.battery.status.max_discharge_power_W = (LB_Discharge_Power_Limit * 1000); //kW to W
}
if (system_scaled_SOC_pptt == 0) { //Scaled SOC% value is 0.00%, we should not discharge battery further
system_max_discharge_power_W = 0;
if (datalayer.battery.status.reported_soc ==
0) { //Scaled SOC% value is 0.00%, we should not discharge battery further
datalayer.battery.status.max_discharge_power_W = 0;
}
// Define power able to be put into the battery
if (LB_Charge_Power_Limit > 60) { //if >60kW can be put into the battery
system_max_charge_power_W = 60000; //cap value so we don't go over uint16 value
datalayer.battery.status.max_charge_power_W = 60000; //cap value so we don't go over uint16 value
} else {
system_max_charge_power_W = (LB_Charge_Power_Limit * 1000); //kW to W
datalayer.battery.status.max_charge_power_W = (LB_Charge_Power_Limit * 1000); //kW to W
}
if (system_scaled_SOC_pptt == 10000) //Scaled SOC% value is 100.00%
if (datalayer.battery.status.reported_soc == 10000) //Scaled SOC% value is 100.00%
{
system_max_charge_power_W = 0; //No need to charge further, set max power to 0
datalayer.battery.status.max_charge_power_W = 0; //No need to charge further, set max power to 0
}
//Map all cell voltages to the global array
for (int i = 0; i < 96; ++i) {
system_cellvoltages_mV[i] = cell_voltages[i];
datalayer.battery.status.cell_voltages_mV[i] = cell_voltages[i];
}
/*Extra safety functions below*/
if (LB_GIDS < 10) //700Wh left in battery!
{ //Battery is running abnormally low, some discharge logic might have failed. Zero it all out.
set_event(EVENT_BATTERY_EMPTY, 0);
system_real_SOC_pptt = 0;
system_max_discharge_power_W = 0;
datalayer.battery.status.real_soc = 0;
datalayer.battery.status.max_discharge_power_W = 0;
}
//Check if SOC% is plausible
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 (datalayer.battery.status.voltage_dV >
(datalayer.battery.info.max_design_voltage_dV -
100)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
if (LB_SOC < 650) {
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, LB_SOC / 10); // Set event with the SOC as data
} else {
@ -246,22 +250,22 @@ 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
set_event(EVENT_BATTERY_FULL, 0);
system_max_charge_power_W = 0;
datalayer.battery.status.max_charge_power_W = 0;
} else {
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
set_event(EVENT_BATTERY_EMPTY, 0);
system_max_discharge_power_W = 0;
datalayer.battery.status.max_discharge_power_W = 0;
} else {
clear_event(EVENT_BATTERY_EMPTY);
}
if (LB_Relay_Cut_Request) { //LB_FAIL, BMS requesting shutdown and contactors to be opened
//Note, this is sometimes triggered during the night while idle, and the BMS recovers after a while. Removed latching from this scenario
system_max_discharge_power_W = 0;
system_max_charge_power_W = 0;
datalayer.battery.status.max_discharge_power_W = 0;
datalayer.battery.status.max_charge_power_W = 0;
}
if (LB_Failsafe_Status > 0) // 0 is normal, start charging/discharging
@ -342,9 +346,10 @@ void update_values_battery() { /* This function maps all the values fetched via
}
}
if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
system_max_charge_power_W = 0;
system_max_discharge_power_W = 0;
if (datalayer.battery.status.bms_status ==
FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
datalayer.battery.status.max_charge_power_W = 0;
datalayer.battery.status.max_discharge_power_W = 0;
}
/*Finally print out values to serial if configured to do so*/
@ -380,9 +385,9 @@ void receive_can_battery(CAN_frame_t rx_frame) {
LB_Failsafe_Status = (rx_frame.data.u8[1] & 0x07);
LB_MainRelayOn_flag = (bool)((rx_frame.data.u8[3] & 0x20) >> 5);
if (LB_MainRelayOn_flag) {
batteryAllowsContactorClosing = true;
datalayer.system.status.battery_allows_contactor_closing = true;
} else {
batteryAllowsContactorClosing = false;
datalayer.system.status.battery_allows_contactor_closing = false;
}
LB_Full_CHARGE_flag = (bool)((rx_frame.data.u8[3] & 0x10) >> 4);
LB_Interlock = (bool)((rx_frame.data.u8[3] & 0x08) >> 3);
@ -518,8 +523,8 @@ void receive_can_battery(CAN_frame_t rx_frame) {
cell_deviation_mV = (min_max_voltage[1] - min_max_voltage[0]);
system_cell_max_voltage_mV = min_max_voltage[1];
system_cell_min_voltage_mV = min_max_voltage[0];
datalayer.battery.status.cell_max_voltage_mV = min_max_voltage[1];
datalayer.battery.status.cell_min_voltage_mV = min_max_voltage[0];
if (cell_deviation_mV > MAX_CELL_DEVIATION) {
set_event(EVENT_CELL_DEVIATION_HIGH, 0);
@ -841,9 +846,10 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Nissan LEAF battery selected");
#endif
system_number_of_cells = 96;
system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 2600; // 260.0V under this, discharging further is disabled
datalayer.battery.info.number_of_cells = 96;
datalayer.battery.info.max_design_voltage_dV =
4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 2600; // 260.0V under this, discharging further is disabled
}
#endif

View file

@ -7,28 +7,6 @@
#define BATTERY_SELECTED
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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
uint16_t Temp_fromRAW_to_F(uint16_t temperature);
bool is_message_corrupt(CAN_frame_t rx_frame);
void setup_battery(void);

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef RENAULT_KANGOO_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -54,55 +55,55 @@ static unsigned long GVL_pause = 0;
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
system_real_SOC_pptt = (LB_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
datalayer.battery.status.real_soc = (LB_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
system_SOH_pptt = (LB_SOH * 100); //Increase range from 99% -> 99.00%
datalayer.battery.status.soh_pptt = (LB_SOH * 100); //Increase range from 99% -> 99.00%
system_battery_voltage_dV = LB_Battery_Voltage;
datalayer.battery.status.voltage_dV = LB_Battery_Voltage;
system_battery_current_dA = LB_Current;
datalayer.battery.status.current_dA = LB_Current;
system_capacity_Wh = BATTERY_WH_MAX; //Hardcoded to header value
system_remaining_capacity_Wh = (uint16_t)((system_real_SOC_pptt / 10000) * system_capacity_Wh);
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
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 */
if (LB_Discharge_Power_Limit_Watts > 60000) //if >60kW can be pulled from battery
{
system_max_discharge_power_W = 60000; //cap value so we don't go over the uint16 limit
datalayer.battery.status.max_discharge_power_W = 60000; //cap value so we don't go over the uint16 limit
} else {
system_max_discharge_power_W = LB_Discharge_Power_Limit_Watts;
datalayer.battery.status.max_discharge_power_W = LB_Discharge_Power_Limit_Watts;
}
if (system_scaled_SOC_pptt == 0) //Scaled SOC% value is 0.00%, we should not discharge battery further
if (datalayer.battery.status.reported_soc == 0) //Scaled SOC% value is 0.00%, we should not discharge battery further
{
system_max_discharge_power_W = 0;
datalayer.battery.status.max_discharge_power_W = 0;
}
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 */
if (LB_Charge_Power_Limit_Watts > 60000) //if >60kW can be put into the battery
{
system_max_charge_power_W = 60000; //cap value so we don't go over the uint16 limit
datalayer.battery.status.max_charge_power_W = 60000; //cap value so we don't go over the uint16 limit
} else {
system_max_charge_power_W = LB_Charge_Power_Limit_Watts;
datalayer.battery.status.max_charge_power_W = LB_Charge_Power_Limit_Watts;
}
if (system_scaled_SOC_pptt == 10000) //Scaled SOC% value is 100.00%
if (datalayer.battery.status.reported_soc == 10000) //Scaled SOC% value is 100.00%
{
system_max_charge_power_W = 0; //No need to charge further, set max power to 0
datalayer.battery.status.max_charge_power_W = 0; //No need to charge further, set max power to 0
}
system_active_power_W = (system_battery_voltage_dV * LB_Current); //TODO: check if scaling is OK
datalayer.battery.status.active_power_W =
(datalayer.battery.status.voltage_dV * LB_Current); //TODO: check if scaling is OK
system_temperature_min_dC = (LB_MIN_TEMPERATURE * 10);
datalayer.battery.status.temperature_min_dC = (LB_MIN_TEMPERATURE * 10);
system_temperature_max_dC = (LB_MAX_TEMPERATURE * 10);
datalayer.battery.status.temperature_max_dC = (LB_MAX_TEMPERATURE * 10);
system_cell_min_voltage_mV = LB_Cell_Min_Voltage;
datalayer.battery.status.cell_min_voltage_mV = LB_Cell_Min_Voltage;
system_cell_max_voltage_mV = LB_Cell_Max_Voltage;
datalayer.battery.status.cell_max_voltage_mV = LB_Cell_Max_Voltage;
cell_deviation_mV = (system_temperature_max_dC - system_temperature_min_dC);
cell_deviation_mV = (datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC);
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) {
@ -127,21 +128,21 @@ void update_values_battery() { //This function maps all the values fetched via
#ifdef DEBUG_VIA_USB
Serial.println("Values going to inverter:");
Serial.print("SOH%: ");
Serial.print(system_SOH_pptt);
Serial.print(datalayer.battery.status.soh_pptt);
Serial.print(", SOC% scaled: ");
Serial.print(system_scaled_SOC_pptt);
Serial.print(datalayer.battery.status.reported_soc);
Serial.print(", Voltage: ");
Serial.print(system_battery_voltage_dV);
Serial.print(datalayer.battery.status.voltage_dV);
Serial.print(", Max discharge power: ");
Serial.print(system_max_discharge_power_W);
Serial.print(datalayer.battery.status.max_discharge_power_W);
Serial.print(", Max charge power: ");
Serial.print(system_max_charge_power_W);
Serial.print(datalayer.battery.status.max_charge_power_W);
Serial.print(", Max temp: ");
Serial.print(system_temperature_max_dC);
Serial.print(datalayer.battery.status.temperature_max_dC);
Serial.print(", Min temp: ");
Serial.print(system_temperature_min_dC);
Serial.print(datalayer.battery.status.temperature_min_dC);
Serial.print(", BMS Status (3=OK): ");
Serial.print(system_bms_status);
Serial.print(datalayer.battery.status.bms_status);
Serial.println("Battery values: ");
Serial.print("Real SOC: ");
@ -248,8 +249,9 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Renault Kangoo battery selected");
#endif
system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
datalayer.battery.info.max_design_voltage_dV =
4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
}
#endif

View file

@ -12,29 +12,6 @@
3000 // Min Cell Voltage mV! if voltage goes under this, discharging further is disabled
#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
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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 setup_battery(void);
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef RENAULT_ZOE_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -38,34 +39,33 @@ static unsigned long previousMillis1000 = 0; // will store last time a 1000ms C
static unsigned long GVL_pause = 0;
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
system_SOH_pptt = (LB_SOH * 100); //Increase range from 99% -> 99.00%
datalayer.battery.status.soh_pptt = (LB_SOH * 100); //Increase range from 99% -> 99.00%
system_real_SOC_pptt = (LB_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
datalayer.battery.status.real_soc = (LB_SOC * 10); //increase LB_SOC range from 0-100.0 -> 100.00
system_battery_voltage_dV = LB_Battery_Voltage;
datalayer.battery.status.voltage_dV = LB_Battery_Voltage;
system_battery_current_dA = LB_Current;
system_capacity_Wh = BATTERY_WH_MAX; //Use the configured value to avoid overflows
datalayer.battery.status.current_dA = LB_Current;
//Calculate the remaining Wh amount from SOC% and max Wh value.
system_remaining_capacity_Wh = static_cast<int>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
system_max_discharge_power_W;
datalayer.battery.status.max_discharge_power_W;
system_max_charge_power_W;
datalayer.battery.status.max_charge_power_W;
system_active_power_W;
datalayer.battery.status.active_power_W;
system_temperature_min_dC;
datalayer.battery.status.temperature_min_dC;
system_temperature_max_dC;
datalayer.battery.status.temperature_max_dC;
system_cell_min_voltage_mV;
datalayer.battery.status.cell_min_voltage_mV;
system_cell_max_voltage_mV;
datalayer.battery.status.cell_max_voltage_mV;
cell_deviation_mV = (system_cell_max_voltage_mV - system_cell_min_voltage_mV);
cell_deviation_mV = (datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV);
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) {
@ -90,21 +90,21 @@ void update_values_battery() { //This function maps all the values fetched via
#ifdef DEBUG_VIA_USB
Serial.println("Values going to inverter:");
Serial.print("SOH%: ");
Serial.print(system_SOH_pptt);
Serial.print(datalayer.battery.status.soh_pptt);
Serial.print(", SOC% scaled: ");
Serial.print(system_scaled_SOC_pptt);
Serial.print(datalayer.battery.status.reported_soc);
Serial.print(", Voltage: ");
Serial.print(system_battery_voltage_dV);
Serial.print(datalayer.battery.status.voltage_dV);
Serial.print(", Max discharge power: ");
Serial.print(system_max_discharge_power_W);
Serial.print(datalayer.battery.status.max_discharge_power_W);
Serial.print(", Max charge power: ");
Serial.print(system_max_charge_power_W);
Serial.print(datalayer.battery.status.max_charge_power_W);
Serial.print(", Max temp: ");
Serial.print(system_temperature_max_dC);
Serial.print(datalayer.battery.status.temperature_max_dC);
Serial.print(", Min temp: ");
Serial.print(system_temperature_min_dC);
Serial.print(datalayer.battery.status.temperature_min_dC);
Serial.print(", BMS Status (3=OK): ");
Serial.print(system_bms_status);
Serial.print(datalayer.battery.status.bms_status);
Serial.println("Battery values: ");
Serial.print("Real SOC: ");
@ -158,8 +158,9 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Renault Zoe battery selected");
#endif
system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
datalayer.battery.info.max_design_voltage_dV =
4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
}
#endif

View file

@ -1,6 +1,5 @@
#ifndef RENAULT_ZOE_BATTERY_H
#define RENAULT_ZOE_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -12,29 +11,6 @@
3000 // Min Cell Voltage mV! if voltage goes under this, discharging further is disabled
#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
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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
void setup_battery(void);
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef SANTA_FE_PHEV_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -56,25 +57,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
system_real_SOC_pptt;
datalayer.battery.status.real_soc;
system_battery_voltage_dV;
datalayer.battery.status.voltage_dV;
system_battery_current_dA;
datalayer.battery.status.current_dA;
system_capacity_Wh = BATTERY_WH_MAX;
datalayer.battery.info.total_capacity_Wh;
system_remaining_capacity_Wh;
datalayer.battery.status.remaining_capacity_Wh;
system_max_discharge_power_W;
datalayer.battery.status.max_discharge_power_W;
system_max_charge_power_W;
datalayer.battery.status.max_charge_power_W;
system_active_power_W;
datalayer.battery.status.active_power_W;
system_temperature_min_dC;
datalayer.battery.status.temperature_min_dC;
system_temperature_max_dC;
datalayer.battery.status.temperature_max_dC;
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
if (!CANstillAlive) {
@ -181,8 +182,9 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Hyundai Santa Fe PHEV battery selected");
#endif
system_max_design_voltage_dV = 4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
datalayer.battery.info.max_design_voltage_dV =
4040; // 404.0V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 3100; // 310.0V under this, discharging further is disabled
}
#endif

View file

@ -6,28 +6,6 @@
#define BATTERY_SELECTED
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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);
void setup_battery(void);

View file

@ -1,6 +1,6 @@
#include "../include.h"
#ifdef SERIAL_LINK_RECEIVER
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SERIAL-LINK-RECEIVER-FROM-BATTERY.h"
@ -28,25 +28,30 @@ SerialDataLink dataLinkReceive(SerialReceiver, 0, 0x01, sendingNumVariables,
static bool batteryFault = false; // used locally - mainly to indicate Battery CAN failure
void __getData() {
system_real_SOC_pptt = (uint16_t)dataLinkReceive.getReceivedData(0);
system_SOH_pptt = (uint16_t)dataLinkReceive.getReceivedData(1);
system_battery_voltage_dV = (uint16_t)dataLinkReceive.getReceivedData(2);
system_battery_current_dA = (int16_t)dataLinkReceive.getReceivedData(3);
system_capacity_Wh = (uint32_t)(dataLinkReceive.getReceivedData(4) * 10); //add back missing decimal
system_remaining_capacity_Wh = (uint32_t)(dataLinkReceive.getReceivedData(5) * 10); //add back missing decimal
system_max_discharge_power_W = (uint32_t)(dataLinkReceive.getReceivedData(6) * 10); //add back missing decimal
system_max_charge_power_W = (uint32_t)(dataLinkReceive.getReceivedData(7) * 10); //add back missing decimal
uint16_t _system_bms_status = (uint16_t)dataLinkReceive.getReceivedData(8);
system_active_power_W = (uint32_t)(dataLinkReceive.getReceivedData(9) * 10); //add back missing decimal
system_temperature_min_dC = (int16_t)dataLinkReceive.getReceivedData(10);
system_temperature_max_dC = (int16_t)dataLinkReceive.getReceivedData(11);
system_cell_max_voltage_mV = (uint16_t)dataLinkReceive.getReceivedData(12);
system_cell_min_voltage_mV = (uint16_t)dataLinkReceive.getReceivedData(13);
system_LFP_Chemistry = (bool)dataLinkReceive.getReceivedData(14);
batteryAllowsContactorClosing = (bool)dataLinkReceive.getReceivedData(15);
datalayer.battery.status.real_soc = (uint16_t)dataLinkReceive.getReceivedData(0);
datalayer.battery.status.soh_pptt = (uint16_t)dataLinkReceive.getReceivedData(1);
datalayer.battery.status.voltage_dV = (uint16_t)dataLinkReceive.getReceivedData(2);
datalayer.battery.status.current_dA = (int16_t)dataLinkReceive.getReceivedData(3);
datalayer.battery.info.total_capacity_Wh =
(uint32_t)(dataLinkReceive.getReceivedData(4) * 10); //add back missing decimal
datalayer.battery.status.remaining_capacity_Wh =
(uint32_t)(dataLinkReceive.getReceivedData(5) * 10); //add back missing decimal
datalayer.battery.status.max_discharge_power_W =
(uint32_t)(dataLinkReceive.getReceivedData(6) * 10); //add back missing decimal
datalayer.battery.status.max_charge_power_W =
(uint32_t)(dataLinkReceive.getReceivedData(7) * 10); //add back missing decimal
uint16_t _datalayer.battery.status.bms_status = (uint16_t)dataLinkReceive.getReceivedData(8);
datalayer.battery.status.active_power_W =
(uint32_t)(dataLinkReceive.getReceivedData(9) * 10); //add back missing decimal
datalayer.battery.status.temperature_min_dC = (int16_t)dataLinkReceive.getReceivedData(10);
datalayer.battery.status.temperature_max_dC = (int16_t)dataLinkReceive.getReceivedData(11);
datalayer.battery.status.cell_max_voltage_mV = (uint16_t)dataLinkReceive.getReceivedData(12);
datalayer.battery.status.cell_min_voltage_mV = (uint16_t)dataLinkReceive.getReceivedData(13);
datalayer.battery.info.chemistry = (battery_chemistry_enum)dataLinkReceive.getReceivedData(14);
datalayer.system.status.battery_allows_contactor_closing = (bool)dataLinkReceive.getReceivedData(15);
batteryFault = false;
if (_system_bms_status == FAULT) {
if (_datalayer.battery.status.bms_status == FAULT) {
batteryFault = true;
set_event(EVENT_SERIAL_TRANSMITTER_FAILURE, 0);
}
@ -54,7 +59,7 @@ void __getData() {
void updateData() {
// --- update with fresh data
dataLinkReceive.updateData(0, inverterAllowsContactorClosing);
dataLinkReceive.updateData(0, datalayer.system.status.inverter_allows_contactor_closing);
//dataLinkReceive.updateData(1,var2); // For future expansion,
//dataLinkReceive.updateData(2,var3); // if inverter needs to send data to battery
}
@ -99,8 +104,8 @@ void manageSerialLinkReceiver() {
{
__getData();
reads++;
lastGoodMaxCharge = system_max_charge_power_W;
lastGoodMaxDischarge = system_max_discharge_power_W;
lastGoodMaxCharge = datalayer.battery.status.max_charge_power_W;
lastGoodMaxDischarge = datalayer.battery.status.max_discharge_power_W;
//--- if BatteryFault then assume Data is stale
if (!batteryFault)
lastGood = currentTime;
@ -116,13 +121,13 @@ void manageSerialLinkReceiver() {
if (minutesLost > 0 && lastGood > 0) {
// lose 25% each minute of data loss
if (minutesLost < 4) {
system_max_charge_power_W = (lastGoodMaxCharge * (4 - minutesLost)) / 4;
system_max_discharge_power_W = (lastGoodMaxDischarge * (4 - minutesLost)) / 4;
datalayer.battery.status.max_charge_power_W = (lastGoodMaxCharge * (4 - minutesLost)) / 4;
datalayer.battery.status.max_discharge_power_W = (lastGoodMaxDischarge * (4 - minutesLost)) / 4;
set_event(EVENT_SERIAL_RX_WARNING, minutesLost);
} else {
// Times Up -
system_max_charge_power_W = 0;
system_max_discharge_power_W = 0;
datalayer.battery.status.max_charge_power_W = 0;
datalayer.battery.status.max_discharge_power_W = 0;
set_event(EVENT_SERIAL_RX_FAILURE, uint8_t(min(minutesLost, 255uL)));
//----- Throw Error
}
@ -137,9 +142,9 @@ void manageSerialLinkReceiver() {
}
Serial.print(minutesLost);
Serial.print(", max Charge = ");
Serial.print(system_max_charge_power_W);
Serial.print(datalayer.battery.status.max_charge_power_W);
Serial.print(", max Discharge = ");
Serial.println(system_max_discharge_power_W);
Serial.println(datalayer.battery.status.max_discharge_power_W);
}
}
@ -176,39 +181,39 @@ void manageSerialLinkReceiver() {
void update_values_serial_link() {
Serial.println("Values from battery: ");
Serial.print("SOC: ");
Serial.print(system_real_SOC_pptt);
Serial.print(datalayer.battery.status.real_soc);
Serial.print(" SOH: ");
Serial.print(system_SOH_pptt);
Serial.print(datalayer.battery.status.soh_pptt);
Serial.print(" Voltage: ");
Serial.print(system_battery_voltage_dV);
Serial.print(datalayer.battery.status.voltage_dV);
Serial.print(" Current: ");
Serial.print(system_battery_current_dA);
Serial.print(datalayer.battery.status.current_dA);
Serial.print(" Capacity: ");
Serial.print(system_capacity_Wh);
Serial.print(datalayer.battery.info.total_capacity_Wh);
Serial.print(" Remain cap: ");
Serial.print(system_remaining_capacity_Wh);
Serial.print(datalayer.battery.status.remaining_capacity_Wh);
Serial.print(" Max discharge W: ");
Serial.print(system_max_discharge_power_W);
Serial.print(datalayer.battery.status.max_discharge_power_W);
Serial.print(" Max charge W: ");
Serial.print(system_max_charge_power_W);
Serial.print(datalayer.battery.status.max_charge_power_W);
Serial.print(" BMS status: ");
Serial.print(system_bms_status);
Serial.print(datalayer.battery.status.bms_status);
Serial.print(" Power: ");
Serial.print(system_active_power_W);
Serial.print(datalayer.battery.status.active_power_W);
Serial.print(" Temp min: ");
Serial.print(system_temperature_min_dC);
Serial.print(datalayer.battery.status.temperature_min_dC);
Serial.print(" Temp max: ");
Serial.print(system_temperature_max_dC);
Serial.print(datalayer.battery.status.temperature_max_dC);
Serial.print(" Cell max: ");
Serial.print(system_cell_max_voltage_mV);
Serial.print(datalayer.battery.status.cell_max_voltage_mV);
Serial.print(" Cell min: ");
Serial.print(system_cell_min_voltage_mV);
Serial.print(datalayer.battery.status.cell_min_voltage_mV);
Serial.print(" LFP : ");
Serial.print(system_LFP_Chemistry);
Serial.print(" batteryAllowsContactorClosing: ");
Serial.print(batteryAllowsContactorClosing);
Serial.print(" inverterAllowsContactorClosing: ");
Serial.print(inverterAllowsContactorClosing);
Serial.print(datalayer.battery.info.chemistry);
Serial.print(" Battery Allows Contactor Closing: ");
Serial.print(datalayer.system.status.battery_allows_contactor_closing);
Serial.print(" Inverter Allows Contactor Closing: ");
Serial.print(datalayer.system.status.inverter_allows_contactor_closing);
Serial.println("");
}

View file

@ -5,35 +5,9 @@
#define BATTERY_SELECTED
#include <Arduino.h>
#include "../include.h"
#include "../lib/mackelec-SerialDataLink/SerialDataLink.h"
// https://github.com/mackelec/SerialDataLink
// These parameters need to be mapped on the battery side
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
extern uint8_t system_bms_status; //Enum 0-5
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
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 bool system_LFP_Chemistry; //Set to true or false depending on cell chemistry
extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false
// Parameters to send to the transmitter
extern bool inverterAllowsContactorClosing; //Bool, 1=true, 0=false
void manageSerialLinkReceiver();
void update_values_serial_link();
void setup_battery(void);

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef TESLA_MODEL_3_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -165,70 +166,69 @@ 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
//Calculate the SOH% to send to inverter
if (bat_beginning_of_life != 0) { //div/0 safeguard
system_SOH_pptt =
datalayer.battery.status.soh_pptt =
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 (system_SOH_pptt > 10000) {
system_SOH_pptt = 10000;
if (datalayer.battery.status.soh_pptt > 10000) {
datalayer.battery.status.soh_pptt = 10000;
}
//If the value is unavailable, set SOH to 99%
if (nominal_full_pack_energy < REASONABLE_ENERGYAMOUNT) {
system_SOH_pptt = 9900;
datalayer.battery.status.soh_pptt = 9900;
}
system_real_SOC_pptt = (soc_vi * 10); //increase SOC range from 0-100.0 -> 100.00
datalayer.battery.status.real_soc = (soc_vi * 10); //increase SOC range from 0-100.0 -> 100.00
system_battery_voltage_dV = (volts * 10); //One more decimal needed (370 -> 3700)
datalayer.battery.status.voltage_dV = (volts * 10); //One more decimal needed (370 -> 3700)
system_battery_current_dA = amps; //13.0A
system_capacity_Wh = BATTERY_WH_MAX; //Use the configured value to avoid overflows
datalayer.battery.status.current_dA = amps; //13.0A
//Calculate the remaining Wh amount from SOC% and max Wh value.
system_remaining_capacity_Wh =
static_cast<uint32_t>((static_cast<double>(system_real_SOC_pptt) / 10000) * BATTERY_WH_MAX);
datalayer.battery.status.remaining_capacity_Wh = static_cast<uint32_t>(
(static_cast<double>(datalayer.battery.status.real_soc) / 10000) * datalayer.battery.info.total_capacity_Wh);
// Define the allowed discharge power
system_max_discharge_power_W = (max_discharge_current * volts);
datalayer.battery.status.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
if (system_scaled_SOC_pptt == 0) {
system_max_discharge_power_W = 0;
} else if (system_max_discharge_power_W > MAXDISCHARGEPOWERALLOWED) {
system_max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
if (datalayer.battery.status.reported_soc == 0) {
datalayer.battery.status.max_discharge_power_W = 0;
} else if (datalayer.battery.status.max_discharge_power_W > MAXDISCHARGEPOWERALLOWED) {
datalayer.battery.status.max_discharge_power_W = MAXDISCHARGEPOWERALLOWED;
}
//The allowed charge power behaves strangely. We instead estimate this value
if (system_scaled_SOC_pptt == 10000) { // When scaled SOC is 100.00%, set allowed charge power to 0
system_max_charge_power_W = 0;
if (datalayer.battery.status.reported_soc == 10000) { // When scaled SOC is 100.00%, set allowed charge power to 0
datalayer.battery.status.max_charge_power_W = 0;
} else if (soc_vi > 990) {
system_max_charge_power_W = FLOAT_MAX_POWER_W;
datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W;
} else if (soc_vi > RAMPDOWN_SOC) { // When real SOC is between RAMPDOWN_SOC-99%, ramp the value between Max<->0
system_max_charge_power_W = MAXCHARGEPOWERALLOWED * (1 - (soc_vi - RAMPDOWN_SOC) / (1000.0 - RAMPDOWN_SOC));
datalayer.battery.status.max_charge_power_W =
MAXCHARGEPOWERALLOWED * (1 - (soc_vi - RAMPDOWN_SOC) / (1000.0 - RAMPDOWN_SOC));
//If the cellvoltages start to reach overvoltage, only allow a small amount of power in
if (system_LFP_Chemistry) {
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
if (cell_max_v > (MAX_CELL_VOLTAGE_LFP - FLOAT_START_MV)) {
system_max_charge_power_W = FLOAT_MAX_POWER_W;
datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W;
}
} else { //NCM/A
if (cell_max_v > (MAX_CELL_VOLTAGE_NCA_NCM - FLOAT_START_MV)) {
system_max_charge_power_W = FLOAT_MAX_POWER_W;
datalayer.battery.status.max_charge_power_W = FLOAT_MAX_POWER_W;
}
}
} else { // No limits, max charging power allowed
system_max_charge_power_W = MAXCHARGEPOWERALLOWED;
datalayer.battery.status.max_charge_power_W = MAXCHARGEPOWERALLOWED;
}
power = ((volts / 10) * amps);
system_active_power_W = power;
datalayer.battery.status.active_power_W = power;
system_temperature_min_dC = min_temp;
datalayer.battery.status.temperature_min_dC = min_temp;
system_temperature_max_dC = max_temp;
datalayer.battery.status.temperature_max_dC = max_temp;
system_cell_max_voltage_mV = cell_max_v;
datalayer.battery.status.cell_max_voltage_mV = cell_max_v;
system_cell_min_voltage_mV = cell_min_v;
datalayer.battery.status.cell_min_voltage_mV = cell_min_v;
/* Value mapping is completed. Start to check all safeties */
@ -250,24 +250,25 @@ void update_values_battery() { //This function maps all the values fetched via
// NCM/A batteries have 96s, LFP has 102-106s
// Drawback with this check is that it takes 3-5minutes before all cells have been counted!
if (system_number_of_cells > 101) {
system_LFP_Chemistry = true;
if (datalayer.battery.info.number_of_cells > 101) {
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
}
//Once cell chemistry is determined, set maximum and minimum total pack voltage safety limits
if (system_LFP_Chemistry) {
system_max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
system_min_design_voltage_dV = MIN_PACK_VOLTAGE_LFP;
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_LFP;
} else { // NCM/A chemistry
system_max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
system_min_design_voltage_dV = MIN_PACK_VOLTAGE_NCMA;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_NCMA;
}
//Check if SOC% is plausible
if (system_battery_voltage_dV >
(system_max_design_voltage_dV - 20)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
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);
if (datalayer.battery.status.voltage_dV >
(datalayer.battery.info.max_design_voltage_dV -
20)) { // When pack voltage is close to max, and SOC% is still low, raise FAULT
if (datalayer.battery.status.real_soc < 5000) { //When SOC is less than 50.00% when approaching max voltage
set_event(EVENT_SOC_PLAUSIBILITY_ERROR, datalayer.battery.status.real_soc / 100);
}
}
@ -285,7 +286,7 @@ void update_values_battery() { //This function maps all the values fetched via
set_event(EVENT_KWH_PLAUSIBILITY_ERROR, nominal_full_pack_energy);
}
if (system_LFP_Chemistry) { //LFP limits used for voltage safeties
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) { //LFP limits used for voltage safeties
if (cell_max_v >= MAX_CELL_VOLTAGE_LFP) {
set_event(EVENT_CELL_OVER_VOLTAGE, (cell_max_v - MAX_CELL_VOLTAGE_LFP));
}
@ -311,9 +312,10 @@ void update_values_battery() { //This function maps all the values fetched via
}
}
if (system_bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
system_max_charge_power_W = 0;
system_max_discharge_power_W = 0;
if (datalayer.battery.status.bms_status ==
FAULT) { //Incase we enter a critical fault state, zero out the allowed limits
datalayer.battery.status.max_charge_power_W = 0;
datalayer.battery.status.max_discharge_power_W = 0;
}
/* Safeties verified. Perform USB serial printout if configured to do so */
@ -347,7 +349,7 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.print("YES, ");
else
Serial.print("NO, ");
if (system_LFP_Chemistry) {
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
Serial.print("LFP chemistry detected!");
}
Serial.println("");
@ -371,14 +373,14 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.println("");
Serial.println("Values passed to the inverter: ");
print_SOC(" SOC: ", system_scaled_SOC_pptt);
print_int_with_units(" Max discharge power: ", system_max_discharge_power_W, "W");
print_SOC(" SOC: ", datalayer.battery.status.reported_soc);
print_int_with_units(" Max discharge power: ", datalayer.battery.status.max_discharge_power_W, "W");
Serial.print(", ");
print_int_with_units(" Max charge power: ", system_max_charge_power_W, "W");
print_int_with_units(" Max charge power: ", datalayer.battery.status.max_charge_power_W, "W");
Serial.println("");
print_int_with_units(" Max temperature: ", ((int16_t)system_temperature_min_dC * 0.1), "°C");
print_int_with_units(" Max temperature: ", ((int16_t)datalayer.battery.status.temperature_min_dC * 0.1), "°C");
Serial.print(", ");
print_int_with_units(" Min temperature: ", ((int16_t)system_temperature_max_dC * 0.1), "°C");
print_int_with_units(" Min temperature: ", ((int16_t)datalayer.battery.status.temperature_max_dC * 0.1), "°C");
Serial.println("");
#endif
}
@ -474,11 +476,11 @@ void receive_can_battery(CAN_frame_t rx_frame) {
{
// Example, frame3=0x89,frame2=0x1D = 35101 / 10 = 3510mV
volts = ((rx_frame.data.u8[3] << 8) | rx_frame.data.u8[2]) / 10;
system_cellvoltages_mV[mux * 3] = volts;
datalayer.battery.status.cell_voltages_mV[mux * 3] = volts;
volts = ((rx_frame.data.u8[5] << 8) | rx_frame.data.u8[4]) / 10;
system_cellvoltages_mV[1 + mux * 3] = volts;
datalayer.battery.status.cell_voltages_mV[1 + mux * 3] = volts;
volts = ((rx_frame.data.u8[7] << 8) | rx_frame.data.u8[6]) / 10;
system_cellvoltages_mV[2 + mux * 3] = volts;
datalayer.battery.status.cell_voltages_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
// cell voltages. Then, 2 + mux_max * 3 + 1 is the number of cell voltages.
@ -487,7 +489,7 @@ void receive_can_battery(CAN_frame_t rx_frame) {
mux_zero_counter++;
if (mux_zero_counter == 2u) {
// The max index will be 2 + mux_max * 3 (see above), so "+ 1" for the number of cells
system_number_of_cells = 2 + 3 * mux_max + 1;
datalayer.battery.info.number_of_cells = 2 + 3 * mux_max + 1;
// Increase the counter arbitrarily another time to make the initial if-statement evaluate to false
mux_zero_counter++;
}
@ -590,15 +592,15 @@ the first, for a few cycles, then stop all messages which causes the contactor
}
previousMillis30 = currentMillis;
if (inverterAllowsContactorClosing == 1) {
if (system_bms_status == ACTIVE) {
if (datalayer.system.status.inverter_allows_contactor_closing) {
if (datalayer.battery.status.bms_status == ACTIVE) {
send221still = 50;
batteryAllowsContactorClosing = true;
datalayer.system.status.battery_allows_contactor_closing = true;
ESP32Can.CANWriteFrame(&TESLA_221_1);
ESP32Can.CANWriteFrame(&TESLA_221_2);
} else { //system_bms_status == FAULT or inverter requested opening contactors
} else { //datalayer.battery.status.bms_status == FAULT or inverter requested opening contactors
if (send221still > 0) {
batteryAllowsContactorClosing = false;
datalayer.system.status.battery_allows_contactor_closing = false;
ESP32Can.CANWriteFrame(&TESLA_221_1);
send221still--;
}
@ -632,9 +634,10 @@ void printFaultCodesIfActive() {
if (pyroTestInProgress == 1) {
Serial.println("ERROR: Please wait for Pyro Connection check to finish, HV cables successfully seated!");
}
if (inverterAllowsContactorClosing == 0) {
if (datalayer.system.status.inverter_allows_contactor_closing == false) {
Serial.println(
"ERROR: Solar inverter does not allow for contactor closing. Check inverterAllowsContactorClosing parameter");
"ERROR: Solar inverter does not allow for contactor closing. Check "
"datalayer.system.status.inverter_allows_contactor_closing parameter");
}
// Check each symbol and print debug information if its value is 1
printDebugIfActive(WatchdogReset, "ERROR: The processor has experienced a reset due to watchdog reset");
@ -704,12 +707,12 @@ void setup_battery(void) { // Performs one time setup at startup
#endif
#ifdef LFP_CHEMISTRY
system_LFP_Chemistry = true;
system_max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
system_min_design_voltage_dV = MIN_PACK_VOLTAGE_LFP;
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_LFP;
#else
system_max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
system_min_design_voltage_dV = MIN_PACK_VOLTAGE_NCMA;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_NCMA;
#endif
}

View file

@ -1,6 +1,5 @@
#ifndef TESLA_MODEL_3_BATTERY_H
#define TESLA_MODEL_3_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -19,30 +18,6 @@
#define MAX_PACK_VOLTAGE_LFP 3880 // V+1, if pack voltage goes over this, charge stops
#define MIN_PACK_VOLTAGE_LFP 2968 // V+1, if pack voltage goes below this, discharge stops
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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 inverterAllowsContactorClosing; //Bool, 1=true, 0=false
extern bool system_LFP_Chemistry; //Bool, 1=true, 0=false
void printFaultCodesIfActive();
void printDebugIfActive(uint8_t symbol, const char* message);
void print_int_with_units(char* header, int value, char* units);

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef TEST_FAKE_BATTERY
#include "../datalayer/datalayer.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "TEST-FAKE-BATTERY.h"
@ -15,48 +16,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 */
system_real_SOC_pptt = 5000; // 50.00%
datalayer.battery.status.real_soc = 5000; // 50.00%
system_SOH_pptt = 9900; // 99.00%
datalayer.battery.status.soh_pptt = 9900; // 99.00%
//system_battery_voltage_dV = 3700; // 370.0V , value set in startup in .ino file, editable via webUI
//datalayer.battery.status.voltage_dV = 3700; // 370.0V , value set in startup in .ino file, editable via webUI
system_battery_current_dA = 0; // 0 A
datalayer.battery.status.current_dA = 0; // 0 A
system_capacity_Wh = 30000; // 30kWh
datalayer.battery.info.total_capacity_Wh = 30000; // 30kWh
system_remaining_capacity_Wh = 15000; // 15kWh
datalayer.battery.status.remaining_capacity_Wh = 15000; // 15kWh
system_cell_max_voltage_mV = 3596;
datalayer.battery.status.cell_max_voltage_mV = 3596;
system_cell_min_voltage_mV = 3500;
datalayer.battery.status.cell_min_voltage_mV = 3500;
system_active_power_W = 0; // 0W
datalayer.battery.status.active_power_W = 0; // 0W
system_temperature_min_dC = 50; // 5.0*C
datalayer.battery.status.temperature_min_dC = 50; // 5.0*C
system_temperature_max_dC = 60; // 6.0*C
datalayer.battery.status.temperature_max_dC = 60; // 6.0*C
system_max_discharge_power_W = 5000; // 5kW
datalayer.battery.status.max_discharge_power_W = 5000; // 5kW
system_max_charge_power_W = 5000; // 5kW
datalayer.battery.status.max_charge_power_W = 5000; // 5kW
for (int i = 0; i < 97; ++i) {
system_cellvoltages_mV[i] = 3500 + i;
datalayer.battery.status.cell_voltages_mV[i] = 3500 + i;
}
/*Finally print out values to serial if configured to do so*/
#ifdef DEBUG_VIA_USB
Serial.println("FAKE Values going to inverter");
print_units("SOH%: ", (system_SOH_pptt * 0.01), "% ");
print_units(", SOC%: ", (system_scaled_SOC_pptt * 0.01), "% ");
print_units(", Voltage: ", (system_battery_voltage_dV * 0.1), "V ");
print_units(", Max discharge power: ", system_max_discharge_power_W, "W ");
print_units(", Max charge power: ", system_max_charge_power_W, "W ");
print_units(", Max temp: ", (system_temperature_max_dC * 0.1), "°C ");
print_units(", Min temp: ", (system_temperature_min_dC * 0.1), "°C ");
print_units(", Max cell voltage: ", system_cell_max_voltage_mV, "mV ");
print_units(", Min cell voltage: ", system_cell_min_voltage_mV, "mV ");
print_units("SOH%: ", (datalayer.battery.status.soh_pptt * 0.01), "% ");
print_units(", SOC%: ", (datalayer.battery.status.reported_soc * 0.01), "% ");
print_units(", Voltage: ", (datalayer.battery.status.voltage_dV * 0.1), "V ");
print_units(", Max discharge power: ", datalayer.battery.status.max_discharge_power_W, "W ");
print_units(", Max charge power: ", datalayer.battery.status.max_charge_power_W, "W ");
print_units(", Max temp: ", (datalayer.battery.status.temperature_max_dC * 0.1), "°C ");
print_units(", Min temp: ", (datalayer.battery.status.temperature_min_dC * 0.1), "°C ");
print_units(", Max cell voltage: ", datalayer.battery.status.cell_max_voltage_mV, "mV ");
print_units(", Min cell voltage: ", datalayer.battery.status.cell_min_voltage_mV, "mV ");
Serial.println("");
#endif
}
@ -88,8 +89,9 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Test mode with fake battery selected");
#endif
system_max_design_voltage_dV = 4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 2450; // 245.0V under this, discharging further is disabled
datalayer.battery.info.max_design_voltage_dV =
4040; // 404.4V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 2450; // 245.0V under this, discharging further is disabled
}
#endif

View file

@ -4,29 +4,6 @@
#define BATTERY_SELECTED
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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 setup_battery(void);
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef VOLVO_SPA_BATTERY
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
@ -81,37 +82,36 @@ void update_values_battery() { //This function maps all the values fetched via
remaining_capacity = (78200 - CHARGE_ENERGY);
//system_real_SOC_pptt = SOC_BMS; // Use BMS reported SOC, havent figured out how to get the BMS to calibrate empty/full yet
//datalayer.battery.status.real_soc = SOC_BMS; // Use BMS reported SOC, havent figured out how to get the BMS to calibrate empty/full yet
SOC_CALC = remaining_capacity / 78; // Use calculated SOC based on remaining_capacity
system_real_SOC_pptt = SOC_CALC * 10;
datalayer.battery.status.real_soc = SOC_CALC * 10;
if (BATT_U > MAX_U) // Protect if overcharged
{
system_real_SOC_pptt = 10000;
datalayer.battery.status.real_soc = 10000;
} else if (BATT_U < MIN_U) //Protect if undercharged
{
system_real_SOC_pptt = 0;
datalayer.battery.status.real_soc = 0;
}
system_battery_voltage_dV = BATT_U * 10;
system_battery_current_dA = BATT_I * 10;
system_capacity_Wh = BATTERY_WH_MAX;
system_remaining_capacity_Wh = remaining_capacity; // Will wrap! Known limitation due to uint16_t size.
datalayer.battery.status.voltage_dV = BATT_U * 10;
datalayer.battery.status.current_dA = BATT_I * 10;
datalayer.battery.status.remaining_capacity_Wh = remaining_capacity;
//system_max_discharge_power_W = HvBattPwrLimDchaSoft * 1000; // Use power limit reported from BMS, not trusted ATM
system_max_discharge_power_W = 30000;
system_max_charge_power_W = 30000;
system_active_power_W = (BATT_U)*BATT_I;
system_temperature_min_dC = BATT_T_MIN;
system_temperature_max_dC = BATT_T_MAX;
//datalayer.battery.status.max_discharge_power_W = HvBattPwrLimDchaSoft * 1000; // Use power limit reported from BMS, not trusted ATM
datalayer.battery.status.max_discharge_power_W = 30000;
datalayer.battery.status.max_charge_power_W = 30000;
datalayer.battery.status.active_power_W = (BATT_U)*BATT_I;
datalayer.battery.status.temperature_min_dC = BATT_T_MIN;
datalayer.battery.status.temperature_max_dC = BATT_T_MAX;
system_cell_max_voltage_mV = CELL_U_MAX * 10; // Use min/max reported from BMS
system_cell_min_voltage_mV = CELL_U_MIN * 10;
datalayer.battery.status.cell_max_voltage_mV = CELL_U_MAX * 10; // Use min/max reported from BMS
datalayer.battery.status.cell_min_voltage_mV = CELL_U_MIN * 10;
//Map all cell voltages to the global array
for (int i = 0; i < 108; ++i) {
system_cellvoltages_mV[i] = cell_voltages[i];
datalayer.battery.status.cell_voltages_mV[i] = cell_voltages[i];
}
/* Check if the BMS is still sending CAN messages. If we go 60s without messages we raise an error*/
@ -128,7 +128,7 @@ void update_values_battery() { //This function maps all the values fetched via
Serial.print("Calculated SOC%: ");
Serial.println(SOC_CALC);
Serial.print("Rescaled SOC%: ");
Serial.println(system_scaled_SOC_pptt / 10);
Serial.println(datalayer.battery.status.reported_soc / 100);
Serial.print("Battery current: ");
Serial.println(BATT_I);
Serial.print("Battery voltage: ");
@ -287,7 +287,7 @@ void receive_can_battery(CAN_frame_t rx_frame) {
if ((rx_frame.data.u8[0] == 0x07) && (rx_frame.data.u8[1] == 0x62) && (rx_frame.data.u8[2] == 0x49) &&
(rx_frame.data.u8[3] == 0x6D)) // SOH response frame
{
system_SOH_pptt = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
datalayer.battery.status.soh_pptt = ((rx_frame.data.u8[6] << 8) | rx_frame.data.u8[7]);
} else if ((rx_frame.data.u8[0] == 0x10) && (rx_frame.data.u8[1] == 0x0B) && (rx_frame.data.u8[2] == 0x62) &&
(rx_frame.data.u8[3] == 0x4B)) // First response frame of cell voltages
{
@ -357,15 +357,15 @@ void send_can_battery() {
ESP32Can.CANWriteFrame(&VOLVO_536); //Send 0x536 Network managing frame to keep BMS alive
ESP32Can.CANWriteFrame(&VOLVO_372); //Send 0x372 ECMAmbientTempCalculated
if (system_bms_status == ACTIVE) {
batteryAllowsContactorClosing = true;
} else { //system_bms_status == FAULT or inverter requested opening contactors
batteryAllowsContactorClosing = false;
if (datalayer.battery.status.bms_status == ACTIVE) {
datalayer.system.status.battery_allows_contactor_closing = true;
} else { //datalayer.battery.status.bms_status == FAULT or inverter requested opening contactors
datalayer.system.status.battery_allows_contactor_closing = false;
}
}
if (currentMillis - previousMillis60s >= INTERVAL_60_S) {
previousMillis60s = currentMillis;
if (system_bms_status == ACTIVE) {
if (datalayer.battery.status.bms_status == ACTIVE) {
readCellVoltages();
}
}
@ -376,8 +376,9 @@ void setup_battery(void) { // Performs one time setup at startup
Serial.println("Volvo SPA XC40 Recharge / Polestar2 78kWh battery selected");
#endif
system_number_of_cells = 108;
system_max_design_voltage_dV = 4540; // 454.0V, over this, charging is not possible (goes into forced discharge)
system_min_design_voltage_dV = 2938; // 293.8V under this, discharging further is disabled
datalayer.battery.info.number_of_cells = 108;
datalayer.battery.info.max_design_voltage_dV =
4540; // 454.0V, over this, charging is not possible (goes into forced discharge)
datalayer.battery.info.min_design_voltage_dV = 2938; // 293.8V under this, discharging further is disabled
}
#endif

View file

@ -6,28 +6,6 @@
#define BATTERY_SELECTED
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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
void setup_battery(void);
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef CHEVYVOLT_CHARGER
#include "../datalayer/datalayer.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "CHEVY-VOLT-CHARGER.h"

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef NISSAN_LEAF_CHARGER
#include "../datalayer/datalayer.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "NISSAN-LEAF-CHARGER.h"
@ -227,12 +228,13 @@ void send_can_nissanleaf_charger() {
}
// if actual battery_voltage is less than setpoint got to max power set from web ui
if (system_battery_voltage_dV < (CHARGER_SET_HV * 10)) { //system_battery_voltage_dV = V+1, 0-500.0 (0-5000)
if (datalayer.battery.status.voltage_dV <
(CHARGER_SET_HV * 10)) { //datalayer.battery.status.voltage_dV = V+1, 0-500.0 (0-5000)
OBCpower = OBCpowerSetpoint;
}
// decrement charger power if volt setpoint is reached
if (system_battery_voltage_dV >= (CHARGER_SET_HV * 10)) {
if (datalayer.battery.status.voltage_dV >= (CHARGER_SET_HV * 10)) {
if (OBCpower > 0x64) {
OBCpower--;
}

View file

@ -4,8 +4,6 @@
#include "../include.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
void send_can_nissanleaf_charger();
void receive_can_nissanleaf_charger(CAN_frame_t rx_frame);

View file

@ -1,83 +1,164 @@
#ifndef _DATALAYER_H_
#define _DATALAYER_H_
#include "../devboard/utils/soc_scaling.h"
#include "../include.h"
typedef struct {
/** float */
float max_design_voltage_V;
float min_design_voltage_V;
/** uint32_t */
uint32_t total_capacity;
/** Total energy capacity in Watt-hours */
uint32_t total_capacity_Wh = BATTERY_WH_MAX;
/** uint16_t */
uint16_t number_of_cells;
/** The maximum intended packvoltage, in deciVolt. 4900 = 490.0 V */
uint16_t max_design_voltage_dV = 5000;
/** The minimum intended packvoltage, in deciVolt. 3300 = 330.0 V */
uint16_t min_design_voltage_dV = 2500;
/** BYD CAN specific setting, max charge in deciAmpere. 300 = 30.0 A */
uint16_t max_charge_amp_dA = BATTERY_MAX_CHARGE_AMP;
/** BYD CAN specific setting, max discharge in deciAmpere. 300 = 30.0 A */
uint16_t max_discharge_amp_dA = BATTERY_MAX_DISCHARGE_AMP;
/** uint8_t */
/** Total number of cells in the pack */
uint8_t number_of_cells;
/** Other */
battery_chemistry_enum chemistry;
} DATALAYER_BATTERY_DATA_TYPE;
/** Chemistry of the pack. NCA, NMC or LFP (so far) */
battery_chemistry_enum chemistry = battery_chemistry_enum::NCA;
} DATALAYER_BATTERY_INFO_TYPE;
typedef struct {
/** float */
float temperature_max_C;
float temperature_min_C;
float current_A;
float voltage_V;
float soh_pct;
/** int32_t */
/** Instantaneous battery power in Watts */
int32_t active_power_W;
/** uint32_t */
int32_t active_power_W;
uint32_t remaining_capacity;
/** Remaining energy capacity in Watt-hours */
uint32_t remaining_capacity_Wh;
/** Maximum allowed battery discharge power in Watts */
uint32_t max_discharge_power_W = 0;
/** Maximum allowed battery charge power in Watts */
uint32_t max_charge_power_W = 0;
/** int16_t */
/** Maximum temperature currently measured in the pack, in d°C. 150 = 15.0 °C */
int16_t temperature_max_dC;
/** Minimum temperature currently measured in the pack, in d°C. 150 = 15.0 °C */
int16_t temperature_min_dC;
/** Instantaneous battery current in deciAmpere. 95 = 9.5 A */
int16_t current_dA;
/** uint16_t */
uint32_t max_discharge_power_W;
uint32_t max_charge_power_W;
uint16_t cell_max_voltage_mV;
uint16_t cell_min_voltage_mV;
/** State of health in integer-percent x 100. 9900 = 99.00% */
uint16_t soh_pptt = 9900;
/** Instantaneous battery voltage in deciVolts. 3700 = 370.0 V */
uint16_t voltage_dV = 3700;
/** Maximum cell voltage currently measured in the pack, in mV */
uint16_t cell_max_voltage_mV = 3700;
/** Minimum cell voltage currently measured in the pack, in mV */
uint16_t cell_min_voltage_mV = 3700;
/** All cell voltages currently measured in the pack, in mV.
* Use with battery.info.number_of_cells to get valid data.
*/
uint16_t cell_voltages_mV[MAX_AMOUNT_CELLS];
/** The "real" SOC reported from the battery, in integer-percent x 100. 9550 = 95.50% */
uint16_t real_soc;
/** The SOC reported to the inverter, in integer-percent x 100. 9550 = 95.50%.
* This value will either be scaled or not scaled depending on the value of
* battery.settings.soc_scaling_active
*/
uint16_t reported_soc;
/** Other */
// bms_status_enum bms_status; // Not ready yet
ScaledSoc soc;
/** The current BMS status */
bms_status_enum bms_status = ACTIVE;
} DATALAYER_BATTERY_STATUS_TYPE;
typedef struct {
DATALAYER_BATTERY_DATA_TYPE data;
/** SOC scaling setting. Set to true to use SOC scaling */
bool soc_scaling_active = BATTERY_USE_SCALED_SOC;
/** Minimum percentage setting. Set this value to the lowest real SOC
* you want the inverter to be able to use. At this real SOC, the inverter
* will "see" 0% */
uint16_t min_percentage = BATTERY_MINPERCENTAGE;
/** Maximum percentage setting. Set this value to the highest real SOC
* you want the inverter to be able to use. At this real SOC, the inverter
* will "see" 100% */
uint16_t max_percentage = BATTERY_MAXPERCENTAGE;
} DATALAYER_BATTERY_SETTINGS_TYPE;
typedef struct {
DATALAYER_BATTERY_INFO_TYPE info;
DATALAYER_BATTERY_STATUS_TYPE status;
DATALAYER_BATTERY_SETTINGS_TYPE settings;
} DATALAYER_BATTERY_TYPE;
typedef struct {
// TODO
} DATALAYER_SYSTEM_INFO_TYPE;
typedef struct {
#ifdef FUNCTION_TIME_MEASUREMENT
int64_t main_task_max_us = 0;
int64_t main_task_10s_max_us = 0;
/** Core task measurement variable */
int64_t core_task_max_us = 0;
/** Core task measurement variable, reset each 10 seconds */
int64_t core_task_10s_max_us = 0;
/** MQTT task measurement variable, reset each 10 seconds */
int64_t mqtt_task_10s_max_us = 0;
/** loop() task measurement variable, reset each 10 seconds */
int64_t loop_task_10s_max_us = 0;
/** OTA/Wifi handling function measurement variable */
int64_t time_wifi_us = 0;
int64_t time_mqtt_us = 0;
/** CAN RX or serial link function measurement variable */
int64_t time_comm_us = 0;
/** 10 ms function measurement variable */
int64_t time_10ms_us = 0;
/** 5 s function measurement variable */
int64_t time_5s_us = 0;
/** CAN TX function measurement variable */
int64_t time_cantx_us = 0;
int64_t time_events_us = 0;
/** Function measurement snapshot variable.
* This will show the performance of OTA/Wifi handling when the total time reached a new worst case
*/
int64_t time_snap_wifi_us = 0;
/** Function measurement snapshot variable.
* This will show the performance of CAN RX or serial link when the total time reached a new worst case
*/
int64_t time_snap_comm_us = 0;
/** Function measurement snapshot variable.
* This will show the performance of the 10 ms functionality of the core task when the total time reached a new worst case
*/
int64_t time_snap_10ms_us = 0;
/** Function measurement snapshot variable.
* This will show the performance of the 5 s functionality of the core task when the total time reached a new worst case
*/
int64_t time_snap_5s_us = 0;
/** Function measurement snapshot variable.
* This will show the performance of CAN TX when the total time reached a new worst case
*/
int64_t time_snap_cantx_us = 0;
#endif
/** True if the battery allows for the contactors to close */
bool battery_allows_contactor_closing = false;
/** True if the inverter allows for the contactors to close */
bool inverter_allows_contactor_closing = true;
} DATALAYER_SYSTEM_STATUS_TYPE;
typedef struct {
DATALAYER_SYSTEM_STATUS_TYPE status;
} DATALAYER_SYSTEM_TYPE;
} DATALAYER_SYSTEM_SETTINGS_TYPE;
typedef struct {
bool batteryAllowsContactorClosing = false;
bool inverterAllowsContactorClosing = true;
} DATALAYER_SETTINGS_TYPE;
DATALAYER_SYSTEM_INFO_TYPE info;
DATALAYER_SYSTEM_STATUS_TYPE status;
DATALAYER_SYSTEM_SETTINGS_TYPE settings;
} DATALAYER_SYSTEM_TYPE;
class DataLayer {
public:
DATALAYER_BATTERY_TYPE battery;
DATALAYER_SYSTEM_TYPE system;
DATALAYER_SETTINGS_TYPE settings;
};
extern DataLayer datalayer;

View file

@ -4,6 +4,7 @@
#include <freertos/FreeRTOS.h>
#include "../../../USER_SETTINGS.h"
#include "../../battery/BATTERIES.h"
#include "../../datalayer/datalayer.h"
#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h"
#include "../../lib/knolleary-pubsubclient/PubSubClient.h"
#include "../utils/timer.h"
@ -39,7 +40,7 @@ static void publish_cell_voltages(void) {
static String state_topic = String("battery-emulator_") + String(hostname) + "/spec_data";
// If the cell voltage number isn't initialized...
if (system_number_of_cells == 0u) {
if (datalayer.battery.info.number_of_cells == 0u) {
return;
}
@ -47,7 +48,7 @@ static void publish_cell_voltages(void) {
mqtt_first_transmission = false;
String topic = "homeassistant/sensor/battery-emulator/cell_voltage";
for (int i = 0; i < system_number_of_cells; i++) {
for (int i = 0; i < datalayer.battery.info.number_of_cells; i++) {
int cellNumber = i + 1;
doc["name"] = "Battery Cell Voltage " + String(cellNumber);
doc["object_id"] = "battery_voltage_cell" + String(cellNumber);
@ -74,13 +75,13 @@ static void publish_cell_voltages(void) {
doc.clear(); // clear after sending autoconfig
} else {
// If cell voltages haven't been populated...
if (system_number_of_cells == 0u) {
if (datalayer.battery.info.number_of_cells == 0u) {
return;
}
JsonArray cell_voltages = doc["cell_voltages"].to<JsonArray>();
for (size_t i = 0; i < system_number_of_cells; ++i) {
cell_voltages.add(((float)system_cellvoltages_mV[i]) / 1000.0);
for (size_t i = 0; i < datalayer.battery.info.number_of_cells; ++i) {
cell_voltages.add(((float)datalayer.battery.status.cell_voltages_mV[i]) / 1000.0);
}
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
@ -150,16 +151,16 @@ static void publish_common_info(void) {
}
doc.clear();
} else {
doc["SOC"] = ((float)system_scaled_SOC_pptt) / 100.0;
doc["SOC_real"] = ((float)system_real_SOC_pptt) / 100.0;
doc["state_of_health"] = ((float)system_SOH_pptt) / 100.0;
doc["temperature_min"] = ((float)((int16_t)system_temperature_min_dC)) / 10.0;
doc["temperature_max"] = ((float)((int16_t)system_temperature_max_dC)) / 10.0;
doc["stat_batt_power"] = ((float)((int16_t)system_active_power_W));
doc["battery_current"] = ((float)((int16_t)system_battery_current_dA)) / 10.0;
doc["cell_max_voltage"] = ((float)system_cell_max_voltage_mV) / 1000.0;
doc["cell_min_voltage"] = ((float)system_cell_min_voltage_mV) / 1000.0;
doc["battery_voltage"] = ((float)system_battery_voltage_dV) / 10.0;
doc["SOC"] = ((float)datalayer.battery.status.reported_soc) / 100.0;
doc["SOC_real"] = ((float)datalayer.battery.status.real_soc) / 100.0;
doc["state_of_health"] = ((float)datalayer.battery.status.soh_pptt) / 100.0;
doc["temperature_min"] = ((float)((int16_t)datalayer.battery.status.temperature_min_dC)) / 10.0;
doc["temperature_max"] = ((float)((int16_t)datalayer.battery.status.temperature_max_dC)) / 10.0;
doc["stat_batt_power"] = ((float)((int32_t)datalayer.battery.status.active_power_W));
doc["battery_current"] = ((float)((int16_t)datalayer.battery.status.current_dA)) / 10.0;
doc["cell_max_voltage"] = ((float)datalayer.battery.status.cell_max_voltage_mV) / 1000.0;
doc["cell_min_voltage"] = ((float)datalayer.battery.status.cell_min_voltage_mV) / 1000.0;
doc["battery_voltage"] = ((float)datalayer.battery.status.voltage_dV) / 10.0;
serializeJson(doc, mqtt_msg);
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {

View file

@ -41,19 +41,6 @@
extern const char* version_number; // The current software version, used for mqtt
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
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[MAX_AMOUNT_CELLS]; //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_password;

View file

@ -1,5 +1,5 @@
#include "events.h"
#include "../../datalayer/datalayer.h"
#ifndef UNIT_TEST
#include <EEPROM.h>
#endif
@ -47,6 +47,7 @@ typedef struct {
uint32_t time_seconds;
MyTimer second_timer;
MyTimer ee_timer;
MyTimer update_timer;
EVENTS_LEVEL_TYPE level;
uint16_t event_log_head_index;
uint16_t event_log_tail_index;
@ -76,6 +77,7 @@ void run_event_handling(void) {
update_event_time();
run_sequence_on_target();
check_ee_write();
update_event_level();
}
/* Initialization function */
@ -165,6 +167,7 @@ void init_events(void) {
events.second_timer.set_interval(1000);
// Write to EEPROM every X minutes (if an event has been set)
events.ee_timer.set_interval(EE_WRITE_PERIOD_MINUTES * 60 * 1000);
events.update_timer.set_interval(2000);
}
void set_event(EVENTS_ENUM_TYPE event, uint8_t data) {
@ -307,7 +310,9 @@ static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) {
// Check if the event is latching
events.entries[event].state = latched ? EVENT_STATE_ACTIVE_LATCHED : EVENT_STATE_ACTIVE;
update_event_level();
// Update event level, only upwards. Downward changes are done in Software.ino:loop()
events.level = max(events.level, events.entries[event].level);
update_bms_status();
#ifdef DEBUG_VIA_USB
@ -320,13 +325,13 @@ static void update_bms_status(void) {
case EVENT_LEVEL_INFO:
case EVENT_LEVEL_WARNING:
case EVENT_LEVEL_DEBUG:
system_bms_status = ACTIVE;
datalayer.battery.status.bms_status = ACTIVE;
break;
case EVENT_LEVEL_UPDATE:
system_bms_status = UPDATING;
datalayer.battery.status.bms_status = UPDATING;
break;
case EVENT_LEVEL_ERROR:
system_bms_status = FAULT;
datalayer.battery.status.bms_status = FAULT;
break;
default:
break;
@ -334,12 +339,13 @@ static void update_bms_status(void) {
}
static void update_event_level(void) {
events.level = EVENT_LEVEL_INFO;
EVENTS_LEVEL_TYPE temporary_level = EVENT_LEVEL_INFO;
for (uint8_t i = 0u; i < EVENT_NOF_EVENTS; i++) {
if ((events.entries[i].state == EVENT_STATE_ACTIVE) || (events.entries[i].state == EVENT_STATE_ACTIVE_LATCHED)) {
events.level = max(events.entries[i].level, events.level);
temporary_level = max(events.entries[i].level, temporary_level);
}
}
events.level = temporary_level;
}
static void update_event_time(void) {

View file

@ -111,6 +111,4 @@ void run_event_handling(void);
void run_sequence_on_target(void);
extern uint8_t system_bms_status; //Enum 0-5
#endif // __MYTIMER_H__

View file

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

View file

@ -1,36 +0,0 @@
#include "soc_scaling.h"
float ScaledSoc::set_real_soc(float soc) {
real_soc = soc;
scaled_soc = map_float(real_soc, 0.0f, 100.0f, min_real_soc, max_real_soc);
real_soc_initialized = true;
return scaled_soc;
}
float ScaledSoc::get_scaled_soc(void) {
if (real_soc_initialized) {
return scaled_soc;
} else {
return -1.0f;
}
}
float ScaledSoc::get_real_soc(void) {
if (real_soc_initialized) {
return real_soc;
} else {
return -1.0f;
}
}
float ScaledSoc::get_soc(void) {
if (real_soc_initialized) {
if (soc_scaling_active) {
return scaled_soc;
} else {
return real_soc;
}
} else {
return -1.0f;
}
}

View file

@ -1,29 +0,0 @@
#ifndef __SOC_SCALING_H__
#define __SOC_SCALING_H__
#include "value_mapping.h"
class ScaledSoc {
private:
float min_real_soc, max_real_soc, real_soc, scaled_soc;
bool soc_scaling_active;
bool real_soc_initialized = false;
public:
ScaledSoc() : min_real_soc(20.0f), max_real_soc(80.0f), soc_scaling_active(true) {}
ScaledSoc(float min_soc, float max_soc, bool scaling_active) {
min_real_soc = min_soc;
max_real_soc = max_soc;
soc_scaling_active = scaling_active;
}
// Set the real soc
float set_real_soc(float soc);
// Get scaled soc regardless of setting
float get_scaled_soc(void);
// Get real soc regardless of setting
float get_real_soc(void);
// Get scaled or real soc based on the scaling setting
float get_soc(void);
};
#endif

View file

@ -1,18 +1,10 @@
#ifndef _TYPES_H_
#define _TYPES_H_
// enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAULT = 4, UPDATING = 5 };
enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAULT = 4, UPDATING = 5 };
enum battery_chemistry_enum { NCA, NMC, LFP };
enum led_color { GREEN, YELLOW, RED, BLUE, RGB };
// Inverter definitions
#define STANDBY 0
#define INACTIVE 1
#define DARKSTART 2
#define ACTIVE 3
#define FAULT 4
#define UPDATING 5
#define DISCHARGING 1
#define CHARGING 2

View file

@ -1,5 +1,6 @@
#include "cellmonitor_html.h"
#include <Arduino.h>
#include "../../datalayer/datalayer.h"
String cellmonitor_processor(const String& var) {
if (var == "ABC") {
@ -37,10 +38,10 @@ String cellmonitor_processor(const String& var) {
// Populate cell data
content += "const data = [";
for (uint8_t i = 0u; i < MAX_AMOUNT_CELLS; i++) {
if (system_cellvoltages_mV[i] == 0) {
if (datalayer.battery.status.cell_voltages_mV[i] == 0) {
continue;
}
content += String(system_cellvoltages_mV[i]) + ",";
content += String(datalayer.battery.status.cell_voltages_mV[i]) + ",";
}
content += "];";

View file

@ -3,10 +3,6 @@
#include "../../include.h"
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[MAX_AMOUNT_CELLS]; //Array with all cell voltages in mV
/**
* @brief Replaces placeholder with content section in web page
*

View file

@ -1,5 +1,6 @@
#include "settings_html.h"
#include <Arduino.h>
#include "../../datalayer/datalayer.h"
String settings_processor(const String& var) {
if (var == "ABC") {
@ -13,20 +14,24 @@ String settings_processor(const String& var) {
content += "<div style='background-color: #303E47; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
// 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(datalayer.battery.info.total_capacity_Wh) +
" 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>&#10003;</span>" : "<span style='color: red;'>&#10005;</span>") +
content += "<h4 style='color: white;'>Rescale SOC: <span id='BATTERY_USE_SCALED_SOC'>" +
String(datalayer.battery.settings.soc_scaling_active ? "<span>&#10003;</span>"
: "<span style='color: red;'>&#10005;</span>") +
"</span> <button onclick='editUseScaledSOC()'>Edit</button></h4>";
content += "<h4 style='color: " + String(USE_SCALED_SOC ? "white" : "darkgrey") +
";'>SOC max percentage: " + String(MAXPERCENTAGE / 10.0, 1) +
content += "<h4 style='color: " + String(datalayer.battery.settings.soc_scaling_active ? "white" : "darkgrey") +
";'>SOC max percentage: " + String(datalayer.battery.settings.max_percentage / 100.0, 1) +
" </span> <button onclick='editSocMax()'>Edit</button></h4>";
content += "<h4 style='color: " + String(USE_SCALED_SOC ? "white" : "darkgrey") +
";'>SOC min percentage: " + String(MINPERCENTAGE / 10.0, 1) +
content += "<h4 style='color: " + String(datalayer.battery.settings.soc_scaling_active ? "white" : "darkgrey") +
";'>SOC min percentage: " + String(datalayer.battery.settings.min_percentage / 100.0, 1) +
" </span> <button onclick='editSocMin()'>Edit</button></h4>";
content += "<h4 style='color: white;'>Max charge speed: " + String(MAXCHARGEAMP / 10.0, 1) +
content +=
"<h4 style='color: white;'>Max charge speed: " + String(datalayer.battery.info.max_charge_amp_dA / 10.0, 1) +
" A </span> <button onclick='editMaxChargeA()'>Edit</button></h4>";
content += "<h4 style='color: white;'>Max discharge speed: " + String(MAXDISCHARGEAMP / 10.0, 1) +
content += "<h4 style='color: white;'>Max discharge speed: " +
String(datalayer.battery.info.max_discharge_amp_dA / 10.0, 1) +
" A </span> <button onclick='editMaxDischargeA()'>Edit</button></h4>";
// Close the block
content += "</div>";
@ -34,7 +39,8 @@ String settings_processor(const String& var) {
#ifdef TEST_FAKE_BATTERY
// Start a new block with blue background color
content += "<div style='background-color: #2E37AD; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
float voltageFloat = static_cast<float>(system_battery_voltage_dV) / 10.0; // Convert to float and divide by 10
float voltageFloat =
static_cast<float>(datalayer.battery.status.voltage_dV) / 10.0; // Convert to float and divide by 10
content += "<h4 style='color: white;'>Fake battery voltage: " + String(voltageFloat, 1) +
" V </span> <button onclick='editFakeBatteryVoltage()'>Edit</button></h4>";

View file

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

View file

@ -68,7 +68,7 @@ void init_webserver() {
server.on("/updateBatterySize", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
BATTERY_WH_MAX = value.toInt();
datalayer.battery.info.total_capacity_Wh = value.toInt();
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
@ -80,7 +80,7 @@ void init_webserver() {
server.on("/updateUseScaledSOC", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
USE_SCALED_SOC = value.toInt();
datalayer.battery.settings.soc_scaling_active = value.toInt();
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
@ -92,7 +92,7 @@ void init_webserver() {
server.on("/updateSocMax", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
MAXPERCENTAGE = value.toInt() * 10;
datalayer.battery.settings.max_percentage = value.toInt() * 100;
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
@ -104,7 +104,7 @@ void init_webserver() {
server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
MINPERCENTAGE = value.toInt() * 10;
datalayer.battery.settings.min_percentage = value.toInt() * 100;
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
@ -116,7 +116,7 @@ void init_webserver() {
server.on("/updateMaxChargeA", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
MAXCHARGEAMP = value.toInt() * 10;
datalayer.battery.info.max_charge_amp_dA = value.toInt() * 10;
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
@ -128,7 +128,7 @@ void init_webserver() {
server.on("/updateMaxDischargeA", HTTP_GET, [](AsyncWebServerRequest* request) {
if (request->hasParam("value")) {
String value = request->getParam("value")->value();
MAXDISCHARGEAMP = value.toInt() * 10;
datalayer.battery.info.max_discharge_amp_dA = value.toInt() * 10;
storeSettings();
request->send(200, "text/plain", "Updated successfully");
} else {
@ -146,7 +146,7 @@ void init_webserver() {
String value = request->getParam("value")->value();
float val = value.toFloat();
system_battery_voltage_dV = val * 10;
datalayer.battery.status.voltage_dV = val * 10;
request->send(200, "text/plain", "Updated successfully");
});
@ -184,7 +184,7 @@ void init_webserver() {
String value = request->getParam("value")->value();
float val = value.toFloat();
if (!(val <= MAXCHARGEAMP && val <= CHARGER_MAX_A)) {
if (!(val <= datalayer.battery.info.max_charge_amp_dA && val <= CHARGER_MAX_A)) {
request->send(400, "text/plain", "Bad Request");
}
@ -372,16 +372,17 @@ String processor(const String& var) {
content += "<h4>Software: " + String(version_number) + "</h4>";
#ifdef FUNCTION_TIME_MEASUREMENT
// Load information
content += "<h4>Main task max load: " + String(datalayer.system.status.main_task_max_us) + " us</h4>";
content += "<h4>Main task max load last 10 s: " + String(datalayer.system.status.main_task_10s_max_us) + " us</h4>";
content += "<h4>MQTT task max load last 10 s: " + String(datalayer.system.status.time_mqtt_us) + " us</h4>";
content += "<h4>Max function load last 10 s:</h4>";
content += "<h4>Events function timing: " + String(datalayer.system.status.time_events_us) + " us</h4>";
content += "<h4>10ms function timing: " + String(datalayer.system.status.time_10ms_us) + " us</h4>";
content += "<h4>5s function timing: " + String(datalayer.system.status.time_5s_us) + " us</h4>";
content += "<h4>CAN/serial RX function timing: " + String(datalayer.system.status.time_comm_us) + " us</h4>";
content += "<h4>CAN TX function timing: " + String(datalayer.system.status.time_cantx_us) + " us</h4>";
content += "<h4>Wifi and OTA function timing: " + String(datalayer.system.status.time_wifi_us) + " us</h4>";
content += "<h4>Core task max load: " + String(datalayer.system.status.core_task_max_us) + " us</h4>";
content += "<h4>Core task max load last 10 s: " + String(datalayer.system.status.core_task_10s_max_us) + " us</h4>";
content += "<h4>MQTT task max load last 10 s: " + String(datalayer.system.status.mqtt_task_10s_max_us) + " us</h4>";
content +=
"<h4>loop() task max load last 10 s: " + String(datalayer.system.status.loop_task_10s_max_us) + " us</h4>";
content += "<h4>Max load @ worst case execution of core task:</h4>";
content += "<h4>10ms function timing: " + String(datalayer.system.status.time_snap_10ms_us) + " us</h4>";
content += "<h4>5s function timing: " + String(datalayer.system.status.time_snap_5s_us) + " us</h4>";
content += "<h4>CAN/serial RX function timing: " + String(datalayer.system.status.time_snap_comm_us) + " us</h4>";
content += "<h4>CAN TX function timing: " + String(datalayer.system.status.time_snap_cantx_us) + " us</h4>";
content += "<h4>Wifi and OTA function timing: " + String(datalayer.system.status.time_snap_wifi_us) + " us</h4>";
#endif
wl_status_t status = WiFi.status();
@ -507,14 +508,19 @@ String processor(const String& var) {
content += "padding: 10px; margin-bottom: 10px; border-radius: 50px;'>";
// Display battery statistics within this block
float socRealFloat = static_cast<float>(system_real_SOC_pptt) / 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 sohFloat = static_cast<float>(system_SOH_pptt) / 100.0; // Convert to float and divide by 100
float voltageFloat = static_cast<float>(system_battery_voltage_dV) / 10.0; // Convert to float and divide by 10
float currentFloat = static_cast<float>(system_battery_current_dA) / 10.0; // Convert to float and divide by 10
float powerFloat = static_cast<float>(system_active_power_W); // Convert to float
float tempMaxFloat = static_cast<float>(system_temperature_max_dC) / 10.0; // Convert to float
float tempMinFloat = static_cast<float>(system_temperature_min_dC) / 10.0; // Convert to float
float socRealFloat =
static_cast<float>(datalayer.battery.status.real_soc) / 100.0; // Convert to float and divide by 100
float socScaledFloat =
static_cast<float>(datalayer.battery.status.reported_soc) / 100.0; // Convert to float and divide by 100
float sohFloat =
static_cast<float>(datalayer.battery.status.soh_pptt) / 100.0; // Convert to float and divide by 100
float voltageFloat =
static_cast<float>(datalayer.battery.status.voltage_dV) / 10.0; // Convert to float and divide by 10
float currentFloat =
static_cast<float>(datalayer.battery.status.current_dA) / 10.0; // Convert to float and divide by 10
float powerFloat = static_cast<float>(datalayer.battery.status.active_power_W); // Convert to float
float tempMaxFloat = static_cast<float>(datalayer.battery.status.temperature_max_dC) / 10.0; // Convert to float
float tempMinFloat = static_cast<float>(datalayer.battery.status.temperature_min_dC) / 10.0; // Convert to float
content += "<h4 style='color: white;'>Real SOC: " + String(socRealFloat, 2) + "</h4>";
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) + "</h4>";
@ -522,24 +528,24 @@ String processor(const String& var) {
content += "<h4 style='color: white;'>Voltage: " + String(voltageFloat, 1) + " V</h4>";
content += "<h4 style='color: white;'>Current: " + String(currentFloat, 1) + " A</h4>";
content += formatPowerValue("Power", powerFloat, "", 1);
content += formatPowerValue("Total capacity", system_capacity_Wh, "h", 0);
content += formatPowerValue("Remaining capacity", system_remaining_capacity_Wh, "h", 1);
content += formatPowerValue("Max discharge power", system_max_discharge_power_W, "", 1);
content += formatPowerValue("Max charge power", system_max_charge_power_W, "", 1);
content += "<h4>Cell max: " + String(system_cell_max_voltage_mV) + " mV</h4>";
content += "<h4>Cell min: " + String(system_cell_min_voltage_mV) + " mV</h4>";
content += formatPowerValue("Total capacity", datalayer.battery.info.total_capacity_Wh, "h", 0);
content += formatPowerValue("Remaining capacity", datalayer.battery.status.remaining_capacity_Wh, "h", 1);
content += formatPowerValue("Max discharge power", datalayer.battery.status.max_discharge_power_W, "", 1);
content += formatPowerValue("Max charge power", datalayer.battery.status.max_charge_power_W, "", 1);
content += "<h4>Cell max: " + String(datalayer.battery.status.cell_max_voltage_mV) + " mV</h4>";
content += "<h4>Cell min: " + String(datalayer.battery.status.cell_min_voltage_mV) + " mV</h4>";
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " C</h4>";
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " C</h4>";
if (system_bms_status == ACTIVE) {
if (datalayer.battery.status.bms_status == ACTIVE) {
content += "<h4>BMS Status: OK </h4>";
} else if (system_bms_status == UPDATING) {
} else if (datalayer.battery.status.bms_status == UPDATING) {
content += "<h4>BMS Status: UPDATING </h4>";
} else {
content += "<h4>BMS Status: FAULT </h4>";
}
if (system_battery_current_dA == 0) {
if (datalayer.battery.status.current_dA == 0) {
content += "<h4>Battery idle</h4>";
} else if (system_battery_current_dA < 0) {
} else if (datalayer.battery.status.current_dA < 0) {
content += "<h4>Battery discharging!</h4>";
} else { // > 0
content += "<h4>Battery charging!</h4>";
@ -547,14 +553,14 @@ String processor(const String& var) {
content += "<h4>Automatic contactor closing allowed:</h4>";
content += "<h4>Battery: ";
if (batteryAllowsContactorClosing) {
if (datalayer.system.status.battery_allows_contactor_closing == true) {
content += "<span>&#10003;</span>";
} else {
content += "<span style='color: red;'>&#10005;</span>";
}
content += " Inverter: ";
if (inverterAllowsContactorClosing) {
if (datalayer.system.status.inverter_allows_contactor_closing == true) {
content += "<span>&#10003;</span></h4>";
} else {
content += "<span style='color: red;'>&#10005;</span></h4>";
@ -604,8 +610,8 @@ String processor(const String& var) {
#endif
#ifdef NISSANLEAF_CHARGER
float chgPwrDC = static_cast<float>(charger_stat_HVcur * 100);
charger_stat_HVcur = chgPwrDC / (system_battery_voltage_dV / 10); // P/U=I
charger_stat_HVvol = static_cast<float>(system_battery_voltage_dV / 10);
charger_stat_HVcur = chgPwrDC / (datalayer.battery.status.voltage_dV / 10); // P/U=I
charger_stat_HVvol = static_cast<float>(datalayer.battery.status.voltage_dV / 10);
float ACvol = charger_stat_ACvol;
float HVvol = charger_stat_HVvol;
float HVcur = charger_stat_HVcur;

View file

@ -16,27 +16,6 @@
#endif
extern const char* version_number; // The current software version, shown on webserver
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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 inverterAllowsContactorClosing; //Bool, 1=true, 0=false
extern const char* ssid;
extern const char* password;

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef BYD_CAN
#include "../datalayer/datalayer.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "BYD-CAN.h"
@ -115,32 +116,38 @@ static bool initialDataSent = 0;
void update_values_can_byd() { //This function maps all the values fetched from battery CAN to the correct CAN messages
//Calculate values
charge_current = ((system_max_charge_power_W * 10) /
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
charge_current =
((datalayer.battery.status.max_charge_power_W * 10) /
datalayer.battery.info.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
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
if (charge_current > MAXCHARGEAMP) {
charge_current = MAXCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
if (charge_current > datalayer.battery.info.max_charge_amp_dA) {
charge_current =
datalayer.battery.info
.max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
}
discharge_current = ((system_max_discharge_power_W * 10) /
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
discharge_current =
((datalayer.battery.status.max_discharge_power_W * 10) /
datalayer.battery.info.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
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
if (discharge_current > MAXDISCHARGEAMP) {
if (discharge_current > datalayer.battery.info.max_discharge_amp_dA) {
discharge_current =
MAXDISCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
datalayer.battery.info
.max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
}
temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
temperature_average =
((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2);
//Map values to CAN messages
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
BYD_110.data.u8[0] = (system_max_design_voltage_dV >> 8);
BYD_110.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
BYD_110.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8);
BYD_110.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
//Minvoltage (eg 300.0V = 3000 , 16bits long)
BYD_110.data.u8[2] = (system_min_design_voltage_dV >> 8);
BYD_110.data.u8[3] = (system_min_design_voltage_dV & 0x00FF);
BYD_110.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >> 8);
BYD_110.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
//Maximum discharge power allowed (Unit: A+1)
BYD_110.data.u8[4] = (discharge_current >> 8);
BYD_110.data.u8[5] = (discharge_current & 0x00FF);
@ -149,11 +156,11 @@ void update_values_can_byd() { //This function maps all the values fetched from
BYD_110.data.u8[7] = (charge_current & 0x00FF);
//SOC (100.00%)
BYD_150.data.u8[0] = (system_scaled_SOC_pptt >> 8);
BYD_150.data.u8[1] = (system_scaled_SOC_pptt & 0x00FF);
BYD_150.data.u8[0] = (datalayer.battery.status.reported_soc >> 8);
BYD_150.data.u8[1] = (datalayer.battery.status.reported_soc & 0x00FF);
//StateOfHealth (100.00%)
BYD_150.data.u8[2] = (system_SOH_pptt >> 8);
BYD_150.data.u8[3] = (system_SOH_pptt & 0x00FF);
BYD_150.data.u8[2] = (datalayer.battery.status.soh_pptt >> 8);
BYD_150.data.u8[3] = (datalayer.battery.status.soh_pptt & 0x00FF);
//Maximum discharge power allowed (Unit: A+1)
BYD_150.data.u8[4] = (discharge_current >> 8);
BYD_150.data.u8[5] = (discharge_current & 0x00FF);
@ -162,21 +169,21 @@ void update_values_can_byd() { //This function maps all the values fetched from
BYD_150.data.u8[7] = (charge_current & 0x00FF);
//Voltage (ex 370.0)
BYD_1D0.data.u8[0] = (system_battery_voltage_dV >> 8);
BYD_1D0.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
BYD_1D0.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
BYD_1D0.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
//Current (ex 81.0A)
BYD_1D0.data.u8[2] = (system_battery_current_dA >> 8);
BYD_1D0.data.u8[3] = (system_battery_current_dA & 0x00FF);
BYD_1D0.data.u8[2] = (datalayer.battery.status.current_dA >> 8);
BYD_1D0.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF);
//Temperature average
BYD_1D0.data.u8[4] = (temperature_average >> 8);
BYD_1D0.data.u8[5] = (temperature_average & 0x00FF);
//Temperature max
BYD_210.data.u8[0] = (system_temperature_max_dC >> 8);
BYD_210.data.u8[1] = (system_temperature_max_dC & 0x00FF);
BYD_210.data.u8[0] = (datalayer.battery.status.temperature_max_dC >> 8);
BYD_210.data.u8[1] = (datalayer.battery.status.temperature_max_dC & 0x00FF);
//Temperature min
BYD_210.data.u8[2] = (system_temperature_min_dC >> 8);
BYD_210.data.u8[3] = (system_temperature_min_dC & 0x00FF);
BYD_210.data.u8[2] = (datalayer.battery.status.temperature_min_dC >> 8);
BYD_210.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF);
#ifdef DEBUG_VIA_USB
if (char1_151 != 0) {

View file

@ -6,29 +6,6 @@
#define INVERTER_SELECTED
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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 send_can_byd();
void receive_can_byd(CAN_frame_t rx_frame);

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef BYD_MODBUS
#include "../datalayer/datalayer.h"
#include "BYD-MODBUS.h"
void update_modbus_registers_byd() {
@ -34,19 +35,22 @@ void handle_update_data_modbusp201_byd() {
static uint16_t system_data[13];
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
if (system_capacity_Wh > 60000) {
if (datalayer.battery.info.total_capacity_Wh > 60000) {
system_data[2] = 60000;
} else {
system_data[2] =
(system_capacity_Wh); // Id.: p203 Value.: 32000 Scaled value.: 32kWh Comment.: Capacity rated, maximum value is 60000 (60kWh)
(datalayer.battery.info
.total_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[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)
system_data[5] =
(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)
(datalayer.battery.info
.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_min_design_voltage_dV); // Id.: p207 Value.: 2776 Scaled value.: 277,6VDC Comment.: Min Voltage, if lower Gen24 disables battery
(datalayer.battery.info
.min_design_voltage_dV); // Id.: p207 Value.: 2776 Scaled value.: 277,6VDC Comment.: Min Voltage, if lower Gen24 disables battery
system_data[7] =
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
@ -64,60 +68,69 @@ void handle_update_data_modbusp301_byd() {
static uint16_t battery_data[24];
static uint8_t bms_char_dis_status = STANDBY;
if (system_battery_current_dA == 0) {
if (datalayer.battery.status.current_dA == 0) {
bms_char_dis_status = STANDBY;
} else if (system_battery_current_dA < 0) { //Negative value = Discharging
} else if (datalayer.battery.status.current_dA < 0) { //Negative value = Discharging
bms_char_dis_status = DISCHARGING;
} else { //Positive value = Charging
bms_char_dis_status = CHARGING;
}
if (system_bms_status == ACTIVE) {
if (datalayer.battery.status.bms_status == ACTIVE) {
battery_data[8] =
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
datalayer.battery.status
.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 {
battery_data[8] = 0;
}
battery_data[0] =
system_bms_status; // Id.: p301 Value.: 3 Scaled value.: 3 Comment.: status(*): ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
datalayer.battery.status
.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[2] = 128 + bms_char_dis_status; // Id.: p303 Value.: 130 Scaled value.: 130 Comment.: mode(*): normal
battery_data[3] =
system_scaled_SOC_pptt; // Id.: p304 Value.: 1700 Scaled value.: 50% Comment.: SOC: (50% would equal 5000)
if (system_capacity_Wh > 60000) {
datalayer.battery.status
.reported_soc; // Id.: p304 Value.: 1700 Scaled value.: 50% Comment.: SOC: (50% would equal 5000)
if (datalayer.battery.info.total_capacity_Wh > 60000) {
battery_data[4] = 60000;
} else {
battery_data[4] = system_capacity_Wh; // Id.: p305 Value.: 32000 Scaled value.: 32kWh Comment.: tot cap:
battery_data[4] =
datalayer.battery.info.total_capacity_Wh; // Id.: p305 Value.: 32000 Scaled value.: 32kWh Comment.: tot cap:
}
if (system_remaining_capacity_Wh > 60000) {
if (datalayer.battery.status.remaining_capacity_Wh > 60000) {
battery_data[5] = 60000;
} else {
battery_data[5] =
system_remaining_capacity_Wh; // Id.: p306 Value.: 13260 Scaled value.: 13,26kWh Comment.: remaining cap: 7.68kWh
// Id.: p306 Value.: 13260 Scaled value.: 13,26kWh Comment.: remaining cap: 7.68kWh
battery_data[5] = datalayer.battery.status.remaining_capacity_Wh;
}
if (system_max_discharge_power_W > 30000) {
if (datalayer.battery.status.max_discharge_power_W > 30000) {
battery_data[6] = 30000;
} else {
battery_data[6] =
system_max_discharge_power_W; // Id.: p307 Value.: 25604 Scaled value.: 25,604kW Comment.: max/target discharge power: 0W (0W > restricts to no discharge)
datalayer.battery.status
.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) {
if (datalayer.battery.status.max_charge_power_W > 30000) {
battery_data[7] = 30000;
} else {
battery_data[7] =
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
datalayer.battery.status
.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[9] =
2000; // Id.: p310 Value.: 64121 Scaled value.: 6412,1W Comment.: Current Power to API: if>32768... -(65535-61760)=3775W
battery_data[10] =
system_battery_voltage_dV; // Id.: p311 Value.: 3161 Scaled value.: 316,1VDC Comment.: Batt Voltage inner: 173.2V
datalayer.battery.status
.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[12] =
system_temperature_min_dC; // Id.: p313 Value.: 75 Scaled value.: 7,5 Comment.: temp min: 7 degrees (if below 0....65535-t)
datalayer.battery.status
.temperature_min_dC; // Id.: p313 Value.: 75 Scaled value.: 7,5 Comment.: temp min: 7 degrees (if below 0....65535-t)
battery_data[13] =
system_temperature_max_dC; // Id.: p314 Value.: 95 Scaled value.: 9,5 Comment.: temp max: 9 degrees (if below 0....65535-t)
datalayer.battery.status
.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[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
@ -129,27 +142,29 @@ void handle_update_data_modbusp301_byd() {
battery_data[21] =
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[23] = system_SOH_pptt; // Id.: p324 Value.: 9900 Scaled value.: 99% Comment.: SOH
battery_data[23] = datalayer.battery.status.soh_pptt; // Id.: p324 Value.: 9900 Scaled value.: 99% Comment.: SOH
static uint16_t i = 300;
memcpy(&mbPV[i], battery_data, sizeof(battery_data));
}
void verify_temperature_modbus() {
if (system_LFP_Chemistry) {
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
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
// 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.
// When using EV packs with NCM/LMO/NCA chemsitry, this is not a problem, since these chemistries are OK for outdoor cold use.
if (system_temperature_min_dC < 0) {
if (system_temperature_min_dC < -90 && system_temperature_min_dC > -200) { // Between -9.0 and -20.0C degrees
system_temperature_min_dC = -90; //Cap value to -9.0C
if (datalayer.battery.status.temperature_min_dC < 0) {
if (datalayer.battery.status.temperature_min_dC < -90 &&
datalayer.battery.status.temperature_min_dC > -200) { // Between -9.0 and -20.0C degrees
datalayer.battery.status.temperature_min_dC = -90; //Cap value to -9.0C
}
}
if (system_temperature_max_dC < 0) { // Signed value on negative side
if (system_temperature_max_dC < -90 && system_temperature_max_dC > -200) { // Between -9.0 and -20.0C degrees
system_temperature_max_dC = -90; //Cap value to -9.0C
if (datalayer.battery.status.temperature_max_dC < 0) { // Signed value on negative side
if (datalayer.battery.status.temperature_max_dC < -90 &&
datalayer.battery.status.temperature_max_dC > -200) { // Between -9.0 and -20.0C degrees
datalayer.battery.status.temperature_max_dC = -90; //Cap value to -9.0C
}
}
}

View file

@ -8,24 +8,6 @@
#define MAX_POWER 40960 //BYD Modbus specific value
extern uint16_t mbPV[MB_RTU_NUM_VALUES];
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
extern uint8_t system_bms_status; //Enum 0-5
extern bool batteryAllowsContactorClosing; //Bool, true/false
extern bool inverterAllowsContactorClosing; //Bool, true/false
extern bool system_LFP_Chemistry; //Bool, true/false
void handle_static_data_modbus_byd();
void verify_temperature_modbus();

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef LUNA2000_MODBUS
#include "../datalayer/datalayer.h"
#include "LUNA2000-MODBUS.h"
void update_modbus_registers_luna2000() {
@ -16,9 +17,9 @@ void handle_update_data_modbus32051() {
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[4] = 616; //Goes between 616- -520 [NOTE, SIGNED VALUE]
system_data[5] = system_temperature_max_dC; //Temperature max?
system_data[6] = system_temperature_min_dC; //Temperature min?
system_data[7] = (system_scaled_SOC_pptt / 100); //SOC 0-100%, no decimals
system_data[5] = datalayer.battery.status.temperature_max_dC; //Temperature max?
system_data[6] = datalayer.battery.status.temperature_min_dC; //Temperature min?
system_data[7] = (datalayer.battery.status.reported_soc / 100); //SOC 0-100%, no decimals
system_data[8] = 98; //Always 98 in logs
static uint16_t i = 2051;
memcpy(&mbPV[i], system_data, sizeof(system_data));
@ -28,17 +29,17 @@ void handle_update_data_modbus39500() {
// Store the data into the array
static uint16_t system_data[26];
system_data[0] = 0;
system_data[1] = system_capacity_Wh; //Capacity? 5000 with 5kWh battery
system_data[1] = datalayer.battery.info.total_capacity_Wh; //Capacity? 5000 with 5kWh battery
system_data[2] = 0;
system_data[3] = system_capacity_Wh; //Capacity? 5000 with 5kWh battery
system_data[3] = datalayer.battery.info.total_capacity_Wh; //Capacity? 5000 with 5kWh battery
system_data[4] = 0;
system_data[5] = 2500; //???
system_data[6] = 0;
system_data[7] = 2500; //???
system_data[8] = (system_scaled_SOC_pptt / 100); //SOC 0-100%, no decimals
system_data[8] = (datalayer.battery.status.reported_soc / 100); //SOC 0-100%, no decimals
system_data[9] =
1; //Running status, equiv to register 37762, 0 = Offline, 1 = Standby,2 = Running, 3 = Fault, 4 = sleep mode
system_data[10] = system_battery_voltage_dV; //Battery bus voltage (766.5V = 7665)
system_data[10] = datalayer.battery.status.voltage_dV; //Battery bus voltage (766.5V = 7665)
system_data[11] = 9; //TODO: GOES LOWER WITH LOW SOC
system_data[12] = 0;
system_data[13] = 699; //TODO: GOES LOWER WITH LOW SOC
@ -52,7 +53,7 @@ void handle_update_data_modbus39500() {
system_data[21] = 0;
system_data[22] = 0;
system_data[23] = 0;
system_data[24] = (system_scaled_SOC_pptt / 10); //SOC 0-100.0%, 1x decimal
system_data[24] = (datalayer.battery.status.reported_soc / 10); //SOC 0-100.0%, 1x decimal
system_data[25] = 0;
system_data[26] = 1; //Always 1 in logs
static uint16_t i = 9500;

View file

@ -7,27 +7,6 @@
#define MB_RTU_NUM_VALUES 30000
extern uint16_t mbPV[MB_RTU_NUM_VALUES];
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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 handle_update_data_modbus32051();

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef PYLON_CAN
#include "../datalayer/datalayer.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "PYLON-CAN.h"
@ -184,53 +185,53 @@ void update_values_can_pylon() { //This function maps all the values fetched fr
PYLON_4281.data.u8[3] = 0;
//Voltage (370.0)
PYLON_4210.data.u8[0] = (system_battery_voltage_dV >> 8);
PYLON_4210.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
PYLON_4211.data.u8[0] = (system_battery_voltage_dV >> 8);
PYLON_4211.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
PYLON_4210.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
PYLON_4210.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
PYLON_4211.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
PYLON_4211.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
//Current (15.0)
PYLON_4210.data.u8[2] = (system_battery_current_dA >> 8);
PYLON_4210.data.u8[3] = (system_battery_current_dA & 0x00FF);
PYLON_4211.data.u8[2] = (system_battery_current_dA >> 8);
PYLON_4211.data.u8[3] = (system_battery_current_dA & 0x00FF);
PYLON_4210.data.u8[2] = (datalayer.battery.status.current_dA >> 8);
PYLON_4210.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF);
PYLON_4211.data.u8[2] = (datalayer.battery.status.current_dA >> 8);
PYLON_4211.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF);
//SOC (100.00%)
PYLON_4210.data.u8[6] = (system_scaled_SOC_pptt * 0.01); //Remove decimals
PYLON_4211.data.u8[6] = (system_scaled_SOC_pptt * 0.01); //Remove decimals
PYLON_4210.data.u8[6] = (datalayer.battery.status.reported_soc / 100); //Remove decimals
PYLON_4211.data.u8[6] = (datalayer.battery.status.reported_soc / 100); //Remove decimals
//StateOfHealth (100.00%)
PYLON_4210.data.u8[7] = (system_SOH_pptt * 0.01);
PYLON_4211.data.u8[7] = (system_SOH_pptt * 0.01);
PYLON_4210.data.u8[7] = (datalayer.battery.status.soh_pptt / 100);
PYLON_4211.data.u8[7] = (datalayer.battery.status.soh_pptt / 100);
#ifdef INVERT_VOLTAGE //Useful for Sofar inverters \
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Discharge Cutoff Voltage
PYLON_4220.data.u8[0] = (system_max_design_voltage_dV & 0x00FF);
PYLON_4220.data.u8[1] = (system_max_design_voltage_dV >> 8);
PYLON_4221.data.u8[0] = (system_max_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[1] = (system_max_design_voltage_dV >> 8);
PYLON_4220.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
PYLON_4220.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV >> 8);
PYLON_4221.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV >> 8);
//Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage
PYLON_4220.data.u8[2] = (system_min_design_voltage_dV & 0x00FF);
PYLON_4220.data.u8[3] = (system_min_design_voltage_dV >> 8);
PYLON_4221.data.u8[2] = (system_min_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[3] = (system_min_design_voltage_dV >> 8);
PYLON_4220.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
PYLON_4220.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV >> 8);
PYLON_4221.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV >> 8);
#else
//Minvoltage (eg 300.0V = 3000 , 16bits long) Charge Cutoff Voltage
PYLON_4220.data.u8[0] = (system_min_design_voltage_dV >> 8);
PYLON_4220.data.u8[1] = (system_min_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[0] = (system_min_design_voltage_dV >> 8);
PYLON_4221.data.u8[1] = (system_min_design_voltage_dV & 0x00FF);
PYLON_4220.data.u8[0] = (datalayer.battery.info.min_design_voltage_dV >> 8);
PYLON_4220.data.u8[1] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[0] = (datalayer.battery.info.min_design_voltage_dV >> 8);
PYLON_4221.data.u8[1] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Discharge Cutoff Voltage
PYLON_4220.data.u8[2] = (system_max_design_voltage_dV >> 8);
PYLON_4220.data.u8[3] = (system_max_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[2] = (system_max_design_voltage_dV >> 8);
PYLON_4221.data.u8[3] = (system_max_design_voltage_dV & 0x00FF);
PYLON_4220.data.u8[2] = (datalayer.battery.info.max_design_voltage_dV >> 8);
PYLON_4220.data.u8[3] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
PYLON_4221.data.u8[2] = (datalayer.battery.info.max_design_voltage_dV >> 8);
PYLON_4221.data.u8[3] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
#endif
//In case we run into any errors/faults, we can set charge / discharge forbidden
if (system_bms_status == FAULT) {
if (datalayer.battery.status.bms_status == FAULT) {
PYLON_4280.data.u8[0] = 0xAA;
PYLON_4280.data.u8[1] = 0xAA;
PYLON_4280.data.u8[2] = 0xAA;

View file

@ -1,33 +1,10 @@
#ifndef PYLON_CAN_H
#define PYLON_CAN_H
#include <Arduino.h>
#include "../include.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#define INVERTER_SELECTED
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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 receive_can_pylon(CAN_frame_t rx_frame);
void send_system_data();

View file

@ -1,6 +1,7 @@
#include "../include.h"
#ifdef SERIAL_LINK_TRANSMITTER
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SERIAL-LINK-TRANSMITTER-INVERTER.h"
@ -25,7 +26,7 @@ SerialDataLink dataLinkTransmit(Serial2, 0x01, 0, BATTERY_SEND_NUM_VARIABLES, re
void printSendingValues();
void _getData() {
inverterAllowsContactorClosing = dataLinkTransmit.getReceivedData(0);
datalayer.system.status.inverter_allows_contactor_closing = dataLinkTransmit.getReceivedData(0);
//var2 = dataLinkTransmit.getReceivedData(1); // For future expansion,
//var3 = dataLinkTransmit.getReceivedData(2); // if inverter needs to send data to battery
}
@ -107,8 +108,8 @@ void manageSerialLinkTransmitter() {
Serial.println("SerialDataLink : max_target_discharge_power = 0");
Serial.println("SerialDataLink : max_target_charge_power = 0");
system_max_discharge_power_W = 0;
system_max_charge_power_W = 0;
datalayer.battery.status.max_discharge_power_W = 0;
datalayer.battery.status.max_charge_power_W = 0;
set_event(EVENT_SERIAL_TX_FAILURE, 0);
// throw error
}
@ -128,22 +129,24 @@ void manageSerialLinkTransmitter() {
if (currentTime - updateDataTime > INTERVAL_1_S) {
updateDataTime = currentTime;
dataLinkTransmit.updateData(0, system_real_SOC_pptt);
dataLinkTransmit.updateData(1, system_SOH_pptt);
dataLinkTransmit.updateData(2, system_battery_voltage_dV);
dataLinkTransmit.updateData(3, system_battery_current_dA);
dataLinkTransmit.updateData(4, system_capacity_Wh / 10); //u32, remove .0 to fit 16bit
dataLinkTransmit.updateData(5, system_remaining_capacity_Wh / 10); //u32, remove .0 to fit 16bit
dataLinkTransmit.updateData(6, system_max_discharge_power_W / 10); //u32, remove .0 to fit 16bit
dataLinkTransmit.updateData(7, system_max_charge_power_W / 10); //u32, remove .0 to fit 16bit
dataLinkTransmit.updateData(8, system_bms_status);
dataLinkTransmit.updateData(9, system_active_power_W / 10); //u32, remove .0 to fit 16bit
dataLinkTransmit.updateData(10, system_temperature_min_dC);
dataLinkTransmit.updateData(11, system_temperature_max_dC);
dataLinkTransmit.updateData(12, system_cell_max_voltage_mV);
dataLinkTransmit.updateData(13, system_cell_min_voltage_mV);
dataLinkTransmit.updateData(14, (int16_t)system_LFP_Chemistry);
dataLinkTransmit.updateData(15, batteryAllowsContactorClosing);
dataLinkTransmit.updateData(0, datalayer.battery.status.real_soc);
dataLinkTransmit.updateData(1, datalayer.battery.status.soh_pptt);
dataLinkTransmit.updateData(2, datalayer.battery.status.voltage_dV);
dataLinkTransmit.updateData(3, datalayer.battery.status.current_dA);
dataLinkTransmit.updateData(4, datalayer.battery.info.total_capacity_Wh / 10); //u32, remove .0 to fit 16bit
dataLinkTransmit.updateData(5,
datalayer.battery.status.remaining_capacity_Wh / 10); //u32, remove .0 to fit 16bit
dataLinkTransmit.updateData(6,
datalayer.battery.status.max_discharge_power_W / 10); //u32, remove .0 to fit 16bit
dataLinkTransmit.updateData(7, datalayer.battery.status.max_charge_power_W / 10); //u32, remove .0 to fit 16bit
dataLinkTransmit.updateData(8, datalayer.battery.status.bms_status);
dataLinkTransmit.updateData(9, datalayer.battery.status.active_power_W / 10); //u32, remove .0 to fit 16bit
dataLinkTransmit.updateData(10, datalayer.battery.status.temperature_min_dC);
dataLinkTransmit.updateData(11, datalayer.battery.status.temperature_max_dC);
dataLinkTransmit.updateData(12, datalayer.battery.status.cell_max_voltage_mV);
dataLinkTransmit.updateData(13, datalayer.battery.status.cell_min_voltage_mV);
dataLinkTransmit.updateData(14, (int16_t)datalayer.battery.info.chemistry);
dataLinkTransmit.updateData(15, datalayer.system.status.battery_allows_contactor_closing);
}
}
}
@ -151,39 +154,39 @@ void manageSerialLinkTransmitter() {
void printSendingValues() {
Serial.println("Values from battery: ");
Serial.print("SOC: ");
Serial.print(system_real_SOC_pptt);
Serial.print(datalayer.battery.status.real_soc);
Serial.print(" SOH: ");
Serial.print(system_SOH_pptt);
Serial.print(datalayer.battery.status.soh_pptt);
Serial.print(" Voltage: ");
Serial.print(system_battery_voltage_dV);
Serial.print(datalayer.battery.status.voltage_dV);
Serial.print(" Current: ");
Serial.print(system_battery_current_dA);
Serial.print(datalayer.battery.status.current_dA);
Serial.print(" Capacity: ");
Serial.print(system_capacity_Wh);
Serial.print(datalayer.battery.info.total_capacity_Wh);
Serial.print(" Remain cap: ");
Serial.print(system_remaining_capacity_Wh);
Serial.print(datalayer.battery.status.remaining_capacity_Wh);
Serial.print(" Max discharge W: ");
Serial.print(system_max_discharge_power_W);
Serial.print(datalayer.battery.status.max_discharge_power_W);
Serial.print(" Max charge W: ");
Serial.print(system_max_charge_power_W);
Serial.print(datalayer.battery.status.max_charge_power_W);
Serial.print(" BMS status: ");
Serial.print(system_bms_status);
Serial.print(datalayer.battery.status.bms_status);
Serial.print(" Power: ");
Serial.print(system_active_power_W);
Serial.print(datalayer.battery.status.active_power_W);
Serial.print(" Temp min: ");
Serial.print(system_temperature_min_dC);
Serial.print(datalayer.battery.status.temperature_min_dC);
Serial.print(" Temp max: ");
Serial.print(system_temperature_max_dC);
Serial.print(datalayer.battery.status.temperature_max_dC);
Serial.print(" Cell max: ");
Serial.print(system_cell_max_voltage_mV);
Serial.print(datalayer.battery.status.cell_max_voltage_mV);
Serial.print(" Cell min: ");
Serial.print(system_cell_min_voltage_mV);
Serial.print(datalayer.battery.status.cell_min_voltage_mV);
Serial.print(" LFP : ");
Serial.print(system_LFP_Chemistry);
Serial.print(" batteryAllowsContactorClosing: ");
Serial.print(batteryAllowsContactorClosing);
Serial.print(" inverterAllowsContactorClosing: ");
Serial.print(inverterAllowsContactorClosing);
Serial.print(datalayer.battery.info.chemistry);
Serial.print(" Battery Allows Contactor Closing: ");
Serial.print(datalayer.system.status.battery_allows_contactor_closing);
Serial.print(" Inverter Allows Contactor Closing: ");
Serial.print(datalayer.system.status.inverter_allows_contactor_closing);
Serial.println("");
}

View file

@ -7,27 +7,6 @@
#define INVERTER_SELECTED
// These parameters need to be mapped for the inverter
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
extern uint8_t system_bms_status; //Enum 0-5
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
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 bool batteryAllowsContactorClosing; //Bool, true/false
extern bool system_LFP_Chemistry; //Bool, true/false
// parameters received from receiver
extern bool inverterAllowsContactorClosing; //Bool, true/false
void manageSerialLinkTransmitter();
#endif

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef SMA_CAN
#include "../datalayer/datalayer.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "SMA-CAN.h"
@ -104,36 +105,42 @@ 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
//Calculate values
charge_current = ((system_max_charge_power_W * 10) /
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
charge_current =
((datalayer.battery.status.max_charge_power_W * 10) /
datalayer.battery.info.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
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
if (charge_current > MAXCHARGEAMP) {
charge_current = MAXCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
if (charge_current > datalayer.battery.info.max_charge_amp_dA) {
charge_current =
datalayer.battery.info
.max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
}
discharge_current = ((system_max_discharge_power_W * 10) /
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
discharge_current =
((datalayer.battery.status.max_discharge_power_W * 10) /
datalayer.battery.info.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
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
if (discharge_current > MAXDISCHARGEAMP) {
if (discharge_current > datalayer.battery.info.max_discharge_amp_dA) {
discharge_current =
MAXDISCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
datalayer.battery.info
.max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
}
temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
temperature_average =
((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2);
ampere_hours_remaining =
((system_remaining_capacity_Wh / system_battery_voltage_dV) * 100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
ampere_hours_remaining = ((datalayer.battery.status.remaining_capacity_Wh / datalayer.battery.status.voltage_dV) *
100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
//Map values to CAN messages
//Maxvoltage (eg 400.0V = 4000 , 16bits long)
SMA_358.data.u8[0] = (system_max_design_voltage_dV >> 8);
SMA_358.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
SMA_358.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8);
SMA_358.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
//Minvoltage (eg 300.0V = 3000 , 16bits long)
SMA_358.data.u8[2] =
(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);
SMA_358.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >>
8); //Minvoltage behaves strange on SMA, cuts out at 56% of the set value?
SMA_358.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
//Discharge limited current, 500 = 50A, (0.1, A)
SMA_358.data.u8[4] = (discharge_current >> 8);
SMA_358.data.u8[5] = (discharge_current & 0x00FF);
@ -142,26 +149,26 @@ void update_values_can_sma() { //This function maps all the values fetched from
SMA_358.data.u8[7] = (charge_current & 0x00FF);
//SOC (100.00%)
SMA_3D8.data.u8[0] = (system_scaled_SOC_pptt >> 8);
SMA_3D8.data.u8[1] = (system_scaled_SOC_pptt & 0x00FF);
SMA_3D8.data.u8[0] = (datalayer.battery.status.reported_soc >> 8);
SMA_3D8.data.u8[1] = (datalayer.battery.status.reported_soc & 0x00FF);
//StateOfHealth (100.00%)
SMA_3D8.data.u8[2] = (system_SOH_pptt >> 8);
SMA_3D8.data.u8[3] = (system_SOH_pptt & 0x00FF);
SMA_3D8.data.u8[2] = (datalayer.battery.status.soh_pptt >> 8);
SMA_3D8.data.u8[3] = (datalayer.battery.status.soh_pptt & 0x00FF);
//State of charge (AH, 0.1)
SMA_3D8.data.u8[4] = (ampere_hours_remaining >> 8);
SMA_3D8.data.u8[5] = (ampere_hours_remaining & 0x00FF);
//Voltage (370.0)
SMA_4D8.data.u8[0] = (system_battery_voltage_dV >> 8);
SMA_4D8.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
SMA_4D8.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
SMA_4D8.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
//Current (TODO: signed OK?)
SMA_4D8.data.u8[2] = (system_battery_current_dA >> 8);
SMA_4D8.data.u8[3] = (system_battery_current_dA & 0x00FF);
SMA_4D8.data.u8[2] = (datalayer.battery.status.current_dA >> 8);
SMA_4D8.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF);
//Temperature average
SMA_4D8.data.u8[4] = (temperature_average >> 8);
SMA_4D8.data.u8[5] = (temperature_average & 0x00FF);
//Battery ready
if (system_bms_status == ACTIVE) {
if (datalayer.battery.status.bms_status == ACTIVE) {
SMA_4D8.data.u8[6] = READY_STATE;
} else {
SMA_4D8.data.u8[6] = STOP_STATE;

View file

@ -1,33 +1,10 @@
#ifndef SMA_CAN_H
#define SMA_CAN_H
#include <Arduino.h>
#include "../include.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#define INVERTER_SELECTED
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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
#define READY_STATE 0x03
#define STOP_STATE 0x02

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef SMA_TRIPOWER_CAN
#include "../datalayer/datalayer.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "SMA-TRIPOWER-CAN.h"
@ -162,29 +163,35 @@ InvInitState invInitState = SYSTEM_FREQUENCY;
void update_values_can_sma_tripower() { //This function maps all the values fetched from battery CAN to the inverter CAN
//Calculate values
charge_current = ((system_max_charge_power_W * 10) /
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
charge_current =
((datalayer.battery.status.max_charge_power_W * 10) /
datalayer.battery.info.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
charge_current = (charge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
if (charge_current > MAXCHARGEAMP) {
charge_current = MAXCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
if (charge_current > datalayer.battery.info.max_charge_amp_dA) {
charge_current =
datalayer.battery.info
.max_charge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
}
discharge_current = ((system_max_discharge_power_W * 10) /
system_max_design_voltage_dV); //Charge power in W , max volt in V+1decimal (P=UI, solve for I)
discharge_current =
((datalayer.battery.status.max_discharge_power_W * 10) /
datalayer.battery.info.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
discharge_current = (discharge_current * 10); //Value needs a decimal before getting sent to inverter (81.0A)
if (discharge_current > MAXDISCHARGEAMP) {
if (discharge_current > datalayer.battery.info.max_discharge_amp_dA) {
discharge_current =
MAXDISCHARGEAMP; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
datalayer.battery.info
.max_discharge_amp_dA; //Cap the value to the max allowed Amp. Some inverters cannot handle large values.
}
temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
temperature_average =
((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2);
ampere_hours_remaining =
((system_remaining_capacity_Wh / system_battery_voltage_dV) * 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)
ampere_hours_remaining = ((datalayer.battery.status.remaining_capacity_Wh / datalayer.battery.status.voltage_dV) *
100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
ampere_hours_max = ((datalayer.battery.info.total_capacity_Wh / datalayer.battery.status.voltage_dV) *
100); //(WH[10000] * V+1[3600])*100 = 270 (27.0Ah)
batteryState = OPERATE;
inverterControlFlags = INVERTER_STAY_ON;
@ -192,11 +199,11 @@ void update_values_can_sma_tripower() { //This function maps all the values fet
//Map values to CAN messages
// Battery Limits
//Battery Max Charge Voltage (eg 400.0V = 4000 , 16bits long)
SMA_00D.data.u8[0] = (system_max_design_voltage_dV >> 8);
SMA_00D.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
SMA_00D.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8);
SMA_00D.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
//Battery Min Discharge Voltage (eg 300.0V = 3000 , 16bits long)
SMA_00D.data.u8[2] = (system_min_design_voltage_dV >> 8);
SMA_00D.data.u8[3] = (system_min_design_voltage_dV & 0x00FF);
SMA_00D.data.u8[2] = (datalayer.battery.info.min_design_voltage_dV >> 8);
SMA_00D.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
//Discharge limited current, 500 = 50A, (0.1, A)
SMA_00D.data.u8[4] = (discharge_current >> 8);
SMA_00D.data.u8[5] = (discharge_current & 0x00FF);
@ -206,11 +213,11 @@ void update_values_can_sma_tripower() { //This function maps all the values fet
// Battery State
//SOC (100.00%)
SMA_00F.data.u8[0] = (system_scaled_SOC_pptt >> 8);
SMA_00F.data.u8[1] = (system_scaled_SOC_pptt & 0x00FF);
SMA_00F.data.u8[0] = (datalayer.battery.status.reported_soc >> 8);
SMA_00F.data.u8[1] = (datalayer.battery.status.reported_soc & 0x00FF);
//StateOfHealth (100.00%)
SMA_00F.data.u8[2] = (system_SOH_pptt >> 8);
SMA_00F.data.u8[3] = (system_SOH_pptt & 0x00FF);
SMA_00F.data.u8[2] = (datalayer.battery.status.soh_pptt >> 8);
SMA_00F.data.u8[3] = (datalayer.battery.status.soh_pptt & 0x00FF);
//State of charge (AH, 0.1)
SMA_00F.data.u8[4] = (ampere_hours_remaining >> 8);
SMA_00F.data.u8[5] = (ampere_hours_remaining & 0x00FF);
@ -232,11 +239,11 @@ void update_values_can_sma_tripower() { //This function maps all the values fet
// Battery Measurements
//Voltage (370.0)
SMA_013.data.u8[0] = (system_battery_voltage_dV >> 8);
SMA_013.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
SMA_013.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
SMA_013.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
//Current (TODO: signed OK?)
SMA_013.data.u8[2] = (system_battery_current_dA >> 8);
SMA_013.data.u8[3] = (system_battery_current_dA & 0x00FF);
SMA_013.data.u8[2] = (datalayer.battery.status.current_dA >> 8);
SMA_013.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF);
//Temperature average
SMA_013.data.u8[4] = (temperature_average >> 8);
SMA_013.data.u8[5] = (temperature_average & 0x00FF);
@ -246,11 +253,11 @@ void update_values_can_sma_tripower() { //This function maps all the values fet
// Battery Temperature and Cellvoltages
// Battery max temperature
SMA_014.data.u8[0] = (system_temperature_max_dC >> 8);
SMA_014.data.u8[1] = (system_temperature_max_dC & 0x00FF);
SMA_014.data.u8[0] = (datalayer.battery.status.temperature_max_dC >> 8);
SMA_014.data.u8[1] = (datalayer.battery.status.temperature_max_dC & 0x00FF);
// Battery min temperature
SMA_014.data.u8[2] = (system_temperature_min_dC >> 8);
SMA_014.data.u8[3] = (system_temperature_min_dC & 0x00FF);
SMA_014.data.u8[2] = (datalayer.battery.status.temperature_min_dC >> 8);
SMA_014.data.u8[3] = (datalayer.battery.status.temperature_min_dC & 0x00FF);
// Battery Cell Voltage (sum)
//SMA_014.data.u8[4] = (??? >> 8); //TODO scaling?
//SMA_014.data.u8[5] = (??? & 0x00FF); //TODO scaling?

View file

@ -1,33 +1,10 @@
#ifndef SMA_CAN_TRIPOWER_H
#define SMA_CAN_TRIPOWER_H
#include <Arduino.h>
#include "../include.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#define INVERTER_SELECTED
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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_sma_tripower();
void send_can_sma_tripower();
void receive_can_sma_tripower(CAN_frame_t rx_frame);

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef SOFAR_CAN
#include "../datalayer/datalayer.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "SOFAR-CAN.h"
@ -282,29 +283,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
//Maxvoltage (eg 400.0V = 4000 , 16bits long) Charge Cutoff Voltage
SOFAR_351.data.u8[0] = (system_max_design_voltage_dV >> 8);
SOFAR_351.data.u8[1] = (system_max_design_voltage_dV & 0x00FF);
SOFAR_351.data.u8[0] = (datalayer.battery.info.max_design_voltage_dV >> 8);
SOFAR_351.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV & 0x00FF);
//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[4] = DC discharge current limitation (Default 25.0A)
//SOFAR_351.data.u8[5] = DC discharge current limitation
//Minvoltage (eg 300.0V = 3000 , 16bits long) Discharge Cutoff Voltage
SOFAR_351.data.u8[6] = (system_min_design_voltage_dV >> 8);
SOFAR_351.data.u8[7] = (system_min_design_voltage_dV & 0x00FF);
SOFAR_351.data.u8[6] = (datalayer.battery.info.min_design_voltage_dV >> 8);
SOFAR_351.data.u8[7] = (datalayer.battery.info.min_design_voltage_dV & 0x00FF);
//SOC
SOFAR_355.data.u8[0] = (system_scaled_SOC_pptt / 100);
SOFAR_355.data.u8[2] = (system_SOH_pptt / 100);
SOFAR_355.data.u8[0] = (datalayer.battery.status.reported_soc / 100);
SOFAR_355.data.u8[2] = (datalayer.battery.status.soh_pptt / 100);
//SOFAR_355.data.u8[6] = (AH_remaining >> 8);
//SOFAR_355.data.u8[7] = (AH_remaining & 0x00FF);
//Voltage (370.0)
SOFAR_356.data.u8[0] = (system_battery_voltage_dV >> 8);
SOFAR_356.data.u8[1] = (system_battery_voltage_dV & 0x00FF);
SOFAR_356.data.u8[2] = (system_battery_current_dA >> 8);
SOFAR_356.data.u8[3] = (system_battery_current_dA & 0x00FF);
SOFAR_356.data.u8[2] = (system_temperature_max_dC >> 8);
SOFAR_356.data.u8[3] = (system_temperature_max_dC & 0x00FF);
SOFAR_356.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
SOFAR_356.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
SOFAR_356.data.u8[2] = (datalayer.battery.status.current_dA >> 8);
SOFAR_356.data.u8[3] = (datalayer.battery.status.current_dA & 0x00FF);
SOFAR_356.data.u8[2] = (datalayer.battery.status.temperature_max_dC >> 8);
SOFAR_356.data.u8[3] = (datalayer.battery.status.temperature_max_dC & 0x00FF);
}
void receive_can_sofar(CAN_frame_t rx_frame) {

View file

@ -1,37 +1,10 @@
#ifndef SOFAR_CAN_H
#define SOFAR_CAN_H
#include <Arduino.h>
#include "../include.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#define INVERTER_SELECTED
// These parameters need to be mapped for the inverter
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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 max_voltage;
void update_values_can_sofar();
void send_can_sofar();
void receive_can_sofar(CAN_frame_t rx_frame);

View file

@ -1,5 +1,6 @@
#include "../include.h"
#ifdef SOLAX_CAN
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "SOLAX-CAN.h"
@ -123,7 +124,7 @@ void CAN_WriteFrame(CAN_frame_t* tx_frame) {
void update_values_can_solax() { //This function maps all the values fetched from battery CAN to the correct CAN messages
// If not receiveing any communication from the inverter, open contactors and return to battery announce state
if (millis() - LastFrameTime >= SolaxTimeout) {
inverterAllowsContactorClosing = false;
datalayer.system.status.inverter_allows_contactor_closing = false;
STATE = BATTERY_ANNOUNCE;
#ifndef DUAL_CAN
ESP32Can.CANStop(); // Baud rate switching might have taken down the interface. Reboot it!
@ -131,75 +132,78 @@ void update_values_can_solax() { //This function maps all the values fetched fr
#endif
}
//Calculate the required values
temperature_average = ((system_temperature_max_dC + system_temperature_min_dC) / 2);
temperature_average =
((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2);
//system_max_charge_power_W (30000W max)
if (system_scaled_SOC_pptt > 9999) //99.99%
//datalayer.battery.status.max_charge_power_W (30000W max)
if (datalayer.battery.status.reported_soc > 9999) //99.99%
{ //Additional safety incase SOC% is 100, then do not charge battery further
max_charge_rate_amp = 0;
} else { //We can pass on the battery charge rate (in W) to the inverter (that takes A)
if (system_max_charge_power_W >= 30000) {
if (datalayer.battery.status.max_charge_power_W >= 30000) {
max_charge_rate_amp = 75; //Incase battery can take over 30kW, cap value to 75A
} else { //Calculate the W value into A
max_charge_rate_amp = (system_max_charge_power_W / (system_battery_voltage_dV * 0.1)); // P/U = I
max_charge_rate_amp =
(datalayer.battery.status.max_charge_power_W / (datalayer.battery.status.voltage_dV * 0.1)); // P/U = I
}
}
//system_max_discharge_power_W (30000W max)
if (system_scaled_SOC_pptt < 100) //1.00%
//datalayer.battery.status.max_discharge_power_W (30000W max)
if (datalayer.battery.status.reported_soc < 100) //1.00%
{ //Additional safety incase SOC% is below 1, then do not charge battery further
max_discharge_rate_amp = 0;
} else { //We can pass on the battery discharge rate to the inverter
if (system_max_discharge_power_W >= 30000) {
if (datalayer.battery.status.max_discharge_power_W >= 30000) {
max_discharge_rate_amp = 75; //Incase battery can be charged with over 30kW, cap value to 75A
} else { //Calculate the W value into A
max_discharge_rate_amp = (system_max_discharge_power_W / (system_battery_voltage_dV * 0.1)); // P/U = I
max_discharge_rate_amp =
(datalayer.battery.status.max_discharge_power_W / (datalayer.battery.status.voltage_dV * 0.1)); // P/U = I
}
}
// Batteries might be larger than uint16_t value can take
if (system_capacity_Wh > 65000) {
if (datalayer.battery.info.total_capacity_Wh > 65000) {
capped_capacity_Wh = 65000;
} else {
capped_capacity_Wh = system_capacity_Wh;
capped_capacity_Wh = datalayer.battery.info.total_capacity_Wh;
}
// Batteries might be larger than uint16_t value can take
if (system_remaining_capacity_Wh > 65000) {
if (datalayer.battery.status.remaining_capacity_Wh > 65000) {
capped_remaining_capacity_Wh = 65000;
} else {
capped_remaining_capacity_Wh = system_remaining_capacity_Wh;
capped_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh;
}
//Put the values into the CAN messages
//BMS_Limits
SOLAX_1872.data.u8[0] = (uint8_t)system_max_design_voltage_dV;
SOLAX_1872.data.u8[1] = (system_max_design_voltage_dV >> 8);
SOLAX_1872.data.u8[2] = (uint8_t)system_min_design_voltage_dV;
SOLAX_1872.data.u8[3] = (system_min_design_voltage_dV >> 8);
SOLAX_1872.data.u8[0] = (uint8_t)datalayer.battery.info.max_design_voltage_dV;
SOLAX_1872.data.u8[1] = (datalayer.battery.info.max_design_voltage_dV >> 8);
SOLAX_1872.data.u8[2] = (uint8_t)datalayer.battery.info.min_design_voltage_dV;
SOLAX_1872.data.u8[3] = (datalayer.battery.info.min_design_voltage_dV >> 8);
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[6] = (uint8_t)(max_discharge_rate_amp * 10);
SOLAX_1872.data.u8[7] = ((max_discharge_rate_amp * 10) >> 8);
//BMS_PackData
SOLAX_1873.data.u8[0] = (uint8_t)system_battery_voltage_dV; // OK
SOLAX_1873.data.u8[1] = (system_battery_voltage_dV >> 8);
SOLAX_1873.data.u8[2] = (int8_t)system_battery_current_dA; // OK, Signed (Active current in Amps x 10)
SOLAX_1873.data.u8[3] = (system_battery_current_dA >> 8);
SOLAX_1873.data.u8[4] = (uint8_t)(system_scaled_SOC_pptt / 100); //SOC (100.00%)
SOLAX_1873.data.u8[0] = (uint8_t)datalayer.battery.status.voltage_dV; // OK
SOLAX_1873.data.u8[1] = (datalayer.battery.status.voltage_dV >> 8);
SOLAX_1873.data.u8[2] = (int8_t)datalayer.battery.status.current_dA; // OK, Signed (Active current in Amps x 10)
SOLAX_1873.data.u8[3] = (datalayer.battery.status.current_dA >> 8);
SOLAX_1873.data.u8[4] = (uint8_t)(datalayer.battery.status.reported_soc / 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[6] = (uint8_t)(capped_remaining_capacity_Wh / 100);
SOLAX_1873.data.u8[7] = ((capped_remaining_capacity_Wh / 100) >> 8);
//BMS_CellData
SOLAX_1874.data.u8[0] = (int8_t)system_temperature_max_dC;
SOLAX_1874.data.u8[1] = (system_temperature_max_dC >> 8);
SOLAX_1874.data.u8[2] = (int8_t)system_temperature_min_dC;
SOLAX_1874.data.u8[3] = (system_temperature_min_dC >> 8);
SOLAX_1874.data.u8[4] = (uint8_t)(system_cell_max_voltage_mV);
SOLAX_1874.data.u8[5] = (system_cell_max_voltage_mV >> 8);
SOLAX_1874.data.u8[6] = (uint8_t)(system_cell_min_voltage_mV);
SOLAX_1874.data.u8[7] = (system_cell_min_voltage_mV >> 8);
SOLAX_1874.data.u8[0] = (int8_t)datalayer.battery.status.temperature_max_dC;
SOLAX_1874.data.u8[1] = (datalayer.battery.status.temperature_max_dC >> 8);
SOLAX_1874.data.u8[2] = (int8_t)datalayer.battery.status.temperature_min_dC;
SOLAX_1874.data.u8[3] = (datalayer.battery.status.temperature_min_dC >> 8);
SOLAX_1874.data.u8[4] = (uint8_t)(datalayer.battery.status.cell_max_voltage_mV);
SOLAX_1874.data.u8[5] = (datalayer.battery.status.cell_max_voltage_mV >> 8);
SOLAX_1874.data.u8[6] = (uint8_t)(datalayer.battery.status.cell_min_voltage_mV);
SOLAX_1874.data.u8[7] = (datalayer.battery.status.cell_min_voltage_mV >> 8);
//BMS_Status
SOLAX_1875.data.u8[0] = (uint8_t)temperature_average;
@ -208,11 +212,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.
//BMS_PackTemps (strange name, since it has voltages?)
SOLAX_1876.data.u8[2] = (uint8_t)system_cell_max_voltage_mV;
SOLAX_1876.data.u8[3] = (system_cell_max_voltage_mV >> 8);
SOLAX_1876.data.u8[2] = (uint8_t)datalayer.battery.status.cell_max_voltage_mV;
SOLAX_1876.data.u8[3] = (datalayer.battery.status.cell_max_voltage_mV >> 8);
SOLAX_1876.data.u8[6] = (uint8_t)system_cell_min_voltage_mV;
SOLAX_1876.data.u8[7] = (system_cell_min_voltage_mV >> 8);
SOLAX_1876.data.u8[6] = (uint8_t)datalayer.battery.status.cell_min_voltage_mV;
SOLAX_1876.data.u8[7] = (datalayer.battery.status.cell_min_voltage_mV >> 8);
//Unknown
SOLAX_1877.data.u8[4] = (uint8_t)0x50; // Battery type
@ -221,8 +225,8 @@ 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
//BMS_PackStats
SOLAX_1878.data.u8[0] = (uint8_t)(system_battery_voltage_dV);
SOLAX_1878.data.u8[1] = ((system_battery_voltage_dV) >> 8);
SOLAX_1878.data.u8[0] = (uint8_t)(datalayer.battery.status.voltage_dV);
SOLAX_1878.data.u8[1] = ((datalayer.battery.status.voltage_dV) >> 8);
SOLAX_1878.data.u8[4] = (uint8_t)capped_capacity_Wh;
SOLAX_1878.data.u8[5] = (capped_capacity_Wh >> 8);
@ -242,7 +246,7 @@ void receive_can_solax(CAN_frame_t rx_frame) {
#ifdef DEBUG_VIA_USB
Serial.println("Solax Battery State: Announce");
#endif
inverterAllowsContactorClosing = false;
datalayer.system.status.inverter_allows_contactor_closing = false;
SOLAX_1875.data.u8[4] = (0x00); // Inform Inverter: Contactor 0=off, 1=on.
for (int i = 0; i <= number_of_batteries; i++) {
CAN_WriteFrame(&SOLAX_1872);
@ -277,7 +281,7 @@ void receive_can_solax(CAN_frame_t rx_frame) {
break;
case (CONTACTOR_CLOSED):
inverterAllowsContactorClosing = true;
datalayer.system.status.inverter_allows_contactor_closing = true;
SOLAX_1875.data.u8[4] = (0x01); // Inform Inverter: Contactor 0=off, 1=on.
CAN_WriteFrame(&SOLAX_1872);
CAN_WriteFrame(&SOLAX_1873);

View file

@ -1,6 +1,5 @@
#ifndef SOLAX_CAN_H
#define SOLAX_CAN_H
#include <Arduino.h>
#include "../include.h"
#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "../lib/pierremolinaro-acan2515/ACAN2515.h"
@ -9,28 +8,6 @@
extern ACAN2515 can;
extern uint32_t system_capacity_Wh; //Wh, 0-500000Wh
extern uint32_t system_remaining_capacity_Wh; //Wh, 0-500000Wh
extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0
extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0
extern int32_t system_active_power_W; //W, -200000 to 200000
extern int16_t system_battery_current_dA; //A+1, -1000 - 1000
extern uint16_t system_battery_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_max_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_min_design_voltage_dV; //V+1, 0-1000.0 (0-10000)
extern uint16_t system_scaled_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_real_SOC_pptt; //SOC%, 0-100.00 (0-10000)
extern uint16_t system_SOH_pptt; //SOH%, 0-100.00 (0-10000)
extern uint32_t system_max_discharge_power_W; //W, 0-200000
extern uint32_t system_max_charge_power_W; //W, 0-200000
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[MAX_AMOUNT_CELLS]; //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
// Timeout in milliseconds
#define SolaxTimeout 2000

View file

@ -218,7 +218,7 @@ static bool _start_async_task(){
return false;
}
if(!_async_service_task_handle){
xTaskCreateUniversal(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
xTaskCreateUniversal(_async_service_task, "async_tcp", 8192 * 2, NULL, TASK_CONNECTIVITY_PRIO, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
if(!_async_service_task_handle){
return false;
}

View file

@ -30,10 +30,13 @@ extern "C" {
#include "lwip/pbuf.h"
}
#include "../../../system_settings.h"
#include "../../../devboard/hal/hal.h"
//If core is not defined, then we are running in Arduino or PIO
#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
#define CONFIG_ASYNC_TCP_RUNNING_CORE 0 //any available core
#define CONFIG_ASYNC_TCP_USE_WDT 1 //if enabled, adds between 33us and 200us per event
#define CONFIG_ASYNC_TCP_RUNNING_CORE WIFI_CORE //any available core
#define CONFIG_ASYNC_TCP_USE_WDT 0 //if enabled, adds between 33us and 200us per event
#endif
class AsyncClient;

View file

@ -9,9 +9,13 @@
* Parameter: TASK_CONNECTIVITY_PRIO
* Description:
* Defines the priority of various wireless functionality (TCP, MQTT, etc)
*
* Parameter: TASK_MODBUS_PRIO
* Description:
* Defines the priority of MODBUS handling
*/
#define TASK_CORE_PRIO 4
#define TASK_WIFI_PRIO 3
#define TASK_CONNECTIVITY_PRIO 3
#define TASK_MODBUS_PRIO 8
/** MAX AMOUNT OF CELLS
@ -35,6 +39,11 @@
* Description:
* The period of whatever LED mode is active. If CLASSIC, then a ramp up and ramp down will finish in
* LED_PERIOD_MS milliseconds
*
* Parameter: LED_EXECUTION_FREQUENCY
* Description:
* Defines how often the LED handling will run, basically the FPS. The animation will honor its overall
* frequency but the animation will be choppier
*/
#define LED_MODE_DEFAULT FLOW
#define LED_PERIOD_MS 3000