Skip to content

Commit

Permalink
prevent relay switching too much with shaper
Browse files Browse the repository at this point in the history
filtering shaper input
  • Loading branch information
KipK committed Apr 2, 2023
1 parent 4be7f09 commit d5303bf
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 52 deletions.
49 changes: 27 additions & 22 deletions src/app_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ uint32_t divert_min_charge_time;

// Current Shaper settings
uint32_t current_shaper_max_pwr;
double current_shaper_smoothing_factor;
uint32_t current_shaper_min_pause_time; // in seconds
uint32_t current_shaper_data_maxinterval; // in seconds

// Tesla Client settings
String tesla_access_token;
Expand Down Expand Up @@ -120,35 +123,35 @@ ConfigOptDefenition<uint32_t> flagsOpt = ConfigOptDefenition<uint32_t>(flags, CO

ConfigOpt *opts[] =
{
// Wifi Network Strings
// Wifi Network Strings
new ConfigOptDefenition<String>(esid, "", "ssid", "ws"),
new ConfigOptSecret(epass, "", "pass", "wp"),

// Language String
// Language String
new ConfigOptDefenition<String>(lang, "", "lang", "lan"),

// Web server authentication (leave blank for none)
// Web server authentication (leave blank for none)
new ConfigOptDefenition<String>(www_username, "", "www_username", "au"),
new ConfigOptSecret(www_password, "", "www_password", "ap"),

// Advanced settings
// Advanced settings
new ConfigOptDefenition<String>(esp_hostname, esp_hostname_default, "hostname", "hn"),
new ConfigOptDefenition<String>(sntp_hostname, SNTP_DEFAULT_HOST, "sntp_hostname", "sh"),

// Time
// Time
new ConfigOptDefenition<String>(time_zone, DEFAULT_TIME_ZONE, "time_zone", "tz"),

// Limit
// Limit
new ConfigOptDefenition<String>(limit_default_type, {}, "limit_default_type", "ldt"),
new ConfigOptDefenition<uint32_t>(limit_default_value, {}, "limit_default_value", "ldv"),

// EMONCMS SERVER strings
// EMONCMS SERVER strings
new ConfigOptDefenition<String>(emoncms_server, "https://data.openevse.com/emoncms", "emoncms_server", "es"),
new ConfigOptDefenition<String>(emoncms_node, esp_hostname, "emoncms_node", "en"),
new ConfigOptSecret(emoncms_apikey, "", "emoncms_apikey", "ea"),
new ConfigOptDefenition<String>(emoncms_fingerprint, "", "emoncms_fingerprint", "ef"),

// MQTT Settings
// MQTT Settings
new ConfigOptDefenition<String>(mqtt_server, "emonpi", "mqtt_server", "ms"),
new ConfigOptDefenition<uint32_t>(mqtt_port, 1883, "mqtt_port", "mpt"),
new ConfigOptDefenition<String>(mqtt_topic, esp_hostname, "mqtt_topic", "mt"),
Expand All @@ -161,48 +164,51 @@ ConfigOpt *opts[] =
new ConfigOptDefenition<String>(mqtt_vehicle_soc, "", "mqtt_vehicle_soc", "mc"),
new ConfigOptDefenition<String>(mqtt_vehicle_range, "", "mqtt_vehicle_range", "mr"),
new ConfigOptDefenition<String>(mqtt_vehicle_eta, "", "mqtt_vehicle_eta", "met"),
new ConfigOptDefenition<String>(mqtt_announce_topic, "openevse/announce/"+ESPAL.getShortId(), "mqtt_announce_topic", "ma"),
new ConfigOptDefenition<String>(mqtt_announce_topic, "openevse/announce/" + ESPAL.getShortId(), "mqtt_announce_topic", "ma"),

// OCPP 1.6 Settings
// OCPP 1.6 Settings
new ConfigOptDefenition<String>(ocpp_server, "", "ocpp_server", "ows"),
new ConfigOptDefenition<String>(ocpp_chargeBoxId, "", "ocpp_chargeBoxId", "cid"),
new ConfigOptDefenition<String>(ocpp_authkey, "", "ocpp_authkey", "oky"),
new ConfigOptDefenition<String>(ocpp_idtag, "", "ocpp_idtag", "idt"),

// Ohm Connect Settings
// Ohm Connect Settings
new ConfigOptDefenition<String>(ohm, "", "ohm", "o"),

// Divert settings
// Divert settings
new ConfigOptDefenition<double>(divert_PV_ratio, 1.1, "divert_PV_ratio", "dpr"),
new ConfigOptDefenition<double>(divert_attack_smoothing_factor, 0.4, "divert_attack_smoothing_factor", "da"),
new ConfigOptDefenition<double>(divert_decay_smoothing_factor, 0.05, "divert_decay_smoothing_factor", "dd"),
new ConfigOptDefenition<uint32_t>(divert_min_charge_time, (10 * 60), "divert_min_charge_time", "dt"),

// Current Shaper settings
new ConfigOptDefenition<uint32_t>(current_shaper_max_pwr, 0 , "current_shaper_max_pwr", "smp"),
// Current Shaper settings
new ConfigOptDefenition<uint32_t>(current_shaper_max_pwr, 0, "current_shaper_max_pwr", "smp"),
new ConfigOptDefenition<double>(current_shaper_smoothing_factor, 0.05, "current_shaper_smoothing_factor", "ssf"),
new ConfigOptDefenition<uint32_t>(current_shaper_min_pause_time, 300, "current_shaper_min_pause_time", "spt"),
new ConfigOptDefenition<uint32_t>(current_shaper_data_maxinterval, 120, "current_shaper_data_maxinterval", "sdm"),

// Tesla client settings
// Tesla client settings
new ConfigOptSecret(tesla_access_token, "", "tesla_access_token", "tat"),
new ConfigOptSecret(tesla_refresh_token, "", "tesla_refresh_token", "trt"),
new ConfigOptDefenition<uint64_t>(tesla_created_at, -1, "tesla_created_at", "tc"),
new ConfigOptDefenition<uint64_t>(tesla_expires_in, -1, "tesla_expires_in", "tx"),
new ConfigOptDefenition<String>(tesla_vehicle_id, "", "tesla_vehicle_id", "ti"),

// RFID storage
// RFID storage
new ConfigOptDefenition<String>(rfid_storage, "", "rfid_storage", "rs"),

#if RGB_LED
// LED brightness
// LED brightness
new ConfigOptDefenition<uint8_t>(led_brightness, LED_DEFAULT_BRIGHTNESS, "led_brightness", "lb"),
#endif

// Scheduler options
// Scheduler options
new ConfigOptDefenition<uint32_t>(scheduler_start_window, SCHEDULER_DEFAULT_START_WINDOW, "scheduler_start_window", "ssw"),

// Flags
// Flags
&flagsOpt,

// Virtual Options
// Virtual Options
new ConfigOptVirtualBool(flagsOpt, CONFIG_SERVICE_EMONCMS, CONFIG_SERVICE_EMONCMS, "emoncms_enabled", "ee"),
new ConfigOptVirtualBool(flagsOpt, CONFIG_SERVICE_MQTT, CONFIG_SERVICE_MQTT, "mqtt_enabled", "me"),
new ConfigOptVirtualBool(flagsOpt, CONFIG_MQTT_ALLOW_ANY_CERT, 0, "mqtt_reject_unauthorized", "mru"),
Expand All @@ -223,8 +229,7 @@ ConfigOpt *opts[] =
new ConfigOptVirtualBool(flagsOpt, CONFIG_FACTORY_WRITE_LOCK, CONFIG_FACTORY_WRITE_LOCK, "factory_write_lock", "fwl"),
new ConfigOptVirtualBool(flagsOpt, CONFIG_THREEPHASE, CONFIG_THREEPHASE, "is_threephase", "itp"),
new ConfigOptVirtualMqttProtocol(flagsOpt, "mqtt_protocol", "mprt"),
new ConfigOptVirtualChargeMode(flagsOpt, "charge_mode", "chmd")
};
new ConfigOptVirtualChargeMode(flagsOpt, "charge_mode", "chmd")};

