Rewrite part 1

This commit is contained in:
Daniel 2024-02-19 00:44:04 +02:00
parent d4c514f43c
commit 032a0d75ee
18 changed files with 335 additions and 293 deletions

View file

@ -53,26 +53,27 @@ uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory
ModbusServerRTU MBserver(Serial2, 2000); ModbusServerRTU MBserver(Serial2, 2000);
#endif #endif
// Common inverter parameters. Batteries map their values to these variables // Common system parameters. Batteries map their values to these variables
uint16_t max_voltage = 5000; //V+1, 0-500.0 (0-5000) uint32_t system_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-150000Wh
uint16_t min_voltage = 2500; //V+1, 0-500.0 (0-5000) uint32_t system_remaining_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-150000Wh
uint16_t battery_voltage = 3700; //V+1, 0-500.0 (0-5000) int16_t system_temperature_max_dC = 0; //C+1, -50.0 - 50.0
uint16_t battery_current = 0; int16_t system_temperature_min_dC = 0; //C+1, -50.0 - 50.0
uint16_t SOC = 5000; //SOC%, 0-100.00 (0-10000) int16_t system_active_power_W = 0; //Watts, -32000 to 32000
uint16_t StateOfHealth = 9900; //SOH%, 0-100.00 (0-10000) int16_t system_battery_current_dA = 0; //A+1, -1000 - 1000
uint16_t capacity_Wh = BATTERY_WH_MAX; //Wh, 0-60000 uint16_t system_battery_voltage_dV = 3700; //V+1, 0-500.0 (0-5000)
uint16_t remaining_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-60000 uint16_t system_max_design_voltage_dV = 5000; //V+1, 0-500.0 (0-5000)
uint16_t max_target_discharge_power = 0; // 0W (0W > restricts to no discharge), Updates later on from CAN uint16_t system_min_design_voltage_dV = 2500; //V+1, 0-500.0 (0-5000)
uint16_t max_target_charge_power = 4312; // Init to 4.3kW, Updates later on from CAN uint16_t system_scaled_SOC_pptt = 5000; //SOC%, 0-100.00 (0-10000)
uint16_t temperature_max = 50; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) uint16_t system_real_SOC_pptt = 5000; //SOC%, 0-100.00 (0-10000)
uint16_t temperature_min = 60; // Reads from battery later uint16_t system_SOH_pptt = 9900; //SOH%, 0-100.00 (0-10000)
uint8_t bms_status = ACTIVE; // ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING] uint16_t system_max_discharge_power_W = 0; //Watts, 0 to 65535
uint16_t stat_batt_power = 0; // Power going in/out of battery uint16_t system_max_charge_power_W = 4312; //Watts, 0 to 65535
uint16_t cell_max_voltage = 3700; // Stores the highest cell voltage value in the system uint16_t system_cell_max_voltage_mV = 3700; //mV, 0-5000 , Stores the highest cell millivolt value
uint16_t cell_min_voltage = 3700; // Stores the minimum cell voltage value in the system uint16_t system_cell_min_voltage_mV = 3700; //mV, 0-5000, Stores the minimum cell millivolt value
uint16_t cellvoltages[120]; // Stores all cell voltages uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV. Oversized to accomodate all setups
uint8_t nof_cellvoltages = 0; // Total number of cell voltages, set by each battery. uint8_t system_bms_status = ACTIVE; //ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING]
bool LFP_Chemistry = false; uint8_t system_number_of_cells = 0; //Total number of cell voltages, set by each battery
bool system_LFP_Chemistry = false; //Set to true or false depending on cell chemistry
// Common charger parameters // Common charger parameters
volatile float charger_setpoint_HV_VDC = 0.0f; volatile float charger_setpoint_HV_VDC = 0.0f;
@ -184,6 +185,7 @@ void loop() {
if (millis() - previousMillisUpdateVal >= intervalUpdateValues) // Every 4.8s if (millis() - previousMillisUpdateVal >= intervalUpdateValues) // Every 4.8s
{ {
previousMillisUpdateVal = millis(); previousMillisUpdateVal = millis();
update_SOC(); // Check if real or calculated SOC% value should be sent
update_values(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus. update_values(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus.
if (DUMMY_EVENT_ENABLED) { if (DUMMY_EVENT_ENABLED) {
set_event(EVENT_DUMMY_ERROR, (uint8_t)millis()); set_event(EVENT_DUMMY_ERROR, (uint8_t)millis());
@ -240,6 +242,14 @@ void init_stored_settings() {
if (temp != 0) { if (temp != 0) {
MAXDISCHARGEAMP = temp; MAXDISCHARGEAMP = temp;
} }
temp = settings.getBool("USE_SCALED_SOC", false);
if (temp == false) {
USE_SCALED_SOC = temp;
}
if (temp == true) {
USE_SCALED_SOC = temp;
}
settings.end(); settings.end();
} }
@ -599,6 +609,23 @@ void handle_contactors() {
} }
#endif #endif
void update_SOC() {
if (USE_SCALED_SOC) { //User has configred a SOC window. Calculate a SOC% to send towards inverter
static int16_t CalculatedSOC = 0;
CalculatedSOC = system_real_SOC_pptt;
CalculatedSOC = (10000) * (CalculatedSOC - (MINPERCENTAGE * 10)) / (MAXPERCENTAGE * 10 - MINPERCENTAGE * 10);
if (CalculatedSOC < 0) { //We are in the real SOC% range of 0-MINPERCENTAGE%
CalculatedSOC = 0;
}
if (CalculatedSOC > 10000) { //We are in the real SOC% range of MAXPERCENTAGE-100%
CalculatedSOC = 10000;
}
system_scaled_SOC_pptt = CalculatedSOC;
} else { // No SOC window wanted. Set scaled to same as real.
system_scaled_SOC_pptt = system_real_SOC_pptt;
}
}
void update_values() { void update_values() {
// Battery // Battery
update_values_battery(); // Map the fake values to the correct registers update_values_battery(); // Map the fake values to the correct registers
@ -660,5 +687,7 @@ void storeSettings() {
settings.putUInt("MINPERCENTAGE", MINPERCENTAGE); settings.putUInt("MINPERCENTAGE", MINPERCENTAGE);
settings.putUInt("MAXCHARGEAMP", MAXCHARGEAMP); settings.putUInt("MAXCHARGEAMP", MAXCHARGEAMP);
settings.putUInt("MAXDISCHARGEAMP", MAXDISCHARGEAMP); settings.putUInt("MAXDISCHARGEAMP", MAXDISCHARGEAMP);
settings.putBool("USE_SCALED_SOC", USE_SCALED_SOC);
settings.end(); settings.end();
} }

View file

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

View file

@ -12,7 +12,7 @@
//#define CHADEMO_BATTERY //#define CHADEMO_BATTERY
//#define IMIEV_CZERO_ION_BATTERY //#define IMIEV_CZERO_ION_BATTERY
//#define KIA_HYUNDAI_64_BATTERY //#define KIA_HYUNDAI_64_BATTERY
//#define NISSAN_LEAF_BATTERY #define NISSAN_LEAF_BATTERY
//#define RENAULT_KANGOO_BATTERY //#define RENAULT_KANGOO_BATTERY
//#define RENAULT_ZOE_BATTERY //#define RENAULT_ZOE_BATTERY
//#define SANTA_FE_PHEV_BATTERY //#define SANTA_FE_PHEV_BATTERY
@ -21,7 +21,7 @@
/* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki */ /* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki */
//#define BYD_CAN //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus //#define BYD_CAN //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus
//#define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU #define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU
//#define LUNA2000_MODBUS //Enable this line to emulate a "Luna2000 battery" over Modbus RTU //#define LUNA2000_MODBUS //Enable this line to emulate a "Luna2000 battery" over Modbus RTU
//#define PYLON_CAN //Enable this line to emulate a "Pylontech battery" over CAN bus //#define PYLON_CAN //Enable this line to emulate a "Pylontech battery" over CAN bus
//#define SMA_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" over CAN bus //#define SMA_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" over CAN bus
@ -55,12 +55,13 @@
//#define NISSANLEAF_CHARGER //Enable this line to control a Nissan LEAF PDM connected to battery - for example, when generator charging //#define NISSANLEAF_CHARGER //Enable this line to control a Nissan LEAF PDM connected to battery - for example, when generator charging
/* Battery limits: These are set in the USER_SETTINGS.cpp file, or later on via the Webserver */ /* Battery limits: These are set in the USER_SETTINGS.cpp file, or later on via the Webserver */
extern volatile uint16_t BATTERY_WH_MAX; extern volatile uint32_t BATTERY_WH_MAX;
extern volatile uint16_t MAXPERCENTAGE; extern volatile uint16_t MAXPERCENTAGE;
extern volatile uint16_t MINPERCENTAGE; extern volatile uint16_t MINPERCENTAGE;
extern volatile uint16_t MAXCHARGEAMP; extern volatile uint16_t MAXCHARGEAMP;
extern volatile uint16_t MAXDISCHARGEAMP; extern volatile uint16_t MAXDISCHARGEAMP;
extern volatile uint8_t AccessPointEnabled; extern volatile uint8_t AccessPointEnabled;
extern volatile bool USE_SCALED_SOC;
/* Charger limits (Optional): Set in the USER_SETTINGS.cpp or later in the webserver */ /* Charger limits (Optional): Set in the USER_SETTINGS.cpp or later in the webserver */
extern volatile float charger_setpoint_HV_VDC; extern volatile float charger_setpoint_HV_VDC;

View file

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

View file

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

View file

@ -30,7 +30,7 @@ static void publish_cell_voltages(void) {
static bool mqtt_first_transmission = true; static bool mqtt_first_transmission = true;
// If the cell voltage number isn't initialized... // If the cell voltage number isn't initialized...
if (nof_cellvoltages == 0u) { if (system_number_of_cells == 0u) {
return; return;
} }
// At startup, re-post the discovery message for home assistant // At startup, re-post the discovery message for home assistant
@ -39,7 +39,7 @@ static void publish_cell_voltages(void) {
// Base topic for any cell voltage "sensor" // Base topic for any cell voltage "sensor"
String topic = "homeassistant/sensor/battery-emulator/cell_voltage"; String topic = "homeassistant/sensor/battery-emulator/cell_voltage";
for (int i = 0; i < nof_cellvoltages; i++) { for (int i = 0; i < system_number_of_cells; i++) {
// Build JSON message with device configuration for each cell voltage // Build JSON message with device configuration for each cell voltage
// Probably shouldn't be BatteryEmulator here, instead "LeafBattery" // Probably shouldn't be BatteryEmulator here, instead "LeafBattery"
// or similar but hey, it works. // or similar but hey, it works.
@ -83,15 +83,15 @@ static void publish_cell_voltages(void) {
// is the string content // is the string content
// If cell voltages haven't been populated... // If cell voltages haven't been populated...
if (nof_cellvoltages == 0u) { if (system_number_of_cells == 0u) {
return; return;
} }
} }
size_t msg_length = snprintf(mqtt_msg, sizeof(mqtt_msg), "{\n\"cell_voltages\":["); size_t msg_length = snprintf(mqtt_msg, sizeof(mqtt_msg), "{\n\"cell_voltages\":[");
for (size_t i = 0; i < nof_cellvoltages; ++i) { for (size_t i = 0; i < system_number_of_cells; ++i) {
msg_length += snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "%s%.3f", (i == 0) ? "" : ", ", msg_length += snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "%s%.3f", (i == 0) ? "" : ", ",
((float)cellvoltages[i]) / 1000); ((float)system_cellvoltages_mV[i]) / 1000);
} }
snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "]\n}\n"); snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "]\n}\n");
@ -170,20 +170,21 @@ static void publish_common_info(void) {
} else { } else {
snprintf(mqtt_msg, sizeof(mqtt_msg), snprintf(mqtt_msg, sizeof(mqtt_msg),
"{\n" "{\n"
" \"SOC\": %.3f,\n" " \"system_scaled_SOC_pptt\": %.3f,\n"
" \"state_of_health\": %.3f,\n" " \"system_SOH_pptt\": %.3f,\n"
" \"temperature_min\": %.3f,\n" " \"system_temperature_min_dC\": %.3f,\n"
" \"temperature_max\": %.3f,\n" " \"system_temperature_max_dC\": %.3f,\n"
" \"stat_batt_power\": %.3f,\n" " \"system_active_power_W\": %.3f,\n"
" \"battery_current\": %.3f,\n" " \"system_battery_current_dA\": %.3f,\n"
" \"cell_max_voltage\": %.3f,\n" " \"system_cell_max_voltage_mV\": %.3f,\n"
" \"cell_min_voltage\": %.3f,\n" " \"system_cell_min_voltage_mV\": %.3f,\n"
" \"battery_voltage\": %d\n" " \"system_battery_voltage_dV\": %d\n"
"}\n", "}\n",
((float)SOC) / 100.0, ((float)StateOfHealth) / 100.0, ((float)((int16_t)temperature_min)) / 10.0, ((float)system_scaled_SOC_pptt) / 100.0, ((float)system_SOH_pptt) / 100.0,
((float)((int16_t)temperature_max)) / 10.0, ((float)((int16_t)stat_batt_power)), ((float)((int16_t)system_temperature_min_dC)) / 10.0, ((float)((int16_t)system_temperature_max_dC)) / 10.0,
((float)((int16_t)battery_current)) / 10.0, ((float)cell_max_voltage) / 1000, ((float)((int16_t)system_active_power_W)), ((float)((int16_t)system_battery_current_dA)) / 10.0,
((float)cell_min_voltage) / 1000, battery_voltage / 10.0); ((float)system_cell_max_voltage_mV) / 1000, ((float)system_cell_min_voltage_mV) / 1000,
system_battery_voltage_dV / 10.0);
bool result = client.publish(state_topic, mqtt_msg, true); bool result = client.publish(state_topic, mqtt_msg, true);
} }

View file

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

View file

@ -299,13 +299,13 @@ static void update_bms_status(void) {
case EVENT_LEVEL_INFO: case EVENT_LEVEL_INFO:
case EVENT_LEVEL_WARNING: case EVENT_LEVEL_WARNING:
case EVENT_LEVEL_DEBUG: case EVENT_LEVEL_DEBUG:
bms_status = ACTIVE; system_bms_status = ACTIVE;
break; break;
case EVENT_LEVEL_UPDATE: case EVENT_LEVEL_UPDATE:
bms_status = UPDATING; system_bms_status = UPDATING;
break; break;
case EVENT_LEVEL_ERROR: case EVENT_LEVEL_ERROR:
bms_status = FAULT; system_bms_status = FAULT;
break; break;
default: default:
break; break;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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