From b9ad1e3054ce849c68ab3860a91ba9d5f36314da Mon Sep 17 00:00:00 2001 From: SW-Nico Date: Wed, 3 Apr 2024 21:54:52 +0200 Subject: [PATCH] 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() --- lib/VeDirectFrameHandler/VeDirectData.cpp | 10 ++-- lib/VeDirectFrameHandler/VeDirectData.h | 50 ++++++++++--------- .../VeDirectFrameHandler.cpp | 21 ++++---- .../VeDirectFrameHandler.h | 2 +- .../VeDirectFrameHexHandler.cpp | 6 +++ .../VeDirectMpptController.cpp | 44 ++++++++-------- .../VeDirectShuntController.cpp | 14 +++++- src/BatteryStats.cpp | 14 +++--- src/MqttHandlVedirectHass.cpp | 6 +-- src/MqttHandleVedirect.cpp | 48 +++++++++--------- src/VictronMppt.cpp | 12 ++--- src/WebApi_ws_vedirect_live.cpp | 32 ++++++------ 12 files changed, 144 insertions(+), 115 deletions(-) diff --git a/lib/VeDirectFrameHandler/VeDirectData.cpp b/lib/VeDirectFrameHandler/VeDirectData.cpp index 012ec29e..d28f9f78 100644 --- a/lib/VeDirectFrameHandler/VeDirectData.cpp +++ b/lib/VeDirectFrameHandler/VeDirectData.cpp @@ -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 diff --git a/lib/VeDirectFrameHandler/VeDirectData.h b/lib/VeDirectFrameHandler/VeDirectData.h index b1158d77..86be497f 100644 --- a/lib/VeDirectFrameHandler/VeDirectData.h +++ b/lib/VeDirectFrameHandler/VeDirectData.h @@ -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 { diff --git a/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp b/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp index 3e07169a..074285db 100644 --- a/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp +++ b/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp @@ -104,10 +104,10 @@ void VeDirectFrameHandler::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(_state)); if (_verboseLogging) { dumpDebugBuffer(); } @@ -236,27 +236,27 @@ void VeDirectFrameHandler::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::State VeDirectFrameHandler::hexRxEvent(uint template bool VeDirectFrameHandler::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 diff --git a/lib/VeDirectFrameHandler/VeDirectFrameHandler.h b/lib/VeDirectFrameHandler/VeDirectFrameHandler.h index c2d66088..1c482920 100644 --- a/lib/VeDirectFrameHandler/VeDirectFrameHandler.h +++ b/lib/VeDirectFrameHandler/VeDirectFrameHandler.h @@ -74,7 +74,7 @@ private: char _value[VE_MAX_VALUE_LEN]; // buffer for the field value std::array _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., diff --git a/lib/VeDirectFrameHandler/VeDirectFrameHexHandler.cpp b/lib/VeDirectFrameHandler/VeDirectFrameHexHandler.cpp index f7f44b5b..392d2f8a 100644 --- a/lib/VeDirectFrameHandler/VeDirectFrameHexHandler.cpp +++ b/lib/VeDirectFrameHandler/VeDirectFrameHexHandler.cpp @@ -93,6 +93,12 @@ bool VeDirectFrameHandler::disassembleHexData(VeDirectHexData &data) { case Response::ASYNC: data.addr = static_cast(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); diff --git a/lib/VeDirectFrameHandler/VeDirectMpptController.cpp b/lib/VeDirectFrameHandler/VeDirectMpptController.cpp index ef3ef187..de3a7a59 100644 --- a/lib/VeDirectFrameHandler/VeDirectMpptController.cpp +++ b/lib/VeDirectFrameHandler/VeDirectMpptController.cpp @@ -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(_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(_tmpFrame.panelPower_PPV_W * 1000000) / _tmpFrame.panelVoltage_VPV_mV; } - if (_tmpFrame.PPV > 0) { - _efficiency.addNumber(static_cast(_tmpFrame.P * 100) / _tmpFrame.PPV); - _tmpFrame.E = _efficiency.getAverage(); + if (_tmpFrame.panelPower_PPV_W > 0) { + _efficiency.addNumber(static_cast(_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; diff --git a/lib/VeDirectFrameHandler/VeDirectShuntController.cpp b/lib/VeDirectFrameHandler/VeDirectShuntController.cpp index 522b152b..54229b93 100644 --- a/lib/VeDirectFrameHandler/VeDirectShuntController.cpp +++ b/lib/VeDirectFrameHandler/VeDirectShuntController.cpp @@ -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(atoi(value.c_str())); + return true; + } return false; } diff --git a/src/BatteryStats.cpp b/src/BatteryStats.cpp index 12a6b601..720695f3 100644 --- a/src/BatteryStats.cpp +++ b/src/BatteryStats.cpp @@ -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(shuntData.SOC) / 10, 1/*precision*/, millis()); - _current = shuntData.I; + _current = static_cast(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(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(); } diff --git a/src/MqttHandlVedirectHass.cpp b/src/MqttHandlVedirectHass.cpp index 4619c1e4..a8e4c44b 100644 --- a/src/MqttHandlVedirectHass.cpp +++ b/src/MqttHandlVedirectHass.cpp @@ -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(); diff --git a/src/MqttHandleVedirect.cpp b/src/MqttHandleVedirect.cpp index ce96c827..9bfd0906 100644 --- a/src/MqttHandleVedirect.cpp +++ b/src/MqttHandleVedirect.cpp @@ -62,10 +62,10 @@ void MqttHandleVedirectClass::loop() std::optional 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 } diff --git a/src/VictronMppt.cpp b/src/VictronMppt.cpp index f210c93e..770be014 100644 --- a/src/VictronMppt.cpp +++ b/src/VictronMppt.cpp @@ -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(networkPower.second / 1000.0 * upController->getData().E / 100); + return static_cast(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(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); } diff --git a/src/WebApi_ws_vedirect_live.cpp b/src/WebApi_ws_vedirect_live.cpp index 9fffeed6..f583ebd0 100644 --- a/src/WebApi_ws_vedirect_live.cpp +++ b/src/WebApi_ws_vedirect_live.cpp @@ -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; }