Adding enable/disable option and pin to control a switch/relais to power the Huawei PSU

This commit is contained in:
MalteSchm 2023-03-26 11:00:37 +02:00
parent db7ad52a4d
commit 0b5c47cd2e
10 changed files with 154 additions and 34 deletions

View File

@ -119,6 +119,7 @@ struct CONFIG_T {
float PowerLimiter_VoltageLoadCorrectionFactor;
bool Battery_Enabled;
bool Huawei_Enabled;
char Security_Password[WIFI_MAX_PASSWORD_STRLEN + 1];
bool Security_AllowReadonly;

View File

@ -45,10 +45,12 @@ struct RectifierParameters_t {
class HuaweiCanClass {
public:
void init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs);
void init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power);
void loop();
void setValue(float in, uint8_t parameterType);
RectifierParameters_t& get();
void setPower(bool power);
RectifierParameters_t * get();
unsigned long getLastUpdate();
private:
@ -62,6 +64,7 @@ private:
SPIClass *hspi;
MCP_CAN *CAN;
uint8_t _huawei_irq;
uint8_t _huawei_power;
};

View File

@ -38,6 +38,7 @@ struct PinMapping_t {
uint8_t huawei_clk;
uint8_t huawei_irq;
uint8_t huawei_cs;
uint8_t huawei_power;
};
class PinMappingClass {

View File

@ -2,14 +2,17 @@
#pragma once
#include <ESPAsyncWebServer.h>
#include <AsyncJson.h>
class WebApiHuaweiClass {
public:
void init(AsyncWebServer* server);
void loop();
void getJsonData(JsonObject& root);
private:
void onStatus(AsyncWebServerRequest* request);
void onAdminGet(AsyncWebServerRequest* request);
void onAdminPost(AsyncWebServerRequest* request);
void onPost(AsyncWebServerRequest* request);
AsyncWebServer* _server;

View File

@ -109,3 +109,5 @@
#define POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR 0.001
#define BATTERY_ENABLED false
#define HUAWEI_ENABLED false

View File

@ -138,6 +138,9 @@ bool ConfigurationClass::write()
JsonObject battery = doc.createNestedObject("battery");
battery["enabled"] = config.Battery_Enabled;
JsonObject huawei = doc.createNestedObject("huawei");
huawei["enabled"] = config.Huawei_Enabled;
// Serialize JSON to file
if (serializeJson(doc, f) == 0) {
MessageOutput.println("Failed to write file");
@ -303,6 +306,9 @@ bool ConfigurationClass::read()
JsonObject battery = doc["battery"];
config.Battery_Enabled = battery["enabled"] | BATTERY_ENABLED;
JsonObject huawei = doc["huawei"];
config.Huawei_Enabled = huawei["enabled"] | HUAWEI_ENABLED;
f.close();
return true;
}

View File

@ -11,7 +11,7 @@
HuaweiCanClass HuaweiCan;
void HuaweiCanClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs)
void HuaweiCanClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huawei_clk, uint8_t huawei_irq, uint8_t huawei_cs, uint8_t huawei_power)
{
hspi = new SPIClass(VSPI);
@ -32,11 +32,15 @@ void HuaweiCanClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huaw
CAN->setMode(MCP_NORMAL); // Change to normal mode to allow messages to be transmitted
pinMode(huawei_power, OUTPUT);
digitalWrite(huawei_power,HIGH);
_huawei_power = huawei_power;
}
RectifierParameters_t& HuaweiCanClass::get()
RectifierParameters_t * HuaweiCanClass::get()
{
return _rp;
return &_rp;
}
unsigned long HuaweiCanClass::getLastUpdate()
@ -179,3 +183,9 @@ void HuaweiCanClass::setValue(float in, uint8_t parameterType)
MessageOutput.println("Error Sending Message...");
}
}
void HuaweiCanClass::setPower(bool power) {
digitalWrite(_huawei_power, power);
}

View File

