Skip to content

Commit

Permalink
Implement global data store to handle all invert total values
Browse files Browse the repository at this point in the history
Use the new values in the LED, MQTT and Web interface.
  • Loading branch information
tbnobody committed May 21, 2023
1 parent 56fe72d commit cd98941
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 82 deletions.
61 changes: 61 additions & 0 deletions include/Datastore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include <TimeoutHelper.h>

class DatastoreClass {
public:
DatastoreClass();
void init();
void loop();

// Sum of yield total of all enabled inverters, a inverter which is just disabled at night is also included
float totalAcYieldTotalEnabled = 0;

// Sum of yield day of all enabled inverters, a inverter which is just disabled at night is also included
float totalAcYieldDayEnabled = 0;

// Sum of total AC power of all enabled inverters
float totalAcPowerEnabled = 0;

// Sum of total DC power of all enabled inverters
float totalDcPowerEnabled = 0;

// Sum of total DC power of all enabled inverters with maxStringPower set
float totalDcPowerIrradiation = 0;

// Sum of total installed irradiation of all enabled inverters
float totalDcIrradiationInstalled = 0;

// Percentage (1-100) of total irradiation
float totalDcIrradiation = 0;

// Amount of relevant digits for yield total
unsigned int totalAcYieldTotalDigits = 0;

// Amount of relevant digits for yield total
unsigned int totalAcYieldDayDigits = 0;

// Amount of relevant digits for AC power
unsigned int totalAcPowerDigits = 0;

// Amount of relevant digits for DC power
unsigned int totalDcPowerDigits = 0;

// True, if at least one inverter is reachable
bool isAtLeastOneReachable = false;

// True if at least one inverter is producing
bool isAtLeastOneProducing = false;

// True if all enabled inverters are producing
bool isAllEnabledProducing = false;

// True if all enabled inverters are reachable
bool isAllEnabledReachable = false;

private:
TimeoutHelper _updateTimeout;
};

extern DatastoreClass Datastore;
106 changes: 106 additions & 0 deletions src/Datastore.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2023 Thomas Basler and others
*/
#include "Datastore.h"
#include "Configuration.h"
#include <Hoymiles.h>

DatastoreClass Datastore;

DatastoreClass::DatastoreClass()
{
}

void DatastoreClass::init()
{
_updateTimeout.set(1000);
}

void DatastoreClass::loop()
{
if (Hoymiles.isAllRadioIdle() && _updateTimeout.occured()) {

uint8_t isProducing = 0;
uint8_t isReachable = 0;

totalAcYieldTotalEnabled = 0;
totalAcYieldTotalDigits = 0;

totalAcYieldDayEnabled = 0;
totalAcYieldDayDigits = 0;

totalAcPowerEnabled = 0;
totalAcPowerDigits = 0;

totalDcPowerEnabled = 0;
totalDcPowerDigits = 0;

totalDcPowerIrradiation = 0;
totalDcIrradiationInstalled = 0;

isAllEnabledProducing = true;
isAllEnabledReachable = true;

for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
if (inv == nullptr) {
continue;
}

auto cfg = Configuration.getInverterConfig(inv->serial());
if (cfg == nullptr) {
continue;
}

if (inv->isProducing()) {
isProducing++;
} else {
if (inv->getEnablePolling()) {
isAllEnabledProducing = false;
}
}

if (inv->isReachable()) {
isReachable++;
} else {
if (inv->getEnablePolling()) {
isAllEnabledReachable = false;
}
}

for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
if (cfg->Poll_Enable) {
totalAcYieldTotalEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT);
totalAcYieldDayEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD);

totalAcYieldTotalDigits = max<unsigned int>(totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT));
totalAcYieldDayDigits = max<unsigned int>(totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD));
}
if (inv->getEnablePolling()) {
totalAcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
totalAcPowerDigits = max<unsigned int>(totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC));
}
}

for (auto& c : inv->Statistics()->getChannelsByType(TYPE_DC)) {
if (inv->getEnablePolling()) {
totalDcPowerEnabled += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcPowerDigits = max<unsigned int>(totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_DC, c, FLD_PDC));

if (inv->Statistics()->getStringMaxPower(c) > 0) {
totalDcPowerIrradiation += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcIrradiationInstalled += inv->Statistics()->getStringMaxPower(c);
}
}
}
}

