From aa8a190f5a7778b89b528c7a11311066fd89ce3e Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 19:03:29 -0400 Subject: [PATCH 01/12] try this --- openpilot/system/hardware/hardwared.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index dd9c92269b..6a8afb0cb8 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -21,6 +21,7 @@ from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert from openpilot.common.hardware import HARDWARE, COMMA_HARDWARE from openpilot.common.basedir import BASEDIR from openpilot.common.hardware.usb import CHESTNUT_FW_VERSION, CHESTNUT_ROM_USB_IDS, CHESTNUT_USB_IDS, get_usb_state, get_usb_topology, set_usb_state +from openpilot.system.hardware.chestnut.flash import vbus_write from openpilot.common.linux import LinuxSystemStats from openpilot.system.loggerd.config import get_available_percent from openpilot.common.swaglog import cloudlog @@ -49,6 +50,7 @@ class Chestnut: self.attempts = 0 self.last_attempt = 0. self.flashed = False + self.vbus_on = None def flash(self) -> None: ret = subprocess.run(["sudo", sys.executable, os.path.join(BASEDIR, "openpilot/system/hardware/chestnut/flash.py"), CHESTNUT_FW_VERSION], @@ -56,9 +58,16 @@ class Chestnut: cloudlog.event("chestnut flash done", returncode=ret.returncode, output=ret.stdout[-1000:], error=ret.returncode != 0) self.flashed = ret.returncode == 0 + def set_vbus(self, on: bool) -> None: + if on == self.vbus_on: + return + vbus_write("1" if on else "0") + self.vbus_on = on + def update(self, offroad: bool, usb_state: list[dict]) -> None: mismatch = any((d["vendorId"], d["productId"]) in CHESTNUT_USB_IDS + CHESTNUT_ROM_USB_IDS and d["product"] != f"custom {CHESTNUT_FW_VERSION}-CLEAN" for d in usb_state) + self.set_vbus(not offroad or mismatch) if not mismatch: self.flashed = False return From 7419b2a0b01177f7cea5c12622ddeb89b9c5174f Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 19:22:02 -0400 Subject: [PATCH 02/12] perms --- openpilot/system/hardware/hardwared.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index 6a8afb0cb8..0b293941c8 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -21,7 +21,7 @@ from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert from openpilot.common.hardware import HARDWARE, COMMA_HARDWARE from openpilot.common.basedir import BASEDIR from openpilot.common.hardware.usb import CHESTNUT_FW_VERSION, CHESTNUT_ROM_USB_IDS, CHESTNUT_USB_IDS, get_usb_state, get_usb_topology, set_usb_state -from openpilot.system.hardware.chestnut.flash import vbus_write +from openpilot.system.hardware.chestnut.flash import VBUS_PATH from openpilot.common.linux import LinuxSystemStats from openpilot.system.loggerd.config import get_available_percent from openpilot.common.swaglog import cloudlog @@ -61,7 +61,7 @@ class Chestnut: def set_vbus(self, on: bool) -> None: if on == self.vbus_on: return - vbus_write("1" if on else "0") + subprocess.run(["sudo", "tee", VBUS_PATH], input=b"1" if on else b"0", stdout=subprocess.DEVNULL, check=False) self.vbus_on = on def update(self, offroad: bool, usb_state: list[dict]) -> None: From 7bb32de4b8623c6cad6224f4b7d0652664e4cbf6 Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 19:31:18 -0400 Subject: [PATCH 03/12] ignition --- openpilot/system/hardware/hardwared.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index 0b293941c8..e376bc96cd 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -64,10 +64,10 @@ class Chestnut: subprocess.run(["sudo", "tee", VBUS_PATH], input=b"1" if on else b"0", stdout=subprocess.DEVNULL, check=False) self.vbus_on = on - def update(self, offroad: bool, usb_state: list[dict]) -> None: + def update(self, ignition: bool, offroad: bool, usb_state: list[dict]) -> None: mismatch = any((d["vendorId"], d["productId"]) in CHESTNUT_USB_IDS + CHESTNUT_ROM_USB_IDS and d["product"] != f"custom {CHESTNUT_FW_VERSION}-CLEAN" for d in usb_state) - self.set_vbus(not offroad or mismatch) + self.set_vbus(ignition or mismatch) if not mismatch: self.flashed = False return @@ -309,7 +309,7 @@ def hardware_thread(end_event, hw_queue) -> None: msg.deviceState.screenBrightnessPercent = HARDWARE.get_screen_brightness() set_usb_state(msg.deviceState, last_hw_state.usb_state) - chestnut.update(started_ts is None, last_hw_state.usb_state) + chestnut.update(onroad_conditions["ignition"], started_ts is None, last_hw_state.usb_state) current_channel = get_build_metadata().channel chestnut_target = CHESTNUT_BRANCHES.get(current_channel) chestnut_needs_switch = msg.deviceState.chestnutPresent and not big_model_available and chestnut_target is not None From 4033119fa0e78f65adb98306a93d2ba26a10b261 Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 19:50:10 -0400 Subject: [PATCH 04/12] Revert "ignition" This reverts commit 304df24970537b16f7c304dd1eb2406d5a8b9a91. --- openpilot/system/hardware/hardwared.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index e376bc96cd..0b293941c8 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -64,10 +64,10 @@ class Chestnut: subprocess.run(["sudo", "tee", VBUS_PATH], input=b"1" if on else b"0", stdout=subprocess.DEVNULL, check=False) self.vbus_on = on - def update(self, ignition: bool, offroad: bool, usb_state: list[dict]) -> None: + def update(self, offroad: bool, usb_state: list[dict]) -> None: mismatch = any((d["vendorId"], d["productId"]) in CHESTNUT_USB_IDS + CHESTNUT_ROM_USB_IDS and d["product"] != f"custom {CHESTNUT_FW_VERSION}-CLEAN" for d in usb_state) - self.set_vbus(ignition or mismatch) + self.set_vbus(not offroad or mismatch) if not mismatch: self.flashed = False return @@ -309,7 +309,7 @@ def hardware_thread(end_event, hw_queue) -> None: msg.deviceState.screenBrightnessPercent = HARDWARE.get_screen_brightness() set_usb_state(msg.deviceState, last_hw_state.usb_state) - chestnut.update(onroad_conditions["ignition"], started_ts is None, last_hw_state.usb_state) + chestnut.update(started_ts is None, last_hw_state.usb_state) current_channel = get_build_metadata().channel chestnut_target = CHESTNUT_BRANCHES.get(current_channel) chestnut_needs_switch = msg.deviceState.chestnutPresent and not big_model_available and chestnut_target is not None From b7c40b4c4494f0dcd1c45443db51c4abe62746c3 Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 20:25:22 -0400 Subject: [PATCH 05/12] fix ui --- openpilot/selfdrive/ui/ui_state.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpilot/selfdrive/ui/ui_state.py b/openpilot/selfdrive/ui/ui_state.py index 4d93e1aacb..124852e4ba 100644 --- a/openpilot/selfdrive/ui/ui_state.py +++ b/openpilot/selfdrive/ui/ui_state.py @@ -220,6 +220,7 @@ class UIState(UIStateSP): ChestnutState.UNCOMPILED if detected else ChestnutState.DISCONNECTED) return + self.chestnut_present = self.chestnut_present or detected model_seen = self.sm.recv_frame["modelV2"] > self.started_frame if not self.chestnut_present: self.chestnut_state = ChestnutState.DISCONNECTED From a5ec1b3f161b4ff3d5ff1c76f670fc83a49f7430 Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 20:33:19 -0400 Subject: [PATCH 06/12] do we need this --- openpilot/sunnypilot/modeld_v2/modeld.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpilot/sunnypilot/modeld_v2/modeld.py b/openpilot/sunnypilot/modeld_v2/modeld.py index d9c04d7824..6428dc9324 100755 --- a/openpilot/sunnypilot/modeld_v2/modeld.py +++ b/openpilot/sunnypilot/modeld_v2/modeld.py @@ -366,15 +366,15 @@ def main(demo=False): import threading def load(): nonlocal model - model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height, chestnut=True) + try: + model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height, chestnut=True) + except Exception: + cloudlog.exception("chestnut big model load failed") t = threading.Thread(target=load, daemon=True) t.start() t.join(60) - if model is None: - params.put_bool("ChestnutActive", False) - raise RuntimeError("chestnut model load failed or timed out (60s)") - params.put_bool("ChestnutActive", True) - else: + params.put_bool("ChestnutActive", model is not None) + if model is None: model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height, chestnut=False) params.put_bool("ChestnutLoading", False) From 34e35d49e1358ab854868bb58060829692750980 Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 20:41:31 -0400 Subject: [PATCH 07/12] Revert "do we need this" This reverts commit 1daecafee401e981b91a9f2df50d4e98b5113542. --- openpilot/sunnypilot/modeld_v2/modeld.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpilot/sunnypilot/modeld_v2/modeld.py b/openpilot/sunnypilot/modeld_v2/modeld.py index 6428dc9324..d9c04d7824 100755 --- a/openpilot/sunnypilot/modeld_v2/modeld.py +++ b/openpilot/sunnypilot/modeld_v2/modeld.py @@ -366,15 +366,15 @@ def main(demo=False): import threading def load(): nonlocal model - try: - model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height, chestnut=True) - except Exception: - cloudlog.exception("chestnut big model load failed") + model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height, chestnut=True) t = threading.Thread(target=load, daemon=True) t.start() t.join(60) - params.put_bool("ChestnutActive", model is not None) - if model is None: + if model is None: + params.put_bool("ChestnutActive", False) + raise RuntimeError("chestnut model load failed or timed out (60s)") + params.put_bool("ChestnutActive", True) + else: model = ModelState(cam_w=vipc_client_main.width, cam_h=vipc_client_main.height, chestnut=False) params.put_bool("ChestnutLoading", False) From cf0c41af965dec3ef390ef420f56bc6be4b31a88 Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 21:29:28 -0400 Subject: [PATCH 08/12] AuxPowerSave --- openpilot/common/params_keys.h | 1 + openpilot/sunnypilot/sunnylink/settings_ui.json | 6 ++++++ .../sunnylink/settings_ui_src/pages/device.yaml | 4 ++++ openpilot/system/hardware/hardwared.py | 8 +++++++- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/openpilot/common/params_keys.h b/openpilot/common/params_keys.h index 3c5f30ab3d..6b6328df58 100644 --- a/openpilot/common/params_keys.h +++ b/openpilot/common/params_keys.h @@ -132,6 +132,7 @@ inline static std::unordered_map keys = { {"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}}, {"ChestnutActive", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}}, {"ChestnutLoading", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}}, + {"AuxPowerSave", {PERSISTENT | BACKUP, BOOL}}, {"Version", {PERSISTENT, STRING}}, // --- sunnypilot params --- // diff --git a/openpilot/sunnypilot/sunnylink/settings_ui.json b/openpilot/sunnypilot/sunnylink/settings_ui.json index 63bf342fcb..f0cbc07837 100644 --- a/openpilot/sunnypilot/sunnylink/settings_ui.json +++ b/openpilot/sunnypilot/sunnylink/settings_ui.json @@ -1675,6 +1675,12 @@ "widget": "toggle", "title": "Onroad Uploads" }, + { + "key": "AuxPowerSave", + "widget": "toggle", + "title": "Disable Aux Port When Offroad", + "description": "Power off the aux USB-C port while offroad to save power. It powers back on automatically when you go onroad." + }, { "key": "MaxTimeOffroad", "widget": "option", diff --git a/openpilot/sunnypilot/sunnylink/settings_ui_src/pages/device.yaml b/openpilot/sunnypilot/sunnylink/settings_ui_src/pages/device.yaml index bf5f7883ab..d519a1f9b9 100644 --- a/openpilot/sunnypilot/sunnylink/settings_ui_src/pages/device.yaml +++ b/openpilot/sunnypilot/sunnylink/settings_ui_src/pages/device.yaml @@ -30,6 +30,10 @@ sections: - key: OnroadUploads widget: toggle title: Onroad Uploads + - key: AuxPowerSave + widget: toggle + title: Disable Aux Port When Offroad + description: Power off the aux USB-C port while offroad to save power. It powers back on automatically when you go onroad. - key: MaxTimeOffroad widget: option title: Max Time Offroad diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index 0b293941c8..e6e8486a1e 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -51,6 +51,9 @@ class Chestnut: self.last_attempt = 0. self.flashed = False self.vbus_on = None + self.params = Params() + self.powersave = False + self.last_offroad = None def flash(self) -> None: ret = subprocess.run(["sudo", sys.executable, os.path.join(BASEDIR, "openpilot/system/hardware/chestnut/flash.py"), CHESTNUT_FW_VERSION], @@ -67,7 +70,10 @@ class Chestnut: def update(self, offroad: bool, usb_state: list[dict]) -> None: mismatch = any((d["vendorId"], d["productId"]) in CHESTNUT_USB_IDS + CHESTNUT_ROM_USB_IDS and d["product"] != f"custom {CHESTNUT_FW_VERSION}-CLEAN" for d in usb_state) - self.set_vbus(not offroad or mismatch) + if offroad != self.last_offroad: + self.powersave = self.params.get_bool("AuxPowerSave") + self.last_offroad = offroad + self.set_vbus((not offroad or mismatch) or not self.powersave) if not mismatch: self.flashed = False return From a763c93496e1dac7a1818a1edc752b89ae622f22 Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 21:41:50 -0400 Subject: [PATCH 09/12] simple for now --- openpilot/system/hardware/hardwared.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index e6e8486a1e..565c4afea6 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -52,8 +52,6 @@ class Chestnut: self.flashed = False self.vbus_on = None self.params = Params() - self.powersave = False - self.last_offroad = None def flash(self) -> None: ret = subprocess.run(["sudo", sys.executable, os.path.join(BASEDIR, "openpilot/system/hardware/chestnut/flash.py"), CHESTNUT_FW_VERSION], @@ -70,10 +68,7 @@ class Chestnut: def update(self, offroad: bool, usb_state: list[dict]) -> None: mismatch = any((d["vendorId"], d["productId"]) in CHESTNUT_USB_IDS + CHESTNUT_ROM_USB_IDS and d["product"] != f"custom {CHESTNUT_FW_VERSION}-CLEAN" for d in usb_state) - if offroad != self.last_offroad: - self.powersave = self.params.get_bool("AuxPowerSave") - self.last_offroad = offroad - self.set_vbus((not offroad or mismatch) or not self.powersave) + self.set_vbus((not offroad or mismatch) or not self.params.get_bool("AuxPowerSave")) if not mismatch: self.flashed = False return From d7aa0f500234016bd73ae4163cc63c108cfeeb95 Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 21:48:13 -0400 Subject: [PATCH 10/12] ignore for now --- openpilot/system/hardware/hardwared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index 565c4afea6..a503a396f6 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -68,7 +68,7 @@ class Chestnut: def update(self, offroad: bool, usb_state: list[dict]) -> None: mismatch = any((d["vendorId"], d["productId"]) in CHESTNUT_USB_IDS + CHESTNUT_ROM_USB_IDS and d["product"] != f"custom {CHESTNUT_FW_VERSION}-CLEAN" for d in usb_state) - self.set_vbus((not offroad or mismatch) or not self.params.get_bool("AuxPowerSave")) + self.set_vbus(not offroad or mismatch) if not mismatch: self.flashed = False return From 2f2692d51504055af63b59f6cf0cb69f71c13dbf Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 21:53:55 -0400 Subject: [PATCH 11/12] Revert "ignore for now" This reverts commit f571b2b9201f0a7a5571d3114a317bd9e55d879b. --- openpilot/system/hardware/hardwared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index a503a396f6..565c4afea6 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -68,7 +68,7 @@ class Chestnut: def update(self, offroad: bool, usb_state: list[dict]) -> None: mismatch = any((d["vendorId"], d["productId"]) in CHESTNUT_USB_IDS + CHESTNUT_ROM_USB_IDS and d["product"] != f"custom {CHESTNUT_FW_VERSION}-CLEAN" for d in usb_state) - self.set_vbus(not offroad or mismatch) + self.set_vbus((not offroad or mismatch) or not self.params.get_bool("AuxPowerSave")) if not mismatch: self.flashed = False return From d44645fc53dff32ed28910c254b303f9442f6d9e Mon Sep 17 00:00:00 2001 From: royjr Date: Sun, 30 Aug 2026 21:56:49 -0400 Subject: [PATCH 12/12] Revert "simple for now" This reverts commit 01420fc08488374ec8fe3d17b757ccd1564d1328. --- openpilot/system/hardware/hardwared.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openpilot/system/hardware/hardwared.py b/openpilot/system/hardware/hardwared.py index 565c4afea6..e6e8486a1e 100755 --- a/openpilot/system/hardware/hardwared.py +++ b/openpilot/system/hardware/hardwared.py @@ -52,6 +52,8 @@ class Chestnut: self.flashed = False self.vbus_on = None self.params = Params() + self.powersave = False + self.last_offroad = None def flash(self) -> None: ret = subprocess.run(["sudo", sys.executable, os.path.join(BASEDIR, "openpilot/system/hardware/chestnut/flash.py"), CHESTNUT_FW_VERSION], @@ -68,7 +70,10 @@ class Chestnut: def update(self, offroad: bool, usb_state: list[dict]) -> None: mismatch = any((d["vendorId"], d["productId"]) in CHESTNUT_USB_IDS + CHESTNUT_ROM_USB_IDS and d["product"] != f"custom {CHESTNUT_FW_VERSION}-CLEAN" for d in usb_state) - self.set_vbus((not offroad or mismatch) or not self.params.get_bool("AuxPowerSave")) + if offroad != self.last_offroad: + self.powersave = self.params.get_bool("AuxPowerSave") + self.last_offroad = offroad + self.set_vbus((not offroad or mismatch) or not self.powersave) if not mismatch: self.flashed = False return