Troubleshooting
Symptom → cause → fix, where the logs are and what is in them, and the observability built into the app for diagnosing problems after the fact.
Where to Look First
Start with the status text on the affected page and the logs. OCTAVE has no global error-notification layer, but individual views can show details on request. These are the main places to look:
- The Connection Log / status text on the relevant page — the OBD settings card shows each connection step and rfcomm / permission errors; the phone-mirror view shows the adb / authorization message.
- Settings → Device → Diagnostics card (
diagnosticsManager) — a 2×2 grid: Refresh (last 200 log lines), Copy (last 400 lines to the clipboard), Export logs (copies the log files to the Downloads folder — the way to get logs off an Android tablet or a head unit without a shell) and Open folder (hidden on Android/iOS, wherecanOpenFolderis false). The log directory path is printed on the card. - The rotating log files described next. A hung UI, a crash and a dropped adapter all leave a trace there even when the screen shows nothing.
Log Files, Crash Traces & Debug Mode
Log directory
OCTAVE writes to a logs/ subdirectory of the app data directory (SettingsManager::getAppDataDir()):
| Platform | Path |
|---|---|
| Windows | %APPDATA%\OCTAVE\logs\ |
| macOS | ~/Library/Application Support/OCTAVE/logs/ |
| Linux | $XDG_CONFIG_HOME/OCTAVE/logs/, i.e. ~/.config/OCTAVE/logs/ |
| Android | getAppDataDir() has no Android branch, so the Android build falls through the Linux rule into the app's private home directory. It is not browsable from a file manager — use Export logs on the Diagnostics card, which writes into Download/. On Android and iOS the message handler also forwards every line to Qt's default handler (stderr goes nowhere on a phone), so adb logcat / os_log show the same messages. |
Log files
| File | Level | Max size | Backups | Notes |
|---|---|---|---|---|
octave-cpp.log | INFO+ | 5 MB | 3 | src/util/logger.cpp, custom size-based rotation |
octave-cpp-error.log | WARNING+ | 2 MB | 5 | also the raw crash descriptor for the signal handler |
octave-cpp-debug.log | everything | 10 MB | 2 | only with --debug |
Total disk is bounded at roughly 32 MB without debug, ~62 MB with it.
Line format
# file and stderr (millisecond timestamps, Qt category in the module column):
YYYY-MM-DD HH:MM:SS.zzz | LEVEL | octave.elm327 | obdmanager.cpp:1772 | message
Each manager logs through its own QLoggingCategory: octave.settings, octave.media, octave.network, octave.phonemirror (+ octave.phonemirror.client for the scrcpy-protocol client), octave.androidauto (+ octave.androidauto.session and octave.androidauto.connection for the built-in receiver), octave.embeddeddhu, octave.audioanalyzer, octave.berryimu, octave.gesture, octave.esp32volume, octave.dashboards, octave.diagnostics, octave.ui_watchdog and octave.elm327 (protocol transcript and the Android BLE bridge lines). The exceptions are obdmanager.cpp, clock.cpp and volumecontroller.cpp, which log through plain qDebug()/qWarning() and therefore appear under the category default — so there is no octave.obd category to filter on; look for the [OBD Worker] message prefix instead. With --debug the filter rules are *.debug=true, qt.*.debug=false; without it *.debug=false, so category-debug output costs nothing.
Crash traces
- Unix: a signal handler for SIGSEGV, SIGABRT, SIGFPE, SIGBUS and SIGILL writes
==== FATAL SIGSEGV - backtrace (addresses; symbolise with addr2line against this build) ====plusbacktrace_symbols_fd()frames tooctave-cpp-error.logand stderr, then restores the default action and re-raises so the OS still produces a core / tombstone. The addresses are only meaningful against the exact binary that crashed. The handler never touches Qt (every log line is already flushed as it is written, so it has nothing to flush); aqFatalgoes through the message handler first, which flushes the files before Qt aborts, and a normal exit flushes them last in theaboutToQuitcleanup. Windows:SetUnhandledExceptionFilterwrites the exception code, address and aCaptureStackBackTraceframe list. - SIGTERM / SIGINT (launcher scripts, systemd, Ctrl-C) on desktop Unix are turned into
app.quit()through a self-pipe so theaboutToQuitcleanup runs and child processes / adb forwards are not orphaned (src/main.cpp).
UI watchdog: "UI thread blocked for N s and counting"
A watchdog thread (src/util/uiwatchdog.cpp) posts a ping to the GUI thread every 2 s; if the GUI thread has not answered within 5 s a WARNING is logged under octave.ui_watchdog, once per stall, followed by UI thread responsive again after N s when it recovers. A frozen head unit therefore leaves a timestamped trace even though nothing was drawn. Look at the lines immediately before the warning to see which manager was busy; the watchdog is stopped before Qt tears down the QObjects so a shutdown never shows as a stall.
Enabling debug mode
./build/octave --debug # debug logging (--smoke is also available)
Creates the -debug.log file with DEBUG-level output. Useful for: the ELM327 TX/RX transcript ([OBD Worker] TX/RX lines with round-trip times at debug level under octave.elm327), serial data from the ESP32 knob, settings load/save, media library scanning and tag extraction, and the phone-mirror adb / server launch sequence.
Installation Issues
These concern installing and starting a packaged build. Build-time problems are on the Building page.
Windows
| Problem | Solution |
|---|---|
"DLL not found" launching the shipped .exe | The current installer bundles the VC++ runtime DLLs and has no registry warning. Reinstall the complete package if files are missing; for an older or hand-built copy missing MSVCP140/VCRUNTIME140, see Build Troubleshooting. |
macOS
| Problem | Solution |
|---|---|
"App is damaged" Gatekeeper error on the .dmg build | The workflow does not configure Developer ID signing or notarization. Check the downloaded package and follow Apple's Gatekeeper guidance; a damaged-app message can also indicate modified or corrupted files. |
Linux
| Problem | Solution |
|---|---|
Missing libxcb / libGL libraries (a binary built from source) | Qt's xcb platform plugin and the renderer need the host's XCB, GL and audio libraries. On Debian/Ubuntu the core set is libxcb-xinerama0 libxcb-cursor0 libxkbcommon0 libgl1 libegl1 libpulse0; the full XCB list is under Display. |
| AppImage won't run | chmod +x OCTAVE-<version>-linux-x86_64.AppImage; if FUSE is missing, sudo apt install libfuse2 or run ./OCTAVE-<version>-linux-x86_64.AppImage --appimage-extract && ./squashfs-root/AppRun. |
| AppImage from a GitHub release fails with a glibc error on an old distro | CI builds on Ubuntu 22.04; older glibc cannot load it. Build locally instead (Building → Linux). |
| Locally built AppImage starts, no window appears | The QML tree is not at usr/frontend/ next to usr/bin/octave; see AppImage structure. |
Display & Rendering Issues
XCB platform plugin errors (Linux)
qt.qpa.plugin: Could not load the Qt platform plugin "xcb"
Install the X11/XCB libraries Qt's xcb plugin loads (Debian/Ubuntu package names):
sudo apt install libxcb1 libxcb-cursor0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 \
libxcb-randr0 libxcb-render0 libxcb-render-util0 libxcb-shape0 libxcb-shm0 \
libxcb-sync1 libxcb-xfixes0 libxcb-xinerama0 libxcb-xkb1 libxcb-util1 \
libxkbcommon0 libxkbcommon-x11-0
The AppImage ships its own platform plugins and sets QT_QPA_PLATFORM_PLUGIN_PATH in its AppRun, but host libraries and a working display are still required. Run with QT_DEBUG_PLUGINS=1 to identify missing dependencies or plugin conflicts, and check DISPLAY/WAYLAND_DISPLAY.
Wayland vs X11
Qt picks the platform plugin from the session. To force one:
export QT_QPA_PLATFORM=xcb # force X11 / XWayland
export QT_QPA_PLATFORM=wayland # force Wayland
./build/octave
The dev screenshot and recording tools do not care which one is active: they grab the window from inside Qt (grabWindow()) rather than through the compositor — see Development → Screenshots.
Black UI, missing icons or album art
The nav icons and media controls are SVGs wrapped in ColorOverlay and the album art uses MultiEffect / DropShadow — GPU shader effects that need a real GL context. In some headless configurations the rendering backend cannot provide those effects, so an offscreen smoke test does not verify their appearance. On a real display, a black window usually means a broken GL stack: try QT_QUICK_BACKEND=software (slow but reliable) and check the log for module "..." is not installed, which means the Qt build lacks QtQuick.Effects / Qt5Compat.GraphicalEffects (Building).
Nothing appears at startup
- QML import path: the binary looks for
frontend/next to itself, then one level up; running a binary copied elsewhere logsCould not locate frontend/ directory. - Controls style: src/main.cpp forces the
Basicstyle (QQuickStyle::setStyle("Basic")) because the sliders are fully custom-drawn, so settingQT_QUICK_CONTROLS_STYLEdoes not change it. - Run with
--debugand read the QML warnings in the debug log.
UI scale wrong
The uiScale setting ranges 0.5–2.0 in 0.05 steps (Settings → Display, or edit settingsConfigure.json; an old default of 0.6 is migrated to 1.0 on load). On top of it src/main.cpp computes screenAutoScale from the shorter screen edge divided by 720, clamped to 0.4–3.0, so a portrait-reporting Android tablet no longer gets a 3× factor.
Audio Issues
No or very quiet audio
- The volume slider is mapped through a quadratic curve,
(volume/100)^2.0(src/managers/volumecontroller.cpp): 50 % on the slider is 25 % linear gain, 10 % is 1 %. Low slider values are genuinely quiet. - Mute:
volumeController.muted(the command server'sget_media_statusreports it asmuted).toggleMute()ignores a press within 150 ms of the previous one, so a bouncing ESP32 button cannot double-toggle. Turning the volume up from any input unmutes. - Phone-mirror ducking: while the phone is producing sound, local playback is ducked to
scrcpyAudioDuckLevel(default 0.1) and released 1.5 s after the phone goes quiet (scrcpyAudioDuckEnabled, default on). If music seems to drop out during mirroring, this is why. - Verify the output device in the system mixer.
PulseAudio / PipeWire (Linux)
pactl list short sinks # is there a sink at all?
pactl info # identifies the audio server (PulseAudio or PipeWire compatibility)
Media backend
OCTAVE assumes Qt Multimedia's FFmpeg backend: the media manager passes probe-size / analyzeduration hints to the player, and the aarch64 AppImage deliberately ships without Qt's GStreamer plugin (Building). OCTAVE does not set QT_MEDIA_BACKEND or any GStreamer variable; if you set one yourself and playback breaks, unset it.
Playback stalls, a track keeps skipping, or playback stops by itself
The media manager recovers like this: an invalid or stalled file is retried at most 2 times, then skipped; after 10 consecutive unplayable tracks (or the playlist length, if shorter) playback stops and the source is cleared rather than looping forever. Check the log for the file names; the usual cause is an unsupported container. Extensions scanned: .mp3 .m4a .mp4 .flac .ogg .opus .wav (.mp4 because an audio-only MP4 is the same container as .m4a).
OBD Connection Issues
Read the Connection Log on Settings → OBD first: every init step, rfcomm command and error the manager sees is appended there. The status dot and label above it follow connectionStatusChanged, and get_connection_status() returns the same vocabulary (initially "Disconnected"). The colour follows the manager's connectionState: Connected (accent); Connecting (warning) for Connecting, Reconnecting, Stale Connection and the Android BLE steps (Connecting BLE..., Discovering services..., Initializing ELM327..., Querying PIDs...); No Vehicle (info); Disconnected (grey); and Failed (error) for Device Not Found, Connection Failed, Error, No MAC Address, BLE connect failed, BLE error and anything else.
Which port / address to enter
The single field (obdBluetoothPort, placeholder "MAC address, /dev path, or COM port") accepts a Bluetooth MAC, a device path or a COM port. Left empty, the default is /dev/rfcomm0 on Linux, COM3 on Windows, /dev/tty.OBD on macOS, and nothing on Android (a MAC is required). While disconnected, the OBD manager rescans every 10 s: Linux globs /dev/rfcomm*, /dev/ttyUSB*, /dev/ttyACM* (/dev/ttyS* is excluded as a false-positive source); Windows filters serial-port descriptions for bluetooth / obd / elm / serial / usb; macOS looks at tty.OBD*, tty.Bluetooth*, cu.*, *ELM*. Note the OBD scanner does not exclude the ESP32 volume knob's USB serial port; the knob's own manager does exclude the OBD port.
Serial port permissions (Linux)
sudo usermod -aG dialout $USER # log out and back in, then: groups | grep dialout
Bluetooth ELM327 pairing (Linux)
The Linux adapter picker uses direct RFCOMM sockets through connect_direct(); this path does not need a privileged /dev/rfcommN binding. A bare MAC entered through connect_to_adapter() uses the older rfcomm bind path. Pair and trust the adapter in the picker or with bluetoothctl. If using a serial-node connection, you can bind manually and enter the resulting device path:
sudo rfcomm bind 0 XX:XX:XX:XX:XX:XX
ls -la /dev/rfcomm0
"rfcomm bind failed: rfcomm binary not found" in the Connection Log means bluez-utils (Arch) / the package that ships rfcomm is missing; the log also tells you to bind once with sudo and enter /dev/rfcomm0.
Connection is slow or times out
- The connection runs on a dedicated worker thread (
OBDConnectionWorkeron aQThread) so the UI never blocks; progress is reported throughconnectionProgress/connectionDetail. - The connection timeout is 5 s by default and is set at runtime through
obdManager.set_connection_timeout(seconds), clamped to 5–60 s (there is noobdConnectionTimeoutsettings key). The worker uses it for the vehicle-verify wait (the RPM probe after init) and fixed per-command timeouts otherwise (300–1500 ms for init commands, 500 ms per poll). obdFastMode(default on): the worker polls with a 0 ms interval instead of 50 ms, and asks the adapter for a single reply per PID (010C1), so it doesn't wait out its timeout for more ECUs; on a CAN car it asks for up to six PIDs per request. Turn it off for an adapter that misreads that.- The last successful protocol is not cached: every connect sends the fixed init sequence ending in
ATSP0(auto-detect). obdAutoReconnectAttemptsis 0–10 and defaults to 0 = off (shown as "Off" on the slider). With 0, the active retry loop is off, but the 10 s passive scan remains: a saved directrfcomm://address can be retried on each scan. Serial-node discovery retries when the detected list changes. With 1–10 a failed connect is retried that many times with amin(10, 2 + 2n)s backoff before falling back to the passive scan.
"Adapter stopped responding" / a dead port pegs a CPU core
An rfcomm port that has gone away can look readable while delivering nothing. The worker guards against spinning on it: non-productive waits are paced to ~10 Hz; after 50 consecutive "readable but zero bytes" polls the worker logs Port reports readable but delivers no data; treating as disconnected, releases the port and emits connectionLost("Adapter stopped responding"); every failed init and the no-vehicle path also release the port so no read notifier stays armed; a USB unplug (ResourceError / DeviceNotFoundError) is treated as connection lost.
"No Data" for a parameter
- The OBD manager queries the adapter's supported-PID bitmaps (ranges 00/20/40/60/80/A0/C0/E0) automatically 2 s after every successful connect; the OBD settings page shows the scan progress. There is no manual "Scan Vehicle" button.
- The parameter catalog (src/managers/parametercatalog.cpp, served by frontend/OBDParameterModel.qml) lists 129 entries: 121 vehicle/adapter entries (including the
IGNITION_TIMINGalias and extended values) and 8 BerryIMU-derived values. The base decoder table has 82 Mode-01 entries plus adapter voltage (ELM_VOLTAGE,ATRV); the extended table adds 37 values from 23 PIDs. These are supported by the software, not a promise that a vehicle provides every value. Parameter toggles apply live without a reconnect. Unsupported parameters are filtered by vehicle support and the active view's polling demand; see Which PIDs Get Polled. - Many parameters only report with the engine running.
Data watchdog: "Stale Connection" / "No data received, reconnecting..."
While connected and at least one parameter is being watched, the OBD manager checks every 5 s whether any data arrived in the last 30 s (DATA_WATCHDOG_TIMEOUT_MS = 30000). If not, it logs the messages above, tears the connection down and reconnects after 100 ms. A watchdog warning can reflect the vehicle, adapter, or transport, so inspect the protocol log before diagnosing the cause. Android uses a separate per-PID watchdog; it does not start this desktop timer.
Reading the ELM327 transcript
TX/RX lines with round-trip times are logged at debug level under octave.elm327 (run with --debug and read octave-cpp-debug.log); short responses and decoder exceptions are warnings and always logged.
Black box says "unavailable" / Trip History stays empty after a drive
The OBD Home tile reads "Black box unavailable", the Black Box card in OBD settings shows the error under
Status, and octave-cpp.log has a octave.tripstore warning such as
Cannot open .../history/blackbox.sqlite: .... The recorder then records nothing rather than
pretend. Common causes: the config directory is not writable or the disk is full (the error names the path);
or a build older than the bundled SQLite (third_party/sqlite/) running on a
Debian / Ubuntu box without the libqt6sql6-sqlite package, which showed as
Driver not loaded. Current builds compile SQLite in and need no package; for an old build install
that package and restart OCTAVE. See OBD Manager → Black Box.
Android OBD (Bluetooth / BLE)
AndroidThe APK connects to ELM327 adapters over Bluetooth through android/src/org/octave/app/OctaveOBDBridge.java (Qt's QLowEnergyController is deliberately bypassed). The manifest declares BLUETOOTH_CONNECT, BLUETOOTH_SCAN (neverForLocation, so no location permission is needed on Android 12+) and BLUETOOTH_ADVERTISE; ACCESS_FINE_LOCATION is only declared up to API 30.
| Symptom | Cause / fix |
|---|---|
| Status "Requesting Bluetooth permission — tap Allow, then tap Connect again" | First connect on API 31+: the bridge calls requestPermissions() and returns; answer the system dialog and press Connect again. |
| Status "Bluetooth permission denied", detail "Grant Bluetooth in Settings -> Apps -> OCTAVE -> Permissions" | The permission was refused. Grant it in the system app settings. The permission flow lives entirely in the Java bridge now (OctaveOBDBridge.connect() requests BLUETOOTH_CONNECT); Qt's QBluetoothPermission is no longer involved. |
| Nothing happens with an empty port field | Android requires the adapter's MAC address; there is no default and no serial scan. Once Bluetooth permission has been granted (first Connect tap), the adapter list on the OBD settings page fills with the phone's paired devices as "<name> · <MAC>" (OctaveOBDBridge.bondedDevices()) — pick the ELM327 there instead of typing the address. An empty list means the permission is still missing or Bluetooth is off. |
| Where are the BLE logs? | The Java side queues I|/W| lines; the OBD manager drains up to 50 per 50 ms poll and re-emits them as [BLE] ... under octave.elm327, so they land in octave-cpp.log, the Connection Log and the Diagnostics export. No adb logcat needed. |
| Update refuses to install | Signing key mismatch (INSTALL_FAILED_UPDATE_INCOMPATIBLE). Releases up to 0.9.3 each used a throwaway key, and a local build uses your own key. Uninstall org.octave.app once, then install; from 0.9.4 on, releases update in place (Building → Android). |
Phone Mirror Issues
Desktop mirroring needs adb, the phone server jar, and libavcodec for decoding; the full design is on the Phone Mirror page. Symptoms specific to recent behaviour:
| Symptom | Cause / fix |
|---|---|
"adb not found: …" (the rest says how to get it: in a source checkout python3 scripts/fetch_platform_tools.py; on Linux sudo apt install adb or Arch's android-tools; on Windows / macOS reinstall, since releases bundle adb) | OCTAVE looks for a bundled adb first (platform-tools/ next to the binary, tools/platform-tools/<os>/ in a checkout, Contents/Resources/platform-tools/ in the bundle), then PATH, then common SDK paths. On ARM boards Google ships no adb; the distro's (sudo apt install adb) works over USB. |
| "adb is too old for wireless pairing (needs platform-tools 30 or newer)" | The adb found has no pair / mdns commands, e.g. Ubuntu 22.04's /usr/bin/adb 28.x. OCTAVE prefers any adb that reports platform-tools 30+ and check ~/Android/Sdk/platform-tools/adb before /usr/bin/adb. On an ARM64 board, an x86-64 SDK adb runs through box64 binfmt; symlink it into PATH (for example ~/.local/bin/adb) or leave it at ~/Android/Sdk/platform-tools/adb. USB mirroring still works with the old adb. |
| "Phone is connected but USB debugging is not authorized. Unlock the phone and tap 'Allow' on the USB debugging prompt." | adb devices shows unauthorized. Unlock, accept the prompt (tick "always allow"); if the prompt never appears, unplug and replug. |
| Picture freezes for a moment when the cable is bumped, then continues | Working as designed: for virtual-display sessions the server keeps the session alive for octave_persist_ms = 60 s after the socket drops; the client reattaches to the same session within 55 s with a 4 s deadline, and the view holds the last frame and shows a "Reconnecting to phone…" badge only after 2 s. If the attach fails or the window expires ("No client reconnected within N ms" in the server log) a fresh session starts. |
| Mirror goes dark when the phone screen is off / shows a black frame with a status bar | The phone-side MirrorKeeper reports sleep state; while asleep every incoming frame is dropped and the last good one is held. A stride-2 luma test over the Y plane detects black frames, with a 5 % lenient fraction (a solid status strip is 3–4 % of samples) for 3 s around a reattach. |
| Cannot get the phone back after it went dark on the dash | The pick-up path: a power press forces the panel back on (relightOnWake), a second press within the 10 s pickup window (the 8 s screen grace plus 2 s) marks the phone as in use, and waking to an unlocked phone counts as in use. |
| Music ducks while mirroring | Intentional: see Audio. The phone's own volume is never changed; OCTAVE applies its own gain (scrcpyAudioGain, default 2.0, soft-limited). |
| Server refuses to start: version mismatch | The jar in tools/phone-server/ and the client (kServerVersion in src/phone_mirror/scrcpyclient.h) must agree on 3.3.4-octave; rebuild the jar after editing phone_server/src (Building). |
| Wi-Fi: the phone scans the QR code but nothing happens / "The phone did not scan the code within 120 s" | Phone and OCTAVE must be on the same network; client isolation on guest networks blocks the mDNS discovery and the connection. A phone-hosted hotspot does not work: Android only enables Wireless debugging while the phone is a Wi-Fi client, so let the OCTAVE machine host the access point (Mirroring over Wi-Fi). Tap "Show QR code" for a fresh code after it expires. |
| Wi-Fi phone does not come back after a drive / phone reboot | Wireless debugging switches itself off when the phone leaves the network and picks a new port when switched on. Turn it back on (there is a quick-settings tile); OCTAVE retries the remembered address every 5 s and follows the new port over mDNS. |
| Mirror stutters over Wi-Fi | Lower Wi-Fi bit rate in Settings → Accessories → Phone Dock → Picture Quality (default 8 Mbps; USB 16 Mbps). Prefer 5 GHz; a 2.4 GHz link shared with Bluetooth audio in a car is marginal. Plugging in a cable switches the next session back to USB. |
phoneMirrorManager.nativeAvailable is false (desktop) | The desktop check requires libavcodec, a server jar, and adb. Check the installation diagnostic and bundled files first; if the decoder was omitted at build time, rebuild with FFmpeg development packages. Mobile builds deliberately report mirroring unavailable. |
Android Auto Issues
These are for the built-in backend (Settings → Accessories → Phone Dock → Android Auto → Head unit = Built-in); setup and the meaning of every state are on the Android Auto page. The page keeps retrying on its own (every 3 s while it waits for something, 8 s after an error) until its close button is tapped, so after fixing the cause there is nothing to restart in OCTAVE. The status line under the page title, and the Status row on the settings card, give the exact reason; the log has it under octave.androidauto.
| Symptom | Cause / fix |
|---|---|
| No Head unit choice on the Android Auto settings card | The build has no built-in receiver (androidAutoManager.nativeAvailable is false): it was built without OpenSSL or FFmpeg, or it is not a desktop build. Rebuild with both (OCTAVE_HAVE_NATIVE_AA); only the Google DHU backend is available until then. |
| "Waiting for the phone" / "Connect your phone with USB debugging on (or pair it for wireless debugging in Phone Mirror)" | adb devices lists no usable phone. Plug it in with USB debugging on and accept the authorization prompt, or pair it for wireless debugging in Phone Mirror; the adb symptoms under Phone Mirror Issues apply unchanged. |
| "adb was not found (it comes with OCTAVE's platform tools, or the Android SDK)." | The same adb search as Phone Mirror found nothing: bundled platform-tools/, then PATH, then the common SDK paths. In a source checkout run python3 scripts/fetch_platform_tools.py; on Linux the distro's adb works too. Retried every 8 s. |
| "Waiting for the phone's head unit server" | The phone is connected but nothing listens on its port 5277. On the phone: Android Auto settings → ⋮ → Start head unit server. No ⋮ entry: developer mode is off; tap Version in Android Auto's settings about 10 times first. Recent Android Auto versions (17.9 checked) do not let adb start the server, so it has to be tapped on the phone. |
| "the phone's head unit server did not answer (restart it: Android Auto settings > Stop / Start head unit server)" | The server accepted the connection but never answered within 10 s. This happens after a session was cut off without a bye-bye at the wrong moment: the server keeps accepting TCP connections but stops answering. On the phone, ⋮ → Stop head unit server, then Start head unit server; OCTAVE reconnects within seconds. OCTAVE always ends with a bye-bye (close button, app quit) to avoid this. |
"Android Auto needs a certificate" (state needsCertificate) | No head-unit certificate was found. OCTAVE ships none; put a PEM certificate and its key in the folder the status line names (<config dir>/android-auto/headunit.crt and headunit.key) or point androidAutoCertPath at the certificate. "No private key next to the head-unit certificate (looked for …)": the file at androidAutoCertPath holds no key and the sibling .key file is missing. Picked up on the next retry, within 3 s. See The head-unit certificate. |
| "the head-unit certificate is not a PEM certificate" / "OpenSSL refused the head-unit certificate" / "no private key found for the head-unit certificate" / "the private key does not match the head-unit certificate" | The files exist but OpenSSL can't use them: not PEM (convert DER with openssl x509 -inform der), a key for another certificate, or a passphrase-protected key (not supported: OCTAVE has no way to ask for the passphrase). The log line carries OpenSSL's own reason. |
| "TLS handshake failed: …", or "the phone closed the connection" right after connecting | The phone refused the head unit's certificate. Android Auto only accepts a client certificate that chains to Google's head-unit certificate authority; a self-signed or otherwise unrecognised one is rejected during the handshake. Phone logcat (adb logcat | grep -i CAR.GAL) shows the phone's side. |
| "the phone refused protocol version 1.6" | The phone's Android Auto no longer speaks the protocol version OCTAVE asks for. Note the Android Auto version from the phone's app info and report it; the receiver was checked against 17.9. |
| "the phone stopped answering" / "Android Auto ended (…); reconnecting..." | No ping answer for 10 s (a dropped Wi-Fi or USB link, the phone asleep or killed), or the phone ended the session. OCTAVE reconnects on its own while the page is wanted. |
| Picture but no sound | The log says "no audio output device; Android Auto audio will not play" or "could not start audio output": check OCTAVE's audio output device (Settings → Media) and its volume / mute, which Android Auto audio follows. The phone's own volume is not used. |
| Google Assistant / voice commands hear nothing | Expected for now: the microphone channel is announced but OCTAVE sends no audio on it. |
| Video stops when leaving the page | Intentional: a hidden page tells the phone to stop sending video; audio continues and the picture returns when the page is shown again. The close button (X) ends the session. |
Hardware Sensor Issues
I²C permissions and bus
sudo usermod -aG i2c $USER
sudo raspi-config # Raspberry Pi: Interface Options > I2C > Enable
i2cdetect -y 2
# Expected: 0x6A (LSM6DSL), 0x1C (LIS3MDL), 0x77 (BMP388), 0x73 (PAJ7620U2)
The default bus number is the constant I2C_BUS = 2 (src/managers/berryimumanager.cpp, src/managers/gesturemanager.cpp) — there is no setting. The managers open /dev/i2c-2 directly with ioctl(I2C_SLAVE) (Linux only).
"Device not found" errors
| Error | Cause | Solution |
|---|---|---|
/dev/i2c-2 not found | I²C not enabled or the sensors are on another bus | Enable I²C; move the sensors to bus 2 or change the constant and rebuild/restart. |
Permission denied on /dev/i2c-2 | User not in the i2c group | sudo usermod -aG i2c $USER, then log out/in. |
| Expected chip ID 0x7620, got 0x0000 | PAJ7620U2 not responding | Check wiring, 3.3 V supply, pull-ups. |
Reconnection behaviour
BerryIMU and gesture managers retry up to 5 times with linear backoff (5, 10, 15, 20, 25 s), then give up. Both skip the retries on a permission error (it cannot fix itself): when open() fails with EACCES / EPERM they log permission denied on /dev/i2c-2 - add the user to the i2c group; not retrying. Fix the group, log out and in, then toggle the sensor off and on in Settings → Accessories. A bus that fails mid-run is reported as "Error" and retried after 5 consecutive failed block reads (read loop error: 5 consecutive I2C read failures), so one NACK on a marginal cable no longer goes unnoticed as frozen zeros.
IMU updates look slow
The software read/fusion loop stays active, but the signals emitted to QML drop to 5 Hz (IDLE_EMIT_INTERVAL = 0.2) unless a page that draws them is showing — Car, Sensors, Sensor Home, the OBD menu, or the home screen when an IMU parameter is on a home card — where they run at ~60 Hz. This is the idle-CPU optimisation, not a fault.
ESP32 volume knob not connecting
- Confirm the ESP32-S3 dongle appears in
dmesgafter plugging in and that you are indialout. - The knob's manager excludes the OBD adapter's port from its scan;
esp32VolumePortdefaults to empty, which means an ESP32-S3 is auto-selected at startup; no other port is. A dongle that isn't an ESP32-S3 has to be picked in the Volume Knob settings, and then shows "Port … open: turn the knob to confirm" until the first turn or press (nothing is written to it before that). A settings file from an older build may still hold the former"COM7"default; clear it. - A dongle that disappears is detected on
QSerialPort'sResourceError/DeviceNotFoundError/PermissionError/ReadError/WriteErroror a rejected keepalive write; withesp32AutoReconnecton it then retries five times. - Baud rate is 115200. The reader is driven by
QSerialPort::readyRead.
Performance Issues
UI stuttering
- Look for
octave.ui_watchdogwarnings in the log — they sample every 2 s and warn when the last GUI response is more than 5 s old; this is not a complete record of all UI stalls. - Run with
--profile(build/octave --profileon a Debug build) and watch the[PERF]line: event-loop latency is green under 5 ms average, yellow under 16 ms, red above (STUTTER); the jitter column is the delay a background thread sees trying to run every 10 ms (CPU contention); slow slots are those with p95 > 2 ms. Details on the Development page. - Check CPU: if a core is pegged while the OBD adapter is unplugged, see the dead-port note under OBD.
High memory usage
- The cover cache keeps up to 500 files (
m_maxCacheFiles); filenames hash the album identifier, not image contents. The in-memory metadata cache has a 1000-entry limit. The separate persistentmetadata_cache.jsonstore is not bounded by that limit; see Metadata Caching. - Audio analyzer: the FFT is pre-computed for the entire track when it starts, so a long lossless file costs memory up front.
Slow library scan
The library scan runs synchronously on the GUI thread (progress via scanProgress); a very large first scan will block the UI until it finishes. Desktop neighbouring-track pre-cache (±3 tracks, 80 ms after a track change) and album-colour extraction run off-thread. Android pre-cache is synchronous.
Dev Tooling Issues
| Symptom | Cause / fix |
|---|---|
MCP servers octave-perf / octave-benchmark fail to connect | The servers are Python scripts (dev/mcp_server.py, dev/mcp_benchmark.py) that drive the app over its command server. Check the interpreter and script paths in the local, gitignored .mcp.json, install the mcp package for that interpreter, and run the app with --profile. Machine-specific entries copied from another workstation may be invalid. The benchmark server also needs --mcp or it runs its CLI suite and exits. |
build/octave --dev / --profile logs --dev / --profile need a build with -DOCTAVE_DEV_TOOLS=ON (the default for Debug); ignoring | Only -DCMAKE_BUILD_TYPE=Debug builds include the dev tooling by default. For just the simulated car, --sim works in every build. Build a Debug tree (cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug) or pass -DOCTAVE_DEV_TOOLS=ON. The tools pick the binary through dev/app_launch.py; point OCTAVE_BIN at the right one if several builds exist. |
command server could not listen on 127.0.0.1:9876 | Another OCTAVE is still running with --profile, often a story or sweep that was interrupted. Stop it, or start this one with --command-port N and pass --port N to the tool. |
| Screenshots have blank icons / album art | Offscreen rendering may lack the effects needed for a faithful screenshot; use the default Xvfb mode (sudo pacman -S xorg-server-xvfb) or --visible. |
| Screenshot tool navigates but every capture shows the home page | The page is listed in the command server's navigate map (kPages in src/dev/coreactions.cpp) but missing from Main.qml's navigateTo page map; a name missing from kPages returns Unknown page: … instead. Register a new page in both. Arguments reach the QML function as QVariant (invokeByName() in src/service/commandserver.cpp), since QML functions take untyped parameters. |
ctest --test-dir build hangs or a UI test fails to start | Run with QT_QPA_PLATFORM=offscreen, as CI does. Use ctest --test-dir build --output-on-failure -V -R <name> to run one test verbosely. The tests are only built with OCTAVE_BUILD_TESTS (on by default for desktop Debug builds). |
WSL Display Issues
OCTAVE needs a display: DISPLAY or WAYLAND_DISPLAY must be set. WSLg supports WSL 2 on Windows 10 build 19044+ and Windows 11; follow Microsoft's GUI-app instructions. An external X server needs a DISPLAY value appropriate to its address and WSL network mode; do not assume :0. Software rendering can help diagnose graphics problems, but does not reproduce shader effects or the 3D view:
export QT_QUICK_BACKEND=software
export LIBGL_ALWAYS_SOFTWARE=1
./build/octave