diff --git a/Software/Software.ino b/Software/Software.ino index b75f0ed8..1d99a5df 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -53,26 +53,27 @@ uint16_t mbPV[MB_RTU_NUM_VALUES]; // Process variable memory ModbusServerRTU MBserver(Serial2, 2000); #endif -// Common inverter parameters. Batteries map their values to these variables -uint16_t max_voltage = 5000; //V+1, 0-500.0 (0-5000) -uint16_t min_voltage = 2500; //V+1, 0-500.0 (0-5000) -uint16_t battery_voltage = 3700; //V+1, 0-500.0 (0-5000) -uint16_t battery_current = 0; -uint16_t SOC = 5000; //SOC%, 0-100.00 (0-10000) -uint16_t StateOfHealth = 9900; //SOH%, 0-100.00 (0-10000) -uint16_t capacity_Wh = BATTERY_WH_MAX; //Wh, 0-60000 -uint16_t remaining_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-60000 -uint16_t max_target_discharge_power = 0; // 0W (0W > restricts to no discharge), Updates later on from CAN -uint16_t max_target_charge_power = 4312; // Init to 4.3kW, Updates later on from CAN -uint16_t temperature_max = 50; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) -uint16_t temperature_min = 60; // Reads from battery later -uint8_t bms_status = ACTIVE; // ACTIVE - [0..5]<>[STANDBY,INACTIVE,DARKSTART,ACTIVE,FAULT,UPDATING] -uint16_t stat_batt_power = 0; // Power going in/out of battery -uint16_t cell_max_voltage = 3700; // Stores the highest cell voltage value in the system -uint16_t cell_min_voltage = 3700; // Stores the minimum cell voltage value in the system -uint16_t cellvoltages[120]; // Stores all cell voltages -uint8_t nof_cellvoltages = 0; // Total number of cell voltages, set by each battery. -bool LFP_Chemistry = false; +// Common system parameters. Batteries map their values to these variables +uint32_t system_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-150000Wh +uint32_t system_remaining_capacity_Wh = BATTERY_WH_MAX; //Wh, 0-150000Wh +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 +int16_t system_active_power_W = 0; //Watts, -32000 to 32000 +int16_t system_battery_current_dA = 0; //A+1, -1000 - 1000 +uint16_t system_battery_voltage_dV = 3700; //V+1, 0-500.0 (0-5000) +uint16_t system_max_design_voltage_dV = 5000; //V+1, 0-500.0 (0-5000) +uint16_t system_min_design_voltage_dV = 2500; //V+1, 0-500.0 (0-5000) +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) +uint16_t system_max_discharge_power_W = 0; //Watts, 0 to 65535 +uint16_t system_max_charge_power_W = 4312; //Watts, 0 to 65535 +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[120]; //Array with all cell voltages in mV. 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; @@ -184,6 +185,7 @@ void loop() { if (millis() - previousMillisUpdateVal >= intervalUpdateValues) // Every 4.8s { 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. if (DUMMY_EVENT_ENABLED) { set_event(EVENT_DUMMY_ERROR, (uint8_t)millis()); @@ -240,6 +242,14 @@ void init_stored_settings() { if (temp != 0) { 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(); } @@ -599,6 +609,23 @@ 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; + } else { // No SOC window wanted. Set scaled to same as real. + system_scaled_SOC_pptt = system_real_SOC_pptt; + } +} + void update_values() { // Battery update_values_battery(); // Map the fake values to the correct registers @@ -660,5 +687,7 @@ void storeSettings() { settings.putUInt("MINPERCENTAGE", MINPERCENTAGE); settings.putUInt("MAXCHARGEAMP", MAXCHARGEAMP); settings.putUInt("MAXDISCHARGEAMP", MAXDISCHARGEAMP); + settings.putBool("USE_SCALED_SOC", USE_SCALED_SOC); + settings.end(); } diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp index bcc2a677..63fb6296 100644 --- a/Software/USER_SETTINGS.cpp +++ b/Software/USER_SETTINGS.cpp @@ -4,16 +4,17 @@ /* They can be defined here, or later on in the WebUI */ /* Battery settings */ -volatile uint16_t BATTERY_WH_MAX = - 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! +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 (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 = - 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 = - 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 = - 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 */ diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 53a762cf..be643f53 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -12,7 +12,7 @@ //#define CHADEMO_BATTERY //#define IMIEV_CZERO_ION_BATTERY //#define KIA_HYUNDAI_64_BATTERY -//#define NISSAN_LEAF_BATTERY +#define NISSAN_LEAF_BATTERY //#define RENAULT_KANGOO_BATTERY //#define RENAULT_ZOE_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 */ //#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 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 @@ -55,12 +55,13 @@ //#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 uint16_t BATTERY_WH_MAX; +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; 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 */ extern volatile float charger_setpoint_HV_VDC; diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp index 08ef5499..bc505676 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.cpp +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.cpp @@ -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 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 CalculatedSOC = 0; // Temporary value used for calculating SOC 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_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 */ /* 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 - 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 + system_real_SOC_pptt = (LB_SOC * 10); - 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_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 if (LEAF_Battery_Type == ZE0_BATTERY) { //Since we only have average value, send the minimum as -1.0 degrees below average - temperature_min = - convert2unsignedint16((LB_AverageTemperature * 10) - 10); //add sign if negative and increase range - temperature_max = convert2unsignedint16((LB_AverageTemperature * 10)); + 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 } else if (LEAF_Battery_Type == AZE0_BATTERY) { //Use the value sent constantly via CAN in 5C0 (only available on AZE0) - temperature_min = - convert2unsignedint16((LB_HistData_Temperature_MIN * 10)); //add sign if negative and increase range - temperature_max = convert2unsignedint16((LB_HistData_Temperature_MAX * 10)); + 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 } 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 - temperature_min = convert2unsignedint16((temp_polled_min)); - temperature_max = convert2unsignedint16((temp_polled_max)); + system_temperature_min_dC = temp_polled_min; + system_temperature_max_dC = temp_polled_max; } else { - temperature_min = convert2unsignedint16((temp_polled_max)); - temperature_max = convert2unsignedint16((temp_polled_min)); + system_temperature_min_dC = temp_polled_max; + system_temperature_max_dC = temp_polled_min; } } } // Define power able to be discharged 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 + if (LB_Discharge_Power_Limit > 30) { //if >30kW can be pulled from battery + system_max_discharge_power_W = 30000; //cap value so we don't go over the Fronius limits } 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 - max_target_discharge_power = 0; + if (system_scaled_SOC_pptt == 0) { //Scaled SOC% value is 0.00%, we should not discharge battery further + system_max_discharge_power_W = 0; } // Define power able to 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 + if (LB_Charge_Power_Limit > 30) { //if >30kW can be put into the battery + system_max_charge_power_W = 30000; //cap value so we don't go over the Fronius limits } 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 for (int i = 0; i < 96; ++i) { - cellvoltages[i] = cell_voltages[i]; + system_cellvoltages_mV[i] = cell_voltages[i]; } /*Extra safety functions below*/ if (LB_GIDS < 6) //500Wh left in battery { //Battery is running abnormally low, some discharge logic might have failed. Zero it all out. set_event(EVENT_BATTERY_EMPTY, 0); - SOC = 0; - max_target_discharge_power = 0; + system_real_SOC_pptt = 0; + system_max_discharge_power_W = 0; } //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) { set_event(EVENT_SOC_PLAUSIBILITY_ERROR, LB_SOC / 10); // Set event with the SOC as data } 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 set_event(EVENT_BATTERY_FULL, 0); - max_target_charge_power = 0; + system_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); - max_target_discharge_power = 0; + system_max_discharge_power_W = 0; } else { clear_event(EVENT_BATTERY_EMPTY); } @@ -285,8 +274,8 @@ void update_values_battery() { /* This function maps all the values fetched via #endif //Note, this is sometimes triggered during the night while idle, and the BMS recovers after a while. Removed latching from this scenario errorCode = 1; - max_target_discharge_power = 0; - max_target_charge_power = 0; + system_max_discharge_power_W = 0; + system_max_charge_power_W = 0; } 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); } - if (bms_status == FAULT) { //Incase we enter a critical fault state, zero out the allowed limits - max_target_charge_power = 0; - max_target_discharge_power = 0; + 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; } /*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("Values going to inverter"); - print_with_units("SOH%: ", (StateOfHealth * 0.01), "% "); - print_with_units(", SOC% scaled: ", (SOC * 0.01), "% "); - print_with_units(", Voltage: ", (battery_voltage * 0.1), "V "); - print_with_units(", Max discharge power: ", max_target_discharge_power, "W "); - print_with_units(", Max charge power: ", max_target_charge_power, "W "); - print_with_units(", Max temp: ", ((int16_t)temperature_max * 0.1), "°C "); - print_with_units(", Min temp: ", ((int16_t)temperature_min * 0.1), "°C "); + print_with_units("SOH%: ", (system_SOH_pptt * 0.01), "% "); + print_with_units(", SOC% scaled: ", (system_scaled_SOC_pptt * 0.01), "% "); + print_with_units(", Voltage: ", (system_battery_voltage_dV * 0.1), "V "); + print_with_units(", Max discharge power: ", system_max_discharge_power_W, "W "); + print_with_units(", Max charge power: ", system_max_charge_power_W, "W "); + print_with_units(", Max temp: ", (system_temperature_max_dC * 0.1), "°C "); + print_with_units(", Min temp: ", (system_temperature_min_dC * 0.1), "°C "); Serial.println(""); Serial.print("BMS Status: "); - if (bms_status == 3) { + if (system_bms_status == 3) { Serial.print("Active, "); } else { 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_max_voltage = min_max_voltage[1]; - cell_min_voltage = min_max_voltage[0]; + system_cell_max_voltage_mV = min_max_voltage[1]; + system_cell_min_voltage_mV = min_max_voltage[0]; if (cell_deviation_mV > MAX_CELL_DEVIATION) { 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) { uint8_t crc = 0; 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 Serial.println("Nissan LEAF battery selected"); - nof_cellvoltages = 96; - max_voltage = 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_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 = 2450; // 245.0V under this, discharging further is disabled } #endif diff --git a/Software/src/battery/NISSAN-LEAF-BATTERY.h b/Software/src/battery/NISSAN-LEAF-BATTERY.h index 1d3ad7d1..d437ed73 100644 --- a/Software/src/battery/NISSAN-LEAF-BATTERY.h +++ b/Software/src/battery/NISSAN-LEAF-BATTERY.h @@ -8,26 +8,27 @@ #define BATTERY_SELECTED // These parameters need to be mapped for the inverter -extern uint16_t max_voltage; //V+1, 0-500.0 (0-5000) -extern uint16_t min_voltage; //V+1, 0-500.0 (0-5000) -extern uint16_t SOC; //SOC%, 0-100.00 (0-10000) -extern uint16_t StateOfHealth; //SOH%, 0-100.00 (0-10000) -extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) -extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) -extern uint16_t capacity_Wh; //Wh, 0-60000 -extern uint16_t remaining_capacity_Wh; //Wh, 0-60000 -extern uint16_t max_target_discharge_power; //W, 0-60000 -extern uint16_t max_target_charge_power; //W, 0-60000 -extern uint16_t stat_batt_power; //W, Goes thru convert2unsignedint16 function (5W = 5, -5W = 65530) -extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) -extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) -extern uint16_t cell_max_voltage; //mV, 0-4350 -extern uint16_t cell_min_voltage; //mV, 0-4350 -extern uint16_t cellvoltages[120]; //mV 0-4350 per cell -extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery. -extern bool batteryAllowsContactorClosing; //Bool, 1=true, 0=false +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 int16_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-500.0 (0-5000) +extern uint16_t system_max_design_voltage_dV; //V+1, 0-500.0 (0-5000) +extern uint16_t system_min_design_voltage_dV; //V+1, 0-500.0 (0-5000) +extern uint16_t 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_max_discharge_power_W; //W, 0-65000 +extern uint16_t system_max_charge_power_W; //W, 0-65000 +extern uint16_t system_cell_max_voltage_mV; //mV, 0-5000, Stores the highest cell millivolt value +extern uint16_t system_cell_min_voltage_mV; //mV, 0-5000, Stores the minimum cell millivolt value +extern uint16_t system_cellvoltages_mV[120]; //Array with all cell voltages in mV +extern uint8_t system_number_of_cells; //Total number of cell voltages, set by each battery +extern uint8_t system_bms_status; //Enum 0-5 +extern bool batteryAllowsContactorClosing; //Bool, true/false -uint16_t convert2unsignedint16(int16_t signed_value); uint16_t Temp_fromRAW_to_F(uint16_t temperature); bool is_message_corrupt(CAN_frame_t rx_frame); void setup_battery(void); diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index f755420f..68045f2a 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -30,7 +30,7 @@ static void publish_cell_voltages(void) { static bool mqtt_first_transmission = true; // If the cell voltage number isn't initialized... - if (nof_cellvoltages == 0u) { + if (system_number_of_cells == 0u) { return; } // 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" 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 // Probably shouldn't be BatteryEmulator here, instead "LeafBattery" // or similar but hey, it works. @@ -83,15 +83,15 @@ static void publish_cell_voltages(void) { // is the string content // If cell voltages haven't been populated... - if (nof_cellvoltages == 0u) { + if (system_number_of_cells == 0u) { return; } } 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) ? "" : ", ", - ((float)cellvoltages[i]) / 1000); + ((float)system_cellvoltages_mV[i]) / 1000); } snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "]\n}\n"); @@ -170,20 +170,21 @@ static void publish_common_info(void) { } else { snprintf(mqtt_msg, sizeof(mqtt_msg), "{\n" - " \"SOC\": %.3f,\n" - " \"state_of_health\": %.3f,\n" - " \"temperature_min\": %.3f,\n" - " \"temperature_max\": %.3f,\n" - " \"stat_batt_power\": %.3f,\n" - " \"battery_current\": %.3f,\n" - " \"cell_max_voltage\": %.3f,\n" - " \"cell_min_voltage\": %.3f,\n" - " \"battery_voltage\": %d\n" + " \"system_scaled_SOC_pptt\": %.3f,\n" + " \"system_SOH_pptt\": %.3f,\n" + " \"system_temperature_min_dC\": %.3f,\n" + " \"system_temperature_max_dC\": %.3f,\n" + " \"system_active_power_W\": %.3f,\n" + " \"system_battery_current_dA\": %.3f,\n" + " \"system_cell_max_voltage_mV\": %.3f,\n" + " \"system_cell_min_voltage_mV\": %.3f,\n" + " \"system_battery_voltage_dV\": %d\n" "}\n", - ((float)SOC) / 100.0, ((float)StateOfHealth) / 100.0, ((float)((int16_t)temperature_min)) / 10.0, - ((float)((int16_t)temperature_max)) / 10.0, ((float)((int16_t)stat_batt_power)), - ((float)((int16_t)battery_current)) / 10.0, ((float)cell_max_voltage) / 1000, - ((float)cell_min_voltage) / 1000, battery_voltage / 10.0); + ((float)system_scaled_SOC_pptt) / 100.0, ((float)system_SOH_pptt) / 100.0, + ((float)((int16_t)system_temperature_min_dC)) / 10.0, ((float)((int16_t)system_temperature_max_dC)) / 10.0, + ((float)((int16_t)system_active_power_W)), ((float)((int16_t)system_battery_current_dA)) / 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); } diff --git a/Software/src/devboard/mqtt/mqtt.h b/Software/src/devboard/mqtt/mqtt.h index b7b58c8f..60c9db7b 100644 --- a/Software/src/devboard/mqtt/mqtt.h +++ b/Software/src/devboard/mqtt/mqtt.h @@ -41,16 +41,18 @@ extern const char* version_number; // The current software version, used for mqtt -extern uint16_t SOC; -extern uint16_t StateOfHealth; -extern uint16_t temperature_min; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) -extern uint16_t temperature_max; //C+1, Goes thru convert2unsignedint16 function (15.0C = 150, -15.0C = 65385) -extern uint16_t cell_max_voltage; //mV, 0-4350 -extern uint16_t cell_min_voltage; //mV, 0-4350 -extern uint16_t cellvoltages[120]; //mV 0-4350 per cell -extern uint8_t nof_cellvoltages; // Total number of cell voltages, set by each battery. -extern uint16_t battery_voltage; //V+1, 0-500.0 (0-5000) -extern uint16_t battery_current; //A+1, Goes thru convert2unsignedint16 function (5.0A = 50, -5.0A = 65485) +extern int16_t system_temperature_min_dC; //C+1, -50.0 - 50.0 +extern int16_t system_temperature_max_dC; //C+1, -50.0 - 50.0 +extern int16_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-500.0 (0-5000) +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[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_password; diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index 58c45f56..1a6e17d7 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -299,13 +299,13 @@ static void update_bms_status(void) { case EVENT_LEVEL_INFO: case EVENT_LEVEL_WARNING: case EVENT_LEVEL_DEBUG: - bms_status = ACTIVE; + system_bms_status = ACTIVE; break; case EVENT_LEVEL_UPDATE: - bms_status = UPDATING; + system_bms_status = UPDATING; break; case EVENT_LEVEL_ERROR: - bms_status = FAULT; + system_bms_status = FAULT; break; default: break; diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h index 83b0f499..e2b922e7 100644 --- a/Software/src/devboard/utils/events.h +++ b/Software/src/devboard/utils/events.h @@ -99,6 +99,6 @@ void run_event_handling(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__ diff --git a/Software/src/devboard/utils/events_test_on_target.cpp b/Software/src/devboard/utils/events_test_on_target.cpp index 0af89304..3ecf0ee0 100644 --- a/Software/src/devboard/utils/events_test_on_target.cpp +++ b/Software/src/devboard/utils/events_test_on_target.cpp @@ -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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_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("bms_status: "); - Serial.println(bms_status); + Serial.print("system_bms_status: "); + Serial.println(system_bms_status); } break; case ETOT_DONE: diff --git a/Software/src/devboard/webserver/cellmonitor_html.cpp b/Software/src/devboard/webserver/cellmonitor_html.cpp index 1051e87c..627fef3d 100644 --- a/Software/src/devboard/webserver/cellmonitor_html.cpp +++ b/Software/src/devboard/webserver/cellmonitor_html.cpp @@ -18,9 +18,9 @@ String cellmonitor_processor(const String& var) { // Display max, min, and deviation voltage values content += "