Skip to content

Commit

Permalink
huawei grid charger: use DataPointContainer
Browse files Browse the repository at this point in the history
  • Loading branch information
schlimmchen committed Dec 26, 2024
1 parent 229a1f4 commit 4c5634a
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 171 deletions.
14 changes: 12 additions & 2 deletions include/DataPoints.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <string>
#include <unordered_map>
#include <variant>
#include <limits>
#include <algorithm>

using tCellVoltages = std::map<uint8_t, uint16_t>;

Expand Down Expand Up @@ -58,8 +60,6 @@ class DataPointContainer {
public:
DataPointContainer() = default;

//template<Label L> using Traits = LabelTraits<L>;

template<Label L>
void add(typename Traits<L>::type val) {
_dataPoints.emplace(
Expand Down Expand Up @@ -114,6 +114,16 @@ class DataPointContainer {
}
}

uint32_t getLastUpdate() const
{
uint32_t now = millis();
uint32_t diff = std::numeric_limits<uint32_t>::max()/2;
for (auto iter = _dataPoints.cbegin(); iter != _dataPoints.cend(); ++iter) {
diff = std::min(diff, now - iter->second.getTimestamp());
}
return now - diff;
}

private:
tMap _dataPoints;
};
24 changes: 6 additions & 18 deletions include/gridcharger/huawei/Controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

#include <cstdint>
#include <memory>
#include <ArduinoJson.h>
#include <TaskSchedulerDeclarations.h>
#include <gridcharger/huawei/HardwareInterface.h>
#include <gridcharger/huawei/DataPoints.h>

namespace GridCharger::Huawei {

Expand All @@ -17,29 +19,16 @@ namespace GridCharger::Huawei {
#define HUAWEI_MODE_AUTO_EXT 2
#define HUAWEI_MODE_AUTO_INT 3

typedef struct RectifierParameters {
float input_voltage;
float input_frequency;
float input_current;
float input_power;
float input_temp;
float efficiency;
float output_voltage;
float output_current;
float max_output_current;
float output_power;
float output_temp;
} RectifierParameters_t;

class Controller {
public:
void init(Scheduler& scheduler);
void updateSettings();
void setParameter(float val, HardwareInterface::Setting setting);
void setMode(uint8_t mode);

RectifierParameters_t * get();
uint32_t getLastUpdate() const { return _lastUpdateReceivedMillis; };
DataPointContainer const& getDataPoints() const { return _dataPoints; }
void getJsonData(JsonVariant& root) const;

bool getAutoPowerStatus() const { return _autoPowerEnabled; };
uint8_t getMode() const { return _mode; };

Expand All @@ -60,9 +49,8 @@ class Controller {
std::mutex _mutex;
uint8_t _mode = HUAWEI_MODE_AUTO_EXT;

RectifierParameters_t _rp;
DataPointContainer _dataPoints;

uint32_t _lastUpdateReceivedMillis; // Timestamp for last data seen from the PSU
uint32_t _outputCurrentOnSinceMillis; // Timestamp since when the PSU was idle at zero amps
uint32_t _nextAutoModePeriodicIntMillis; // When to set the next output voltage in automatic mode
uint32_t _lastPowerMeterUpdateReceivedMillis; // Timestamp of last seen power meter value
Expand Down
51 changes: 51 additions & 0 deletions include/gridcharger/huawei/DataPoints.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

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

namespace GridCharger::Huawei {

enum class DataPointLabel : uint8_t {
InputPower = 0x70,
InputFrequency = 0x71,
InputCurrent = 0x72,
OutputPower = 0x73,
Efficiency = 0x74,
OutputVoltage = 0x75,
OutputCurrentMax = 0x76,
InputVoltage = 0x78,
OutputTemperature = 0x7F,
InputTemperature = 0x80,
OutputCurrent = 0x81
};

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(InputPower, "W");
LABEL_TRAIT(InputFrequency, "Hz");
LABEL_TRAIT(InputCurrent, "A");
LABEL_TRAIT(OutputPower, "W");
LABEL_TRAIT(Efficiency, "%");
LABEL_TRAIT(OutputVoltage, "V");
LABEL_TRAIT(OutputCurrentMax, "A");
LABEL_TRAIT(InputVoltage, "V");
LABEL_TRAIT(OutputTemperature, "°C");
LABEL_TRAIT(InputTemperature, "°C");
LABEL_TRAIT(OutputCurrent, "A");
#undef LABEL_TRAIT

} // namespace GridCharger::Huawei

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

namespace GridCharger::Huawei {
using DataPointContainer = DataPointContainer<DataPoint<float>, DataPointLabel, DataPointLabelTraits>;
} // namespace GridCharger::Huawei
22 changes: 5 additions & 17 deletions include/gridcharger/huawei/HardwareInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
#include <atomic>
#include <array>
#include <mutex>
#include <map>
#include <memory>
#include <queue>
#include <cstdint>
#include <FreeRTOS.h>
#include <freertos/task.h>
#include <gridcharger/huawei/DataPoints.h>

namespace GridCharger::Huawei {

Expand All @@ -28,21 +29,7 @@ class HardwareInterface {
};
void setParameter(Setting setting, float val);

enum class Property : uint8_t {
InputPower = 0x70,
InputFrequency = 0x71,
InputCurrent = 0x72,
OutputPower = 0x73,
Efficiency = 0x74,
OutputVoltage = 0x75,
OutputCurrentMax = 0x76,
InputVoltage = 0x78,
OutputTemperature = 0x7F,
InputTemperature = 0x80,
OutputCurrent = 0x81
};
using property_t = std::pair<float, uint32_t>; // value and timestamp
property_t getParameter(Property prop) const;
std::unique_ptr<DataPointContainer> getCurrentData() { return std::move(_upDataCurrent); }

static uint32_t constexpr _dataRequestIntervalMillis = 2500;

Expand Down Expand Up @@ -73,7 +60,8 @@ class HardwareInterface {
std::atomic<bool> _taskDone = false;
bool _stopLoop = false;

std::map<HardwareInterface::Property, HardwareInterface::property_t> _stats;
std::unique_ptr<DataPointContainer> _upDataCurrent = nullptr;
std::unique_ptr<DataPointContainer> _upDataInFlight = nullptr;

std::queue<std::pair<HardwareInterface::Setting, uint16_t>> _sendQueue;

Expand Down
5 changes: 5 additions & 0 deletions src/DataPoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ template std::string dataPointValueToStr(uint8_t const& v);
template std::string dataPointValueToStr(uint16_t const& v);
template std::string dataPointValueToStr(uint32_t const& v);

template<> std::string dataPointValueToStr(float const& v) {
snprintf(conversionBuffer, sizeof(conversionBuffer), "%.2f", v);
return conversionBuffer;
}

template<>
std::string dataPointValueToStr(std::string const& v) {
return v;
Expand Down
48 changes: 29 additions & 19 deletions src/MqttHandleHuawei.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,36 @@ void MqttHandleHuaweiClass::loop()
return;
}

auto const* rp = HuaweiCan.get();

if ((millis() - _lastPublish) > (config.Mqtt.PublishInterval * 1000) ) {
MqttSettings.publish("huawei/data_age", String((millis() - HuaweiCan.getLastUpdate()) / 1000));
MqttSettings.publish("huawei/input_voltage", String(rp->input_voltage));
MqttSettings.publish("huawei/input_current", String(rp->input_current));
MqttSettings.publish("huawei/input_power", String(rp->input_power));
MqttSettings.publish("huawei/output_voltage", String(rp->output_voltage));
MqttSettings.publish("huawei/output_current", String(rp->output_current));
MqttSettings.publish("huawei/max_output_current", String(rp->max_output_current));
MqttSettings.publish("huawei/output_power", String(rp->output_power));
MqttSettings.publish("huawei/input_temp", String(rp->input_temp));
MqttSettings.publish("huawei/output_temp", String(rp->output_temp));
MqttSettings.publish("huawei/efficiency", String(rp->efficiency));
MqttSettings.publish("huawei/mode", String(HuaweiCan.getMode()));


yield();
_lastPublish = millis();
if ((millis() - _lastPublish) <= (config.Mqtt.PublishInterval * 1000)) {
return;
}

auto const& dataPoints = HuaweiCan.getDataPoints();

#define PUB(l, t) \
{ \
auto oDataPoint = dataPoints.get<GridCharger::Huawei::DataPointLabel::l>(); \
if (oDataPoint) { \
MqttSettings.publish("huawei/" t, String(*oDataPoint)); \
} \
}

PUB(InputVoltage, "input_voltage");
PUB(InputCurrent, "input_current");
PUB(InputPower, "input_power");
PUB(OutputVoltage, "output_voltage");
PUB(OutputCurrent, "output_current");
PUB(OutputCurrentMax, "max_output_current");
PUB(OutputPower, "output_power");
PUB(InputTemperature, "input_temp");
PUB(OutputTemperature, "output_temp");
PUB(Efficiency, "efficiency");
#undef PUB

MqttSettings.publish("huawei/data_age", String((millis() - dataPoints.getLastUpdate()) / 1000));
MqttSettings.publish("huawei/mode", String(HuaweiCan.getMode()));

_lastPublish = millis();
}


Expand Down
29 changes: 1 addition & 28 deletions src/WebApi_Huawei.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,6 @@ void WebApiHuaweiClass::init(AsyncWebServer& server, Scheduler& scheduler)
_server->on("/api/huawei/limit/config", HTTP_POST, std::bind(&WebApiHuaweiClass::onPost, this, _1));
}

void WebApiHuaweiClass::getJsonData(JsonVariant& root) {
auto const* rp = HuaweiCan.get();

root["data_age"] = (millis() - HuaweiCan.getLastUpdate()) / 1000;
root["input_voltage"]["v"] = rp->input_voltage;
root["input_voltage"]["u"] = "V";
root["input_current"]["v"] = rp->input_current;
root["input_current"]["u"] = "A";
root["input_power"]["v"] = rp->input_power;
root["input_power"]["u"] = "W";
root["output_voltage"]["v"] = rp->output_voltage;
root["output_voltage"]["u"] = "V";
root["output_current"]["v"] = rp->output_current;
root["output_current"]["u"] = "A";
root["max_output_current"]["v"] = rp->max_output_current;
root["max_output_current"]["u"] = "A";
root["output_power"]["v"] = rp->output_power;
root["output_power"]["u"] = "W";
root["input_temp"]["v"] = rp->input_temp;
root["input_temp"]["u"] = "°C";
root["output_temp"]["v"] = rp->output_temp;
root["output_temp"]["u"] = "°C";
root["efficiency"]["v"] = rp->efficiency * 100;
root["efficiency"]["u"] = "%";

}

void WebApiHuaweiClass::onStatus(AsyncWebServerRequest* request)
{
if (!WebApi.checkCredentialsReadonly(request)) {
Expand All @@ -59,7 +32,7 @@ void WebApiHuaweiClass::onStatus(AsyncWebServerRequest* request)

AsyncJsonResponse* response = new AsyncJsonResponse();
auto& root = response->getRoot();
getJsonData(root);
HuaweiCan.getJsonData(root);

response->setLength();
request->send(response);
Expand Down
25 changes: 1 addition & 24 deletions src/WebApi_ws_Huawei.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,30 +99,7 @@ void WebApiWsHuaweiLiveClass::sendDataTaskCb()

void WebApiWsHuaweiLiveClass::generateCommonJsonResponse(JsonVariant& root)
{
auto const* rp = HuaweiCan.get();

root["data_age"] = (millis() - HuaweiCan.getLastUpdate()) / 1000;
root["input_voltage"]["v"] = rp->input_voltage;
root["input_voltage"]["u"] = "V";
root["input_current"]["v"] = rp->input_current;
root["input_current"]["u"] = "A";
root["input_power"]["v"] = rp->input_power;
root["input_power"]["u"] = "W";
root["output_voltage"]["v"] = rp->output_voltage;
root["output_voltage"]["u"] = "V";
root["output_current"]["v"] = rp->output_current;
root["output_current"]["u"] = "A";
root["max_output_current"]["v"] = rp->max_output_current;
root["max_output_current"]["u"] = "A";
root["output_power"]["v"] = rp->output_power;
root["output_power"]["u"] = "W";
root["input_temp"]["v"] = rp->input_temp;
root["input_temp"]["u"] = "°C";
root["output_temp"]["v"] = rp->output_temp;
root["output_temp"]["u"] = "°C";
root["efficiency"]["v"] = rp->efficiency * 100;
root["efficiency"]["u"] = "%";

HuaweiCan.getJsonData(root);
}

void WebApiWsHuaweiLiveClass::onWebsocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len)
Expand Down
9 changes: 6 additions & 3 deletions src/WebApi_ws_live.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,16 @@ void WebApiWsLiveClass::generateOnBatteryJsonResponse(JsonVariant& root, bool al
if (!all) { _lastPublishVictron = millis(); }
}

if (all || (HuaweiCan.getLastUpdate() - _lastPublishHuawei) < halfOfAllMillis ) {
if (all || (HuaweiCan.getDataPoints().getLastUpdate() - _lastPublishHuawei) < halfOfAllMillis ) {
auto huaweiObj = root["huawei"].to<JsonObject>();
huaweiObj["enabled"] = config.Huawei.Enabled;

if (config.Huawei.Enabled) {
auto const* rp = HuaweiCan.get();
addTotalField(huaweiObj, "Power", rp->input_power, "W", 2);
auto const& dataPoints = HuaweiCan.getDataPoints();
auto oInputPower = dataPoints.get<GridCharger::Huawei::DataPointLabel::InputPower>();
if (oInputPower) {
addTotalField(huaweiObj, "Power", *oInputPower, "W", 2);
}
}

if (!all) { _lastPublishHuawei = millis(); }
Expand Down
Loading

0 comments on commit 4c5634a

Please sign in to comment.