Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added option to limit AC charger power based on battery SoC #449

Closed
wants to merge 11 commits into from
Closed
3 changes: 3 additions & 0 deletions include/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,13 @@ struct CONFIG_T {

bool Huawei_Enabled;
bool Huawei_Auto_Power_Enabled;
bool Huawei_Auto_Power_Reduce_On_BatterySoC_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;
uint8_t Huawei_Auto_Power_BatterySoC_Threshold;
float Huawei_Auto_Power_Reduced_Upper_Power_Limit;

char Security_Password[WIFI_MAX_PASSWORD_STRLEN + 1];
bool Security_AllowReadonly;
Expand Down
4 changes: 4 additions & 0 deletions include/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,13 @@
#define BATTERY_JKBMS_POLLING_INTERVAL 5

#define HUAWEI_ENABLED false
#define HUAWEI_AUTO_POWER_ENABLED false
#define HUAWEI_AUTO_POWER_REDUCE_ON_BATTERYSOC_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
#define HUAWEI_AUTO_POWER_BATTERYSOC_THRESHOLD 90
#define HUAWEI_AUTO_POWER_REDUCED_UPPER_POWER_LIMIT 500

#define VERBOSE_LOGGING true
8 changes: 7 additions & 1 deletion src/Configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,13 @@ bool ConfigurationClass::write()
JsonObject huawei = doc.createNestedObject("huawei");
huawei["enabled"] = config.Huawei_Enabled;
huawei["auto_power_enabled"] = config.Huawei_Auto_Power_Enabled;
huawei["auto_power_reduce_on_batterysoc_enabled"] = config.Huawei_Auto_Power_Reduce_On_BatterySoC_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;
huawei["batterysoc_threshold"] = config.Huawei_Auto_Power_BatterySoC_Threshold;
huawei["reduced_upper_power_limit"] = config.Huawei_Auto_Power_Reduced_Upper_Power_Limit;

// Serialize JSON to file
if (serializeJson(doc, f) == 0) {
Expand Down Expand Up @@ -413,11 +416,14 @@ 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_Enabled = huawei["auto_power_enabled"] | HUAWEI_AUTO_POWER_ENABLED;
config.Huawei_Auto_Power_Reduce_On_BatterySoC_Enabled = huawei["auto_power_reduce_on_batterysoc_enabled"] | HUAWEI_AUTO_POWER_REDUCE_ON_BATTERYSOC_ENABLED;
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;
config.Huawei_Auto_Power_BatterySoC_Threshold = huawei["batterysoc_threshold"] | HUAWEI_AUTO_POWER_BATTERYSOC_THRESHOLD;
config.Huawei_Auto_Power_Reduced_Upper_Power_Limit = huawei["reduced_upper_power_limit"] | HUAWEI_AUTO_POWER_REDUCED_UPPER_POWER_LIMIT;

f.close();
return true;
Expand Down
12 changes: 10 additions & 2 deletions src/Huawei_can.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "PowerMeter.h"
#include "PowerLimiter.h"
#include "Configuration.h"
#include "Battery.h"
#include <SPI.h>
#include <mcp_can.h>

Expand Down Expand Up @@ -230,7 +231,7 @@ void HuaweiCanClass::loop()
// 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);
MessageOutput.printf("[HuaweiCanClass::loop] newPowerLimit: %f, output_power: %f \r\n", newPowerLimit, _rp.output_power);

if (newPowerLimit > config.Huawei_Auto_Power_Lower_Power_Limit) {

Expand All @@ -249,7 +250,14 @@ void HuaweiCanClass::loop()
_autoPowerEnabled = 10;
}

// Limit power to maximum
if (config.Battery_Enabled && config.Huawei_Auto_Power_Reduce_On_BatterySoC_Enabled){
uint8_t _batterySoC = Battery.getStats()->getSoC();
if (_batterySoC >= config.Huawei_Auto_Power_BatterySoC_Threshold && newPowerLimit > config.Huawei_Auto_Power_Reduced_Upper_Power_Limit){
MessageOutput.printf("[HuaweiCanClass::loop] Current battery SoC %i reached threshold %i, reducing output power to %f \r\n", _batterySoC, config.Huawei_Auto_Power_BatterySoC_Threshold, config.Huawei_Auto_Power_Reduced_Upper_Power_Limit);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How often will this appear once the threshold is reached? In every call to loop()? Or is this section of the function only reached with a fixed interval? I am asking to make sure that this does not flood the serial line or the web console.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does get called regularly when the battery SoC threshold is reached, but since all other outputs in this function are like this, it doesn't really make a huge difference.

Personally, I would like to add a verbose logging switch to this settings page and link most console outputs to it, but I didn't get around to it yet.

newPowerLimit = config.Huawei_Auto_Power_Reduced_Upper_Power_Limit;
}
}
// Limit power to maximum
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing an empty line before this comment, and comment was indented by accident. It should actually not change.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

if (newPowerLimit > config.Huawei_Auto_Power_Upper_Power_Limit) {
newPowerLimit = config.Huawei_Auto_Power_Upper_Power_Limit;
}
Expand Down
8 changes: 8 additions & 0 deletions src/WebApi_Huawei.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,13 @@ void WebApiHuaweiClass::onAdminGet(AsyncWebServerRequest* request)

root[F("enabled")] = config.Huawei_Enabled;
root[F("auto_power_enabled")] = config.Huawei_Auto_Power_Enabled;
root[F("auto_power_reduce_on_batterysoc_enabled")] = config.Huawei_Auto_Power_Reduce_On_BatterySoC_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;
root[F("batterysoc_threshold")] = config.Huawei_Auto_Power_BatterySoC_Threshold;
root[F("reduced_upper_power_limit")] = config.Huawei_Auto_Power_Reduced_Upper_Power_Limit;

response->setLength();
request->send(response);
Expand Down Expand Up @@ -251,13 +254,18 @@ void WebApiHuaweiClass::onAdminPost(AsyncWebServerRequest* request)
return;
}



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_Reduce_On_BatterySoC_Enabled = root[F("auto_power_reduce_on_batterysoc_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>();
config.Huawei_Auto_Power_BatterySoC_Threshold = root[F("batterysoc_threshold")].as<uint8_t>();
config.Huawei_Auto_Power_Reduced_Upper_Power_Limit = root[F("reduced_upper_power_limit")].as<float>();
Configuration.write();

retMsg[F("type")] = F("success");
Expand Down
2 changes: 2 additions & 0 deletions webapp/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ dist-ssr
coverage
*.local
vite.user.ts
.yarn
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change has nothing to do with the feature described in this PR.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, but I didn't know any other way to avoid pushing the 400+ cached yarn packages into the repo.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment below regarding the git staging area and selecting parts of your workdir to be included in the next commit.

.yarnrc.yml

/cypress/videos/
/cypress/screenshots/
Expand Down
5 changes: 5 additions & 0 deletions webapp/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -777,12 +777,17 @@
"Configuration": "AC Ladegerät Konfiguration",
"EnableHuawei": "Huawei R4850G2 an CAN Bus Interface aktiv",
"EnableAutoPower": "Automatische Leistungssteuerung",
"EnableReducePowerOnBatterySoC": "Leistung abhängig vom Batterie-Ladezustand reduzieren",
"EnableReducePowerOnBatterySoCHint": "Wenn eine Batterie per CAN-Bus verbunden ist, kann die maximale Ausgangsleistung ab einem bestimmten SoC reduziert werden",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will work for all kind of battery interfaces (Pylontech via CAN, JK BMS via serial, or Victron SmartShunt via Serial, or in the future also via MQTT). Please change the wording accordingly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do 👍

"Limits": "Limits",
"BatterySoCLimits": "Batterie SoC-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",
"BatterySoCThreshold": "Batterie SoC-Schwellenwert",
"ReducedUpperPowerLimit": "Maximale Leistung, wenn SoC-Schwellenwert erreicht ist",
"Seconds": "@:dtuadmin.Seconds",
"Save": "@:dtuadmin.Save"
},
Expand Down
5 changes: 5 additions & 0 deletions webapp/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -787,12 +787,17 @@
"Configuration": "AC Charger Configuration",
"EnableHuawei": "Enable Huawei R4850G2 on CAN Bus Interface",
"EnableAutoPower": "Automatic power control",
"EnableReducePowerOnBatterySoC": "Reduce power based on battery SoC",
"EnableReducePowerOnBatterySoCHint": "If a CAN bus battery is connected, the maximum power output can be reduced when a certain SoC is reached",
"Limits": "Limits",
"BatterySoCLimits": "Battery SoC 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",
"BatterySoCThreshold": "Battery SoC threshold",
"ReducedUpperPowerLimit": "Maximum output power if SoC threshold is reached",
"Seconds": "@:dtuadmin.Seconds",
"Save": "@:dtuadmin.Save"
},
Expand Down
5 changes: 5 additions & 0 deletions webapp/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -727,12 +727,17 @@
"Configuration": "AC Charger Configuration",
"EnableHuawei": "Enable Huawei R4850G2 on CAN Bus Interface",
"EnableAutoPower": "Automatic power control",
"EnableReducePowerOnBatterySoC": "Reduce power based on battery SoC",
"EnableReducePowerOnBatterySoCHint": "If a CAN bus battery is connected, the maximum power output can be reduced when a certain SoC is reached",
"Limits": "Limits",
"BatterySoCLimits": "Battery SoC 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",
"BatterySoCThreshold": "Battery SoC threshold",
"ReducedUpperPowerLimit": "Maximum output power if SoC threshold is reached",
"Seconds": "@:dtuadmin.Seconds",
"Save": "@:dtuadmin.Save"
},
Expand Down
3 changes: 3 additions & 0 deletions webapp/src/types/AcChargerConfig.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
export interface AcChargerConfig {
enabled: boolean;
auto_power_enabled: boolean;
auto_power_reduce_on_batterysoc_enabled: boolean;
voltage_limit: number;
enable_voltage_limit: number;
lower_power_limit: number;
upper_power_limit: number;
batterysoc_threshold: number;
reduced_upper_power_limit: number;
}
32 changes: 31 additions & 1 deletion webapp/src/views/AcChargerAdminView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
:label="$t('acchargeradmin.EnableAutoPower')"
v-model="acChargerConfigList.auto_power_enabled"
type="checkbox" wide/>
<InputElement v-show="acChargerConfigList.auto_power_enabled"
:label="$t('acchargeradmin.EnableReducePowerOnBatterySoC')"
v-model="acChargerConfigList.auto_power_reduce_on_batterysoc_enabled"
type="checkbox" wide>
<BIconInfoCircle v-tooltip :title="$t('acchargeradmin.EnableReducePowerOnBatterySoCHint')" />
</InputElement>

