diff --git a/firmware/console/status_loop.cpp b/firmware/console/status_loop.cpp index 6fd4a94d21..a8f58d045f 100644 --- a/firmware/console/status_loop.cpp +++ b/firmware/console/status_loop.cpp @@ -650,6 +650,7 @@ static void updateIgnition(int rpm) { } static void updateFlags() { + engine->outputChannels.isMainRelayOn = enginePins.mainRelay.getLogicValue(); engine->outputChannels.isFuelPumpOn = enginePins.fuelPumpRelay.getLogicValue(); engine->outputChannels.isFanOn = enginePins.fanRelay.getLogicValue(); engine->outputChannels.isFan2On = enginePins.fanRelay2.getLogicValue(); @@ -710,7 +711,7 @@ void updateTunerStudioState() { // 104 tsOutputChannels->rpmAcceleration = engine->rpmCalculator.getRpmAcceleration(); - + // Output both the estimated air flow, and measured air flow (if available) tsOutputChannels->mafMeasured = Sensor::getOrZero(SensorType::Maf); tsOutputChannels->mafEstimate = engine->engineState.airflowEstimate; diff --git a/firmware/controllers/actuators/main_relay.cpp b/firmware/controllers/actuators/main_relay.cpp index cf827569d3..a03d3006d6 100644 --- a/firmware/controllers/actuators/main_relay.cpp +++ b/firmware/controllers/actuators/main_relay.cpp @@ -6,7 +6,20 @@ void MainRelayController::onSlowCallback() { isBenchTest = engine->isInMainRelayBench(); #if EFI_MAIN_RELAY_CONTROL - mainRelayState = isBenchTest | hasIgnitionVoltage; + hasIgnitionVoltage = Sensor::getOrZero(SensorType::BatteryVoltage) > 5; + + if (hasIgnitionVoltage) { + m_lastIgnitionTime.reset(); + } + + delayedShutoffRequested = engine->module()->needsDelayedShutoff(); + + // Query whether any engine modules want to keep the lights on +// todo: fix this amazing C++ lambda magic +// delayedShutoffRequested = engine->engineModules.aggregate([](auto& m, bool prev) { return m->needsDelayedShutoff() | prev; }, false); + // TODO: delayed shutoff timeout? + + mainRelayState = isBenchTest | hasIgnitionVoltage | delayedShutoffRequested; #else // not EFI_MAIN_RELAY_CONTROL mainRelayState = !isBenchTest; #endif @@ -14,6 +27,9 @@ void MainRelayController::onSlowCallback() { enginePins.mainRelay.setValue(mainRelayState); } -void MainRelayController::onIgnitionStateChanged(bool ignitionOn) { - hasIgnitionVoltage = ignitionOn; +bool MainRelayController::needsDelayedShutoff() { + // Prevent main relay from turning off if we had igniton voltage in the past 1 second + // This avoids accidentally killing the car during a transient, for example + // right when the starter is engaged. + return !m_lastIgnitionTime.hasElapsedSec(1); } diff --git a/firmware/controllers/actuators/main_relay.h b/firmware/controllers/actuators/main_relay.h index 6850a51b9f..a3f0f3ca1c 100644 --- a/firmware/controllers/actuators/main_relay.h +++ b/firmware/controllers/actuators/main_relay.h @@ -3,7 +3,11 @@ #include "engine_module.h" #include "main_relay_generated.h" -struct MainRelayController : public EngineModule, public main_relay_s { +class MainRelayController : public EngineModule, public main_relay_s { +public: void onSlowCallback() override; - void onIgnitionStateChanged(bool ignitionOn) override; + bool needsDelayedShutoff() override; + +private: + Timer m_lastIgnitionTime; }; diff --git a/firmware/controllers/core/engine_module.h b/firmware/controllers/core/engine_module.h index 5384b26f1b..09aab74220 100644 --- a/firmware/controllers/core/engine_module.h +++ b/firmware/controllers/core/engine_module.h @@ -13,4 +13,7 @@ public: // Called whenever the ignition switch state changes virtual void onIgnitionStateChanged(bool /*ignitionOn*/) { } + + // Queried to determine whether this module needs a delayed shutoff, defaults to false + virtual bool needsDelayedShutoff() { return false; } }; diff --git a/firmware/tunerstudio/rusefi.input b/firmware/tunerstudio/rusefi.input index 211a56852c..a942888c96 100644 --- a/firmware/tunerstudio/rusefi.input +++ b/firmware/tunerstudio/rusefi.input @@ -1274,6 +1274,7 @@ gaugeCategory = DynoView ; minor info indicator = { isFanOn }, "fan off", "fan on", white, black, green, black indicator = { isFan2On }, "fan 2 off", "fan 2 on", white, black, green, black + indicator = { isMainRelayOn }, "main relay off", "main relay on", white, black, green, black indicator = { isCylinderCleanupActivated}, "no cyl cleanup", "cyl cleanup", white, black, yellow, black indicator = { isFuelPumpOn}, "pump off", "pump on", white, black, green, black indicator = { clutchUpState }, "Clutch Up", "clutch Up", white, black, red, black @@ -1436,7 +1437,7 @@ menuDialog = main subMenu = crankingCltCurve, "Fuel CLT multiplier" subMenu = crankingCltCurveE100, "Fuel CLT multiplier (Flex Fuel E100)", 0, { flexSensorPin != @@ADC_CHANNEL_NONE@@ } - subMenu = crankingDurationCurve, "Fuel duration multiplier" + subMenu = crankingDurationCurve, "Fuel duration multiplier" subMenu = crankingTpsCurve, "Fuel TPS multiplier" subMenu = std_separator @@ -2111,7 +2112,7 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@\x00\x31\x00\x00" field = "Offset cyl 8", timing_offset_cylinder8, {cylindersCount > 7} field = "Offset cyl 9", timing_offset_cylinder9, {cylindersCount > 8} field = "Offset cyl 10", timing_offset_cylinder10, {cylindersCount > 9} - field = "Offset cyl 11", timing_offset_cylinder11, {cylindersCount > 10} + field = "Offset cyl 11", timing_offset_cylinder11, {cylindersCount > 10} field = "Offset cyl 12", timing_offset_cylinder12, {cylindersCount > 11} dialog = multisparkDwellParams, "Delay & Dwell" diff --git a/firmware/util/containers/type_list.h b/firmware/util/containers/type_list.h index 2f6733deac..7f4aaedfd1 100644 --- a/firmware/util/containers/type_list.h +++ b/firmware/util/containers/type_list.h @@ -45,6 +45,14 @@ struct type_list { others.apply_all(f); } + // Applies an accumulator function over the sequence of elements. + // The specified seed value is used as the initial accumulator value, + // and the specified function is used to select the result value. + template + auto aggregate(func_t const& accumulator, return_t seed) { + return others.aggregate(accumulator, first.aggregate(accumulator, seed)); + } + /* * Return the container object for type get_t. * @@ -92,6 +100,11 @@ public: f(me); } + template + auto aggregate(func_t const& accumulator, return_t seed) { + return accumulator(me, seed); + } + template static constexpr bool has() { return std::is_same_v; diff --git a/unit_tests/tests/test_main_relay.cpp b/unit_tests/tests/test_main_relay.cpp index d004f2c5a0..698f2671cb 100644 --- a/unit_tests/tests/test_main_relay.cpp +++ b/unit_tests/tests/test_main_relay.cpp @@ -2,18 +2,17 @@ #include "main_relay.h" -TEST(MainRelay, mr) { +TEST(MainRelay, mainRelayLogic) { EngineTestHelper eth(TEST_ENGINE); MainRelayController dut; // Ignition is off, MR is off - dut.onIgnitionStateChanged(false); dut.onSlowCallback(); EXPECT_EQ(enginePins.mainRelay.getLogicValue(), false); - // Ignition is now on, MR is on - dut.onIgnitionStateChanged(true); + // Battery above threshold - MR is on + Sensor::setMockValue(SensorType::BatteryVoltage, 13); dut.onSlowCallback(); EXPECT_EQ(enginePins.mainRelay.getLogicValue(), true); }