Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use DataPoints for all PowerMeter Providers #1651

Open
wants to merge 2 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions include/powermeter/DataPoints.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#pragma once

#include <cstdint>
#include <DataPoints.h>

namespace PowerMeters {

enum class DataPointLabel {
PowerTotal,
PowerL1,
PowerL2,
PowerL3,
VoltageL1,
VoltageL2,
VoltageL3,
CurrentL1,
CurrentL2,
CurrentL3,
Import,
Export,
};

template<DataPointLabel> struct DataPointLabelTraits;

#define LABEL_TRAIT(n, u) template<> struct DataPointLabelTraits<DataPointLabel::n> { \
using type = float; \
static constexpr char const name[] = #n; \
static constexpr char const unit[] = u; \
};

LABEL_TRAIT(PowerTotal, "W");
LABEL_TRAIT(PowerL1, "W");
LABEL_TRAIT(PowerL2, "W");
LABEL_TRAIT(PowerL3, "W");
LABEL_TRAIT(VoltageL1, "V");
LABEL_TRAIT(VoltageL2, "V");
LABEL_TRAIT(VoltageL3, "V");
LABEL_TRAIT(CurrentL1, "A");
LABEL_TRAIT(CurrentL2, "A");
LABEL_TRAIT(CurrentL3, "A");
LABEL_TRAIT(Import, "kWh");
LABEL_TRAIT(Export, "kWh");
#undef LABEL_TRAIT

} // namespace PowerMeters

template class DataPointContainer<DataPoint<float>,
PowerMeters::DataPointLabel,
PowerMeters::DataPointLabelTraits>;

namespace PowerMeters {
using DataPointContainer = DataPointContainer<DataPoint<float>, DataPointLabel, DataPointLabelTraits>;
} // namespace PowerMeters
19 changes: 6 additions & 13 deletions include/powermeter/Provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
#pragma once

#include <atomic>
#include "Configuration.h"
#include <Configuration.h>
#include <powermeter/DataPoints.h>

