diff --git a/CLAUDE.md b/CLAUDE.md index e13358a..d9b6ba2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,7 +15,7 @@ ClearPilot is a custom fork of **FrogPilot** (itself a fork of comma.ai's openpi - **Native dashcamd**: C++ process capturing raw camera frames via VisionIPC with OMX H.264 hardware encoding - **Standstill power saving**: model inference throttled to 1fps and fan quieted when car is stopped - **ClearPilot menu**: sidebar settings panel replacing stock home screen (Home, Dashcam, Debug panels) -- **Status window**: live system stats (temp, fan, storage, RAM, WiFi, VPN, GPS, telemetry status) +- **Status window**: live system stats (temp, fan, storage, RAM, WiFi, VPN, GPS, telemetry, dashcam status) - **Debug button (LFA)**: steering wheel button repurposed for screen toggle and future UI actions - **Telemetry system**: diff-based CSV logger via ZMQ IPC, toggleable from Debug panel - **Bench mode**: `--bench` flag for onroad UI testing without car connected @@ -101,7 +101,7 @@ ClearPilot uses memory params (`/dev/shm/params/d/`) for UI toggles that should - **Python access**: Use `Params("/dev/shm/params")` - **Defaults**: Set in `manager_init()` via `Params("/dev/shm/params").put(key, value)` - **UI toggles**: Use `ToggleControl` with manual `toggleFlipped` lambda that writes via `Params("/dev/shm/params")`. Do NOT use `ParamControl` for memory params — it reads/writes persistent params only -- **Current memory params**: `TelemetryEnabled` (default "0"), `VpnEnabled` (default "1"), `ModelStandby` (default "0"), `ScreenDisplayMode` +- **Current memory params**: `TelemetryEnabled` (default "0"), `VpnEnabled` (default "1"), `ModelStandby` (default "0"), `ScreenDisplayMode`, `DashcamState` (default "stopped"), `DashcamFrames` (default "0") - **IMPORTANT — method names differ between C++ and Python**: C++ uses camelCase (`putBool`, `getBool`, `getInt`), Python uses snake_case (`put_bool`, `get_bool`, `get_int`). This is a common source of silent failures — the wrong casing compiles/runs but doesn't work. ### Building Native (C++) Processes @@ -245,20 +245,21 @@ A single `session.log` in each session directory records major events: - **Segment length**: 3 minutes per file - **Save path**: `/data/media/0/videos/YYYYMMDD-HHMMSS/YYYYMMDD-HHMMSS.mp4` (trip directories) - **GPS subtitles**: companion `.srt` file per segment with 1Hz entries (speed MPH, lat/lon, UTC timestamp) -- **Trip lifecycle**: starts recording on launch with 10-min idle timer; car in drive cancels timer; park/off restarts timer; ignition cycle = new trip +- **Trip lifecycle**: waits in WAITING state until valid system time + GPS fix + car in drive; records until car parked 10 min or ignition off; then returns to WAITING - **Graceful shutdown**: thermald sets `DashcamShutdown` param, dashcamd closes segment and acks within 15s -- **Storage**: ~56 MB per 3-minute segment at 2500 kbps +- **Storage**: ~56 MB per 3-minute segment at 2500 kbps (verified: actual bitrate ~2570 kbps) +- **Crash handler**: SIGSEGV/SIGABRT handler writes backtrace to `/tmp/dashcamd_crash.log` - **Storage device**: WDC SDINDDH4-128G UFS 2.1 — automotive grade, ~384 TB write endurance, no concern for continuous writes -### Key Differences from Old Screen Recorder +### OmxEncoder -| | Old (screen recorder) | New (dashcamd) | -|---|---|---| -| Source | `QWidget::grab()` screen capture | Raw NV12 from VisionIPC | -| Resolution | 1440x720 | 1928x1208 | -| Works with screen off | No (needs visible widget) | Yes (independent of UI) | -| Process type | Part of UI process | Standalone native process | -| Encoder input | RGBA -> NV12 conversion | NV12 direct (added `encode_frame_nv12`) | +The OMX encoder (`selfdrive/frogpilot/screenrecorder/omx_encoder.cc`) was ported from upstream FrogPilot with the following key properties: + +- Each encoder instance calls `OMX_Init()` in constructor and `OMX_Deinit()` in destructor — manages its own OMX lifecycle +- Constructor takes 5 args: `(path, width, height, fps, bitrate)` — no h265/downscale params +- `encoder_close()` calls `av_write_trailer` + ffmpeg faststart remux (`-movflags +faststart`) +- Destructor has null guards and error handling on all OMX state transitions +- ClearPilot addition: `encode_frame_nv12()` for direct NV12 input (dashcamd), alongside original `encode_frame_rgba()` (screen recorder) ### Key Files @@ -266,7 +267,7 @@ A single `session.log` in each session directory records major events: |------|------| | `selfdrive/clearpilot/dashcamd.cc` | Main dashcam process — VisionIPC -> OMX encoder | | `selfdrive/clearpilot/SConscript` | Build config for dashcamd | -| `selfdrive/frogpilot/screenrecorder/omx_encoder.cc` | OMX encoder (added `encode_frame_nv12` method) | +| `selfdrive/frogpilot/screenrecorder/omx_encoder.cc` | OMX encoder (upstream FrogPilot port + `encode_frame_nv12`) | | `selfdrive/frogpilot/screenrecorder/omx_encoder.h` | Encoder header | | `selfdrive/manager/process_config.py` | dashcamd registered as NativeProcess, camerad always_run, encoderd disabled | | `system/loggerd/deleter.py` | Trip-aware storage rotation (oldest trip first, then segments within active trip) | @@ -275,12 +276,14 @@ A single `session.log` in each session directory records major events: - `DashcamDebug` — when `"1"`, dashcamd runs even without car connected (for bench testing) - `DashcamShutdown` — set by thermald before power-off, dashcamd acks by clearing it +- `DashcamState` (memory param) — "stopped", "waiting", or "recording" — published every 5s +- `DashcamFrames` (memory param) — per-trip encoded frame count, resets each trip — published every 5s ## Standstill Power Saving When `carState.standstill` is true: -- **modeld**: skips GPU inference on 19/20 frames (1fps vs 20fps), reports 0 frame drops to avoid triggering `modeldLagging` in controlsd +- **modeld**: skips GPU inference on 19/20 frames (1fps vs 20fps), reports 0 frame drops to avoid triggering `modeldLagging` in controlsd. Runs full 20fps during calibration (`liveCalibration.calStatus != calibrated`) - **dmonitoringmodeld**: same 1fps throttle, added `carState` subscription - **Fan controller**: uses offroad clamps (0-30%) instead of onroad (30-100%) at standstill; thermal protection still active via feedforward if temp > 60°C @@ -321,6 +324,12 @@ The Hyundai Tucson's LFA steering wheel button cycles through 5 display modes vi **Not in drive (parked/off):** any except 3 → 3 (screen off), state 3 → 0 (auto-normal) +**Shift to drive from screen off:** auto-resets to mode 0 (auto-normal) via `controlsd` + +**Shift to park from nightrider:** auto-switches to mode 3 (screen off) via `home.cc` + +**Tap screen while screen off:** resets to mode 0 (auto-normal) via `window.cc` touch handler + ### Nightrider Mode - Camera feed suppressed (OpenGL clears to black instead of rendering camera texture) @@ -357,7 +366,10 @@ Display power is managed by `Device::updateWakefulness()` in `selfdrive/ui/ui.cc - **Ignition off (offroad)**: screen blanks after `ScreenTimeout` seconds (default 120) of no touch. Tapping wakes it. - **Ignition on (onroad)**: screen stays on unconditionally — ignition=true short-circuits the timeout check. -- **Debug button (LFA)**: cycles through display modes including screen off (state 3). Only state 3 calls `Hardware::set_display_power(false)`. +- **ScreenDisplayMode 3 override**: `updateWakefulness` checks `ScreenDisplayMode` first — if mode 3, calls `setAwake(false)` unconditionally, preventing ignition-on from overriding screen-off. +- **Debug button (LFA)**: cycles through display modes including screen off (state 3). +- **Park transition**: always shows splash screen; if coming from nightrider mode, auto-switches to screen off (mode 3) via `home.cc`. +- **Touch wake**: tapping screen while in mode 3 resets to mode 0 via `window.cc` event filter. ## Offroad UI (ClearPilot Menu) @@ -500,7 +512,7 @@ Power On - **GPS data**: logged directly by telemetryd via cereal `gpsLocation` subscription at 1Hz — group: `gps` (latitude, longitude, speed, altitude, bearing, accuracy) - **CSV location**: `/data/log2/current/telemetry.csv` (or session directory) - **Onroad overlay**: when telemetry enabled, status bar shows temp, fan %, model FPS, and STANDSTILL indicator -- **Speed limit**: `speed_limit.calculated` is the final computed speed limit value (in vehicle units, MPH when `is_metric=False`). This is the value that will be used for the future speed limit warning chime feature +- **Speed limit**: processed by `selfdrive/clearpilot/speed_logic.py` (SpeedState class), converts m/s to display units, writes to memory params. Cruise warning signs appear when cruise set speed exceeds speed limit by threshold (10 mph if limit >= 50, 7 mph if < 50) or is 5+ mph under. Ding sound plays when warning sign appears or speed limit changes while visible (30s cooldown) ### Key Dependencies diff --git a/common/params.cc b/common/params.cc index 72a839c..ac9c14e 100755 --- a/common/params.cc +++ b/common/params.cc @@ -111,6 +111,7 @@ std::unordered_map keys = { {"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, {"DashcamDebug", PERSISTENT}, {"DashcamFrames", PERSISTENT}, + {"DashcamState", PERSISTENT}, {"DashcamShutdown", CLEAR_ON_MANAGER_START}, {"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION}, {"DisablePowerDown", PERSISTENT}, diff --git a/selfdrive/clearpilot/dashcamd b/selfdrive/clearpilot/dashcamd index d3b19ca..1ab5a52 100755 Binary files a/selfdrive/clearpilot/dashcamd and b/selfdrive/clearpilot/dashcamd differ diff --git a/selfdrive/clearpilot/dashcamd.cc b/selfdrive/clearpilot/dashcamd.cc index 42495bc..fb45f8d 100644 --- a/selfdrive/clearpilot/dashcamd.cc +++ b/selfdrive/clearpilot/dashcamd.cc @@ -7,44 +7,32 @@ * Trip directory structure: * /data/media/0/videos/YYYYMMDD-HHMMSS/ (trip directory, named at trip start) * YYYYMMDD-HHMMSS.mp4 (3-minute segments) + * YYYYMMDD-HHMMSS.srt (GPS subtitle sidecar) * * Trip lifecycle state machine: * - * On process start (after time-valid wait): - * - Create trip directory, start recording immediately with 10-min idle timer - * - If car is already in drive, timer is cancelled and recording continues - * - If car stays parked/off for 10 minutes, trip ends + * WAITING: + * - Process starts in this state + * - Waits for valid system time (year >= 2024) AND car in drive gear + * - Transitions to RECORDING when both conditions met * - * IDLE_TIMEOUT → RECORDING: - * - Car enters drive gear before timer expires → cancel timer, resume recording - * in the same trip (no new trip directory) + * RECORDING: + * - Actively encoding frames, car is in drive + * - Car leaves drive → start 10-min idle timer → IDLE_TIMEOUT * - * RECORDING → IDLE_TIMEOUT: - * - Car leaves drive gear (park, off, neutral) → start 10-minute idle timer, - * continue recording frames during the timeout period - * - * IDLE_TIMEOUT → TRIP_ENDED: - * - 10-minute timer expires → close segment, trip is over - * - * TRIP_ENDED → RECORDING (new trip): - * - Car enters drive gear → create new trip directory, start recording - * - * Any state → (new trip) on ignition off→on: - * - Ignition transitions off→on → close current trip if active, create new trip - * directory, start recording with 10-min idle timer. This applies even from - * TRIP_ENDED — turning the car on always starts a new trip. If the car is in - * park after ignition on, the idle timer runs; entering drive cancels it. + * IDLE_TIMEOUT: + * - Car left drive, still recording with timer running + * - Car re-enters drive → cancel timer → RECORDING + * - Timer expires → close trip → WAITING + * - Ignition off → close trip → WAITING * * Graceful shutdown (DashcamShutdown param): * - thermald sets DashcamShutdown="1" before device power-off - * - dashcamd closes current segment, sets DashcamShutdown="0" (ack), exits - * - thermald waits up to 15s for ack, then proceeds with shutdown + * - dashcamd closes current segment, acks, exits * - * GPS subtitle track: - * - Each .mp4 segment has a companion .srt subtitle file - * - Updated at most once per second from gpsLocation cereal messages - * - Format: "35 MPH | 44.9216°N 93.3260°W | 2026-04-13 05:19:00 UTC" - * - Most players auto-detect .srt files alongside .mp4 files + * Published params (memory, every 5s): + * - DashcamState: "waiting" or "recording" + * - DashcamFrames: per-trip encoded frame count (resets each trip) */ #include @@ -75,10 +63,9 @@ const double IDLE_TIMEOUT_SECONDS = 600.0; // 10 minutes ExitHandler do_exit; enum TripState { - IDLE, // no trip active, waiting for drive + WAITING, // no trip, waiting for valid time + drive gear RECORDING, // actively recording, car in drive - IDLE_TIMEOUT, // car parked/off, recording with 10-min timer - TRIP_ENDED, // trip closed, waiting for next drive + IDLE_TIMEOUT, // car left drive, recording with 10-min timer }; static std::string make_timestamp() { @@ -137,15 +124,6 @@ int main(int argc, char *argv[]) { // Ensure base output directory exists mkdir(VIDEOS_BASE.c_str(), 0755); - // Wait for valid system time so trip/segment names have real timestamps - LOGW("dashcamd: waiting for system time"); - while (!do_exit) { - if (system_time_valid()) break; - usleep(1000000); // 1 Hz poll - } - if (do_exit) return 0; - LOGW("dashcamd: system time valid"); - LOGW("dashcamd: started, connecting to camerad road stream"); VisionIpcClient vipc("camerad", VISION_STREAM_ROAD, false); while (!do_exit && !vipc.connect(false)) { @@ -172,45 +150,40 @@ int main(int argc, char *argv[]) { // Subscribe to carState (gear), deviceState (ignition), gpsLocation (subtitles) SubMaster sm({"carState", "deviceState", "gpsLocation"}); Params params; + Params params_memory("/dev/shm/params"); // Trip state - TripState state = IDLE; + TripState state = WAITING; OmxEncoder *encoder = nullptr; std::string trip_dir; - int frame_count = 0; + int frame_count = 0; // per-segment (for rotation) + int trip_frames = 0; // per-trip (published to params) int recv_count = 0; uint64_t segment_start_ts = 0; double idle_timer_start = 0.0; // SRT subtitle state FILE *srt_file = nullptr; - int srt_index = 0; // subtitle entry counter (1-based) - int srt_segment_sec = 0; // seconds elapsed in current segment - double last_srt_write = 0; // monotonic time of last SRT write + int srt_index = 0; + int srt_segment_sec = 0; + double last_srt_write = 0; - // Ignition tracking for off→on detection + // Ignition tracking bool prev_started = false; bool started_initialized = false; - // Param check throttle (don't hit filesystem every frame) + // Param publish throttle int param_check_counter = 0; + double last_param_write = 0; - // Total encoded frames counter + param writer - Params params_memory("/dev/shm/params"); - int total_frames = 0; - double last_frame_count_write = 0; + // Publish initial state + params_memory.put("DashcamState", "waiting"); + params_memory.put("DashcamFrames", "0"); - // Helper: start a new trip with recording + optional idle timer + LOGW("dashcamd: entering main loop in WAITING state"); + + // Helper: start a new trip auto start_new_trip = [&]() { - // Close existing encoder if any - if (encoder) { - if (state == RECORDING || state == IDLE_TIMEOUT) { - encoder->encoder_close(); - } - delete encoder; - encoder = nullptr; - } - trip_dir = VIDEOS_BASE + "/" + make_timestamp(); mkdir(trip_dir.c_str(), 0755); LOGW("dashcamd: new trip %s", trip_dir.c_str()); @@ -221,7 +194,6 @@ int main(int argc, char *argv[]) { LOGW("dashcamd: opening segment %s", seg_name.c_str()); encoder->encoder_open((seg_name + ".mp4").c_str()); - // Open companion SRT file std::string srt_path = trip_dir + "/" + seg_name + ".srt"; srt_file = fopen(srt_path.c_str(), "w"); srt_index = 0; @@ -229,38 +201,38 @@ int main(int argc, char *argv[]) { last_srt_write = 0; frame_count = 0; + trip_frames = 0; segment_start_ts = nanos_since_boot(); state = RECORDING; + + params_memory.put("DashcamState", "recording"); + params_memory.put("DashcamFrames", "0"); }; + // Helper: close current trip auto close_trip = [&]() { if (srt_file) { fclose(srt_file); srt_file = nullptr; } if (encoder) { - if (state == RECORDING || state == IDLE_TIMEOUT) { - encoder->encoder_close(); - LOGW("dashcamd: segment closed"); - } + encoder->encoder_close(); + LOGW("dashcamd: segment closed"); delete encoder; encoder = nullptr; } - state = TRIP_ENDED; + state = WAITING; frame_count = 0; + trip_frames = 0; idle_timer_start = 0.0; - LOGW("dashcamd: trip ended"); - }; + LOGW("dashcamd: trip ended, returning to WAITING"); - // Start recording immediately — if the car is in drive, great; if parked/off, - // the 10-min idle timer will stop the trip if drive is never detected. - start_new_trip(); - idle_timer_start = nanos_since_boot() / 1e9; - state = IDLE_TIMEOUT; - LOGW("dashcamd: recording started, waiting for drive (10-min idle timer active)"); + params_memory.put("DashcamState", "waiting"); + params_memory.put("DashcamFrames", "0"); + }; while (!do_exit) { VisionBuf *buf = vipc.recv(); if (buf == nullptr) continue; - // CLEARPILOT: skip frames to match target FPS (SOURCE_FPS -> CAMERA_FPS) + // Skip frames to match target FPS (SOURCE_FPS -> CAMERA_FPS) recv_count++; if (SOURCE_FPS > CAMERA_FPS && (recv_count % (SOURCE_FPS / CAMERA_FPS)) != 0) continue; @@ -278,25 +250,21 @@ int main(int argc, char *argv[]) { gear == cereal::CarState::GearShifter::LOW || gear == cereal::CarState::GearShifter::MANUMATIC); - // Detect ignition off→on transition (new ignition cycle = new trip) - if (started_initialized && !prev_started && started) { - LOGW("dashcamd: ignition on — new cycle"); + // Detect ignition off → close any active trip + if (started_initialized && prev_started && !started) { + LOGW("dashcamd: ignition off"); if (state == RECORDING || state == IDLE_TIMEOUT) { close_trip(); } - // Start recording immediately, idle timer until drive is detected - start_new_trip(); - idle_timer_start = now; - state = IDLE_TIMEOUT; } prev_started = started; started_initialized = true; - // Check for graceful shutdown request (every ~1 second = 20 frames) + // Check for graceful shutdown request (every ~1 second) if (++param_check_counter >= CAMERA_FPS) { param_check_counter = 0; if (params.getBool("DashcamShutdown")) { - LOGW("dashcamd: shutdown requested, closing trip"); + LOGW("dashcamd: shutdown requested"); if (state == RECORDING || state == IDLE_TIMEOUT) { close_trip(); } @@ -306,32 +274,30 @@ int main(int argc, char *argv[]) { } } - // State machine transitions + // State machine switch (state) { - case IDLE: - case TRIP_ENDED: - if (in_drive) { + case WAITING: { + bool has_gps = sm.valid("gpsLocation") && sm["gpsLocation"].getGpsLocation().getHasFix(); + if (in_drive && system_time_valid() && has_gps) { start_new_trip(); } break; + } case RECORDING: if (!in_drive) { - // Car left drive — start idle timeout idle_timer_start = now; state = IDLE_TIMEOUT; - LOGW("dashcamd: car not in drive, starting 10-min idle timer"); + LOGW("dashcamd: car left drive, starting 10-min idle timer"); } break; case IDLE_TIMEOUT: if (in_drive) { - // Back in drive — cancel timer, resume recording same trip idle_timer_start = 0.0; state = RECORDING; LOGW("dashcamd: back in drive, resuming trip"); } else if ((now - idle_timer_start) >= IDLE_TIMEOUT_SECONDS) { - // Timer expired — end trip LOGW("dashcamd: idle timeout expired"); close_trip(); } @@ -368,19 +334,15 @@ int main(int argc, char *argv[]) { continue; } - if (frame_count == 0) { - LOGW("dashcamd: first encode w=%d h=%d stride=%d buf_y=%p buf_uv=%p", width, height, y_stride, buf->y, buf->uv); - } - // Feed NV12 frame directly to OMX encoder encoder->encode_frame_nv12(buf->y, y_stride, buf->uv, uv_stride, width, height, ts); frame_count++; - total_frames++; + trip_frames++; - // Write total frame count to params_memory every 5 seconds - if (now - last_frame_count_write >= 5.0) { - params_memory.put("DashcamFrames", std::to_string(total_frames)); - last_frame_count_write = now; + // Publish state every 5 seconds + if (now - last_param_write >= 5.0) { + params_memory.put("DashcamFrames", std::to_string(trip_frames)); + last_param_write = now; } // Write GPS subtitle at most once per second @@ -388,7 +350,6 @@ int main(int argc, char *argv[]) { last_srt_write = now; srt_index++; - // Read GPS data double lat = 0, lon = 0, speed_ms = 0; bool has_gps = sm.valid("gpsLocation") && sm["gpsLocation"].getGpsLocation().getHasFix(); if (has_gps) { @@ -421,13 +382,11 @@ int main(int argc, char *argv[]) { } // Clean exit - if (srt_file) { fclose(srt_file); srt_file = nullptr; } - if (encoder) { - if (state == RECORDING || state == IDLE_TIMEOUT) { - encoder->encoder_close(); - } - delete encoder; + if (state == RECORDING || state == IDLE_TIMEOUT) { + close_trip(); } + params_memory.put("DashcamState", "stopped"); + params_memory.put("DashcamFrames", "0"); LOGW("dashcamd: stopped"); return 0; diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py index 7e12861..7190e4d 100755 --- a/selfdrive/manager/manager.py +++ b/selfdrive/manager/manager.py @@ -88,6 +88,7 @@ def manager_init(frogpilot_functions) -> None: params_memory.put("TelemetryEnabled", "0") params_memory.put("VpnEnabled", "1") params_memory.put("DashcamFrames", "0") + params_memory.put("DashcamState", "stopped") params_memory.put("ModelStandby", "0") params_memory.put("ModelStandbyTs", "0") params_memory.put("CarIsMetric", "0") diff --git a/selfdrive/ui/qt/window.cc b/selfdrive/ui/qt/window.cc index f2f65d5..d4d8f06 100755 --- a/selfdrive/ui/qt/window.cc +++ b/selfdrive/ui/qt/window.cc @@ -264,8 +264,8 @@ static StatusWindow::StatusData collectStatus() { d.telemetry = readFile("/data/params/d/TelemetryEnabled"); // Dashcam - QString dashcam_pid = shellCmd("pgrep -x dashcamd"); - d.dashcam_status = dashcam_pid.isEmpty() ? "stopped" : "recording"; + d.dashcam_state = readFile("/dev/shm/params/d/DashcamState"); + if (d.dashcam_state.isEmpty()) d.dashcam_state = "stopped"; d.dashcam_frames = readFile("/dev/shm/params/d/DashcamFrames"); // Panda: checked on UI thread in applyResults() via scene.pandaType @@ -383,11 +383,14 @@ void StatusWindow::applyResults() { telemetry_label->setStyleSheet("color: grey; font-size: 38px;"); } - if (d.dashcam_status == "recording") { + if (d.dashcam_state == "recording") { QString text = "Recording"; if (!d.dashcam_frames.isEmpty() && d.dashcam_frames != "0") text += " (" + d.dashcam_frames + " frames)"; dashcam_label->setText(text); dashcam_label->setStyleSheet("color: #17c44d; font-size: 38px;"); + } else if (d.dashcam_state == "waiting") { + dashcam_label->setText("Waiting"); + dashcam_label->setStyleSheet("color: #ffaa00; font-size: 38px;"); } else { dashcam_label->setText("Stopped"); dashcam_label->setStyleSheet("color: #ff4444; font-size: 38px;"); diff --git a/selfdrive/ui/qt/window.h b/selfdrive/ui/qt/window.h index 1d01a54..2897cb1 100755 --- a/selfdrive/ui/qt/window.h +++ b/selfdrive/ui/qt/window.h @@ -20,7 +20,7 @@ public: struct StatusData { QString time, storage, ram, load, temp, fan, ip, wifi; QString vpn_status, vpn_ip, gps, telemetry; - QString dashcam_status, dashcam_frames; + QString dashcam_state, dashcam_frames; float temp_c = 0; };