Merge branch 'pr/MalteSchm/288' into development
This commit is contained in:
commit
1f39ed7b9b
@ -122,6 +122,20 @@ Other settings are:
|
||||
#### Power Limiter States
|
||||

|
||||
|
||||
### Huawei PSU
|
||||
|
||||
The Huawei PSU can be used to charge a battery. This can be be useful if an external (AC) connected solar system shall be utilized or if variable energy prices should be exploited.
|
||||
|
||||
Some points for consideration are:
|
||||
* Make sure to consider the PSU voltage range when selecting the battery voltage as lower voltages <42V are not supported.
|
||||
* The PSU runs a noisy fan and it is therefore desireable to switch it off when not being used. Some users have found that switching the slot detect pins with a relay accomplishes this. A GPIO pin is made available from the ESP to turn the PSU on/off
|
||||
|
||||
#### Operation modes
|
||||
|
||||
openDTU-onBattery supports three operation modes for the Huawei PSU:
|
||||
1. Fully manual - In this mode the PSU needs to be turned on/off externally using MQTT and voltage and current limits need to be provided. See [MQTT Documentation](docs/MQTT_Topics.md) for details on these commands
|
||||
2. Manual with auto power on / off - In this mode the PSU is turned on when a current limit > 1A is set. If the current limit is < 1A for some time the PSU is turned off. Current and voltage limits need to be provided externally using MQTT. See [MQTT Documentation](docs/MQTT_Topics.md) for details on these commands.
|
||||
3. Automatic - In this mode the PSU power is controlled by the Power Meter and information provided in the web-interface. If excess power is present the PSU is turned on. The voltage limit is set as per web-interface and the current limit is set so that the maximum PSU output power equals the Power Meter value. Minium and maximum PSU power levels as configured in the web-interface are respected in this process. The PSU is turned off if the output current is limited and the output power drops below the minium power level. This will disable automatic mode until the battery is discharged below the start voltage level (set in the web-interface). This mode can be enabled using the web-interface and MQTT. See [MQTT Documentation](docs/MQTT_Topics.md)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@ -158,7 +158,7 @@ cmd topics are used to set values. Status topics are updated from values set in
|
||||
| --------------------------------------- | ----- | ---------------------------------------------------- | -------------------------- |
|
||||
| huawei/cmd/limit_online_voltage | W | Online voltage (i.e. CAN bus connected) | Volt (V) |
|
||||
| huawei/cmd/limit_online_current | W | Online current (i.e. CAN bus connected) | Ampere (A) |
|
||||
| huawei/cmd/power | W | Controls output pin GPIO to drive solid state relais | 0 / 1 |
|
||||
| huawei/cmd/mode | W | Controls GPIO output pin to switch slot detect | 0 (off) / 1 (on) / 2 (set automatically depending on online_current value) / 3 (set automatically based on Power Meter reading ) |
|
||||
| huawei/data_age | R | How old the data is | Seconds |
|
||||
| huawei/input_voltage | R | Input voltage | Volt (V) |
|
||||
| huawei/input_current | R | Input current | Ampere (A) |
|
||||
|
||||
@ -157,6 +157,11 @@ struct CONFIG_T {
|
||||
|
||||
bool Battery_Enabled;
|
||||
bool Huawei_Enabled;
|
||||
bool Huawei_Auto_Power_Enabled;
|
||||
float Huawei_Auto_Power_Voltage_Limit;
|
||||
float Huawei_Auto_Power_Enable_Voltage_Limit;
|
||||
float Huawei_Auto_Power_Lower_Power_Limit;
|
||||
float Huawei_Auto_Power_Upper_Power_Limit;
|
||||
|
||||
char Security_Password[WIFI_MAX_PASSWORD_STRLEN + 1];
|
||||
bool Security_AllowReadonly;
|
||||
|
||||
@ -52,6 +52,16 @@
|
||||
#define R48xx_DATA_OUTPUT_CURRENT 0x81
|
||||
#define R48xx_DATA_OUTPUT_CURRENT1 0x82
|
||||
|
||||
#define HUAWEI_MODE_OFF 0
|
||||
#define HUAWEI_MODE_ON 1
|
||||
#define HUAWEI_MODE_AUTO_EXT 2
|
||||
#define HUAWEI_MODE_AUTO_INT 3
|
||||
|
||||
// Wait time/current before shuting down the PSU / charger
|
||||
// This is set to allow the fan to run for some time
|
||||
#define HUAWEI_AUTO_MODE_SHUTDOWN_DELAY 60000
|
||||
#define HUAWEI_AUTO_MODE_SHUTDOWN_CURRENT 1.0
|
||||
|
||||
struct RectifierParameters_t {
|
||||
float input_voltage;
|
||||
float input_frequency;
|
||||
@ -72,24 +82,33 @@ public:
|
||||
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);
|
||||
void setPower(bool power);
|
||||
void setMode(uint8_t mode);
|
||||
|
||||
RectifierParameters_t * get();
|
||||
uint32_t getLastUpdate();
|
||||
bool getAutoPowerStatus();
|
||||
|
||||
private:
|
||||
void sendRequest();
|
||||
void onReceive(uint8_t* frame, uint8_t len);
|
||||
|
||||
uint32_t previousMillis;
|
||||
uint32_t lastUpdate;
|
||||
RectifierParameters_t _rp;
|
||||
|
||||
SPIClass *spi;
|
||||
MCP_CAN *CAN;
|
||||
uint8_t _huawei_irq;
|
||||
uint8_t _huawei_power;
|
||||
bool initialized = false;
|
||||
bool _initialized = false;
|
||||
uint8_t _huawei_irq; // IRQ pin
|
||||
uint8_t _huawei_power; // Power pin
|
||||
uint8_t _mode = HUAWEI_MODE_AUTO_EXT;
|
||||
|
||||
RectifierParameters_t _rp;
|
||||
|
||||
uint32_t _lastUpdateReceivedMillis; // Timestamp for last data seen from the PSU
|
||||
uint32_t _nextRequestMillis = 0; // When to send next data request to PSU
|
||||
uint32_t _nextAutoModePeriodicIntMillis; // When to send the next output volume request in Automatic mode
|
||||
uint32_t _lastPowerMeterUpdateReceivedMillis; // Timestamp of last power meter value
|
||||
uint32_t _outputCurrentOnSinceMillis; // Timestamp since when the PSU was idle at zero amps
|
||||
bool _newOutputPowerReceived = false;
|
||||
uint8_t _autoPowerEnabled = false;
|
||||
bool _autoPowerActive = false;
|
||||
};
|
||||
|
||||
extern HuaweiCanClass HuaweiCan;
|
||||
|
||||
@ -130,3 +130,7 @@
|
||||
#define BATTERY_ENABLED false
|
||||
|
||||
#define HUAWEI_ENABLED false
|
||||
#define HUAWEI_AUTO_POWER_VOLTAGE_LIMIT 42.0
|
||||
#define HUAWEI_AUTO_POWER_ENABLE_VOLTAGE_LIMIT 42.0
|
||||
#define HUAWEI_AUTO_POWER_LOWER_POWER_LIMIT 150
|
||||
#define HUAWEI_AUTO_POWER_UPPER_POWER_LIMIT 2000
|
||||
|
||||
@ -173,6 +173,11 @@ bool ConfigurationClass::write()
|
||||
|
||||
JsonObject huawei = doc.createNestedObject("huawei");
|
||||
huawei["enabled"] = config.Huawei_Enabled;
|
||||
huawei["auto_power_enabled"] = config.Huawei_Auto_Power_Enabled;
|
||||
huawei["voltage_limit"] = config.Huawei_Auto_Power_Voltage_Limit;
|
||||
huawei["enable_voltage_limit"] = config.Huawei_Auto_Power_Enable_Voltage_Limit;
|
||||
huawei["lower_power_limit"] = config.Huawei_Auto_Power_Lower_Power_Limit;
|
||||
huawei["upper_power_limit"] = config.Huawei_Auto_Power_Upper_Power_Limit;
|
||||
|
||||
// Serialize JSON to file
|
||||
if (serializeJson(doc, f) == 0) {
|
||||
@ -374,6 +379,11 @@ bool ConfigurationClass::read()
|
||||
|
||||
JsonObject huawei = doc["huawei"];
|
||||
config.Huawei_Enabled = huawei["enabled"] | HUAWEI_ENABLED;
|
||||
config.Huawei_Auto_Power_Enabled = huawei["auto_power_enabled"] | false;
|
||||
config.Huawei_Auto_Power_Voltage_Limit = huawei["voltage_limit"] | HUAWEI_AUTO_POWER_VOLTAGE_LIMIT;
|
||||
config.Huawei_Auto_Power_Enable_Voltage_Limit = huawei["enable_voltage_limit"] | HUAWEI_AUTO_POWER_ENABLE_VOLTAGE_LIMIT;
|
||||
config.Huawei_Auto_Power_Lower_Power_Limit = huawei["lower_power_limit"] | HUAWEI_AUTO_POWER_LOWER_POWER_LIMIT;
|
||||
config.Huawei_Auto_Power_Upper_Power_Limit = huawei["upper_power_limit"] | HUAWEI_AUTO_POWER_UPPER_POWER_LIMIT;
|
||||
|
||||
f.close();
|
||||
return true;
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
*/
|
||||
#include "Huawei_can.h"
|
||||
#include "MessageOutput.h"
|
||||
#include "PowerMeter.h"
|
||||
#include "PowerLimiter.h"
|
||||
#include "Configuration.h"
|
||||
#include <SPI.h>
|
||||
#include <mcp_can.h>
|
||||
@ -14,7 +16,9 @@ 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, uint8_t huawei_power)
|
||||
{
|
||||
initialized = false;
|
||||
if (_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
const CONFIG_T& config = Configuration.get();
|
||||
|
||||
@ -32,12 +36,12 @@ void HuaweiCanClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huaw
|
||||
|
||||
CAN = new MCP_CAN(spi, huawei_cs);
|
||||
if (!CAN->begin(MCP_ANY, CAN_125KBPS, MCP_8MHZ) == CAN_OK) {
|
||||
MessageOutput.println("Error Initializing MCP2515...");
|
||||
MessageOutput.println("[HuaweiCanClass::init] Error Initializing MCP2515...");
|
||||
return;
|
||||
}
|
||||
|
||||
MessageOutput.println("MCP2515 Initialized Successfully!");
|
||||
initialized = true;
|
||||
MessageOutput.println("[HuaweiCanClass::init] MCP2515 Initialized Successfully!");
|
||||
_initialized = true;
|
||||
|
||||
// Change to normal mode to allow messages to be transmitted
|
||||
CAN->setMode(MCP_NORMAL);
|
||||
@ -45,6 +49,10 @@ void HuaweiCanClass::init(uint8_t huawei_miso, uint8_t huawei_mosi, uint8_t huaw
|
||||
pinMode(huawei_power, OUTPUT);
|
||||
digitalWrite(huawei_power, HIGH);
|
||||
_huawei_power = huawei_power;
|
||||
|
||||
if (config.Huawei_Auto_Power_Enabled) {
|
||||
_mode = HUAWEI_MODE_AUTO_INT;
|
||||
}
|
||||
}
|
||||
|
||||
RectifierParameters_t * HuaweiCanClass::get()
|
||||
@ -54,23 +62,18 @@ RectifierParameters_t * HuaweiCanClass::get()
|
||||
|
||||
uint32_t HuaweiCanClass::getLastUpdate()
|
||||
{
|
||||
return lastUpdate;
|
||||
return _lastUpdateReceivedMillis;
|
||||
}
|
||||
|
||||
uint8_t data[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
// Requests current values from Huawei unit. Response is handled in onReceive
|
||||
void HuaweiCanClass::sendRequest()
|
||||
{
|
||||
if (previousMillis < millis()) {
|
||||
// Send extended message
|
||||
byte sndStat = CAN->sendMsgBuf(0x108040FE, 1, 8, data);
|
||||
if(sndStat == CAN_OK) {
|
||||
MessageOutput.println("Message Sent Successfully!");
|
||||
} else {
|
||||
MessageOutput.println("Error Sending Message...");
|
||||
}
|
||||
|
||||
previousMillis += 5000;
|
||||
// Send extended message
|
||||
byte sndStat = CAN->sendMsgBuf(0x108040FE, 1, 8, data);
|
||||
if(sndStat != CAN_OK) {
|
||||
MessageOutput.println("[HuaweiCanClass::sendRequest] Error Sending Message...");
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,8 +100,9 @@ void HuaweiCanClass::onReceive(uint8_t* frame, uint8_t len)
|
||||
|
||||
case R48xx_DATA_OUTPUT_POWER:
|
||||
_rp.output_power = value / 1024.0;
|
||||
_newOutputPowerReceived = true;
|
||||
// We'll only update last update on the important params
|
||||
lastUpdate = millis();
|
||||
_lastUpdateReceivedMillis = millis();
|
||||
break;
|
||||
|
||||
case R48xx_DATA_EFFICIENCY:
|
||||
@ -110,7 +114,7 @@ void HuaweiCanClass::onReceive(uint8_t* frame, uint8_t len)
|
||||
break;
|
||||
|
||||
case R48xx_DATA_OUTPUT_CURRENT_MAX:
|
||||
_rp.max_output_current = value / MAX_CURRENT_MULTIPLIER;
|
||||
_rp.max_output_current = static_cast<float>(value) / MAX_CURRENT_MULTIPLIER;
|
||||
break;
|
||||
|
||||
case R48xx_DATA_INPUT_VOLTAGE:
|
||||
@ -133,12 +137,16 @@ void HuaweiCanClass::onReceive(uint8_t* frame, uint8_t len)
|
||||
case R48xx_DATA_OUTPUT_CURRENT:
|
||||
_rp.output_current = value / 1024.0;
|
||||
|
||||
/* This is normally the last parameter received. Print */
|
||||
lastUpdate = millis(); // We'll only update last update on the important params
|
||||
if (_rp.output_current > HUAWEI_AUTO_MODE_SHUTDOWN_CURRENT) {
|
||||
_outputCurrentOnSinceMillis = millis();
|
||||
}
|
||||
|
||||
MessageOutput.printf("In: %.02fV, %.02fA, %.02fW\n", _rp.input_voltage, _rp.input_current, _rp.input_power);
|
||||
MessageOutput.printf("Out: %.02fV, %.02fA of %.02fA, %.02fW\n", _rp.output_voltage, _rp.output_current, _rp.max_output_current, _rp.output_power);
|
||||
MessageOutput.printf("Eff: %.01f%%, Temp in: %.01fC, Temp out: %.01fC\n", _rp.efficiency * 100, _rp.input_temp, _rp.output_temp);
|
||||
/* This is normally the last parameter received. Print */
|
||||
_lastUpdateReceivedMillis = millis(); // We'll only update last update on the important params
|
||||
|
||||
MessageOutput.printf("[HuaweiCanClass::onReceive] In: %.02fV, %.02fA, %.02fW\n", _rp.input_voltage, _rp.input_current, _rp.input_power);
|
||||
MessageOutput.printf("[HuaweiCanClass::onReceive] Out: %.02fV, %.02fA of %.02fA, %.02fW\n", _rp.output_voltage, _rp.output_current, _rp.max_output_current, _rp.output_power);
|
||||
MessageOutput.printf("[HuaweiCanClass::onReceive] Eff: %.01f%%, Temp in: %.01fC, Temp out: %.01fC\n", _rp.efficiency * 100, _rp.input_temp, _rp.output_temp);
|
||||
|
||||
break;
|
||||
|
||||
@ -157,7 +165,7 @@ void HuaweiCanClass::loop()
|
||||
|
||||
const CONFIG_T& config = Configuration.get();
|
||||
|
||||
if (!config.Huawei_Enabled || !initialized) {
|
||||
if (!config.Huawei_Enabled || !_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -175,12 +183,110 @@ void HuaweiCanClass::loop()
|
||||
// https://www.beyondlogic.org/review-huawei-r4850g2-power-supply-53-5vdc-3kw/
|
||||
}
|
||||
}
|
||||
sendRequest();
|
||||
|
||||
// Request updated values in regular intervals
|
||||
if (_nextRequestMillis < millis()) {
|
||||
MessageOutput.println("[HUAWEI********************* Sending request");
|
||||
sendRequest();
|
||||
_nextRequestMillis = millis() + 5000;
|
||||
}
|
||||
|
||||
// If the output current is low for a long time, shutdown PSU
|
||||
if (_outputCurrentOnSinceMillis + HUAWEI_AUTO_MODE_SHUTDOWN_DELAY < millis() &&
|
||||
(_mode == HUAWEI_MODE_AUTO_EXT || _mode == HUAWEI_MODE_AUTO_INT)) {
|
||||
digitalWrite(_huawei_power, 1);
|
||||
}
|
||||
|
||||
// ***********************
|
||||
// Automatic power control
|
||||
// ***********************
|
||||
|
||||
if (_mode == HUAWEI_MODE_AUTO_INT ) {
|
||||
|
||||
// Set voltage limit in periodic intervals
|
||||
if ( _nextAutoModePeriodicIntMillis < millis()) {
|
||||
MessageOutput.printf("[HuaweiCanClass::loop] Periodically setting voltage limit: %f \r\n", config.Huawei_Auto_Power_Voltage_Limit);
|
||||
setValue(config.Huawei_Auto_Power_Voltage_Limit, HUAWEI_ONLINE_VOLTAGE);
|
||||
_nextAutoModePeriodicIntMillis = millis() + 60000;
|
||||
}
|
||||
|
||||
// Re-enable automatic power control if the output voltage has dropped below threshold
|
||||
if(_rp.output_voltage < config.Huawei_Auto_Power_Enable_Voltage_Limit ) {
|
||||
_autoPowerEnabled = 10;
|
||||
}
|
||||
|
||||
if ((PowerLimiter.getPowerLimiterState() == PL_UI_STATE_INACTIVE ||
|
||||
PowerLimiter.getPowerLimiterState() == PL_UI_STATE_CHARGING) &&
|
||||
PowerMeter.getLastPowerMeterUpdate() > _lastPowerMeterUpdateReceivedMillis &&
|
||||
_newOutputPowerReceived &&
|
||||
_autoPowerEnabled > 0) {
|
||||
// Power Limiter is inactive and we have received both:
|
||||
// a new PowerMeter and a new output power value. Also we're _autoPowerEnabled
|
||||
// So we're good to calculate a new limit
|
||||
|
||||
_newOutputPowerReceived = false;
|
||||
_lastPowerMeterUpdateReceivedMillis = PowerMeter.getLastPowerMeterUpdate();
|
||||
|
||||
// Calculate new power limit
|
||||
float newPowerLimit = -1 * round(PowerMeter.getPowerTotal());
|
||||
newPowerLimit += _rp.output_power;
|
||||
MessageOutput.printf("[HuaweiCanClass::loop] PL: %f, OP: %f \r\n", newPowerLimit, _rp.output_power);
|
||||
|
||||
if (newPowerLimit > config.Huawei_Auto_Power_Lower_Power_Limit) {
|
||||
|
||||
// Check if the output power has dropped below the lower limit (i.e. the battery is full)
|
||||
// and if the PSU should be turned off. Also we use a simple counter mechanism here to be able
|
||||
// to ramp up from zero output power when starting up
|
||||
if (_rp.output_power < config.Huawei_Auto_Power_Lower_Power_Limit) {
|
||||
MessageOutput.printf("[HuaweiCanClass::loop] Power and voltage limit reached. Disabling automatic power control .... \r\n");
|
||||
_autoPowerEnabled--;
|
||||
if (_autoPowerEnabled == 0) {
|
||||
_autoPowerActive = false;
|
||||
setValue(0, HUAWEI_ONLINE_CURRENT);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
_autoPowerEnabled = 10;
|
||||
}
|
||||
|
||||
// Limit power to maximum
|
||||
if (newPowerLimit > config.Huawei_Auto_Power_Upper_Power_Limit) {
|
||||
newPowerLimit = config.Huawei_Auto_Power_Upper_Power_Limit;
|
||||
}
|
||||
|
||||
// Set the actual output limit
|
||||
float efficiency = (_rp.efficiency > 0.5 ? _rp.efficiency : 1.0);
|
||||
float outputCurrent = efficiency * (newPowerLimit / _rp.output_voltage);
|
||||
MessageOutput.printf("[HuaweiCanClass::loop] Output current %f \r\n", outputCurrent);
|
||||
_autoPowerActive = true;
|
||||
setValue(outputCurrent, HUAWEI_ONLINE_CURRENT);
|
||||
|
||||
// Issue next request for updated output values in 2s to allow for output stabilization
|
||||
_nextRequestMillis = millis() + 2000;
|
||||
} else {
|
||||
// requested PL is below minium. Set current to 0
|
||||
_autoPowerActive = false;
|
||||
setValue(0.0, HUAWEI_ONLINE_CURRENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HuaweiCanClass::setValue(float in, uint8_t parameterType)
|
||||
{
|
||||
uint16_t value;
|
||||
|
||||
if (in < 0) {
|
||||
MessageOutput.printf("[HuaweiCanClass::setValue] Error: Tried to set voltage/current to negative value %f \r\n", in);
|
||||
}
|
||||
|
||||
// Start PSU if needed
|
||||
if (in > HUAWEI_AUTO_MODE_SHUTDOWN_CURRENT && parameterType == HUAWEI_ONLINE_CURRENT &&
|
||||
(_mode == HUAWEI_MODE_AUTO_EXT || _mode == HUAWEI_MODE_AUTO_INT)) {
|
||||
digitalWrite(_huawei_power, 0);
|
||||
_outputCurrentOnSinceMillis = millis();
|
||||
}
|
||||
|
||||
if (parameterType == HUAWEI_OFFLINE_VOLTAGE || parameterType == HUAWEI_ONLINE_VOLTAGE) {
|
||||
value = in * 1024;
|
||||
} else if (parameterType == HUAWEI_OFFLINE_CURRENT || parameterType == HUAWEI_ONLINE_CURRENT) {
|
||||
@ -193,13 +299,39 @@ void HuaweiCanClass::setValue(float in, uint8_t parameterType)
|
||||
|
||||
// Send extended message
|
||||
byte sndStat = CAN->sendMsgBuf(0x108180FE, 1, 8, data);
|
||||
if (sndStat == CAN_OK) {
|
||||
MessageOutput.println("Message Sent Successfully!");
|
||||
} else {
|
||||
MessageOutput.println("Error Sending Message...");
|
||||
if (sndStat != CAN_OK) {
|
||||
MessageOutput.println("[HuaweiCanClass::setValue] Error Sending Message...");
|
||||
}
|
||||
}
|
||||
|
||||
void HuaweiCanClass::setPower(bool power) {
|
||||
digitalWrite(_huawei_power, !power);
|
||||
void HuaweiCanClass::setMode(uint8_t mode) {
|
||||
const CONFIG_T& config = Configuration.get();
|
||||
|
||||
if(mode == HUAWEI_MODE_OFF) {
|
||||
digitalWrite(_huawei_power, 1);
|
||||
_mode = HUAWEI_MODE_OFF;
|
||||
}
|
||||
if(mode == HUAWEI_MODE_ON) {
|
||||
digitalWrite(_huawei_power, 0);
|
||||
_mode = HUAWEI_MODE_ON;
|
||||
}
|
||||
|
||||
if (mode == HUAWEI_MODE_AUTO_INT && !config.Huawei_Auto_Power_Enabled ) {
|
||||
MessageOutput.println("[HuaweiCanClass::setMode] WARNING: Trying to setmode to internal automatic power control without being enabled in the UI. Ignoring command");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mode == HUAWEI_MODE_AUTO_INT && mode != HUAWEI_MODE_AUTO_INT) {
|
||||
_autoPowerActive = false;
|
||||
setValue(0, HUAWEI_ONLINE_CURRENT);
|
||||
}
|
||||
|
||||
if(mode == HUAWEI_MODE_AUTO_EXT || mode == HUAWEI_MODE_AUTO_INT) {
|
||||
_mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
bool HuaweiCanClass::getAutoPowerStatus() {
|
||||
return _autoPowerActive;
|
||||
}
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
#define TOPIC_SUB_LIMIT_ONLINE_VOLTAGE "limit_online_voltage"
|
||||
#define TOPIC_SUB_LIMIT_ONLINE_CURRENT "limit_online_current"
|
||||
#define TOPIC_SUB_POWER "power"
|
||||
#define TOPIC_SUB_MODE "mode"
|
||||
|
||||
MqttHandleHuaweiClass MqttHandleHuawei;
|
||||
|
||||
@ -28,7 +28,7 @@ void MqttHandleHuaweiClass::init()
|
||||
String topic = MqttSettings.getPrefix();
|
||||
MqttSettings.subscribe(String(topic + "huawei/cmd/" + TOPIC_SUB_LIMIT_ONLINE_VOLTAGE).c_str(), 0, std::bind(&MqttHandleHuaweiClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "huawei/cmd/" + TOPIC_SUB_LIMIT_ONLINE_CURRENT).c_str(), 0, std::bind(&MqttHandleHuaweiClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "huawei/cmd/" + TOPIC_SUB_POWER).c_str(), 0, std::bind(&MqttHandleHuaweiClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
MqttSettings.subscribe(String(topic + "huawei/cmd/" + TOPIC_SUB_MODE).c_str(), 0, std::bind(&MqttHandleHuaweiClass::onMqttMessage, this, _1, _2, _3, _4, _5, _6));
|
||||
|
||||
_lastPublish = millis();
|
||||
|
||||
@ -108,13 +108,23 @@ void MqttHandleHuaweiClass::onMqttMessage(const espMqttClientTypes::MessagePrope
|
||||
// Set current limit
|
||||
MessageOutput.printf("Limit Current: %f A\r\n", payload_val);
|
||||
HuaweiCan.setValue(payload_val, HUAWEI_ONLINE_CURRENT);
|
||||
} else if (!strcmp(setting, TOPIC_SUB_POWER)) {
|
||||
} else if (!strcmp(setting, TOPIC_SUB_MODE)) {
|
||||
// Control power on/off
|
||||
MessageOutput.printf("Power: %f A\r\n", payload_val);
|
||||
if(payload_val > 0) {
|
||||
HuaweiCan.setPower(true);
|
||||
} else {
|
||||
HuaweiCan.setPower(false);
|
||||
if(payload_val == 3) {
|
||||
MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Full internal control");
|
||||
HuaweiCan.setMode(HUAWEI_MODE_AUTO_INT);
|
||||
}
|
||||
if(payload_val == 2) {
|
||||
MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Internal on/off control, external power limit");
|
||||
HuaweiCan.setMode(HUAWEI_MODE_AUTO_EXT);
|
||||
}
|
||||
if(payload_val == 1) {
|
||||
MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Turned ON");
|
||||
HuaweiCan.setMode(HUAWEI_MODE_ON);
|
||||
}
|
||||
if(payload_val == 0) {
|
||||
MessageOutput.println("[Huawei MQTT::] Received MQTT msg. New mode: Turned OFF");
|
||||
HuaweiCan.setMode(HUAWEI_MODE_OFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@
|
||||
#include "Configuration.h"
|
||||
#include "MqttSettings.h"
|
||||
#include "NetworkSettings.h"
|
||||
#include "Huawei_can.h"
|
||||
#include <VeDirectFrameHandler.h>
|
||||
#include "MessageOutput.h"
|
||||
#include <ctime>
|
||||
@ -337,7 +338,7 @@ uint8_t PowerLimiterClass::getPowerLimiterState() {
|
||||
}
|
||||
|
||||
int32_t PowerLimiterClass::getLastRequestedPowerLimit() {
|
||||
return _lastRequestedPowerLimit;
|
||||
return _lastRequestedPowerLimit;
|
||||
}
|
||||
|
||||
bool PowerLimiterClass::getMode() {
|
||||
@ -406,7 +407,15 @@ int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr<InverterAbstract> inve
|
||||
if(batteryDischargeEnabled && useFullSolarPassthrough(inverter)) {
|
||||
// Case 5
|
||||
newPowerLimit = newPowerLimit > adjustedVictronChargePower ? newPowerLimit : adjustedVictronChargePower;
|
||||
}
|
||||
} else {
|
||||
// We check if the PSU is on and disable the Power Limiter in this case.
|
||||
// The PSU should reduce power or shut down first before the Power Limiter kicks in
|
||||
// The only case where this is not desired is if the battery is over the Full Solar Passthrough Threshold
|
||||
// In this case the Power Limiter should start. The PSU will shutdown when the Power Limiter is active
|
||||
if (HuaweiCan.getAutoPowerStatus()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// We should use Victron solar power only (corrected by efficiency factor)
|
||||
if (solarPowerEnabled && !batteryDischargeEnabled) {
|
||||
|
||||
@ -190,6 +190,11 @@ void WebApiHuaweiClass::onAdminGet(AsyncWebServerRequest* request)
|
||||
const CONFIG_T& config = Configuration.get();
|
||||
|
||||
root[F("enabled")] = config.Huawei_Enabled;
|
||||
root[F("auto_power_enabled")] = config.Huawei_Auto_Power_Enabled;
|
||||
root[F("voltage_limit")] = static_cast<int>(config.Huawei_Auto_Power_Voltage_Limit * 100) / 100.0;
|
||||
root[F("enable_voltage_limit")] = static_cast<int>(config.Huawei_Auto_Power_Enable_Voltage_Limit * 100) / 100.0;
|
||||
root[F("lower_power_limit")] = config.Huawei_Auto_Power_Lower_Power_Limit;
|
||||
root[F("upper_power_limit")] = config.Huawei_Auto_Power_Upper_Power_Limit;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
@ -234,7 +239,11 @@ void WebApiHuaweiClass::onAdminPost(AsyncWebServerRequest* request)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(root.containsKey("enabled"))) {
|
||||
if (!(root.containsKey("enabled")) ||
|
||||
!(root.containsKey("auto_power_enabled")) ||
|
||||
!(root.containsKey("voltage_limit")) ||
|
||||
!(root.containsKey("lower_power_limit")) ||
|
||||
!(root.containsKey("upper_power_limit"))) {
|
||||
retMsg[F("message")] = F("Values are missing!");
|
||||
retMsg[F("code")] = WebApiError::GenericValueMissing;
|
||||
response->setLength();
|
||||
@ -244,6 +253,11 @@ void WebApiHuaweiClass::onAdminPost(AsyncWebServerRequest* request)
|
||||
|
||||
CONFIG_T& config = Configuration.get();
|
||||
config.Huawei_Enabled = root[F("enabled")].as<bool>();
|
||||
config.Huawei_Auto_Power_Enabled = root[F("auto_power_enabled")].as<bool>();
|
||||
config.Huawei_Auto_Power_Voltage_Limit = root[F("voltage_limit")].as<float>();
|
||||
config.Huawei_Auto_Power_Enable_Voltage_Limit = root[F("enable_voltage_limit")].as<float>();
|
||||
config.Huawei_Auto_Power_Lower_Power_Limit = root[F("lower_power_limit")].as<float>();
|
||||
config.Huawei_Auto_Power_Upper_Power_Limit = root[F("upper_power_limit")].as<float>();
|
||||
Configuration.write();
|
||||
|
||||
retMsg[F("type")] = F("success");
|
||||
@ -254,6 +268,7 @@ void WebApiHuaweiClass::onAdminPost(AsyncWebServerRequest* request)
|
||||
request->send(response);
|
||||
|
||||
const PinMapping_t& pin = PinMapping.get();
|
||||
// Properly turn this on
|
||||
if (config.Huawei_Enabled) {
|
||||
MessageOutput.println(F("Initialize Huawei AC charger interface... "));
|
||||
if (PinMapping.isValidHuaweiConfig()) {
|
||||
@ -265,5 +280,18 @@ void WebApiHuaweiClass::onAdminPost(AsyncWebServerRequest* request)
|
||||
}
|
||||
}
|
||||
|
||||
HuaweiCan.setPower(config.Huawei_Enabled);
|
||||
// Properly turn this off
|
||||
if (!config.Huawei_Enabled) {
|
||||
HuaweiCan.setValue(0, HUAWEI_ONLINE_CURRENT);
|
||||
delay(500);
|
||||
HuaweiCan.setMode(HUAWEI_MODE_OFF);
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.Huawei_Auto_Power_Enabled) {
|
||||
HuaweiCan.setMode(HUAWEI_MODE_AUTO_INT);
|
||||
return;
|
||||
}
|
||||
|
||||
HuaweiCan.setMode(HUAWEI_MODE_AUTO_EXT);
|
||||
}
|
||||
|
||||
@ -733,6 +733,13 @@
|
||||
"ChargerSettings": "AC Ladegerät Einstellungen",
|
||||
"Configuration": "AC Ladegerät Konfiguration",
|
||||
"EnableHuawei": "Huawei R4850G2 an CAN Bus Interface aktiv",
|
||||
"EnableAutoPower": "Automatische Leistungssteuerung",
|
||||
"Limits": "Limits",
|
||||
"VoltageLimit": "Ladespannungslimit",
|
||||
"enableVoltageLimit": "Start Spannungslimit",
|
||||
"enableVoltageLimitHint": "Die automatische Leistungssteuerung wird deaktiviert wenn die Ausgangsspannung über diesem Wert liegt und wenn gleichzeitig die Ausgangsleistung unter die minimale Leistung fällt.\nDie automatische Leistungssteuerung wird re-aktiveiert wenn die Batteriespannung unter diesen Wert fällt.",
|
||||
"lowerPowerLimit": "Minimale Leistung",
|
||||
"upperPowerLimit": "Maximale Leistung",
|
||||
"Seconds": "@:dtuadmin.Seconds",
|
||||
"Save": "@:dtuadmin.Save"
|
||||
},
|
||||
|
||||
@ -738,6 +738,13 @@
|
||||
"ChargerSettings": "AC Charger Settings",
|
||||
"Configuration": "AC Charger Configuration",
|
||||
"EnableHuawei": "Enable Huawei R4850G2 on CAN Bus Interface",
|
||||
"EnableAutoPower": "Automatic power control",
|
||||
"Limits": "Limits",
|
||||
"VoltageLimit": "Charge Voltage limit",
|
||||
"enableVoltageLimit": "Re-enable voltage limit",
|
||||
"enableVoltageLimitHint": "Automatic power control is disabled if the output voltage is higher then this value and if the output power drops below the minimum output power limit (set below).\nAutomatic power control is re-enabled if the battery voltage drops below the value set in this field.",
|
||||
"lowerPowerLimit": "Minimum output power",
|
||||
"upperPowerLimit": "Maximum output power",
|
||||
"Seconds": "@:dtuadmin.Seconds",
|
||||
"Save": "@:dtuadmin.Save"
|
||||
},
|
||||
|
||||
@ -693,6 +693,13 @@
|
||||
"ChargerSettings": "AC Charger Settings",
|
||||
"Configuration": "AC Charger Configuration",
|
||||
"EnableHuawei": "Enable Huawei R4850G2 on CAN Bus Interface",
|
||||
"EnableAutoPower": "Automatic power control",
|
||||
"Limits": "Limits",
|
||||
"VoltageLimit": "Charge Voltage limit",
|
||||
"enableVoltageLimit": "Re-enable voltage limit",
|
||||
"enableVoltageLimitHint": "Automatic power control is disabled if the output voltage is higher then this value and if the output power drops below the minimum output power limit (set below).\nAutomatic power control is re-enabled if the battery voltage drops below the value set in this field.",
|
||||
"lowerPowerLimit": "Minimum output power",
|
||||
"upperPowerLimit": "Maximum output power",
|
||||
"Seconds": "@:dtuadmin.Seconds",
|
||||
"Save": "@:dtuadmin.Save"
|
||||
},
|
||||
|
||||
@ -1,3 +1,8 @@
|
||||
export interface AcChargerConfig {
|
||||
enabled: boolean;
|
||||
auto_power_enabled: boolean;
|
||||
voltage_limit: number;
|
||||
enable_voltage_limit: number;
|
||||
lower_power_limit: number;
|
||||
upper_power_limit: number;
|
||||
}
|
||||
|
||||
@ -9,6 +9,54 @@
|
||||
<InputElement :label="$t('acchargeradmin.EnableHuawei')"
|
||||
v-model="acChargerConfigList.enabled"
|
||||
type="checkbox" wide/>
|
||||
<InputElement v-show="acChargerConfigList.enabled"
|
||||
:label="$t('acchargeradmin.EnableAutoPower')"
|
||||
v-model="acChargerConfigList.auto_power_enabled"
|
||||
type="checkbox" wide/>
|
||||
|
||||
<CardElement :text="$t('acchargeradmin.Limits')" textVariant="text-bg-primary" add-space
|
||||
v-show="acChargerConfigList.auto_power_enabled">
|
||||
<div class="row mb-3">
|
||||
<label for="voltageLimit" class="col-sm-2 col-form-label">{{ $t('acchargeradmin.VoltageLimit') }}:</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="number" step="0.01" class="form-control" id="voltageLimit"
|
||||
placeholder="42" v-model="acChargerConfigList.voltage_limit"
|
||||
aria-describedby="voltageLimitDescription" min="42" max="58.5" required/>
|
||||
<span class="input-group-text" id="voltageLimitDescription">V</span>
|
||||
</div>
|
||||
</div>
|
||||
<label for="enableVoltageLimit" class="col-sm-2 col-form-label">{{ $t('acchargeradmin.enableVoltageLimit') }}:
|
||||
<BIconInfoCircle v-tooltip :title="$t('acchargeradmin.enableVoltageLimitHint')" />
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="number" step="0.01" class="form-control" id="enableVoltageLimit"
|
||||
placeholder="42" v-model="acChargerConfigList.enable_voltage_limit"
|
||||
aria-describedby="enableVoltageLimitDescription" min="42" max="58.5" required/>
|
||||
<span class="input-group-text" id="enableVoltageLimitDescription">V</span>
|
||||
</div>
|
||||
</div>
|
||||
<label for="lowerPowerLimit" class="col-sm-2 col-form-label">{{ $t('acchargeradmin.lowerPowerLimit') }}:</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" id="lowerPowerLimit"
|
||||
placeholder="150" v-model="acChargerConfigList.lower_power_limit"
|
||||
aria-describedby="lowerPowerLimitDescription" min="100" max="3000" required/>
|
||||
<span class="input-group-text" id="lowerPowerLimitDescription">W</span>
|
||||
</div>
|
||||
</div>
|
||||
<label for="upperPowerLimit" class="col-sm-2 col-form-label">{{ $t('acchargeradmin.upperPowerLimit') }}:</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
<input type="number" class="form-control" id="upperPowerLimit"
|
||||
placeholder="2000" v-model="acChargerConfigList.upper_power_limit"
|
||||
aria-describedby="lowerPowerLimitDescription" min="100" max="3000" required/>
|
||||
<span class="input-group-text" id="upperPowerLimitDescription">W</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardElement>
|
||||
</CardElement>
|
||||
|
||||
<button type="submit" class="btn btn-primary mb-3">{{ $t('acchargeradmin.Save') }}</button>
|
||||
@ -21,6 +69,7 @@ import BasePage from '@/components/BasePage.vue';
|
||||
import BootstrapAlert from "@/components/BootstrapAlert.vue";
|
||||
import CardElement from '@/components/CardElement.vue';
|
||||
import InputElement from '@/components/InputElement.vue';
|
||||
import { BIconInfoCircle } from 'bootstrap-icons-vue';
|
||||
import type { AcChargerConfig } from "@/types/AcChargerConfig";
|
||||
import { authHeader, handleResponse } from '@/utils/authentication';
|
||||
import { defineComponent } from 'vue';
|
||||
@ -31,6 +80,7 @@ export default defineComponent({
|
||||
BootstrapAlert,
|
||||
CardElement,
|
||||
InputElement,
|
||||
BIconInfoCircle,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user