From 7294fe03bec59e7f619b91feb9e3311808e5c10e Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Sun, 8 Feb 2026 13:05:05 -0600 Subject: [PATCH] TorquePedal --- panda/board/safety/safety_gm.h | 36 ++++++++++++++++++++++++++++--- panda/python/__init__.py | 1 + selfdrive/car/gm/carcontroller.py | 3 ++- selfdrive/car/gm/interface.py | 19 ++++++++++------ 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/panda/board/safety/safety_gm.h b/panda/board/safety/safety_gm.h index 4b8bc3b2d..4ff53aa3b 100644 --- a/panda/board/safety/safety_gm.h +++ b/panda/board/safety/safety_gm.h @@ -82,6 +82,7 @@ const uint16_t GM_PARAM_NO_ACC = 64; const uint16_t GM_PARAM_PEDAL_LONG = 128; // TODO: this can be inferred const uint16_t GM_PARAM_PEDAL_INTERCEPTOR = 256; const uint16_t GM_PARAM_BOLT_2017 = 512; +const uint16_t GM_PARAM_BOLT_2022_PEDAL = 1024; enum { GM_BTN_UNPRESS = 1, @@ -103,6 +104,7 @@ bool gm_pedal_long = false; bool gm_cc_long = false; bool gm_skip_relay_check = false; bool gm_force_ascm = false; +bool gm_bolt_2022_pedal = false; static void handle_gm_wheel_buttons(const CANPacket_t *to_push) { int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4; @@ -175,6 +177,11 @@ static void gm_rx_hook(const CANPacket_t *to_push) { } } + // Cruise check for ACC models with pedal interceptor - block stock ACC + if ((addr == 0x1C4) && gm_has_acc && enable_gas_interceptor && gm_bolt_2022_pedal) { + cruise_engaged_prev = false; + } + // Cruise check for CC only cars if ((addr == 0x3D1) && !gm_has_acc) { bool cruise_engaged = (GET_BYTE(to_push, 4) >> 7) != 0U; @@ -205,6 +212,12 @@ static void gm_rx_hook(const CANPacket_t *to_push) { } generic_rx_checks(stock_ecu_detected); } + + // Cruise check for Gen2 Bolt (ASCMActiveCruiseControlStatus on bus 2) + if (gm_bolt_2022_pedal && (GET_ADDR(to_push) == 0x370) && (GET_BUS(to_push) == 2U)) { + bool cruise_engaged = (GET_BYTE(to_push, 2) >> 7) != 0U; // ACCCmdActive + cruise_engaged_prev = cruise_engaged; + } } static bool gm_tx_hook(const CANPacket_t *to_send) { @@ -259,6 +272,9 @@ static bool gm_tx_hook(const CANPacket_t *to_send) { int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U; bool allowed_btn = (button == GM_BTN_CANCEL) && cruise_engaged_prev; + if (gm_hw == GM_CAM && enable_gas_interceptor && gm_bolt_2022_pedal && button == GM_BTN_CANCEL) { + allowed_btn = true; + } // For standard CC, allow spamming of SET / RESUME if (gm_cc_long) { allowed_btn |= cruise_engaged_prev && (button == GM_BTN_SET || button == GM_BTN_RESUME || button == GM_BTN_UNPRESS); @@ -301,10 +317,20 @@ static int gm_fwd_hook(int bus_num, int addr) { } if (bus_num == 2) { - // block lkas message and acc messages if gm_cam_long, forward all others bool is_lkas_msg = (addr == 0x180); - bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370); - bool block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long) || (addr == 0x184); + bool block_msg = false; + if (gm_bolt_2022_pedal) { + // Block 0x370 only for experimental long without pedal interceptor + bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB); + if (gm_cam_long && !enable_gas_interceptor) { + is_acc_msg = is_acc_msg || (addr == 0x370); + } + block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long); + } else { + // block lkas message and acc messages if gm_cam_long, forward all others + bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370); + block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long) || (addr == 0x184); + } if (!block_msg) { bus_fwd = 0; } @@ -336,6 +362,10 @@ static safety_config gm_init(uint16_t param) { gm_pedal_long = GET_FLAG(param, GM_PARAM_PEDAL_LONG); gm_cc_long = GET_FLAG(param, GM_PARAM_CC_LONG); gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG) && !gm_cc_long; + gm_bolt_2022_pedal = GET_FLAG(param, GM_PARAM_BOLT_2022_PEDAL); + if (gm_hw == GM_CAM && enable_gas_interceptor && gm_bolt_2022_pedal) { + gm_cam_long = true; + } gm_pcm_cruise = ((gm_hw == GM_CAM) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long) || (gm_hw == GM_SDGM); gm_skip_relay_check = GET_FLAG(param, GM_PARAM_NO_CAMERA); gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC); diff --git a/panda/python/__init__.py b/panda/python/__init__.py index 70ad290a5..a5d346564 100644 --- a/panda/python/__init__.py +++ b/panda/python/__init__.py @@ -239,6 +239,7 @@ class Panda: FLAG_GM_PEDAL_LONG = 128 # TODO: This can be inferred FLAG_GM_GAS_INTERCEPTOR = 256 FLAG_GM_BOLT_2017 = 512 + FLAG_GM_BOLT_2022_PEDAL = 1024 FLAG_FORD_LONG_CONTROL = 1 FLAG_FORD_CANFD = 2 diff --git a/selfdrive/car/gm/carcontroller.py b/selfdrive/car/gm/carcontroller.py index 28ae12c80..4097734eb 100644 --- a/selfdrive/car/gm/carcontroller.py +++ b/selfdrive/car/gm/carcontroller.py @@ -465,7 +465,8 @@ class CarController(CarControllerBase): if self.CP.carFingerprint in SDGM_CAR: can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, CS.buttons_counter, CruiseButtons.CANCEL)) else: - can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, CS.buttons_counter, CruiseButtons.CANCEL)) + cancel_bus = CanBus.POWERTRAIN if (self.CP.enableGasInterceptor and self.CP.carFingerprint == CAR.CHEVROLET_BOLT_CC_2022_2023) else CanBus.CAMERA + can_sends.append(gmcan.create_buttons(self.packer_pt, cancel_bus, CS.buttons_counter, CruiseButtons.CANCEL)) if self.CP.networkLocation == NetworkLocation.fwdCamera: # Silence "Take Steering" alert sent by camera, forward PSCMStatus with HandsOffSWlDetectionStatus=1 diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index cc42f7a79..765e34ae6 100644 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -141,6 +141,10 @@ class CarInterface(CarInterfaceBase): ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR # When a pedal interceptor is present, always use normal longitudinal (block stock cruise) experimental_long = False + if candidate == CAR.CHEVROLET_BOLT_ACC_2022_2023: + # Hard-block pedal interceptor for ACC fingerprinted Bolts + ret.enableGasInterceptor = False + ret.safetyConfigs[0].safetyParam &= ~Panda.FLAG_GM_GAS_INTERCEPTOR if candidate in EV_CAR: ret.transmissionType = TransmissionType.direct @@ -149,13 +153,13 @@ class CarInterface(CarInterfaceBase): ret.longitudinalTuning.kiBP = [5., 35., 60.] + is_bolt_2022_2023_pedal = candidate == CAR.CHEVROLET_BOLT_CC_2022_2023 and ret.enableGasInterceptor + if candidate in CAMERA_ACC_CAR: - # For ACC models with pedal interceptor, behave like CC_ONLY_CAR - ret.experimentalLongitudinalAvailable = (candidate not in CC_ONLY_CAR) and not ret.enableGasInterceptor + ret.experimentalLongitudinalAvailable = candidate not in CC_ONLY_CAR ret.networkLocation = NetworkLocation.fwdCamera ret.radarUnavailable = True # no radar - # Only use pcmCruise if no pedal interceptor (bolt_cc style behavior) - ret.pcmCruise = not ret.enableGasInterceptor + ret.pcmCruise = True ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM # Use default minEnableSpeed for ACC models (will be overridden by pedal interceptor section if present) ret.minEnableSpeed = 5 * CV.KPH_TO_MS @@ -175,6 +179,9 @@ class CarInterface(CarInterfaceBase): ret.pcmCruise = False ret.openpilotLongitudinalControl = True ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG + if is_bolt_2022_2023_pedal: + ret.experimentalLongitudinalAvailable = False + ret.pcmCruise = False elif candidate in SDGM_CAR: ret.longitudinalTuning.kiV = [0., 0., 0.] # TODO: tuning @@ -264,10 +271,10 @@ class CarInterface(CarInterfaceBase): ret.flags |= GMFlags.PEDAL_LONG.value # Enable pedal interceptor for ACC models when detected - if candidate in CAMERA_ACC_CAR and ret.enableGasInterceptor: - # ACC models with pedal interceptor get full pedal longitudinal control + if is_bolt_2022_2023_pedal: ret.flags |= GMFlags.PEDAL_LONG.value ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_ACC + ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_BOLT_2022_PEDAL if candidate == CAR.CHEVROLET_SILVERADO: # On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop