Skip to content

Commit

Permalink
Merge PR hoylabs#1077 from helgeerbe/powermeter-refactoring
Browse files Browse the repository at this point in the history
this PowerMeter refactoring tackles many issues and prepares to solve many more.
  • Loading branch information
schlimmchen authored Jul 10, 2024
2 parents 6a3f90f + 0c16652 commit e358513
Show file tree
Hide file tree
Showing 45 changed files with 2,871 additions and 1,308 deletions.
109 changes: 81 additions & 28 deletions include/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "PinMapping.h"
#include <cstdint>
#include <ArduinoJson.h>

#define CONFIG_FILENAME "/config.json"
#define CONFIG_VERSION 0x00011c00 // 0.1.28 // make sure to clean all after change
Expand Down Expand Up @@ -30,14 +31,15 @@

#define DEV_MAX_MAPPING_NAME_STRLEN 63

#define POWERMETER_MAX_PHASES 3
#define POWERMETER_MAX_HTTP_URL_STRLEN 1024
#define POWERMETER_MAX_USERNAME_STRLEN 64
#define POWERMETER_MAX_PASSWORD_STRLEN 64
#define POWERMETER_MAX_HTTP_HEADER_KEY_STRLEN 64
#define POWERMETER_MAX_HTTP_HEADER_VALUE_STRLEN 256
#define POWERMETER_MAX_HTTP_JSON_PATH_STRLEN 256
#define POWERMETER_HTTP_TIMEOUT 1000
#define HTTP_REQUEST_MAX_URL_STRLEN 1024
#define HTTP_REQUEST_MAX_USERNAME_STRLEN 64
#define HTTP_REQUEST_MAX_PASSWORD_STRLEN 64
#define HTTP_REQUEST_MAX_HEADER_KEY_STRLEN 64
#define HTTP_REQUEST_MAX_HEADER_VALUE_STRLEN 256

#define POWERMETER_MQTT_MAX_VALUES 3
#define POWERMETER_HTTP_JSON_MAX_VALUES 3
#define POWERMETER_HTTP_JSON_MAX_PATH_STRLEN 256

