Feature: know and use SoC precision
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 #573.
This commit is contained in:
parent
7c069b1cc4
commit
6df358242c
@ -17,7 +17,7 @@ class BatteryStats {
|
|||||||
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
|
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
|
||||||
bool updateAvailable(uint32_t since) const { return _lastUpdate > since; }
|
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; }
|
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }
|
||||||
|
|
||||||
float getVoltage() const { return _voltage; }
|
float getVoltage() const { return _voltage; }
|
||||||
@ -36,18 +36,26 @@ class BatteryStats {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void mqttPublish() const;
|
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) {
|
void setVoltage(float voltage, uint32_t timestamp) {
|
||||||
_voltage = voltage;
|
_voltage = voltage;
|
||||||
_lastUpdateVoltage = timestamp;
|
_lastUpdateVoltage = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _manufacturer = "unknown";
|
String _manufacturer = "unknown";
|
||||||
uint8_t _SoC = 0;
|
|
||||||
uint32_t _lastUpdateSoC = 0;
|
|
||||||
uint32_t _lastUpdate = 0;
|
uint32_t _lastUpdate = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t _lastMqttPublish = 0;
|
uint32_t _lastMqttPublish = 0;
|
||||||
|
float _soc = 0;
|
||||||
|
uint8_t _socPrecision = 0; // decimal places
|
||||||
|
uint32_t _lastUpdateSoC = 0;
|
||||||
float _voltage = 0; // total battery pack voltage
|
float _voltage = 0; // total battery pack voltage
|
||||||
uint32_t _lastUpdateVoltage = 0;
|
uint32_t _lastUpdateVoltage = 0;
|
||||||
};
|
};
|
||||||
@ -61,7 +69,6 @@ class PylontechBatteryStats : public BatteryStats {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void setManufacturer(String&& m) { _manufacturer = std::move(m); }
|
void setManufacturer(String&& m) { _manufacturer = std::move(m); }
|
||||||
void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = millis(); }
|
|
||||||
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
|
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
|
||||||
|
|
||||||
float _chargeVoltage;
|
float _chargeVoltage;
|
||||||
@ -155,9 +162,7 @@ class MqttBatteryStats : public BatteryStats {
|
|||||||
// we do NOT publish the same data under a different topic.
|
// we do NOT publish the same data under a different topic.
|
||||||
void mqttPublish() const final { }
|
void mqttPublish() const final { }
|
||||||
|
|
||||||
// the SoC is the only interesting value in this case, which is already
|
// if the voltage is subscribed to at all, it alone does not warrant a
|
||||||
// displayed at the top of the live view. do not generate a card.
|
// card in the live view, since the SoC is already displayed at the top
|
||||||
void getLiveViewData(JsonVariant& root) const final { }
|
void getLiveViewData(JsonVariant& root) const final { }
|
||||||
|
|
||||||
void setSoC(uint8_t SoC) { _SoC = SoC; _lastUpdateSoC = _lastUpdate = millis(); }
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -56,7 +56,7 @@ void BatteryStats::getLiveViewData(JsonVariant& root) const
|
|||||||
root[F("manufacturer")] = _manufacturer;
|
root[F("manufacturer")] = _manufacturer;
|
||||||
root[F("data_age")] = getAgeSeconds();
|
root[F("data_age")] = getAgeSeconds();
|
||||||
|
|
||||||
addLiveViewValue(root, "SoC", _SoC, "%", 0);
|
addLiveViewValue(root, "SoC", _soc, "%", _socPrecision);
|
||||||
addLiveViewValue(root, "voltage", _voltage, "V", 2);
|
addLiveViewValue(root, "voltage", _voltage, "V", 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +212,7 @@ void BatteryStats::mqttPublish() const
|
|||||||
{
|
{
|
||||||
MqttSettings.publish(F("battery/manufacturer"), _manufacturer);
|
MqttSettings.publish(F("battery/manufacturer"), _manufacturer);
|
||||||
MqttSettings.publish(F("battery/dataAge"), String(getAgeSeconds()));
|
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));
|
MqttSettings.publish(F("battery/voltage"), String(_voltage));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,9 +334,9 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)
|
|||||||
|
|
||||||
auto oSoCValue = dp.get<Label::BatterySoCPercent>();
|
auto oSoCValue = dp.get<Label::BatterySoCPercent>();
|
||||||
if (oSoCValue.has_value()) {
|
if (oSoCValue.has_value()) {
|
||||||
_SoC = *oSoCValue;
|
|
||||||
auto oSoCDataPoint = dp.getDataPointFor<Label::BatterySoCPercent>();
|
auto oSoCDataPoint = dp.getDataPointFor<Label::BatterySoCPercent>();
|
||||||
_lastUpdateSoC = oSoCDataPoint->getTimestamp();
|
BatteryStats::setSoC(*oSoCValue, 0/*precision*/,
|
||||||
|
oSoCDataPoint->getTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto oVoltage = dp.get<Label::BatteryVoltageMilliVolt>();
|
auto oVoltage = dp.get<Label::BatteryVoltageMilliVolt>();
|
||||||
@ -367,8 +367,8 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)
|
|||||||
|
|
||||||
void VictronSmartShuntStats::updateFrom(VeDirectShuntController::veShuntStruct const& shuntData) {
|
void VictronSmartShuntStats::updateFrom(VeDirectShuntController::veShuntStruct const& shuntData) {
|
||||||
BatteryStats::setVoltage(shuntData.V, millis());
|
BatteryStats::setVoltage(shuntData.V, millis());
|
||||||
|
BatteryStats::setSoC(static_cast<float>(shuntData.SOC) / 10, 1/*precision*/, millis());
|
||||||
|
|
||||||
_SoC = shuntData.SOC / 10;
|
|
||||||
_current = shuntData.I;
|
_current = shuntData.I;
|
||||||
_modelName = shuntData.getPidAsString().data();
|
_modelName = shuntData.getPidAsString().data();
|
||||||
_chargeCycles = shuntData.H4;
|
_chargeCycles = shuntData.H4;
|
||||||
@ -387,7 +387,6 @@ void VictronSmartShuntStats::updateFrom(VeDirectShuntController::veShuntStruct c
|
|||||||
_alarmHighTemperature = shuntData.AR & 64;
|
_alarmHighTemperature = shuntData.AR & 64;
|
||||||
|
|
||||||
_lastUpdate = VeDirectShunt.getLastUpdate();
|
_lastUpdate = VeDirectShunt.getLastUpdate();
|
||||||
_lastUpdateSoC = VeDirectShunt.getLastUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const {
|
void VictronSmartShuntStats::getLiveViewData(JsonVariant& root) const {
|
||||||
|
|||||||
@ -82,7 +82,7 @@ void MqttBattery::onMqttMessageSoC(espMqttClientTypes::MessageProperties const&
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stats->setSoC(static_cast<uint8_t>(*soc));
|
_stats->setSoC(*soc, 0/*precision*/, millis());
|
||||||
|
|
||||||
if (_verboseLogging) {
|
if (_verboseLogging) {
|
||||||
MessageOutput.printf("MqttBattery: Updated SoC to %d from '%s'\r\n",
|
MessageOutput.printf("MqttBattery: Updated SoC to %d from '%s'\r\n",
|
||||||
|
|||||||
@ -136,7 +136,7 @@ void PylontechCanReceiver::loop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 0x355: {
|
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);
|
_stats->_stateOfHealth = this->readUnsignedInt16(rx_message.data + 2);
|
||||||
|
|
||||||
if (_verboseLogging) {
|
if (_verboseLogging) {
|
||||||
@ -282,7 +282,7 @@ void PylontechCanReceiver::dummyData()
|
|||||||
};
|
};
|
||||||
|
|
||||||
_stats->setManufacturer("Pylontech US3000C");
|
_stats->setManufacturer("Pylontech US3000C");
|
||||||
_stats->setSoC(42);
|
_stats->setSoC(42, 0/*precision*/, millis());
|
||||||
_stats->_chargeVoltage = dummyFloat(50);
|
_stats->_chargeVoltage = dummyFloat(50);
|
||||||
_stats->_chargeCurrentLimitation = dummyFloat(33);
|
_stats->_chargeCurrentLimitation = dummyFloat(33);
|
||||||
_stats->_dischargeCurrentLimitation = dummyFloat(12);
|
_stats->_dischargeCurrentLimitation = dummyFloat(12);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user