diff --git a/common/params_keys.h b/common/params_keys.h index 1aa462b711..fa608f2e3b 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -441,6 +441,7 @@ inline static std::unordered_map keys = { {"IncreaseFollowingRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}}, {"IncreaseFollowingRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}}, {"IncreaseFollowingSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}}, + {"AggressiveCoolingEnabled", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}}, {"IncreaseThermalLimits", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}}, {"IssueReported", {CLEAR_ON_MANAGER_START, JSON, "{}", "{}"}}, {"KonikDongleId", {PERSISTENT, STRING, "", "", 0}}, diff --git a/starpilot/common/assets/device_settings_layout.json b/starpilot/common/assets/device_settings_layout.json index ac2ae2150e..b557428f55 100644 --- a/starpilot/common/assets/device_settings_layout.json +++ b/starpilot/common/assets/device_settings_layout.json @@ -4652,6 +4652,17 @@ "parent_key": "DeviceManagement", "settings_tier": "simple" }, + { + "key": "AggressiveCoolingEnabled", + "label": "C3/C3X Aggressive Cooling", + "description": "Runs the fan on a lower, more aggressive temperature curve: the fan ramps up sooner and reacts faster to rising temperature, keeping the device meaningfully cooler on hot days at the cost of more fan noise.", + "picker_description": "Cooler-running, louder fan curve for comma 3/3X.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "DeviceManagement", + "settings_tier": "simple", + "requires_capability": "IsTiciOrTizi" + }, { "key": "UseKonikServer", "label": "Use Konik Server", diff --git a/starpilot/common/starpilot_variables.py b/starpilot/common/starpilot_variables.py index f66d06cbfe..3bb8db35b7 100644 --- a/starpilot/common/starpilot_variables.py +++ b/starpilot/common/starpilot_variables.py @@ -1004,6 +1004,7 @@ class StarPilotVariables: ) toggle.device_shutdown_time = device_shutdown_seconds(device_shutdown_hours) toggle.increase_thermal_limits = self.get_value("IncreaseThermalLimits", condition=device_management) + toggle.aggressive_cooling = self.get_value("AggressiveCoolingEnabled", condition=device_management) toggle.low_voltage_shutdown = self.get_value("LowVoltageShutdown", cast=float, condition=device_management, min=VBATT_PAUSE_CHARGING, max=12.5) # Keep force-onroad desktop simulations from polluting logs, but never disable # loggerd/encoderd on real devices because that breaks route continuity/uploads. diff --git a/starpilot/system/the_galaxy/the_galaxy.py b/starpilot/system/the_galaxy/the_galaxy.py index 3343cf1aca..1aaa7b66ce 100644 --- a/starpilot/system/the_galaxy/the_galaxy.py +++ b/starpilot/system/the_galaxy/the_galaxy.py @@ -4147,6 +4147,9 @@ def _get_vehicle_parked(): except Exception: return False +def _get_is_tici_or_tizi(): + return HARDWARE.get_device_type() in ("tici", "tizi") + def _get_alpha_longitudinal_available(): cp_bytes = _safe_params_get_live_raw("CarParamsPersistent") if not cp_bytes: @@ -6206,6 +6209,7 @@ def setup(app): result["VehicleParked"] = _get_vehicle_parked() result["AlphaLongitudinalAvailable"] = _get_alpha_longitudinal_available() result["HasRivianAngleHarness"] = _get_has_rivian_angle_harness() + result["IsTiciOrTizi"] = _get_is_tici_or_tizi() for key in ("CalibratedLateralAcceleration", "CalibrationProgress"): try: diff --git a/system/hardware/fan_controller.py b/system/hardware/fan_controller.py index f326e7ee99..534b103ff0 100755 --- a/system/hardware/fan_controller.py +++ b/system/hardware/fan_controller.py @@ -7,13 +7,18 @@ from openpilot.common.swaglog import cloudlog from openpilot.common.pid import PIDController from openpilot.system.hardware import HARDWARE -# raise fan setpoint on tici/tizi to reduce noise -# after raising LMH threshold in AGNOS 18.1 to prevent CPU throttling -OFFSET = 0 if HARDWARE.get_device_type() == "mici" else 5 +# comma 4 (mici) always uses the stock curve; comma 3/3X (tici/tizi) can opt into a more +# aggressive, cooler-targeting curve via the "C3/C3X Aggressive Cooling" toggle +IS_MICI = HARDWARE.get_device_type() == "mici" + +# original comma/sunnypilot curve: quieter on tici/tizi (higher setpoint) than on mici +STOCK_CURVE = dict(offset=0, k_p=0, ff_low=60.0, ff_high=100.0) if IS_MICI else \ + dict(offset=5, k_p=0, ff_low=65.0, ff_high=105.0) +AGGRESSIVE_CURVE = dict(offset=-5, k_p=1.0, ff_low=55.0, ff_high=80.0) class BaseFanController(ABC): @abstractmethod - def update(self, cur_temp: float, ignition: bool) -> int: + def update(self, cur_temp: float, ignition: bool, aggressive_cooling: bool = False) -> int: pass @@ -23,20 +28,26 @@ class TiciFanController(BaseFanController): cloudlog.info("Setting up TICI fan handler") self.last_ignition = False - self.controller = PIDController(k_p=0, k_i=4e-3, rate=(1 / DT_HW)) + self.last_aggressive_cooling = False + self.controller = PIDController(k_p=STOCK_CURVE["k_p"], k_i=4e-3, rate=(1 / DT_HW)) + + def update(self, cur_temp: float, ignition: bool, aggressive_cooling: bool = False) -> int: + use_aggressive = aggressive_cooling and not IS_MICI + curve = AGGRESSIVE_CURVE if use_aggressive else STOCK_CURVE - def update(self, cur_temp: float, ignition: bool) -> int: self.controller.pos_limit = 100 if ignition else 30 self.controller.neg_limit = 30 if ignition else 0 + self.controller._k_p = [[0], [curve["k_p"]]] - if ignition != self.last_ignition: + if ignition != self.last_ignition or use_aggressive != self.last_aggressive_cooling: self.controller.reset() - error = cur_temp - (75 + OFFSET) + error = cur_temp - (75 + curve["offset"]) fan_pwr_out = int(self.controller.update( error=error, - feedforward=np.interp(cur_temp, [60.0 + OFFSET, 100.0 + OFFSET], [0, 100]) + feedforward=np.interp(cur_temp, [curve["ff_low"], curve["ff_high"]], [0, 100]) )) self.last_ignition = ignition + self.last_aggressive_cooling = use_aggressive return fan_pwr_out diff --git a/system/hardware/hardwared.py b/system/hardware/hardwared.py index 648350c3ae..3477a9e39e 100644 --- a/system/hardware/hardwared.py +++ b/system/hardware/hardwared.py @@ -460,7 +460,7 @@ def hardware_thread(end_event, hw_queue) -> None: msg.deviceState.maxTempC = all_comp_temp if fan_controller is not None: - msg.deviceState.fanSpeedPercentDesired = fan_controller.update(all_comp_temp, onroad_conditions["ignition"]) + msg.deviceState.fanSpeedPercentDesired = fan_controller.update(all_comp_temp, onroad_conditions["ignition"], starpilot_toggles.aggressive_cooling) # StarPilot variables if starpilot_toggles.increase_thermal_limits: