From 7a43e2cb6736421e45b4b5e2e51cb381ac3ecd9e Mon Sep 17 00:00:00 2001 From: Zeph Date: Wed, 25 Feb 2026 00:52:48 -0600 Subject: [PATCH 1/5] controlsd: fix steer_limited_by_safety not updating under MADS (#1716) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `steer_limited_by_safety` update in `publish()` is gated by `selfdriveState.active`, which is False during MADS lateral-only control. This causes the flag to never update once cruise deactivates — it stays stuck at whatever value it had during the last ramp-up (typically True), permanently suppressing the saturation timer in `_check_saturation` and preventing the "Turn Exceeds Steering Limit" alert from firing. Use `CC.latActive` instead, which already accounts for MADS via `get_lat_active()` in ControlsExt. Bug was introduced in #446 (MADS), which updated `CC.latActive` to use `mads.active` but missed updating the `steer_limited_by_safety` gate in `publish()`. --- selfdrive/controls/controlsd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 12b146e80b..1c8e9f76b1 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -185,7 +185,7 @@ class Controls(ControlsExt): hudControl.leftLaneDepart = self.sm['driverAssistance'].leftLaneDeparture hudControl.rightLaneDepart = self.sm['driverAssistance'].rightLaneDeparture - if self.sm['selfdriveState'].active: + if self.get_lat_active(self.sm): CO = self.sm['carOutput'] if self.CP.steerControlType == car.CarParams.SteerControlType.angle: self.steer_limited_by_safety = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \ From 538e1e8a9a77ababf8492108867b8adccc59e7d3 Mon Sep 17 00:00:00 2001 From: Zeph Date: Wed, 25 Feb 2026 01:00:07 -0600 Subject: [PATCH 2/5] soundd: trigger timeout warning during MADS lateral-only (#1717) * soundd: trigger timeout warning during MADS lateral-only The selfdrive timeout alert (warningImmediate) only fires when selfdriveState.enabled is True. During MADS lateral-only mode, enabled is False even though the system is actively steering. If selfdrived stops publishing while MADS lateral is active, the driver gets no audible warning that steering has become unresponsive. Add selfdriveStateSP to the SubMaster and check mads.active alongside selfdriveState.enabled so the timeout alert fires whenever the system is actuating steering. * test_soundd: add MADS lateral-only timeout test Test that the selfdrive timeout warning fires when selfdriveState.enabled is False but selfdriveStateSP.mads.active is True. --------- Co-authored-by: Jason Wen --- selfdrive/ui/soundd.py | 4 ++-- selfdrive/ui/tests/test_soundd.py | 30 ++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/selfdrive/ui/soundd.py b/selfdrive/ui/soundd.py index c41fec2676..edf28b7128 100644 --- a/selfdrive/ui/soundd.py +++ b/selfdrive/ui/soundd.py @@ -65,7 +65,7 @@ def check_selfdrive_timeout_alert(sm): ss_missing = time.monotonic() - sm.recv_time['selfdriveState'] if ss_missing > SELFDRIVE_STATE_TIMEOUT: - if sm['selfdriveState'].enabled and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < 10: + if (sm['selfdriveState'].enabled or sm['selfdriveStateSP'].mads.enabled) and (ss_missing - SELFDRIVE_STATE_TIMEOUT) < 10: return True return False @@ -158,7 +158,7 @@ class Soundd(QuietMode): # sounddevice must be imported after forking processes import sounddevice as sd - sm = messaging.SubMaster(['selfdriveState', 'soundPressure']) + sm = messaging.SubMaster(['selfdriveState', 'selfdriveStateSP', 'soundPressure']) with self.get_stream(sd) as stream: rk = Ratekeeper(20) diff --git a/selfdrive/ui/tests/test_soundd.py b/selfdrive/ui/tests/test_soundd.py index a9da8455eb..226117ae8f 100644 --- a/selfdrive/ui/tests/test_soundd.py +++ b/selfdrive/ui/tests/test_soundd.py @@ -10,8 +10,8 @@ AudibleAlert = car.CarControl.HUDControl.AudibleAlert class TestSoundd: def test_check_selfdrive_timeout_alert(self): - sm = SubMaster(['selfdriveState']) - pm = PubMaster(['selfdriveState']) + sm = SubMaster(['selfdriveState', 'selfdriveStateSP']) + pm = PubMaster(['selfdriveState', 'selfdriveStateSP']) for _ in range(100): cs = messaging.new_message('selfdriveState') @@ -31,5 +31,31 @@ class TestSoundd: assert check_selfdrive_timeout_alert(sm) + def test_check_selfdrive_timeout_alert_mads_lateral_only(self): + sm = SubMaster(['selfdriveState', 'selfdriveStateSP']) + pm = PubMaster(['selfdriveState', 'selfdriveStateSP']) + + for _ in range(100): + cs = messaging.new_message('selfdriveState') + cs.selfdriveState.enabled = False + + ss_sp = messaging.new_message('selfdriveStateSP') + ss_sp.selfdriveStateSP.mads.enabled = True + + pm.send("selfdriveState", cs) + pm.send("selfdriveStateSP", ss_sp) + + time.sleep(0.01) + + sm.update(0) + + assert not check_selfdrive_timeout_alert(sm) + + for _ in range(SELFDRIVE_STATE_TIMEOUT * 110): + sm.update(0) + time.sleep(0.01) + + assert check_selfdrive_timeout_alert(sm) + # TODO: add test with micd for checking that soundd actually outputs sounds From a4166563e1fe28d9f64637f7049ebe65cc59bfed Mon Sep 17 00:00:00 2001 From: Lukas Heintz <61192133+lukasloetkolben@users.noreply.github.com> Date: Wed, 25 Feb 2026 08:39:50 +0100 Subject: [PATCH 3/5] pandad: flasher for Rivian long upgrade module (#1712) * flasher for Rivian long upgrade * self-contained no dependency on Panda.F4_DEVICES * standalone flasher * move to sp module * use brand field directly * use brand field directly * use brand field directly * bump * add some logging --------- Co-authored-by: Jason Wen --- opendbc_repo | 2 +- selfdrive/pandad/pandad.py | 5 + sunnypilot/selfdrive/pandad/__init__.py | 0 .../selfdrive/pandad/rivian_long_flasher.py | 96 ++++++++++++++++++ .../pandad/rivian_long_fw.bin.signed | Bin 0 -> 59500 bytes 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 sunnypilot/selfdrive/pandad/__init__.py create mode 100755 sunnypilot/selfdrive/pandad/rivian_long_flasher.py create mode 100644 sunnypilot/selfdrive/pandad/rivian_long_fw.bin.signed diff --git a/opendbc_repo b/opendbc_repo index 613a562bba..cbcd126951 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 613a562bba80997b5c1c74cd799641d06fb2b38c +Subproject commit cbcd126951094311ab5a43dd384a495cd881c080 diff --git a/selfdrive/pandad/pandad.py b/selfdrive/pandad/pandad.py index d21e60f838..c9ddfd6338 100755 --- a/selfdrive/pandad/pandad.py +++ b/selfdrive/pandad/pandad.py @@ -12,6 +12,8 @@ from openpilot.common.params import Params from openpilot.system.hardware import HARDWARE from openpilot.common.swaglog import cloudlog +from openpilot.sunnypilot.selfdrive.pandad.rivian_long_flasher import flash_rivian_long + def get_expected_signature() -> bytes: try: @@ -129,6 +131,9 @@ def main() -> None: for serial in panda_serials: pandas.append(flash_panda(serial)) + # flash Rivian longitudinal upgrade panda + flash_rivian_long(pandas) + # Ensure internal panda is present if expected internal_pandas = [panda for panda in pandas if panda.is_internal()] if HARDWARE.has_internal_panda() and len(internal_pandas) == 0: diff --git a/sunnypilot/selfdrive/pandad/__init__.py b/sunnypilot/selfdrive/pandad/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sunnypilot/selfdrive/pandad/rivian_long_flasher.py b/sunnypilot/selfdrive/pandad/rivian_long_flasher.py new file mode 100755 index 0000000000..58ff0683be --- /dev/null +++ b/sunnypilot/selfdrive/pandad/rivian_long_flasher.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +""" +Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + +This file is part of sunnypilot and is licensed under the MIT License. +See the LICENSE.md file in the root directory for more details. +""" +import os +from itertools import accumulate + +from cereal import car, messaging +from panda import Panda +from openpilot.common.params import Params +from openpilot.common.swaglog import cloudlog + +FW_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "rivian_long_fw.bin.signed") +SECTOR_SIZES = [0x4000] * 4 + [0x10000] + [0x20000] * 11 + + +def _is_rivian() -> bool: + params = Params() + + # check fixed fingerprint + if bundle := params.get("CarPlatformBundle"): + if bundle.get("brand") == "rivian": + return True + + # check cached fingerprint + CP_bytes = params.get("CarParamsPersistent") + if CP_bytes is not None: + CP = messaging.log_from_bytes(CP_bytes, car.CarParams) + if CP.brand == "rivian": + return True + + return False + + +def _flash_static(handle, code): + assert Panda.flasher_present(handle) + last_sector = next((i + 1 for i, v in enumerate(accumulate(SECTOR_SIZES[1:])) if v > len(code)), -1) + assert 1 <= last_sector < 7, "Invalid firmware size" + + handle.controlWrite(Panda.REQUEST_IN, 0xb1, 0, 0, b'') + for i in range(1, last_sector + 1): + handle.controlWrite(Panda.REQUEST_IN, 0xb2, i, 0, b'') + for i in range(0, len(code), 0x10): + handle.bulkWrite(2, code[i:i + 0x10]) + try: + handle.controlWrite(Panda.REQUEST_IN, 0xd8, 0, 0, b'', expect_disconnect=True) + except Exception: + pass + + +def _flash_panda(panda: Panda) -> None: + expected_sig = Panda.get_signature_from_firmware(FW_PATH) + if not panda.bootstub and panda.get_signature() == expected_sig: + cloudlog.info(f"F4 panda {panda.get_usb_serial()} already up to date") + return + + cloudlog.info(f"Flashing F4 panda {panda.get_usb_serial()}") + with open(FW_PATH, "rb") as f: + code = f.read() + + if not panda.bootstub: + # enter bootstub directly, panda.reset() rejects deprecated hw types + try: + panda._handle.controlWrite(Panda.REQUEST_IN, 0xd1, 1, 0, b'', timeout=15000, expect_disconnect=True) + except Exception: + pass + panda.close() + panda.reconnect() + + _flash_static(panda._handle, code) + panda.reconnect() + + +def flash_rivian_long(pandas: list[Panda]) -> None: + if not os.path.isfile(FW_PATH): + cloudlog.error(f"Rivian longitudinal upgrade firmware not found at {FW_PATH}") + return + + if not _is_rivian(): + cloudlog.info("Not a Rivian, skipping longitudinal upgrade...") + return + + for panda in pandas: + # only flash external black pandas (HW_TYPE_BLACK = 0x03) + if panda.get_type() == b'\x03' and not panda.is_internal(): + try: + _flash_panda(panda) + except Exception: + cloudlog.exception(f"Failed to flash F4 panda {panda.get_usb_serial()}") + + +if __name__ == '__main__': + flash_rivian_long([Panda(s) for s in Panda.list()]) diff --git a/sunnypilot/selfdrive/pandad/rivian_long_fw.bin.signed b/sunnypilot/selfdrive/pandad/rivian_long_fw.bin.signed new file mode 100644 index 0000000000000000000000000000000000000000..bdbd237ba99589813bdda2199ad71f42bfc2937a GIT binary patch literal 59500 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 Date: Wed, 25 Feb 2026 02:51:39 -0500 Subject: [PATCH 4/5] bump --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index a95e060e85..7b9eb4b247 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit a95e060e855dc59898dd3bf2274a598dacdd2571 +Subproject commit 7b9eb4b247e09e4aa40b78394e45d92f5b42a569 From f43dc93bd91ef16fb374954b4c106d2aa1bb9cc8 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Wed, 25 Feb 2026 03:34:05 -0500 Subject: [PATCH 5/5] Revert "bump" This reverts commit 06a5a380df70244a2a7c82218ca7748095aa9d6d. --- panda | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/panda b/panda index 7b9eb4b247..a95e060e85 160000 --- a/panda +++ b/panda @@ -1 +1 @@ -Subproject commit 7b9eb4b247e09e4aa40b78394e45d92f5b42a569 +Subproject commit a95e060e855dc59898dd3bf2274a598dacdd2571