When the car is in park (ignition still on), pause everything that
isn't strictly needed and keep controlsd in a minimal-cycle keepalive
mode so the car's steering ECU keeps seeing the LFA/LKAS CAN-FD
messages and stays in tester mode (no steering fault on shift to drive).
controlsd:
- Detects park gear, writes ParkMode to /dev/shm/params.
- park_mode_tick(): publishes a do-nothing CarControl through
self.card (CarController.update unconditionally appends the
steering messages every cycle, which is the actual heartbeat).
Still runs clearpilot_state_control so the LFA/debug button +
ScreenDisplayMode keep working in park.
- After park→drive, stay in keepalive-tick mode until SubMaster
reports all services healthy (an 8s hard cap as safety net).
Avoids the burst of commIssue alerts that would otherwise fire
while modeld/plannerd/paramsd/torqued/dmonitoringd/calibrationd/
frogpilot_process spin back up from cold.
manager / process_config:
- New gating helpers _park_mode(), only_onroad_active,
driverview_active, always_run_unless_parked.
- Re-gated to only_onroad_active: modeld, sensord, soundd, locationd,
calibrationd, torqued, paramsd, plannerd, radard, speed_logicd.
- Re-gated to driverview_active: dmonitoringmodeld, dmonitoringd.
- frogpilot_process → always_run_unless_parked (preserves offroad
behavior, only pauses when ignition+parked).
- controlsd stays plain only_onroad — it's the writer + heartbeat.
- ParkMode registered in params.cc, defaulted to "0" in manager_init.
thermald + fan_controller (broken-tree rule, ported):
- thermald subscribes to carState; passes standstill, is_parked,
cruise_engaged into fan_controller.update.
- New fan range rules:
parked → 0-100% (no floor, full cooling)
cruise on → 30-100%
standstill → 10-100%
moving → 30-100%
ignition off → 0-30% (existing)
dashcamd: already park-aware via gear-driven trip lifecycle — no
change needed.
Verified: build clean. Launched on bench: park mode kicks in on
startup (gearShifter=unknown initially → eventually park),
modeld/plannerd/paramsd/torqued/calibrationd/frogpilot_process
correctly disappear from the manager process list, gpsd/dashcamd/
ui/controlsd/pandad stay alive, ParkMode=1 in /dev/shm/params.
Restoring the working tree to the pristine pre-Claude baseline previously
preserved at /data/clearpilot (now /data/clearpilot-baseline). The prior
modified-but-broken tree is snapshotted at /data/openpilot-broken-2026-05-03
and tagged here as pre-reset-2026-05-03 for reference.
From here, features (UI changes, dashcam, telemetry, GPS, display modes,
speed logic, standstill power saving, etc.) will be re-introduced one at
a time with proper testing.
The new fan control plumbing referenced log.CarState.GearShifter.park,
but CarState is defined in car.capnp and only DeviceState/PandaState
live in log.capnp. Thermald crashed immediately on first carState tick,
which meant deviceState stopped publishing entirely. The UI then read
stale/default values — freeSpacePercent=0 → "100% full" → "Out of Storage"
alert despite 65 GB actually free. The fan also stopped updating.
from cereal import car, log (was just log)
car.CarState.GearShifter.park (was log.CarState...)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fan control rework (thermald → 4Hz):
- DT_TRML 0.5s → 0.25s (thermald loop + fan PID now at 4Hz)
- New clamp rules based on (gear, cruise_engaged, standstill):
parked → 0-100%
in drive + cruise engaged (any speed) → 30-100%
in drive + cruise off + standstill → 10-100%
in drive + cruise off + moving → 30-100%
- thermald now reads gearShifter (via carState) and controlsState.enabled,
passes them to fan_controller.update()
- Removed BENCH_MODE special case — new rules cover bench automatically
- Removed ignition-based branches — gear is the correct signal
System health overlay:
- Subscribed UI to peripheralState so we can read fanSpeedRpm
- Added FAN row: actual fan% (RPM / 65) to sit alongside LAG/DROP/TEMP/CPU/MEM.
Shows the real fan output vs. what the PID is asking for.
Migrate hot signals from paramsMemory to cereal (frogpilotCarControl):
- Added latRequested @3 and noLatLaneChange @4 to FrogPilotCarControl schema
- controlsd sets FPCC.latRequested / FPCC.noLatLaneChange (send-on-change
already gates the IPC)
- modeld reads from sm['frogpilotCarControl'] (added to its subscribers)
instead of paramsMemory (saves ~20 file-read syscalls/sec)
- carcontroller reads from frogpilot_variables (set in-process by controlsd)
instead of paramsMemory (saves ~100 file-read syscalls/sec in 100Hz path).
Dropped carcontroller's now-unused Params instance and import.
- UI (ui.cc, onroad.cc) reads from sm['frogpilotCarControl'].noLatLaneChange
- Removed LatRequested and no_lat_lane_change param registrations + defaults
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Python Params uses snake_case; the C++ camelCase call raised
AttributeError, killing thermald_thread at the exact moment of shutdown.
Result: DoShutdown never got set, the 10-minute timer "worked" once (set
DashcamShutdown=True) and then thermald died silently. Device kept
draining the battery instead of powering down.
Caught because CLAUDE.md specifically flags this pattern as a common
source of silent failures between C++ and Python.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fan controller: allow full 100% fan when offroad temp >= 75°C (startup cooling)
- ModelFps memory param: modeld publishes actual FPS (20 or 4) so downstream
consumers can adjust frame-rate-dependent logic
- Longitudinal planner: dynamically adjusts dt and v_desired_filter based on
ModelFps; FCW crash_cnt threshold scales with FPS to maintain consistent
0.15s trigger window at both 20fps and 4fps
- controlsd: suppress commIssue alerts for 2s after lateral control engages
(FPS transition from 4->20 causes transient freq check failures)
- Shutdown timer: hardcoded to 10 minutes (was 45min via FrogPilot param),
screen taps reset the countdown via ShutdownTouchReset memory param,
removed Shutdown Timer UI selector from ClearPilot menu
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dashcam recording now organized by trip in /data/media/0/videos/YYYYMMDD-HHMMSS/.
Starts recording immediately on launch (with 10-min idle timer), transitions to
continuous recording when drive gear detected. New trip on every ignition cycle.
Graceful shutdown via DashcamShutdown param with 15s ack timeout in thermald.
- Bitrate reduced to 2500 kbps (was 4 Mbps)
- Trip state machine: IDLE → RECORDING ↔ IDLE_TIMEOUT → TRIP_ENDED
- Deleter: trip-aware deletion (oldest trip first, then segments within active trip)
- camerad changed to always_run (was driverview) so dashcam works offroad
- DashcamShutdown param for graceful close before device power-off
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>