Skip to content

Commit 11e8b3f

Browse files
Charger: Fix soft over current detection when PWM changes often (EVerest#762)
Signed-off-by: Cornelius Claussen <cc@pionix.de> Co-authored-by: Kai Hermann <kai-uwe.hermann@pionix.de>
1 parent eb5cbc4 commit 11e8b3f

File tree

2 files changed

+39
-11
lines changed

2 files changed

+39
-11
lines changed

modules/EvseManager/Charger.cpp

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ void Charger::run_state_machine() {
482482
// make sure we are enabling PWM
483483
if (not hlc_use_5percent_current_session) {
484484
auto m = get_max_current_internal();
485-
update_pwm_now_if_changed(ampere_to_duty_cycle(m));
485+
update_pwm_now_if_changed_ampere(m);
486486
} else {
487487
update_pwm_now_if_changed(PWM_5_PERCENT);
488488
}
@@ -569,12 +569,12 @@ void Charger::run_state_machine() {
569569
if (hlc_use_5percent_current_session) {
570570
update_pwm_now_if_changed(PWM_5_PERCENT);
571571
} else {
572-
update_pwm_now_if_changed(ampere_to_duty_cycle(get_max_current_internal()));
572+
update_pwm_now_if_changed_ampere(get_max_current_internal());
573573
}
574574
} else {
575575
// update PWM if it has changed and 5 seconds have passed since last update
576576
if (not hlc_use_5percent_current_session) {
577-
update_pwm_max_every_5seconds(ampere_to_duty_cycle(get_max_current_internal()));
577+
update_pwm_max_every_5seconds_ampere(get_max_current_internal());
578578
}
579579
}
580580
}
@@ -643,7 +643,7 @@ void Charger::run_state_machine() {
643643
} else {
644644
// update PWM if it has changed and 5 seconds have passed since last update
645645
if (not errors_prevent_charging_internal()) {
646-
update_pwm_max_every_5seconds(ampere_to_duty_cycle(get_max_current_internal()));
646+
update_pwm_max_every_5seconds_ampere(get_max_current_internal());
647647
}
648648
}
649649
}
@@ -887,13 +887,15 @@ void Charger::process_cp_events_independent(CPEvent cp_event) {
887887
}
888888
}
889889

