diff --git a/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp b/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp index c53bf540..fa80dd8c 100644 --- a/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp +++ b/lib/VeDirectFrameHandler/VeDirectFrameHandler.cpp @@ -259,6 +259,19 @@ void VeDirectFrameHandler::textRxEvent(char * name, char * value) { */ void VeDirectFrameHandler::frameEndEvent(bool valid) { if ( valid ) { + _tmpFrame.P = _tmpFrame.V * _tmpFrame.I; + + _tmpFrame.IPV = 0; + if ( _tmpFrame.VPV > 0) { + _tmpFrame.IPV = _tmpFrame.PPV / _tmpFrame.VPV; + } + + _tmpFrame.E = 0; + if ( _tmpFrame.PPV > 0) { + _efficiency.addNumber(static_cast(_tmpFrame.P * 100) / _tmpFrame.PPV); + _tmpFrame.E = _efficiency.getAverage(); + } + veFrame = _tmpFrame; setLastUpdate(); } diff --git a/lib/VeDirectFrameHandler/VeDirectFrameHandler.h b/lib/VeDirectFrameHandler/VeDirectFrameHandler.h index d02024a9..13b51eaa 100644 --- a/lib/VeDirectFrameHandler/VeDirectFrameHandler.h +++ b/lib/VeDirectFrameHandler/VeDirectFrameHandler.h @@ -36,10 +36,13 @@ typedef struct { uint32_t OR; // off reason uint8_t MPPT; // state of MPP tracker uint32_t HSDS; // day sequence number 1...365 + int32_t P; // battery output power in W (calculated) double V; // battery voltage in V double I; // battery current in A - double VPV; // panel voltage in V + double E; // efficiency in percent (calculated, moving average) int32_t PPV; // panel power in W + double VPV; // panel voltage in V + double IPV; // panel current in A (calculated) double H19; // yield total kWh double H20; // yield today kWh int32_t H21; // maximum power today W @@ -47,6 +50,38 @@ typedef struct { int32_t H23; // maximum power yesterday W } veStruct; +template +class MovingAverage { +public: + MovingAverage() + : _sum(0) + , _index(0) + , _count(0) { } + + void addNumber(T num) { + if (_count < WINDOW_SIZE) { + _count++; + } else { + _sum -= _window[_index]; + } + + _window[_index] = num; + _sum += num; + _index = (_index + 1) % WINDOW_SIZE; + } + + double getAverage() const { + if (_count == 0) { return 0.0; } + return static_cast(_sum) / _count; + } + +private: + std::array _window; + T _sum; + size_t _index; + size_t _count; +}; + class VeDirectFrameHandler { public: @@ -82,6 +117,7 @@ private: char _name[VE_MAX_VALUE_LEN]; // buffer for the field name char _value[VE_MAX_VALUE_LEN]; // buffer for the field value veStruct _tmpFrame{}; // private struct for received name and value pairs + MovingAverage _efficiency; unsigned long _pollInterval; unsigned long _lastPoll; }; diff --git a/src/WebApi_ws_vedirect_live.cpp b/src/WebApi_ws_vedirect_live.cpp index 68f8d925..539917a7 100644 --- a/src/WebApi_ws_vedirect_live.cpp +++ b/src/WebApi_ws_vedirect_live.cpp @@ -61,7 +61,7 @@ void WebApiWsVedirectLiveClass::loop() String buffer; // free JsonDocument as soon as possible { - DynamicJsonDocument root(1024); + DynamicJsonDocument root(2048); JsonVariant var = root; generateJsonResponse(var); serializeJson(root, buffer); @@ -88,47 +88,64 @@ void WebApiWsVedirectLiveClass::loop() void WebApiWsVedirectLiveClass::generateJsonResponse(JsonVariant& root) { // device info - root["data_age"] = (millis() - VeDirect.getLastUpdate() ) / 1000; - root["age_critical"] = !VeDirect.isDataValid(); - root["PID"] = VeDirect.getPidAsString(VeDirect.veFrame.PID); - root["SER"] = VeDirect.veFrame.SER; - root["FW"] = VeDirect.veFrame.FW; - root["LOAD"] = VeDirect.veFrame.LOAD == true ? "ON" : "OFF"; - root["CS"] = VeDirect.getCsAsString(VeDirect.veFrame.CS); - root["ERR"] = VeDirect.getErrAsString(VeDirect.veFrame.ERR); - root["OR"] = VeDirect.getOrAsString(VeDirect.veFrame.OR); - root["MPPT"] = VeDirect.getMpptAsString(VeDirect.veFrame.MPPT); - root["HSDS"]["v"] = VeDirect.veFrame.HSDS; - root["HSDS"]["u"] = "Days"; + root["device"]["data_age"] = (millis() - VeDirect.getLastUpdate() ) / 1000; + root["device"]["age_critical"] = !VeDirect.isDataValid(); + root["device"]["PID"] = VeDirect.getPidAsString(VeDirect.veFrame.PID); + root["device"]["SER"] = VeDirect.veFrame.SER; + root["device"]["FW"] = VeDirect.veFrame.FW; + root["device"]["LOAD"] = VeDirect.veFrame.LOAD == true ? "ON" : "OFF"; + root["device"]["CS"] = VeDirect.getCsAsString(VeDirect.veFrame.CS); + root["device"]["ERR"] = VeDirect.getErrAsString(VeDirect.veFrame.ERR); + root["device"]["OR"] = VeDirect.getOrAsString(VeDirect.veFrame.OR); + root["device"]["MPPT"] = VeDirect.getMpptAsString(VeDirect.veFrame.MPPT); + root["device"]["HSDS"]["v"] = VeDirect.veFrame.HSDS; + root["device"]["HSDS"]["u"] = "d"; // battery info - root["V"]["v"] = VeDirect.veFrame.V; - root["V"]["u"] = "V"; - root["I"]["v"] = VeDirect.veFrame.I; - root["I"]["u"] = "A"; + root["output"]["P"]["v"] = VeDirect.veFrame.P; + root["output"]["P"]["u"] = "W"; + root["output"]["P"]["d"] = 0; + root["output"]["V"]["v"] = VeDirect.veFrame.V; + root["output"]["V"]["u"] = "V"; + root["output"]["V"]["d"] = 2; + root["output"]["I"]["v"] = VeDirect.veFrame.I; + root["output"]["I"]["u"] = "A"; + root["output"]["I"]["d"] = 2; + root["output"]["E"]["v"] = VeDirect.veFrame.E; + root["output"]["E"]["u"] = "%"; + root["output"]["E"]["d"] = 1; // panel info - root["VPV"]["v"] = VeDirect.veFrame.VPV; - root["VPV"]["u"] = "V"; - root["PPV"]["v"] = VeDirect.veFrame.PPV; - root["PPV"]["u"] = "W"; - root["H19"]["v"] = VeDirect.veFrame.H19; - root["H19"]["u"] = "kWh"; - root["H20"]["v"] = VeDirect.veFrame.H20; - root["H20"]["u"] = "kWh"; - root["H21"]["v"] = VeDirect.veFrame.H21; - root["H21"]["u"] = "W"; - root["H22"]["v"] = VeDirect.veFrame.H22; - root["H22"]["u"] = "kWh"; - root["H23"]["v"] = VeDirect.veFrame.H23; - root["H23"]["u"] = "W"; + root["input"]["PPV"]["v"] = VeDirect.veFrame.PPV; + root["input"]["PPV"]["u"] = "W"; + root["input"]["PPV"]["d"] = 0; + root["input"]["VPV"]["v"] = VeDirect.veFrame.VPV; + root["input"]["VPV"]["u"] = "V"; + root["input"]["VPV"]["d"] = 2; + root["input"]["IPV"]["v"] = VeDirect.veFrame.IPV; + root["input"]["IPV"]["u"] = "A"; + root["input"]["IPV"]["d"] = 2; + root["input"]["YieldToday"]["v"] = VeDirect.veFrame.H20; + root["input"]["YieldToday"]["u"] = "kWh"; + root["input"]["YieldToday"]["d"] = 3; + root["input"]["YieldYesterday"]["v"] = VeDirect.veFrame.H22; + root["input"]["YieldYesterday"]["u"] = "kWh"; + root["input"]["YieldYesterday"]["d"] = 3; + root["input"]["YieldTotal"]["v"] = VeDirect.veFrame.H19; + root["input"]["YieldTotal"]["u"] = "kWh"; + root["input"]["YieldTotal"]["d"] = 3; + root["input"]["MaximumPowerToday"]["v"] = VeDirect.veFrame.H21; + root["input"]["MaximumPowerToday"]["u"] = "W"; + root["input"]["MaximumPowerToday"]["d"] = 0; + root["input"]["MaximumPowerYesterday"]["v"] = VeDirect.veFrame.H23; + root["input"]["MaximumPowerYesterday"]["u"] = "W"; + root["input"]["MaximumPowerYesterday"]["d"] = 0; // power limiter state + root["dpl"]["PLSTATE"] = -1; if (Configuration.get().PowerLimiter_Enabled) - root["PLSTATE"] = PowerLimiter.getPowerLimiterState(); - else - root["PLSTATE"] = -1; - root["PLLIMIT"] = PowerLimiter.getLastRequestedPowewrLimit(); + root["dpl"]["PLSTATE"] = PowerLimiter.getPowerLimiterState(); + root["dpl"]["PLLIMIT"] = PowerLimiter.getLastRequestedPowewrLimit(); if (VeDirect.getLastUpdate() > _newestVedirectTimestamp) { _newestVedirectTimestamp = VeDirect.getLastUpdate(); diff --git a/webapp/src/components/VedirectView.vue b/webapp/src/components/VedirectView.vue index a4a4f48b..43df79dd 100644 --- a/webapp/src/components/VedirectView.vue +++ b/webapp/src/components/VedirectView.vue @@ -34,21 +34,21 @@
@@ -119,15 +119,15 @@ - - {{ $t('vedirecthome.BatteryVoltage') }} - {{formatNumber(vedirectData.V.v)}} - {{vedirectData.V.u}} - - - {{ $t('vedirecthome.BatteryCurrent') }} - {{formatNumber(vedirectData.I.v)}} - {{vedirectData.I.u}} + + {{ $t('vedirecthome.output.' + key) }} + + {{ $n(prop.v, 'decimal', { + minimumFractionDigits: prop.d, + maximumFractionDigits: prop.d}) + }} + + {{prop.u}} @@ -149,40 +149,15 @@ - - {{ $t('vedirecthome.PanelVoltage') }} - {{formatNumber(vedirectData.VPV.v)}} - {{vedirectData.VPV.u}} - - - {{ $t('vedirecthome.PanelPower') }} - {{formatNumber(vedirectData.PPV.v)}} - {{vedirectData.PPV.u}} - - - {{ $t('vedirecthome.YieldTotal') }} - {{formatNumber(vedirectData.H19.v)}} - {{vedirectData.H19.u}} - - - {{ $t('vedirecthome.YieldToday') }} - {{formatNumber(vedirectData.H20.v)}} - {{vedirectData.H20.u}} - - - {{ $t('vedirecthome.MaximumPowerToday') }} - {{formatNumber(vedirectData.H21.v)}} - {{vedirectData.H21.u}} - - - {{ $t('vedirecthome.YieldYesterday') }} - {{formatNumber(vedirectData.H22.v)}} - {{vedirectData.H22.u}} - - - {{ $t('vedirecthome.MaximumPowerYesterday') }} - {{formatNumber(vedirectData.H23.v)}} - {{vedirectData.H23.u}} + + {{ $t('vedirecthome.input.' + key) }} + + {{ $n(prop.v, 'decimal', { + minimumFractionDigits: prop.d, + maximumFractionDigits: prop.d}) + }} + + {{prop.u}} @@ -203,7 +178,7 @@