VE.Direct: process more values and refactor variable names
* process "IL", "AR" and "MON" * discard "BMV" and (unsolicited) History Data * simplify isDataValid() * veMpptStruct, veStruct: new, verbose variable names, including units, and replace floats (save values with original integer precision) * comment on rollover situation in isDataValid()
This commit is contained in:
parent
3934906001
commit
b9ad1e3054
@ -134,7 +134,7 @@ frozen::string const& veStruct::getPidAsString() const
|
||||
{ 0xA3F0, "Smart BuckBoost 12V/12V-50A" },
|
||||
};
|
||||
|
||||
return getAsString(values, PID);
|
||||
return getAsString(values, productID_PID);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -154,7 +154,7 @@ frozen::string const& veMpptStruct::getCsAsString() const
|
||||
{ 252, "External Control" }
|
||||
};
|
||||
|
||||
return getAsString(values, CS);
|
||||
return getAsString(values, currentState_CS);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -168,7 +168,7 @@ frozen::string const& veMpptStruct::getMpptAsString() const
|
||||
{ 2, "MPP Tracker active" }
|
||||
};
|
||||
|
||||
return getAsString(values, MPPT);
|
||||
return getAsString(values, stateOfTracker_MPPT);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -199,7 +199,7 @@ frozen::string const& veMpptStruct::getErrAsString() const
|
||||
{ 118, "User settings invalid" }
|
||||
};
|
||||
|
||||
return getAsString(values, ERR);
|
||||
return getAsString(values, errorCode_ERR);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -220,7 +220,7 @@ frozen::string const& veMpptStruct::getOrAsString() const
|
||||
{ 0x00000100, "Analysing input voltage" }
|
||||
};
|
||||
|
||||
return getAsString(values, OR);
|
||||
return getAsString(values, offReason_OR);
|
||||
}
|
||||
|
||||
frozen::string const& VeDirectHexData::getResponseAsString() const
|
||||
|
||||
@ -7,32 +7,33 @@
|
||||
#define VE_MAX_HEX_LEN 100 // Maximum size of hex frame - max payload 34 byte (=68 char) + safe buffer
|
||||
|
||||
typedef struct {
|
||||
uint16_t PID = 0; // product id
|
||||
char SER[VE_MAX_VALUE_LEN]; // serial number
|
||||
char FW[VE_MAX_VALUE_LEN]; // firmware release number
|
||||
float V = 0; // battery voltage in V
|
||||
float I = 0; // battery current in A
|
||||
float E = 0; // efficiency in percent (calculated, moving average)
|
||||
uint16_t productID_PID = 0; // product id
|
||||
char serialNr_SER[VE_MAX_VALUE_LEN]; // serial number
|
||||
char firmwareNr_FW[VE_MAX_VALUE_LEN]; // firmware release number
|
||||
uint32_t batteryVoltage_V_mV = 0; // battery voltage in mV
|
||||
int32_t batteryCurrent_I_mA = 0; // battery current in mA (can be negative)
|
||||
float mpptEfficiency_Percent = 0; // efficiency in percent (calculated, moving average)
|
||||
|
||||
frozen::string const& getPidAsString() const; // product ID as string
|
||||
} veStruct;
|
||||
|
||||
struct veMpptStruct : veStruct {
|
||||
uint8_t MPPT; // state of MPP tracker
|
||||
int32_t PPV; // panel power in W
|
||||
int32_t P; // battery output power in W (calculated)
|
||||
float VPV; // panel voltage in V
|
||||
float IPV; // panel current in A (calculated)
|
||||
bool LOAD; // virtual load output state (on if battery voltage reaches upper limit, off if battery reaches lower limit)
|
||||
uint8_t CS; // current state of operation e.g. OFF or Bulk
|
||||
uint8_t ERR; // error code
|
||||
uint32_t OR; // off reason
|
||||
uint32_t HSDS; // day sequence number 1...365
|
||||
float H19; // yield total kWh
|
||||
float H20; // yield today kWh
|
||||
int32_t H21; // maximum power today W
|
||||
float H22; // yield yesterday kWh
|
||||
int32_t H23; // maximum power yesterday W
|
||||
uint8_t stateOfTracker_MPPT; // state of MPP tracker
|
||||
uint16_t panelPower_PPV_W; // panel power in W
|
||||
uint32_t panelVoltage_VPV_mV; // panel voltage in mV
|
||||
uint32_t panelCurrent_mA; // panel current in mA (calculated)
|
||||
int16_t batteryOutputPower_W; // battery output power in W (calculated, can be negative if load output is used)
|
||||
uint32_t loadCurrent_IL_mA; // Load current in mA (Available only for models with a load output)
|
||||
bool loadOutputState_LOAD; // virtual load output state (on if battery voltage reaches upper limit, off if battery reaches lower limit)
|
||||
uint8_t currentState_CS; // current state of operation e.g. OFF or Bulk
|
||||
uint8_t errorCode_ERR; // error code
|
||||
uint32_t offReason_OR; // off reason
|
||||
uint16_t daySequenceNr_HSDS; // day sequence number 1...365
|
||||
uint32_t yieldTotal_H19_Wh; // yield total resetable Wh
|
||||
uint32_t yieldToday_H20_Wh; // yield today Wh
|
||||
uint16_t maxPowerToday_H21_W; // maximum power today W
|
||||
uint32_t yieldYesterday_H22_Wh; // yield yesterday Wh
|
||||
uint16_t maxPowerYesterday_H23_W; // maximum power yesterday W
|
||||
|
||||
// these are values communicated through the HEX protocol. the pair's first
|
||||
// value is the timestamp the respective info was last received. if it is
|
||||
@ -59,7 +60,7 @@ struct veShuntStruct : veStruct {
|
||||
int32_t SOC; // State-of-charge
|
||||
uint32_t TTG; // Time-to-go
|
||||
bool ALARM; // Alarm condition active
|
||||
uint32_t AR; // Alarm Reason
|
||||
uint16_t alarmReason_AR; // Alarm Reason
|
||||
int32_t H1; // Depth of the deepest discharge
|
||||
int32_t H2; // Depth of the last discharge
|
||||
int32_t H3; // Depth of the average discharge
|
||||
@ -78,6 +79,7 @@ struct veShuntStruct : veStruct {
|
||||
int32_t H16; // Maximum auxiliary (battery) voltage
|
||||
int32_t H17; // Amount of discharged energy
|
||||
int32_t H18; // Amount of charged energy
|
||||
int8_t dcMonitorMode_MON; // DC monitor mode
|
||||
};
|
||||
|
||||
enum class VeDirectHexCommand : uint8_t {
|
||||
@ -120,7 +122,9 @@ enum class VeDirectHexRegister : uint16_t {
|
||||
SmartBatterySenseTemperature = 0xEDEC,
|
||||
NetworkInfo = 0x200D,
|
||||
NetworkMode = 0x200E,
|
||||
NetworkStatus = 0x200F
|
||||
NetworkStatus = 0x200F,
|
||||
HistoryTotal = 0x104F,
|
||||
HistoryMPPTD30 = 0x10BE
|
||||
};
|
||||
|
||||
struct VeDirectHexData {
|
||||
|
||||
@ -104,10 +104,10 @@ void VeDirectFrameHandler<T>::loop()
|
||||
_lastByteMillis = millis();
|
||||
}
|
||||
|
||||
// there will never be a large gap between two bytes of the same frame.
|
||||
// there will never be a large gap between two bytes.
|
||||
// if such a large gap is observed, reset the state machine so it tries
|
||||
// to decode a new frame once more data arrives.
|
||||
if (State::IDLE != _state && (millis() - _lastByteMillis) > 500) {
|
||||
// to decode a new frame / hex messages once more data arrives.
|
||||
if ((State::IDLE != _state) && ((millis() - _lastByteMillis) > 500)) {
|
||||
_msgOut->printf("%s Resetting state machine (was %d) after timeout\r\n",
|
||||
_logId, static_cast<unsigned>(_state));
|
||||
if (_verboseLogging) { dumpDebugBuffer(); }
|
||||
@ -236,27 +236,27 @@ void VeDirectFrameHandler<T>::processTextData(std::string const& name, std::stri
|
||||
if (processTextDataDerived(name, value)) { return; }
|
||||
|
||||
if (name == "PID") {
|
||||
_tmpFrame.PID = strtol(value.c_str(), nullptr, 0);
|
||||
_tmpFrame.productID_PID = strtol(value.c_str(), nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "SER") {
|
||||
strcpy(_tmpFrame.SER, value.c_str());
|
||||
strcpy(_tmpFrame.serialNr_SER, value.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "FW") {
|
||||
strcpy(_tmpFrame.FW, value.c_str());
|
||||
strcpy(_tmpFrame.firmwareNr_FW, value.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "V") {
|
||||
_tmpFrame.V = round(atof(value.c_str()) / 10.0) / 100.0;
|
||||
_tmpFrame.batteryVoltage_V_mV = atol(value.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "I") {
|
||||
_tmpFrame.I = round(atof(value.c_str()) / 10.0) / 100.0;
|
||||
_tmpFrame.batteryCurrent_I_mA = atol(value.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -307,7 +307,10 @@ typename VeDirectFrameHandler<T>::State VeDirectFrameHandler<T>::hexRxEvent(uint
|
||||
template<typename T>
|
||||
bool VeDirectFrameHandler<T>::isDataValid() const
|
||||
{
|
||||
return strlen(_tmpFrame.SER) > 0 && _lastUpdate > 0 && (millis() - _lastUpdate) < (10 * 1000);
|
||||
// VE.Direct text frame data is valid if we receive a device serialnumber and
|
||||
// the data is not older as 10 seconds
|
||||
// we accept a glitch where the data is valid for ten seconds when serialNr_SER != "" and (millis() - _lastUpdate) overflows
|
||||
return strlen(_tmpFrame.serialNr_SER) > 0 && (millis() - _lastUpdate) < (10 * 1000);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
@ -74,7 +74,7 @@ private:
|
||||
char _value[VE_MAX_VALUE_LEN]; // buffer for the field value
|
||||
std::array<uint8_t, 512> _debugBuffer;
|
||||
unsigned _debugIn;
|
||||
uint32_t _lastByteMillis;
|
||||
uint32_t _lastByteMillis; // time of last parsed byte
|
||||
|
||||
/**
|
||||
* not every frame contains every value the device is communicating, i.e.,
|
||||
|
||||
@ -93,6 +93,12 @@ bool VeDirectFrameHandler<T>::disassembleHexData(VeDirectHexData &data) {
|
||||
case Response::ASYNC:
|
||||
data.addr = static_cast<VeDirectHexRegister>(AsciiHexLE2Int(buffer+2, 4));
|
||||
|
||||
// future option: Up to now we do not use historical data
|
||||
if ((data.addr >= VeDirectHexRegister::HistoryTotal) && (data.addr <= VeDirectHexRegister::HistoryMPPTD30)) {
|
||||
state = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// future option: to analyse the flags here?
|
||||
data.flags = AsciiHexLE2Int(buffer+6, 2);
|
||||
|
||||
|
||||
@ -19,56 +19,60 @@ void VeDirectMpptController::init(int8_t rx, int8_t tx, Print* msgOut, bool verb
|
||||
|
||||
bool VeDirectMpptController::processTextDataDerived(std::string const& name, std::string const& value)
|
||||
{
|
||||
if (name == "IL") {
|
||||
_tmpFrame.loadCurrent_IL_mA = atol(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "LOAD") {
|
||||
_tmpFrame.LOAD = (value == "ON");
|
||||
_tmpFrame.loadOutputState_LOAD = (value == "ON");
|
||||
return true;
|
||||
}
|
||||
if (name == "CS") {
|
||||
_tmpFrame.CS = atoi(value.c_str());
|
||||
_tmpFrame.currentState_CS = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "ERR") {
|
||||
_tmpFrame.ERR = atoi(value.c_str());
|
||||
_tmpFrame.errorCode_ERR = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "OR") {
|
||||
_tmpFrame.OR = strtol(value.c_str(), nullptr, 0);
|
||||
_tmpFrame.offReason_OR = strtol(value.c_str(), nullptr, 0);
|
||||
return true;
|
||||
}
|
||||
if (name == "MPPT") {
|
||||
_tmpFrame.MPPT = atoi(value.c_str());
|
||||
_tmpFrame.stateOfTracker_MPPT = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "HSDS") {
|
||||
_tmpFrame.HSDS = atoi(value.c_str());
|
||||
_tmpFrame.daySequenceNr_HSDS = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "VPV") {
|
||||
_tmpFrame.VPV = round(atof(value.c_str()) / 10.0) / 100.0;
|
||||
_tmpFrame.panelVoltage_VPV_mV = atol(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "PPV") {
|
||||
_tmpFrame.PPV = atoi(value.c_str());
|
||||
_tmpFrame.panelPower_PPV_W = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "H19") {
|
||||
_tmpFrame.H19 = atof(value.c_str()) / 100.0;
|
||||
_tmpFrame.yieldTotal_H19_Wh = atol(value.c_str()) * 10;
|
||||
return true;
|
||||
}
|
||||
if (name == "H20") {
|
||||
_tmpFrame.H20 = atof(value.c_str()) / 100.0;
|
||||
_tmpFrame.yieldToday_H20_Wh = atol(value.c_str()) * 10;
|
||||
return true;
|
||||
}
|
||||
if (name == "H21") {
|
||||
_tmpFrame.H21 = atoi(value.c_str());
|
||||
_tmpFrame.maxPowerToday_H21_W = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "H22") {
|
||||
_tmpFrame.H22 = atof(value.c_str()) / 100.0;
|
||||
_tmpFrame.yieldYesterday_H22_Wh = atol(value.c_str()) * 10;
|
||||
return true;
|
||||
}
|
||||
if (name == "H23") {
|
||||
_tmpFrame.H23 = atoi(value.c_str());
|
||||
_tmpFrame.maxPowerYesterday_H23_W = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -80,15 +84,15 @@ bool VeDirectMpptController::processTextDataDerived(std::string const& name, std
|
||||
* This function is called at the end of the received frame.
|
||||
*/
|
||||
void VeDirectMpptController::frameValidEvent() {
|
||||
_tmpFrame.P = _tmpFrame.V * _tmpFrame.I;
|
||||
_tmpFrame.batteryOutputPower_W = static_cast<int16_t>(_tmpFrame.batteryVoltage_V_mV * _tmpFrame.batteryCurrent_I_mA / 1000000);
|
||||
|
||||
if (_tmpFrame.VPV > 0) {
|
||||
_tmpFrame.IPV = _tmpFrame.PPV / _tmpFrame.VPV;
|
||||
if ((_tmpFrame.panelVoltage_VPV_mV > 0) && (_tmpFrame.panelPower_PPV_W >= 1)) {
|
||||
_tmpFrame.panelCurrent_mA = static_cast<uint32_t>(_tmpFrame.panelPower_PPV_W * 1000000) / _tmpFrame.panelVoltage_VPV_mV;
|
||||
}
|
||||
|
||||
if (_tmpFrame.PPV > 0) {
|
||||
_efficiency.addNumber(static_cast<float>(_tmpFrame.P * 100) / _tmpFrame.PPV);
|
||||
_tmpFrame.E = _efficiency.getAverage();
|
||||
if (_tmpFrame.panelPower_PPV_W > 0) {
|
||||
_efficiency.addNumber(static_cast<float>(_tmpFrame.batteryOutputPower_W * 100) / _tmpFrame.panelPower_PPV_W);
|
||||
_tmpFrame.mpptEfficiency_Percent = _efficiency.getAverage();
|
||||
}
|
||||
|
||||
if (!_canSend) { return; }
|
||||
@ -98,7 +102,7 @@ void VeDirectMpptController::frameValidEvent() {
|
||||
// charger periodically sends human readable (TEXT) data to the serial port. For firmware
|
||||
// versions v1.53 and above, the charger always periodically sends TEXT data to the serial port.
|
||||
// --> We just use hex commandes for firmware >= 1.53 to keep text messages alive
|
||||
if (atoi(_tmpFrame.FW) < 153) { return; }
|
||||
if (atoi(_tmpFrame.firmwareNr_FW) < 153) { return; }
|
||||
|
||||
using Command = VeDirectHexCommand;
|
||||
using Register = VeDirectHexRegister;
|
||||
|
||||
@ -35,6 +35,10 @@ bool VeDirectShuntController::processTextDataDerived(std::string const& name, st
|
||||
_tmpFrame.ALARM = (value == "ON");
|
||||
return true;
|
||||
}
|
||||
if (name == "AR") {
|
||||
_tmpFrame.alarmReason_AR = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
if (name == "H1") {
|
||||
_tmpFrame.H1 = atoi(value.c_str());
|
||||
return true;
|
||||
@ -107,6 +111,14 @@ bool VeDirectShuntController::processTextDataDerived(std::string const& name, st
|
||||
_tmpFrame.H18 = atoi(value.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "BMV") {
|
||||
// This field contains a textual description of the BMV model,
|
||||
// for example 602S or 702. It is deprecated, refer to the field PID instead.
|
||||
return true;
|
||||
}
|
||||
if (name == "MON") {
|
||||
_tmpFrame.dcMonitorMode_MON = static_cast<int8_t>(atoi(value.c_str()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -374,10 +374,10 @@ void JkBmsBatteryStats::updateFrom(JkBms::DataPointContainer const& dp)
|
||||
}
|
||||
|
||||
void VictronSmartShuntStats::updateFrom(VeDirectShuntController::data_t const& shuntData) {
|
||||
BatteryStats::setVoltage(shuntData.V, millis());
|
||||
BatteryStats::setVoltage(shuntData.batteryVoltage_V_mV / 1000.0, millis());
|
||||
BatteryStats::setSoC(static_cast<float>(shuntData.SOC) / 10, 1/*precision*/, millis());
|
||||
|
||||
_current = shuntData.I;
|
||||
_current = static_cast<float>(shuntData.batteryCurrent_I_mA) / 1000;
|
||||
_modelName = shuntData.getPidAsString().data();
|
||||
_chargeCycles = shuntData.H4;
|
||||
_timeToGo = shuntData.TTG / 60;
|
||||
@ -390,11 +390,11 @@ void VictronSmartShuntStats::updateFrom(VeDirectShuntController::data_t const& s
|
||||
_consumedAmpHours = static_cast<float>(shuntData.CE) / 1000;
|
||||
_lastFullCharge = shuntData.H9 / 60;
|
||||
// shuntData.AR is a bitfield, so we need to check each bit individually
|
||||
_alarmLowVoltage = shuntData.AR & 1;
|
||||
_alarmHighVoltage = shuntData.AR & 2;
|
||||
_alarmLowSOC = shuntData.AR & 4;
|
||||
_alarmLowTemperature = shuntData.AR & 32;
|
||||
_alarmHighTemperature = shuntData.AR & 64;
|
||||
_alarmLowVoltage = shuntData.alarmReason_AR & 1;
|
||||
_alarmHighVoltage = shuntData.alarmReason_AR & 2;
|
||||
_alarmLowSOC = shuntData.alarmReason_AR & 4;
|
||||
_alarmLowTemperature = shuntData.alarmReason_AR & 32;
|
||||
_alarmHighTemperature = shuntData.alarmReason_AR & 64;
|
||||
|
||||
_lastUpdate = VeDirectShunt.getLastUpdate();
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ void MqttHandleVedirectHassClass::publishSensor(const char *caption, const char
|
||||
const char *unitOfMeasurement,
|
||||
const VeDirectMpptController::data_t &mpptData)
|
||||
{
|
||||
String serial = mpptData.SER;
|
||||
String serial = mpptData.serialNr_SER;
|
||||
|
||||
String sensorId = caption;
|
||||
sensorId.replace(" ", "_");
|
||||
@ -153,7 +153,7 @@ void MqttHandleVedirectHassClass::publishBinarySensor(const char *caption, const
|
||||
const char *payload_on, const char *payload_off,
|
||||
const VeDirectMpptController::data_t &mpptData)
|
||||
{
|
||||
String serial = mpptData.SER;
|
||||
String serial = mpptData.serialNr_SER;
|
||||
|
||||
String sensorId = caption;
|
||||
sensorId.replace(" ", "_");
|
||||
@ -198,7 +198,7 @@ void MqttHandleVedirectHassClass::publishBinarySensor(const char *caption, const
|
||||
void MqttHandleVedirectHassClass::createDeviceInfo(JsonObject &object,
|
||||
const VeDirectMpptController::data_t &mpptData)
|
||||
{
|
||||
String serial = mpptData.SER;
|
||||
String serial = mpptData.serialNr_SER;
|
||||
object["name"] = "Victron(" + serial + ")";
|
||||
object["ids"] = serial;
|
||||
object["cu"] = String("http://") + NetworkSettings.localIP().toString();
|
||||
|
||||
@ -62,10 +62,10 @@ void MqttHandleVedirectClass::loop()
|
||||
std::optional<VeDirectMpptController::data_t> optMpptData = VictronMppt.getData(idx);
|
||||
if (!optMpptData.has_value()) { continue; }
|
||||
|
||||
auto const& kvFrame = _kvFrames[optMpptData->SER];
|
||||
auto const& kvFrame = _kvFrames[optMpptData->serialNr_SER];
|
||||
publish_mppt_data(*optMpptData, kvFrame);
|
||||
if (!_PublishFull) {
|
||||
_kvFrames[optMpptData->SER] = *optMpptData;
|
||||
_kvFrames[optMpptData->serialNr_SER] = *optMpptData;
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ void MqttHandleVedirectClass::publish_mppt_data(const VeDirectMpptController::da
|
||||
const VeDirectMpptController::data_t &previousData) const {
|
||||
String value;
|
||||
String topic = "victron/";
|
||||
topic.concat(currentData.SER);
|
||||
topic.concat(currentData.serialNr_SER);
|
||||
topic.concat("/");
|
||||
|
||||
#define PUBLISH(sm, t, val) \
|
||||
@ -108,26 +108,26 @@ void MqttHandleVedirectClass::publish_mppt_data(const VeDirectMpptController::da
|
||||
MqttSettings.publish(topic + t, String(val)); \
|
||||
}
|
||||
|
||||
PUBLISH(PID, "PID", currentData.getPidAsString().data());
|
||||
PUBLISH(SER, "SER", currentData.SER);
|
||||
PUBLISH(FW, "FW", currentData.FW);
|
||||
PUBLISH(LOAD, "LOAD", (currentData.LOAD ? "ON" : "OFF"));
|
||||
PUBLISH(CS, "CS", currentData.getCsAsString().data());
|
||||
PUBLISH(ERR, "ERR", currentData.getErrAsString().data());
|
||||
PUBLISH(OR, "OR", currentData.getOrAsString().data());
|
||||
PUBLISH(MPPT, "MPPT", currentData.getMpptAsString().data());
|
||||
PUBLISH(HSDS, "HSDS", currentData.HSDS);
|
||||
PUBLISH(V, "V", currentData.V);
|
||||
PUBLISH(I, "I", currentData.I);
|
||||
PUBLISH(P, "P", currentData.P);
|
||||
PUBLISH(VPV, "VPV", currentData.VPV);
|
||||
PUBLISH(IPV, "IPV", currentData.IPV);
|
||||
PUBLISH(PPV, "PPV", currentData.PPV);
|
||||
PUBLISH(E, "E", currentData.E);
|
||||
PUBLISH(H19, "H19", currentData.H19);
|
||||
PUBLISH(H20, "H20", currentData.H20);
|
||||
PUBLISH(H21, "H21", currentData.H21);
|
||||
PUBLISH(H22, "H22", currentData.H22);
|
||||
PUBLISH(H23, "H23", currentData.H23);
|
||||
PUBLISH(productID_PID, "PID", currentData.getPidAsString().data());
|
||||
PUBLISH(serialNr_SER, "SER", currentData.serialNr_SER);
|
||||
PUBLISH(firmwareNr_FW, "FW", currentData.firmwareNr_FW);
|
||||
PUBLISH(loadOutputState_LOAD, "LOAD", (currentData.loadOutputState_LOAD ? "ON" : "OFF"));
|
||||
PUBLISH(currentState_CS, "CS", currentData.getCsAsString().data());
|
||||
PUBLISH(errorCode_ERR, "ERR", currentData.getErrAsString().data());
|
||||
PUBLISH(offReason_OR, "OR", currentData.getOrAsString().data());
|
||||
PUBLISH(stateOfTracker_MPPT, "MPPT", currentData.getMpptAsString().data());
|
||||
PUBLISH(daySequenceNr_HSDS, "HSDS", currentData.daySequenceNr_HSDS);
|
||||
PUBLISH(batteryVoltage_V_mV, "V", currentData.batteryVoltage_V_mV / 1000.0);
|
||||
PUBLISH(batteryCurrent_I_mA, "I", currentData.batteryCurrent_I_mA / 1000.0);
|
||||
PUBLISH(batteryOutputPower_W, "P", currentData.batteryOutputPower_W);
|
||||
PUBLISH(panelVoltage_VPV_mV, "VPV", currentData.panelVoltage_VPV_mV / 1000.0);
|
||||
PUBLISH(panelCurrent_mA, "IPV", currentData.panelCurrent_mA / 1000.0);
|
||||
PUBLISH(panelPower_PPV_W, "PPV", currentData.panelPower_PPV_W);
|
||||
PUBLISH(mpptEfficiency_Percent, "E", currentData.mpptEfficiency_Percent);
|
||||
PUBLISH(yieldTotal_H19_Wh, "H19", currentData.yieldTotal_H19_Wh / 1000.0);
|
||||
PUBLISH(yieldToday_H20_Wh, "H20", currentData.yieldToday_H20_Wh / 1000.0);
|
||||
PUBLISH(maxPowerToday_H21_W, "H21", currentData.maxPowerToday_H21_W);
|
||||
PUBLISH(yieldYesterday_H22_Wh, "H22", currentData.yieldYesterday_H22_Wh / 1000.0);
|
||||
PUBLISH(maxPowerYesterday_H23_W, "H23", currentData.maxPowerYesterday_H23_W);
|
||||
#undef PUBLILSH
|
||||
}
|
||||
|
||||
@ -148,10 +148,10 @@ int32_t VictronMpptClass::getPowerOutputWatts() const
|
||||
// the calculated efficiency of the connected charge controller.
|
||||
auto networkPower = upController->getData().NetworkTotalDcInputPowerMilliWatts;
|
||||
if (networkPower.first > 0) {
|
||||
return static_cast<int32_t>(networkPower.second / 1000.0 * upController->getData().E / 100);
|
||||
return static_cast<int32_t>(networkPower.second / 1000.0 * upController->getData().mpptEfficiency_Percent / 100);
|
||||
}
|
||||
|
||||
sum += upController->getData().P;
|
||||
sum += upController->getData().batteryOutputPower_W;
|
||||
}
|
||||
|
||||
return sum;
|
||||
@ -172,7 +172,7 @@ int32_t VictronMpptClass::getPanelPowerWatts() const
|
||||
return static_cast<int32_t>(networkPower.second / 1000.0);
|
||||
}
|
||||
|
||||
sum += upController->getData().PPV;
|
||||
sum += upController->getData().panelPower_PPV_W;
|
||||
}
|
||||
|
||||
return sum;
|
||||
@ -184,7 +184,7 @@ float VictronMpptClass::getYieldTotal() const
|
||||
|
||||
for (const auto& upController : _controllers) {
|
||||
if (!upController->isDataValid()) { continue; }
|
||||
sum += upController->getData().H19;
|
||||
sum += upController->getData().yieldTotal_H19_Wh / 1000.0;
|
||||
}
|
||||
|
||||
return sum;
|
||||
@ -196,7 +196,7 @@ float VictronMpptClass::getYieldDay() const
|
||||
|
||||
for (const auto& upController : _controllers) {
|
||||
if (!upController->isDataValid()) { continue; }
|
||||
sum += upController->getData().H20;
|
||||
sum += upController->getData().yieldToday_H20_Wh / 1000.0;
|
||||
}
|
||||
|
||||
return sum;
|
||||
@ -208,7 +208,7 @@ float VictronMpptClass::getOutputVoltage() const
|
||||
|
||||
for (const auto& upController : _controllers) {
|
||||
if (!upController->isDataValid()) { continue; }
|
||||
float volts = upController->getData().V;
|
||||
float volts = upController->getData().batteryVoltage_V_mV / 1000.0;
|
||||
if (min == -1) { min = volts; }
|
||||
min = std::min(min, volts);
|
||||
}
|
||||
|
||||
@ -128,7 +128,7 @@ void WebApiWsVedirectLiveClass::generateJsonResponse(JsonVariant& root, bool ful
|
||||
|
||||
if (!fullUpdate && !hasUpdate(idx)) { continue; }
|
||||
|
||||
String serial(optMpptData->SER);
|
||||
String serial(optMpptData->serialNr_SER);
|
||||
if (serial.isEmpty()) { continue; } // serial required as index
|
||||
|
||||
const JsonObject &nested = array.createNestedObject(serial);
|
||||
@ -147,17 +147,17 @@ void WebApiWsVedirectLiveClass::generateJsonResponse(JsonVariant& root, bool ful
|
||||
|
||||
void WebApiWsVedirectLiveClass::populateJson(const JsonObject &root, const VeDirectMpptController::data_t &mpptData) {
|
||||
root["product_id"] = mpptData.getPidAsString();
|
||||
root["firmware_version"] = String(mpptData.FW);
|
||||
root["firmware_version"] = String(mpptData.firmwareNr_FW);
|
||||
|
||||
const JsonObject &values = root.createNestedObject("values");
|
||||
|
||||
const JsonObject &device = values.createNestedObject("device");
|
||||
device["LOAD"] = mpptData.LOAD ? "ON" : "OFF";
|
||||
device["LOAD"] = mpptData.loadOutputState_LOAD ? "ON" : "OFF";
|
||||
device["CS"] = mpptData.getCsAsString();
|
||||
device["MPPT"] = mpptData.getMpptAsString();
|
||||
device["OR"] = mpptData.getOrAsString();
|
||||
device["ERR"] = mpptData.getErrAsString();
|
||||
device["HSDS"]["v"] = mpptData.HSDS;
|
||||
device["HSDS"]["v"] = mpptData.daySequenceNr_HSDS;
|
||||
device["HSDS"]["u"] = "d";
|
||||
if (mpptData.MpptTemperatureMilliCelsius.first > 0) {
|
||||
device["MpptTemperature"]["v"] = mpptData.MpptTemperatureMilliCelsius.second / 1000.0;
|
||||
@ -166,16 +166,16 @@ void WebApiWsVedirectLiveClass::populateJson(const JsonObject &root, const VeDir
|
||||
}
|
||||
|
||||
const JsonObject &output = values.createNestedObject("output");
|
||||
output["P"]["v"] = mpptData.P;
|
||||
output["P"]["v"] = mpptData.batteryOutputPower_W;
|
||||
output["P"]["u"] = "W";
|
||||
output["P"]["d"] = 0;
|
||||
output["V"]["v"] = mpptData.V;
|
||||
output["V"]["v"] = mpptData.batteryVoltage_V_mV / 1000.0;
|
||||
output["V"]["u"] = "V";
|
||||
output["V"]["d"] = 2;
|
||||
output["I"]["v"] = mpptData.I;
|
||||
output["I"]["v"] = mpptData.batteryCurrent_I_mA / 1000.0;
|
||||
output["I"]["u"] = "A";
|
||||
output["I"]["d"] = 2;
|
||||
output["E"]["v"] = mpptData.E;
|
||||
output["E"]["v"] = mpptData.mpptEfficiency_Percent;
|
||||
output["E"]["u"] = "%";
|
||||
output["E"]["d"] = 1;
|
||||
|
||||
@ -185,28 +185,28 @@ void WebApiWsVedirectLiveClass::populateJson(const JsonObject &root, const VeDir
|
||||
input["NetworkPower"]["u"] = "W";
|
||||
input["NetworkPower"]["d"] = "0";
|
||||
}
|
||||
input["PPV"]["v"] = mpptData.PPV;
|
||||
input["PPV"]["v"] = mpptData.panelPower_PPV_W;
|
||||
input["PPV"]["u"] = "W";
|
||||
input["PPV"]["d"] = 0;
|
||||
input["VPV"]["v"] = mpptData.VPV;
|
||||
input["VPV"]["v"] = mpptData.panelVoltage_VPV_mV / 1000.0;
|
||||
input["VPV"]["u"] = "V";
|
||||
input["VPV"]["d"] = 2;
|
||||
input["IPV"]["v"] = mpptData.IPV;
|
||||
input["IPV"]["v"] = mpptData.panelCurrent_mA / 1000.0;
|
||||
input["IPV"]["u"] = "A";
|
||||
input["IPV"]["d"] = 2;
|
||||
input["YieldToday"]["v"] = mpptData.H20;
|
||||
input["YieldToday"]["v"] = mpptData.yieldToday_H20_Wh / 1000.0;
|
||||
input["YieldToday"]["u"] = "kWh";
|
||||
input["YieldToday"]["d"] = 3;
|
||||
input["YieldYesterday"]["v"] = mpptData.H22;
|
||||
input["YieldYesterday"]["v"] = mpptData.yieldYesterday_H22_Wh / 1000.0;
|
||||
input["YieldYesterday"]["u"] = "kWh";
|
||||
input["YieldYesterday"]["d"] = 3;
|
||||
input["YieldTotal"]["v"] = mpptData.H19;
|
||||
input["YieldTotal"]["v"] = mpptData.yieldTotal_H19_Wh / 1000.0;
|
||||
input["YieldTotal"]["u"] = "kWh";
|
||||
input["YieldTotal"]["d"] = 3;
|
||||
input["MaximumPowerToday"]["v"] = mpptData.H21;
|
||||
input["MaximumPowerToday"]["v"] = mpptData.maxPowerToday_H21_W;
|
||||
input["MaximumPowerToday"]["u"] = "W";
|
||||
input["MaximumPowerToday"]["d"] = 0;
|
||||
input["MaximumPowerYesterday"]["v"] = mpptData.H23;
|
||||
input["MaximumPowerYesterday"]["v"] = mpptData.maxPowerYesterday_H23_W;
|
||||
input["MaximumPowerYesterday"]["u"] = "W";
|
||||
input["MaximumPowerYesterday"]["d"] = 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user