Skip to content

Commit

Permalink
Feature: know and use SoC precision
Browse files Browse the repository at this point in the history
the Victron SmartShunt communicates the SoC value in permille. this
should be displayed in the web UI accordingly. this is a good excuse to
fully move ownership of the SoC value to the BatteryStats base class and
add a precision indicator variable. this is required to be set each time
a derived class (a battery provider) wants to update the SoC value. the
precision is then used when populating the JSON data for the web UI
(live view).

related to helgeerbe#573.
  • Loading branch information
schlimmchen committed Feb 18, 2024
1 parent 7394275 commit 3a39a50
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 17 deletions.
21 changes: 13 additions & 8 deletions include/BatteryStats.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class BatteryStats {
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
bool updateAvailable(uint32_t since) const { return _lastUpdate > since; }

uint8_t getSoC() const { return _SoC; }
uint8_t getSoC() const { return _soc; }
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }

float getVoltage() const { return _voltage; }
Expand All @@ -36,18 +36,26 @@ class BatteryStats {

protected:
virtual void mqttPublish() const;

void setSoC(float soc, uint8_t precision, uint32_t timestamp) {
_soc = soc;
_socPrecision = precision;
_lastUpdateSoC = timestamp;
}

void setVoltage(float voltage, uint32_t timestamp) {
_voltage = voltage;
_lastUpdateVoltage = timestamp;
}

String _manufacturer = "unknown";
uint8_t _SoC = 0;
uint32_t _lastUpdateSoC = 0;
uint32_t _lastUpdate = 0;

private:
uint32_t _lastMqttPublish = 0;
float _soc = 0;
uint8_t _socPrecision = 0; // decimal places
uint32_t _lastUpdateSoC = 0;
float _voltage = 0; // total battery pack voltage
uint32_t _lastUpdateVoltage = 0;
};
Expand All @@ -61,7 +69,6 @@ class PylontechBatteryStats : public BatteryStats {

private:
void setManufacturer(String&& m) { _manufacturer = std::move(m); }
void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = millis(); }
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }

float _chargeVoltage;
Expand Down Expand Up @@ -155,9 +162,7 @@ class MqttBatteryStats : public BatteryStats {
// we do NOT publish the same data under a different topic.
void mqttPublish() const final { }

// the SoC is the only interesting value in this case, which is already
// displayed at the top of the live view. do not generate a card.
// if the voltage is subscribed to at all, it alone does not warrant a
// card in the live view, since the SoC is already displayed at the top
void getLiveViewData(JsonVariant& root) const final { }

void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = _lastUpdate = millis(); }
};
11 changes: 5 additions & 6 deletions src/BatteryStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ void BatteryStats::getLiveViewData(JsonVariant& root) const
root[F("manufacturer")] = _manufacturer;
root[F("data_age")] = getAgeSeconds();

addLiveViewValue(root, "SoC", _SoC, "%", 0);
addLiveViewValue(root, "SoC", _soc, "%", _socPrecision);
addLiveViewValue(root, "voltage", _voltage, "V", 2);
}

Expand Down Expand Up @@ -212,7 +212,7 @@ void BatteryStats::mqttPublish() const
{
MqttSettings.publish(F("battery/manufacturer"), _manufacturer);
MqttSettings.publish(F("battery/dataAge"), String(getAgeSeconds()));
MqttSettings.publish(F("battery/stateOfCharge"), String(_SoC));
MqttSettings.publish(F("battery/stateOfCharge"), String(_soc));
MqttSettings.publish(F("battery/voltage"), String(_voltage));
}

Expand Down Expand Up @@ -334,9 +334,9 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)

auto oSoCValue = dp.get<Label::BatterySoCPercent>();
if (oSoCValue.has_value()) {
_SoC = *oSoCValue;
auto oSoCDataPoint = dp.getDataPointFor<Label::BatterySoCPercent>();
_lastUpdateSoC = oSoCDataPoint->getTimestamp();
BatteryStats::setSoC(*oSoCValue, 0/*precision*/,
oSoCDataPoint->getTimestamp());
}

auto oVoltage = dp.get<Label::BatteryVoltageMilliVolt>();
Expand Down Expand Up @@ -367,8 +367,8 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)

void VictronSmartShuntStats::updateFrom(VeDirectShuntController::veShuntStruct const& shuntData) {
BatteryStats::setVoltage(shuntData.V, millis());
BatteryStats::setSoC(static_cast<float>(shuntData.SOC) / 10, 1/*precision*/, millis());

_SoC = shuntData.SOC / 10;
_current = shuntData.I;
_modelName = shuntData.getPidAsString().data();
_chargeCycles = shuntData.H4;
Expand All @@ -387,7 +387,6 @@ void VictronSmartShuntStats::updateFrom(VeDirectShuntController::veShuntStruct c
_alarmHighTemperature = shuntData.AR & 64;

_lastUpdate = VeDirectShunt.getLastUpdate();
_lastUpdateSoC = VeDirectShunt.getLastUpdate();
}

void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const {
Expand Down
2 changes: 1 addition & 1 deletion src/MqttBattery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ void MqttBattery::onMqttMessageSoC(espMqttClientTypes::MessageProperties const&
return;
}

_stats->setSoC(static_cast<uint8_t>(*soc));
_stats->setSoC(*soc, 0/*precision*/, millis());

if (_verboseLogging) {
MessageOutput.printf("MqttBattery: Updated SoC to %d from '%s'\r\n",
Expand Down
4 changes: 2 additions & 2 deletions src/PylontechCanReceiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ void PylontechCanReceiver::loop()
}

case 0x355: {
_stats->setSoC(static_cast<uint8_t>(this->readUnsignedInt16(rx_message.data)));
_stats->setSoC(static_cast<uint8_t>(this->readUnsignedInt16(rx_message.data)), 0/*precision*/, millis());
_stats->_stateOfHealth = this->readUnsignedInt16(rx_message.data + 2);

if (_verboseLogging) {
Expand Down Expand Up @@ -282,7 +282,7 @@ void PylontechCanReceiver::dummyData()
};

_stats->setManufacturer("Pylontech US3000C");
_stats->setSoC(42);
_stats->setSoC(42, 0/*precision*/, millis());
_stats->_chargeVoltage = dummyFloat(50);
_stats->_chargeCurrentLimitation = dummyFloat(33);
_stats->_dischargeCurrentLimitation = dummyFloat(12);
Expand Down

0 comments on commit 3a39a50

Please sign in to comment.