diff --git a/cereal/custom.capnp b/cereal/custom.capnp index fdb89da84c..cfd20a398f 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -247,6 +247,17 @@ struct BackupManagerSP @0xf98d843bfd7004a3 { } struct CarStateSP @0xb86e6369214c01c8 { + buttonEvents @0 :List(ButtonEvent); + + struct ButtonEvent { + pressed @0 :Bool; + type @1 :Type; + + enum Type { + unknown @0; + customButton @1; + } + } } struct LiveMapDataSP @0xf416ec09499d9d19 { diff --git a/common/params_keys.h b/common/params_keys.h index 23e938eb61..60c5a3b6e0 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -154,6 +154,7 @@ inline static std::unordered_map keys = { {"QuickBootToggle", {PERSISTENT | BACKUP, BOOL, "0"}}, {"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}}, {"ShowAdvancedControls", {PERSISTENT | BACKUP, BOOL, "0"}}, + {"SteeringCustomButtonMapping", {PERSISTENT | BACKUP, INT, "0"}}, // MADS params {"Mads", {PERSISTENT | BACKUP, BOOL, "1"}}, diff --git a/opendbc_repo b/opendbc_repo index 5509df5eb3..6676b7cb33 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 5509df5eb3e965335c04c61d4039e8378bf18718 +Subproject commit 6676b7cb3384fa582c73ad1b0a7e34da9288ee21 diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index e203270579..533802c62b 100755 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -45,6 +45,8 @@ EventName = log.OnroadEvent.EventName ButtonType = car.CarState.ButtonEvent.Type SafetyModel = car.CarParams.SafetyModel +ButtonTypeSP = custom.CarStateSP.ButtonEvent.Type + IGNORED_SAFETY_MODES = (SafetyModel.silent, SafetyModel.noOutput) @@ -88,7 +90,7 @@ class SelfdriveD(CruiseHelper): self.calibrator = PoseCalibrator() # Setup sockets - self.pm = messaging.PubMaster(['selfdriveState', 'onroadEvents'] + ['selfdriveStateSP', 'onroadEventsSP']) + self.pm = messaging.PubMaster(['selfdriveState', 'onroadEvents'] + ['selfdriveStateSP', 'onroadEventsSP', 'userFlag']) self.gps_location_service = get_gps_location_service(self.params) self.gps_packets = [self.gps_location_service] @@ -97,6 +99,7 @@ class SelfdriveD(CruiseHelper): # TODO: de-couple selfdrived with card/conflate on carState without introducing controls mismatches self.car_state_sock = messaging.sub_sock('carState', timeout=20) + self.car_state_sp_sock = messaging.sub_sock('carStateSP', timeout=20) ignore = self.sensor_packets + self.gps_packets + ['alertDebug'] if SIMULATION: @@ -117,6 +120,8 @@ class SelfdriveD(CruiseHelper): self.is_ldw_enabled = self.params.get_bool("IsLdwEnabled") self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") + self.custom_button_mapping = self.params.get("SteeringCustomButtonMapping") + car_recognized = self.CP.brand != 'mock' # cleanup old params @@ -126,6 +131,7 @@ class SelfdriveD(CruiseHelper): self.params.remove("ExperimentalMode") self.CS_prev = car.CarState.new_message() + self.CS_SP_prev = custom.CarStateSP.new_message() self.AM = AlertManager() self.events = Events() @@ -447,7 +453,9 @@ class SelfdriveD(CruiseHelper): def data_sample(self): _car_state = messaging.recv_one(self.car_state_sock) + _car_state_sp = messaging.recv_one(self.car_state_sp_sock) CS = _car_state.carState if _car_state else self.CS_prev + CS_SP = _car_state_sp.carStateSP if _car_state_sp else self.CS_SP_prev self.sm.update(0) @@ -490,7 +498,7 @@ class SelfdriveD(CruiseHelper): if ps.safetyModel not in IGNORED_SAFETY_MODES): self.mismatch_counter += 1 - return CS + return CS, CS_SP def update_alerts(self, CS): clear_event_types = set() @@ -509,7 +517,7 @@ class SelfdriveD(CruiseHelper): self.AM.add_many(self.sm.frame, alerts + alerts_sp) self.AM.process_alerts(self.sm.frame, clear_event_types) - def publish_selfdriveState(self, CS): + def publish_selfdriveState(self, CS, CS_SP): # selfdriveState ss_msg = messaging.new_message('selfdriveState') ss_msg.valid = True @@ -559,8 +567,17 @@ class SelfdriveD(CruiseHelper): self.pm.send('onroadEventsSP', ce_send_sp) self.events_sp_prev = self.events_sp.names.copy() + # custom button handling for bookmark + custom_pressed = any(be.type == ButtonTypeSP.customButton for be in CS_SP.buttonEvents) + if custom_pressed: + # 0 = Off + # 1 = bookmark + if self.custom_button_mapping == 1: + uf_msg = messaging.new_message('userFlag', valid=True) + self.pm.send('userFlag', uf_msg) + def step(self): - CS = self.data_sample() + CS, CS_SP = self.data_sample() self.update_events(CS) if not self.CP.passive and self.initialized: self.enabled, self.active = self.state_machine.update(self.events) @@ -568,9 +585,10 @@ class SelfdriveD(CruiseHelper): self.mads.update(CS) self.update_alerts(CS) - self.publish_selfdriveState(CS) + self.publish_selfdriveState(CS, CS_SP) self.CS_prev = CS + self.CS_SP_prev = CS_SP def params_thread(self, evt): while not evt.is_set(): @@ -579,6 +597,7 @@ class SelfdriveD(CruiseHelper): self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") self.experimental_mode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl self.personality = self.params.get("LongitudinalPersonality", return_default=True) + self.custom_button_mapping = self.params.get("SteeringCustomButtonMapping") self.mads.read_params() time.sleep(0.1) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc index 01ea64f0cb..74483ffc41 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc @@ -8,6 +8,19 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" HyundaiSettings::HyundaiSettings(QWidget *parent) : BrandSettingsInterface(parent) { + + std::vector custom_btn_texts{ tr("Off"), tr("Bookmark") }; + customButtonMapping = new ButtonParamControlSP( + "SteeringCustomButtonMapping", + tr("Steering Custom ☆ Button"), + tr("Customize the steering wheel custom/star button for openpilot control.\n" + "This will not disable OEM functionality."), + "", + custom_btn_texts, + 300 + ); + list->addItem(customButtonMapping); + std::vector tuning_texts{ tr("Off"), tr("Dynamic"), tr("Predictive") }; longitudinalTuningToggle = new ButtonParamControl( "HyundaiLongitudinalTuning", @@ -36,6 +49,18 @@ void HyundaiSettings::updateSettings() { has_longitudinal_control = false; } + auto cp_sp_bytes = params.get("CarParamsSPPersistent"); + if (!cp_sp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_sp_bytes.data(), cp_sp_bytes.size())); + cereal::CarParamsSP::Reader CP_SP = cmsg.getRoot(); + + // TODO-SP: Better way to get the flag value in qt? + has_custom_button = CP_SP.getFlags() & 64; + } else { + has_custom_button = false; + } + LongitudinalTuningOption longitudinal_tuning_option; if (longitudinal_tuning_param == int(LongitudinalTuningOption::PREDICTIVE)) { longitudinal_tuning_option = LongitudinalTuningOption::PREDICTIVE; @@ -54,4 +79,6 @@ void HyundaiSettings::updateSettings() { longitudinalTuningToggle->setEnabled(!longitudinal_tuning_disabled); longitudinalTuningToggle->setDescription(longitudinal_tuning_description); longitudinalTuningToggle->showDescription(); + + customButtonMapping->setVisible(has_custom_button); } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h index c94d40cfde..55bc4da5a2 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h @@ -29,7 +29,9 @@ public: private: bool has_longitudinal_control = false; + bool has_custom_button = false; ButtonParamControl *longitudinalTuningToggle = nullptr; + ButtonParamControlSP *customButtonMapping = nullptr; static QString toggleDisableMsg(bool _offroad, bool _has_longitudinal_control) { if (!_has_longitudinal_control) {