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

Charger: Fix soft over current detection when PWM changes often #762

Merged
merged 2 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions modules/EvseManager/Charger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ void Charger::run_state_machine() {
// make sure we are enabling PWM
if (not hlc_use_5percent_current_session) {
auto m = get_max_current_internal();
update_pwm_now_if_changed(ampere_to_duty_cycle(m));
update_pwm_now_if_changed_ampere(m);
} else {
update_pwm_now_if_changed(PWM_5_PERCENT);
}
Expand Down Expand Up @@ -569,12 +569,12 @@ void Charger::run_state_machine() {
if (hlc_use_5percent_current_session) {
update_pwm_now_if_changed(PWM_5_PERCENT);
} else {
update_pwm_now_if_changed(ampere_to_duty_cycle(get_max_current_internal()));
update_pwm_now_if_changed_ampere(get_max_current_internal());
}
} else {
// update PWM if it has changed and 5 seconds have passed since last update
if (not hlc_use_5percent_current_session) {
update_pwm_max_every_5seconds(ampere_to_duty_cycle(get_max_current_internal()));
update_pwm_max_every_5seconds_ampere(get_max_current_internal());
}
}
}
Expand Down Expand Up @@ -643,7 +643,7 @@ void Charger::run_state_machine() {
} else {
// update PWM if it has changed and 5 seconds have passed since last update
if (not errors_prevent_charging_internal()) {
update_pwm_max_every_5seconds(ampere_to_duty_cycle(get_max_current_internal()));
update_pwm_max_every_5seconds_ampere(get_max_current_internal());
}
}
}
Expand Down Expand Up @@ -887,13 +887,15 @@ void Charger::process_cp_events_independent(CPEvent cp_event) {
}
}

void Charger::update_pwm_max_every_5seconds(float dc) {
void Charger::update_pwm_max_every_5seconds_ampere(float ampere) {
float dc = ampere_to_duty_cycle(ampere);
if (dc not_eq internal_context.update_pwm_last_dc) {
auto now = std::chrono::steady_clock::now();
auto timeSinceLastUpdate =
auto time_since_last_update =
std::chrono::duration_cast<std::chrono::milliseconds>(now - internal_context.last_pwm_update).count();
if (timeSinceLastUpdate >= 5000) {
if (time_since_last_update >= IEC_PWM_MAX_UPDATE_INTERVAL) {
update_pwm_now(dc);
internal_context.pwm_set_last_ampere = ampere;
}
}
}
Expand All @@ -919,17 +921,27 @@ void Charger::update_pwm_now_if_changed(float dc) {
}
}

void Charger::update_pwm_now_if_changed_ampere(float ampere) {
float dc = ampere_to_duty_cycle(ampere);
if (internal_context.update_pwm_last_dc not_eq dc) {
update_pwm_now(dc);
internal_context.pwm_set_last_ampere = ampere;
}
}

void Charger::pwm_off() {
session_log.evse(false, "Set PWM Off");
shared_context.pwm_running = false;
internal_context.update_pwm_last_dc = 1.;
internal_context.pwm_set_last_ampere = 0.;
bsp->set_pwm_off();
}

void Charger::pwm_F() {
session_log.evse(false, "Set PWM F");
shared_context.pwm_running = false;
internal_context.update_pwm_last_dc = 0.;
internal_context.pwm_set_last_ampere = 0.;
bsp->set_pwm_F();
}

Expand Down Expand Up @@ -1486,6 +1498,16 @@ float Charger::get_max_current_internal() {
return maxc;
}

float Charger::get_max_current_signalled_to_ev_internal() {
// For basic charging, the max current signalled to the EV may be different from the actual current limit
// for up to 5 seconds as the PWM may only be updated every 5 seconds according to IEC61851-1.
if (not shared_context.hlc_charging_active) {
return internal_context.pwm_set_last_ampere;
} else {
return get_max_current_internal();
}
}

void Charger::set_current_drawn_by_vehicle(float l1, float l2, float l3) {
Everest::scoped_lock_timeout lock(state_machine_mutex,
Everest::MutexDescription::Charger_set_current_drawn_by_vehicle);
Expand All @@ -1495,8 +1517,9 @@ void Charger::set_current_drawn_by_vehicle(float l1, float l2, float l3) {
}

void Charger::check_soft_over_current() {

// Allow some tolerance
float limit = (get_max_current_internal() + soft_over_current_measurement_noise_A) *
float limit = (get_max_current_signalled_to_ev_internal() + soft_over_current_measurement_noise_A) *
(1. + soft_over_current_tolerance_percent / 100.);

if (shared_context.current_drawn_by_vehicle[0] > limit or shared_context.current_drawn_by_vehicle[1] > limit or
Expand All @@ -1515,9 +1538,9 @@ void Charger::check_soft_over_current() {
internal_context.over_current = false;
}
auto now = std::chrono::steady_clock::now();
auto timeSinceOverCurrentStarted =
auto time_since_over_current_started =
std::chrono::duration_cast<std::chrono::milliseconds>(now - internal_context.last_over_current_event).count();
if (internal_context.over_current and timeSinceOverCurrentStarted >= SOFT_OVER_CURRENT_TIMEOUT) {
if (internal_context.over_current and time_since_over_current_started >= SOFT_OVER_CURRENT_TIMEOUT) {
auto errstr =
fmt::format("Soft overcurrent event (L1:{}, L2:{}, L3:{}, limit {}) triggered",
shared_context.current_drawn_by_vehicle[0], shared_context.current_drawn_by_vehicle[1],
Expand Down
7 changes: 6 additions & 1 deletion modules/EvseManager/Charger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class Charger {
// external input to charger: update max_current and new validUntil
bool set_max_current(float ampere, std::chrono::time_point<date::utc_clock> validUntil);
float get_max_current();

sigslot::signal<float> signal_max_current;

void setup(bool three_phases, bool has_ventilation, const std::string& country_code, const ChargeMode charge_mode,
Expand Down Expand Up @@ -206,6 +207,7 @@ class Charger {

bool errors_prevent_charging_internal();
float get_max_current_internal();
float get_max_current_signalled_to_ev_internal();
bool deauthorize_internal();
bool pause_charging_wait_for_power_internal();

Expand All @@ -218,7 +220,8 @@ class Charger {

void update_pwm_now(float dc);
void update_pwm_now_if_changed(float dc);
void update_pwm_max_every_5seconds(float dc);
void update_pwm_now_if_changed_ampere(float dc);
void update_pwm_max_every_5seconds_ampere(float dc);
void pwm_off();
void pwm_F();

Expand Down Expand Up @@ -327,6 +330,7 @@ class Charger {

bool pp_warning_printed{false};
bool no_energy_warning_printed{false};
float pwm_set_last_ampere{0};
} internal_context;

// main Charger thread
Expand Down Expand Up @@ -368,6 +372,7 @@ class Charger {
// 4 seconds according to table 3 of ISO15118-3
static constexpr int T_STEP_EF = 4000;
static constexpr int SOFT_OVER_CURRENT_TIMEOUT = 7000;
static constexpr int IEC_PWM_MAX_UPDATE_INTERVAL = 5000;

types::evse_manager::EnableDisableSource active_enable_disable_source{
types::evse_manager::Enable_source::Unspecified, types::evse_manager::Enable_state::Unassigned, 10000};
Expand Down
Loading