<CardElement :text="$t('acchargeradmin.Limits')" textVariant="text-bg-primary" add-space
v-show="acChargerConfigList.auto_power_enabled">
Expand Down Expand Up @@ -51,12 +57,36 @@
<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/>
aria-describedby="upperPowerLimitDescription" min="100" max="3000" required/>
<span class="input-group-text" id="upperPowerLimitDescription">W</span>
</div>
</div>
</div>
</CardElement>
<CardElement :text="$t('acchargeradmin.BatterySoCLimits')" textVariant="text-bg-primary" add-space
v-show="acChargerConfigList.auto_power_reduce_on_batterysoc_enabled">
<div class="row mb-3">
<label for="batterySoCThreshold" class="col-sm-2 col-form-label">{{ $t('acchargeradmin.BatterySoCThreshold') }}:</label>
<div class="col-sm-10">
<div class="input-group">
<input type="number" class="form-control" id="batterySoCThreshold"
placeholder="90" v-model="acChargerConfigList.batterysoc_threshold"
aria-describedby="batterySoCThresholdDescription" min="1" max="99" required/>
<span class="input-group-text" id="batterySoCThresholdDescription">%</span>
</div>
</div>
<label for="reducedUpperPowerLimit" class="col-sm-2 col-form-label">{{ $t('acchargeradmin.ReducedUpperPowerLimit') }}:</label>
<div class="col-sm-10">
<div class="input-group">
<input type="number" class="form-control" id="reducedUpperPowerLimit"
placeholder="500" v-model="acChargerConfigList.reduced_upper_power_limit"
aria-describedby="reducedUpperPowerLimitDescription" min="100" max="3000" required/>
<span class="input-group-text" id="reducedUpperPowerLimitDescription">W</span>
</div>
</div>

</div>
</CardElement>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The div in this CardElement is not properly indented. Why is it indented two or three levels to many?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly don't know, but I will change it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm? Now the diff looks way to big... There are now changes to pats that are not concerned with this feature.

Copy link
Author

@eu-gh eu-gh Oct 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Purely cosmetic, I added another <div> layer so the input fields will disappear altogether if the charger is disabled.

</CardElement>

<button type="submit" class="btn btn-primary mb-3">{{ $t('acchargeradmin.Save') }}</button>
Expand Down
Loading