isAtLeastOneProducing = isProducing > 0;
isAtLeastOneReachable = isReachable > 0;

totalDcIrradiation = totalDcIrradiationInstalled > 0 ? totalDcPowerIrradiation / totalDcIrradiationInstalled * 100.0f : 0;

_updateTimeout.reset();
}
}
21 changes: 3 additions & 18 deletions src/Led_Single.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
#include "Led_Single.h"
#include "Configuration.h"
#include "Datastore.h"
#include "MqttSettings.h"
#include "NetworkSettings.h"
#include "PinMapping.h"
Expand Down Expand Up @@ -57,27 +58,11 @@ void LedSingleClass::loop()
// Update inverter status
_ledState[1] = LedState_t::Off;
if (Hoymiles.getNumInverters()) {
bool allReachable = true;
bool allProducing = true;
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
if (inv == nullptr) {
continue;
}
if (inv->getEnablePolling()) {
if (!inv->isReachable()) {
allReachable = false;
}
if (!inv->isProducing()) {
allProducing = false;
}
}
}
// set LED status
if (allReachable && allProducing) {
if (Datastore.isAllEnabledReachable && Datastore.isAllEnabledProducing) {
_ledState[1] = LedState_t::On;
}
if (allReachable && !allProducing) {
if (Datastore.isAllEnabledReachable && !Datastore.isAllEnabledProducing) {
_ledState[1] = LedState_t::Blink;
}
}
Expand Down
58 changes: 8 additions & 50 deletions src/MqttHandleInverterTotal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
#include "MqttHandleInverterTotal.h"
#include "Configuration.h"
#include "Datastore.h"
#include "MqttSettings.h"
#include <Hoymiles.h>

Expand All @@ -21,56 +22,13 @@ void MqttHandleInverterTotalClass::loop()
}

if (_lastPublish.occured()) {
float totalAcPower = 0;
float totalDcPower = 0;
float totalDcPowerIrr = 0;
float totalDcPowerIrrInst = 0;
float totalAcYieldDay = 0;
float totalAcYieldTotal = 0;
uint8_t totalAcPowerDigits = 0;
uint8_t totalDcPowerDigits = 0;
uint8_t totalAcYieldDayDigits = 0;
uint8_t totalAcYieldTotalDigits = 0;
bool totalReachable = true;

for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
if (inv == nullptr || !inv->getEnablePolling()) {
continue;
}

if (!inv->isReachable()) {
totalReachable = false;
}

for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
totalAcPower += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
totalAcPowerDigits = max<uint8_t>(totalAcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_PAC));

totalAcYieldDay += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD);
totalAcYieldDayDigits = max<uint8_t>(totalAcYieldDayDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YD));

totalAcYieldTotal += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT);
totalAcYieldTotalDigits = max<uint8_t>(totalAcYieldTotalDigits, inv->Statistics()->getChannelFieldDigits(TYPE_AC, c, FLD_YT));
}
for (auto& c : inv->Statistics()->getChannelsByType(TYPE_DC)) {
totalDcPower += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcPowerDigits = max<uint8_t>(totalDcPowerDigits, inv->Statistics()->getChannelFieldDigits(TYPE_DC, c, FLD_PDC));

if (inv->Statistics()->getStringMaxPower(c) > 0) {
totalDcPowerIrr += inv->Statistics()->getChannelFieldValue(TYPE_DC, c, FLD_PDC);
totalDcPowerIrrInst += inv->Statistics()->getStringMaxPower(c);
}
}
}

