From 0f11b3a9b49ea45e5beca3d37d35f19d1e37dea0 Mon Sep 17 00:00:00 2001 From: infiniteCable2 Date: Fri, 28 Aug 2026 21:53:35 +0200 Subject: [PATCH] eGPU Temp Overlay MICI (#25) * eGPU temperature Mici UI * positioning, state of display overlay, transparency, no dynamic on/off --- openpilot/common/params_keys.h | 1 + .../ui/mici/layouts/settings/ictoggles.py | 3 + .../ui/mici/onroad/augmented_road_view.py | 5 ++ .../mici/onroad/egpu_temperature_overlay.py | 84 +++++++++++++++++++ openpilot/selfdrive/ui/ui_state.py | 9 +- 5 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 openpilot/selfdrive/ui/mici/onroad/egpu_temperature_overlay.py diff --git a/openpilot/common/params_keys.h b/openpilot/common/params_keys.h index 77fcab2b3..455a54349 100644 --- a/openpilot/common/params_keys.h +++ b/openpilot/common/params_keys.h @@ -151,6 +151,7 @@ inline static std::unordered_map keys = { {"ForceRHDForBSM", {PERSISTENT, BOOL}}, {"DisableCarSteerAlerts", {PERSISTENT, BOOL}}, {"ShowAccelBar", {PERSISTENT, BOOL}}, + {"ICShowEgpuTemperatures", {PERSISTENT, BOOL}}, {"LiveCurvatureParameters", {PERSISTENT | DONT_LOG, BYTES}}, // --- sunnypilot params --- // diff --git a/openpilot/selfdrive/ui/mici/layouts/settings/ictoggles.py b/openpilot/selfdrive/ui/mici/layouts/settings/ictoggles.py index c88688120..b97adea20 100644 --- a/openpilot/selfdrive/ui/mici/layouts/settings/ictoggles.py +++ b/openpilot/selfdrive/ui/mici/layouts/settings/ictoggles.py @@ -22,6 +22,7 @@ class ICTogglesLayoutMici(NavScroller): enable_dark_mode = BigParamControl("Dark Mode", "DarkMode") enable_onroad_screen_timer = BigParamControl("Onroad Screen Timeout", "DisableScreenTimer") enable_accel_bar = BigParamControl("Enable Accel Bar", "ShowAccelBar") + show_egpu_temperatures = BigParamControl("Show eGPU Temperatures", "ICShowEgpuTemperatures") enable_curvatured = BigParamControl("Enable Dynamic Steering Learner", "EnableCurvatureD") show_curvatured_graph = BigParamControl("Show Dynamic Steering Learner Graph", "ShowDynamicSteeringLearnerGraph") @@ -42,6 +43,7 @@ class ICTogglesLayoutMici(NavScroller): enable_dark_mode, enable_onroad_screen_timer, enable_accel_bar, + show_egpu_temperatures, enable_curvatured, show_curvatured_graph, ]) @@ -60,6 +62,7 @@ class ICTogglesLayoutMici(NavScroller): ("DarkMode", enable_dark_mode), ("DisableScreenTimer", enable_onroad_screen_timer), ("ShowAccelBar", enable_accel_bar), + ("ICShowEgpuTemperatures", show_egpu_temperatures), ("EnableCurvatureD", enable_curvatured), ("ShowDynamicSteeringLearnerGraph", show_curvatured_graph), ) diff --git a/openpilot/selfdrive/ui/mici/onroad/augmented_road_view.py b/openpilot/selfdrive/ui/mici/onroad/augmented_road_view.py index e37e9b7ea..31df38c45 100644 --- a/openpilot/selfdrive/ui/mici/onroad/augmented_road_view.py +++ b/openpilot/selfdrive/ui/mici/onroad/augmented_road_view.py @@ -7,6 +7,7 @@ from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus from openpilot.selfdrive.ui.mici.onroad import SIDE_PANEL_WIDTH from openpilot.selfdrive.ui.mici.onroad.alert_renderer import AlertRenderer from openpilot.selfdrive.ui.mici.onroad.driver_state import DriverStateRenderer +from openpilot.selfdrive.ui.mici.onroad.egpu_temperature_overlay import EgpuTemperatureOverlay from openpilot.selfdrive.ui.mici.onroad.hud_renderer import HudRenderer from openpilot.selfdrive.ui.mici.onroad.model_renderer import ModelRenderer from openpilot.selfdrive.ui.mici.onroad.confidence_ball import ConfidenceBall @@ -152,6 +153,7 @@ class AugmentedRoadView(CameraView): self._bookmark_icon = BookmarkIcon(bookmark_callback) self._model_renderer = ModelRenderer() + self._egpu_temperature_overlay = EgpuTemperatureOverlay() self._hud_renderer = HudRenderer() self._alert_renderer = AlertRenderer() self._driver_state_renderer = DriverStateRenderer() @@ -221,6 +223,9 @@ class AugmentedRoadView(CameraView): # Fade out bottom of overlays for looks rl.draw_texture_ex(self._fade_texture, rl.Vector2(self._content_rect.x, self._content_rect.y), 0.0, 1.0, rl.WHITE) + # Render custom data overlays before alerts and the HUD so safety-critical elements stay on top. + self._egpu_temperature_overlay.render(self._content_rect) + alert_to_render, not_animating_out = self._alert_renderer.will_render() # Hide DMoji when disengaged unless AlwaysOnDM is enabled diff --git a/openpilot/selfdrive/ui/mici/onroad/egpu_temperature_overlay.py b/openpilot/selfdrive/ui/mici/onroad/egpu_temperature_overlay.py new file mode 100644 index 000000000..91e74416b --- /dev/null +++ b/openpilot/selfdrive/ui/mici/onroad/egpu_temperature_overlay.py @@ -0,0 +1,84 @@ +import pyray as rl + +from openpilot.selfdrive.ui.mici.onroad.dynamic_steering_learner_graph import CONFIG as STEERING_GRAPH_CONFIG +from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus +from openpilot.system.ui.lib.application import FontWeight, gui_app +from openpilot.system.ui.lib.text_measure import measure_text_cached +from openpilot.system.ui.widgets import Widget + + +class EgpuTemperatureOverlay(Widget): + PANEL_WIDTH = 145 + PANEL_HEIGHT = 86 + GAP_TO_STEERING_GRAPH = 8 + HORIZONTAL_PADDING = 12 + BACKGROUND_ALPHA = 108 + + TITLE_FONT_SIZE = 20 + ROW_FONT_SIZE = 18 + + def __init__(self): + super().__init__() + self._font_title = gui_app.font(FontWeight.SEMI_BOLD) + self._font_label = gui_app.font(FontWeight.MEDIUM) + self._font_value = gui_app.font(FontWeight.SEMI_BOLD) + + self._core_temp: float | None = None + self._vram_temp: float | None = None + self._available = False + self._shown_this_drive = False + self._started_frame = -1 + + def _update_state(self) -> None: + sm = ui_state.sm + if ui_state.started_frame != self._started_frame: + self._started_frame = ui_state.started_frame + self._shown_this_drive = False + self._core_temp = None + self._vram_temp = None + + self._available = (sm.seen["chestnutState"] and sm.alive["chestnutState"] and sm.valid["chestnutState"] and + sm.recv_frame["chestnutState"] >= ui_state.started_frame) + + if self._available and (sm.updated["chestnutState"] or self._core_temp is None or self._vram_temp is None): + self._core_temp = float(sm["chestnutState"].tempC) + self._vram_temp = float(sm["chestnutState"].memoryTempC) + + if self._available and ui_state.status != UIStatus.DISENGAGED: + self._shown_this_drive = True + + @staticmethod + def _format_temperature(temperature: float | None) -> str: + return "—" if temperature is None else f"{temperature:.0f}°C" + + def _draw_row(self, panel: rl.Rectangle, y: float, label: str, value: str) -> None: + label_pos = rl.Vector2(panel.x + self.HORIZONTAL_PADDING, y) + rl.draw_text_ex(self._font_label, label, label_pos, self.ROW_FONT_SIZE, 0, rl.Color(205, 205, 205, 255)) + + value_size = measure_text_cached(self._font_value, value, self.ROW_FONT_SIZE) + value_pos = rl.Vector2(panel.x + panel.width - self.HORIZONTAL_PADDING - value_size.x, y) + value_color = rl.WHITE if self._available else rl.Color(166, 166, 166, 255) + rl.draw_text_ex(self._font_value, value, value_pos, self.ROW_FONT_SIZE, 0, value_color) + + def _render(self, rect: rl.Rectangle) -> None: + if not ui_state.ic_show_egpu_temperatures or not ui_state.usbgpu or not self._shown_this_drive: + return + + graph_top = rect.y + rect.height * STEERING_GRAPH_CONFIG.zero_line_screen_y_frac - STEERING_GRAPH_CONFIG.height * 0.5 + panel = rl.Rectangle( + rect.x + rect.width - STEERING_GRAPH_CONFIG.right_margin - self.PANEL_WIDTH, + graph_top - self.GAP_TO_STEERING_GRAPH - self.PANEL_HEIGHT, + self.PANEL_WIDTH, + self.PANEL_HEIGHT, + ) + + rl.draw_rectangle_rounded(panel, 0.18, 10, rl.Color(0, 0, 0, self.BACKGROUND_ALPHA)) + rl.draw_rectangle_rounded_lines_ex(panel, 0.18, 10, 1, rl.Color(255, 255, 255, 45)) + + title_pos = rl.Vector2(panel.x + self.HORIZONTAL_PADDING, panel.y + 7) + rl.draw_text_ex(self._font_title, "eGPU", title_pos, self.TITLE_FONT_SIZE, 0, rl.WHITE) + + core = self._format_temperature(self._core_temp if self._available else None) + vram = self._format_temperature(self._vram_temp if self._available else None) + self._draw_row(panel, panel.y + 34, "CORE", core) + self._draw_row(panel, panel.y + 58, "VRAM", vram) diff --git a/openpilot/selfdrive/ui/ui_state.py b/openpilot/selfdrive/ui/ui_state.py index e58b66cb3..e1aac88e4 100644 --- a/openpilot/selfdrive/ui/ui_state.py +++ b/openpilot/selfdrive/ui/ui_state.py @@ -70,9 +70,10 @@ class UIState(UIStateSP): "carOutput", "carControl", "vehicleParameters", - "testJoystick", - "rawAudioData", - ] + ic_services + self.sm_services_ext + "testJoystick", + "rawAudioData", + "chestnutState", + ] + ic_services + self.sm_services_ext ) self.prime_state = PrimeState() @@ -107,6 +108,7 @@ class UIState(UIStateSP): self.dark_mode: bool = False self.onroad_screen_timeout: bool = False self.enable_accel_bar: bool = False + self.ic_show_egpu_temperatures: bool = False self.has_alert: bool = False self.has_status_change: bool = False self._status_prev: UIStatus = self.status @@ -259,6 +261,7 @@ class UIState(UIStateSP): self.dark_mode = self.params.get_bool("DarkMode") self.onroad_screen_timeout = self.params.get_bool("DisableScreenTimer") self.enable_accel_bar = self.params.get_bool("ShowAccelBar") + self.ic_show_egpu_temperatures = self.params.get_bool("ICShowEgpuTemperatures") class Device(DeviceSP):