890-
void Charger::update_pwm_max_every_5seconds(float dc) {
890+
void Charger::update_pwm_max_every_5seconds_ampere(float ampere) {
891+
float dc = ampere_to_duty_cycle(ampere);
891892
if (dc not_eq internal_context.update_pwm_last_dc) {
892893
auto now = std::chrono::steady_clock::now();
893-
auto timeSinceLastUpdate =
894+
auto time_since_last_update =
894895
std::chrono::duration_cast<std::chrono::milliseconds>(now - internal_context.last_pwm_update).count();
895-
if (timeSinceLastUpdate >= 5000) {
896+
if (time_since_last_update >= IEC_PWM_MAX_UPDATE_INTERVAL) {
896897
update_pwm_now(dc);
898+
internal_context.pwm_set_last_ampere = ampere;
897899
}
898900
}
899901
}
@@ -919,17 +921,27 @@ void Charger::update_pwm_now_if_changed(float dc) {
919921
}
920922
}
921923

924+
void Charger::update_pwm_now_if_changed_ampere(float ampere) {
925+
float dc = ampere_to_duty_cycle(ampere);
926+
if (internal_context.update_pwm_last_dc not_eq dc) {
927+
update_pwm_now(dc);
928+
internal_context.pwm_set_last_ampere = ampere;
929+
}
930+
}
931+
922932
void Charger::pwm_off() {
923933
session_log.evse(false, "Set PWM Off");
924934
shared_context.pwm_running = false;
925935
internal_context.update_pwm_last_dc = 1.;
936+
internal_context.pwm_set_last_ampere = 0.;
926937
bsp->set_pwm_off();
927938
}
928939

929940
void Charger::pwm_F() {
930941
session_log.evse(false, "Set PWM F");
931942
shared_context.pwm_running = false;
932943
internal_context.update_pwm_last_dc = 0.;
944+
internal_context.pwm_set_last_ampere = 0.;
933945
bsp->set_pwm_F();
934946
}
935947

@@ -1486,6 +1498,16 @@ float Charger::get_max_current_internal() {
14861498
return maxc;
14871499
}
14881500

1501+
float Charger::get_max_current_signalled_to_ev_internal() {
1502+
// For basic charging, the max current signalled to the EV may be different from the actual current limit
1503+
// for up to 5 seconds as the PWM may only be updated every 5 seconds according to IEC61851-1.
1504+
if (not shared_context.hlc_charging_active) {
1505+
return internal_context.pwm_set_last_ampere;
1506+
} else {
1507+
return get_max_current_internal();
1508+
}
1509+
}
1510+
14891511
void Charger::set_current_drawn_by_vehicle(float l1, float l2, float l3) {
14901512
Everest::scoped_lock_timeout lock(state_machine_mutex,
14911513
Everest::MutexDescription::Charger_set_current_drawn_by_vehicle);
@@ -1495,8 +1517,9 @@ void Charger::set_current_drawn_by_vehicle(float l1, float l2, float l3) {
14951517
}
14961518

14971519
void Charger::check_soft_over_current() {
1520+
14981521
// Allow some tolerance
1499-
float limit = (get_max_current_internal() + soft_over_current_measurement_noise_A) *
1522+
float limit = (get_max_current_signalled_to_ev_internal() + soft_over_current_measurement_noise_A) *
15001523
(1. + soft_over_current_tolerance_percent / 100.);
15011524

15021525
if (shared_context.current_drawn_by_vehicle[0] > limit or shared_context.current_drawn_by_vehicle[1] > limit or
@@ -1515,9 +1538,9 @@ void Charger::check_soft_over_current() {
15151538
internal_context.over_current = false;
15161539
}
15171540
auto now = std::chrono::steady_clock::now();
1518-
auto timeSinceOverCurrentStarted =
1541+
auto time_since_over_current_started =
15191542
std::chrono::duration_cast<std::chrono::milliseconds>(now - internal_context.last_over_current_event).count();
1520-
if (internal_context.over_current and timeSinceOverCurrentStarted >= SOFT_OVER_CURRENT_TIMEOUT) {
1543+
if (internal_context.over_current and time_since_over_current_started >= SOFT_OVER_CURRENT_TIMEOUT) {
15211544
auto errstr =
15221545
fmt::format("Soft overcurrent event (L1:{}, L2:{}, L3:{}, limit {}) triggered",
15231546
shared_context.current_drawn_by_vehicle[0], shared_context.current_drawn_by_vehicle[1],

modules/EvseManager/Charger.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ class Charger {
9595
// external input to charger: update max_current and new validUntil
9696
bool set_max_current(float ampere, std::chrono::time_point<date::utc_clock> validUntil);
9797
float get_max_current();
98+
9899
sigslot::signal<float> signal_max_current;
99100

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

207208
bool errors_prevent_charging_internal();
208209
float get_max_current_internal();
210+
float get_max_current_signalled_to_ev_internal();
209211
bool deauthorize_internal();
210212
bool pause_charging_wait_for_power_internal();
211213

@@ -218,7 +220,8 @@ class Charger {
218220

219221
void update_pwm_now(float dc);
220222
void update_pwm_now_if_changed(float dc);
221-
void update_pwm_max_every_5seconds(float dc);
223+
void update_pwm_now_if_changed_ampere(float dc);
224+
void update_pwm_max_every_5seconds_ampere(float dc);
222225
void pwm_off();
223226
void pwm_F();
224227

@@ -327,6 +330,7 @@ class Charger {
327330

328331
bool pp_warning_printed{false};
329332
bool no_energy_warning_printed{false};
333+
float pwm_set_last_ampere{0};
330334
} internal_context;
331335

332336
// main Charger thread
@@ -368,6 +372,7 @@ class Charger {
368372
// 4 seconds according to table 3 of ISO15118-3
369373
static constexpr int T_STEP_EF = 4000;
370374
static constexpr int SOFT_OVER_CURRENT_TIMEOUT = 7000;
375+
static constexpr int IEC_PWM_MAX_UPDATE_INTERVAL = 5000;
371376

372377
types::evse_manager::EnableDisableSource active_enable_disable_source{
373378
types::evse_manager::Enable_source::Unspecified, types::evse_manager::Enable_state::Unassigned, 10000};

0 commit comments

Comments
 (0)