MqttSettings.publish("ac/power", String(totalAcPower, static_cast<unsigned int>(totalAcPowerDigits)));
MqttSettings.publish("ac/yieldtotal", String(totalAcYieldTotal, static_cast<unsigned int>(totalAcYieldTotalDigits)));
MqttSettings.publish("ac/yieldday", String(totalAcYieldDay, static_cast<unsigned int>(totalAcYieldDayDigits)));
MqttSettings.publish("ac/is_valid", String(totalReachable));
MqttSettings.publish("dc/power", String(totalDcPower, static_cast<unsigned int>(totalAcPowerDigits)));
MqttSettings.publish("dc/irradiation", String(totalDcPowerIrrInst > 0 ? totalDcPowerIrr / totalDcPowerIrrInst * 100.0f : 0, 3));
MqttSettings.publish("dc/is_valid", String(totalReachable));
MqttSettings.publish("ac/power", String(Datastore.totalAcPowerEnabled, Datastore.totalAcPowerDigits));
MqttSettings.publish("ac/yieldtotal", String(Datastore.totalAcYieldTotalEnabled, Datastore.totalAcYieldTotalDigits));
MqttSettings.publish("ac/yieldday", String(Datastore.totalAcYieldDayEnabled, Datastore.totalAcYieldDayDigits));
MqttSettings.publish("ac/is_valid", String(Datastore.isAllEnabledReachable));
MqttSettings.publish("dc/power", String(Datastore.totalDcPowerEnabled, Datastore.totalDcPowerDigits));
MqttSettings.publish("dc/irradiation", String(Datastore.totalDcIrradiation, 3));
MqttSettings.publish("dc/is_valid", String(Datastore.isAllEnabledReachable));

_lastPublish.set(Configuration.get().Mqtt_PublishInterval * 1000);
}
Expand Down
18 changes: 4 additions & 14 deletions src/WebApi_ws_live.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
#include "WebApi_ws_live.h"
#include "Configuration.h"
#include "Datastore.h"
#include "MessageOutput.h"
#include "WebApi.h"
#include "defaults.h"
Expand Down Expand Up @@ -90,10 +91,6 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
{
JsonArray invArray = root.createNestedArray("inverters");

float totalPower = 0;
float totalYieldDay = 0;
float totalYieldTotal = 0;

// Loop all inverters
for (uint8_t i = 0; i < Hoymiles.getNumInverters(); i++) {
auto inv = Hoymiles.getInverterByPos(i);
Expand Down Expand Up @@ -158,19 +155,12 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
if (inv->Statistics()->getLastUpdate() > _newestInverterTimestamp) {
_newestInverterTimestamp = inv->Statistics()->getLastUpdate();
}

for (auto& c : inv->Statistics()->getChannelsByType(TYPE_AC)) {
totalPower += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_PAC);
totalYieldDay += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YD);
totalYieldTotal += inv->Statistics()->getChannelFieldValue(TYPE_AC, c, FLD_YT);
}
}

JsonObject totalObj = root.createNestedObject("total");
// todo: Fixed hard coded name, unit and digits
addTotalField(totalObj, "Power", totalPower, "W", 1);
addTotalField(totalObj, "YieldDay", totalYieldDay, "Wh", 0);
addTotalField(totalObj, "YieldTotal", totalYieldTotal, "kWh", 2);
addTotalField(totalObj, "Power", Datastore.totalAcPowerEnabled, "W", Datastore.totalAcPowerDigits);
addTotalField(totalObj, "YieldDay", Datastore.totalAcYieldDayEnabled, "Wh", Datastore.totalAcYieldDayDigits);
addTotalField(totalObj, "YieldTotal", Datastore.totalAcYieldTotalEnabled, "kWh", Datastore.totalAcYieldTotalDigits);

JsonObject hintObj = root.createNestedObject("hints");
struct tm timeinfo;
Expand Down
6 changes: 6 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Copyright (C) 2022 Thomas Basler and others
*/
#include "Configuration.h"
#include "Datastore.h"
#include "Display_Graphic.h"
#include "InverterSettings.h"
#include "Led_Single.h"
Expand Down Expand Up @@ -141,6 +142,8 @@ void setup()
MessageOutput.println("done");

InverterSettings.init();

Datastore.init();
}

void loop()
Expand All @@ -149,11 +152,14 @@ void loop()
yield();
InverterSettings.loop();
yield();
Datastore.loop();
yield();
MqttHandleDtu.loop();
yield();
MqttHandleInverter.loop();
yield();
MqttHandleInverterTotal.loop();
yield();
MqttHandleHass.loop();
yield();
WebApi.loop();
Expand Down

0 comments on commit cd98941

Please sign in to comment.