struct CHANNEL_CONFIG_T {
uint16_t MaxChannelPower;
Expand All @@ -61,22 +63,66 @@ struct INVERTER_CONFIG_T {
CHANNEL_CONFIG_T channel[INV_MAX_CHAN_COUNT];
};

struct POWERMETER_HTTP_PHASE_CONFIG_T {
struct HTTP_REQUEST_CONFIG_T {
char Url[HTTP_REQUEST_MAX_URL_STRLEN + 1];

enum Auth { None, Basic, Digest };
enum Unit { Watts = 0, MilliWatts = 1, KiloWatts = 2 };
bool Enabled;
char Url[POWERMETER_MAX_HTTP_URL_STRLEN + 1];
Auth AuthType;
char Username[POWERMETER_MAX_USERNAME_STRLEN +1];
char Password[POWERMETER_MAX_USERNAME_STRLEN +1];
char HeaderKey[POWERMETER_MAX_HTTP_HEADER_KEY_STRLEN + 1];
char HeaderValue[POWERMETER_MAX_HTTP_HEADER_VALUE_STRLEN + 1];

char Username[HTTP_REQUEST_MAX_USERNAME_STRLEN + 1];
char Password[HTTP_REQUEST_MAX_PASSWORD_STRLEN + 1];
char HeaderKey[HTTP_REQUEST_MAX_HEADER_KEY_STRLEN + 1];
char HeaderValue[HTTP_REQUEST_MAX_HEADER_VALUE_STRLEN + 1];
uint16_t Timeout;
char JsonPath[POWERMETER_MAX_HTTP_JSON_PATH_STRLEN + 1];
};
using HttpRequestConfig = struct HTTP_REQUEST_CONFIG_T;

struct POWERMETER_MQTT_VALUE_T {
char Topic[MQTT_MAX_TOPIC_STRLEN + 1];
char JsonPath[POWERMETER_HTTP_JSON_MAX_PATH_STRLEN + 1];

enum Unit { Watts = 0, MilliWatts = 1, KiloWatts = 2 };
Unit PowerUnit;

bool SignInverted;
};
using PowerMeterHttpConfig = struct POWERMETER_HTTP_PHASE_CONFIG_T;
using PowerMeterMqttValue = struct POWERMETER_MQTT_VALUE_T;

struct POWERMETER_MQTT_CONFIG_T {
PowerMeterMqttValue Values[POWERMETER_MQTT_MAX_VALUES];
};
using PowerMeterMqttConfig = struct POWERMETER_MQTT_CONFIG_T;

struct POWERMETER_SERIAL_SDM_CONFIG_T {
uint32_t Address;
uint32_t PollingInterval;
};
using PowerMeterSerialSdmConfig = struct POWERMETER_SERIAL_SDM_CONFIG_T;

struct POWERMETER_HTTP_JSON_VALUE_T {
HttpRequestConfig HttpRequest;
bool Enabled;
char JsonPath[POWERMETER_HTTP_JSON_MAX_PATH_STRLEN + 1];

enum Unit { Watts = 0, MilliWatts = 1, KiloWatts = 2 };
Unit PowerUnit;

bool SignInverted;
};
using PowerMeterHttpJsonValue = struct POWERMETER_HTTP_JSON_VALUE_T;

struct POWERMETER_HTTP_JSON_CONFIG_T {
uint32_t PollingInterval;
bool IndividualRequests;
PowerMeterHttpJsonValue Values[POWERMETER_HTTP_JSON_MAX_VALUES];
};
using PowerMeterHttpJsonConfig = struct POWERMETER_HTTP_JSON_CONFIG_T;

struct POWERMETER_HTTP_SML_CONFIG_T {
uint32_t PollingInterval;
HttpRequestConfig HttpRequest;
};
using PowerMeterHttpSmlConfig = struct POWERMETER_HTTP_SML_CONFIG_T;

struct CONFIG_T {
struct {
Expand Down Expand Up @@ -187,19 +233,14 @@ struct CONFIG_T {
bool UpdatesOnly;
} Vedirect;

struct {
struct PowerMeterConfig {
bool Enabled;
bool VerboseLogging;
uint32_t Interval;
uint32_t Source;
char MqttTopicPowerMeter1[MQTT_MAX_TOPIC_STRLEN + 1];
char MqttTopicPowerMeter2[MQTT_MAX_TOPIC_STRLEN + 1];
char MqttTopicPowerMeter3[MQTT_MAX_TOPIC_STRLEN + 1];
uint32_t SdmBaudrate;
uint32_t SdmAddress;
uint32_t HttpInterval;
bool HttpIndividualRequests;
PowerMeterHttpConfig Http_Phase[POWERMETER_MAX_PHASES];
PowerMeterMqttConfig Mqtt;
PowerMeterSerialSdmConfig SerialSdm;
PowerMeterHttpJsonConfig HttpJson;
PowerMeterHttpSmlConfig HttpSml;
} PowerMeter;

struct {
Expand Down Expand Up @@ -272,6 +313,18 @@ class ConfigurationClass {
INVERTER_CONFIG_T* getFreeInverterSlot();
INVERTER_CONFIG_T* getInverterConfig(const uint64_t serial);
void deleteInverterById(const uint8_t id);

static void serializeHttpRequestConfig(HttpRequestConfig const& source, JsonObject& target);
static void serializePowerMeterMqttConfig(PowerMeterMqttConfig const& source, JsonObject& target);
static void serializePowerMeterSerialSdmConfig(PowerMeterSerialSdmConfig const& source, JsonObject& target);
static void serializePowerMeterHttpJsonConfig(PowerMeterHttpJsonConfig const& source, JsonObject& target);
static void serializePowerMeterHttpSmlConfig(PowerMeterHttpSmlConfig const& source, JsonObject& target);

static void deserializeHttpRequestConfig(JsonObject const& source, HttpRequestConfig& target);
static void deserializePowerMeterMqttConfig(JsonObject const& source, PowerMeterMqttConfig& target);
static void deserializePowerMeterSerialSdmConfig(JsonObject const& source, PowerMeterSerialSdmConfig& target);
static void deserializePowerMeterHttpJsonConfig(JsonObject const& source, PowerMeterHttpJsonConfig& target);
static void deserializePowerMeterHttpSmlConfig(JsonObject const& source, PowerMeterHttpSmlConfig& target);
};

extern ConfigurationClass Configuration;
77 changes: 77 additions & 0 deletions include/HttpGetter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include "Configuration.h"
#include <memory>
#include <vector>
#include <utility>
#include <string>
#include <HTTPClient.h>
#include <WiFiClient.h>

using up_http_client_t = std::unique_ptr<HTTPClient>;
using sp_wifi_client_t = std::shared_ptr<WiFiClient>;

class HttpRequestResult {
public:
HttpRequestResult(bool success,
up_http_client_t upHttpClient = nullptr,
sp_wifi_client_t spWiFiClient = nullptr)
: _success(success)
, _upHttpClient(std::move(upHttpClient))
, _spWiFiClient(std::move(spWiFiClient)) { }

~HttpRequestResult() {
// the wifi client *must* die *after* the http client, as the http
// client uses the wifi client in its destructor.
if (_upHttpClient) { _upHttpClient->end(); }
_upHttpClient = nullptr;
_spWiFiClient = nullptr;
}

HttpRequestResult(HttpRequestResult const&) = delete;
HttpRequestResult(HttpRequestResult&&) = delete;
HttpRequestResult& operator=(HttpRequestResult const&) = delete;
HttpRequestResult& operator=(HttpRequestResult&&) = delete;

operator bool() const { return _success; }

Stream* getStream() {
if(!_upHttpClient) { return nullptr; }
return _upHttpClient->getStreamPtr();
}

private:
bool _success;
up_http_client_t _upHttpClient;
sp_wifi_client_t _spWiFiClient;
};

class HttpGetter {
public:
explicit HttpGetter(HttpRequestConfig const& cfg)
: _config(cfg) { }

bool init();
void addHeader(char const* key, char const* value);
HttpRequestResult performGetRequest();

char const* getErrorText() const { return _errBuffer; }

private:
String getAuthDigest(String const& authReq, unsigned int counter);
HttpRequestConfig const& _config;

template<typename... Args>
void logError(char const* format, Args... args);
char _errBuffer[256];

bool _useHttps;
String _host;
String _uri;
uint16_t _port;

sp_wifi_client_t _spWiFiClient; // reused for multiple HTTP requests

std::vector<std::pair<std::string, std::string>> _additionalHeaders;
};
34 changes: 0 additions & 34 deletions include/HttpPowerMeter.h

This file was deleted.

71 changes: 10 additions & 61 deletions include/PowerMeter.h
Original file line number Diff line number Diff line change
@@ -1,78 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once

#include "Configuration.h"
#include <espMqttClient.h>
#include <Arduino.h>
#include <map>
#include <list>
#include <mutex>
#include "SDM.h"
#include "sml.h"
#include "PowerMeterProvider.h"
#include <TaskSchedulerDeclarations.h>
#include <SoftwareSerial.h>

typedef struct {
const unsigned char OBIS[6];
void (*Fn)(double&);
float* Arg;
} OBISHandler;
#include <memory>
#include <mutex>

class PowerMeterClass {
public:
enum class Source : unsigned {
MQTT = 0,
SDM1PH = 1,
SDM3PH = 2,
HTTP = 3,
SML = 4,
SMAHM2 = 5
};
void init(Scheduler& scheduler);
float getPowerTotal(bool forceUpdate = true);
uint32_t getLastPowerMeterUpdate();
bool isDataValid();

void updateSettings();

float getPowerTotal() const;
uint32_t getLastUpdate() const;
bool isDataValid() const;

private:
void loop();
void mqtt();

void onMqttMessage(const espMqttClientTypes::MessageProperties& properties,
const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total);

Task _loopTask;

bool _verboseLogging = true;
uint32_t _lastPowerMeterCheck;
// Used in Power limiter for safety check
uint32_t _lastPowerMeterUpdate;

float _powerMeter1Power = 0.0;
float _powerMeter2Power = 0.0;
float _powerMeter3Power = 0.0;
float _powerMeter1Voltage = 0.0;
float _powerMeter2Voltage = 0.0;
float _powerMeter3Voltage = 0.0;
float _powerMeterImport = 0.0;
float _powerMeterExport = 0.0;

std::map<String, float*> _mqttSubscriptions;

mutable std::mutex _mutex;

static char constexpr _sdmSerialPortOwner[] = "SDM power meter";
std::unique_ptr<HardwareSerial> _upSdmSerial = nullptr;
std::unique_ptr<SDM> _upSdm = nullptr;
std::unique_ptr<SoftwareSerial> _upSmlSerial = nullptr;

void readPowerMeter();

bool smlReadLoop();
const std::list<OBISHandler> smlHandlerList{
{{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeter1Power},
{{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterImport},
{{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}
};
std::unique_ptr<PowerMeterProvider> _upProvider = nullptr;
};

extern PowerMeterClass PowerMeter;
Loading

0 comments on commit e358513

Please sign in to comment.