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.
VisionIPC frames from camerad → OMX H.264 hardware encoder → 3-min MP4
segments + SRT GPS subtitles in /data/media/0/videos/<trip>/. Manages
its own trip lifecycle (WAITING/RECORDING/IDLE_TIMEOUT) and writes
DashcamState/DashcamFrames memory params for the UI's Status window.
Honors DashcamShutdown for graceful close before power-off.
Files added:
- selfdrive/clearpilot/dashcamd.cc + SConscript
Files modified:
- selfdrive/frogpilot/screenrecorder/omx_encoder.{cc,h}: ported broken's
version, which adds encode_frame_nv12() (direct NV12 input from camerad,
alongside the existing encode_frame_rgba used by the disabled screen
recorder) and simplifies the libyuv conversion paths to NEON-only since
this device is aarch64.
- selfdrive/SConscript: register selfdrive/clearpilot/SConscript so the
dashcamd binary is part of the build.
- selfdrive/manager/process_config.py:
- camerad gating driverview → always_run so dashcamd can record the
moment ignition+drive arrives without waiting for camera startup.
- Register dashcamd as NativeProcess gated always_run.
- system/loggerd/deleter.py:
- MIN_BYTES 5 GB → 9 GB to leave headroom for dashcam footage.
- delete_oldest_video(): trip-aware cleanup. Drops entire oldest trip
dir first; if only the active trip remains, drops oldest segment
inside it; cleans up legacy flat .mp4s too.
- cleanup_log2(): keeps /data/log2 session logs under 4 GB total.
- Hooked into deleter_thread: video first when out of bytes/percent;
log2 quota check on the idle path. New code uses print(stderr) per
the no-cloudlog rule.
Verified: built clean, manager started, dashcamd in WAITING state
(DashcamState=waiting, DashcamFrames=0), camerad running, no errors.
Adds the AT-command-based GPS daemon for the Quectel EC25 modem (the
device has no u-blox chip and qcomgpsd's diag interface hangs on this
hardware). Trimmed from broken's version: dropped cloudlog calls and
the IsDaylight / ScreenDisplayMode auto-switching (those belong to the
display-modes feature, port later).
Used solely for system clock initialization, on-screen UI speed, and
per-segment dashcam GPS metadata.
Self-driving must NOT consume gpsLocation — feeding it to locationd's
kalman filter screws up the math. Patch locationd to skip GPS:
- locationd_thread() no longer subscribes to gpsLocation/gpsLocationExternal
- handle_msg's GPS branches commented (dead code without subscription)
- the "save LastGPSPosition once a minute when gpsOK" block commented
(dead because gpsOK is now permanently false)
Result: liveLocationKalman.gpsOK = false for all self-driving consumers
(controlsd, paramsd, torqued, frogpilot_planner). They already handle
that case. Other liveLocationKalman fields still publish from the
camera-odometry + IMU + calibration kalman state.
system/clearpilot/__init__.py added so system.clearpilot.gpsd is a
valid Python module.
We don't use comma's upload/replay pipeline, and the segment recorder
was silently filling /data with 30 MB rlog files per minute (the 64 GB
we just cleared). Disable the writers entirely.
- process_config.py: comment out loggerd, encoderd, stream_encoderd
- manager.py: skip save_bootlog() at manager init (was writing
boot info to /data/media/0/realdata/boot/<bootid>/)
deleter still runs for cleanup of any leftover data; logmessaged still
runs (in-memory log routing IPC). uploader was already disabled in
baseline. No process consumes loggerd/encoderd output onroad, so this
is purely a sink removal.
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.
- modeld: enter standby when latActive=false (not just standstill),
exception for lane changes (no_lat_lane_change). Fix Python capnp
property access (.latActive not getLatActive())
- controlsd: move model_suppress computation early, suppress radarFault,
posenetInvalid, locationdTemporaryError, paramsdTemporaryError during
model standby + 2s grace period. All cascade from modeld not publishing
- dashcamd: always_run (manages own trip lifecycle), wait for valid frame
dimensions before encoding (fix SIGSEGV on early start)
- Fan: driving range 15-100% (was 30-100%)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Telemetry status bar in onroad UI: temp, fan %, model FPS, standstill
- Fix paramsMemory usage: Params("/dev/shm/params") not "/dev/shm/params/d"
- Telemetry/VPN toggles use ToggleControl with manual paramsMemory writes
- TelemetryEnabled/VpnEnabled registered PERSISTENT, written to memory path
- GPS telemetry: telemetryd subscribes to gpsLocation at 1Hz via cereal
- Nightrider: force CameraWidget bg black to eliminate color bleed border
- Suppress "Always On Lateral active" status bar message
- Re-enable gpsd and dashcamd
- CLAUDE.md: document memory params pattern, speed_limit.calculated usage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix controlsd crash: self.state.name AttributeError when state is int
- Move telemetry tlog import to module level in carstate.py (was per-frame)
- Remove FrogPilot screen recorder from UI (was crashing OMX on init)
- Ready screen: boot logo background, 8-bit READY! sprite, error states
(panda not connected, car not recognized) with 10s startup grace period
- ClearPilot menu: always opens to General, QButtonGroup for sidebar,
System Status uses ButtonControl, VPN toggle with process control
- Sidebar hidden on construction (no flash before splash)
- Status window: threaded data collection (QtConcurrent), panda detection
via scene.pandaType (SPI, not USB), only refreshes when visible
- VPN: moved to system/clearpilot/, SIGTERM graceful shutdown, keepalive
ping through tunnel, killall openvpn on disable, launched from
launch_openpilot.sh instead of continue.sh
- Disable gpsd and dashcamd temporarily for perf testing
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>
GPS: fix AT response parsing (strip mmcli `response: '...'` wrapper),
fix capnp field names (horizontalAccuracy, hasFix), set system clock
directly from first GPS fix when time is invalid, kill system gpsd on
startup.
Logging: replace module-level log dir creation with init_log_dir()
called from manager_init(). Active session always at /data/log2/current
(real dir until time resolves, then symlink to timestamped dir). Add
LogDirInitialized param. Redirect both stdout+stderr for all processes.
Also: thorough process cleanup in launch scripts, dashcamd binary,
CLAUDE.md updates for GPS/telemetry/bench/logging docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Device has no u-blox chip (/dev/ttyHS0 missing). GPS is via the Quectel
EC25 LTE modem. Uncommented qcomgpsd in process_config.
GPS hardware confirmed working — modem returns fix via AT+QGPSLOC with
7 satellites, 1m accuracy. However qcomgpsd's diag interface reader
appears to stall after setup — the cereal gpsLocationExternal message
is not being populated. The diag subprocess exits as a zombie. Needs
further investigation into the ModemDiag interface.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bench mode (--bench flag):
- bench_onroad.py publishes fake vehicle state as managed process
- manager blocks real car processes (pandad, thermald, controlsd, etc.)
- bench_cmd.py for setting params and querying UI state
- BLOCKER: UI segfaults (SIGSEGV) when OnroadWindow becomes visible
without camera frames — need to make CameraWidget handle missing
VisionIPC gracefully before bench drive mode works
ClearPilot menu:
- Replaced grid launcher with sidebar settings panel (ClearPilotPanel)
- Sidebar: Home, Dashcam, Debug
- Home panel: Status and System Settings buttons
- Debug panel: telemetry logging toggle (ParamControl)
- Tap from any view (splash, onroad) opens ClearPilotPanel
- Back button returns to splash/onroad
Status window:
- Live system stats refreshing every 1 second
- Storage, RAM, load, IP, WiFi, VPN, GPS, time, telemetry status
- Tap anywhere to close, returns to previous view
- Honors interactive timeout
UI introspection RPC:
- ZMQ REP server at ipc:///tmp/clearpilot_ui_rpc
- Dumps full widget tree with visibility, geometry, stacked indices
- bench_cmd dump queries it, detects crash loops via process uptime
- ui_dump.py standalone tool
Other:
- Telemetry toggle wired to TelemetryEnabled param with disk space guard
- Telemetry disabled on every manager start
- Blinking red circle on onroad UI when telemetry recording
- Fixed showDriverView overriding park/drive transitions every frame
- Fixed offroadTransition sidebar visibility race in MainWindow
- launch_openpilot.sh: cd to /data/openpilot, --bench flag support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- telemetry.py: client library, tlog(group, data) sends JSON over ZMQ PUSH
- telemetryd.py: collector process, diffs against previous state per group,
writes only changed values to /data/log2/{session}/telemetry.csv
- Registered as always-run managed process in process_config.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New dashcamd: connects to camerad via VisionIPC, feeds raw NV12
frames directly to OMX H.264 encoder. Full 1928x1208 resolution,
4Mbps, 3-minute MP4 segments. Works regardless of UI state.
- Added encode_frame_nv12() to OmxEncoder — skips RGBA->NV12 conversion
- Suspends recording after 10 minutes of standstill
- Disabled old screen recorder timer in onroad.cc
- Suppress debug button alert (clpDebug event still fires for screen toggle)
- launch_openpilot.sh self-cleans other instances before starting
- Register DashcamDebug param in params.cc and manager.py
- Add dashcamd to build system (SConscript) and process_config
- Updated CLAUDE.md with all session changes
- Added GOALS.md feature roadmap
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Re-enable FrogPilot OMX screen recorder (H.264 MP4, 1440x720, 2Mbps)
- Auto-start recording when car is on, auto-stop when off
- Hide all recorder UI elements (invisible to driver)
- Add ScreenRecorderDebug param for bench testing without car
- Disable encoderd (camera .hevc files) — CAN/sensor logs still recorded
- Raise deleter free space threshold from 5GB to 9GB
- Deleter rotates oldest videos before log segments
- Add CLAUDE.md project documentation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>