From 19043bdafb8e42e0674e965b0ed0b29ee99a2f10 Mon Sep 17 00:00:00 2001 From: Florin Mihut Date: Mon, 1 Dec 2025 14:14:10 +0100 Subject: [PATCH 01/14] feat(over_voltage): Internal over-voltage monitor implementation in EVSEManager Implementation of an internal over-voltage monitor on top of the external one. This is a safety mechanism that reads the measured values received from over-voltage monitor and raises the over voltage faults (in case the external one is not reacting correctly). There 2 limits that can be set: an error and an emergency limit. In case of an voltage over the error limit, we are checking if this is just a very short peak or it will stay over 400ms (default value - configurable). If at the end of the 400ms we stayed above the error limit, we trigger the MREC50OverVoltage error. In case of an voltage over the emergency error, we trigger ASAP the MREC50OverVoltage fault.Implementation of an internal over-voltage monitor on top of the external one. This is a safety mechanism that reads the measured values received from over-voltage monitor and raises the over voltage faults (in case the external one is not reacting correctly). There 2 limits that can be set: an error and an emergency limit. In case of an voltage over the error limit, we are checking if this is just a very short peak or it will stay over 400ms (default value - configurable). If at the end of the 400ms we stayed above the error limit, we trigger the MREC50OverVoltage error. In case of an voltage over the emergency error, we trigger ASAP the MREC50OverVoltage fault. Signed-off-by: Florin Mihut --- errors/evse_manager.yaml | 2 + modules/EVSE/EvseManager/CMakeLists.txt | 1 + modules/EVSE/EvseManager/ErrorHandling.cpp | 7 ++ modules/EVSE/EvseManager/ErrorHandling.hpp | 2 + modules/EVSE/EvseManager/EvseManager.cpp | 39 +++++- modules/EVSE/EvseManager/EvseManager.hpp | 3 + modules/EVSE/EvseManager/doc.rst | 7 +- modules/EVSE/EvseManager/manifest.yaml | 7 ++ .../over_voltage/OverVoltageMonitor.cpp | 114 ++++++++++++++++++ .../over_voltage/OverVoltageMonitor.hpp | 52 ++++++++ .../EVSE/EvseManager/tests/ChargerTest.cpp | 3 + 11 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp create mode 100644 modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.hpp diff --git a/errors/evse_manager.yaml b/errors/evse_manager.yaml index ce3dfe50a2..674c2a65ec 100644 --- a/errors/evse_manager.yaml +++ b/errors/evse_manager.yaml @@ -9,6 +9,8 @@ errors: description: Internal error of the state machine - name: MREC4OverCurrentFailure description: Over current event + - name: MREC50OverVoltage + description: Over voltage event - name: MREC9AuthorizationTimeout description: No authorization was provided within timeout after plugin - name: PowermeterTransactionStartFailed diff --git a/modules/EVSE/EvseManager/CMakeLists.txt b/modules/EVSE/EvseManager/CMakeLists.txt index 2e3d7a9353..d698574548 100644 --- a/modules/EVSE/EvseManager/CMakeLists.txt +++ b/modules/EVSE/EvseManager/CMakeLists.txt @@ -51,6 +51,7 @@ target_compile_features(${MODULE_NAME} PUBLIC cxx_std_17) target_sources(${MODULE_NAME} PRIVATE + "over_voltage/OverVoltageMonitor.cpp" "evse/evse_managerImpl.cpp" "energy_grid/energyImpl.cpp" "token_provider/auth_token_providerImpl.cpp" diff --git a/modules/EVSE/EvseManager/ErrorHandling.cpp b/modules/EVSE/EvseManager/ErrorHandling.cpp index d804e4eeb1..007f65476e 100644 --- a/modules/EVSE/EvseManager/ErrorHandling.cpp +++ b/modules/EVSE/EvseManager/ErrorHandling.cpp @@ -126,6 +126,13 @@ void ErrorHandling::clear_overcurrent_error() { process_error(); } +void ErrorHandling::raise_over_voltage_error(Everest::error::Severity severity, const std::string& description) { + Everest::error::Error error_object = + p_evse->error_factory->create_error("evse_manager/MREC50OverVoltage", "", description, severity); + p_evse->raise_error(error_object); + process_error(); +} + // Find out if the current error set is fatal to charging or not void ErrorHandling::process_error() { const auto fatal = errors_prevent_charging(); diff --git a/modules/EVSE/EvseManager/ErrorHandling.hpp b/modules/EVSE/EvseManager/ErrorHandling.hpp index b26048528b..7f1c5b3ebd 100644 --- a/modules/EVSE/EvseManager/ErrorHandling.hpp +++ b/modules/EVSE/EvseManager/ErrorHandling.hpp @@ -79,6 +79,8 @@ class ErrorHandling { void raise_overcurrent_error(const std::string& description); void clear_overcurrent_error(); + void raise_over_voltage_error(Everest::error::Severity severity, const std::string& description); + void raise_internal_error(const std::string& description); void clear_internal_error(); diff --git a/modules/EVSE/EvseManager/EvseManager.cpp b/modules/EVSE/EvseManager/EvseManager.cpp index 573ad62bda..530c5fc69d 100644 --- a/modules/EVSE/EvseManager/EvseManager.cpp +++ b/modules/EVSE/EvseManager/EvseManager.cpp @@ -204,6 +204,17 @@ void EvseManager::ready() { config.fail_on_powermeter_errors ? r_powermeter_billing() : EMPTY_POWERMETER_VECTOR, r_over_voltage_monitor, config.inoperative_error_use_vendor_id)); + internal_over_voltage_monitor = std::make_unique( + [this](OverVoltageMonitor::FaultType type, const std::string& description) { + if (this->error_handling) { + const auto severity = type == OverVoltageMonitor::FaultType::Emergency + ? Everest::error::Severity::High + : Everest::error::Severity::Medium; + this->error_handling->raise_over_voltage_error(severity, description); + } + }, + std::chrono::milliseconds(config.internal_over_voltage_duration_ms)); + if (not config.lock_connector_in_state_b) { EVLOG_warning << "Unlock connector in CP state B. This violates IEC61851-1:2019 D.6.5 Table D.9 line 4 and " "should not be used in public environments!"; @@ -456,6 +467,10 @@ void EvseManager::ready() { if (not r_over_voltage_monitor.empty()) { r_over_voltage_monitor[0]->call_start(); } + if (internal_over_voltage_monitor) { + internal_over_voltage_monitor->reset(); + internal_over_voltage_monitor->start_monitor(); + } }); r_hlc[0]->subscribe_current_demand_finished([this] { @@ -464,8 +479,21 @@ void EvseManager::ready() { if (not r_over_voltage_monitor.empty()) { r_over_voltage_monitor[0]->call_stop(); } + if (internal_over_voltage_monitor) { + internal_over_voltage_monitor->stop_monitor(); + } }); + // Subscribe to voltage measurements from over_voltage_monitor interface + // The internal monitor acts as a software watchdog following the hardware OVM values + if (not r_over_voltage_monitor.empty()) { + r_over_voltage_monitor[0]->subscribe_voltage_measurement_V([this](float voltage_V) { + if (internal_over_voltage_monitor) { + internal_over_voltage_monitor->update_voltage(voltage_V); + } + }); + } + // Isolation monitoring for DC charging handler if (not r_imd.empty()) { @@ -767,6 +795,10 @@ void EvseManager::ready() { r_over_voltage_monitor[0]->call_set_limits(get_emergency_over_voltage_threshold(), get_error_over_voltage_threshold()); } + if (internal_over_voltage_monitor) { + internal_over_voltage_monitor->set_limits(get_emergency_over_voltage_threshold(), + get_error_over_voltage_threshold()); + } }); r_hlc[0]->subscribe_departure_time([this](const std::string& t) { @@ -1000,6 +1032,10 @@ void EvseManager::ready() { if (not r_over_voltage_monitor.empty() and event == CPEvent::CarUnplugged) { r_over_voltage_monitor[0]->call_reset_over_voltage_error(); } + if (internal_over_voltage_monitor and event == CPEvent::CarUnplugged) { + internal_over_voltage_monitor->stop_monitor(); + internal_over_voltage_monitor->reset(); + } charger->bsp_event_queue.push(event); @@ -1393,7 +1429,8 @@ void EvseManager::ready_to_start_charging() { charger->enable_disable_initial_state_publish(); this->p_evse->publish_ready(true); - EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green), "πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); + EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green), + "πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); if (!initial_powermeter_value_received) { EVLOG_warning << "No powermeter value received yet!"; } diff --git a/modules/EVSE/EvseManager/EvseManager.hpp b/modules/EVSE/EvseManager/EvseManager.hpp index d84408b3c0..7d4cbe38dc 100644 --- a/modules/EVSE/EvseManager/EvseManager.hpp +++ b/modules/EVSE/EvseManager/EvseManager.hpp @@ -47,6 +47,7 @@ #include "PersistentStore.hpp" #include "SessionLog.hpp" #include "VarContainer.hpp" +#include "over_voltage/OverVoltageMonitor.hpp" #include "scoped_lock_timeout.hpp" // ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 @@ -74,6 +75,7 @@ struct Conf { bool ac_enforce_hlc; bool ac_with_soc; int dc_isolation_voltage_V; + int internal_over_voltage_duration_ms; bool dbg_hlc_auth_after_tstep; int hack_sleep_in_cable_check; int hack_sleep_in_cable_check_volkswagen; @@ -324,6 +326,7 @@ class EvseManager : public Everest::ModuleBase { VarContainer isolation_measurement; VarContainer powersupply_measurement; VarContainer selftest_result; + std::unique_ptr internal_over_voltage_monitor; // Track voltage to earth failures for debouncing int voltage_to_earth_failure_count{0}; diff --git a/modules/EVSE/EvseManager/doc.rst b/modules/EVSE/EvseManager/doc.rst index 28c12cf6eb..8f116be380 100644 --- a/modules/EVSE/EvseManager/doc.rst +++ b/modules/EVSE/EvseManager/doc.rst @@ -62,6 +62,11 @@ In addition, on the DC side the following hardware modules can be connected: CableCheck, PreCharge and CurrentDemand steps. * DC power supply: This is the AC/DC converter that actually charges the car. +Software over-voltage supervision is always active. The configuration option +``internal_over_voltage_duration_ms`` defines for how long the measured DC voltage +must exceed the negotiated limit before EvseManager raises ``MREC5OverVoltage``. +Set it to ``0`` to trigger immediately once the threshold is crossed. + Published variables =================== @@ -281,7 +286,7 @@ Powermeter errors cause the EvseManager to become Inoperative, if fail_on_powerm * powermeter/CommunicationFault -When a charging session is stopped because of an error, the EvseManager differentiates between **Emergency Shutdowns** and **Error Shutdowns**. The severity of the +When a charging session is stopped because of an error, the EvseManager differentiates between **Emergency Shutdowns** and **Error Shutdowns**. The severity of the error influences the type of the shudown. Emergency shutdowns are caused by errors with `Severity::High` and error shutdowns are caused by errors with `Severity::Medium` or `Severity::Low`. In case of an **Emergency Shutdown** the EvseManager will immediately: diff --git a/modules/EVSE/EvseManager/manifest.yaml b/modules/EVSE/EvseManager/manifest.yaml index e2a1d4558c..771335a0d1 100644 --- a/modules/EVSE/EvseManager/manifest.yaml +++ b/modules/EVSE/EvseManager/manifest.yaml @@ -98,6 +98,13 @@ config: Default is 0, which means the voltage will be determined according to IEC 61851-23 (2023) CC.4.1.2 type: integer default: 0 + internal_over_voltage_duration_ms: + description: >- + Time in milliseconds the internal software over voltage monitor waits before raising MREC5 once the measured + voltage exceeds the negotiated limit. + type: integer + minimum: 0 + default: 400 dbg_hlc_auth_after_tstep: description: >- Special mode: send HLC auth ok only after t_step_XX is finished (true) or directly when available (false) diff --git a/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp b/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp new file mode 100644 index 0000000000..dba0cba208 --- /dev/null +++ b/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "over_voltage/OverVoltageMonitor.hpp" + +#include +#include + +#include + +namespace module { + +OverVoltageMonitor::OverVoltageMonitor(ErrorCallback callback, std::chrono::milliseconds duration) : + error_callback_(std::move(callback)), duration_(duration) { +} + +void OverVoltageMonitor::set_limits(double emergency_limit, double error_limit) { + emergency_limit_ = emergency_limit; + error_limit_ = error_limit; + limits_valid_ = true; +} + +void OverVoltageMonitor::start_monitor() { + fault_latched_ = false; + cancel_error_timer(); + running_ = true; +} + +void OverVoltageMonitor::stop_monitor() { + running_ = false; + cancel_error_timer(); +} + +void OverVoltageMonitor::reset() { + fault_latched_ = false; + cancel_error_timer(); +} + +void OverVoltageMonitor::update_voltage(double voltage_v) { + last_voltage_ = voltage_v; + if (!running_ || fault_latched_ || !limits_valid_) { + return; + } + + if (voltage_v >= emergency_limit_) { + cancel_error_timer(); + trigger_fault(FaultType::Emergency, + fmt::format("Voltage {:.2f} V exceeded emergency limit {:.2f} V.", voltage_v, emergency_limit_)); + return; + } + + if (voltage_v >= error_limit_) { + arm_error_timer(voltage_v); + } else { + cancel_error_timer(); + } +} + +void OverVoltageMonitor::trigger_fault(FaultType type, const std::string& reason) { + fault_latched_ = true; + running_ = false; + cancel_error_timer(); + if (error_callback_) { + error_callback_(type, reason); + } +} + +void OverVoltageMonitor::arm_error_timer(double voltage_v) { + if (duration_.count() == 0) { + trigger_fault(FaultType::Error, fmt::format("Voltage {:.2f} V exceeded limit {:.2f} V for at least {} ms.", + voltage_v, error_limit_, duration_.count())); + return; + } + + uint64_t token; + { + std::lock_guard lock(timer_mutex_); + if (error_timer_active_) { + timer_voltage_snapshot_ = std::max(timer_voltage_snapshot_, voltage_v); + return; + } + error_timer_active_ = true; + timer_voltage_snapshot_ = voltage_v; + token = ++timer_sequence_; + current_timer_sequence_ = token; + } + + std::thread([this, token]() { + std::this_thread::sleep_for(duration_); + double voltage = 0.0; + { + std::lock_guard lock(timer_mutex_); + if (!error_timer_active_ || current_timer_sequence_ != token) { + return; + } + error_timer_active_ = false; + voltage = timer_voltage_snapshot_; + } + trigger_fault(FaultType::Error, fmt::format("Voltage {:.2f} V exceeded limit {:.2f} V for at least {} ms.", + voltage, error_limit_, duration_.count())); + }).detach(); +} + +void OverVoltageMonitor::cancel_error_timer() { + std::lock_guard lock(timer_mutex_); + if (!error_timer_active_) { + return; + } + error_timer_active_ = false; + ++timer_sequence_; + current_timer_sequence_ = timer_sequence_; +} + +} // namespace module diff --git a/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.hpp b/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.hpp new file mode 100644 index 0000000000..016fa30a31 --- /dev/null +++ b/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.hpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace module { + +class OverVoltageMonitor { +public: + enum class FaultType { + Error, + Emergency + }; + using ErrorCallback = std::function; + + OverVoltageMonitor(ErrorCallback callback, std::chrono::milliseconds duration); + + void set_limits(double emergency_limit, double error_limit); + void start_monitor(); + void stop_monitor(); + void update_voltage(double voltage_v); + void reset(); + +private: + void trigger_fault(FaultType type, const std::string& reason); + void arm_error_timer(double voltage_v); + void cancel_error_timer(); + + ErrorCallback error_callback_; + std::chrono::milliseconds duration_; + bool running_{false}; + bool limits_valid_{false}; + bool fault_latched_{false}; + double emergency_limit_{std::numeric_limits::infinity()}; + double error_limit_{std::numeric_limits::infinity()}; + double last_voltage_{0.0}; + + std::mutex timer_mutex_; + bool error_timer_active_{false}; + uint64_t timer_sequence_{0}; + uint64_t current_timer_sequence_{0}; + double timer_voltage_snapshot_{0.0}; +}; + +} // namespace module diff --git a/modules/EVSE/EvseManager/tests/ChargerTest.cpp b/modules/EVSE/EvseManager/tests/ChargerTest.cpp index 1e417045e8..22a5062ec0 100644 --- a/modules/EVSE/EvseManager/tests/ChargerTest.cpp +++ b/modules/EVSE/EvseManager/tests/ChargerTest.cpp @@ -755,6 +755,9 @@ void ErrorHandling::raise_overcurrent_error(const std::string& description) { void ErrorHandling::clear_overcurrent_error() { } +void ErrorHandling::raise_over_voltage_error(Everest::error::Severity severity, const std::string& description) { +} + void ErrorHandling::raise_internal_error(const std::string& description) { } void ErrorHandling::clear_internal_error() { From 80ca88820b7f3fea25203eb24e687d8c839bad8b Mon Sep 17 00:00:00 2001 From: florinmihut Date: Wed, 10 Dec 2025 14:04:51 +0100 Subject: [PATCH 02/14] Fix logging statement for charging readiness Signed-off-by: florinmihut --- modules/EVSE/EvseManager/EvseManager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/EVSE/EvseManager/EvseManager.cpp b/modules/EVSE/EvseManager/EvseManager.cpp index 530c5fc69d..9f192684fd 100644 --- a/modules/EVSE/EvseManager/EvseManager.cpp +++ b/modules/EVSE/EvseManager/EvseManager.cpp @@ -1429,8 +1429,7 @@ void EvseManager::ready_to_start_charging() { charger->enable_disable_initial_state_publish(); this->p_evse->publish_ready(true); - EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green), - "πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); + EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green),"πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); if (!initial_powermeter_value_received) { EVLOG_warning << "No powermeter value received yet!"; } From ea7ba5f49c448e0fa478932be0a2f6ead957573d Mon Sep 17 00:00:00 2001 From: florinmihut Date: Wed, 10 Dec 2025 16:36:10 +0100 Subject: [PATCH 03/14] Fix formatting issue in logging message Signed-off-by: florinmihut --- modules/EVSE/EvseManager/EvseManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/EVSE/EvseManager/EvseManager.cpp b/modules/EVSE/EvseManager/EvseManager.cpp index 9f192684fd..3cbc34808c 100644 --- a/modules/EVSE/EvseManager/EvseManager.cpp +++ b/modules/EVSE/EvseManager/EvseManager.cpp @@ -1429,7 +1429,7 @@ void EvseManager::ready_to_start_charging() { charger->enable_disable_initial_state_publish(); this->p_evse->publish_ready(true); - EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green),"πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); + EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green), "πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); if (!initial_powermeter_value_received) { EVLOG_warning << "No powermeter value received yet!"; } From fbf6453e378cf504ad31fc3391e455978a2a9e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piet=20G=C3=B6mpel?= Date: Tue, 16 Dec 2025 20:53:55 +0100 Subject: [PATCH 04/14] Added test cases for software over voltage monitoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Piet GΓΆmpel --- modules/EVSE/EvseManager/tests/CMakeLists.txt | 2 + .../tests/OverVoltageMonitorTest.cpp | 190 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 modules/EVSE/EvseManager/tests/OverVoltageMonitorTest.cpp diff --git a/modules/EVSE/EvseManager/tests/CMakeLists.txt b/modules/EVSE/EvseManager/tests/CMakeLists.txt index fcf095aac0..c373a7b5b5 100644 --- a/modules/EVSE/EvseManager/tests/CMakeLists.txt +++ b/modules/EVSE/EvseManager/tests/CMakeLists.txt @@ -18,9 +18,11 @@ target_sources(${TEST_TARGET_NAME} PRIVATE EventQueueTest.cpp ErrorHandlingTest.cpp IECStateMachineTest.cpp + OverVoltageMonitorTest.cpp ../ErrorHandling.cpp ../IECStateMachine.cpp ../backtrace.cpp + ../over_voltage/OverVoltageMonitor.cpp ) target_compile_definitions(${TEST_TARGET_NAME} PRIVATE diff --git a/modules/EVSE/EvseManager/tests/OverVoltageMonitorTest.cpp b/modules/EVSE/EvseManager/tests/OverVoltageMonitorTest.cpp new file mode 100644 index 0000000000..8ac823c67d --- /dev/null +++ b/modules/EVSE/EvseManager/tests/OverVoltageMonitorTest.cpp @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include + +#include "over_voltage/OverVoltageMonitor.hpp" + +#include +#include +#include +#include + +using namespace module; +using namespace std::chrono_literals; + +class OverVoltageMonitorTest : public ::testing::Test { +protected: + struct CallbackState { + std::mutex mtx; + std::condition_variable cv; + bool called{false}; + OverVoltageMonitor::FaultType type; + std::string reason; + }; + + static OverVoltageMonitor make_monitor(CallbackState& state, std::chrono::milliseconds duration) { + return OverVoltageMonitor( + [&state](OverVoltageMonitor::FaultType type, const std::string& reason) { + std::lock_guard lock(state.mtx); + state.called = true; + state.type = type; + state.reason = reason; + state.cv.notify_all(); + }, + duration); + } + + static bool wait_for_callback(CallbackState& state, std::chrono::milliseconds timeout = 500ms) { + std::unique_lock lock(state.mtx); + return state.cv.wait_for(lock, timeout, [&state] { return state.called; }); + } +}; + +TEST_F(OverVoltageMonitorTest, no_fault_below_limits) { + CallbackState state; + auto monitor = make_monitor(state, 100ms); + + monitor.set_limits(450.0, 420.0); + monitor.start_monitor(); + + monitor.update_voltage(400.0); + monitor.update_voltage(410.0); + + EXPECT_FALSE(wait_for_callback(state)); +} + +TEST_F(OverVoltageMonitorTest, emergency_fault_triggers_immediately) { + CallbackState state; + auto monitor = make_monitor(state, 200ms); + + monitor.set_limits(450.0, 420.0); + monitor.start_monitor(); + + monitor.update_voltage(460.0); + + ASSERT_TRUE(wait_for_callback(state)); + EXPECT_EQ(state.type, OverVoltageMonitor::FaultType::Emergency); +} + +TEST_F(OverVoltageMonitorTest, error_fault_triggers_after_duration) { + CallbackState state; + auto monitor = make_monitor(state, 100ms); + + monitor.set_limits(450.0, 420.0); + monitor.start_monitor(); + + monitor.update_voltage(430.0); + + ASSERT_TRUE(wait_for_callback(state, 300ms)); + EXPECT_EQ(state.type, OverVoltageMonitor::FaultType::Error); +} + +TEST_F(OverVoltageMonitorTest, voltage_drop_cancels_error_timer) { + CallbackState state; + auto monitor = make_monitor(state, 150ms); + + monitor.set_limits(450.0, 420.0); + monitor.start_monitor(); + + monitor.update_voltage(430.0); // above error limit + std::this_thread::sleep_for(50ms); + monitor.update_voltage(410.0); // below error limit + + EXPECT_FALSE(wait_for_callback(state, 300ms)); +} + +TEST_F(OverVoltageMonitorTest, zero_duration_triggers_immediately) { + CallbackState state; + auto monitor = make_monitor(state, 0ms); + + monitor.set_limits(450.0, 420.0); + monitor.start_monitor(); + + monitor.update_voltage(425.0); + + ASSERT_TRUE(wait_for_callback(state)); + EXPECT_EQ(state.type, OverVoltageMonitor::FaultType::Error); +} + +TEST_F(OverVoltageMonitorTest, fault_is_latched) { + CallbackState state; + auto monitor = make_monitor(state, 50ms); + + monitor.set_limits(450.0, 420.0); + monitor.start_monitor(); + + monitor.update_voltage(430.0); + ASSERT_TRUE(wait_for_callback(state)); + + { + std::lock_guard lock(state.mtx); + state.called = false; + } + + // Further voltage updates should not retrigger + monitor.update_voltage(460.0); + EXPECT_FALSE(wait_for_callback(state, 200ms)); +} + +TEST_F(OverVoltageMonitorTest, stop_monitor_suppresses_fault) { + CallbackState state; + auto monitor = make_monitor(state, 100ms); + + monitor.set_limits(450.0, 420.0); + monitor.start_monitor(); + + monitor.update_voltage(430.0); + monitor.stop_monitor(); + + EXPECT_FALSE(wait_for_callback(state, 300ms)); +} + +TEST_F(OverVoltageMonitorTest, reset_clears_latched_fault_and_allows_retrigger) { + CallbackState state; + auto monitor = make_monitor(state, 50ms); + + monitor.set_limits(450.0, 420.0); + monitor.start_monitor(); + + // First fault + monitor.update_voltage(430.0); + ASSERT_TRUE(wait_for_callback(state, 300ms)); + EXPECT_EQ(state.type, OverVoltageMonitor::FaultType::Error); + + // Clear callback state + { + std::lock_guard lock(state.mtx); + state.called = false; + } + + // Reset should clear latch and timers + monitor.reset(); + + // Must restart monitoring explicitly + monitor.start_monitor(); + + // Second fault should be possible again + monitor.update_voltage(430.0); + ASSERT_TRUE(wait_for_callback(state, 300ms)); + EXPECT_EQ(state.type, OverVoltageMonitor::FaultType::Error); +} + +TEST_F(OverVoltageMonitorTest, multiple_voltage_updates_update_timer_snapshot) { + CallbackState state; + auto monitor = make_monitor(state, 100ms); + + monitor.set_limits(500.0, 420.0); + monitor.start_monitor(); + + // First update arms the timer + monitor.update_voltage(430.0); + + // Subsequent updates while timer is active + monitor.update_voltage(435.0); + monitor.update_voltage(432.0); + monitor.update_voltage(440.0); // highest value + + ASSERT_TRUE(wait_for_callback(state, 300ms)); + EXPECT_EQ(state.type, OverVoltageMonitor::FaultType::Error); +} \ No newline at end of file From 76f67b2e4c4f7c0f931aec77ca117fef72c40b56 Mon Sep 17 00:00:00 2001 From: florinmihut Date: Thu, 18 Dec 2025 17:46:11 +0100 Subject: [PATCH 05/14] Update errors/evse_manager.yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piet GΓΆmpel <37657534+Pietfried@users.noreply.github.com> Signed-off-by: florinmihut --- errors/evse_manager.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors/evse_manager.yaml b/errors/evse_manager.yaml index 674c2a65ec..8a0140709b 100644 --- a/errors/evse_manager.yaml +++ b/errors/evse_manager.yaml @@ -9,7 +9,7 @@ errors: description: Internal error of the state machine - name: MREC4OverCurrentFailure description: Over current event - - name: MREC50OverVoltage + - name: MREC5OverVoltage description: Over voltage event - name: MREC9AuthorizationTimeout description: No authorization was provided within timeout after plugin From dba0bba0bdf96c4ca8a87524462bfe15fd14d7a2 Mon Sep 17 00:00:00 2001 From: florinmihut Date: Thu, 18 Dec 2025 17:46:23 +0100 Subject: [PATCH 06/14] Update modules/EVSE/EvseManager/ErrorHandling.cpp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piet GΓΆmpel <37657534+Pietfried@users.noreply.github.com> Signed-off-by: florinmihut --- modules/EVSE/EvseManager/ErrorHandling.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/EVSE/EvseManager/ErrorHandling.cpp b/modules/EVSE/EvseManager/ErrorHandling.cpp index 007f65476e..0f77c141b6 100644 --- a/modules/EVSE/EvseManager/ErrorHandling.cpp +++ b/modules/EVSE/EvseManager/ErrorHandling.cpp @@ -128,7 +128,7 @@ void ErrorHandling::clear_overcurrent_error() { void ErrorHandling::raise_over_voltage_error(Everest::error::Severity severity, const std::string& description) { Everest::error::Error error_object = - p_evse->error_factory->create_error("evse_manager/MREC50OverVoltage", "", description, severity); + p_evse->error_factory->create_error("evse_manager/MREC5OverVoltage", "", description, severity); p_evse->raise_error(error_object); process_error(); } From b66990c83aa3a36c2174e51614362130f5cf8dfe Mon Sep 17 00:00:00 2001 From: Florin Mihut Date: Fri, 19 Dec 2025 01:17:53 +0100 Subject: [PATCH 07/14] Fic the review comments Signed-off-by: Florin Mihut --- modules/EVSE/EvseManager/CMakeLists.txt | 2 +- .../over_voltage/OverVoltageMonitor.cpp | 92 +++++++++++----- .../over_voltage/OverVoltageMonitor.hpp | 104 +++++++++++++++++- 3 files changed, 166 insertions(+), 32 deletions(-) diff --git a/modules/EVSE/EvseManager/CMakeLists.txt b/modules/EVSE/EvseManager/CMakeLists.txt index d698574548..11ccd0cf32 100644 --- a/modules/EVSE/EvseManager/CMakeLists.txt +++ b/modules/EVSE/EvseManager/CMakeLists.txt @@ -19,6 +19,7 @@ target_sources(${MODULE_NAME} ErrorHandling.cpp backtrace.cpp PersistentStore.cpp + over_voltage/OverVoltageMonitor.cpp ) target_link_libraries(${MODULE_NAME} @@ -51,7 +52,6 @@ target_compile_features(${MODULE_NAME} PUBLIC cxx_std_17) target_sources(${MODULE_NAME} PRIVATE - "over_voltage/OverVoltageMonitor.cpp" "evse/evse_managerImpl.cpp" "energy_grid/energyImpl.cpp" "token_provider/auth_token_providerImpl.cpp" diff --git a/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp b/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp index dba0cba208..31b793c05b 100644 --- a/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp +++ b/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.cpp @@ -4,14 +4,28 @@ #include "over_voltage/OverVoltageMonitor.hpp" #include +#include +#include #include - #include namespace module { OverVoltageMonitor::OverVoltageMonitor(ErrorCallback callback, std::chrono::milliseconds duration) : error_callback_(std::move(callback)), duration_(duration) { + timer_thread_ = std::thread(&OverVoltageMonitor::timer_thread_func, this); +} + +OverVoltageMonitor::~OverVoltageMonitor() { + { + std::lock_guard lock(timer_mutex_); + timer_thread_exit_ = true; + timer_armed_ = false; + } + timer_cv_.notify_one(); + if (timer_thread_.joinable()) { + timer_thread_.join(); + } } void OverVoltageMonitor::set_limits(double emergency_limit, double error_limit) { @@ -37,7 +51,6 @@ void OverVoltageMonitor::reset() { } void OverVoltageMonitor::update_voltage(double voltage_v) { - last_voltage_ = voltage_v; if (!running_ || fault_latched_ || !limits_valid_) { return; } @@ -72,43 +85,70 @@ void OverVoltageMonitor::arm_error_timer(double voltage_v) { return; } - uint64_t token; { std::lock_guard lock(timer_mutex_); - if (error_timer_active_) { + if (timer_armed_) { timer_voltage_snapshot_ = std::max(timer_voltage_snapshot_, voltage_v); return; } - error_timer_active_ = true; + timer_armed_ = true; timer_voltage_snapshot_ = voltage_v; - token = ++timer_sequence_; - current_timer_sequence_ = token; + timer_deadline_ = std::chrono::steady_clock::now() + duration_; } + timer_cv_.notify_one(); +} - std::thread([this, token]() { - std::this_thread::sleep_for(duration_); - double voltage = 0.0; - { - std::lock_guard lock(timer_mutex_); - if (!error_timer_active_ || current_timer_sequence_ != token) { - return; +void OverVoltageMonitor::cancel_error_timer() { + { + std::lock_guard lock(timer_mutex_); + if (!timer_armed_) { + return; + } + timer_armed_ = false; + } + timer_cv_.notify_one(); +} + +void OverVoltageMonitor::timer_thread_func() { + std::unique_lock lock(timer_mutex_); + + while (!timer_thread_exit_) { + // Wait until a timer is armed or exit is requested + timer_cv_.wait(lock, [this] { return timer_thread_exit_ || timer_armed_; }); + if (timer_thread_exit_) { + break; + } + + // Capture the current deadline and wait until it expires or is cancelled/updated + auto deadline = timer_deadline_; + while (!timer_thread_exit_ && timer_armed_) { + if (timer_cv_.wait_until(lock, deadline) == std::cv_status::timeout) { + break; } - error_timer_active_ = false; - voltage = timer_voltage_snapshot_; + // Woken up: check for exit, cancellation or re-arming with a new deadline + if (timer_thread_exit_ || !timer_armed_ || timer_deadline_ != deadline) { + break; + } + } + + if (timer_thread_exit_) { + break; } + if (!timer_armed_ || timer_deadline_ != deadline) { + // Timer was cancelled or re-armed; go back to waiting + continue; + } + + // Timer expired with this deadline and is still armed + const double voltage = timer_voltage_snapshot_; + timer_armed_ = false; + + // Release the lock while invoking the callback path + lock.unlock(); trigger_fault(FaultType::Error, fmt::format("Voltage {:.2f} V exceeded limit {:.2f} V for at least {} ms.", voltage, error_limit_, duration_.count())); - }).detach(); -} - -void OverVoltageMonitor::cancel_error_timer() { - std::lock_guard lock(timer_mutex_); - if (!error_timer_active_) { - return; + lock.lock(); } - error_timer_active_ = false; - ++timer_sequence_; - current_timer_sequence_ = timer_sequence_; } } // namespace module diff --git a/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.hpp b/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.hpp index 016fa30a31..e48c877910 100644 --- a/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.hpp +++ b/modules/EVSE/EvseManager/over_voltage/OverVoltageMonitor.hpp @@ -4,31 +4,124 @@ #pragma once #include +#include #include #include #include #include -#include +#include namespace module { +/** + * @brief Simple software over-voltage watchdog used by EvseManager. + * + * The monitor observes a DC voltage and compares it against two thresholds: + * + * - @ref FaultType::Emergency: if the measured voltage is greater than or equal to the + * configured emergency limit, an emergency fault is raised immediately. + * - @ref FaultType::Error: if the measured voltage is greater than or equal to the configured + * error limit continuously for at least the configured duration, an error fault is raised. + * + * After a fault has been raised, it is latched until @ref reset() is called and monitoring + * is started again via @ref start_monitor(). + * + * Thread-safety: + * - Public APIs are intended to be called from EvseManager threads and from callbacks of the + * external over_voltage_monitor interface. + * - Internally, a dedicated background thread waits on an error timer condition and synchronizes + * access to timer-related state via an internal mutex and condition variable. + */ class OverVoltageMonitor { public: + /** + * @brief Type of over-voltage fault. + * + * - Error: voltage above the error limit for at least the configured duration. + * - Emergency: voltage above the emergency limit, triggers immediately. + */ enum class FaultType { Error, Emergency }; + + /** + * @brief Callback type used to report detected faults. + * + * The callback is invoked from an internal monitoring context when a fault is detected. + * It receives the fault type and a human-readable description. + */ using ErrorCallback = std::function; + /** + * @brief Construct a new OverVoltageMonitor. + * + * @param callback Function that will be called whenever a fault is detected. + * @param duration Duration for which the voltage must stay above the error limit before + * an @ref FaultType::Error is raised. A duration of 0 ms means that an + * error fault will be raised immediately when the error limit is exceeded. + */ OverVoltageMonitor(ErrorCallback callback, std::chrono::milliseconds duration); + /** + * @brief Destructor joins the internal timer thread before destroying the object. + */ + ~OverVoltageMonitor(); + + /** + * @brief Configure the error and emergency voltage limits. + * + * This must be called before monitoring can become active. Calling this function marks + * the limits as valid and enables evaluation in @ref update_voltage(). + * + * @param emergency_limit Emergency limit in volts; exceeding this immediately triggers an + * @ref FaultType::Emergency. + * @param error_limit Error limit in volts; exceeding this for at least the configured + * duration triggers an @ref FaultType::Error. + */ void set_limits(double emergency_limit, double error_limit); + + /** + * @brief Start monitoring of incoming voltage samples. + * + * Clears any latched fault state and cancels a pending error timer, then enables + * evaluation in @ref update_voltage(). + */ void start_monitor(); + + /** + * @brief Stop monitoring of incoming voltage samples. + * + * Monitoring is disabled and any pending error timer is cancelled. Existing latched + * faults remain active until @ref reset() is called. + */ void stop_monitor(); + + /** + * @brief Feed a new voltage sample to the monitor. + * + * If monitoring is active and limits have been configured, this function evaluates the + * sample against the configured error and emergency limits and may: + * + * - Trigger an immediate emergency fault. + * - Arm or update an error timer. + * - Cancel an active error timer if the voltage falls back below the error limit. + * + * @param voltage_v Measured DC voltage in volts. + */ void update_voltage(double voltage_v); + + /** + * @brief Reset the internal fault latch and cancel timers. + * + * This clears any previously raised fault and stops the internal error timer. Monitoring + * remains disabled until @ref start_monitor() is called again. + */ void reset(); private: + void timer_thread_func(); + void trigger_fault(FaultType type, const std::string& reason); void arm_error_timer(double voltage_v); void cancel_error_timer(); @@ -40,13 +133,14 @@ class OverVoltageMonitor { bool fault_latched_{false}; double emergency_limit_{std::numeric_limits::infinity()}; double error_limit_{std::numeric_limits::infinity()}; - double last_voltage_{0.0}; std::mutex timer_mutex_; - bool error_timer_active_{false}; - uint64_t timer_sequence_{0}; - uint64_t current_timer_sequence_{0}; double timer_voltage_snapshot_{0.0}; + std::chrono::steady_clock::time_point timer_deadline_{}; + bool timer_armed_{false}; + bool timer_thread_exit_{false}; + std::condition_variable timer_cv_; + std::thread timer_thread_; }; } // namespace module From 249a70f169db692b39526ca8af031f97ddfb5bc7 Mon Sep 17 00:00:00 2001 From: florinmihut Date: Thu, 18 Dec 2025 17:47:30 +0100 Subject: [PATCH 08/14] Update modules/EVSE/EvseManager/doc.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piet GΓΆmpel <37657534+Pietfried@users.noreply.github.com> Signed-off-by: florinmihut --- modules/EVSE/EvseManager/doc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/EVSE/EvseManager/doc.rst b/modules/EVSE/EvseManager/doc.rst index 8f116be380..69b0336a89 100644 --- a/modules/EVSE/EvseManager/doc.rst +++ b/modules/EVSE/EvseManager/doc.rst @@ -62,7 +62,7 @@ In addition, on the DC side the following hardware modules can be connected: CableCheck, PreCharge and CurrentDemand steps. * DC power supply: This is the AC/DC converter that actually charges the car. -Software over-voltage supervision is always active. The configuration option +Software over-voltage supervision is always active during DC charging. The configuration option ``internal_over_voltage_duration_ms`` defines for how long the measured DC voltage must exceed the negotiated limit before EvseManager raises ``MREC5OverVoltage``. Set it to ``0`` to trigger immediately once the threshold is crossed. From 12c0db7f8905626b556eed65eceeed89ac65470b Mon Sep 17 00:00:00 2001 From: Florin Mihut Date: Mon, 1 Dec 2025 14:14:10 +0100 Subject: [PATCH 09/14] feat(over_voltage): Internal over-voltage monitor implementation in EVSEManager Implementation of an internal over-voltage monitor on top of the external one. This is a safety mechanism that reads the measured values received from over-voltage monitor and raises the over voltage faults (in case the external one is not reacting correctly). There 2 limits that can be set: an error and an emergency limit. In case of an voltage over the error limit, we are checking if this is just a very short peak or it will stay over 400ms (default value - configurable). If at the end of the 400ms we stayed above the error limit, we trigger the MREC50OverVoltage error. In case of an voltage over the emergency error, we trigger ASAP the MREC50OverVoltage fault.Implementation of an internal over-voltage monitor on top of the external one. This is a safety mechanism that reads the measured values received from over-voltage monitor and raises the over voltage faults (in case the external one is not reacting correctly). There 2 limits that can be set: an error and an emergency limit. In case of an voltage over the error limit, we are checking if this is just a very short peak or it will stay over 400ms (default value - configurable). If at the end of the 400ms we stayed above the error limit, we trigger the MREC50OverVoltage error. In case of an voltage over the emergency error, we trigger ASAP the MREC50OverVoltage fault. Signed-off-by: Florin Mihut --- modules/EVSE/EvseManager/CMakeLists.txt | 1 + modules/EVSE/EvseManager/EvseManager.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/EVSE/EvseManager/CMakeLists.txt b/modules/EVSE/EvseManager/CMakeLists.txt index 11ccd0cf32..8e2333275e 100644 --- a/modules/EVSE/EvseManager/CMakeLists.txt +++ b/modules/EVSE/EvseManager/CMakeLists.txt @@ -52,6 +52,7 @@ target_compile_features(${MODULE_NAME} PUBLIC cxx_std_17) target_sources(${MODULE_NAME} PRIVATE + "over_voltage/OverVoltageMonitor.cpp" "evse/evse_managerImpl.cpp" "energy_grid/energyImpl.cpp" "token_provider/auth_token_providerImpl.cpp" diff --git a/modules/EVSE/EvseManager/EvseManager.cpp b/modules/EVSE/EvseManager/EvseManager.cpp index 3cbc34808c..530c5fc69d 100644 --- a/modules/EVSE/EvseManager/EvseManager.cpp +++ b/modules/EVSE/EvseManager/EvseManager.cpp @@ -1429,7 +1429,8 @@ void EvseManager::ready_to_start_charging() { charger->enable_disable_initial_state_publish(); this->p_evse->publish_ready(true); - EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green), "πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); + EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green), + "πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); if (!initial_powermeter_value_received) { EVLOG_warning << "No powermeter value received yet!"; } From ad5a71138ce6d7ac86507353ad8dc7015ee06e96 Mon Sep 17 00:00:00 2001 From: Florin Mihut Date: Mon, 1 Dec 2025 14:14:10 +0100 Subject: [PATCH 10/14] feat(over_voltage): Internal over-voltage monitor implementation in EVSEManager Implementation of an internal over-voltage monitor on top of the external one. This is a safety mechanism that reads the measured values received from over-voltage monitor and raises the over voltage faults (in case the external one is not reacting correctly). There 2 limits that can be set: an error and an emergency limit. In case of an voltage over the error limit, we are checking if this is just a very short peak or it will stay over 400ms (default value - configurable). If at the end of the 400ms we stayed above the error limit, we trigger the MREC50OverVoltage error. In case of an voltage over the emergency error, we trigger ASAP the MREC50OverVoltage fault.Implementation of an internal over-voltage monitor on top of the external one. This is a safety mechanism that reads the measured values received from over-voltage monitor and raises the over voltage faults (in case the external one is not reacting correctly). There 2 limits that can be set: an error and an emergency limit. In case of an voltage over the error limit, we are checking if this is just a very short peak or it will stay over 400ms (default value - configurable). If at the end of the 400ms we stayed above the error limit, we trigger the MREC50OverVoltage error. In case of an voltage over the emergency error, we trigger ASAP the MREC50OverVoltage fault. Signed-off-by: Florin Mihut --- modules/EVSE/EvseManager/doc.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/EVSE/EvseManager/doc.rst b/modules/EVSE/EvseManager/doc.rst index 69b0336a89..3e0511bd08 100644 --- a/modules/EVSE/EvseManager/doc.rst +++ b/modules/EVSE/EvseManager/doc.rst @@ -62,11 +62,20 @@ In addition, on the DC side the following hardware modules can be connected: CableCheck, PreCharge and CurrentDemand steps. * DC power supply: This is the AC/DC converter that actually charges the car. +<<<<<<< HEAD Software over-voltage supervision is always active during DC charging. The configuration option ``internal_over_voltage_duration_ms`` defines for how long the measured DC voltage must exceed the negotiated limit before EvseManager raises ``MREC5OverVoltage``. Set it to ``0`` to trigger immediately once the threshold is crossed. +||||||| parent of afc3ead44 (feat(over_voltage): Internal over-voltage monitor implementation in EVSEManager) +======= +Software over-voltage supervision is always active. The configuration option +``internal_over_voltage_duration_ms`` defines for how long the measured DC voltage +must exceed the negotiated limit before EvseManager raises ``MREC5OverVoltage``. +Set it to ``0`` to trigger immediately once the threshold is crossed. + +>>>>>>> afc3ead44 (feat(over_voltage): Internal over-voltage monitor implementation in EVSEManager) Published variables =================== From 0a4b722989860a117b46445e142aa15d04103a96 Mon Sep 17 00:00:00 2001 From: florinmihut Date: Wed, 10 Dec 2025 14:04:51 +0100 Subject: [PATCH 11/14] Fix logging statement for charging readiness Signed-off-by: florinmihut --- modules/EVSE/EvseManager/EvseManager.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/EVSE/EvseManager/EvseManager.cpp b/modules/EVSE/EvseManager/EvseManager.cpp index 530c5fc69d..9f192684fd 100644 --- a/modules/EVSE/EvseManager/EvseManager.cpp +++ b/modules/EVSE/EvseManager/EvseManager.cpp @@ -1429,8 +1429,7 @@ void EvseManager::ready_to_start_charging() { charger->enable_disable_initial_state_publish(); this->p_evse->publish_ready(true); - EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green), - "πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); + EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green),"πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); if (!initial_powermeter_value_received) { EVLOG_warning << "No powermeter value received yet!"; } From c5690f2d04aeb9f9c9c15ecbf5746edcf1848485 Mon Sep 17 00:00:00 2001 From: florinmihut Date: Wed, 10 Dec 2025 16:36:10 +0100 Subject: [PATCH 12/14] Fix formatting issue in logging message Signed-off-by: florinmihut --- modules/EVSE/EvseManager/EvseManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/EVSE/EvseManager/EvseManager.cpp b/modules/EVSE/EvseManager/EvseManager.cpp index 9f192684fd..3cbc34808c 100644 --- a/modules/EVSE/EvseManager/EvseManager.cpp +++ b/modules/EVSE/EvseManager/EvseManager.cpp @@ -1429,7 +1429,7 @@ void EvseManager::ready_to_start_charging() { charger->enable_disable_initial_state_publish(); this->p_evse->publish_ready(true); - EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green),"πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); + EVLOG_info << fmt::format(fmt::emphasis::bold | fg(fmt::terminal_color::green), "πŸŒ€πŸŒ€πŸŒ€ Ready to start charging πŸŒ€πŸŒ€πŸŒ€"); if (!initial_powermeter_value_received) { EVLOG_warning << "No powermeter value received yet!"; } From 288460a58e0faf87e06d0e5965d0547d290039fc Mon Sep 17 00:00:00 2001 From: Florin Mihut Date: Fri, 19 Dec 2025 01:17:53 +0100 Subject: [PATCH 13/14] Fic the review comments Signed-off-by: Florin Mihut --- modules/EVSE/EvseManager/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/EVSE/EvseManager/CMakeLists.txt b/modules/EVSE/EvseManager/CMakeLists.txt index 8e2333275e..11ccd0cf32 100644 --- a/modules/EVSE/EvseManager/CMakeLists.txt +++ b/modules/EVSE/EvseManager/CMakeLists.txt @@ -52,7 +52,6 @@ target_compile_features(${MODULE_NAME} PUBLIC cxx_std_17) target_sources(${MODULE_NAME} PRIVATE - "over_voltage/OverVoltageMonitor.cpp" "evse/evse_managerImpl.cpp" "energy_grid/energyImpl.cpp" "token_provider/auth_token_providerImpl.cpp" From aaa0d78b638572c6527a57a0048ec43e8c1b5256 Mon Sep 17 00:00:00 2001 From: florinmihut Date: Thu, 18 Dec 2025 17:47:30 +0100 Subject: [PATCH 14/14] Update modules/EVSE/EvseManager/doc.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piet GΓΆmpel <37657534+Pietfried@users.noreply.github.com> Signed-off-by: florinmihut --- modules/EVSE/EvseManager/doc.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/EVSE/EvseManager/doc.rst b/modules/EVSE/EvseManager/doc.rst index 3e0511bd08..405217f185 100644 --- a/modules/EVSE/EvseManager/doc.rst +++ b/modules/EVSE/EvseManager/doc.rst @@ -62,6 +62,7 @@ In addition, on the DC side the following hardware modules can be connected: CableCheck, PreCharge and CurrentDemand steps. * DC power supply: This is the AC/DC converter that actually charges the car. +<<<<<<< HEAD <<<<<<< HEAD Software over-voltage supervision is always active during DC charging. The configuration option ``internal_over_voltage_duration_ms`` defines for how long the measured DC voltage @@ -71,6 +72,11 @@ Set it to ``0`` to trigger immediately once the threshold is crossed. ||||||| parent of afc3ead44 (feat(over_voltage): Internal over-voltage monitor implementation in EVSEManager) ======= Software over-voltage supervision is always active. The configuration option +||||||| parent of 7d60e60da (Update modules/EVSE/EvseManager/doc.rst) +Software over-voltage supervision is always active. The configuration option +======= +Software over-voltage supervision is always active during DC charging. The configuration option +>>>>>>> 7d60e60da (Update modules/EVSE/EvseManager/doc.rst) ``internal_over_voltage_duration_ms`` defines for how long the measured DC voltage must exceed the negotiated limit before EvseManager raises ``MREC5OverVoltage``. Set it to ``0`` to trigger immediately once the threshold is crossed.