diff --git a/common/params.cc b/common/params.cc index 4f10520b5..c7cae02ff 100644 --- a/common/params.cc +++ b/common/params.cc @@ -469,6 +469,8 @@ std::unordered_map keys = { {"RoadEdgesWidth", PERSISTENT}, {"RoadName", CLEAR_ON_MANAGER_START}, {"RoadNameUI", PERSISTENT}, + {"RedPanda", PERSISTENT}, + {"RemoteStartBootsComma", PERSISTENT}, {"RotatingWheel", PERSISTENT}, {"ScreenBrightness", PERSISTENT}, {"ScreenBrightnessOnroad", PERSISTENT}, diff --git a/frogpilot/common/frogpilot_variables.py b/frogpilot/common/frogpilot_variables.py index 61515408a..7b33464f8 100644 --- a/frogpilot/common/frogpilot_variables.py +++ b/frogpilot/common/frogpilot_variables.py @@ -237,6 +237,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [ ("FullMap", "0", 2, "0"), ("GasRegenCmd", "1", 2, "0"), ("GMPedalLongitudinal", "1", 2, "1"), + ("RedPanda", "0", 3, "0"), + ("RemoteStartBootsComma", "0", 3, "0"), ("GithubSshKeys", "", 0, ""), ("GithubUsername", "", 0, ""), ("GoatScream", "0", 1, "0"), @@ -808,6 +810,9 @@ class FrogPilotVariables: toggle.vEgoStarting = 0.15 if toggle.experimental_gm_tune else toggle.vEgoStarting toggle.vEgoStopping = 0.15 if toggle.experimental_gm_tune else toggle.vEgoStopping + toggle.red_panda = toggle.car_make == "gm" and (params.get_bool("RedPanda") if tuning_level >= level["RedPanda"] else default.get_bool("RedPanda")) + toggle.remote_start_boots_comma = toggle.car_make == "gm" and (params.get_bool("RemoteStartBootsComma") if tuning_level >= level["RemoteStartBootsComma"] else default.get_bool("RemoteStartBootsComma")) + toggle.force_fingerprint = (params.get_bool("ForceFingerprint") if tuning_level >= level["ForceFingerprint"] else default.get_bool("ForceFingerprint")) and toggle.car_model is not None toggle.frogsgomoo_tweak = toggle.openpilot_longitudinal and toggle.car_make == "toyota" and (params.get_bool("FrogsGoMoosTweak") if tuning_level >= level["FrogsGoMoosTweak"] else default.get_bool("FrogsGoMoosTweak")) diff --git a/frogpilot/ui/qt/offroad/vehicle_settings.cc b/frogpilot/ui/qt/offroad/vehicle_settings.cc index 777d0a790..3754b4344 100644 --- a/frogpilot/ui/qt/offroad/vehicle_settings.cc +++ b/frogpilot/ui/qt/offroad/vehicle_settings.cc @@ -1,6 +1,9 @@ #include #include +#include +#include + #include "frogpilot/ui/qt/offroad/vehicle_settings.h" QStringList getCarNames(const QString &carMake, QMap &carModels) { @@ -170,6 +173,8 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) {"ExperimentalGMTune", tr("FrogsGoMoo's Experimental Tune"), tr("Experimental GM tune by FrogsGoMoo that attempts to smoothen stopping and takeoff control. Use at your own risk!"), ""}, {"GMPedalLongitudinal", tr("Use Pedal for Longitudinal Control"), tr("Use the pedal interceptor for longitudinal control instead of camera ACC/Redneck when available."), ""}, {"LongPitch", tr("Smooth Pedal Response on Hills"), tr("Smoothen acceleration and braking when driving downhill/uphill."), ""}, + {"RedPanda", tr("Red Panda"), tr("Enable Red Panda behavior for GM (alternate safety config and bus numbering). Requires a reboot to take effect."), ""}, + {"RemoteStartBootsComma", tr("Remote Start Boots Comma"), tr("Use GM C9 SystemPowerMode for ignition detection. Toggle requires a panda firmware update and a reboot to take effect."), ""}, {"VoltSNG", tr("Stop-and-Go Hack"), tr("Force stop-and-go on the 2017 Chevy Volt."), ""}, {"HKGToggles", tr("Hyundai/Kia/Genesis Settings"), tr("FrogPilot features for Genesis, Hyundai, and Kia vehicles."), ""}, @@ -300,6 +305,25 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) }); } + ParamControl *remoteStartToggle = static_cast(toggles["RemoteStartBootsComma"]); + QObject::connect(remoteStartToggle, &ToggleControl::toggleFlipped, [parent, remoteStartToggle, this](bool state) { + const QString prompt = tr("Remote Start requires a Panda firmware update. Flash the Panda now?"); + if (!FrogPilotConfirmationDialog::yesorno(prompt, this)) { + params.putBool("RemoteStartBootsComma", !state); + remoteStartToggle->refresh(); + return; + } + + std::thread([parent, this]() { + parent->keepScreenOn = true; + params_memory.putBool("FlashPanda", true); + while (params_memory.getBool("FlashPanda")) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + Hardware::reboot(); + }).detach(); + }); + openDescriptions(forceOpenDescriptions, toggles); QObject::connect(uiState(), &UIState::offroadTransition, [selectMakeButton, selectModelButton, this]() { diff --git a/frogpilot/ui/qt/offroad/vehicle_settings.h b/frogpilot/ui/qt/offroad/vehicle_settings.h index f29cd896a..85f4a91ee 100644 --- a/frogpilot/ui/qt/offroad/vehicle_settings.h +++ b/frogpilot/ui/qt/offroad/vehicle_settings.h @@ -36,7 +36,7 @@ private: std::map toggles; - QSet gmKeys = {"ExperimentalGMTune", "GMPedalLongitudinal", "LongPitch", "VoltSNG"}; + QSet gmKeys = {"ExperimentalGMTune", "GMPedalLongitudinal", "LongPitch", "RedPanda", "RemoteStartBootsComma", "VoltSNG"}; QSet hkgKeys = {"NewLongAPI", "TacoTuneHacks"}; QSet longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "SNGHack", "VoltSNG"}; QSet toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"}; @@ -50,6 +50,7 @@ private: ParamControl *forceFingerprint; Params params; + Params params_memory{"/dev/shm/params"}; Params params_default{"/dev/shm/params_default"}; QJsonObject frogpilotToggleLevels; diff --git a/panda/board/drivers/can_common.h b/panda/board/drivers/can_common.h index 69e23a86d..4494c1f60 100644 --- a/panda/board/drivers/can_common.h +++ b/panda/board/drivers/can_common.h @@ -33,6 +33,7 @@ extern bool can_loopback; // Ignition detected from CAN meessages bool ignition_can = false; uint32_t ignition_can_cnt = 0U; +extern bool gm_remote_start_boots_comma; #define ALL_CAN_SILENT 0xFF #define ALL_CAN_LIVE 0 @@ -202,10 +203,18 @@ void ignition_can_hook(CANPacket_t *to_push) { int len = GET_LEN(to_push); // GM exception - if ((addr == 0xC9) && (len == 8)) { - // Matches SystemPowerMode (1=Run, 0=Off) - ignition_can = (GET_BYTE(to_push, 6) & 0x10U) != 0U; - ignition_can_cnt = 0U; + if (gm_remote_start_boots_comma) { + if ((addr == 0xC9) && (len == 8)) { + // Matches SystemPowerMode (1=Run, 0=Off) + ignition_can = (GET_BYTE(to_push, 6) & 0x10U) != 0U; + ignition_can_cnt = 0U; + } + } else { + if ((addr == 0x1F1) && (len == 8)) { + // SystemPowerMode (2=Run, 3=Crank Request) + ignition_can = (GET_BYTE(to_push, 0) & 0x2U) != 0U; + ignition_can_cnt = 0U; + } } // Tesla exception diff --git a/panda/board/safety/safety_gm.h b/panda/board/safety/safety_gm.h index e194fc807..040004f12 100644 --- a/panda/board/safety/safety_gm.h +++ b/panda/board/safety/safety_gm.h @@ -85,6 +85,7 @@ const uint16_t GM_PARAM_BOLT_2017 = 512; const uint16_t GM_PARAM_BOLT_2022_PEDAL = 1024; const uint16_t GM_PARAM_ASCM_INT = 2048; const uint16_t GM_PARAM_FORCE_BRAKE_C9 = 4096; +const uint16_t GM_PARAM_REMOTE_START_BOOTS_COMMA = 8192; enum { GM_BTN_UNPRESS = 1, @@ -108,6 +109,7 @@ bool gm_skip_relay_check = false; bool gm_force_ascm = false; bool gm_bolt_2022_pedal = false; bool gm_force_brake_c9 = false; +bool gm_remote_start_boots_comma = false; static void handle_gm_wheel_buttons(const CANPacket_t *to_push) { int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4; @@ -376,6 +378,7 @@ static safety_config gm_init(uint16_t param) { gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC); enable_gas_interceptor = GET_FLAG(param, GM_PARAM_PEDAL_INTERCEPTOR); gm_force_brake_c9 = GET_FLAG(param, GM_PARAM_FORCE_BRAKE_C9); + gm_remote_start_boots_comma = GET_FLAG(param, GM_PARAM_REMOTE_START_BOOTS_COMMA); safety_config ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_ASCM_TX_MSGS); if (gm_hw == GM_CAM) { diff --git a/panda/python/__init__.py b/panda/python/__init__.py index 89805a80d..572750748 100644 --- a/panda/python/__init__.py +++ b/panda/python/__init__.py @@ -242,6 +242,7 @@ class Panda: FLAG_GM_BOLT_2022_PEDAL = 1024 FLAG_GM_ASCM_INT = 2048 FLAG_GM_FORCE_BRAKE_C9 = 4096 + FLAG_GM_REMOTE_START_BOOTS_COMMA = 8192 FLAG_FORD_LONG_CONTROL = 1 FLAG_FORD_CANFD = 2 diff --git a/selfdrive/car/gm/interface.py b/selfdrive/car/gm/interface.py index d195a3fc9..91d3a000c 100644 --- a/selfdrive/car/gm/interface.py +++ b/selfdrive/car/gm/interface.py @@ -7,7 +7,7 @@ from panda import Panda from openpilot.common.conversions import Conversions as CV from openpilot.selfdrive.car import create_button_events, get_safety_config from openpilot.selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG -from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR, SDGM_CAR, ASCM_INT +from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR, SDGM_CAR, ASCM_INT, set_red_panda_canbus from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LateralAccelFromTorqueCallbackType, get_friction_threshold from openpilot.selfdrive.controls.lib.drive_helpers import get_friction @@ -141,7 +141,13 @@ class CarInterface(CarInterfaceBase): @staticmethod def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles): ret.carName = "gm" + red_panda = getattr(frogpilot_toggles, "red_panda", False) + set_red_panda_canbus(red_panda) + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)] + if red_panda: + ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput), ret.safetyConfigs[0]] + gm_safety_cfg = ret.safetyConfigs[-1] if red_panda else ret.safetyConfigs[0] ret.autoResumeSng = False ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN] @@ -154,13 +160,13 @@ class CarInterface(CarInterfaceBase): if PEDAL_MSG in fingerprint[0]: ret.enableGasInterceptor = True - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR + gm_safety_cfg.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 + gm_safety_cfg.safetyParam &= ~Panda.FLAG_GM_GAS_INTERCEPTOR if candidate in EV_CAR: ret.transmissionType = TransmissionType.direct @@ -180,19 +186,19 @@ class CarInterface(CarInterfaceBase): ret.minEnableSpeed = 5 * CV.KPH_TO_MS ret.minSteerSpeed = 10 * CV.KPH_TO_MS if candidate in SDGM_CAR: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_SDGM + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_SDGM # Use C9 brake bit only on SDGM variants that lack 0xBE (ECMAcceleratorPos) if ACCELERATOR_POS_MSG not in fingerprint[CanBus.POWERTRAIN]: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_FORCE_BRAKE_C9 + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_FORCE_BRAKE_C9 ret.flags |= GMFlags.FORCE_BRAKE_C9.value ret.minEnableSpeed = -1. # engage speed is decided by pcm ret.minSteerSpeed = 7 * CV.MPH_TO_MS elif candidate in ASCM_INT: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM ret.minSteerSpeed = 7 * CV.MPH_TO_MS - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_ASCM_INT + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_ASCM_INT else: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM # Tuning for experimental long ret.longitudinalTuning.kiV = [0.5, 0.5] if candidate in kaofui_cars else [0.5, 0.5, 0.5] @@ -207,11 +213,10 @@ class CarInterface(CarInterfaceBase): if ret.experimentalLongitudinalAvailable and experimental_long: ret.pcmCruise = False ret.openpilotLongitudinalControl = True - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG if is_bolt_2022_2023_pedal: ret.experimentalLongitudinalAvailable = False ret.pcmCruise = False - else: # ASCM, OBD-II harness ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long ret.networkLocation = NetworkLocation.gateway @@ -231,7 +236,10 @@ class CarInterface(CarInterfaceBase): if ret.enableGasInterceptor: # Need to set ASCM long limits when using pedal interceptor, instead of camera ACC long limits - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_ASCM_LONG + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_ASCM_LONG + + if getattr(frogpilot_toggles, "remote_start_boots_comma", False): + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_REMOTE_START_BOOTS_COMMA # Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below. ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]] @@ -288,7 +296,7 @@ class CarInterface(CarInterfaceBase): ret.lateralTuning.torque.kfDEPRECATED = 0.02 if candidate == CAR.CHEVROLET_BOLT_CC_2017: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_BOLT_2017 + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_BOLT_2017 if ret.enableGasInterceptor: # ACC Bolts use pedal for full longitudinal control, not just sng @@ -297,8 +305,8 @@ class CarInterface(CarInterfaceBase): # Enable pedal interceptor for ACC models when detected 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 + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_NO_ACC + gm_safety_cfg.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 @@ -365,7 +373,7 @@ class CarInterface(CarInterfaceBase): if ret.enableGasInterceptor and frogpilot_toggles.gm_pedal_longitudinal: ret.networkLocation = NetworkLocation.fwdCamera - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM ret.minEnableSpeed = -1 ret.pcmCruise = False ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long @@ -374,7 +382,7 @@ class CarInterface(CarInterfaceBase): if candidate in CC_ONLY_CAR or (candidate in CAMERA_ACC_CAR and ret.enableGasInterceptor): #pedal interceptor tuning ret.flags |= GMFlags.PEDAL_LONG.value - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_PEDAL_LONG + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_PEDAL_LONG # Note: Low speed, stop and go not tested. Should be fairly smooth on highway if candidate in (CAR.CHEVROLET_MALIBU_CC, CAR.CHEVROLET_MALIBU_HYBRID_CC): ret.longitudinalTuning.kiBP = [0.0, 5., 35.] @@ -390,14 +398,14 @@ class CarInterface(CarInterfaceBase): ret.longitudinalTuning.kfDEPRECATED = 0.25 ret.stoppingDecelRate = 0.8 else: # Pedal used for SNG, ACC for longitudinal control otherwise - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG ret.startingState = True ret.vEgoStopping = 0.25 ret.vEgoStarting = 0.25 if ret.enableGasInterceptor and candidate == CAR.CHEVROLET_MALIBU_HYBRID_CC: ret.flags |= GMFlags.PEDAL_LONG.value - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_PEDAL_LONG + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_PEDAL_LONG ret.longitudinalTuning.kiBP = [0.0, 5., 35.] ret.longitudinalTuning.kiV = [0.0, 0.18, 0.25] ret.longitudinalTuning.kfDEPRECATED = 0.15 @@ -408,7 +416,7 @@ class CarInterface(CarInterfaceBase): elif candidate in CC_ONLY_CAR: ret.flags |= GMFlags.CC_LONG.value - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_CC_LONG + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_CC_LONG ret.radarUnavailable = True ret.experimentalLongitudinalAvailable = False ret.minEnableSpeed = 24 * CV.MPH_TO_MS @@ -431,12 +439,12 @@ class CarInterface(CarInterfaceBase): ret.stoppingDecelRate = 11.18 # == 25 mph/s (.04 rate) if candidate in CC_ONLY_CAR: - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_ACC + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_NO_ACC # Exception for flashed cars, or cars whose camera was removed if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and CAM_MSG not in fingerprint[CanBus.CAMERA] and not candidate in (SDGM_CAR | ASCM_INT): ret.flags |= GMFlags.NO_CAMERA.value - ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_CAMERA + gm_safety_cfg.safetyParam |= Panda.FLAG_GM_NO_CAMERA if ACCELERATOR_POS_MSG not in fingerprint[CanBus.POWERTRAIN]: ret.flags |= GMFlags.NO_ACCELERATOR_POS_MSG.value diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 1f35a4ecd..be31c4d42 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -333,6 +333,22 @@ class CanBus: LOOPBACK = 128 DROPPED = 192 +def set_red_panda_canbus(enabled: bool) -> None: + if enabled: + CanBus.POWERTRAIN = 4 + CanBus.OBSTACLE = 5 + CanBus.CAMERA = 6 + CanBus.CHASSIS = 6 + CanBus.LOOPBACK = 132 + CanBus.DROPPED = 196 + else: + CanBus.POWERTRAIN = 0 + CanBus.OBSTACLE = 1 + CanBus.CAMERA = 2 + CanBus.CHASSIS = 2 + CanBus.LOOPBACK = 128 + CanBus.DROPPED = 192 + class GMFlags(IntFlag): PEDAL_LONG = 1 CC_LONG = 2