ConfigJson user_config(opts, sizeof(opts) / sizeof(opts[0]), EEPROM_SIZE, CONFIG_OFFSET);
ConfigJson factory_config(opts, sizeof(opts) / sizeof(opts[0]), EEPROM_SIZE, FACTORY_OFFSET);
Expand Down
3 changes: 3 additions & 0 deletions src/app_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ extern uint32_t scheduler_start_window;

//Shaper settings
extern uint32_t current_shaper_max_pwr;
extern double current_shaper_smoothing_factor;
extern uint32_t current_shaper_min_pause_time;
extern uint32_t current_shaper_data_maxinterval;

// 24-bits of Flags
extern uint32_t flags;
Expand Down
112 changes: 85 additions & 27 deletions src/current_shaper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ CurrentShaperTask shaper;
CurrentShaperTask::CurrentShaperTask() : MicroTasks::Task() {
_changed = false;
_enabled = false;
_max_pwr = 0;
_live_pwr = 0;
_smoothed_live_pwr = 0;
_chg_cur = 0;
_max_cur = 0;
_pause_timer = 0;
_timer = 0;
_updated = false;
}

CurrentShaperTask::~CurrentShaperTask() {
Expand All @@ -18,42 +26,64 @@ void CurrentShaperTask::setup() {
}

unsigned long CurrentShaperTask::loop(MicroTasks::WakeReason reason) {

if (_enabled) {
EvseProperties props;
if (_changed) {
props.setMaxCurrent(_max_cur);
if (_max_cur < evse.getMinCurrent() ) {
if (_max_cur < evse.getMinCurrent()) {
// pause temporary, not enough amps available
props.setState(EvseState::Disabled);
if (!_pause_timer)
{
_pause_timer = millis();
}

}
else {
else if (millis() - _pause_timer >= current_shaper_min_pause_time * 1000)
{
_pause_timer = 0;
props.setState(EvseState::None);
}
_changed = false;
_updated = true;
_timer = millis();
evse.claim(EvseClient_OpenEVSE_Shaper,EvseManager_Priority_Safety, props);
StaticJsonDocument<128> event;
event["shaper"] = 1;
event["shaper_live_pwr"] = _live_pwr;
event["shaper_max_pwr"] = _max_pwr;
event["shaper_cur"] = _max_cur;
event["shaper_updated"] = _updated;
event_send(event);
_changed = false;
// claim only if we have change
if (evse.getState() != props.getState() || evse.getChargeCurrent() != props.getChargeCurrent())
{
evse.claim(EvseClient_OpenEVSE_Shaper, EvseManager_Priority_Safety, props);
StaticJsonDocument<128> event;
event["shaper"] = 1;
event["shaper_live_pwr"] = _live_pwr;
event["shaper_smoothed_live_pwr"] = _smoothed_live_pwr;
event["shaper_max_pwr"] = _max_pwr;
event["shaper_cur"] = _max_cur;
event["shaper_updated"] = _updated;
event_send(event);
}
}
if (millis() - _timer > EVSE_SHAPER_FAILSAFE_TIME) {
else if ( !_updated || millis() - _timer > current_shaper_data_maxinterval * 1000 )
{
//available power has not been updated since EVSE_SHAPER_FAILSAFE_TIME, pause charge
DBUGF("shaper_live_pwr has not been updated in time, pausing charge");
_updated = false;
props.setState(EvseState::Disabled);
evse.claim(EvseClient_OpenEVSE_Shaper,EvseManager_Priority_Limit, props);
StaticJsonDocument<128> event;
event["shaper"] = 1;
event["shaper_live_pwr"] = _live_pwr;
event["shaper_max_pwr"] = _max_pwr;
event["shaper_cur"] = _max_cur;
event["shaper_updated"] = _updated;
event_send(event);

if (_updated) {
_pause_timer = millis();
_updated = false;
}

if (evse.getState(EvseClient_OpenEVSE_Shaper) != EvseState::Disabled)
{
props.setState(EvseState::Disabled);
evse.claim(EvseClient_OpenEVSE_Shaper, EvseManager_Priority_Limit, props);
StaticJsonDocument<128> event;
event["shaper"] = 1;
event["shaper_live_pwr"] = _live_pwr;
event["shaper_smoothed_live_pwr"] = _smoothed_live_pwr;
event["shaper_max_pwr"] = _max_pwr;
event["shaper_cur"] = _max_cur;
event["shaper_updated"] = _updated;
event_send(event);
}
}
}
else {
Expand All @@ -73,8 +103,9 @@ void CurrentShaperTask::begin(EvseManager &evse) {
this -> _evse = &evse;
this -> _max_pwr = current_shaper_max_pwr;
this -> _live_pwr = 0;
this -> _smoothed_live_pwr = 0;
this -> _max_cur = 0;
this -> _updated = false;
this -> _updated = false;
MicroTask.startTask(this);
StaticJsonDocument<128> event;
event["shaper"] = 1;
Expand Down Expand Up @@ -118,20 +149,47 @@ void CurrentShaperTask::shapeCurrent() {
_updated = true;
// adding self produced energy to total
int max_pwr = _max_pwr;
// filtering live_pwr for getting out of pause
uint32_t updt_time = (millis() - _timer) / 1000;

int livepwr;
if (_pause_timer == 0) {
_smoothed_live_pwr = _live_pwr;
livepwr = _live_pwr;
}
else {
// default filtering would consider an update each 10sec.
// Calculating the smoothing factor considering current interval.
if (_live_pwr > _smoothed_live_pwr) {
_smoothed_live_pwr = _live_pwr;
}
else {
double factor = (updt_time * current_shaper_smoothing_factor) / EVSE_SHAPER_FILTER_TAU;
_smoothed_live_pwr = (_live_pwr * factor) + (_smoothed_live_pwr * (1 - factor));
DBUGVAR(_smoothed_live_pwr);
}
livepwr = _smoothed_live_pwr;
}

if (livepwr > _max_pwr) {
livepwr = max_pwr;
}
if (config_divert_enabled() == true) {
if (mqtt_solar != "") {
max_pwr += solar;
}
}
if(!config_threephase_enabled()) {
_max_cur = round(((max_pwr - _live_pwr) / evse.getVoltage()) + (evse.getAmps()));
_max_cur = round(((max_pwr - livepwr) / evse.getVoltage()) + (evse.getAmps()));
}

else {
_max_cur = round(((max_pwr - _live_pwr) / evse.getVoltage() / 3) + (evse.getAmps()));
_max_cur = round(((max_pwr - livepwr) / evse.getVoltage() / 3) + (evse.getAmps()));
}


if (_max_cur > evse.getMaxConfiguredCurrent()) {
_max_cur = evse.getMaxConfiguredCurrent();
}

_changed = true;
}
Expand Down
10 changes: 7 additions & 3 deletions src/current_shaper.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
#ifndef EVSE_SHAPER_LOOP_TIME
#define EVSE_SHAPER_LOOP_TIME 2000
#endif
#ifndef EVSE_SHAPER_FAILSAFE_TIME
#define EVSE_SHAPER_FAILSAFE_TIME 360000
#endif

#ifndef EVSE_SHAPER_FILTER_TAU
#define EVSE_SHAPER_FILTER_TAU 10 // 10 sec
#endif


#include "emonesp.h"
#include <MicroTasks.h>
Expand All @@ -27,9 +29,11 @@ class CurrentShaperTask: public MicroTasks::Task
bool _changed;
int _max_pwr; // total current available from the grid
int _live_pwr; // current available to EVSE
int _smoothed_live_pwr; // filtered live power for getting out of pause only
uint8_t _chg_cur; // calculated charge current to claim
uint8_t _max_cur; // shaper calculated max current
uint32_t _timer;
uint32_t _pause_timer;
bool _updated;

protected:
Expand Down

0 comments on commit d5303bf

Please sign in to comment.