diff --git a/Software/Software.ino b/Software/Software.ino index 6b457798..b458b49c 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -462,6 +462,10 @@ void update_calculated_values() { } else { datalayer.battery.status.reported_remaining_capacity_Wh = 0; } + datalayer.battery.info.reported_total_capacity_Wh = + (datalayer.battery.info.total_capacity_Wh * + (datalayer.battery.settings.max_percentage - datalayer.battery.settings.min_percentage)) / + 10000; } else { datalayer.battery.status.reported_remaining_capacity_Wh = datalayer.battery.status.remaining_capacity_Wh; diff --git a/Software/src/datalayer/datalayer.h b/Software/src/datalayer/datalayer.h index 6122c8f9..e9fc9867 100644 --- a/Software/src/datalayer/datalayer.h +++ b/Software/src/datalayer/datalayer.h @@ -7,6 +7,7 @@ typedef struct { /** uint32_t */ /** Total energy capacity in Watt-hours */ uint32_t total_capacity_Wh = BATTERY_WH_MAX; + uint32_t reported_total_capacity_Wh = BATTERY_WH_MAX; /** uint16_t */ /** The maximum intended packvoltage, in deciVolt. 4900 = 490.0 V */ diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 78224a60..51b65288 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1054,16 +1054,30 @@ String processor(const String& var) { uint16_t cell_delta_mv = datalayer.battery.status.cell_max_voltage_mV - datalayer.battery.status.cell_min_voltage_mV; - content += "

Real SOC: " + String(socRealFloat, 2) + "%

"; - content += "

Scaled SOC: " + String(socScaledFloat, 2) + "%

"; + if (datalayer.battery.settings.soc_scaling_active) + content += "

Scaled SOC: " + String(socScaledFloat, 2) + + "% (real: " + String(socRealFloat, 2) + "%)

"; + else + content += "

SOC: " + String(socRealFloat, 2) + "%

"; + content += "

SOH: " + String(sohFloat, 2) + "%

"; - content += "

Voltage: " + String(voltageFloat, 1) + " V

"; - content += "

Current: " + String(currentFloat, 1) + " A

"; + content += "

Voltage: " + String(voltageFloat, 1) + + " V   Current: " + String(currentFloat, 1) + " A

"; content += formatPowerValue("Power", powerFloat, "", 1); - content += formatPowerValue("Total capacity", datalayer.battery.info.total_capacity_Wh, "h", 1); - content += formatPowerValue("Real Remaining capacity", datalayer.battery.status.remaining_capacity_Wh, "h", 1); - content += - formatPowerValue("Scaled Remaining capacity", datalayer.battery.status.reported_remaining_capacity_Wh, "h", 1); + + if (datalayer.battery.settings.soc_scaling_active) + content += "

Scaled total capacity: " + + formatPowerValue(datalayer.battery.info.reported_total_capacity_Wh, "h", 1) + + " (real: " + formatPowerValue(datalayer.battery.info.total_capacity_Wh, "h", 1) + ")

"; + else + content += formatPowerValue("Total capacity", datalayer.battery.info.total_capacity_Wh, "h", 1); + + if (datalayer.battery.settings.soc_scaling_active) + content += "

Scaled remaining capacity: " + + formatPowerValue(datalayer.battery.status.reported_remaining_capacity_Wh, "h", 1) + + " (real: " + formatPowerValue(datalayer.battery.status.remaining_capacity_Wh, "h", 1) + ")

"; + else + content += formatPowerValue("Remaining capacity", datalayer.battery.status.remaining_capacity_Wh, "h", 1); if (datalayer.system.settings.equipment_stop_active) { content += formatPowerValue("Max discharge power", datalayer.battery.status.max_discharge_power_W, "", 1, "red"); @@ -1087,15 +1101,15 @@ String processor(const String& var) { } } - content += "

Cell max: " + String(datalayer.battery.status.cell_max_voltage_mV) + " mV

"; - content += "

Cell min: " + String(datalayer.battery.status.cell_min_voltage_mV) + " mV

"; + content += "

Cell min/max: " + String(datalayer.battery.status.cell_min_voltage_mV) + " mV / " + + String(datalayer.battery.status.cell_max_voltage_mV) + " mV

"; if (cell_delta_mv > datalayer.battery.info.max_cell_voltage_deviation_mV) { content += "

Cell delta: " + String(cell_delta_mv) + " mV

"; } else { content += "

Cell delta: " + String(cell_delta_mv) + " mV

"; } - content += "

Temperature max: " + String(tempMaxFloat, 1) + " °C

"; - content += "

Temperature min: " + String(tempMinFloat, 1) + " °C

"; + content += + "

Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) + " °C

"; content += "

System status: "; switch (datalayer.battery.status.bms_status) { @@ -1261,16 +1275,30 @@ String processor(const String& var) { tempMinFloat = static_cast(datalayer.battery2.status.temperature_min_dC) / 10.0; // Convert to float cell_delta_mv = datalayer.battery2.status.cell_max_voltage_mV - datalayer.battery2.status.cell_min_voltage_mV; - content += "

Real SOC: " + String(socRealFloat, 2) + "%

"; - content += "

Scaled SOC: " + String(socScaledFloat, 2) + "%

"; + if (datalayer.battery.settings.soc_scaling_active) + content += "

Scaled SOC: " + String(socScaledFloat, 2) + + "% (real: " + String(socRealFloat, 2) + "%)

"; + else + content += "

SOC: " + String(socRealFloat, 2) + "%

"; + content += "

SOH: " + String(sohFloat, 2) + "%

"; - content += "

Voltage: " + String(voltageFloat, 1) + " V

"; - content += "

Current: " + String(currentFloat, 1) + " A

"; + content += "

Voltage: " + String(voltageFloat, 1) + + " V   Current: " + String(currentFloat, 1) + " A

"; content += formatPowerValue("Power", powerFloat, "", 1); - content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 1); - content += formatPowerValue("Real Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1); - content += - formatPowerValue("Scaled Remaining capacity", datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1); + + if (datalayer.battery.settings.soc_scaling_active) + content += "

Scaled total capacity: " + + formatPowerValue(datalayer.battery2.info.reported_total_capacity_Wh, "h", 1) + + " (real: " + formatPowerValue(datalayer.battery2.info.total_capacity_Wh, "h", 1) + ")

"; + else + content += formatPowerValue("Total capacity", datalayer.battery2.info.total_capacity_Wh, "h", 1); + + if (datalayer.battery.settings.soc_scaling_active) + content += "

Scaled remaining capacity: " + + formatPowerValue(datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1) + + " (real: " + formatPowerValue(datalayer.battery2.status.remaining_capacity_Wh, "h", 1) + ")

"; + else + content += formatPowerValue("Remaining capacity", datalayer.battery2.status.remaining_capacity_Wh, "h", 1); if (datalayer.system.settings.equipment_stop_active) { content += formatPowerValue("Max discharge power", datalayer.battery2.status.max_discharge_power_W, "", 1, "red"); @@ -1284,15 +1312,15 @@ String processor(const String& var) { content += "

Max charge current: " + String(maxCurrentChargeFloat, 1) + " A

"; } - content += "

Cell max: " + String(datalayer.battery2.status.cell_max_voltage_mV) + " mV

"; - content += "

Cell min: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV

"; + content += "

Cell min/max: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV / " + + String(datalayer.battery2.status.cell_max_voltage_mV) + " mV

"; if (cell_delta_mv > datalayer.battery2.info.max_cell_voltage_deviation_mV) { content += "

Cell delta: " + String(cell_delta_mv) + " mV

"; } else { content += "

Cell delta: " + String(cell_delta_mv) + " mV

"; } - content += "

Temperature max: " + String(tempMaxFloat, 1) + " °C

"; - content += "

Temperature min: " + String(tempMinFloat, 1) + " °C

"; + content += + "

Temperature min/max: " + String(tempMinFloat, 1) + " °C / " + String(tempMaxFloat, 1) + " °C

"; if (datalayer.battery.status.bms_status == ACTIVE) { content += "

System status: OK

"; } else if (datalayer.battery.status.bms_status == UPDATING) { @@ -1564,6 +1592,13 @@ void onOTAEnd(bool success) { template // This function makes power values appear as W when under 1000, and kW when over String formatPowerValue(String label, T value, String unit, int precision, String color) { String result = "

" + label + ": "; + result += formatPowerValue(value, unit, precision); + result += "

"; + return result; +} +template // This function makes power values appear as W when under 1000, and kW when over +String formatPowerValue(T value, String unit, int precision) { + String result = ""; if (std::is_same::value || std::is_same::value || std::is_same::value) { float convertedValue = static_cast(value); @@ -1575,6 +1610,6 @@ String formatPowerValue(String label, T value, String unit, int precision, Strin } } - result += unit + "

"; + result += unit; return result; } diff --git a/Software/src/devboard/webserver/webserver.h b/Software/src/devboard/webserver/webserver.h index 3e2a71a1..62d8d90c 100644 --- a/Software/src/devboard/webserver/webserver.h +++ b/Software/src/devboard/webserver/webserver.h @@ -103,6 +103,9 @@ void onOTAEnd(bool success); template String formatPowerValue(String label, T value, String unit, int precision, String color = "white"); +template // This function makes power values appear as W when under 1000, and kW when over +String formatPowerValue(T value, String unit, int precision); + extern void store_settings(); void ota_monitor(); diff --git a/Software/src/inverter/SOLAX-CAN.cpp b/Software/src/inverter/SOLAX-CAN.cpp index 05675b1c..dec2b961 100644 --- a/Software/src/inverter/SOLAX-CAN.cpp +++ b/Software/src/inverter/SOLAX-CAN.cpp @@ -117,10 +117,10 @@ void update_values_can_inverter() { //This function maps all the values fetched ((datalayer.battery.status.temperature_max_dC + datalayer.battery.status.temperature_min_dC) / 2); // Batteries might be larger than uint16_t value can take - if (datalayer.battery.info.total_capacity_Wh > 65000) { + if (datalayer.battery.info.reported_total_capacity_Wh > 65000) { capped_capacity_Wh = 65000; } else { - capped_capacity_Wh = datalayer.battery.info.total_capacity_Wh; + capped_capacity_Wh = datalayer.battery.info.reported_total_capacity_Wh; } // Batteries might be larger than uint16_t value can take if (datalayer.battery.status.reported_remaining_capacity_Wh > 65000) { @@ -187,10 +187,10 @@ void update_values_can_inverter() { //This function maps all the values fetched SOLAX_1878.data.u8[0] = (uint8_t)(datalayer.battery.status.voltage_dV); SOLAX_1878.data.u8[1] = ((datalayer.battery.status.voltage_dV) >> 8); - SOLAX_1878.data.u8[4] = (uint8_t)datalayer.battery.info.total_capacity_Wh; - SOLAX_1878.data.u8[5] = (datalayer.battery.info.total_capacity_Wh >> 8); - SOLAX_1878.data.u8[6] = (datalayer.battery.info.total_capacity_Wh >> 16); - SOLAX_1878.data.u8[7] = (datalayer.battery.info.total_capacity_Wh >> 24); + SOLAX_1878.data.u8[4] = (uint8_t)datalayer.battery.info.reported_total_capacity_Wh; + SOLAX_1878.data.u8[5] = (datalayer.battery.info.reported_total_capacity_Wh >> 8); + SOLAX_1878.data.u8[6] = (datalayer.battery.info.reported_total_capacity_Wh >> 16); + SOLAX_1878.data.u8[7] = (datalayer.battery.info.reported_total_capacity_Wh >> 24); // BMS_Answer SOLAX_1801.data.u8[0] = 2; @@ -198,10 +198,11 @@ void update_values_can_inverter() { //This function maps all the values fetched SOLAX_1801.data.u8[4] = 1; //Ultra messages - SOLAX_187E.data.u8[0] = (uint8_t)datalayer.battery.info.total_capacity_Wh; - SOLAX_187E.data.u8[1] = (datalayer.battery.info.total_capacity_Wh >> 8); - SOLAX_187E.data.u8[2] = (datalayer.battery.info.total_capacity_Wh >> 16); - SOLAX_187E.data.u8[3] = (datalayer.battery.info.total_capacity_Wh >> 24); + SOLAX_187E.data.u8[0] = (uint8_t)datalayer.battery.info.reported_total_capacity_Wh; + SOLAX_187E.data.u8[1] = (datalayer.battery.info.reported_total_capacity_Wh >> 8); + SOLAX_187E.data.u8[2] = (datalayer.battery.info.reported_total_capacity_Wh >> 16); + SOLAX_187E.data.u8[3] = (datalayer.battery.info.reported_total_capacity_Wh >> 24); + SOLAX_187E.data.u8[4] = (uint8_t)(datalayer.battery.status.soh_pptt / 100); SOLAX_187E.data.u8[5] = (uint8_t)(datalayer.battery.status.reported_soc / 100); }