@ -72,6 +72,7 @@ PinMappingClass::PinMappingClass()
_pinMapping.huawei_clk = HUAWEI_PIN_SCLK;
_pinMapping.huawei_cs = HUAWEI_PIN_CS;
_pinMapping.huawei_irq = HUAWEI_PIN_IRQ;
_pinMapping.huawei_power = HUAWEI_PIN_POWER;
}
PinMapping_t& PinMappingClass::get()
@ -135,6 +136,7 @@ bool PinMappingClass::init(const String& deviceMapping)
_pinMapping.huawei_clk = doc[i]["huawei"]["clk"] | HUAWEI_PIN_SCLK;
_pinMapping.huawei_irq = doc[i]["huawei"]["irq"] | HUAWEI_PIN_IRQ;
_pinMapping.huawei_cs = doc[i]["huawei"]["cs"] | HUAWEI_PIN_CS;
_pinMapping.huawei_power = doc[i]["huawei"]["power"] | HUAWEI_PIN_POWER;
return true;
}
@ -176,5 +178,6 @@ bool PinMappingClass::isValidHuaweiConfig()
&& _pinMapping.huawei_mosi > 0
&& _pinMapping.huawei_clk > 0
&& _pinMapping.huawei_irq > 0
&& _pinMapping.huawei_cs > 0;
&& _pinMapping.huawei_cs > 0
&& _pinMapping.huawei_power > 0;
}

View File