namespace PowerMeters {

Expand All @@ -24,10 +25,10 @@ class Provider {
virtual bool init() = 0;

virtual void loop() = 0;
virtual float getPowerTotal() const = 0;
virtual bool isDataValid() const;

uint32_t getLastUpdate() const { return _lastUpdate; }
float getPowerTotal() const;
uint32_t getLastUpdate() const { return _dataCurrent.getLastUpdate(); }
void mqttLoop() const;

protected:
Expand All @@ -36,19 +37,11 @@ class Provider {
_verboseLogging = config.PowerMeter.VerboseLogging;
}

void gotUpdate() { _lastUpdate = millis(); }

void mqttPublish(String const& topic, float const& value) const;

bool _verboseLogging;

private:
virtual void doMqttPublish() const = 0;

// gotUpdate() updates this variable potentially from a different thread
// than users that request to read this variable through getLastUpdate().
std::atomic<uint32_t> _lastUpdate = 0;
DataPointContainer _dataCurrent;

private:
mutable uint32_t _lastMqttPublish = 0;
};

Expand Down
8 changes: 1 addition & 7 deletions include/powermeter/json/http/Provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,9 @@ class Provider : public ::PowerMeters::Provider {

bool init() final;
void loop() final;
float getPowerTotal() const final;
bool isDataValid() const final;
void doMqttPublish() const final;

using power_values_t = std::array<float, POWERMETER_HTTP_JSON_MAX_VALUES>;
using poll_result_t = std::variant<power_values_t, String>;
using poll_result_t = std::variant<DataPointContainer, String>;
poll_result_t poll();

private:
Expand All @@ -43,9 +40,6 @@ class Provider : public ::PowerMeters::Provider {

uint32_t _lastPoll = 0;

mutable std::mutex _valueMutex;
power_values_t _powerValues = {};

std::array<std::unique_ptr<HttpGetter>, POWERMETER_HTTP_JSON_MAX_VALUES> _httpGetters;

TaskHandle_t _taskHandle = nullptr;
Expand Down
13 changes: 2 additions & 11 deletions include/powermeter/json/mqtt/Provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,17 @@ class Provider : public ::PowerMeters::Provider {
~Provider();

bool init() final;
void loop() final { }
float getPowerTotal() const final;
void loop() final { };

private:
using MsgProperties = espMqttClientTypes::MessageProperties;
void onMessage(MsgProperties const& properties, char const* topic,
uint8_t const* payload, size_t len, size_t index,
size_t total, float* targetVariable, PowerMeterMqttValue const* cfg);

// we don't need to republish data received from MQTT
void doMqttPublish() const final { };
size_t total, uint8_t const phaseIndex, PowerMeterMqttValue const* cfg);

PowerMeterMqttConfig const _cfg;

using power_values_t = std::array<float, POWERMETER_MQTT_MAX_VALUES>;
power_values_t _powerValues;

std::vector<String> _mqttSubscriptions;

mutable std::mutex _mutex;
};

} // namespace PowerMeters::Json::Mqtt
13 changes: 0 additions & 13 deletions include/powermeter/sdm/serial/Provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ class Provider : public ::PowerMeters::Provider {

bool init() final;
void loop() final;
float getPowerTotal() const final;
bool isDataValid() const final;
void doMqttPublish() const final;

private:
static void pollingLoopHelper(void* context);
Expand All @@ -41,17 +39,6 @@ class Provider : public ::PowerMeters::Provider {

uint32_t _lastPoll = 0;

float _phase1Power = 0.0;
float _phase2Power = 0.0;
float _phase3Power = 0.0;
float _phase1Voltage = 0.0;
float _phase2Voltage = 0.0;
float _phase3Voltage = 0.0;
float _energyImport = 0.0;
float _energyExport = 0.0;

mutable std::mutex _valueMutex;

std::unique_ptr<SoftwareSerial> _upSdmSerial = nullptr;
std::unique_ptr<SDM> _upSdm = nullptr;

Expand Down
50 changes: 14 additions & 36 deletions include/powermeter/sml/Provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@
namespace PowerMeters::Sml {

class Provider : public ::PowerMeters::Provider {
public:
float getPowerTotal() const final;
void doMqttPublish() const final;

protected:
explicit Provider(char const* user)
: _user(user) { }
Expand All @@ -27,46 +23,28 @@ class Provider : public ::PowerMeters::Provider {

private:
std::string _user;
mutable std::mutex _mutex;

using values_t = struct {
std::optional<float> activePowerTotal = std::nullopt;
std::optional<float> activePowerL1 = std::nullopt;
std::optional<float> activePowerL2 = std::nullopt;
std::optional<float> activePowerL3 = std::nullopt;
std::optional<float> voltageL1 = std::nullopt;
std::optional<float> voltageL2 = std::nullopt;
std::optional<float> voltageL3 = std::nullopt;
std::optional<float> currentL1 = std::nullopt;
std::optional<float> currentL2 = std::nullopt;
std::optional<float> currentL3 = std::nullopt;
std::optional<float> energyImport = std::nullopt;
std::optional<float> energyExport = std::nullopt;
};

values_t _values;
values_t _cache;
DataPointContainer _dataInFlight;

using OBISHandler = struct {
uint8_t const OBIS[6];
void (*decoder)(float&);
std::optional<float>* target;
char const* name;
DataPointLabel target;
};

const std::list<OBISHandler> smlHandlerList{
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerTotal, "active power total"},
{{0x01, 0x00, 0x24, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerL1, "active power L1"},
{{0x01, 0x00, 0x38, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerL2, "active power L2"},
{{0x01, 0x00, 0x4c, 0x07, 0x00, 0xff}, &smlOBISW, &_cache.activePowerL3, "active power L3"},
{{0x01, 0x00, 0x20, 0x07, 0x00, 0xff}, &smlOBISVolt, &_cache.voltageL1, "voltage L1"},
{{0x01, 0x00, 0x34, 0x07, 0x00, 0xff}, &smlOBISVolt, &_cache.voltageL2, "voltage L2"},
{{0x01, 0x00, 0x48, 0x07, 0x00, 0xff}, &smlOBISVolt, &_cache.voltageL3, "voltage L3"},
{{0x01, 0x00, 0x1f, 0x07, 0x00, 0xff}, &smlOBISAmpere, &_cache.currentL1, "current L1"},
{{0x01, 0x00, 0x33, 0x07, 0x00, 0xff}, &smlOBISAmpere, &_cache.currentL2, "current L2"},
{{0x01, 0x00, 0x47, 0x07, 0x00, 0xff}, &smlOBISAmpere, &_cache.currentL3, "current L3"},
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_cache.energyImport, "energy import"},
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_cache.energyExport, "energy export"}
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, DataPointLabel::PowerTotal},
{{0x01, 0x00, 0x24, 0x07, 0x00, 0xff}, &smlOBISW, DataPointLabel::PowerL1},
{{0x01, 0x00, 0x38, 0x07, 0x00, 0xff}, &smlOBISW, DataPointLabel::PowerL2},
{{0x01, 0x00, 0x4c, 0x07, 0x00, 0xff}, &smlOBISW, DataPointLabel::PowerL3},
{{0x01, 0x00, 0x20, 0x07, 0x00, 0xff}, &smlOBISVolt, DataPointLabel::VoltageL1},
{{0x01, 0x00, 0x34, 0x07, 0x00, 0xff}, &smlOBISVolt, DataPointLabel::VoltageL2},
{{0x01, 0x00, 0x48, 0x07, 0x00, 0xff}, &smlOBISVolt, DataPointLabel::VoltageL3},
{{0x01, 0x00, 0x1f, 0x07, 0x00, 0xff}, &smlOBISAmpere, DataPointLabel::CurrentL1},
{{0x01, 0x00, 0x33, 0x07, 0x00, 0xff}, &smlOBISAmpere, DataPointLabel::CurrentL2},
{{0x01, 0x00, 0x47, 0x07, 0x00, 0xff}, &smlOBISAmpere, DataPointLabel::CurrentL3},
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, DataPointLabel::Import},
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, DataPointLabel::Export}
};
};

Expand Down
6 changes: 0 additions & 6 deletions include/powermeter/udp/smahm/Provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,13 @@ class Provider : public ::PowerMeters::Provider {

bool init() final;
void loop() final;
float getPowerTotal() const final { return _powerMeterPower; }
void doMqttPublish() const final;

private:
void Soutput(int kanal, int index, int art, int tarif,
char const* name, float value, uint32_t timestamp);

uint8_t* decodeGroup(uint8_t* offset, uint16_t grouplen);

float _powerMeterPower = 0.0;
float _powerMeterL1 = 0.0;
float _powerMeterL2 = 0.0;
float _powerMeterL3 = 0.0;
uint32_t _previousMillis = 0;
uint32_t _serial = 0;
};
Expand Down
12 changes: 7 additions & 5 deletions src/WebApi_powermeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,16 @@ void WebApiPowerMeterClass::onTestHttpJsonRequest(AsyncWebServerRequest* request
auto upMeter = std::make_unique<::PowerMeters::Json::Http::Provider>(*powerMeterConfig);
upMeter->init();
auto res = upMeter->poll();
using values_t = ::PowerMeters::Json::Http::Provider::power_values_t;
using values_t = ::PowerMeters::DataPointContainer;
if (std::holds_alternative<values_t>(res)) {
retMsg["type"] = "success";
auto vals = std::get<values_t>(res);
auto pos = snprintf(response, sizeof(response), "Result: %5.2fW", vals[0]);
for (size_t i = 1; i < vals.size(); ++i) {
if (!powerMeterConfig->Values[i].Enabled) { continue; }
pos += snprintf(response + pos, sizeof(response) - pos, ", %5.2fW", vals[i]);
auto iter = vals.cbegin();
auto pos = snprintf(response, sizeof(response), "Result: %sW", iter->second.getValueText().c_str());
++iter;
while (iter != vals.cend()) {
pos += snprintf(response + pos, sizeof(response) - pos, ", %sW", iter->second.getValueText().c_str());
++iter;
}
snprintf(response + pos, sizeof(response) - pos, ", Total: %5.2f", upMeter->getPowerTotal());
} else {
Expand Down
1 change: 1 addition & 0 deletions src/powermeter/Controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ void Controller::loop()
_upProvider->loop();

auto const& pmcfg = Configuration.get().PowerMeter;
// we don't need to republish data received from MQTT
if (pmcfg.Source == static_cast<uint8_t>(Provider::Type::MQTT)) { return; }
_upProvider->mqttLoop();
}
Expand Down
53 changes: 47 additions & 6 deletions src/powermeter/Provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,33 @@ namespace PowerMeters {

bool Provider::isDataValid() const
{
return _lastUpdate > 0 && ((millis() - _lastUpdate) < (30 * 1000));
return getLastUpdate() > 0 && ((millis() - getLastUpdate()) < (30 * 1000));
}

void Provider::mqttPublish(String const& topic, float const& value) const
float Provider::getPowerTotal() const
{
MqttSettings.publish("powermeter/" + topic, String(value));
auto oPowerTotal = _dataCurrent.get<DataPointLabel::PowerTotal>();

if (oPowerTotal) {
return *oPowerTotal;
}

auto powerTotal = 0.0;

#define ADD(l) \
{ \
auto oDataPoint = _dataCurrent.get<DataPointLabel::l>(); \
if (oDataPoint) { \
powerTotal += *oDataPoint; \
} \
}

ADD(PowerL1);
ADD(PowerL2);
ADD(PowerL3);
#undef ADD

return powerTotal;
}

void Provider::mqttLoop() const
Expand All @@ -21,11 +42,31 @@ void Provider::mqttLoop() const
if (!isDataValid()) { return; }

auto constexpr halfOfAllMillis = std::numeric_limits<uint32_t>::max() / 2;
if ((_lastUpdate - _lastMqttPublish) > halfOfAllMillis) { return; }
if ((getLastUpdate() - _lastMqttPublish) > halfOfAllMillis) { return; }

// based on getPowerTotal() as we can not be sure that the PowerTotal value is set for all providers
MqttSettings.publish("powermeter/powerTotal", String(getPowerTotal()));

mqttPublish("powertotal", getPowerTotal());
#define PUB(l, t) \
{ \
auto oDataPoint = _dataCurrent.get<DataPointLabel::l>(); \
if (oDataPoint) { \
MqttSettings.publish("powermeter/" t, String(*oDataPoint)); \
} \
}

doMqttPublish();
PUB(PowerL1, "power1");
PUB(PowerL2, "power2");
PUB(PowerL3, "power3");
PUB(VoltageL1, "voltage1");
PUB(VoltageL2, "voltage2");
PUB(VoltageL3, "voltage3");
PUB(CurrentL1, "current1");
PUB(CurrentL2, "current2");
PUB(CurrentL3, "current3");
PUB(Import, "import");
PUB(Export, "export");
#undef PUB

_lastMqttPublish = millis();
}
Expand Down
Loading
Loading