@@ -719,69 +719,16 @@ void EvseManager::ready() {
719719 });
720720 }
721721
722- // Car requests a target voltage and current limit
723- r_hlc[0 ]->subscribe_dc_ev_target_voltage_current (
724- [this ](types::iso15118::DcEvTargetValues v) {
725- bool target_changed = false ;
726-
727- // Hack for Skoda Enyaq that should be fixed in a different way
728- if (config.hack_skoda_enyaq and (v.dc_ev_target_voltage < 300 or v.dc_ev_target_current < 0 ))
729- return ;
730-
731- // Limit voltage/current for broken EV implementations
732- const auto ev = get_ev_info ();
733- if (ev.maximum_current_limit .has_value () and
734- v.dc_ev_target_current > ev.maximum_current_limit .value ()) {
735- v.dc_ev_target_current = ev.maximum_current_limit .value ();
736- }
737-
738- if (ev.maximum_voltage_limit .has_value () and
739- v.dc_ev_target_voltage > ev.maximum_voltage_limit .value ()) {
740- v.dc_ev_target_voltage = ev.maximum_voltage_limit .value ();
741- }
742-
743- bool car_breaks_limit{false };
744- const auto hlc_limits = charger->get_evse_max_hlc_limits ();
745- if (v.dc_ev_target_current > hlc_limits.evse_maximum_current_limit ) {
746- v.dc_ev_target_current = hlc_limits.evse_maximum_current_limit ;
747- car_breaks_limit = true ;
748- }
749-
750- const auto actual_voltage =
751- ev_info.present_voltage .has_value () ? ev_info.present_voltage .value () : v.dc_ev_target_voltage ;
752-
753- const auto target_power = v.dc_ev_target_current * actual_voltage;
754- if (target_power > hlc_limits.evse_maximum_power_limit ) {
755- v.dc_ev_target_current = hlc_limits.evse_maximum_power_limit / actual_voltage;
756- car_breaks_limit = true ;
757- }
758-
759- if (v.dc_ev_target_voltage not_eq latest_target_voltage or
760- v.dc_ev_target_current not_eq latest_target_current) {
761- latest_target_voltage = v.dc_ev_target_voltage ;
762- latest_target_current = v.dc_ev_target_current ;
763- target_changed = true ;
764- }
765-
766- if (target_changed) {
767- apply_new_target_voltage_current ();
768- if (not contactor_open) {
769- powersupply_DC_on ();
770- }
771- if (car_breaks_limit) {
772- EVLOG_warning
773- << " EV ignores new EVSE max limits. Setting target current to new EVSE max limits" ;
774- }
775-
776- {
777- Everest::scoped_lock_timeout lock (ev_info_mutex,
778- Everest::MutexDescription::EVSE_publish_ev_info);
779- ev_info.target_voltage = latest_target_voltage;
780- ev_info.target_current = latest_target_current;
781- p_evse->publish_ev_info (ev_info);
782- }
783- }
784- });
722+ // Car requests a target voltage and current limit.
723+ // Store raw EV values and immediately clamp against EVSE limits.
724+ // Clamping is also re-applied by the Charger state machine
725+ // (signal_dc_enforce_target_limits) in case the EV doesnt respect the
726+ // limits or does not change the target values for some time.
727+ r_hlc[0 ]->subscribe_dc_ev_target_voltage_current ([this ](types::iso15118::DcEvTargetValues v) {
728+ raw_ev_target_voltage = v.dc_ev_target_voltage ;
729+ raw_ev_target_current = v.dc_ev_target_current ;
730+ process_dc_ev_target_voltage_current (charger->get_evse_max_hlc_limits ());
731+ });
785732
786733 r_hlc[0 ]->subscribe_d20_dc_dynamic_charge_mode ([this ](types::iso15118::DcChargeDynamicModeValues values) {
787734 static bool last_is_actually_exporting_to_grid{false };
@@ -899,6 +846,11 @@ void EvseManager::ready() {
899846 imd_stop ();
900847 });
901848
849+ // Re-evaluate DC target limits so that updated EVSE
850+ // limits are enforced even when the EV does not send new target values
851+ charger->signal_dc_enforce_target_limits .connect (
852+ [this ](types::iso15118::DcEvseMaximumLimits limits) { process_dc_ev_target_voltage_current (limits); });
853+
902854 // Current demand has finished - switch off DC supply
903855 r_hlc[0 ]->subscribe_current_demand_finished ([this ] { powersupply_DC_off (); });
904856
@@ -2338,6 +2290,10 @@ void EvseManager::powersupply_DC_on() {
23382290// input voltage/current is what the evse/car would like to set.
23392291// if it is more then what the energymanager gave us, we can limit it here.
23402292bool EvseManager::powersupply_DC_set (double _voltage, double _current) {
2293+ if (last_power_supply_voltage == _voltage and last_power_supply_current == _current) {
2294+ return true ;
2295+ }
2296+
23412297 double voltage = _voltage;
23422298 double current = _current;
23432299 static bool last_is_actually_exporting_to_grid{false };
@@ -2392,6 +2348,8 @@ bool EvseManager::powersupply_DC_set(double _voltage, double _current) {
23922348
23932349 // set the new limits for the DC output
23942350 r_powersupply_DC[0 ]->call_setImportVoltageCurrent (voltage, current);
2351+ last_power_supply_voltage = voltage;
2352+ last_power_supply_current = current;
23952353 return true ;
23962354 }
23972355 EVLOG_critical << fmt::format (" DC voltage/current out of limits requested: Voltage {:.2f} Current {:.2f}." ,
@@ -2426,6 +2384,8 @@ bool EvseManager::powersupply_DC_set(double _voltage, double _current) {
24262384
24272385 // set the new limits for the DC output
24282386 r_powersupply_DC[0 ]->call_setExportVoltageCurrent (voltage, current);
2387+ last_power_supply_voltage = voltage;
2388+ last_power_supply_current = current;
24292389 return true ;
24302390 }
24312391 EVLOG_critical << fmt::format (" DC voltage/current out of limits requested: Voltage {:.2f} Current {:.2f}." , voltage,
@@ -2577,10 +2537,75 @@ types::evse_manager::EVInfo EvseManager::get_ev_info() {
25772537 return ev_info;
25782538}
25792539
2540+ void EvseManager::process_dc_ev_target_voltage_current (const types::iso15118::DcEvseMaximumLimits& hlc_limits) {
2541+ double clamped_voltage = raw_ev_target_voltage.load ();
2542+ double clamped_current = raw_ev_target_current.load ();
2543+
2544+ // Hack for Skoda Enyaq that should be fixed in a different way
2545+ if (config.hack_skoda_enyaq and (clamped_voltage < 300 or clamped_current < 0 )) {
2546+ return ;
2547+ }
2548+
2549+ // Limit voltage/current for broken EV implementations
2550+ const auto ev_info_snapshot = get_ev_info ();
2551+ if (ev_info_snapshot.maximum_current_limit .has_value () and
2552+ clamped_current > ev_info_snapshot.maximum_current_limit .value ()) {
2553+ clamped_current = ev_info_snapshot.maximum_current_limit .value ();
2554+ }
2555+ if (ev_info_snapshot.maximum_voltage_limit .has_value () and
2556+ clamped_voltage > ev_info_snapshot.maximum_voltage_limit .value ()) {
2557+ clamped_voltage = ev_info_snapshot.maximum_voltage_limit .value ();
2558+ }
2559+
2560+ bool car_breaks_limit{false };
2561+ if (clamped_current > hlc_limits.evse_maximum_current_limit ) {
2562+ clamped_current = hlc_limits.evse_maximum_current_limit ;
2563+ car_breaks_limit = true ;
2564+ }
2565+
2566+ const auto actual_voltage =
2567+ ev_info_snapshot.present_voltage .has_value () ? ev_info_snapshot.present_voltage .value () : clamped_voltage;
2568+
2569+ const auto target_power = clamped_current * actual_voltage;
2570+ if (target_power > hlc_limits.evse_maximum_power_limit ) {
2571+ clamped_current = hlc_limits.evse_maximum_power_limit / actual_voltage;
2572+ car_breaks_limit = true ;
2573+ }
2574+
2575+ bool target_changed = false ;
2576+ if (clamped_voltage not_eq latest_target_voltage or clamped_current not_eq latest_target_current) {
2577+ latest_target_voltage = clamped_voltage;
2578+ latest_target_current = clamped_current;
2579+ target_changed = true ;
2580+ }
2581+
2582+ apply_new_target_voltage_current ();
2583+
2584+ if (target_changed) {
2585+ if (not contactor_open) {
2586+ powersupply_DC_on ();
2587+ }
2588+ if (car_breaks_limit) {
2589+ EVLOG_warning << " EV ignores new EVSE max limits. Setting target current to new EVSE max limits" ;
2590+ }
2591+
2592+ {
2593+ Everest::scoped_lock_timeout lock (ev_info_mutex, Everest::MutexDescription::EVSE_publish_ev_info);
2594+ ev_info.target_voltage = latest_target_voltage;
2595+ ev_info.target_current = latest_target_current;
2596+ p_evse->publish_ev_info (ev_info);
2597+ }
2598+ }
2599+ }
2600+
25802601void EvseManager::apply_new_target_voltage_current () {
25812602 if (latest_target_voltage > 0 ) {
25822603 powersupply_DC_set (latest_target_voltage, latest_target_current);
25832604 }
2605+
2606+ // We allow the EV to adjust the voltage/current for a few seconds
2607+ // so we reset the timer on every new target value received.
2608+ charger->reset_dc_enforce_target_limits_timer ();
25842609}
25852610
25862611bool EvseManager::session_is_iso_d20_ac_bpt () {
0 commit comments