From 39afadb95f23f5f1b66cb639996813806154a683 Mon Sep 17 00:00:00 2001 From: whoisdomi Date: Mon, 20 Apr 2026 15:09:46 -0500 Subject: [PATCH] IONIQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UI Build LKAS engage/disengage sound It will now make the same sound as op engage/disengage Wheel Control fix 2 build Feature: Model Name on Developer Sidebar Model Name port and correct DEVELOPER_SIDEBAR_METRICS slot numbering Wheel Controls button fixes Mode/Star buttons were not showing on desk LKAS option was showing even when car was using it for AOL, not it hides it if being used for AOL MapGears Sync Accel/Decel on UI MapGears will sync with "Longitudinal Tuning" UI to show chosen accel/decel profile to match with chosen drive mode. Drive mode -> accel/decel profile mapping. Eco → Eco Normal → Standard Sport → Sport+/Sport Writes on gear state change, gated on MapAcceleration/MapDeceleration toggles independently ECU Disable and Auto long/exp check ECU Disable and Auto long/exp check compile Star and Mode buttons to wheel control options cereal/custom.capnp — Added modePressed @16 and customPressed @17 fields to StarPilotCarState opendbc_repo/opendbc/car/hyundai/carstate.py — Added STEERING_WHEEL_MEDIA_BUTTONS (50Hz) to the CANFD CAN parser, initialized mode_button/custom_button state, and set fp_ret.modePressed/fp_ret.customPressed in update_canfd() common/params_keys.h — Added 6 new params: ModeButtonControl, LongModeButtonControl, VeryLongModeButtonControl, StarButtonControl, LongStarButtonControl, VeryLongStarButtonControl starpilot/common/starpilot_variables.py — Added full short/long/very-long toggle processing for both Mode and Star buttons (gated on HyundaiFlags.CANFD), with has_canfd_media_buttons flag propagated to toggles starpilot/controls/starpilot_card.py — Added Mode and Star button press counters and short/long/very-long press event handling, mirroring the existing distance button logic selfdrive/ui/layouts/settings/starpilot/wheel.py — Added 6 new tiles for Mode/Star buttons (short, long, very long each), visible only when cs.isHKGCanFd is true MapGears for HKG Add HKG "Drive Modes" button ability to map to eco/normal/sport accel profiles. Dashboard speed limit reading for CANFD Add FR_CMR_02_100ms to CAN parsers in get_can_parsers_canfd: on ECAN (freq=10) for LKA_STEERING cars, on CAM (freq=0, optional) for all others calculate_canfd_speed_limit and fp_ret.dashboardSpeedLimit assignment were already present CAM bus uses freq=0 to avoid breaking canValid on non-LKA cars that don't have this message CANFD steering limits Raises STEER_MAX to 409 Speed-dependent deltas (DELTA_UP=10/DOWN=8 below 15 m/s, UP=2/DOWN=3 above), Update panda safety ceiling and tests to match. Removes TacoTuneHacks toggle dependency. Ioniq 6 toml values update Updated toml values closer to learned values DBC Update HKG Signals Added 5 new messages to hyundai_canfd.dbc and hyundai_canfd_generated.dbc: DRIVE_MODE_EV (0x205): EV drive mode state with Normal/Eco/Sport (button) values. Can be used to change acceleration profiles in openpilot based on drive mode. CAM_0x361 (0x361): Camera sign recognition with SIGN_TYPE and SIGN_TYPE_2 signals. Dashboard Speed Limit. Can be used as source for SLC. ADAS_0x380 (0x380): ADAS stop sign detection bit. Dashboard stop sign alert. Triggers 80-90ft before stop sign typically and can be used to help stopping for stop signs. DOOR_LOCK (0x414): Not actual command to lock/unlock, but may be used to detect lock state changes. STEERING_WHEEL_MEDIA_BUTTONS (0x448): Steering wheel button inputs (voice, phone, mode, next/prev, menu, scroll, custom) Can be used to assign custom functions to steering wheel buttons in openpilot. Targets Ioniq 6 but may apply to other Hyundai CAN FD vehicles. --- README.md | 2 + cereal/custom.capnp | 5 + common/params_keys.h | 7 ++ opendbc_repo/opendbc/car/disable_ecu.py | 76 ++++++++++++--- .../opendbc/car/hyundai/carcontroller.py | 29 ++++-- opendbc_repo/opendbc/car/hyundai/carstate.py | 24 ++++- opendbc_repo/opendbc/car/hyundai/interface.py | 94 +++++++++++++++++-- opendbc_repo/opendbc/car/hyundai/values.py | 22 +++-- .../opendbc/car/torque_data/override.toml | 2 +- .../dbc/generator/hyundai/hyundai_canfd.dbc | 30 ++++++ .../opendbc/dbc/hyundai_canfd_generated.dbc | 30 ++++++ .../opendbc/safety/modes/hyundai_canfd.h | 8 +- .../safety/tests/test_hyundai_canfd.py | 8 +- selfdrive/car/card.py | 13 +++ selfdrive/controls/controlsd.py | 5 +- selfdrive/selfdrived/events.py | 8 ++ .../ui/layouts/settings/starpilot/lateral.py | 2 +- .../ui/layouts/settings/starpilot/wheel.py | 52 ++++++++++ selfdrive/ui/lib/starpilot_state.py | 7 +- selfdrive/ui/qt/offroad/developer_panel.cc | 1 + starpilot/common/starpilot_variables.py | 70 +++++++++++++- .../controls/lib/starpilot_acceleration.py | 30 +++++- starpilot/controls/lib/starpilot_events.py | 12 ++- starpilot/controls/starpilot_card.py | 34 +++++++ .../ui/qt/offroad/longitudinal_settings.cc | 2 +- starpilot/ui/qt/offroad/starpilot_settings.cc | 3 + starpilot/ui/qt/offroad/starpilot_settings.h | 1 + starpilot/ui/qt/offroad/wheel_settings.cc | 18 +++- starpilot/ui/qt/widgets/developer_sidebar.cc | 14 ++- starpilot/ui/qt/widgets/developer_sidebar.h | 1 + 30 files changed, 548 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index d611694b1d..6ed6c362b9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ **StarPilot** is a custom fork of [comma.ai's openpilot](https://comma.ai/openpilot), an open source driver assistance system. +whoisdomi + Openpilot provides * Automated Lane Centering * Adaptive Cruise Control diff --git a/cereal/custom.capnp b/cereal/custom.capnp index c5a3bd5c08..957b840f01 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -82,6 +82,9 @@ struct StarPilotCarState @0xf35cc4560bbf6ec2 { sportGear @13 :Bool; trafficModeEnabled @14 :Bool; gasStack @15 :Bool; # Compatibility with older StarPilot payloads + modePressed @16 :Bool; + customPressed @17 :Bool; + alwaysOnLateralAllowed @18 :Bool; } struct StarPilotDeviceState @0xda96579883444c35 { @@ -147,6 +150,8 @@ struct StarPilotOnroadEvent @0xa5cd762cd951a455 { youveGotMail @28; switchbackModeActive @29; switchbackModeInactive @30; + lkasEnable @31; + lkasDisable @32; } } diff --git a/common/params_keys.h b/common/params_keys.h index bafd4f58ef..abe575f85d 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -38,6 +38,7 @@ inline static std::unordered_map keys = { {"DoShutdown", {CLEAR_ON_MANAGER_START, BOOL}}, {"DoUninstall", {CLEAR_ON_MANAGER_START, BOOL}}, {"DriverTooDistracted", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, BOOL}}, + {"EcuDisableFailed", {CLEAR_ON_MANAGER_START, BOOL}}, {"AlphaLongitudinalEnabled", {PERSISTENT, BOOL}}, {"ExperimentalLongitudinalEnabled", {PERSISTENT, BOOL}}, {"ExperimentalMode", {PERSISTENT, BOOL}}, @@ -329,6 +330,8 @@ inline static std::unordered_map keys = { {"LockDoors", {PERSISTENT, BOOL, "1", "0", 0}}, {"LockDoorsTimer", {PERSISTENT, INT, "0", "0", 0}}, {"LongDistanceButtonControl", {PERSISTENT, INT, "5", "0", 2}}, + {"LongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}}, + {"LongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}}, {"LongitudinalActuatorDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 3}}, {"LongitudinalActuatorDelayStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}}, {"LateralManeuverStatus", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}}, @@ -358,6 +361,7 @@ inline static std::unordered_map keys = { {"MaxDesiredAcceleration", {PERSISTENT, FLOAT, "4.0", "2.0", 2}}, {"MinimumBackupSize", {PERSISTENT, INT, "0", "0"}}, {"MinimumLaneChangeSpeed", {PERSISTENT, FLOAT, "20.0", "20.0", 2}}, + {"ModeButtonControl", {PERSISTENT, INT, "0", "0", 2}}, {"ModelDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "", ""}}, {"ModelDrivesAndScores", {PERSISTENT, JSON, "{}", "{}"}}, {"ModelReleasedDates", {PERSISTENT, STRING, "", "", 1}}, @@ -517,6 +521,7 @@ inline static std::unordered_map keys = { {"StopDistance", {PERSISTENT, FLOAT, "6.0", "6.0", 2}}, {"StoppingDecelRate", {PERSISTENT, FLOAT, "0.0", "0.0", 3}}, {"StoppingDecelRateStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}}, + {"StarButtonControl", {PERSISTENT, INT, "0", "0", 2}}, {"SwitchbackModeCooldown", {PERSISTENT, INT, "5", "0", 2}}, {"SwitchbackModeEnabled", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}}, {"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2}}, @@ -555,6 +560,8 @@ inline static std::unordered_map keys = { {"VEgoStopping", {PERSISTENT, FLOAT, "0.0", "0.0", 3}}, {"VEgoStoppingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}}, {"VeryLongDistanceButtonControl", {PERSISTENT, INT, "6", "0", 2}}, + {"VeryLongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}}, + {"VeryLongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}}, {"VoltSNG", {PERSISTENT, BOOL, "0", "0", 2}}, {"WarningImmediateVolume", {PERSISTENT, INT, "101", "101", 2}}, {"WarningSoftVolume", {PERSISTENT, INT, "101", "101", 2}}, diff --git a/opendbc_repo/opendbc/car/disable_ecu.py b/opendbc_repo/opendbc/car/disable_ecu.py index 797b40649f..90e441d859 100644 --- a/opendbc_repo/opendbc/car/disable_ecu.py +++ b/opendbc_repo/opendbc/car/disable_ecu.py @@ -1,10 +1,24 @@ +import time +import datetime from opendbc.car.carlog import carlog from opendbc.car.isotp_parallel_query import IsoTpParallelQuery EXT_DIAG_REQUEST = b'\x10\x03' EXT_DIAG_RESPONSE = b'\x50\x03' -COM_CONT_RESPONSE = b'' +# File-based logging for debugging +ECU_LOG_FILE = "/data/ecu_disable.log" + +def ecu_log(msg): + """Write to both carlog and a dedicated log file for debugging.""" + timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] + log_line = f"[{timestamp}] {msg}" + carlog.warning(msg) + try: + with open(ECU_LOG_FILE, "a") as f: + f.write(log_line + "\n") + except Exception: + pass def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10): @@ -13,24 +27,64 @@ def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_r This is used to disable the radar in some cars. Openpilot will emulate the radar. WARNING: THIS DISABLES AEB!""" - carlog.warning(f"ecu disable {hex(addr), sub_addr} ...") + ecu_log(f"=== ECU DISABLE START === addr={hex(addr)}, bus={bus}") + # Try multiple times with different approaches for i in range(retry): try: + # Enter extended diagnostic session + ecu_log(f"attempt {i+1}/{retry}: diag session...") query = IsoTpParallelQuery(can_send, can_recv, bus, [(addr, sub_addr)], [EXT_DIAG_REQUEST], [EXT_DIAG_RESPONSE]) for _, _ in query.get_data(timeout).items(): - carlog.warning("communication control disable tx/rx ...") + ecu_log("diag session OK") - query = IsoTpParallelQuery(can_send, can_recv, bus, [(addr, sub_addr)], [com_cont_req], [COM_CONT_RESPONSE]) - query.get_data(0) + # Small delay to let ECU fully enter diagnostic mode + time.sleep(0.05) - carlog.warning("ecu disabled") - return True + # Send CC command and log the response + ecu_log("sending CC...") + cc_query = IsoTpParallelQuery(can_send, can_recv, bus, [(addr, sub_addr)], [com_cont_req], [b'']) + cc_response = cc_query.get_data(timeout) - except Exception: - carlog.exception("ecu disable exception") + # Log what we got back + cc_success = False + cc_rejected = False + for (rx_addr, _), data in cc_response.items(): + ecu_log(f"CC response: {data.hex() if data else 'empty'}") + # Check for positive response (0x68 = 0x28 + 0x40) + if len(data) >= 1 and data[0] == 0x68: + ecu_log("=== ECU DISABLE CONFIRMED ===") + cc_success = True + # Check for negative response + elif len(data) >= 3 and data[0] == 0x7F: + nrc = data[2] + nrc_meanings = { + 0x12: "subFunctionNotSupported", + 0x13: "incorrectMessageLengthOrInvalidFormat", + 0x22: "conditionsNotCorrect (car must be in IGN-ON, not READY)", + 0x31: "requestOutOfRange", + 0x33: "securityAccessDenied", + } + nrc_name = nrc_meanings.get(nrc, "unknown") + ecu_log(f"CC rejected: NRC 0x{nrc:02x} = {nrc_name}") + cc_rejected = True - carlog.error(f"ecu disable retry ({i + 1}) ...") - carlog.error("ecu disable failed") + if cc_success: + return True + elif cc_rejected: + # ECU explicitly rejected - don't retry, it won't work + ecu_log("=== ECU DISABLE REJECTED ===") + return False + else: + # No response - consider it sent (ECU might have stopped responding) + ecu_log("=== ECU DISABLE SENT (no response) ===") + return True + + except Exception as e: + ecu_log(f"attempt {i+1} exception: {e}") + + time.sleep(0.1) + + ecu_log("=== ECU DISABLE FAILED ===") return False diff --git a/opendbc_repo/opendbc/car/hyundai/carcontroller.py b/opendbc_repo/opendbc/car/hyundai/carcontroller.py index 3d1ed41363..5fd1261187 100644 --- a/opendbc_repo/opendbc/car/hyundai/carcontroller.py +++ b/opendbc_repo/opendbc/car/hyundai/carcontroller.py @@ -8,6 +8,7 @@ from opendbc.car.hyundai.hyundaicanfd import CanBus from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR from opendbc.car.interfaces import CarControllerBase from opendbc.car.vehicle_model import VehicleModel +from openpilot.common.params import Params VisualAlert = structs.CarControl.HUDControl.VisualAlert LongCtrlState = structs.CarControl.Actuators.LongControlState @@ -57,11 +58,16 @@ class CarController(CarControllerBase): self.apply_angle_last = 0.0 self.car_fingerprint = CP.carFingerprint self.last_button_frame = 0 + self.ecu_disable_failed = False + self._ecu_disable_checked = False + self._params = Params() + self.long_active_ecu = self.CP.openpilotLongitudinalControl def update(self, CC, CS, now_nanos, starpilot_toggles): actuators = CC.actuators hud_control = CC.hudControl + self.params = CarControllerParams(self.CP, CS.out.vEgoRaw) apply_angle = CS.out.steeringAngleDeg if self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING: @@ -107,10 +113,19 @@ class CarController(CarControllerBase): can_sends = [] + # Check EcuDisableFailed once after init() has run + if not self._ecu_disable_checked and self.frame > 0: + self.ecu_disable_failed = self._params.get_bool("EcuDisableFailed") + self._ecu_disable_checked = True + + # When ECU disable was skipped (car started in READY mode), don't send any + # longitudinal messages - stock ECU is still active and these would conflict + self.long_active_ecu = self.CP.openpilotLongitudinalControl and not self.ecu_disable_failed + # *** common hyundai stuff *** # tester present - w/ no response (keeps relevant ECU disabled) - if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC) and self.CP.openpilotLongitudinalControl: + if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC) and self.long_active_ecu: # for longitudinal control, either radar or ADAS driving ECU addr, bus = 0x7d0, self.CAN.ECAN if self.CP.flags & HyundaiFlags.CANFD else 0 if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value: @@ -155,7 +170,7 @@ class CarController(CarControllerBase): left_lane_warning, right_lane_warning)) # Button messages - if not self.CP.openpilotLongitudinalControl: + if not self.long_active_ecu: if CC.cruiseControl.cancel: can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP)) elif CC.cruiseControl.resume: @@ -166,7 +181,7 @@ class CarController(CarControllerBase): if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15: self.last_button_frame = self.frame - if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl: + if self.frame % 2 == 0 and self.long_active_ecu: # TODO: unclear if this is needed jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0 use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value @@ -179,11 +194,11 @@ class CarController(CarControllerBase): can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled)) # 5 Hz ACC options - if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl: + if self.frame % 20 == 0 and self.long_active_ecu: can_sends.extend(hyundaican.create_acc_opt(self.packer, self.CP)) # 2 Hz front radar options - if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl: + if self.frame % 50 == 0 and self.long_active_ecu: can_sends.append(hyundaican.create_frt_radar_opt(self.packer)) return can_sends @@ -192,7 +207,7 @@ class CarController(CarControllerBase): can_sends = [] lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING - lka_steering_long = lka_steering and self.CP.openpilotLongitudinalControl + lka_steering_long = lka_steering and self.long_active_ecu # steering control can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, @@ -211,7 +226,7 @@ class CarController(CarControllerBase): if lka_steering and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS: can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, CC.leftBlinker, CC.rightBlinker)) - if self.CP.openpilotLongitudinalControl: + if self.long_active_ecu: if lka_steering: can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame)) else: diff --git a/opendbc_repo/opendbc/car/hyundai/carstate.py b/opendbc_repo/opendbc/car/hyundai/carstate.py index 1c50199c28..8b195cbed7 100644 --- a/opendbc_repo/opendbc/car/hyundai/carstate.py +++ b/opendbc_repo/opendbc/car/hyundai/carstate.py @@ -42,6 +42,8 @@ class CarState(CarStateBase): self.cruise_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) self.main_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES) self.lda_button = 0 + self.mode_button = 0 + self.custom_button = 0 self.gear_msg_canfd = "ACCELERATOR" if CP.flags & HyundaiFlags.EV else \ "GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \ @@ -287,7 +289,7 @@ class CarState(CarStateBase): # cruise state # CAN FD cars enable on main button press, set available if no TCS faults preventing engagement ret.cruiseState.available = cp.vl["TCS"]["ACCEnable"] == 0 - if self.CP.openpilotLongitudinalControl: + if self.CP.openpilotLongitudinalControl and not self.CP.pcmCruise: # These are not used for engage/disengage since openpilot keeps track of state using the buttons ret.cruiseState.enabled = cp.vl["TCS"]["ACC_REQ"] == 1 ret.cruiseState.standstill = False @@ -328,19 +330,37 @@ class CarState(CarStateBase): fp_ret = custom.StarPilotCarState.new_message() fp_ret.dashboardSpeedLimit = calculate_canfd_speed_limit(self.CP, self.FPCP, cp, cp_cam, speed_factor) + if self.CP.flags & HyundaiFlags.EV: + drive_mode = cp.vl["DRIVE_MODE_EV"]["DRIVE_MODE"] + fp_ret.ecoGear = (drive_mode == 4) + fp_ret.sportGear = (drive_mode == 5) + + self.mode_button = cp.vl["STEERING_WHEEL_MEDIA_BUTTONS"]["MODE_BUTTON"] + self.custom_button = cp.vl["STEERING_WHEEL_MEDIA_BUTTONS"]["CUSTOM_BUTTON"] + fp_ret.modePressed = bool(self.mode_button) + fp_ret.customPressed = bool(self.custom_button) + return ret, fp_ret def get_can_parsers_canfd(self, CP): msgs = [] + cam_msgs = [] if not (CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS): # TODO: this can be removed once we add dynamic support to vl_all msgs += [ # this message is 50Hz but the ECU frequently stops transmitting for ~0.5s ("CRUISE_BUTTONS", 1) ] + if CP.flags & HyundaiFlags.CANFD_LKA_STEERING: + msgs.append(("FR_CMR_02_100ms", 10)) + else: + cam_msgs.append(("FR_CMR_02_100ms", 0)) # optional: not all non-LKA CANFD cars have this on CAM bus + if CP.flags & HyundaiFlags.EV: + msgs.append(("DRIVE_MODE_EV", 10)) + msgs.append(("STEERING_WHEEL_MEDIA_BUTTONS", 50)) return { Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, CanBus(CP).ECAN), - Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).CAM), + Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_msgs, CanBus(CP).CAM), } def get_can_parsers(self, CP): diff --git a/opendbc_repo/opendbc/car/hyundai/interface.py b/opendbc_repo/opendbc/car/hyundai/interface.py index 55df57c3f5..c0bed75513 100644 --- a/opendbc_repo/opendbc/car/hyundai/interface.py +++ b/opendbc_repo/opendbc/car/hyundai/interface.py @@ -1,11 +1,13 @@ +import time from opendbc.car import Bus, get_safety_config, structs, uds from opendbc.car.hyundai.hyundaicanfd import CanBus from opendbc.car.hyundai.values import HyundaiFlags, CAR, DBC, \ CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \ + CANFD_SECURITYACCESS_CAR, \ UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR from opendbc.car.interfaces import CarInterfaceBase -from opendbc.car.disable_ecu import disable_ecu +from opendbc.car.disable_ecu import disable_ecu, ecu_log from opendbc.car.hyundai.carcontroller import CarController from opendbc.car.hyundai.carstate import CarState from opendbc.car.hyundai.radar_interface import RadarInterface @@ -16,6 +18,9 @@ Ecu = structs.CarParams.Ecu # Cancel button can sometimes be ACC pause/resume button, main button can also enable on some cars ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.cancel, ButtonType.mainCruise) +# Track when ECU disable happened - used to permanently suppress CAN errors from disabled ECU +ECU_DISABLE_TIMESTAMP = 0.0 + class CarInterface(CarInterfaceBase): CarState = CarState @@ -37,8 +42,9 @@ class CarInterface(CarInterfaceBase): if ret.flags & HyundaiFlags.CANFD: # Shared configuration for CAN-FD cars ret.alphaLongitudinalAvailable = candidate not in CANFD_UNSUPPORTED_LONGITUDINAL_CAR - if lka_steering and Ecu.adas not in [fw.ecu for fw in car_fw]: + if lka_steering and Ecu.adas not in [fw.ecu for fw in car_fw] and candidate not in CANFD_SECURITYACCESS_CAR: # this needs to be figured out for cars without an ADAS ECU + # Cars in CANFD_SECURITYACCESS_CAR are known to have ADAS ECUs that work with SecurityAccess ret.alphaLongitudinalAvailable = False ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN] @@ -59,10 +65,6 @@ class CarInterface(CarInterfaceBase): if not ret.flags & HyundaiFlags.RADAR_SCC: ret.flags |= HyundaiFlags.CANFD_CAMERA_SCC.value - # Ioniq 6 HDA2 uses the camera SCC path for stock ACC, even on LKA steering variants. - if candidate == CAR.HYUNDAI_IONIQ_6: - ret.flags |= HyundaiFlags.CANFD_CAMERA_SCC.value - # Some LKA steering cars have alternative messages for gear checks # ICE cars do not have 0x130; GEARS message on 0x40 or 0x70 instead if 0x130 not in fingerprint[CAN.ECAN]: @@ -143,6 +145,10 @@ class CarInterface(CarInterfaceBase): if ret.flags & HyundaiFlags.NON_SCC: ret.alphaLongitudinalAvailable = False ret.openpilotLongitudinalControl = alpha_long and ret.alphaLongitudinalAvailable + # When longitudinal is enabled, we disable the ADAS ECU which stops radar messages + # Force radarUnavailable to prevent CAN Error from missing radar messages + if ret.openpilotLongitudinalControl: + ret.radarUnavailable = True ret.pcmCruise = not ret.openpilotLongitudinalControl ret.startingState = True ret.vEgoStarting = 0.1 @@ -172,15 +178,50 @@ class CarInterface(CarInterfaceBase): @staticmethod def init(CP, can_recv, can_send, communication_control=None): - # 0x80 silences response + global ECU_DISABLE_TIMESTAMP + from openpilot.common.params import Params + params = Params() + if communication_control is None: - communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x80 | uds.CONTROL_TYPE.DISABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL]) + if CP.carFingerprint == CAR.HYUNDAI_IONIQ_6: + # Don't use 0x80 suppress bit so we can read the ECU response. + # Use ENABLE_RX_DISABLE_TX (0x01) so the ECU can still receive from rear radars for BSM + # while blocking SCC TX. + communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, uds.CONTROL_TYPE.ENABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL]) + else: + # 0x80 silences response for other cars (original behavior) + communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x80 | uds.CONTROL_TYPE.DISABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL]) + + ecu_log(f"=== init() called: opLong={CP.openpilotLongitudinalControl}, flags=0x{CP.flags:x}, safetyParam={CP.safetyConfigs[-1].safetyParam} ===") if CP.openpilotLongitudinalControl and not (CP.flags & (HyundaiFlags.CANFD_CAMERA_SCC | HyundaiFlags.CAMERA_SCC)): addr, bus = 0x7d0, CanBus(CP).ECAN if CP.flags & HyundaiFlags.CANFD else 0 if CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value: addr, bus = 0x730, CanBus(CP).ECAN - disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=communication_control) + + # Try ECU disable. If it succeeds (IGN-ON mode), enable longitudinal. + # If it fails (READY mode returns NRC 0x22, or timeout), strip LONG safety flag + # so panda forwards stock SCC messages normally (lateral-only mode). + ecu_log(f"=== ECU DISABLE attempt: addr=0x{addr:x}, bus={bus} ===") + ecu_disabled = disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=communication_control) + + if CP.carFingerprint == CAR.HYUNDAI_IONIQ_6: + # Ioniq 6: track success/failure to auto-switch between openpilot long and stock ACC + if ecu_disabled: + ECU_DISABLE_TIMESTAMP = time.monotonic() + params.put_bool("EcuDisableFailed", False) + params.put_bool("ExperimentalMode", True) + ecu_log("=== ECU DISABLE SUCCESS - Longitudinal + Experimental ENABLED ===") + else: + params.put_bool("EcuDisableFailed", True) + CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value + ecu_log(f"=== ECU DISABLE FAILED - safetyParam stripped to {CP.safetyConfigs[-1].safetyParam}, lateral-only mode ===") + else: + # Other cars: just log, don't change safety params or params store + if ecu_disabled: + ecu_log("=== ECU DISABLE SUCCESS ===") + else: + ecu_log("=== ECU DISABLE FAILED ===") # for blinkers if CP.flags & HyundaiFlags.ENABLE_BLINKERS: @@ -190,3 +231,38 @@ class CarInterface(CarInterfaceBase): def deinit(CP, can_recv, can_send): communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x80 | uds.CONTROL_TYPE.ENABLE_RX_ENABLE_TX, uds.MESSAGE_TYPE.NORMAL]) CarInterface.init(CP, can_recv, can_send, communication_control) + + def update(self, can_packets, starpilot_toggles): + ret, fp_ret = super().update(can_packets, starpilot_toggles) + + # When ECU disable was skipped (READY mode boot) or failed, suppress CAN timeout errors. + # Keep checking param until it's True (init() sets it AFTER first update() call), + # then cache to avoid per-frame param reads. + if not getattr(self, '_ecu_disable_failed_cached', False): + from openpilot.common.params import Params + self._ecu_disable_failed_cached = Params().get_bool("EcuDisableFailed") + if self._ecu_disable_failed_cached and not ret.canValid: + ret.canValid = True + + global ECU_DISABLE_TIMESTAMP + if ECU_DISABLE_TIMESTAMP > 0 and not ret.canValid: + # Check if any parser has counter/checksum errors (real CAN issues) + has_counter_errors = False + for cp in self.can_parsers.values(): + if cp is not None: + for state in cp.message_states.values(): + if state.counter_fail >= 5: # MAX_BAD_COUNTER from parser.py + has_counter_errors = True + ecu_log(f"REAL CAN ERROR: {state.name} counter_fail={state.counter_fail}") + break + if has_counter_errors: + break + + if has_counter_errors: + # Real CAN error - don't suppress, let it through + ecu_log("ECU_DISABLE: NOT suppressing canValid - counter errors detected") + else: + # Only timeout errors (expected after ECU disable) - suppress silently + ret.canValid = True + + return ret, fp_ret diff --git a/opendbc_repo/opendbc/car/hyundai/values.py b/opendbc_repo/opendbc/car/hyundai/values.py index 12c97740d2..46dbccd5d3 100644 --- a/opendbc_repo/opendbc/car/hyundai/values.py +++ b/opendbc_repo/opendbc/car/hyundai/values.py @@ -28,7 +28,7 @@ class CarControllerParams: ANGLE_MIN_TORQUE_REDUCTION_GAIN = 0.1 ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN = 0.6 - def __init__(self, CP): + def __init__(self, CP, vEgoRaw=100.): self.STEER_DELTA_UP = 3 self.STEER_DELTA_DOWN = 7 self.STEER_DRIVER_ALLOWANCE = 50 @@ -38,12 +38,16 @@ class CarControllerParams: self.STEER_STEP = 1 # 100 Hz if CP.flags & HyundaiFlags.CANFD: - self.STEER_MAX = 270 - self.STEER_DRIVER_ALLOWANCE = 250 + self.STEER_MAX = 409 + self.STEER_DRIVER_ALLOWANCE = 100 self.STEER_DRIVER_MULTIPLIER = 2 - self.STEER_THRESHOLD = 250 - self.STEER_DELTA_UP = 2 - self.STEER_DELTA_DOWN = 3 + self.STEER_THRESHOLD = 100 + if vEgoRaw < 15.0: # below ~34 mph - more aggressive for tight turns + self.STEER_DELTA_UP = 10 + self.STEER_DELTA_DOWN = 8 + else: + self.STEER_DELTA_UP = 2 + self.STEER_DELTA_DOWN = 3 if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING: self.STEER_THRESHOLD = 175 @@ -377,7 +381,7 @@ class CAR(Platforms): HYUNDAI_IONIQ_6 = HyundaiCanFDPlatformConfig( [HyundaiCarDocs("Hyundai Ioniq 6 (with HDA II) 2023-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))], HYUNDAI_IONIQ_5.specs, - flags=HyundaiFlags.EV, + flags=HyundaiFlags.EV | HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT | HyundaiFlags.CANFD_NO_RADAR_DISABLE, ) HYUNDAI_TUCSON_4TH_GEN = HyundaiCanFDPlatformConfig( [ @@ -817,7 +821,9 @@ CAN_GEARS = { CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD) CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR -CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR +# Cars with CANFD_NO_RADAR_DISABLE that now work with SecurityAccess handshake +CANFD_SECURITYACCESS_CAR = {CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN} +CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) - CANFD_SECURITYACCESS_CAR # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR CAMERA_SCC_CAR = CAR.with_flags(HyundaiFlags.CAMERA_SCC) diff --git a/opendbc_repo/opendbc/car/torque_data/override.toml b/opendbc_repo/opendbc/car/torque_data/override.toml index 51066b4e9c..eeaf7b3224 100644 --- a/opendbc_repo/opendbc/car/torque_data/override.toml +++ b/opendbc_repo/opendbc/car/torque_data/override.toml @@ -78,7 +78,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"] "GMC_ACADIA" = [1.6, 1.6, 0.2] "LEXUS_IS_TSS2" = [2.0, 2.0, 0.1] "HYUNDAI_KONA_EV_2ND_GEN" = [2.5, 2.5, 0.1] -"HYUNDAI_IONIQ_6" = [2.5, 2.5, 0.005] +"HYUNDAI_IONIQ_6" = [3.0, 3.0, 0.09] "HYUNDAI_AZERA_6TH_GEN" = [1.8, 1.8, 0.1] "HYUNDAI_AZERA_HEV_6TH_GEN" = [1.8, 1.8, 0.1] "KIA_K8_HEV_1ST_GEN" = [2.5, 2.5, 0.1] diff --git a/opendbc_repo/opendbc/dbc/generator/hyundai/hyundai_canfd.dbc b/opendbc_repo/opendbc/dbc/generator/hyundai/hyundai_canfd.dbc index 7fc2ec6a19..3f9a4acc8e 100644 --- a/opendbc_repo/opendbc/dbc/generator/hyundai/hyundai_canfd.dbc +++ b/opendbc_repo/opendbc/dbc/generator/hyundai/hyundai_canfd.dbc @@ -514,6 +514,11 @@ BO_ 512 ADRV_0x200: 8 ADRV SG_ SET_ME_E1 : 24|8@1+ (1,0) [0|255] "" XXX SG_ SET_ME_3A : 32|8@1+ (1,0) [0|255] "" XXX +BO_ 517 DRIVE_MODE_EV: 8 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ DRIVE_MODE : 24|8@1+ (1,0) [0|255] "" XXX + BO_ 593 RADAR_0x251: 16 FRONT_RADAR SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX @@ -574,6 +579,10 @@ BO_ 837 ADRV_0x345: 8 ADRV SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX SG_ SET_ME_15 : 24|8@1+ (1,0) [0|255] "" XXX +BO_ 865 CAM_0x361: 32 CAMERA + SG_ SIGN_TYPE : 208|8@1+ (1,0) [0|255] "" XXX + SG_ SIGN_TYPE_2 : 240|8@1+ (1,0) [0|255] "" XXX + BO_ 866 CAM_0x362: 32 CAMERA SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX @@ -614,6 +623,9 @@ BO_ 874 BLINDSPOTS_FRONT_CORNER_2: 16 XXX SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX +BO_ 896 ADAS_0x380: 24 XXX + SG_ STOP_SIGN : 83|1@1+ (1,0) [0|1] "" XXX + BO_ 961 BLINKER_STALKS: 8 XXX SG_ CHECKSUM_MAYBE : 7|8@0+ (1,0) [0|255] "" XXX SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX @@ -643,6 +655,23 @@ BO_ 1043 BLINKERS: 8 XXX SG_ RIGHT_LAMP_ALT : 61|1@0+ (1,0) [0|1] "" XXX SG_ USE_ALT_LAMP : 62|1@0+ (1,0) [0|1] "" XXX +BO_ 1044 DOOR_LOCK: 8 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ DOOR_LOCK_STATE : 37|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_LOCK_ACTION : 40|1@1+ (1,0) [0|1] "" XXX + +BO_ 1096 STEERING_WHEEL_MEDIA_BUTTONS: 8 XXX + SG_ VOICE_BUTTON : 16|1@0+ (1,0) [0|1] "" XXX + SG_ PHONE_BUTTON : 18|1@0+ (1,0) [0|1] "" XXX + SG_ MODE_BUTTON : 22|1@0+ (1,0) [0|1] "" XXX + SG_ RIGHT_SCROLL_PRESS : 24|1@0+ (1,0) [0|1] "" XXX + SG_ NEXT_BUTTON : 26|1@0+ (1,0) [0|1] "" XXX + SG_ PREVIOUS_BUTTON : 28|1@0+ (1,0) [0|1] "" XXX + SG_ MENU_BUTTON : 34|1@0+ (1,0) [0|1] "" XXX + SG_ LEFT_SCROLL_PRESS : 38|1@0+ (1,0) [0|1] "" XXX + SG_ CUSTOM_BUTTON : 44|1@0+ (1,0) [0|1] "" XXX + BO_ 1144 DRIVE_MODE: 8 XXX SG_ DRIVE_MODE : 0|16@1+ (1,-61611) [0|61611] "" XXX SG_ DRIVE_MODE2 : 28|3@1+ (1,0) [1|3] "" XXX @@ -940,6 +969,7 @@ VAL_ 506 ISLA_AutoUsmSta 0 "None Auto Function (Delete Menu)" 1 "Auto Off" 2 "Au VAL_ 506 ISLA_Cntry 0 "Europe/Russia/Australia" 1 "Domestic" 2 "China" 3 "USA" 4 "Canada" 5 "Australia" 6 "Reserved" 7 "Reserved" 8 "Reserved" 9 "Reserved" 10 "Reserved" 11 "Reserved" 12 "Reserved" 13 "Reserved" 14 "Reserved" 15 "Initial Value (default)"; VAL_ 506 ISLA_AddtnlSign 0 "No Recognition (default)" 1 "School Crossing" 16 "Do Not Pass" 17 "Reserved" 18 "Reserved" 19 "Reserved" 20 "Reserved" 21 "Reserved" 22 "Reserved" 23 "Reserved" 24 "Exit" 25 "Roundabout" 26 "Right Curve" 27 "Left Curve" 28 "Winding Road" 29 "Reserved" 30 "Reserved" 31 "Reserved" 2 "Pedestrian Crossing" 3 "Bicycle Crossing" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Reserved" 8 "Stop" 9 "Yield" 10 "Stop Ahead" 11 "Yield Ahead" 12 "Road Construction Ahead" 13 "Lane Reduction" 14 "Reserved" 15 "Reserved"; VAL_ 506 ISLA_SchoolZone 0 "No School Zone" 1 "School Zone" 2 "Reserved" 3 "Reserved"; +VAL_ 517 DRIVE_MODE 2 "Normal" 4 "Eco" 5 "Sport"; VAL_ 687 HOD_Dir_Status 0 "HANDS OFF" 1 "HAND TOUCH (SOFT)" 2 "HAND TOUCH (STRONG)" 3 "HAND GRIP (SOFT)" 4 "HAND GRIP (STRONG)" 5 "RESERVED" 6 "RESERVED"; VAL_ 698 IFSref_FR_CMR_Sta 0 "None Option (Default)" 1 "Normal" 2 "Blockage Status" 3 "Error Indicator"; VAL_ 698 IFSref_VehNumVal 0 "No vehicle" 1 "Number of vehicles" 2 "Number of vehicles" 3 "Number of vehicles" 4 "Number of vehicles" 5 "Number of vehicles" 6 "Number of vehicles" 7 "Number of vehicles" 8 "Number of vehicles" 9 "Number of vehicles" 10 "Number of vehicles" 11 "Over than 10 vehicles" 12 "Reserved" 13 "Reserved" 14 "Default" 15 "Error indicator"; diff --git a/opendbc_repo/opendbc/dbc/hyundai_canfd_generated.dbc b/opendbc_repo/opendbc/dbc/hyundai_canfd_generated.dbc index b92d732c15..532a0a05ba 100644 --- a/opendbc_repo/opendbc/dbc/hyundai_canfd_generated.dbc +++ b/opendbc_repo/opendbc/dbc/hyundai_canfd_generated.dbc @@ -751,6 +751,11 @@ BO_ 512 ADRV_0x200: 8 ADRV SG_ SET_ME_E1 : 24|8@1+ (1,0) [0|255] "" XXX SG_ SET_ME_3A : 32|8@1+ (1,0) [0|255] "" XXX +BO_ 517 DRIVE_MODE_EV: 8 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ DRIVE_MODE : 24|8@1+ (1,0) [0|255] "" XXX + BO_ 593 RADAR_0x251: 16 FRONT_RADAR SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX @@ -811,6 +816,10 @@ BO_ 837 ADRV_0x345: 8 ADRV SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX SG_ SET_ME_15 : 24|8@1+ (1,0) [0|255] "" XXX +BO_ 865 CAM_0x361: 32 CAMERA + SG_ SIGN_TYPE : 208|8@1+ (1,0) [0|255] "" XXX + SG_ SIGN_TYPE_2 : 240|8@1+ (1,0) [0|255] "" XXX + BO_ 866 CAM_0x362: 32 CAMERA SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX @@ -851,6 +860,9 @@ BO_ 874 BLINDSPOTS_FRONT_CORNER_2: 16 XXX SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX +BO_ 896 ADAS_0x380: 24 XXX + SG_ STOP_SIGN : 83|1@1+ (1,0) [0|1] "" XXX + BO_ 961 BLINKER_STALKS: 8 XXX SG_ CHECKSUM_MAYBE : 7|8@0+ (1,0) [0|255] "" XXX SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX @@ -880,6 +892,23 @@ BO_ 1043 BLINKERS: 8 XXX SG_ RIGHT_LAMP_ALT : 61|1@0+ (1,0) [0|1] "" XXX SG_ USE_ALT_LAMP : 62|1@0+ (1,0) [0|1] "" XXX +BO_ 1044 DOOR_LOCK: 8 XXX + SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX + SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ DOOR_LOCK_STATE : 37|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_LOCK_ACTION : 40|1@1+ (1,0) [0|1] "" XXX + +BO_ 1096 STEERING_WHEEL_MEDIA_BUTTONS: 8 XXX + SG_ VOICE_BUTTON : 16|1@0+ (1,0) [0|1] "" XXX + SG_ PHONE_BUTTON : 18|1@0+ (1,0) [0|1] "" XXX + SG_ MODE_BUTTON : 22|1@0+ (1,0) [0|1] "" XXX + SG_ RIGHT_SCROLL_PRESS : 24|1@0+ (1,0) [0|1] "" XXX + SG_ NEXT_BUTTON : 26|1@0+ (1,0) [0|1] "" XXX + SG_ PREVIOUS_BUTTON : 28|1@0+ (1,0) [0|1] "" XXX + SG_ MENU_BUTTON : 34|1@0+ (1,0) [0|1] "" XXX + SG_ LEFT_SCROLL_PRESS : 38|1@0+ (1,0) [0|1] "" XXX + SG_ CUSTOM_BUTTON : 44|1@0+ (1,0) [0|1] "" XXX + BO_ 1144 DRIVE_MODE: 8 XXX SG_ DRIVE_MODE : 0|16@1+ (1,-61611) [0|61611] "" XXX SG_ DRIVE_MODE2 : 28|3@1+ (1,0) [1|3] "" XXX @@ -1177,6 +1206,7 @@ VAL_ 506 ISLA_AutoUsmSta 0 "None Auto Function (Delete Menu)" 1 "Auto Off" 2 "Au VAL_ 506 ISLA_Cntry 0 "Europe/Russia/Australia" 1 "Domestic" 2 "China" 3 "USA" 4 "Canada" 5 "Australia" 6 "Reserved" 7 "Reserved" 8 "Reserved" 9 "Reserved" 10 "Reserved" 11 "Reserved" 12 "Reserved" 13 "Reserved" 14 "Reserved" 15 "Initial Value (default)"; VAL_ 506 ISLA_AddtnlSign 0 "No Recognition (default)" 1 "School Crossing" 16 "Do Not Pass" 17 "Reserved" 18 "Reserved" 19 "Reserved" 20 "Reserved" 21 "Reserved" 22 "Reserved" 23 "Reserved" 24 "Exit" 25 "Roundabout" 26 "Right Curve" 27 "Left Curve" 28 "Winding Road" 29 "Reserved" 30 "Reserved" 31 "Reserved" 2 "Pedestrian Crossing" 3 "Bicycle Crossing" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Reserved" 8 "Stop" 9 "Yield" 10 "Stop Ahead" 11 "Yield Ahead" 12 "Road Construction Ahead" 13 "Lane Reduction" 14 "Reserved" 15 "Reserved"; VAL_ 506 ISLA_SchoolZone 0 "No School Zone" 1 "School Zone" 2 "Reserved" 3 "Reserved"; +VAL_ 517 DRIVE_MODE 2 "Normal" 4 "Eco" 5 "Sport"; VAL_ 687 HOD_Dir_Status 0 "HANDS OFF" 1 "HAND TOUCH (SOFT)" 2 "HAND TOUCH (STRONG)" 3 "HAND GRIP (SOFT)" 4 "HAND GRIP (STRONG)" 5 "RESERVED" 6 "RESERVED"; VAL_ 698 IFSref_FR_CMR_Sta 0 "None Option (Default)" 1 "Normal" 2 "Blockage Status" 3 "Error Indicator"; VAL_ 698 IFSref_VehNumVal 0 "No vehicle" 1 "Number of vehicles" 2 "Number of vehicles" 3 "Number of vehicles" 4 "Number of vehicles" 5 "Number of vehicles" 6 "Number of vehicles" 7 "Number of vehicles" 8 "Number of vehicles" 9 "Number of vehicles" 10 "Number of vehicles" 11 "Over than 10 vehicles" 12 "Reserved" 13 "Reserved" 14 "Default" 15 "Error indicator"; diff --git a/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h b/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h index b88162b85c..a592584402 100644 --- a/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h +++ b/opendbc_repo/opendbc/safety/modes/hyundai_canfd.h @@ -146,10 +146,10 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *msg) { static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) { const TorqueSteeringLimits HYUNDAI_CANFD_STEERING_LIMITS = { - .max_torque = 270, - .max_rt_delta = 112, - .max_rate_up = 2, - .max_rate_down = 3, + .max_torque = 409, + .max_rt_delta = 375, + .max_rate_up = 10, + .max_rate_down = 10, .driver_torque_allowance = 250, .driver_torque_multiplier = 2, .type = TorqueDriverLimited, diff --git a/opendbc_repo/opendbc/safety/tests/test_hyundai_canfd.py b/opendbc_repo/opendbc/safety/tests/test_hyundai_canfd.py index f171362fe3..b3fd885b0c 100755 --- a/opendbc_repo/opendbc/safety/tests/test_hyundai_canfd.py +++ b/opendbc_repo/opendbc/safety/tests/test_hyundai_canfd.py @@ -49,11 +49,11 @@ class TestHyundaiCanfdBase(HyundaiButtonBase, common.CarSafetyTest, common.Drive STANDSTILL_THRESHOLD = 12 # 0.375 kph FWD_BLACKLISTED_ADDRS = {2: [0x50, 0x2a4]} - MAX_RATE_UP = 2 - MAX_RATE_DOWN = 3 - MAX_TORQUE_LOOKUP = [0], [270] + MAX_RATE_UP = 10 + MAX_RATE_DOWN = 10 + MAX_TORQUE_LOOKUP = [0], [409] - MAX_RT_DELTA = 112 + MAX_RT_DELTA = 375 DRIVER_TORQUE_ALLOWANCE = 250 DRIVER_TORQUE_FACTOR = 2 diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 4153b19d4b..b6f7707c9b 100644 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -291,6 +291,19 @@ class Car: # Initialize CarInterface, once controls are ready # TODO: this can make us miss at least a few cycles when doing an ECU knockout self.CI.init(self.CP, *self.can_callbacks) + # If ECU disable was skipped/failed, strip LONG safety flag from BOTH CarParams + # and StarPilotCarParams (pandad ORs both safetyParams together) + if self.CP.openpilotLongitudinalControl and self.params.get_bool("EcuDisableFailed"): + # ECU disable failed/rejected - switch to lateral-only mode with stock ACC + LONG_FLAG = 4 # HyundaiSafetyFlags.LONG + for cfg in self.CP.safetyConfigs: + cfg.safetyParam &= ~LONG_FLAG + for cfg in self.FPCP.safetyConfigs: + cfg.safetyParam &= ~LONG_FLAG + # Let stock ACC manage cruise (prevents "controls mismatch" error) + self.CP.pcmCruise = True + self.params.put("CarParams", self.CP.to_bytes()) + self.params.put("StarPilotCarParams", self.FPCP.to_bytes()) # signal pandad to switch to car safety mode self.params.put_bool_nonblocking("ControlsReady", True) diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 93b9848662..1f86416fb6 100644 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -136,7 +136,10 @@ class Controls: CS.steerFaultTemporary, CS.steerFaultPermanent, standstill, self.CP.steerAtStandstill, self.sm['starpilotPlan'].lateralCheck) - CC.longActive = CC.enabled and not any(e.overrideLongitudinal for e in self.sm['onroadEvents']) and not self.sm['starpilotCarState'].pauseLongitudinal and self.CP.openpilotLongitudinalControl + # EcuDisableFailed is set when car started in READY mode (ECU disable was rejected) + # Disable longitudinal so stock ACC works instead + ecu_disable_failed = self.params.get_bool("EcuDisableFailed") + CC.longActive = CC.enabled and not any(e.overrideLongitudinal for e in self.sm['onroadEvents']) and not self.sm['starpilotCarState'].pauseLongitudinal and self.CP.openpilotLongitudinalControl and not ecu_disable_failed actuators = CC.actuators actuators.longControlState = self.LoC.long_control_state diff --git a/selfdrive/selfdrived/events.py b/selfdrive/selfdrived/events.py index e9a54a0141..8c4a387adb 100644 --- a/selfdrive/selfdrived/events.py +++ b/selfdrive/selfdrived/events.py @@ -1232,6 +1232,14 @@ STARPILOT_EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 3.), }, + StarPilotEventName.lkasEnable: { + ET.WARNING: EngagementAlert(AudibleAlert.engage), + }, + + StarPilotEventName.lkasDisable: { + ET.PERMANENT: EngagementAlert(AudibleAlert.disengage), + }, + StarPilotEventName.turningLeft: { ET.WARNING: Alert( "Turning Left", diff --git a/selfdrive/ui/layouts/settings/starpilot/lateral.py b/selfdrive/ui/layouts/settings/starpilot/lateral.py index 0cfc61b804..5e641063f9 100644 --- a/selfdrive/ui/layouts/settings/starpilot/lateral.py +++ b/selfdrive/ui/layouts/settings/starpilot/lateral.py @@ -119,7 +119,7 @@ class StarPilotAlwaysOnLateralLayout(StarPilotPanel): super().__init__() self.CATEGORIES = [ {"title": tr_noop("Always On Lateral"), "type": "toggle", "get_state": lambda: self._params.get_bool("AlwaysOnLateral"), "set_state": lambda x: self._on_reboot_toggle("AlwaysOnLateral", x), "icon": "toggle_icons/icon_always_on_lateral.png", "color": "#597497"}, - {"title": tr_noop("Enable With LKAS"), "type": "toggle", "get_state": lambda: self._params.get_bool("AlwaysOnLateralLKAS"), "set_state": lambda x: self._params.put_bool("AlwaysOnLateralLKAS", x), "icon": "toggle_icons/icon_always_on_lateral.png", "color": "#597497", "visible": lambda: self._params.get_bool("AlwaysOnLateral")}, + {"title": tr_noop("Enable With LKAS"), "type": "toggle", "get_state": lambda: self._params.get_bool("AlwaysOnLateralLKAS"), "set_state": lambda x: self._params.put_bool("AlwaysOnLateralLKAS", x), "icon": "toggle_icons/icon_always_on_lateral.png", "color": "#597497", "visible": lambda: self._params.get_bool("AlwaysOnLateral") and starpilot_state.car_state.lkasAllowedForAOL}, {"title": tr_noop("Pause Below"), "type": "value", "get_value": lambda: f"{self._params.get_int('PauseAOLOnBrake')} mph", "on_click": lambda: self._show_speed_selector("PauseAOLOnBrake"), "icon": "toggle_icons/icon_always_on_lateral.png", "color": "#597497", "visible": lambda: self._params.get_bool("AlwaysOnLateral")}, ] self._rebuild_grid() diff --git a/selfdrive/ui/layouts/settings/starpilot/wheel.py b/selfdrive/ui/layouts/settings/starpilot/wheel.py index 81b4d96233..755d1539d5 100644 --- a/selfdrive/ui/layouts/settings/starpilot/wheel.py +++ b/selfdrive/ui/layouts/settings/starpilot/wheel.py @@ -62,6 +62,54 @@ class StarPilotWheelLayout(StarPilotPanel): "key": "LKASButtonControl", "color": "#64748B", }, + { + "title": tr_noop("Mode Button"), + "type": "value", + "get_value": lambda: self._get_action_name("ModeButtonControl"), + "on_click": lambda: self._show_action_picker("ModeButtonControl"), + "key": "ModeButtonControl", + "color": "#64748B", + }, + { + "title": tr_noop("Mode (Long Press)"), + "type": "value", + "get_value": lambda: self._get_action_name("LongModeButtonControl"), + "on_click": lambda: self._show_action_picker("LongModeButtonControl"), + "key": "LongModeButtonControl", + "color": "#64748B", + }, + { + "title": tr_noop("Mode (Very Long)"), + "type": "value", + "get_value": lambda: self._get_action_name("VeryLongModeButtonControl"), + "on_click": lambda: self._show_action_picker("VeryLongModeButtonControl"), + "key": "VeryLongModeButtonControl", + "color": "#64748B", + }, + { + "title": tr_noop("Star Button"), + "type": "value", + "get_value": lambda: self._get_action_name("StarButtonControl"), + "on_click": lambda: self._show_action_picker("StarButtonControl"), + "key": "StarButtonControl", + "color": "#64748B", + }, + { + "title": tr_noop("Star (Long Press)"), + "type": "value", + "get_value": lambda: self._get_action_name("LongStarButtonControl"), + "on_click": lambda: self._show_action_picker("LongStarButtonControl"), + "key": "LongStarButtonControl", + "color": "#64748B", + }, + { + "title": tr_noop("Star (Very Long)"), + "type": "value", + "get_value": lambda: self._get_action_name("VeryLongStarButtonControl"), + "on_click": lambda: self._show_action_picker("VeryLongStarButtonControl"), + "key": "VeryLongStarButtonControl", + "color": "#64748B", + }, ] self._rebuild_grid() @@ -131,6 +179,10 @@ class StarPilotWheelLayout(StarPilotPanel): visible = True if key == "LKASButtonControl": visible &= not cs.isSubaru + visible &= not (cs.lkasAllowedForAOL and self._params.get_bool("AlwaysOnLateral") and self._params.get_bool("AlwaysOnLateralLKAS")) + if key in ("ModeButtonControl", "LongModeButtonControl", "VeryLongModeButtonControl", + "StarButtonControl", "LongStarButtonControl", "VeryLongStarButtonControl"): + visible &= cs.hasModeStarButtons if not visible: continue tile_type = cat.get("type", "hub") diff --git a/selfdrive/ui/lib/starpilot_state.py b/selfdrive/ui/lib/starpilot_state.py index 98b6f1d87d..23640cfc49 100644 --- a/selfdrive/ui/lib/starpilot_state.py +++ b/selfdrive/ui/lib/starpilot_state.py @@ -7,6 +7,7 @@ from openpilot.system.hardware import HARDWARE, PC from openpilot.selfdrive.ui.ui_state import ui_state from cereal import car, log, custom, messaging from opendbc.car.gm.values import GMFlags +from opendbc.car.hyundai.values import HyundaiFlags @dataclass class StarPilotCarState: @@ -39,6 +40,7 @@ class StarPilotCarState: # ========== Device/Car State ========== isFrogsGoMoo: bool = False hasPCMCruise: bool = False + hasModeStarButtons: bool = False lkasAllowedForAOL: bool = False openpilotLongitudinalControlDisabled: bool = False hasAlphaLongitudinal: bool = False @@ -82,6 +84,7 @@ class StarPilotState: if not fallback_model: fallback_model = "CHEVROLET_BOLT_ACC_2022_2023" + self.car_state.hasModeStarButtons = self.car_state.isHKGCanFd self.car_state.hasPedal = starpilot_toggles.get("has_pedal", True) self.car_state.hasSASCM = starpilot_toggles.get("has_sascm", False) self.car_state.hasSDSU = starpilot_toggles.get("has_sdsu", False) @@ -157,7 +160,9 @@ class StarPilotState: self.car_state.isTSK = bool(self._safe_get(CP, "secOcRequired", False)) self.car_state.isVolt = car_fingerprint.startswith("CHEVROLET_VOLT") - self.car_state.lkasAllowedForAOL = starpilot_toggles.get("lkas_allowed_for_aol", False) + cp_flags = self._safe_get(CP, "flags", 0) + self.car_state.hasModeStarButtons = car_make == "hyundai" and bool(cp_flags & HyundaiFlags.CANFD) + self.car_state.lkasAllowedForAOL = car_make == "hyundai" and bool(cp_flags & (HyundaiFlags.CANFD | HyundaiFlags.HAS_LDA_BUTTON)) self.car_state.longitudinalActuatorDelay = float(self._safe_get(CP, "longitudinalActuatorDelay", self.car_state.longitudinalActuatorDelay)) self.car_state.startAccel = float(self._safe_get(CP, "startAccel", self.car_state.startAccel)) self.car_state.steerActuatorDelay = float(self._safe_get(CP, "steerActuatorDelay", self.car_state.steerActuatorDelay)) diff --git a/selfdrive/ui/qt/offroad/developer_panel.cc b/selfdrive/ui/qt/offroad/developer_panel.cc index db3a60708e..a9c3b68902 100644 --- a/selfdrive/ui/qt/offroad/developer_panel.cc +++ b/selfdrive/ui/qt/offroad/developer_panel.cc @@ -215,6 +215,7 @@ DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : QFrame(parent) { {14, tr("Longitudinal MPC Jerk: Acceleration")}, {15, tr("Longitudinal MPC Jerk: Danger Zone")}, {16, tr("Longitudinal MPC Jerk: Speed Control")}, + {17, tr("Model Name")}, }; ButtonControl *metricToggle = new ButtonControl(title, tr("SELECT"), desc); diff --git a/starpilot/common/starpilot_variables.py b/starpilot/common/starpilot_variables.py index 25b42f7362..5b194c6e90 100644 --- a/starpilot/common/starpilot_variables.py +++ b/starpilot/common/starpilot_variables.py @@ -142,9 +142,11 @@ DEVELOPER_SIDEBAR_METRICS = { "LATERAL_STEERING_ANGLE": 10, "LATERAL_TORQUE_USED": 11, "LONGITUDINAL_ACTUATOR_ACCELERATION": 12, - "LONGITUDINAL_MPC_JERK_ACCELERATION": 13, - "LONGITUDINAL_MPC_JERK_DANGER_ZONE": 14, - "LONGITUDINAL_MPC_JERK_SPEED_CONTROL": 15, + "LONGITUDINAL_MPC_DANGER_FACTOR": 13, + "LONGITUDINAL_MPC_JERK_ACCELERATION": 14, + "LONGITUDINAL_MPC_JERK_DANGER_ZONE": 15, + "LONGITUDINAL_MPC_JERK_SPEED_CONTROL": 16, + "MODEL_NAME": 17, } DEVICE_SHUTDOWN_TIMES = { @@ -823,6 +825,68 @@ class StarPilotVariables: toggle.switchback_mode_via_lkas = lkas_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"] toggle.traffic_mode_via_lkas = toggle.openpilot_longitudinal and lkas_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"] + has_canfd_media_buttons = toggle.car_make == "hyundai" and bool(CP.flags & HyundaiFlags.CANFD) + mode_button_control = self.get_value("ModeButtonControl", cast=float, condition=has_canfd_media_buttons) + toggle.experimental_mode_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"] + toggle.experimental_mode_via_press |= toggle.experimental_mode_via_mode + toggle.force_coast_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["FORCE_COAST"] + toggle.pause_lateral_via_mode = mode_button_control == BUTTON_FUNCTIONS["PAUSE_LATERAL"] + toggle.pause_longitudinal_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"] + toggle.personality_profile_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"] + toggle.switchback_mode_via_mode = mode_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"] + toggle.traffic_mode_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"] + + mode_button_control_long = self.get_value("LongModeButtonControl", cast=float, condition=has_canfd_media_buttons) + toggle.experimental_mode_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"] + toggle.experimental_mode_via_press |= toggle.experimental_mode_via_mode_long + toggle.force_coast_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["FORCE_COAST"] + toggle.pause_lateral_via_mode_long = mode_button_control_long == BUTTON_FUNCTIONS["PAUSE_LATERAL"] + toggle.pause_longitudinal_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"] + toggle.personality_profile_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"] + toggle.switchback_mode_via_mode_long = mode_button_control_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"] + toggle.traffic_mode_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"] + + mode_button_control_very_long = self.get_value("VeryLongModeButtonControl", cast=float, condition=has_canfd_media_buttons) + toggle.experimental_mode_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"] + toggle.experimental_mode_via_press |= toggle.experimental_mode_via_mode_very_long + toggle.force_coast_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["FORCE_COAST"] + toggle.pause_lateral_via_mode_very_long = mode_button_control_very_long == BUTTON_FUNCTIONS["PAUSE_LATERAL"] + toggle.pause_longitudinal_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"] + toggle.personality_profile_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"] + toggle.switchback_mode_via_mode_very_long = mode_button_control_very_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"] + toggle.traffic_mode_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"] + + star_button_control = self.get_value("StarButtonControl", cast=float, condition=has_canfd_media_buttons) + toggle.experimental_mode_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"] + toggle.experimental_mode_via_press |= toggle.experimental_mode_via_star + toggle.force_coast_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["FORCE_COAST"] + toggle.pause_lateral_via_star = star_button_control == BUTTON_FUNCTIONS["PAUSE_LATERAL"] + toggle.pause_longitudinal_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"] + toggle.personality_profile_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"] + toggle.switchback_mode_via_star = star_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"] + toggle.traffic_mode_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"] + + star_button_control_long = self.get_value("LongStarButtonControl", cast=float, condition=has_canfd_media_buttons) + toggle.experimental_mode_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"] + toggle.experimental_mode_via_press |= toggle.experimental_mode_via_star_long + toggle.force_coast_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["FORCE_COAST"] + toggle.pause_lateral_via_star_long = star_button_control_long == BUTTON_FUNCTIONS["PAUSE_LATERAL"] + toggle.pause_longitudinal_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"] + toggle.personality_profile_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"] + toggle.switchback_mode_via_star_long = star_button_control_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"] + toggle.traffic_mode_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"] + + star_button_control_very_long = self.get_value("VeryLongStarButtonControl", cast=float, condition=has_canfd_media_buttons) + toggle.experimental_mode_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"] + toggle.experimental_mode_via_press |= toggle.experimental_mode_via_star_very_long + toggle.force_coast_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["FORCE_COAST"] + toggle.pause_lateral_via_star_very_long = star_button_control_very_long == BUTTON_FUNCTIONS["PAUSE_LATERAL"] + toggle.pause_longitudinal_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"] + toggle.personality_profile_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"] + toggle.switchback_mode_via_star_very_long = star_button_control_very_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"] + toggle.traffic_mode_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"] + toggle.has_canfd_media_buttons = has_canfd_media_buttons + toggle.lock_doors_timer = self.get_value("LockDoorsTimer", cast=float, condition=(toggle.car_make == "toyota")) longitudinal_tuning = toggle.openpilot_longitudinal and self.get_value("LongitudinalTune") diff --git a/starpilot/controls/lib/starpilot_acceleration.py b/starpilot/controls/lib/starpilot_acceleration.py index b6e2d13c1c..4ceeda07e6 100644 --- a/starpilot/controls/lib/starpilot_acceleration.py +++ b/starpilot/controls/lib/starpilot_acceleration.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import numpy as np +from openpilot.common.params import Params from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, get_max_accel from openpilot.starpilot.common.accel_profile import ( @@ -80,10 +81,13 @@ def get_max_allowed_accel(v_ego, ev_tuning=True, truck_tuning=False): class StarPilotAcceleration: def __init__(self, StarPilotPlanner): self.starpilot_planner = StarPilotPlanner + self.params = Params() self.max_accel = 0 self.min_accel = 0 + self.last_gear_state = "init" + def update(self, v_ego, sm, starpilot_toggles): eco_gear = sm["starpilotCarState"].ecoGear sport_gear = sm["starpilotCarState"].sportGear @@ -100,10 +104,7 @@ class StarPilotAcceleration: if eco_gear: self.max_accel = get_max_accel_eco(v_ego, ev_tuning, truck_tuning) else: - if starpilot_toggles.acceleration_profile == ACCELERATION_PROFILES["SPORT"]: - self.max_accel = get_max_accel_sport(v_ego, ev_tuning, truck_tuning) - else: - self.max_accel = get_max_allowed_accel(v_ego, ev_tuning, truck_tuning) + self.max_accel = get_max_allowed_accel(v_ego, ev_tuning, truck_tuning) else: if starpilot_toggles.acceleration_profile == ACCELERATION_PROFILES["ECO"]: self.max_accel = get_max_accel_eco(v_ego, ev_tuning, truck_tuning) @@ -135,3 +136,24 @@ class StarPilotAcceleration: self.min_accel = A_CRUISE_MIN_SPORT else: self.min_accel = A_CRUISE_MIN + + # Sync AccelerationProfile and DecelerationProfile params so the UI reflects the active drive mode + # Eco → Eco, Normal → Standard, Sport → Sport+ + gear_state = "eco" if eco_gear else ("sport" if sport_gear else "normal") + if gear_state != self.last_gear_state: + self.last_gear_state = gear_state + if gear_state == "eco": + if starpilot_toggles.map_acceleration: + self.params.put_nonblocking("AccelerationProfile", ACCELERATION_PROFILES["ECO"]) + if starpilot_toggles.map_deceleration: + self.params.put_nonblocking("DecelerationProfile", DECELERATION_PROFILES["ECO"]) + elif gear_state == "sport": + if starpilot_toggles.map_acceleration: + self.params.put_nonblocking("AccelerationProfile", ACCELERATION_PROFILES["SPORT_PLUS"]) + if starpilot_toggles.map_deceleration: + self.params.put_nonblocking("DecelerationProfile", DECELERATION_PROFILES["SPORT"]) + else: + if starpilot_toggles.map_acceleration: + self.params.put_nonblocking("AccelerationProfile", ACCELERATION_PROFILES["STANDARD"]) + if starpilot_toggles.map_deceleration: + self.params.put_nonblocking("DecelerationProfile", DECELERATION_PROFILES["STANDARD"]) diff --git a/starpilot/controls/lib/starpilot_events.py b/starpilot/controls/lib/starpilot_events.py index 10e910b5eb..51b0f7eecf 100644 --- a/starpilot/controls/lib/starpilot_events.py +++ b/starpilot/controls/lib/starpilot_events.py @@ -22,7 +22,7 @@ class StarPilotEvents: self.events = Events(starpilot=True) - self.always_on_lateral_enabled_previously = False + self.always_on_lateral_allowed_previously = False self.previous_traffic_mode = False self.previous_switchback_mode = False self.random_event_playing = False @@ -57,6 +57,12 @@ class StarPilotEvents: else: self.max_acceleration = 0 + if sm["starpilotCarState"].alwaysOnLateralAllowed != self.always_on_lateral_allowed_previously: + if sm["starpilotCarState"].alwaysOnLateralAllowed: + self.events.add(StarPilotEventName.lkasEnable) + else: + self.events.add(StarPilotEventName.lkasDisable) + if self.starpilot_planner.starpilot_vcruise.forcing_stop: self.events.add(StarPilotEventName.forcingStop) @@ -173,11 +179,10 @@ class StarPilotEvents: elif event_choice == "yourFrogTriedToKillMe": self.events.add(StarPilotEventName.yourFrogTriedToKillMe) - if "youveGotMail" not in self.played_events and sm["starpilotCarState"].alwaysOnLateralEnabled and not self.always_on_lateral_enabled_previously: + if "youveGotMail" not in self.played_events and sm["starpilotCarState"].alwaysOnLateralAllowed and not self.always_on_lateral_allowed_previously: if random.random() < RANDOM_EVENTS_CHANCE / DT_MDL: self.events.add(StarPilotEventName.youveGotMail) - self.always_on_lateral_enabled_previously = sm["starpilotCarState"].alwaysOnLateralEnabled self.random_event_playing |= bool({event for event in self.events.names if RANDOM_EVENT_START <= event <= RANDOM_EVENT_END}) if self.error_log.is_file(): @@ -212,4 +217,5 @@ class StarPilotEvents: elif sm["starpilotModelV2"].turnDirection == TurnDirection.turnRight: self.events.add(StarPilotEventName.turningRight) + self.always_on_lateral_allowed_previously = sm["starpilotCarState"].alwaysOnLateralAllowed self.played_events.update(STARPILOT_EVENT_NAME[event] for event in self.events.names) diff --git a/starpilot/controls/starpilot_card.py b/starpilot/controls/starpilot_card.py index 5d6b712292..52654f1ab3 100644 --- a/starpilot/controls/starpilot_card.py +++ b/starpilot/controls/starpilot_card.py @@ -25,6 +25,10 @@ class StarPilotCard: self.decel_pressed = False self.distancePressed_previously = False self.force_coast = False + self.modePressed_previously = False + self.mode_counter = 0 + self.customPressed_previously = False + self.custom_counter = 0 self.pause_lateral = False self.pause_longitudinal = False self.switchback_mode_enabled = self.params_memory.get_bool("SwitchbackModeEnabled") @@ -120,9 +124,39 @@ class StarPilotCard: if any(be.pressed and be.type == ButtonType.lkas for be in carState.buttonEvents): self.handle_button_event("lkas", sm, starpilot_toggles) + if getattr(starpilot_toggles, "has_canfd_media_buttons", False): + if starpilotCarState.modePressed: + self.mode_counter += 1 + elif not self.modePressed_previously: + self.mode_counter = 0 + self.modePressed_previously = starpilotCarState.modePressed + + if not starpilotCarState.modePressed and 1 <= self.mode_counter < self.long_press_threshold: + self.handle_button_event("mode", sm, starpilot_toggles) + elif self.mode_counter == self.long_press_threshold: + self.handle_button_event("mode_long", sm, starpilot_toggles) + elif self.mode_counter == self.very_long_press_threshold: + self.handle_button_event("mode_long", sm, starpilot_toggles) + self.handle_button_event("mode_very_long", sm, starpilot_toggles) + + if starpilotCarState.customPressed: + self.custom_counter += 1 + elif not self.customPressed_previously: + self.custom_counter = 0 + self.customPressed_previously = starpilotCarState.customPressed + + if not starpilotCarState.customPressed and 1 <= self.custom_counter < self.long_press_threshold: + self.handle_button_event("star", sm, starpilot_toggles) + elif self.custom_counter == self.long_press_threshold: + self.handle_button_event("star_long", sm, starpilot_toggles) + elif self.custom_counter == self.very_long_press_threshold: + self.handle_button_event("star_long", sm, starpilot_toggles) + self.handle_button_event("star_very_long", sm, starpilot_toggles) + self.force_coast &= not (carState.brakePressed or carState.gasPressed) starpilotCarState.accelPressed = self.accel_pressed + starpilotCarState.alwaysOnLateralAllowed = self.always_on_lateral_allowed starpilotCarState.alwaysOnLateralEnabled = self.always_on_lateral_enabled starpilotCarState.decelPressed = self.decel_pressed starpilotCarState.distanceLongPressed = self.very_long_press_threshold > self.gap_counter >= self.long_press_threshold diff --git a/starpilot/ui/qt/offroad/longitudinal_settings.cc b/starpilot/ui/qt/offroad/longitudinal_settings.cc index 2141aea3cf..558d848c34 100644 --- a/starpilot/ui/qt/offroad/longitudinal_settings.cc +++ b/starpilot/ui/qt/offroad/longitudinal_settings.cc @@ -996,7 +996,7 @@ void StarPilotLongitudinalPanel::updateToggles() { } else if (key == "MapGears") { - setVisible &= parent->isToyota; + setVisible &= parent->isToyota || parent->isHKG; setVisible &= !parent->isTSK; } diff --git a/starpilot/ui/qt/offroad/starpilot_settings.cc b/starpilot/ui/qt/offroad/starpilot_settings.cc index 04d84dad36..4aae12fcd2 100644 --- a/starpilot/ui/qt/offroad/starpilot_settings.cc +++ b/starpilot/ui/qt/offroad/starpilot_settings.cc @@ -335,11 +335,13 @@ void StarPilotSettingsWindow::updateVariables() { std::string fallbackFingerprint = fallbackModel.toStdString(); hasPedal = starpilot_toggles.value("has_pedal").toBool(true); + hasModeStarButtons = starpilot_toggles.contains("has_canfd_media_buttons") && starpilot_toggles.value("has_canfd_media_buttons").toBool(); hasSDSU = starpilot_toggles.value("has_sdsu").toBool(); hasZSS = starpilot_toggles.value("has_zss").toBool(); isBolt = fallbackFingerprint.rfind("CHEVROLET_BOLT", 0) == 0; isGM = carMake == "gm"; isHKG = carMake == "hyundai"; + isHKGCanFd = isHKG && hasModeStarButtons; isSubaru = carMake == "subaru"; isToyota = carMake == "toyota"; isVolt = fallbackFingerprint.rfind("CHEVROLET_VOLT", 0) == 0; @@ -382,6 +384,7 @@ void StarPilotSettingsWindow::updateVariables() { isTSK = CP.getSecOcRequired(); isVolt = carFingerprint.rfind("CHEVROLET_VOLT", 0) == 0; latAccelFactor = CP.getLateralTuning().getTorque().getLatAccelFactor(); + hasModeStarButtons = starpilot_toggles.contains("has_canfd_media_buttons") ? starpilot_toggles.value("has_canfd_media_buttons").toBool() : isHKGCanFd; lkasAllowedForAOL = starpilot_toggles.value("lkas_allowed_for_aol").toBool(); longitudinalActuatorDelay = CP.getLongitudinalActuatorDelay(); startAccel = CP.getStartAccel(); diff --git a/starpilot/ui/qt/offroad/starpilot_settings.h b/starpilot/ui/qt/offroad/starpilot_settings.h index c274ebf040..3ae3a68317 100644 --- a/starpilot/ui/qt/offroad/starpilot_settings.h +++ b/starpilot/ui/qt/offroad/starpilot_settings.h @@ -31,6 +31,7 @@ public: bool hasRadar = true; bool hasSDSU = false; bool hasSNG = false; + bool hasModeStarButtons = false; bool hasZSS = false; bool isAngleCar = false; bool isBolt = false; diff --git a/starpilot/ui/qt/offroad/wheel_settings.cc b/starpilot/ui/qt/offroad/wheel_settings.cc index f0348c31e3..9dc14691cc 100644 --- a/starpilot/ui/qt/offroad/wheel_settings.cc +++ b/starpilot/ui/qt/offroad/wheel_settings.cc @@ -56,7 +56,13 @@ StarPilotWheelPanel::StarPilotWheelPanel(StarPilotSettingsWindow *parent, bool f {"DistanceButtonControl", tr("Distance Button"), tr("Action performed when the \"Distance\" button is pressed."), "../../starpilot/assets/toggle_icons/icon_mute.png"}, {"LongDistanceButtonControl", tr("Distance Button (Long Press)"), tr("Action performed when the \"Distance\" button is pressed for more than 0.5 seconds."), "../../starpilot/assets/toggle_icons/icon_mute.png"}, {"VeryLongDistanceButtonControl", tr("Distance Button (Very Long Press)"), tr("Action performed when the \"Distance\" button is pressed for more than 2.5 seconds."), "../../starpilot/assets/toggle_icons/icon_mute.png"}, - {"LKASButtonControl", tr("LKAS Button"), tr("Action performed when the \"LKAS\" button is pressed."), "../../starpilot/assets/toggle_icons/icon_mute.png"} + {"LKASButtonControl", tr("LKAS Button"), tr("Action performed when the \"LKAS\" button is pressed."), "../../starpilot/assets/toggle_icons/icon_mute.png"}, + {"ModeButtonControl", tr("Mode Button"), tr("Action performed when the \"Mode\" button is pressed."), "../../starpilot/assets/toggle_icons/icon_mute.png"}, + {"LongModeButtonControl", tr("Mode Button (Long Press)"), tr("Action performed when the \"Mode\" button is pressed for more than 0.5 seconds."), "../../starpilot/assets/toggle_icons/icon_mute.png"}, + {"VeryLongModeButtonControl", tr("Mode Button (Very Long Press)"), tr("Action performed when the \"Mode\" button is pressed for more than 2.5 seconds."), "../../starpilot/assets/toggle_icons/icon_mute.png"}, + {"StarButtonControl", tr("Star Button"), tr("Action performed when the \"Star\" button is pressed."), "../../starpilot/assets/toggle_icons/icon_mute.png"}, + {"LongStarButtonControl", tr("Star Button (Long Press)"), tr("Action performed when the \"Star\" button is pressed for more than 0.5 seconds."), "../../starpilot/assets/toggle_icons/icon_mute.png"}, + {"VeryLongStarButtonControl", tr("Star Button (Very Long Press)"), tr("Action performed when the \"Star\" button is pressed for more than 2.5 seconds."), "../../starpilot/assets/toggle_icons/icon_mute.png"} }; for (const auto &[param, title, desc, icon] : wheelToggles) { @@ -121,6 +127,16 @@ void StarPilotWheelPanel::updateToggles() { setVisible &= !parent->lkasAllowedForAOL || !(params.getBool("AlwaysOnLateral") && params.getBool("AlwaysOnLateralLKAS")); } + if (!showAllToggles && ( + key == "ModeButtonControl" || + key == "LongModeButtonControl" || + key == "VeryLongModeButtonControl" || + key == "StarButtonControl" || + key == "LongStarButtonControl" || + key == "VeryLongStarButtonControl")) { + setVisible &= parent->hasModeStarButtons; + } + if (ButtonControl *wheelToggle = qobject_cast(toggle)) { if (key == "LKASButtonControl") { const bool lkasLocked = lockLkasButtonIfNeeded(params); diff --git a/starpilot/ui/qt/widgets/developer_sidebar.cc b/starpilot/ui/qt/widgets/developer_sidebar.cc index 00bd5610e5..63e5db4b54 100644 --- a/starpilot/ui/qt/widgets/developer_sidebar.cc +++ b/starpilot/ui/qt/widgets/developer_sidebar.cc @@ -1,5 +1,7 @@ #include "starpilot/ui/qt/widgets/developer_sidebar.h" +#include + void DeveloperSidebar::drawMetric(QPainter &p, const QPair &label, QColor c, int y) { const QRect rect = {12, y, 275, 126}; @@ -17,7 +19,11 @@ void DeveloperSidebar::drawMetric(QPainter &p, const QPair &la p.setPen(QColor(0xff, 0xff, 0xff)); p.setFont(InterFont(35, QFont::DemiBold)); - p.drawText(rect.adjusted(0, 0, -22, 0), Qt::AlignCenter, label.first + "\n" + label.second); + if (label.second.isEmpty()) { + p.drawText(rect.adjusted(8, 8, -22, -8), Qt::AlignCenter | Qt::TextWordWrap, label.first); + } else { + p.drawText(rect.adjusted(8, 8, -22, -8), Qt::AlignCenter, label.first + "\n" + label.second); + } } DeveloperSidebar::DeveloperSidebar(QWidget *parent) : QFrame(parent) { @@ -144,6 +150,11 @@ void DeveloperSidebar::updateState(const UIState &s, const StarPilotUIState &fs) stiffnessFactorStatus = ItemStatus(QPair(tr("STEER STIFF"), QString::number(liveParameters.getStiffnessFactor(), 'f', 5)), metricColor); torqueStatus = ItemStatus(QPair(tr("TORQUE %"), torqueLabel), metricColor); + QString modelName = starpilot_scene.starpilot_toggles.value("model_name").toString(); + modelName.remove(QRegularExpression("\\(.*\\)")); + modelName.remove(QRegularExpression("[^a-zA-Z0-9 \\-\\.:]")); + modelNameStatus = ItemStatus(QPair(modelName.trimmed(), ""), metricColor); + update(); } @@ -171,6 +182,7 @@ void DeveloperSidebar::paintEvent(QPaintEvent *event) { metricMap.insert(14, &accelerationJerkStatus); metricMap.insert(15, &dangerJerkStatus); metricMap.insert(16, &speedJerkStatus); + metricMap.insert(17, &modelNameStatus); int count = 0; for (size_t i = 0; i < metricAssignments.size(); ++i) { diff --git a/starpilot/ui/qt/widgets/developer_sidebar.h b/starpilot/ui/qt/widgets/developer_sidebar.h index c16ab104a3..bb588ecc8e 100644 --- a/starpilot/ui/qt/widgets/developer_sidebar.h +++ b/starpilot/ui/qt/widgets/developer_sidebar.h @@ -37,6 +37,7 @@ private: ItemStatus lateralEngagementStatus; ItemStatus longitudinalEngagementStatus; ItemStatus maxAccelerationStatus; + ItemStatus modelNameStatus; ItemStatus speedJerkStatus; ItemStatus steerAngleStatus; ItemStatus steerRatioStatus;