@ -4,6 +4,7 @@
*/
#include "WebApi_Huawei.h"
#include "Huawei_can.h"
#include "Configuration.h"
#include "WebApi.h"
#include "WebApi_errors.h"
#include <AsyncJson.h>
@ -16,6 +17,8 @@ void WebApiHuaweiClass::init(AsyncWebServer* server)
_server = server;
_server->on("/api/huawei/status", HTTP_GET, std::bind(&WebApiHuaweiClass::onStatus, this, _1));
_server->on("/api/huawei/config", HTTP_GET, std::bind(&WebApiHuaweiClass::onAdminGet, this, _1));
_server->on("/api/huawei/config", HTTP_POST, std::bind(&WebApiHuaweiClass::onAdminPost, this, _1));
_server->on("/api/huawei/limit/config", HTTP_POST, std::bind(&WebApiHuaweiClass::onPost, this, _1));
}
@ -23,6 +26,33 @@ void WebApiHuaweiClass::loop()
{
}
void WebApiHuaweiClass::getJsonData(JsonObject& root) {
const RectifierParameters_t * rp = HuaweiCan.get();
root["data_age"] = (millis() - HuaweiCan.getLastUpdate()) / 1000;
root[F("input_voltage")]["v"] = rp->input_voltage;
root[F("input_voltage")]["u"] = "V";
root[F("input_current")]["v"] = rp->input_current;
root[F("input_current")]["u"] = "A";
root[F("input_power")]["v"] = rp->input_power;
root[F("input_power")]["u"] = "W";
root[F("output_voltage")]["v"] = rp->output_voltage;
root[F("output_voltage")]["u"] = "V";
root[F("output_current")]["v"] = rp->output_current;
root[F("output_current")]["u"] = "A";
root[F("max_output_current")]["v"] = rp->max_output_current;
root[F("max_output_current")]["u"] = "A";
root[F("output_power")]["v"] = rp->output_power;
root[F("output_power")]["u"] = "W";
root[F("input_temp")]["v"] = rp->input_temp;
root[F("input_temp")]["u"] = "°C";
root[F("output_temp")]["v"] = rp->output_temp;
root[F("output_temp")]["u"] = "°C";
root[F("efficiency")]["v"] = rp->efficiency;
root[F("efficiency")]["u"] = "%";
}
void WebApiHuaweiClass::onStatus(AsyncWebServerRequest* request)
{
if (!WebApi.checkCredentialsReadonly(request)) {
@ -31,30 +61,7 @@ void WebApiHuaweiClass::onStatus(AsyncWebServerRequest* request)
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
const RectifierParameters_t& rp = HuaweiCan.get();
root["data_age"] = (millis() - HuaweiCan.getLastUpdate()) / 1000;
root[F("input_voltage")]["v"] = rp.input_voltage;
root[F("input_voltage")]["u"] = "V";
root[F("input_current")]["v"] = rp.input_current;
root[F("input_current")]["u"] = "A";
root[F("input_power")]["v"] = rp.input_power;
root[F("input_power")]["u"] = "W";
root[F("output_voltage")]["v"] = rp.output_voltage;
root[F("output_voltage")]["u"] = "V";
root[F("output_current")]["v"] = rp.output_current;
root[F("output_current")]["u"] = "A";
root[F("max_output_current")]["v"] = rp.max_output_current;
root[F("max_output_current")]["u"] = "A";
root[F("output_power")]["v"] = rp.output_power;
root[F("output_power")]["u"] = "W";
root[F("input_temp")]["v"] = rp.input_temp;
root[F("input_temp")]["u"] = "°C";
root[F("output_temp")]["v"] = rp.output_temp;
root[F("output_temp")]["u"] = "°C";
root[F("efficiency")]["v"] = rp.efficiency;
root[F("efficiency")]["u"] = "%";
getJsonData(root);
response->setLength();
request->send(response);
@ -166,3 +173,83 @@ void WebApiHuaweiClass::onPost(AsyncWebServerRequest* request)
response->setLength();
request->send(response);
}
void WebApiHuaweiClass::onAdminGet(AsyncWebServerRequest* request)
{
if (!WebApi.checkCredentialsReadonly(request)) {
return;
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot();
const CONFIG_T& config = Configuration.get();
root[F("enabled")] = config.Huawei_Enabled;
response->setLength();
request->send(response);
}
void WebApiHuaweiClass::onAdminPost(AsyncWebServerRequest* request)
{
if (!WebApi.checkCredentials(request)) {
return;
}
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject retMsg = response->getRoot();
retMsg[F("type")] = F("warning");
if (!request->hasParam("data", true)) {
retMsg[F("message")] = F("No values found!");
retMsg[F("code")] = WebApiError::GenericNoValueFound;
response->setLength();
request->send(response);
return;
}
String json = request->getParam("data", true)->value();
if (json.length() > 1024) {
retMsg[F("message")] = F("Data too large!");
retMsg[F("code")] = WebApiError::GenericDataTooLarge;
response->setLength();
request->send(response);
return;
}
DynamicJsonDocument root(1024);
DeserializationError error = deserializeJson(root, json);
if (error) {
retMsg[F("message")] = F("Failed to parse data!");
retMsg[F("code")] = WebApiError::GenericParseError;
response->setLength();
request->send(response);
return;
}
if (!(root.containsKey("enabled"))) {
retMsg[F("message")] = F("Values are missing!");
retMsg[F("code")] = WebApiError::GenericValueMissing;
response->setLength();
request->send(response);
return;
}
CONFIG_T& config = Configuration.get();
config.Huawei_Enabled = root[F("enabled")].as<bool>();
Configuration.write();
retMsg[F("type")] = F("success");
retMsg[F("message")] = F("Settings saved!");
retMsg[F("code")] = WebApiError::GenericSuccess;
response->setLength();
request->send(response);
HuaweiCan.setPower(config.Huawei_Enabled);
}

View File

@ -183,6 +183,10 @@ void WebApiWsLiveClass::generateJsonResponse(JsonVariant& root)
JsonObject vedirectObj = root.createNestedObject("vedirect");
vedirectObj[F("enabled")] = Configuration.get().Vedirect_Enabled;
JsonObject huaweiObj = root.createNestedObject("huawei");
huaweiObj[F("enabled")] = Configuration.get().Huawei_Enabled;
}
void WebApiWsLiveClass::addField(JsonObject& root, uint8_t idx, std::shared_ptr<InverterAbstract> inv, ChannelType_t type, ChannelNum_t channel, FieldId_t fieldId, String topic)