178 lines
5.6 KiB
C++
178 lines
5.6 KiB
C++
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
#pragma once
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "AsyncJson.h"
|
|
#include "Arduino.h"
|
|
#include "JkBmsDataPoints.h"
|
|
#include "VeDirectShuntController.h"
|
|
|
|
// mandatory interface for all kinds of batteries
|
|
class BatteryStats {
|
|
public:
|
|
String const& getManufacturer() const { return _manufacturer; }
|
|
|
|
// the last time *any* datum was updated
|
|
uint32_t getAgeSeconds() const { return (millis() - _lastUpdate) / 1000; }
|
|
bool updateAvailable(uint32_t since) const;
|
|
|
|
uint8_t getSoC() const { return _soc; }
|
|
uint32_t getSoCAgeSeconds() const { return (millis() - _lastUpdateSoC) / 1000; }
|
|
|
|
float getVoltage() const { return _voltage; }
|
|
uint32_t getVoltageAgeSeconds() const { return (millis() - _lastUpdateVoltage) / 1000; }
|
|
|
|
// convert stats to JSON for web application live view
|
|
virtual void getLiveViewData(JsonVariant& root) const;
|
|
|
|
void mqttLoop();
|
|
|
|
// the interval at which all battery datums will be re-published, even
|
|
// if they did not change. used to calculate Home Assistent expiration.
|
|
virtual uint32_t getMqttFullPublishIntervalMs() const;
|
|
|
|
bool isSoCValid() const { return _lastUpdateSoC > 0; }
|
|
bool isVoltageValid() const { return _lastUpdateVoltage > 0; }
|
|
|
|
// returns true if the battery reached a critically low voltage/SoC,
|
|
// such that it is in need of charging to prevent degredation.
|
|
virtual bool needsCharging() const { return false; }
|
|
|
|
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";
|
|
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;
|
|
};
|
|
|
|
class PylontechBatteryStats : public BatteryStats {
|
|
friend class PylontechCanReceiver;
|
|
|
|
public:
|
|
void getLiveViewData(JsonVariant& root) const final;
|
|
void mqttPublish() const final;
|
|
bool needsCharging() const final { return _chargeImmediately; }
|
|
|
|
private:
|
|
void setManufacturer(String&& m) { _manufacturer = std::move(m); }
|
|
void setLastUpdate(uint32_t ts) { _lastUpdate = ts; }
|
|
|
|
float _chargeVoltage;
|
|
float _chargeCurrentLimitation;
|
|
float _dischargeCurrentLimitation;
|
|
uint16_t _stateOfHealth;
|
|
// total current into (positive) or from (negative)
|
|
// the battery, i.e., the charging current
|
|
float _current;
|
|
float _temperature;
|
|
|
|
bool _alarmOverCurrentDischarge;
|
|
bool _alarmOverCurrentCharge;
|
|
bool _alarmUnderTemperature;
|
|
bool _alarmOverTemperature;
|
|
bool _alarmUnderVoltage;
|
|
bool _alarmOverVoltage;
|
|
bool _alarmBmsInternal;
|
|
|
|
bool _warningHighCurrentDischarge;
|
|
bool _warningHighCurrentCharge;
|
|
bool _warningLowTemperature;
|
|
bool _warningHighTemperature;
|
|
bool _warningLowVoltage;
|
|
bool _warningHighVoltage;
|
|
bool _warningBmsInternal;
|
|
|
|
bool _chargeEnabled;
|
|
bool _dischargeEnabled;
|
|
bool _chargeImmediately;
|
|
};
|
|
|
|
class JkBmsBatteryStats : public BatteryStats {
|
|
public:
|
|
void getLiveViewData(JsonVariant& root) const final {
|
|
getJsonData(root, false);
|
|
}
|
|
|
|
void getInfoViewData(JsonVariant& root) const {
|
|
getJsonData(root, true);
|
|
}
|
|
|
|
void mqttPublish() const final;
|
|
|
|
uint32_t getMqttFullPublishIntervalMs() const final { return 60 * 1000; }
|
|
|
|
void updateFrom(JkBms::DataPointContainer const& dp);
|
|
|
|
private:
|
|
void getJsonData(JsonVariant& root, bool verbose) const;
|
|
|
|
JkBms::DataPointContainer _dataPoints;
|
|
mutable uint32_t _lastMqttPublish = 0;
|
|
mutable uint32_t _lastFullMqttPublish = 0;
|
|
|
|
uint16_t _cellMinMilliVolt = 0;
|
|
uint16_t _cellAvgMilliVolt = 0;
|
|
uint16_t _cellMaxMilliVolt = 0;
|
|
uint32_t _cellVoltageTimestamp = 0;
|
|
};
|
|
|
|
class VictronSmartShuntStats : public BatteryStats {
|
|
public:
|
|
void getLiveViewData(JsonVariant& root) const final;
|
|
void mqttPublish() const final;
|
|
|
|
void updateFrom(VeDirectShuntController::veShuntStruct const& shuntData);
|
|
|
|
private:
|
|
float _current;
|
|
float _temperature;
|
|
bool _tempPresent;
|
|
uint8_t _chargeCycles;
|
|
uint32_t _timeToGo;
|
|
float _chargedEnergy;
|
|
float _dischargedEnergy;
|
|
String _modelName;
|
|
int32_t _instantaneousPower;
|
|
float _consumedAmpHours;
|
|
int32_t _lastFullCharge;
|
|
|
|
bool _alarmLowVoltage;
|
|
bool _alarmHighVoltage;
|
|
bool _alarmLowSOC;
|
|
bool _alarmLowTemperature;
|
|
bool _alarmHighTemperature;
|
|
};
|
|
|
|
class MqttBatteryStats : public BatteryStats {
|
|
friend class MqttBattery;
|
|
|
|
public:
|
|
// since the source of information was MQTT in the first place,
|
|
// we do NOT publish the same data under a different topic.
|
|
void mqttPublish() const final { }
|
|
|
|
// 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 { }
|
|
};
|