Add reported_total_capacity_Wh and use it for SOLAX. Cleanup scaled values on webpage (#1002)

* Add reported_total_capacity_Wh and soh to SOLAX messages.
* Improve display of scaled values on webpage.
* Additionally also improve display of temperatures, cell voltages and combine voltage and current on one line.
* Refactor formatPowerValue function.
This commit is contained in:
Marijn van Galen 2025-03-27 21:29:12 +01:00 committed by GitHub
parent 63222b4c77
commit 97c942e313
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 79 additions and 35 deletions

View file

@ -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;

View file

@ -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 */

View file

@ -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 += "<h4 style='color: white;'>Real SOC: " + String(socRealFloat, 2) + "&percnt;</h4>";
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) + "&percnt;</h4>";
if (datalayer.battery.settings.soc_scaling_active)
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) +
"&percnt; (real: " + String(socRealFloat, 2) + "&percnt;)</h4>";
else
content += "<h4 style='color: white;'>SOC: " + String(socRealFloat, 2) + "&percnt;</h4>";
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "&percnt;</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;'>Voltage: " + String(voltageFloat, 1) +
" V &nbsp; Current: " + String(currentFloat, 1) + " A</h4>";
content += formatPowerValue("Power", powerFloat, "", 1);
if (datalayer.battery.settings.soc_scaling_active)
content += "<h4 style='color: white;'>Scaled total capacity: " +
formatPowerValue(datalayer.battery.info.reported_total_capacity_Wh, "h", 1) +
" (real: " + formatPowerValue(datalayer.battery.info.total_capacity_Wh, "h", 1) + ")</h4>";
else
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 += "<h4 style='color: white;'>Scaled remaining capacity: " +
formatPowerValue(datalayer.battery.status.reported_remaining_capacity_Wh, "h", 1) +
" (real: " + formatPowerValue(datalayer.battery.status.remaining_capacity_Wh, "h", 1) + ")</h4>";
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 += "<h4>Cell max: " + String(datalayer.battery.status.cell_max_voltage_mV) + " mV</h4>";
content += "<h4>Cell min: " + String(datalayer.battery.status.cell_min_voltage_mV) + " mV</h4>";
content += "<h4>Cell min/max: " + String(datalayer.battery.status.cell_min_voltage_mV) + " mV / " +
String(datalayer.battery.status.cell_max_voltage_mV) + " mV</h4>";
if (cell_delta_mv > datalayer.battery.info.max_cell_voltage_deviation_mV) {
content += "<h4 style='color: red;'>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
} else {
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
}
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " &deg;C</h4>";
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " &deg;C</h4>";
content +=
"<h4>Temperature min/max: " + String(tempMinFloat, 1) + " &deg;C / " + String(tempMaxFloat, 1) + " &deg;C</h4>";
content += "<h4>System status: ";
switch (datalayer.battery.status.bms_status) {
@ -1261,16 +1275,30 @@ String processor(const String& var) {
tempMinFloat = static_cast<float>(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 += "<h4 style='color: white;'>Real SOC: " + String(socRealFloat, 2) + "&percnt;</h4>";
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) + "&percnt;</h4>";
if (datalayer.battery.settings.soc_scaling_active)
content += "<h4 style='color: white;'>Scaled SOC: " + String(socScaledFloat, 2) +
"&percnt; (real: " + String(socRealFloat, 2) + "&percnt;)</h4>";
else
content += "<h4 style='color: white;'>SOC: " + String(socRealFloat, 2) + "&percnt;</h4>";
content += "<h4 style='color: white;'>SOH: " + String(sohFloat, 2) + "&percnt;</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;'>Voltage: " + String(voltageFloat, 1) +
" V &nbsp; Current: " + String(currentFloat, 1) + " A</h4>";
content += formatPowerValue("Power", powerFloat, "", 1);
if (datalayer.battery.settings.soc_scaling_active)
content += "<h4 style='color: white;'>Scaled total capacity: " +
formatPowerValue(datalayer.battery2.info.reported_total_capacity_Wh, "h", 1) +
" (real: " + formatPowerValue(datalayer.battery2.info.total_capacity_Wh, "h", 1) + ")</h4>";
else
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 += "<h4 style='color: white;'>Scaled remaining capacity: " +
formatPowerValue(datalayer.battery2.status.reported_remaining_capacity_Wh, "h", 1) +
" (real: " + formatPowerValue(datalayer.battery2.status.remaining_capacity_Wh, "h", 1) + ")</h4>";
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 += "<h4 style='color: white;'>Max charge current: " + String(maxCurrentChargeFloat, 1) + " A</h4>";
}
content += "<h4>Cell max: " + String(datalayer.battery2.status.cell_max_voltage_mV) + " mV</h4>";
content += "<h4>Cell min: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV</h4>";
content += "<h4>Cell min/max: " + String(datalayer.battery2.status.cell_min_voltage_mV) + " mV / " +
String(datalayer.battery2.status.cell_max_voltage_mV) + " mV</h4>";
if (cell_delta_mv > datalayer.battery2.info.max_cell_voltage_deviation_mV) {
content += "<h4 style='color: red;'>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
} else {
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
}
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " &deg;C</h4>";
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " &deg;C</h4>";
content +=
"<h4>Temperature min/max: " + String(tempMinFloat, 1) + " &deg;C / " + String(tempMaxFloat, 1) + " &deg;C</h4>";
if (datalayer.battery.status.bms_status == ACTIVE) {
content += "<h4>System status: OK </h4>";
} else if (datalayer.battery.status.bms_status == UPDATING) {
@ -1564,6 +1592,13 @@ void onOTAEnd(bool success) {
template <typename T> // 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 = "<h4 style='color: " + color + ";'>" + label + ": ";
result += formatPowerValue(value, unit, precision);
result += "</h4>";
return result;
}
template <typename T> // 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<T, float>::value || std::is_same<T, uint16_t>::value || std::is_same<T, uint32_t>::value) {
float convertedValue = static_cast<float>(value);
@ -1575,6 +1610,6 @@ String formatPowerValue(String label, T value, String unit, int precision, Strin
}
}
result += unit + "</h4>";
result += unit;
return result;
}

View file

@ -103,6 +103,9 @@ void onOTAEnd(bool success);
template <typename T>
String formatPowerValue(String label, T value, String unit, int precision, String color = "white");
template <typename T> // 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();

View file

@ -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);
}