diff --git a/selfdrive/clearpilot/dashcamd.cc b/selfdrive/clearpilot/dashcamd.cc index 97f84fa..0e4fdd5 100644 --- a/selfdrive/clearpilot/dashcamd.cc +++ b/selfdrive/clearpilot/dashcamd.cc @@ -12,20 +12,20 @@ * Trip lifecycle state machine: * * WAITING: - * - Process starts in this state + * - Process starts in this state. Idle. * - Waits for valid system time (year >= 2024) AND car in drive gear * - Transitions to RECORDING when both conditions met * * RECORDING: * - Actively encoding frames, car is in drive - * - Car leaves drive → start 10-min idle timer → IDLE_TIMEOUT - * - * IDLE_TIMEOUT: - * - Car left drive, still recording with timer running - * - Car re-enters drive → cancel timer → RECORDING - * - Timer expires → close trip → WAITING + * - Gear shift into PARK → close trip immediately → WAITING (idle) * - Ignition off → close trip → WAITING * + * IDLE_TIMEOUT (deprecated, retained for safety): + * - Was used to keep recording across brief drive-thru / fuel stops. + * - Now unreachable: drive→park transitions close the trip immediately + * and a fresh trip starts on the next drive engagement. + * * Graceful shutdown (DashcamShutdown param): * - thermald sets DashcamShutdown="1" before device power-off * - dashcamd closes current segment, acks, exits @@ -301,10 +301,11 @@ int main(int argc, char *argv[]) { } case RECORDING: - if (!in_drive) { - idle_timer_start = now; - state = IDLE_TIMEOUT; - LOGW("dashcamd: car left drive, starting 10-min idle timer"); + // CLEARPILOT: close trip immediately on park (no idle timer). User wants + // dashcam idle in park, fresh trip on each drive engagement. + if (gear == cereal::CarState::GearShifter::PARK) { + LOGW("dashcamd: gear in park, closing trip"); + close_trip(); } break; diff --git a/selfdrive/controls/controlsd_parked.py b/selfdrive/controls/controlsd_parked.py index d70e259..2eee920 100644 --- a/selfdrive/controls/controlsd_parked.py +++ b/selfdrive/controls/controlsd_parked.py @@ -11,11 +11,24 @@ Manager swaps between this and the full controlsd via predicate flips: - full runs when: started (which requires ignition AND not_parked) The two are mutually exclusive — only one publishes carState at a time. + +We also keep the Hyundai HDA2 tester-present heartbeat alive while parked +so the ADAS ECU doesn't snap back to default-session and light up LKAS / +blind-spot warning icons on the cluster during the swap. """ from types import SimpleNamespace from openpilot.common.realtime import Priority, config_realtime_process +from openpilot.selfdrive.boardd.boardd import can_list_to_can_capnp from openpilot.selfdrive.car.card import CarD +from openpilot.selfdrive.car.hyundai.hyundaicanfd import CanBus as HyundaiCanBus +from openpilot.selfdrive.car.hyundai.values import HyundaiFlags + +# UDS Tester Present, suppressPositiveResponse — same bytes the full +# carcontroller sends every 100 frames to 0x730 on E-CAN to keep the ADAS +# ECU held in its disabled diagnostic session. +TESTER_PRESENT = b"\x02\x3E\x80\x00\x00\x00\x00\x00" +TESTER_PRESENT_PERIOD_FRAMES = 100 # ~1 Hz at the CAN-paced loop rate def _make_default_frogpilot_variables() -> SimpleNamespace: @@ -43,12 +56,23 @@ def main(): fv = _make_default_frogpilot_variables() + # Determine if this car wants the Hyundai HDA2 tester-present heartbeat, + # and which bus E-CAN is on for this panda configuration. + is_hda2 = card.CP.carName == "hyundai" and bool(card.CP.flags & HyundaiFlags.CANFD_HDA2.value) + ecan = HyundaiCanBus(card.CP).ECAN if is_hda2 else None + # state_update drains CAN, parses carState, publishes carState/carOutput/carParams. # Internally blocks via drain_sock_raw(wait_for_one=True), so the loop is # naturally paced by CAN traffic — no extra sleep needed. + frame = 0 while True: card.state_update(fv) + if is_hda2 and frame % TESTER_PRESENT_PERIOD_FRAMES == 0: + can_sends = [[0x730, 0, TESTER_PRESENT, ecan]] + card.pm.send('sendcan', can_list_to_can_capnp(can_sends, msgtype='sendcan', valid=True)) + frame += 1 + if __name__ == "__main__": main() diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py index c9dd499..b1b100e 100755 --- a/selfdrive/thermald/thermald.py +++ b/selfdrive/thermald/thermald.py @@ -263,11 +263,18 @@ def thermald_thread(end_event, hw_queue) -> None: # during low-speed parking don't kick the heavy stack off. Going OUT of # parked is instant so the full stack starts spinning up the moment the # driver shifts to D/R/N. + # + # Cruise override: if cruise control has any speed set (engaged OR + # paused-with-speed-set), keep the full stack running even in park. This + # means the user can shift to park at a stop, glance at the cluster to + # verify cruise is still set, and roll forward without waiting for full + # controlsd to spin back up. if sm.updated['carState']: - gear = sm['carState'].gearShifter - gear_is_park = gear == car.CarState.GearShifter.park + cs = sm['carState'] + gear_is_park = cs.gearShifter == car.CarState.GearShifter.park + cruise_set = cs.cruiseState.speed > 0 now_mono = time.monotonic() - if gear_is_park: + if gear_is_park and not cruise_set: if parked_since is None: parked_since = now_mono if (not is_parked) and (now_mono - parked_since) >= PARKED_HYSTERESIS_S: