PowerMeter fixes (#342)

* PowerMeter: gracefully handle non-float MQTT values

* PowerMeter: update _lastPowerMeterUpdate conservatively

update the timestampt only if the topic actually matched any
subscription and if the value could be parsed as a float.

* PowerMeter: unsubscribe before subscribing

* PowerMeter: organize subscriptions in a map

this allows for a slightly more elegant code and reduced amount of code
overall.

* PowerMeter: clean up header

* move private methods to private section of class declaration.
* remove unused member variable.
This commit is contained in:
Bernhard Kirchen 2023-07-31 14:16:06 +02:00 committed by GitHub
parent 9bc334e368
commit 6b425d96b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 53 deletions

View File

@ -4,8 +4,8 @@
#include "Configuration.h" #include "Configuration.h"
#include <espMqttClient.h> #include <espMqttClient.h>
#include <Arduino.h> #include <Arduino.h>
#include <Hoymiles.h> #include <map>
#include <memory> #include <list>
#include "SDM.h" #include "SDM.h"
#include "sml.h" #include "sml.h"
@ -37,15 +37,17 @@ public:
SOURCE_SML = 4 SOURCE_SML = 4
}; };
void init(); void init();
void mqtt();
void loop(); void loop();
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
float getPowerTotal(bool forceUpdate = true); float getPowerTotal(bool forceUpdate = true);
uint32_t getLastPowerMeterUpdate(); uint32_t getLastPowerMeterUpdate();
private: private:
void mqtt();
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties,
const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);
bool _verboseLogging = true; bool _verboseLogging = true;
uint32_t _interval;
uint32_t _lastPowerMeterCheck; uint32_t _lastPowerMeterCheck;
// Used in Power limiter for safety check // Used in Power limiter for safety check
uint32_t _lastPowerMeterUpdate; uint32_t _lastPowerMeterUpdate;
@ -59,7 +61,7 @@ private:
float _powerMeterImport = 0.0; float _powerMeterImport = 0.0;
float _powerMeterExport = 0.0; float _powerMeterExport = 0.0;
bool mqttInitDone = false; std::map<String, float*> _mqttSubscriptions;
void readPowerMeter(); void readPowerMeter();

View File

@ -20,80 +20,78 @@ SoftwareSerial inputSerial;
void PowerMeterClass::init() void PowerMeterClass::init()
{ {
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
using std::placeholders::_5;
using std::placeholders::_6;
_lastPowerMeterCheck = 0; _lastPowerMeterCheck = 0;
_lastPowerMeterUpdate = 0; _lastPowerMeterUpdate = 0;
for (auto const& s: _mqttSubscriptions) { MqttSettings.unsubscribe(s.first); }
_mqttSubscriptions.clear();
CONFIG_T& config = Configuration.get(); CONFIG_T& config = Configuration.get();
if (!config.PowerMeter_Enabled) { if (!config.PowerMeter_Enabled) {
return; return;
} }
if (config.PowerMeter_Source == SOURCE_MQTT) { switch(config.PowerMeter_Source) {
if (strlen(config.PowerMeter_MqttTopicPowerMeter1) > 0) { case SOURCE_MQTT: {
MqttSettings.subscribe(config.PowerMeter_MqttTopicPowerMeter1, 0, std::bind(&PowerMeterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); auto subscribe = [this](char const* topic, float* target) {
if (strlen(topic) == 0) { return; }
MqttSettings.subscribe(topic, 0,
std::bind(&PowerMeterClass::onMqttMessage,
this, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4,
std::placeholders::_5, std::placeholders::_6)
);
_mqttSubscriptions.try_emplace(topic, target);
};
subscribe(config.PowerMeter_MqttTopicPowerMeter1, &_powerMeter1Power);
subscribe(config.PowerMeter_MqttTopicPowerMeter2, &_powerMeter2Power);
subscribe(config.PowerMeter_MqttTopicPowerMeter3, &_powerMeter3Power);
break;
} }
if (strlen(config.PowerMeter_MqttTopicPowerMeter2) > 0) { case SOURCE_SDM1PH:
MqttSettings.subscribe(config.PowerMeter_MqttTopicPowerMeter2, 0, std::bind(&PowerMeterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6)); case SOURCE_SDM3PH:
}
if (strlen(config.PowerMeter_MqttTopicPowerMeter3) > 0) {
MqttSettings.subscribe(config.PowerMeter_MqttTopicPowerMeter3, 0, std::bind(&PowerMeterClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
}
}
if(config.PowerMeter_Source == SOURCE_SDM1PH || config.PowerMeter_Source == SOURCE_SDM3PH) {
sdm.begin(); sdm.begin();
} break;
if (config.PowerMeter_Source == SOURCE_HTTP) { case SOURCE_HTTP:
HttpPowerMeter.init(); HttpPowerMeter.init();
} break;
if (config.PowerMeter_Source == SOURCE_SML) { case SOURCE_SML:
pinMode(SML_RX_PIN, INPUT); pinMode(SML_RX_PIN, INPUT);
inputSerial.begin(9600, SWSERIAL_8N1, SML_RX_PIN, -1, false, 128, 95); inputSerial.begin(9600, SWSERIAL_8N1, SML_RX_PIN, -1, false, 128, 95);
inputSerial.enableRx(true); inputSerial.enableRx(true);
inputSerial.enableTx(false); inputSerial.enableTx(false);
inputSerial.flush(); inputSerial.flush();
break;
} }
mqttInitDone = true;
} }
void PowerMeterClass::onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) void PowerMeterClass::onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total)
{ {
CONFIG_T& config = Configuration.get(); for (auto const& subscription: _mqttSubscriptions) {
if (subscription.first != topic) { continue; }
if (!config.PowerMeter_Enabled || config.PowerMeter_Source != SOURCE_MQTT) { std::string value(reinterpret_cast<const char*>(payload), len);
try {
*subscription.second = std::stof(value);
}
catch(std::invalid_argument const& e) {
MessageOutput.printf("PowerMeterClass: cannot parse payload of topic '%s' as float: %s\r\n",
topic, value.c_str());
return; return;
} }
if (strcmp(topic, config.PowerMeter_MqttTopicPowerMeter1) == 0) {
_powerMeter1Power = std::stof(std::string(reinterpret_cast<const char*>(payload), (unsigned int)len));
}
if (strcmp(topic, config.PowerMeter_MqttTopicPowerMeter2) == 0) {
_powerMeter2Power = std::stof(std::string(reinterpret_cast<const char*>(payload), (unsigned int)len));
}
if (strcmp(topic, config.PowerMeter_MqttTopicPowerMeter3) == 0) {
_powerMeter3Power = std::stof(std::string(reinterpret_cast<const char*>(payload), (unsigned int)len));
}
if (_verboseLogging) { if (_verboseLogging) {
MessageOutput.printf("PowerMeterClass: TotalPower: %5.2f\r\n", getPowerTotal()); MessageOutput.printf("PowerMeterClass: Updated from '%s', TotalPower: %5.2f\r\n",
topic, getPowerTotal());
} }
_lastPowerMeterUpdate = millis(); _lastPowerMeterUpdate = millis();
}
} }
float PowerMeterClass::getPowerTotal(bool forceUpdate) float PowerMeterClass::getPowerTotal(bool forceUpdate)